From 5dd827e0fa586521416730e2bb8c3846f6dd91fc Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Sat, 11 Mar 2023 18:15:33 -0800 Subject: libperf evlist: Avoid a use of evsel idx Setting the leader iterates the list, so rather than use idx (which may be changed through list reordering) just count the elements and set afterwards. Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Florian Fischer Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: John Garry Cc: Kajol Jain Cc: Kan Liang Cc: Kim Phillips Cc: Leo Yan Cc: Mark Rutland Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Ravi Bangoria Cc: Sean Christopherson Cc: Steinar H. Gunderson Cc: Stephane Eranian Cc: Suzuki Poulouse Cc: Xing Zhengjun Link: https://lore.kernel.org/r/20230312021543.3060328-2-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/lib/perf/evlist.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'tools/lib') diff --git a/tools/lib/perf/evlist.c b/tools/lib/perf/evlist.c index 61b637f29b82..2d6121e89ccb 100644 --- a/tools/lib/perf/evlist.c +++ b/tools/lib/perf/evlist.c @@ -687,15 +687,14 @@ perf_evlist__next_mmap(struct perf_evlist *evlist, struct perf_mmap *map, void __perf_evlist__set_leader(struct list_head *list, struct perf_evsel *leader) { - struct perf_evsel *first, *last, *evsel; - - first = list_first_entry(list, struct perf_evsel, node); - last = list_last_entry(list, struct perf_evsel, node); - - leader->nr_members = last->idx - first->idx + 1; + struct perf_evsel *evsel; + int n = 0; - __perf_evlist__for_each_entry(list, evsel) + __perf_evlist__for_each_entry(list, evsel) { evsel->leader = leader; + n++; + } + leader->nr_members = n; } void perf_evlist__set_leader(struct perf_evlist *evlist) -- cgit v1.2.3 From 9d2dc632e09c0fe3a8a5890845bbd65b211fd662 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Sat, 11 Mar 2023 18:15:42 -0800 Subject: perf evlist: Remove nr_groups Maintaining the number of groups during event parsing is problematic and since changing to sort/regroup events can only be computed by a linear pass over the evlist. As the value is generally only used in tests, rather than hold it in a variable compute it by passing over the evlist when necessary. This change highlights that libpfm's counting of groups with a single entry disagreed with regular event parsing. The libpfm tests are updated accordingly. Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Florian Fischer Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: John Garry Cc: Kajol Jain Cc: Kan Liang Cc: Kim Phillips Cc: Leo Yan Cc: Mark Rutland Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Ravi Bangoria Cc: Sean Christopherson Cc: Steinar H. Gunderson Cc: Stephane Eranian Cc: Suzuki Poulouse Cc: Xing Zhengjun Link: https://lore.kernel.org/r/20230312021543.3060328-2-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/lib/perf/evlist.c | 18 +++++++++++++++++- tools/lib/perf/include/internal/evlist.h | 1 - tools/lib/perf/include/perf/evlist.h | 1 + tools/perf/builtin-record.c | 2 +- tools/perf/builtin-report.c | 2 +- tools/perf/tests/bpf.c | 1 - tools/perf/tests/parse-events.c | 22 +++++++++++----------- tools/perf/tests/pfm.c | 12 ++++++------ tools/perf/util/evlist.c | 2 +- tools/perf/util/evlist.h | 6 ++++++ tools/perf/util/header.c | 3 +-- tools/perf/util/parse-events.c | 1 - tools/perf/util/parse-events.h | 1 - tools/perf/util/parse-events.y | 10 ---------- tools/perf/util/pfm.c | 1 - 15 files changed, 45 insertions(+), 38 deletions(-) (limited to 'tools/lib') diff --git a/tools/lib/perf/evlist.c b/tools/lib/perf/evlist.c index 2d6121e89ccb..81e8b5fcd8ba 100644 --- a/tools/lib/perf/evlist.c +++ b/tools/lib/perf/evlist.c @@ -703,7 +703,23 @@ void perf_evlist__set_leader(struct perf_evlist *evlist) struct perf_evsel *first = list_entry(evlist->entries.next, struct perf_evsel, node); - evlist->nr_groups = evlist->nr_entries > 1 ? 1 : 0; __perf_evlist__set_leader(&evlist->entries, first); } } + +int perf_evlist__nr_groups(struct perf_evlist *evlist) +{ + struct perf_evsel *evsel; + int nr_groups = 0; + + perf_evlist__for_each_evsel(evlist, evsel) { + /* + * evsels by default have a nr_members of 1, and they are their + * own leader. If the nr_members is >1 then this is an + * indication of a group. + */ + if (evsel->leader == evsel && evsel->nr_members > 1) + nr_groups++; + } + return nr_groups; +} diff --git a/tools/lib/perf/include/internal/evlist.h b/tools/lib/perf/include/internal/evlist.h index 850f07070036..3339bc2f1765 100644 --- a/tools/lib/perf/include/internal/evlist.h +++ b/tools/lib/perf/include/internal/evlist.h @@ -17,7 +17,6 @@ struct perf_mmap_param; struct perf_evlist { struct list_head entries; int nr_entries; - int nr_groups; bool has_user_cpus; bool needs_map_propagation; /** diff --git a/tools/lib/perf/include/perf/evlist.h b/tools/lib/perf/include/perf/evlist.h index 9ca399d49bb4..e894b770779e 100644 --- a/tools/lib/perf/include/perf/evlist.h +++ b/tools/lib/perf/include/perf/evlist.h @@ -47,4 +47,5 @@ LIBPERF_API struct perf_mmap *perf_evlist__next_mmap(struct perf_evlist *evlist, (pos) = perf_evlist__next_mmap((evlist), (pos), overwrite)) LIBPERF_API void perf_evlist__set_leader(struct perf_evlist *evlist); +LIBPERF_API int perf_evlist__nr_groups(struct perf_evlist *evlist); #endif /* __LIBPERF_EVLIST_H */ diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index fb8aab0cedd0..74388164a571 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -2474,7 +2474,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) rec->tool.ordered_events = false; } - if (!rec->evlist->core.nr_groups) + if (evlist__nr_groups(rec->evlist) == 0) perf_header__clear_feat(&session->header, HEADER_GROUP_DESC); if (data->is_pipe) { diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 2ee2ecca208e..6400615b5e98 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -1481,7 +1481,7 @@ repeat: setup_forced_leader(&report, session->evlist); - if (symbol_conf.group_sort_idx && !session->evlist->core.nr_groups) { + if (symbol_conf.group_sort_idx && evlist__nr_groups(session->evlist) == 0) { parse_options_usage(NULL, options, "group-sort-idx", 0); ret = -EINVAL; goto error; diff --git a/tools/perf/tests/bpf.c b/tools/perf/tests/bpf.c index ae9223f27cfb..8beb46066034 100644 --- a/tools/perf/tests/bpf.c +++ b/tools/perf/tests/bpf.c @@ -153,7 +153,6 @@ static int do_test(struct bpf_object *obj, int (*func)(void), } evlist__splice_list_tail(evlist, &parse_state.list); - evlist->core.nr_groups = parse_state.nr_groups; evlist__config(evlist, &opts, NULL); diff --git a/tools/perf/tests/parse-events.c b/tools/perf/tests/parse-events.c index 71a5cb343311..ffa6f0a90741 100644 --- a/tools/perf/tests/parse-events.c +++ b/tools/perf/tests/parse-events.c @@ -53,7 +53,7 @@ static int test__checkevent_tracepoint(struct evlist *evlist) struct evsel *evsel = evlist__first(evlist); TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->core.nr_entries); - TEST_ASSERT_VAL("wrong number of groups", 0 == evlist->core.nr_groups); + TEST_ASSERT_VAL("wrong number of groups", 0 == evlist__nr_groups(evlist)); TEST_ASSERT_VAL("wrong type", PERF_TYPE_TRACEPOINT == evsel->core.attr.type); TEST_ASSERT_VAL("wrong sample_type", PERF_TP_SAMPLE_TYPE == evsel->core.attr.sample_type); @@ -66,7 +66,7 @@ static int test__checkevent_tracepoint_multi(struct evlist *evlist) struct evsel *evsel; TEST_ASSERT_VAL("wrong number of entries", evlist->core.nr_entries > 1); - TEST_ASSERT_VAL("wrong number of groups", 0 == evlist->core.nr_groups); + TEST_ASSERT_VAL("wrong number of groups", 0 == evlist__nr_groups(evlist)); evlist__for_each_entry(evlist, evsel) { TEST_ASSERT_VAL("wrong type", @@ -677,7 +677,7 @@ static int test__group1(struct evlist *evlist) struct evsel *evsel, *leader; TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->core.nr_entries); - TEST_ASSERT_VAL("wrong number of groups", 1 == evlist->core.nr_groups); + TEST_ASSERT_VAL("wrong number of groups", 1 == evlist__nr_groups(evlist)); /* instructions:k */ evsel = leader = evlist__first(evlist); @@ -719,7 +719,7 @@ static int test__group2(struct evlist *evlist) struct evsel *evsel, *leader; TEST_ASSERT_VAL("wrong number of entries", 3 == evlist->core.nr_entries); - TEST_ASSERT_VAL("wrong number of groups", 1 == evlist->core.nr_groups); + TEST_ASSERT_VAL("wrong number of groups", 1 == evlist__nr_groups(evlist)); /* faults + :ku modifier */ evsel = leader = evlist__first(evlist); @@ -775,7 +775,7 @@ static int test__group3(struct evlist *evlist __maybe_unused) struct evsel *evsel, *leader; TEST_ASSERT_VAL("wrong number of entries", 5 == evlist->core.nr_entries); - TEST_ASSERT_VAL("wrong number of groups", 2 == evlist->core.nr_groups); + TEST_ASSERT_VAL("wrong number of groups", 2 == evlist__nr_groups(evlist)); /* group1 syscalls:sys_enter_openat:H */ evsel = leader = evlist__first(evlist); @@ -868,7 +868,7 @@ static int test__group4(struct evlist *evlist __maybe_unused) struct evsel *evsel, *leader; TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->core.nr_entries); - TEST_ASSERT_VAL("wrong number of groups", 1 == evlist->core.nr_groups); + TEST_ASSERT_VAL("wrong number of groups", 1 == evlist__nr_groups(evlist)); /* cycles:u + p */ evsel = leader = evlist__first(evlist); @@ -912,7 +912,7 @@ static int test__group5(struct evlist *evlist __maybe_unused) struct evsel *evsel, *leader; TEST_ASSERT_VAL("wrong number of entries", 5 == evlist->core.nr_entries); - TEST_ASSERT_VAL("wrong number of groups", 2 == evlist->core.nr_groups); + TEST_ASSERT_VAL("wrong number of groups", 2 == evlist__nr_groups(evlist)); /* cycles + G */ evsel = leader = evlist__first(evlist); @@ -998,7 +998,7 @@ static int test__group_gh1(struct evlist *evlist) struct evsel *evsel, *leader; TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->core.nr_entries); - TEST_ASSERT_VAL("wrong number of groups", 1 == evlist->core.nr_groups); + TEST_ASSERT_VAL("wrong number of groups", 1 == evlist__nr_groups(evlist)); /* cycles + :H group modifier */ evsel = leader = evlist__first(evlist); @@ -1038,7 +1038,7 @@ static int test__group_gh2(struct evlist *evlist) struct evsel *evsel, *leader; TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->core.nr_entries); - TEST_ASSERT_VAL("wrong number of groups", 1 == evlist->core.nr_groups); + TEST_ASSERT_VAL("wrong number of groups", 1 == evlist__nr_groups(evlist)); /* cycles + :G group modifier */ evsel = leader = evlist__first(evlist); @@ -1078,7 +1078,7 @@ static int test__group_gh3(struct evlist *evlist) struct evsel *evsel, *leader; TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->core.nr_entries); - TEST_ASSERT_VAL("wrong number of groups", 1 == evlist->core.nr_groups); + TEST_ASSERT_VAL("wrong number of groups", 1 == evlist__nr_groups(evlist)); /* cycles:G + :u group modifier */ evsel = leader = evlist__first(evlist); @@ -1118,7 +1118,7 @@ static int test__group_gh4(struct evlist *evlist) struct evsel *evsel, *leader; TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->core.nr_entries); - TEST_ASSERT_VAL("wrong number of groups", 1 == evlist->core.nr_groups); + TEST_ASSERT_VAL("wrong number of groups", 1 == evlist__nr_groups(evlist)); /* cycles:G + :uG group modifier */ evsel = leader = evlist__first(evlist); diff --git a/tools/perf/tests/pfm.c b/tools/perf/tests/pfm.c index 71b76deb1f92..2e38dfa34b6c 100644 --- a/tools/perf/tests/pfm.c +++ b/tools/perf/tests/pfm.c @@ -76,7 +76,7 @@ static int test__pfm_events(struct test_suite *test __maybe_unused, count_pfm_events(&evlist->core), table[i].nr_events); TEST_ASSERT_EQUAL(table[i].events, - evlist->core.nr_groups, + evlist__nr_groups(evlist), 0); evlist__delete(evlist); @@ -103,22 +103,22 @@ static int test__pfm_group(struct test_suite *test __maybe_unused, { .events = "{instructions}", .nr_events = 1, - .nr_groups = 1, + .nr_groups = 0, }, { .events = "{instructions},{}", .nr_events = 1, - .nr_groups = 1, + .nr_groups = 0, }, { .events = "{},{instructions}", .nr_events = 1, - .nr_groups = 1, + .nr_groups = 0, }, { .events = "{instructions},{instructions}", .nr_events = 2, - .nr_groups = 2, + .nr_groups = 0, }, { .events = "{instructions,cycles},{instructions,cycles}", @@ -161,7 +161,7 @@ static int test__pfm_group(struct test_suite *test __maybe_unused, count_pfm_events(&evlist->core), table[i].nr_events); TEST_ASSERT_EQUAL(table[i].events, - evlist->core.nr_groups, + evlist__nr_groups(evlist), table[i].nr_groups); evlist__delete(evlist); diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 9e4b2bb0e6fa..b74e12239aec 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -1777,7 +1777,7 @@ bool evlist__exclude_kernel(struct evlist *evlist) */ void evlist__force_leader(struct evlist *evlist) { - if (!evlist->core.nr_groups) { + if (evlist__nr_groups(evlist) == 0) { struct evsel *leader = evlist__first(evlist); evlist__set_leader(evlist); diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index d89d8f92802b..46cf402add93 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h @@ -9,6 +9,7 @@ #include #include #include +#include #include "events_stats.h" #include "evsel.h" #include @@ -255,6 +256,11 @@ static inline struct evsel *evlist__last(struct evlist *evlist) return container_of(evsel, struct evsel, core); } +static inline int evlist__nr_groups(struct evlist *evlist) +{ + return perf_evlist__nr_groups(&evlist->core); +} + int evlist__strerror_open(struct evlist *evlist, int err, char *buf, size_t size); int evlist__strerror_mmap(struct evlist *evlist, int err, char *buf, size_t size); diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 404d816ca124..276870221ce0 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -786,7 +786,7 @@ static int write_pmu_mappings(struct feat_fd *ff, static int write_group_desc(struct feat_fd *ff, struct evlist *evlist) { - u32 nr_groups = evlist->core.nr_groups; + u32 nr_groups = evlist__nr_groups(evlist); struct evsel *evsel; int ret; @@ -2807,7 +2807,6 @@ static int process_group_desc(struct feat_fd *ff, void *data __maybe_unused) * Rebuild group relationship based on the group_desc */ session = container_of(ff->ph, struct perf_session, header); - session->evlist->core.nr_groups = nr_groups; i = nr = 0; evlist__for_each_entry(session->evlist, evsel) { diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 93a90651266f..9ec3c1dc81e0 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -2260,7 +2260,6 @@ int __parse_events(struct evlist *evlist, const char *str, if (!ret) { struct evsel *last; - evlist->core.nr_groups += parse_state.nr_groups; last = evlist__last(evlist); last->cmdline_group_boundary = true; diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index fdac44dc696b..767ad1729228 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h @@ -122,7 +122,6 @@ struct parse_events_error { struct parse_events_state { struct list_head list; int idx; - int nr_groups; struct parse_events_error *error; struct evlist *evlist; struct list_head *terms; diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y index f1b153c72d67..3a04602d2982 100644 --- a/tools/perf/util/parse-events.y +++ b/tools/perf/util/parse-events.y @@ -49,14 +49,6 @@ static void free_list_evsel(struct list_head* list_evsel) free(list_evsel); } -static void inc_group_count(struct list_head *list, - struct parse_events_state *parse_state) -{ - /* Count groups only have more than 1 members */ - if (!list_is_last(list->next, list)) - parse_state->nr_groups++; -} - %} %token PE_START_EVENTS PE_START_TERMS @@ -201,7 +193,6 @@ PE_NAME '{' events '}' { struct list_head *list = $3; - inc_group_count(list, _parse_state); /* Takes ownership of $1. */ parse_events__set_leader($1, list); $$ = list; @@ -211,7 +202,6 @@ PE_NAME '{' events '}' { struct list_head *list = $2; - inc_group_count(list, _parse_state); parse_events__set_leader(NULL, list); $$ = list; } diff --git a/tools/perf/util/pfm.c b/tools/perf/util/pfm.c index b59ba825ddc9..6c11914c179f 100644 --- a/tools/perf/util/pfm.c +++ b/tools/perf/util/pfm.c @@ -112,7 +112,6 @@ int parse_libpfm_events_option(const struct option *opt, const char *str, "cannot close a non-existing event group\n"); goto error; } - evlist->core.nr_groups++; grp_leader = NULL; grp_evt = -1; } -- cgit v1.2.3 From 27c6f2455b29f27c8daf209ec8bdf0ac2c567b74 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Tue, 14 Mar 2023 16:42:31 -0700 Subject: perf record: Record dropped sample count When it uses bpf filters, event might drop some samples. It'd be nice if it can report how many samples it lost. As LOST_SAMPLES event can carry the similar information, let's use it for bpf filters. To indicate it's from BPF filters, add a new misc flag for that and do not display cpu load warnings. Signed-off-by: Namhyung Kim Acked-by: Jiri Olsa Cc: Adrian Hunter Cc: Andi Kleen Cc: Hao Luo Cc: Ian Rogers Cc: Ingo Molnar Cc: James Clark Cc: Kan Liang Cc: Leo Yan Cc: Peter Zijlstra Cc: Ravi Bangoria Cc: Song Liu Cc: Stephane Eranian Cc: bpf@vger.kernel.org Link: https://lore.kernel.org/r/20230314234237.3008956-2-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/lib/perf/include/perf/event.h | 2 ++ tools/perf/builtin-record.c | 38 +++++++++++++++++++++++-------------- tools/perf/util/bpf-filter.c | 7 +++++++ tools/perf/util/bpf-filter.h | 5 +++++ tools/perf/util/session.c | 3 ++- 5 files changed, 40 insertions(+), 15 deletions(-) (limited to 'tools/lib') diff --git a/tools/lib/perf/include/perf/event.h b/tools/lib/perf/include/perf/event.h index ad47d7b31046..51b9338f4c11 100644 --- a/tools/lib/perf/include/perf/event.h +++ b/tools/lib/perf/include/perf/event.h @@ -70,6 +70,8 @@ struct perf_record_lost { __u64 lost; }; +#define PERF_RECORD_MISC_LOST_SAMPLES_BPF (1 << 15) + struct perf_record_lost_samples { struct perf_event_header header; __u64 lost; diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 33ebe42b025e..6df8b823859d 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -52,6 +52,7 @@ #include "util/pmu-hybrid.h" #include "util/evlist-hybrid.h" #include "util/off_cpu.h" +#include "util/bpf-filter.h" #include "asm/bug.h" #include "perf.h" #include "cputopo.h" @@ -1856,24 +1857,16 @@ record__switch_output(struct record *rec, bool at_exit) return fd; } -static void __record__read_lost_samples(struct record *rec, struct evsel *evsel, +static void __record__save_lost_samples(struct record *rec, struct evsel *evsel, struct perf_record_lost_samples *lost, - int cpu_idx, int thread_idx) + int cpu_idx, int thread_idx, u64 lost_count, + u16 misc_flag) { - struct perf_counts_values count; struct perf_sample_id *sid; struct perf_sample sample = {}; int id_hdr_size; - if (perf_evsel__read(&evsel->core, cpu_idx, thread_idx, &count) < 0) { - pr_debug("read LOST count failed\n"); - return; - } - - if (count.lost == 0) - return; - - lost->lost = count.lost; + lost->lost = lost_count; if (evsel->core.ids) { sid = xyarray__entry(evsel->core.sample_id, cpu_idx, thread_idx); sample.id = sid->id; @@ -1882,6 +1875,7 @@ static void __record__read_lost_samples(struct record *rec, struct evsel *evsel, id_hdr_size = perf_event__synthesize_id_sample((void *)(lost + 1), evsel->core.attr.sample_type, &sample); lost->header.size = sizeof(*lost) + id_hdr_size; + lost->header.misc = misc_flag; record__write(rec, NULL, lost, lost->header.size); } @@ -1905,6 +1899,7 @@ static void record__read_lost_samples(struct record *rec) evlist__for_each_entry(session->evlist, evsel) { struct xyarray *xy = evsel->core.sample_id; + u64 lost_count; if (xy == NULL || evsel->core.fd == NULL) continue; @@ -1916,12 +1911,27 @@ static void record__read_lost_samples(struct record *rec) for (int x = 0; x < xyarray__max_x(xy); x++) { for (int y = 0; y < xyarray__max_y(xy); y++) { - __record__read_lost_samples(rec, evsel, lost, x, y); + struct perf_counts_values count; + + if (perf_evsel__read(&evsel->core, x, y, &count) < 0) { + pr_debug("read LOST count failed\n"); + goto out; + } + + if (count.lost) { + __record__save_lost_samples(rec, evsel, lost, + x, y, count.lost, 0); + } } } + + lost_count = perf_bpf_filter__lost_count(evsel); + if (lost_count) + __record__save_lost_samples(rec, evsel, lost, 0, 0, lost_count, + PERF_RECORD_MISC_LOST_SAMPLES_BPF); } +out: free(lost); - } static volatile sig_atomic_t workload_exec_errno; diff --git a/tools/perf/util/bpf-filter.c b/tools/perf/util/bpf-filter.c index f20e1bc03778..7bd6f2e41513 100644 --- a/tools/perf/util/bpf-filter.c +++ b/tools/perf/util/bpf-filter.c @@ -69,6 +69,13 @@ int perf_bpf_filter__destroy(struct evsel *evsel) return 0; } +u64 perf_bpf_filter__lost_count(struct evsel *evsel) +{ + struct sample_filter_bpf *skel = evsel->bpf_skel; + + return skel ? skel->bss->dropped : 0; +} + struct perf_bpf_filter_expr *perf_bpf_filter_expr__new(unsigned long sample_flags, enum perf_bpf_filter_op op, unsigned long val) diff --git a/tools/perf/util/bpf-filter.h b/tools/perf/util/bpf-filter.h index eb8e1ac43cdf..f0c66764c6d0 100644 --- a/tools/perf/util/bpf-filter.h +++ b/tools/perf/util/bpf-filter.h @@ -22,6 +22,7 @@ struct perf_bpf_filter_expr *perf_bpf_filter_expr__new(unsigned long sample_flag int perf_bpf_filter__parse(struct list_head *expr_head, const char *str); int perf_bpf_filter__prepare(struct evsel *evsel); int perf_bpf_filter__destroy(struct evsel *evsel); +u64 perf_bpf_filter__lost_count(struct evsel *evsel); #else /* !HAVE_BPF_SKEL */ @@ -38,5 +39,9 @@ static inline int perf_bpf_filter__destroy(struct evsel *evsel __maybe_unused) { return -EOPNOTSUPP; } +static inline u64 perf_bpf_filter__lost_count(struct evsel *evsel __maybe_unused) +{ + return 0; +} #endif /* HAVE_BPF_SKEL*/ #endif /* PERF_UTIL_BPF_FILTER_H */ diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 749d5b5c135b..7d8d057d1772 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -1582,7 +1582,8 @@ static int machines__deliver_event(struct machines *machines, evlist->stats.total_lost += event->lost.lost; return tool->lost(tool, event, sample, machine); case PERF_RECORD_LOST_SAMPLES: - if (tool->lost_samples == perf_event__process_lost_samples) + if (tool->lost_samples == perf_event__process_lost_samples && + !(event->header.misc & PERF_RECORD_MISC_LOST_SAMPLES_BPF)) evlist->stats.total_lost_samples += event->lost_samples.lost; return tool->lost_samples(tool, event, sample, machine); case PERF_RECORD_READ: -- cgit v1.2.3 From c9dc580c43b8d83de0c14158e826f79e41098822 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 3 Apr 2023 11:40:30 -0700 Subject: tools api: Add io__getline Reads a line to allocated memory up to a newline following the getline API. Committer notes: It also adds this new function to the 'api io' 'perf test' entry: $ perf test "api io" 64: Test api io : Ok $ Signed-off-by: Ian Rogers Tested-by: Arnaldo Carvalho de Melo Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ingo Molnar Cc: Jiri Olsa Cc: Mark Rutland Cc: Nathan Chancellor Cc: Nick Desaulniers Cc: Peter Zijlstra Cc: Tom Rix Cc: llvm@lists.linux.dev Link: https://lore.kernel.org/r/20230403184033.1836023-2-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/lib/api/io.h | 45 +++++++++++++++++++++++++++++++++++++++++++++ tools/perf/tests/api-io.c | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+) (limited to 'tools/lib') diff --git a/tools/lib/api/io.h b/tools/lib/api/io.h index 777c20f6b604..d5e8cf0dada0 100644 --- a/tools/lib/api/io.h +++ b/tools/lib/api/io.h @@ -7,7 +7,9 @@ #ifndef __API_IO__ #define __API_IO__ +#include #include +#include #include struct io { @@ -112,4 +114,47 @@ static inline int io__get_dec(struct io *io, __u64 *dec) } } +/* Read up to and including the first newline following the pattern of getline. */ +static inline ssize_t io__getline(struct io *io, char **line_out, size_t *line_len_out) +{ + char buf[128]; + int buf_pos = 0; + char *line = NULL, *temp; + size_t line_len = 0; + int ch = 0; + + /* TODO: reuse previously allocated memory. */ + free(*line_out); + while (ch != '\n') { + ch = io__get_char(io); + + if (ch < 0) + break; + + if (buf_pos == sizeof(buf)) { + temp = realloc(line, line_len + sizeof(buf)); + if (!temp) + goto err_out; + line = temp; + memcpy(&line[line_len], buf, sizeof(buf)); + line_len += sizeof(buf); + buf_pos = 0; + } + buf[buf_pos++] = (char)ch; + } + temp = realloc(line, line_len + buf_pos + 1); + if (!temp) + goto err_out; + line = temp; + memcpy(&line[line_len], buf, buf_pos); + line[line_len + buf_pos] = '\0'; + line_len += buf_pos; + *line_out = line; + *line_len_out = line_len; + return line_len; +err_out: + free(line); + return -ENOMEM; +} + #endif /* __API_IO__ */ diff --git a/tools/perf/tests/api-io.c b/tools/perf/tests/api-io.c index e91cf2c127f1..6aea84ca6673 100644 --- a/tools/perf/tests/api-io.c +++ b/tools/perf/tests/api-io.c @@ -289,6 +289,40 @@ static int test_get_dec(void) return ret; } +static int test_get_line(void) +{ + char path[PATH_MAX]; + struct io io; + char test_string[1024]; + char *line = NULL; + size_t i, line_len = 0; + size_t buf_size = 128; + int ret = 0; + + for (i = 0; i < 512; i++) + test_string[i] = 'a'; + test_string[512] = '\n'; + for (i = 513; i < 1023; i++) + test_string[i] = 'b'; + test_string[1023] = '\0'; + + if (setup_test(path, test_string, buf_size, &io)) + return -1; + + EXPECT_EQUAL((int)io__getline(&io, &line, &line_len), 513); + EXPECT_EQUAL((int)strlen(line), 513); + for (i = 0; i < 512; i++) + EXPECT_EQUAL(line[i], 'a'); + EXPECT_EQUAL(line[512], '\n'); + EXPECT_EQUAL((int)io__getline(&io, &line, &line_len), 510); + for (i = 0; i < 510; i++) + EXPECT_EQUAL(line[i], 'b'); + + free(line); + cleanup_test(path, &io); + return ret; +} + static int test__api_io(struct test_suite *test __maybe_unused, int subtest __maybe_unused) { @@ -300,6 +334,8 @@ static int test__api_io(struct test_suite *test __maybe_unused, ret = TEST_FAIL; if (test_get_dec()) ret = TEST_FAIL; + if (test_get_line()) + ret = TEST_FAIL; return ret; } -- cgit v1.2.3 From 1f94479edb4decdcec3e902528abb47f0ccd5d16 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 12 Apr 2023 12:54:44 -0300 Subject: libperf: Make perf_cpu_map__alloc() available as an internal function for tools/perf to use We had the open coded equivalent in perf_cpu_map__empty_new(), so reuse what is in libperf. Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexey Bayduraev Cc: Dmitriy Vyukov Cc: Jiri Olsa Cc: Namhyung Kim Cc: Riccardo Mancini Cc: Stephane Eranian Cc: Stephen Brennan Link: https://lore.kernel.org/lkml/20230407230405.2931830-3-irogers@google.com [ Split from a larger patch ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/lib/perf/cpumap.c | 2 +- tools/lib/perf/include/internal/cpumap.h | 1 + tools/perf/util/cpumap.c | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) (limited to 'tools/lib') diff --git a/tools/lib/perf/cpumap.c b/tools/lib/perf/cpumap.c index 6cd0be7c1bb4..0833423c243b 100644 --- a/tools/lib/perf/cpumap.c +++ b/tools/lib/perf/cpumap.c @@ -10,7 +10,7 @@ #include #include -static struct perf_cpu_map *perf_cpu_map__alloc(int nr_cpus) +struct perf_cpu_map *perf_cpu_map__alloc(int nr_cpus) { struct perf_cpu_map *cpus = malloc(sizeof(*cpus) + sizeof(struct perf_cpu) * nr_cpus); diff --git a/tools/lib/perf/include/internal/cpumap.h b/tools/lib/perf/include/internal/cpumap.h index 35dd29642296..f5bffb1f8674 100644 --- a/tools/lib/perf/include/internal/cpumap.h +++ b/tools/lib/perf/include/internal/cpumap.h @@ -24,6 +24,7 @@ struct perf_cpu_map { #define MAX_NR_CPUS 2048 #endif +struct perf_cpu_map *perf_cpu_map__alloc(int nr_cpus); int perf_cpu_map__idx(const struct perf_cpu_map *cpus, struct perf_cpu cpu); bool perf_cpu_map__is_subset(const struct perf_cpu_map *a, const struct perf_cpu_map *b); diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c index c8484b75413e..072831f0cad4 100644 --- a/tools/perf/util/cpumap.c +++ b/tools/perf/util/cpumap.c @@ -160,7 +160,7 @@ size_t cpu_map__fprintf(struct perf_cpu_map *map, FILE *fp) struct perf_cpu_map *perf_cpu_map__empty_new(int nr) { - struct perf_cpu_map *cpus = malloc(sizeof(*cpus) + sizeof(int) * nr); + struct perf_cpu_map *cpus = perf_cpu_map__alloc(nr); if (cpus != NULL) { int i; -- cgit v1.2.3 From b277851417e0149aff5e6986e1ad6e2d8054e4a6 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 12 Apr 2023 14:53:35 -0300 Subject: libperf: Add a perf_cpu_map__set_nr() available as an internal function for tools/perf to use We'll need to reference count check 'struct perf_cpu_map', so wrap accesses to its internal state to allow intercepting accesses to its instances. Cc: Adrian Hunter Cc: Alexey Bayduraev Cc: Dmitriy Vyukov Cc: Ian Rogers Cc: Jiri Olsa Cc: Namhyung Kim Cc: Riccardo Mancini Cc: Stephane Eranian Cc: Stephen Brennan Link: https://lore.kernel.org/ Signed-off-by: Arnaldo Carvalho de Melo --- tools/lib/perf/cpumap.c | 5 +++++ tools/lib/perf/include/internal/cpumap.h | 2 ++ 2 files changed, 7 insertions(+) (limited to 'tools/lib') diff --git a/tools/lib/perf/cpumap.c b/tools/lib/perf/cpumap.c index 0833423c243b..6bbcbb83eb14 100644 --- a/tools/lib/perf/cpumap.c +++ b/tools/lib/perf/cpumap.c @@ -10,6 +10,11 @@ #include #include +void perf_cpu_map__set_nr(struct perf_cpu_map *map, int nr_cpus) +{ + map->nr = nr_cpus; +} + struct perf_cpu_map *perf_cpu_map__alloc(int nr_cpus) { struct perf_cpu_map *cpus = malloc(sizeof(*cpus) + sizeof(struct perf_cpu) * nr_cpus); diff --git a/tools/lib/perf/include/internal/cpumap.h b/tools/lib/perf/include/internal/cpumap.h index f5bffb1f8674..b82fd6607a00 100644 --- a/tools/lib/perf/include/internal/cpumap.h +++ b/tools/lib/perf/include/internal/cpumap.h @@ -28,4 +28,6 @@ struct perf_cpu_map *perf_cpu_map__alloc(int nr_cpus); int perf_cpu_map__idx(const struct perf_cpu_map *cpus, struct perf_cpu cpu); bool perf_cpu_map__is_subset(const struct perf_cpu_map *a, const struct perf_cpu_map *b); +void perf_cpu_map__set_nr(struct perf_cpu_map *map, int nr_cpus); + #endif /* __LIBPERF_INTERNAL_CPUMAP_H */ -- cgit v1.2.3 From 4121234a32887a49d5da06e4c219f67abb6e744f Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 17 Apr 2023 12:47:49 -0300 Subject: libperf: Add perf_cpu_map__refcnt() interanl accessor to use in the maps test To remove one more direct access to 'struct perf_cpu_map' so that we can intercept accesses to its instantiations and refcount check it to catch use after free, etc. Cc: Adrian Hunter Cc: Alexey Bayduraev Cc: Dmitriy Vyukov Cc: Ian Rogers Cc: Jiri Olsa Cc: Namhyung Kim Cc: Riccardo Mancini Cc: Stephane Eranian Cc: Stephen Brennan Link: https://lore.kernel.org/lkml/ZD1qdYjG+DL6KOfP@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/lib/perf/cpumap.c | 6 +++--- tools/lib/perf/include/internal/cpumap.h | 4 ++++ tools/perf/tests/cpumap.c | 4 ++-- 3 files changed, 9 insertions(+), 5 deletions(-) (limited to 'tools/lib') diff --git a/tools/lib/perf/cpumap.c b/tools/lib/perf/cpumap.c index 6bbcbb83eb14..27c3e73c6db2 100644 --- a/tools/lib/perf/cpumap.c +++ b/tools/lib/perf/cpumap.c @@ -40,7 +40,7 @@ struct perf_cpu_map *perf_cpu_map__dummy_new(void) static void cpu_map__delete(struct perf_cpu_map *map) { if (map) { - WARN_ONCE(refcount_read(&map->refcnt) != 0, + WARN_ONCE(refcount_read(perf_cpu_map__refcnt(map)) != 0, "cpu_map refcnt unbalanced\n"); free(map); } @@ -49,13 +49,13 @@ static void cpu_map__delete(struct perf_cpu_map *map) struct perf_cpu_map *perf_cpu_map__get(struct perf_cpu_map *map) { if (map) - refcount_inc(&map->refcnt); + refcount_inc(perf_cpu_map__refcnt(map)); return map; } void perf_cpu_map__put(struct perf_cpu_map *map) { - if (map && refcount_dec_and_test(&map->refcnt)) + if (map && refcount_dec_and_test(perf_cpu_map__refcnt(map))) cpu_map__delete(map); } diff --git a/tools/lib/perf/include/internal/cpumap.h b/tools/lib/perf/include/internal/cpumap.h index b82fd6607a00..1e840dd53a11 100644 --- a/tools/lib/perf/include/internal/cpumap.h +++ b/tools/lib/perf/include/internal/cpumap.h @@ -30,4 +30,8 @@ bool perf_cpu_map__is_subset(const struct perf_cpu_map *a, const struct perf_cpu void perf_cpu_map__set_nr(struct perf_cpu_map *map, int nr_cpus); +static inline refcount_t *perf_cpu_map__refcnt(struct perf_cpu_map *map) +{ + return &map->refcnt; +} #endif /* __LIBPERF_INTERNAL_CPUMAP_H */ diff --git a/tools/perf/tests/cpumap.c b/tools/perf/tests/cpumap.c index 3150fc1fed6f..b1a924314e09 100644 --- a/tools/perf/tests/cpumap.c +++ b/tools/perf/tests/cpumap.c @@ -68,7 +68,7 @@ static int process_event_cpus(struct perf_tool *tool __maybe_unused, TEST_ASSERT_VAL("wrong nr", perf_cpu_map__nr(map) == 2); TEST_ASSERT_VAL("wrong cpu", perf_cpu_map__cpu(map, 0).cpu == 1); TEST_ASSERT_VAL("wrong cpu", perf_cpu_map__cpu(map, 1).cpu == 256); - TEST_ASSERT_VAL("wrong refcnt", refcount_read(&map->refcnt) == 1); + TEST_ASSERT_VAL("wrong refcnt", refcount_read(perf_cpu_map__refcnt(map)) == 1); perf_cpu_map__put(map); return 0; } @@ -94,7 +94,7 @@ static int process_event_range_cpus(struct perf_tool *tool __maybe_unused, TEST_ASSERT_VAL("wrong nr", perf_cpu_map__nr(map) == 256); TEST_ASSERT_VAL("wrong cpu", perf_cpu_map__cpu(map, 0).cpu == 1); TEST_ASSERT_VAL("wrong cpu", perf_cpu_map__max(map).cpu == 256); - TEST_ASSERT_VAL("wrong refcnt", refcount_read(&map->refcnt) == 1); + TEST_ASSERT_VAL("wrong refcnt", refcount_read(perf_cpu_map__refcnt(map)) == 1); perf_cpu_map__put(map); return 0; } -- cgit v1.2.3 From a9b867f68e89a54fcac334b77e6d243e872573c9 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Fri, 7 Apr 2023 16:04:01 -0700 Subject: libperf: Add reference count checking macros The macros serve as a way to debug use of a reference counted struct. The macros add a memory allocated pointer that is interposed between the reference counted original struct at a get and freed by a put. The pointer replaces the original struct, so use of the struct name via APIs remains unchanged. Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Alexey Bayduraev Cc: Andi Kleen Cc: Andrew Morton Cc: Andy Shevchenko Cc: Darren Hart Cc: Davidlohr Bueso Cc: Dmitriy Vyukov Cc: Eric Dumazet Cc: German Gomez Cc: Hao Luo Cc: James Clark Cc: Jiri Olsa Cc: John Garry Cc: Kajol Jain Cc: Kan Liang Cc: Leo Yan Cc: Madhavan Srinivasan Cc: Mark Rutland Cc: Masami Hiramatsu Cc: Miaoqian Lin Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Riccardo Mancini Cc: Shunsuke Nakamura Cc: Song Liu Cc: Stephane Eranian Cc: Stephen Brennan Cc: Steven Rostedt (VMware) Cc: Thomas Gleixner Cc: Thomas Richter Cc: Yury Norov Link: http://lore.kernel.org/lkml/20230407230405.2931830-2-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/lib/perf/include/internal/rc_check.h | 94 ++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 tools/lib/perf/include/internal/rc_check.h (limited to 'tools/lib') diff --git a/tools/lib/perf/include/internal/rc_check.h b/tools/lib/perf/include/internal/rc_check.h new file mode 100644 index 000000000000..c0626d8beb59 --- /dev/null +++ b/tools/lib/perf/include/internal/rc_check.h @@ -0,0 +1,94 @@ +/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ +#ifndef __LIBPERF_INTERNAL_RC_CHECK_H +#define __LIBPERF_INTERNAL_RC_CHECK_H + +#include +#include + +/* + * Shared reference count checking macros. + * + * Reference count checking is an approach to sanitizing the use of reference + * counted structs. It leverages address and leak sanitizers to make sure gets + * are paired with a put. Reference count checking adds a malloc-ed layer of + * indirection on a get, and frees it on a put. A missed put will be reported as + * a memory leak. A double put will be reported as a double free. Accessing + * after a put will cause a use-after-free and/or a segfault. + */ + +#ifndef REFCNT_CHECKING +/* Replaces "struct foo" so that the pointer may be interposed. */ +#define DECLARE_RC_STRUCT(struct_name) \ + struct struct_name + +/* Declare a reference counted struct variable. */ +#define RC_STRUCT(struct_name) struct struct_name + +/* + * Interpose the indirection. Result will hold the indirection and object is the + * reference counted struct. + */ +#define ADD_RC_CHK(result, object) (result = object, object) + +/* Strip the indirection layer. */ +#define RC_CHK_ACCESS(object) object + +/* Frees the object and the indirection layer. */ +#define RC_CHK_FREE(object) free(object) + +/* A get operation adding the indirection layer. */ +#define RC_CHK_GET(result, object) ADD_RC_CHK(result, object) + +/* A put operation removing the indirection layer. */ +#define RC_CHK_PUT(object) {} + +#else + +/* Replaces "struct foo" so that the pointer may be interposed. */ +#define DECLARE_RC_STRUCT(struct_name) \ + struct original_##struct_name; \ + struct struct_name { \ + struct original_##struct_name *orig; \ + }; \ + struct original_##struct_name + +/* Declare a reference counted struct variable. */ +#define RC_STRUCT(struct_name) struct original_##struct_name + +/* + * Interpose the indirection. Result will hold the indirection and object is the + * reference counted struct. + */ +#define ADD_RC_CHK(result, object) \ + ( \ + object ? (result = malloc(sizeof(*result)), \ + result ? (result->orig = object, result) \ + : (result = NULL, NULL)) \ + : (result = NULL, NULL) \ + ) + +/* Strip the indirection layer. */ +#define RC_CHK_ACCESS(object) object->orig + +/* Frees the object and the indirection layer. */ +#define RC_CHK_FREE(object) \ + do { \ + zfree(&object->orig); \ + free(object); \ + } while(0) + +/* A get operation adding the indirection layer. */ +#define RC_CHK_GET(result, object) ADD_RC_CHK(result, (object ? object->orig : NULL)) + +/* A put operation removing the indirection layer. */ +#define RC_CHK_PUT(object) \ + do { \ + if (object) { \ + object->orig = NULL; \ + free(object); \ + } \ + } while(0) + +#endif + +#endif /* __LIBPERF_INTERNAL_RC_CHECK_H */ -- cgit v1.2.3 From da885a0e5e0610e011f14a70c2dc7c4ddf79c6d6 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 17 Apr 2023 16:50:02 -0300 Subject: perf cpumap: Add reference count checking Enabled when REFCNT_CHECKING is defined. The change adds a memory allocated pointer that is interposed between the reference counted cpu map at a get and freed by a put. The pointer replaces the original perf_cpu_map struct, so use of the perf_cpu_map via APIs remains unchanged. Any use of the cpu map without the API requires two versions, handled via the RC_CHK_ACCESS macro. This change is intended to catch: - use after put: using a cpumap after you have put it will cause a segv. - unbalanced puts: two puts for a get will result in a double free that can be captured and reported by tools like address sanitizer, including with the associated stack traces of allocation and frees. - missing puts: if a put is missing then the get turns into a memory leak that can be reported by leak sanitizer, including the stack trace at the point the get occurs. Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Alexey Bayduraev Cc: Andi Kleen Cc: Andrew Morton Cc: Andy Shevchenko Cc: Darren Hart Cc: Davidlohr Bueso Cc: Dmitriy Vyukov Cc: Eric Dumazet Cc: German Gomez Cc: Hao Luo Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: John Garry Cc: Kajol Jain Cc: Kan Liang Cc: Leo Yan Cc: Madhavan Srinivasan Cc: Mark Rutland Cc: Masami Hiramatsu Cc: Miaoqian Lin Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Riccardo Mancini Cc: Shunsuke Nakamura Cc: Song Liu Cc: Stephen Brennan Cc: Steven Rostedt (VMware) Cc: Thomas Gleixner Cc: Thomas Richter , Cc: Yury Norov Link: https://lore.kernel.org/lkml/20230407230405.2931830-3-irogers@google.com [ Extracted from a larger patch ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/lib/perf/Makefile | 2 +- tools/lib/perf/cpumap.c | 85 +++++++++++++++++--------------- tools/lib/perf/include/internal/cpumap.h | 5 +- tools/perf/util/cpumap.c | 12 ++--- tools/perf/util/pmu.c | 4 +- 5 files changed, 58 insertions(+), 50 deletions(-) (limited to 'tools/lib') diff --git a/tools/lib/perf/Makefile b/tools/lib/perf/Makefile index d8cad124e4c5..3a9b2140aa04 100644 --- a/tools/lib/perf/Makefile +++ b/tools/lib/perf/Makefile @@ -188,7 +188,7 @@ install_lib: libs cp -fpR $(LIBPERF_ALL) $(DESTDIR)$(libdir_SQ) HDRS := bpf_perf.h core.h cpumap.h threadmap.h evlist.h evsel.h event.h mmap.h -INTERNAL_HDRS := cpumap.h evlist.h evsel.h lib.h mmap.h threadmap.h xyarray.h +INTERNAL_HDRS := cpumap.h evlist.h evsel.h lib.h mmap.h rc_check.h threadmap.h xyarray.h INSTALL_HDRS_PFX := $(DESTDIR)$(prefix)/include/perf INSTALL_HDRS := $(addprefix $(INSTALL_HDRS_PFX)/, $(HDRS)) diff --git a/tools/lib/perf/cpumap.c b/tools/lib/perf/cpumap.c index 27c3e73c6db2..1229b18bcdb1 100644 --- a/tools/lib/perf/cpumap.c +++ b/tools/lib/perf/cpumap.c @@ -12,19 +12,19 @@ void perf_cpu_map__set_nr(struct perf_cpu_map *map, int nr_cpus) { - map->nr = nr_cpus; + RC_CHK_ACCESS(map)->nr = nr_cpus; } struct perf_cpu_map *perf_cpu_map__alloc(int nr_cpus) { - struct perf_cpu_map *cpus = malloc(sizeof(*cpus) + sizeof(struct perf_cpu) * nr_cpus); + RC_STRUCT(perf_cpu_map) *cpus = malloc(sizeof(*cpus) + sizeof(struct perf_cpu) * nr_cpus); + struct perf_cpu_map *result; - if (cpus != NULL) { + if (ADD_RC_CHK(result, cpus)) { cpus->nr = nr_cpus; refcount_set(&cpus->refcnt, 1); - } - return cpus; + return result; } struct perf_cpu_map *perf_cpu_map__dummy_new(void) @@ -32,7 +32,7 @@ struct perf_cpu_map *perf_cpu_map__dummy_new(void) struct perf_cpu_map *cpus = perf_cpu_map__alloc(1); if (cpus) - cpus->map[0].cpu = -1; + RC_CHK_ACCESS(cpus)->map[0].cpu = -1; return cpus; } @@ -42,21 +42,28 @@ static void cpu_map__delete(struct perf_cpu_map *map) if (map) { WARN_ONCE(refcount_read(perf_cpu_map__refcnt(map)) != 0, "cpu_map refcnt unbalanced\n"); - free(map); + RC_CHK_FREE(map); } } struct perf_cpu_map *perf_cpu_map__get(struct perf_cpu_map *map) { - if (map) + struct perf_cpu_map *result; + + if (RC_CHK_GET(result, map)) refcount_inc(perf_cpu_map__refcnt(map)); - return map; + + return result; } void perf_cpu_map__put(struct perf_cpu_map *map) { - if (map && refcount_dec_and_test(perf_cpu_map__refcnt(map))) - cpu_map__delete(map); + if (map) { + if (refcount_dec_and_test(perf_cpu_map__refcnt(map))) + cpu_map__delete(map); + else + RC_CHK_PUT(map); + } } static struct perf_cpu_map *cpu_map__default_new(void) @@ -73,7 +80,7 @@ static struct perf_cpu_map *cpu_map__default_new(void) int i; for (i = 0; i < nr_cpus; ++i) - cpus->map[i].cpu = i; + RC_CHK_ACCESS(cpus)->map[i].cpu = i; } return cpus; @@ -99,15 +106,15 @@ static struct perf_cpu_map *cpu_map__trim_new(int nr_cpus, const struct perf_cpu int i, j; if (cpus != NULL) { - memcpy(cpus->map, tmp_cpus, payload_size); - qsort(cpus->map, nr_cpus, sizeof(struct perf_cpu), cmp_cpu); + memcpy(RC_CHK_ACCESS(cpus)->map, tmp_cpus, payload_size); + qsort(RC_CHK_ACCESS(cpus)->map, nr_cpus, sizeof(struct perf_cpu), cmp_cpu); /* Remove dups */ j = 0; for (i = 0; i < nr_cpus; i++) { - if (i == 0 || cpus->map[i].cpu != cpus->map[i - 1].cpu) - cpus->map[j++].cpu = cpus->map[i].cpu; + if (i == 0 || RC_CHK_ACCESS(cpus)->map[i].cpu != RC_CHK_ACCESS(cpus)->map[i - 1].cpu) + RC_CHK_ACCESS(cpus)->map[j++].cpu = RC_CHK_ACCESS(cpus)->map[i].cpu; } - cpus->nr = j; + perf_cpu_map__set_nr(cpus, j); assert(j <= nr_cpus); } return cpus; @@ -268,20 +275,20 @@ struct perf_cpu perf_cpu_map__cpu(const struct perf_cpu_map *cpus, int idx) .cpu = -1 }; - if (cpus && idx < cpus->nr) - return cpus->map[idx]; + if (cpus && idx < RC_CHK_ACCESS(cpus)->nr) + return RC_CHK_ACCESS(cpus)->map[idx]; return result; } int perf_cpu_map__nr(const struct perf_cpu_map *cpus) { - return cpus ? cpus->nr : 1; + return cpus ? RC_CHK_ACCESS(cpus)->nr : 1; } bool perf_cpu_map__empty(const struct perf_cpu_map *map) { - return map ? map->map[0].cpu == -1 : true; + return map ? RC_CHK_ACCESS(map)->map[0].cpu == -1 : true; } int perf_cpu_map__idx(const struct perf_cpu_map *cpus, struct perf_cpu cpu) @@ -292,10 +299,10 @@ int perf_cpu_map__idx(const struct perf_cpu_map *cpus, struct perf_cpu cpu) return -1; low = 0; - high = cpus->nr; + high = RC_CHK_ACCESS(cpus)->nr; while (low < high) { int idx = (low + high) / 2; - struct perf_cpu cpu_at_idx = cpus->map[idx]; + struct perf_cpu cpu_at_idx = RC_CHK_ACCESS(cpus)->map[idx]; if (cpu_at_idx.cpu == cpu.cpu) return idx; @@ -321,7 +328,7 @@ struct perf_cpu perf_cpu_map__max(const struct perf_cpu_map *map) }; // cpu_map__trim_new() qsort()s it, cpu_map__default_new() sorts it as well. - return map->nr > 0 ? map->map[map->nr - 1] : result; + return RC_CHK_ACCESS(map)->nr > 0 ? RC_CHK_ACCESS(map)->map[RC_CHK_ACCESS(map)->nr - 1] : result; } /** Is 'b' a subset of 'a'. */ @@ -329,15 +336,15 @@ bool perf_cpu_map__is_subset(const struct perf_cpu_map *a, const struct perf_cpu { if (a == b || !b) return true; - if (!a || b->nr > a->nr) + if (!a || RC_CHK_ACCESS(b)->nr > RC_CHK_ACCESS(a)->nr) return false; - for (int i = 0, j = 0; i < a->nr; i++) { - if (a->map[i].cpu > b->map[j].cpu) + for (int i = 0, j = 0; i < RC_CHK_ACCESS(a)->nr; i++) { + if (RC_CHK_ACCESS(a)->map[i].cpu > RC_CHK_ACCESS(b)->map[j].cpu) return false; - if (a->map[i].cpu == b->map[j].cpu) { + if (RC_CHK_ACCESS(a)->map[i].cpu == RC_CHK_ACCESS(b)->map[j].cpu) { j++; - if (j == b->nr) + if (j == RC_CHK_ACCESS(b)->nr) return true; } } @@ -367,27 +374,27 @@ struct perf_cpu_map *perf_cpu_map__merge(struct perf_cpu_map *orig, return perf_cpu_map__get(other); } - tmp_len = orig->nr + other->nr; + tmp_len = RC_CHK_ACCESS(orig)->nr + RC_CHK_ACCESS(other)->nr; tmp_cpus = malloc(tmp_len * sizeof(struct perf_cpu)); if (!tmp_cpus) return NULL; /* Standard merge algorithm from wikipedia */ i = j = k = 0; - while (i < orig->nr && j < other->nr) { - if (orig->map[i].cpu <= other->map[j].cpu) { - if (orig->map[i].cpu == other->map[j].cpu) + while (i < RC_CHK_ACCESS(orig)->nr && j < RC_CHK_ACCESS(other)->nr) { + if (RC_CHK_ACCESS(orig)->map[i].cpu <= RC_CHK_ACCESS(other)->map[j].cpu) { + if (RC_CHK_ACCESS(orig)->map[i].cpu == RC_CHK_ACCESS(other)->map[j].cpu) j++; - tmp_cpus[k++] = orig->map[i++]; + tmp_cpus[k++] = RC_CHK_ACCESS(orig)->map[i++]; } else - tmp_cpus[k++] = other->map[j++]; + tmp_cpus[k++] = RC_CHK_ACCESS(other)->map[j++]; } - while (i < orig->nr) - tmp_cpus[k++] = orig->map[i++]; + while (i < RC_CHK_ACCESS(orig)->nr) + tmp_cpus[k++] = RC_CHK_ACCESS(orig)->map[i++]; - while (j < other->nr) - tmp_cpus[k++] = other->map[j++]; + while (j < RC_CHK_ACCESS(other)->nr) + tmp_cpus[k++] = RC_CHK_ACCESS(other)->map[j++]; assert(k <= tmp_len); merged = cpu_map__trim_new(k, tmp_cpus); diff --git a/tools/lib/perf/include/internal/cpumap.h b/tools/lib/perf/include/internal/cpumap.h index 1e840dd53a11..49649eb51ce4 100644 --- a/tools/lib/perf/include/internal/cpumap.h +++ b/tools/lib/perf/include/internal/cpumap.h @@ -4,6 +4,7 @@ #include #include +#include /** * A sized, reference counted, sorted array of integers representing CPU @@ -12,7 +13,7 @@ * gaps if CPU numbers were used. For events associated with a pid, rather than * a CPU, a single dummy map with an entry of -1 is used. */ -struct perf_cpu_map { +DECLARE_RC_STRUCT(perf_cpu_map) { refcount_t refcnt; /** Length of the map array. */ int nr; @@ -32,6 +33,6 @@ void perf_cpu_map__set_nr(struct perf_cpu_map *map, int nr_cpus); static inline refcount_t *perf_cpu_map__refcnt(struct perf_cpu_map *map) { - return &map->refcnt; + return &RC_CHK_ACCESS(map)->refcnt; } #endif /* __LIBPERF_INTERNAL_CPUMAP_H */ diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c index 240557de129e..75d9c73e0184 100644 --- a/tools/perf/util/cpumap.c +++ b/tools/perf/util/cpumap.c @@ -77,9 +77,9 @@ static struct perf_cpu_map *cpu_map__from_entries(const struct perf_record_cpu_m * otherwise it would become 65535. */ if (data->cpus_data.cpu[i] == (u16) -1) - map->map[i].cpu = -1; + RC_CHK_ACCESS(map)->map[i].cpu = -1; else - map->map[i].cpu = (int) data->cpus_data.cpu[i]; + RC_CHK_ACCESS(map)->map[i].cpu = (int) data->cpus_data.cpu[i]; } } @@ -107,7 +107,7 @@ static struct perf_cpu_map *cpu_map__from_mask(const struct perf_record_cpu_map_ perf_record_cpu_map_data__read_one_mask(data, i, local_copy); for_each_set_bit(cpu, local_copy, 64) - map->map[j++].cpu = cpu + cpus_per_i; + RC_CHK_ACCESS(map)->map[j++].cpu = cpu + cpus_per_i; } return map; @@ -124,11 +124,11 @@ static struct perf_cpu_map *cpu_map__from_range(const struct perf_record_cpu_map return NULL; if (data->range_cpu_data.any_cpu) - map->map[i++].cpu = -1; + RC_CHK_ACCESS(map)->map[i++].cpu = -1; for (int cpu = data->range_cpu_data.start_cpu; cpu <= data->range_cpu_data.end_cpu; i++, cpu++) - map->map[i].cpu = cpu; + RC_CHK_ACCESS(map)->map[i].cpu = cpu; return map; } @@ -164,7 +164,7 @@ struct perf_cpu_map *perf_cpu_map__empty_new(int nr) if (cpus != NULL) { for (int i = 0; i < nr; i++) - cpus->map[i].cpu = -1; + RC_CHK_ACCESS(cpus)->map[i].cpu = -1; } return cpus; diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index 760c848c9fa2..65d0eb9a740a 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c @@ -2015,9 +2015,9 @@ int perf_pmu__cpus_match(struct perf_pmu *pmu, struct perf_cpu_map *cpus, perf_cpu_map__for_each_cpu(cpu, i, cpus) { if (!perf_cpu_map__has(pmu_cpus, cpu)) - unmatched_cpus->map[unmatched_nr++] = cpu; + RC_CHK_ACCESS(unmatched_cpus)->map[unmatched_nr++] = cpu; else - matched_cpus->map[matched_nr++] = cpu; + RC_CHK_ACCESS(matched_cpus)->map[matched_nr++] = cpu; } perf_cpu_map__set_nr(unmatched_cpus, unmatched_nr); -- cgit v1.2.3 From 9be6ab181b7b8d6114e225ae3c7c01bb6380cf8a Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 20 Apr 2023 10:18:12 -0700 Subject: libperf rc_check: Enable implicitly with sanitizers If using leak sanitizer then implicitly enable reference count checking. Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ingo Molnar Cc: Jiri Olsa Cc: Mark Rutland Cc: Namhyung Kim Cc: Peter Zijlstra Link: https://lore.kernel.org/r/20230420171812.561603-1-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/lib/perf/include/internal/rc_check.h | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'tools/lib') diff --git a/tools/lib/perf/include/internal/rc_check.h b/tools/lib/perf/include/internal/rc_check.h index c0626d8beb59..d5d771ccdc7b 100644 --- a/tools/lib/perf/include/internal/rc_check.h +++ b/tools/lib/perf/include/internal/rc_check.h @@ -5,6 +5,14 @@ #include #include +/* + * Enable reference count checking implicitly with leak checking, which is + * integrated into address sanitizer. + */ +#if defined(LEAK_SANITIZER) || defined(ADDRESS_SANITIZER) +#define REFCNT_CHECKING 1 +#endif + /* * Shared reference count checking macros. * -- cgit v1.2.3