diff --git a/tools/perf/Documentation/perf-kallsyms.txt b/tools/perf/Documentation/perf-kallsyms.txt index 954ea9e21236dd6b96d6a1a5330ed410ba278ae0..cf9f4040ea5c7ae1b5890d973efa53592efbe56a 100644 --- a/tools/perf/Documentation/perf-kallsyms.txt +++ b/tools/perf/Documentation/perf-kallsyms.txt @@ -8,7 +8,7 @@ perf-kallsyms - Searches running kernel for symbols SYNOPSIS -------- [verse] -'perf kallsyms symbol_name[,symbol_name...]' +'perf kallsyms' [] symbol_name[,symbol_name...] DESCRIPTION ----------- diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt index 3eea6de35a38dd34593868e54dc2896a6c18f8cd..cc37b3a4be76917ba4e2ea3982836511b9a71218 100644 --- a/tools/perf/Documentation/perf-record.txt +++ b/tools/perf/Documentation/perf-record.txt @@ -191,9 +191,16 @@ OPTIONS -i:: --no-inherit:: Child tasks do not inherit counters. + -F:: --freq=:: - Profile at this frequency. + Profile at this frequency. Use 'max' to use the currently maximum + allowed frequency, i.e. the value in the kernel.perf_event_max_sample_rate + sysctl. Will throttle down to the currently maximum allowed frequency. + See --strict-freq. + +--strict-freq:: + Fail if the specified frequency can't be used. -m:: --mmap-pages=:: @@ -308,7 +315,11 @@ can be provided. Each cgroup is applied to the corresponding event, i.e., first to first event, second cgroup to second event and so on. It is possible to provide an empty cgroup (monitor all the time) using, e.g., -G foo,,bar. Cgroups must have corresponding events, i.e., they always refer to events defined earlier on the command -line. +line. If the user wants to track multiple events for a specific cgroup, the user can +use '-e e1 -e e2 -G foo,foo' or just use '-e e1 -e e2 -G foo'. + +If wanting to monitor, say, 'cycles' for a cgroup and also for system wide, this +command line can be used: 'perf stat -e cycles -G cgroup_name -a -e cycles'. -b:: --branch-any:: diff --git a/tools/perf/Documentation/perf-stat.txt b/tools/perf/Documentation/perf-stat.txt index 2bbe79a50d3c6462d545316e30d51c381b44a834..2b38e222016af251c7872fb6335a7303dc863d2e 100644 --- a/tools/perf/Documentation/perf-stat.txt +++ b/tools/perf/Documentation/perf-stat.txt @@ -118,7 +118,11 @@ can be provided. Each cgroup is applied to the corresponding event, i.e., first to first event, second cgroup to second event and so on. It is possible to provide an empty cgroup (monitor all the time) using, e.g., -G foo,,bar. Cgroups must have corresponding events, i.e., they always refer to events defined earlier on the command -line. +line. If the user wants to track multiple events for a specific cgroup, the user can +use '-e e1 -e e2 -G foo,foo' or just use '-e e1 -e e2 -G foo'. + +If wanting to monitor, say, 'cycles' for a cgroup and also for system wide, this +command line can be used: 'perf stat -e cycles -G cgroup_name -a -e cycles'. -o file:: --output file:: diff --git a/tools/perf/Documentation/perf-top.txt b/tools/perf/Documentation/perf-top.txt index 8a32cc77bead37463b8d2517bf3d9a5c42ce8673..a039407d63b80d7dff62c188be47fd607971e2bc 100644 --- a/tools/perf/Documentation/perf-top.txt +++ b/tools/perf/Documentation/perf-top.txt @@ -55,7 +55,9 @@ Default is to monitor all CPUS. -F :: --freq=:: - Profile at this frequency. + Profile at this frequency. Use 'max' to use the currently maximum + allowed frequency, i.e. the value in the kernel.perf_event_max_sample_rate + sysctl. -i:: --inherit:: diff --git a/tools/perf/arch/x86/tests/perf-time-to-tsc.c b/tools/perf/arch/x86/tests/perf-time-to-tsc.c index 06abe8108b33c5397337312b99e730c124ec929c..7f82d91ef473e82029c2c0ffe825b54db59cbe0f 100644 --- a/tools/perf/arch/x86/tests/perf-time-to-tsc.c +++ b/tools/perf/arch/x86/tests/perf-time-to-tsc.c @@ -60,6 +60,8 @@ int test__perf_time_to_tsc(struct test *test __maybe_unused, int subtest __maybe union perf_event *event; u64 test_tsc, comm1_tsc, comm2_tsc; u64 test_time, comm1_time = 0, comm2_time = 0; + struct perf_mmap *md; + u64 end, start; threads = thread_map__new(-1, getpid(), UINT_MAX); CHECK_NOT_NULL__(threads); @@ -109,7 +111,11 @@ int test__perf_time_to_tsc(struct test *test __maybe_unused, int subtest __maybe perf_evlist__disable(evlist); for (i = 0; i < evlist->nr_mmaps; i++) { - while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) { + md = &evlist->mmap[i]; + if (perf_mmap__read_init(md, false, &start, &end) < 0) + continue; + + while ((event = perf_mmap__read_event(md, false, &start, end)) != NULL) { struct perf_sample sample; if (event->header.type != PERF_RECORD_COMM || @@ -128,8 +134,9 @@ int test__perf_time_to_tsc(struct test *test __maybe_unused, int subtest __maybe comm2_time = sample.time; } next_event: - perf_evlist__mmap_consume(evlist, i); + perf_mmap__consume(md, false); } + perf_mmap__read_done(md); } if (!comm1_time || !comm2_time) diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c index 55d919dc5bc632b4a967dde8a5dbfdacf5da3f63..d2703d3b83666cac140eefe184a406160681e78a 100644 --- a/tools/perf/builtin-kvm.c +++ b/tools/perf/builtin-kvm.c @@ -743,16 +743,24 @@ static bool verify_vcpu(int vcpu) static s64 perf_kvm__mmap_read_idx(struct perf_kvm_stat *kvm, int idx, u64 *mmap_time) { + struct perf_evlist *evlist = kvm->evlist; union perf_event *event; + struct perf_mmap *md; + u64 end, start; u64 timestamp; s64 n = 0; int err; *mmap_time = ULLONG_MAX; - while ((event = perf_evlist__mmap_read(kvm->evlist, idx)) != NULL) { - err = perf_evlist__parse_sample_timestamp(kvm->evlist, event, ×tamp); + md = &evlist->mmap[idx]; + err = perf_mmap__read_init(md, false, &start, &end); + if (err < 0) + return (err == -EAGAIN) ? 0 : -1; + + while ((event = perf_mmap__read_event(md, false, &start, end)) != NULL) { + err = perf_evlist__parse_sample_timestamp(evlist, event, ×tamp); if (err) { - perf_evlist__mmap_consume(kvm->evlist, idx); + perf_mmap__consume(md, false); pr_err("Failed to parse sample\n"); return -1; } @@ -762,7 +770,7 @@ static s64 perf_kvm__mmap_read_idx(struct perf_kvm_stat *kvm, int idx, * FIXME: Here we can't consume the event, as perf_session__queue_event will * point to it, and it'll get possibly overwritten by the kernel. */ - perf_evlist__mmap_consume(kvm->evlist, idx); + perf_mmap__consume(md, false); if (err) { pr_err("Failed to enqueue sample: %d\n", err); @@ -779,6 +787,7 @@ static s64 perf_kvm__mmap_read_idx(struct perf_kvm_stat *kvm, int idx, break; } + perf_mmap__read_done(md); return n; } diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 90726720697315bf78d450c6444f93d9753daa47..12230ddb650690e5c434a20527f5022897b909d7 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -45,6 +45,7 @@ #include #include +#include #include #include #include @@ -881,6 +882,15 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) } } + /* + * If we have just single event and are sending data + * through pipe, we need to force the ids allocation, + * because we synthesize event name through the pipe + * and need the id for that. + */ + if (data->is_pipe && rec->evlist->nr_entries == 1) + rec->opts.sample_id = true; + if (record__open(rec) != 0) { err = -1; goto out_child; @@ -1542,7 +1552,11 @@ static struct option __record_options[] = { OPT_BOOLEAN(0, "tail-synthesize", &record.opts.tail_synthesize, "synthesize non-sample events at the end of output"), OPT_BOOLEAN(0, "overwrite", &record.opts.overwrite, "use overwrite mode"), - OPT_UINTEGER('F', "freq", &record.opts.user_freq, "profile at this frequency"), + OPT_BOOLEAN(0, "strict-freq", &record.opts.strict_freq, + "Fail if the specified frequency can't be used"), + OPT_CALLBACK('F', "freq", &record.opts, "freq or 'max'", + "profile at this frequency", + record__parse_freq), OPT_CALLBACK('m', "mmap-pages", &record.opts, "pages[,pages]", "number of mmap data pages and AUX area tracing mmap pages", record__parse_mmap_pages), @@ -1651,6 +1665,8 @@ int cmd_record(int argc, const char **argv) struct record *rec = &record; char errbuf[BUFSIZ]; + setlocale(LC_ALL, ""); + #ifndef HAVE_LIBBPF_SUPPORT # define set_nobuild(s, l, c) set_option_nobuild(record_options, s, l, "NO_LIBBPF=1", c) set_nobuild('\0', "clang-path", true); diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 2d49eccf98f2776f536e0141b5a4a00c39f1d7a7..6214d2b220b29db937e8f2faf8082d243cb1afe2 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -508,14 +508,13 @@ static int perf_stat_synthesize_config(bool is_pipe) #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) -static int __store_counter_ids(struct perf_evsel *counter, - struct cpu_map *cpus, - struct thread_map *threads) +static int __store_counter_ids(struct perf_evsel *counter) { int cpu, thread; - for (cpu = 0; cpu < cpus->nr; cpu++) { - for (thread = 0; thread < threads->nr; thread++) { + for (cpu = 0; cpu < xyarray__max_x(counter->fd); cpu++) { + for (thread = 0; thread < xyarray__max_y(counter->fd); + thread++) { int fd = FD(counter, cpu, thread); if (perf_evlist__id_add_fd(evsel_list, counter, @@ -535,7 +534,7 @@ static int store_counter_ids(struct perf_evsel *counter) if (perf_evsel__alloc_id(counter, cpus->nr, threads->nr)) return -ENOMEM; - return __store_counter_ids(counter, cpus, threads); + return __store_counter_ids(counter); } static bool perf_evsel__should_store_id(struct perf_evsel *counter) @@ -638,7 +637,19 @@ static int __run_perf_stat(int argc, const char **argv) if (verbose > 0) ui__warning("%s\n", msg); goto try_again; - } + } else if (target__has_per_thread(&target) && + evsel_list->threads && + evsel_list->threads->err_thread != -1) { + /* + * For global --per-thread case, skip current + * error thread. + */ + if (!thread_map__remove(evsel_list->threads, + evsel_list->threads->err_thread)) { + evsel_list->threads->err_thread = -1; + goto try_again; + } + } perf_evsel__open_strerror(counter, &target, errno, msg, sizeof(msg)); diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index b7c823ba8374fb702b4ae3d3033e79b852e4686b..bb4f9fafd11d8304491af1fa7effe807428e5111 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -991,7 +991,7 @@ static int perf_top_overwrite_fallback(struct perf_top *top, evlist__for_each_entry(evlist, counter) counter->attr.write_backward = false; opts->overwrite = false; - ui__warning("fall back to non-overwrite mode\n"); + pr_debug2("fall back to non-overwrite mode\n"); return 1; } @@ -1307,7 +1307,9 @@ int cmd_top(int argc, const char **argv) OPT_STRING(0, "sym-annotate", &top.sym_filter, "symbol name", "symbol to annotate"), OPT_BOOLEAN('z', "zero", &top.zero, "zero history across updates"), - OPT_UINTEGER('F', "freq", &opts->user_freq, "profile at this frequency"), + OPT_CALLBACK('F', "freq", &top.record_opts, "freq or 'max'", + "profile at this frequency", + record__parse_freq), OPT_INTEGER('E', "entries", &top.print_entries, "display this many functions"), OPT_BOOLEAN('U', "hide_user_symbols", &top.hide_user_symbols, diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index e7f1b182fc153f227f8541e485c47a27115a111e..1a93debc1e8d55987a994edf3f8fd1650688b548 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -2472,8 +2472,14 @@ static int trace__run(struct trace *trace, int argc, const char **argv) for (i = 0; i < evlist->nr_mmaps; i++) { union perf_event *event; + struct perf_mmap *md; + u64 end, start; - while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) { + md = &evlist->mmap[i]; + if (perf_mmap__read_init(md, false, &start, &end) < 0) + continue; + + while ((event = perf_mmap__read_event(md, false, &start, end)) != NULL) { struct perf_sample sample; ++trace->nr_events; @@ -2486,7 +2492,7 @@ static int trace__run(struct trace *trace, int argc, const char **argv) trace__handle_event(trace, event, &sample); next_event: - perf_evlist__mmap_consume(evlist, i); + perf_mmap__consume(md, false); if (interrupted) goto out_disable; @@ -2496,6 +2502,7 @@ static int trace__run(struct trace *trace, int argc, const char **argv) draining = true; } } + perf_mmap__read_done(md); } if (trace->nr_events == before) { diff --git a/tools/perf/perf.h b/tools/perf/perf.h index cfe46236a5e5b7d641523e3eb25a3d0acc11eaa4..8fec1abd0f1fd55f833f4eea04c100dcbadda641 100644 --- a/tools/perf/perf.h +++ b/tools/perf/perf.h @@ -61,6 +61,8 @@ struct record_opts { bool tail_synthesize; bool overwrite; bool ignore_missing_thread; + bool strict_freq; + bool sample_id; unsigned int freq; unsigned int mmap_pages; unsigned int auxtrace_mmap_pages; @@ -82,4 +84,6 @@ struct record_opts { struct option; extern const char * const *record_usage; extern struct option *record_options; + +int record__parse_freq(const struct option *opt, const char *str, int unset); #endif diff --git a/tools/perf/tests/bpf.c b/tools/perf/tests/bpf.c index e8399beca62b32ff963f5e6828df85e2cb7d6100..09c9c9f9e827124aec20f98c15fdaa05f0c1d4e5 100644 --- a/tools/perf/tests/bpf.c +++ b/tools/perf/tests/bpf.c @@ -176,13 +176,20 @@ static int do_test(struct bpf_object *obj, int (*func)(void), for (i = 0; i < evlist->nr_mmaps; i++) { union perf_event *event; + struct perf_mmap *md; + u64 end, start; - while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) { + md = &evlist->mmap[i]; + if (perf_mmap__read_init(md, false, &start, &end) < 0) + continue; + + while ((event = perf_mmap__read_event(md, false, &start, end)) != NULL) { const u32 type = event->header.type; if (type == PERF_RECORD_SAMPLE) count ++; } + perf_mmap__read_done(md); } if (count != expect) { diff --git a/tools/perf/tests/code-reading.c b/tools/perf/tests/code-reading.c index c7115d36951108ba38b072c2fedaccd936409945..03ed8c77b1bb69d11aea6a3e87af8d0f7dea5332 100644 --- a/tools/perf/tests/code-reading.c +++ b/tools/perf/tests/code-reading.c @@ -409,15 +409,22 @@ static int process_events(struct machine *machine, struct perf_evlist *evlist, struct state *state) { union perf_event *event; + struct perf_mmap *md; + u64 end, start; int i, ret; for (i = 0; i < evlist->nr_mmaps; i++) { - while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) { + md = &evlist->mmap[i]; + if (perf_mmap__read_init(md, false, &start, &end) < 0) + continue; + + while ((event = perf_mmap__read_event(md, false, &start, end)) != NULL) { ret = process_event(machine, evlist, event, state); - perf_evlist__mmap_consume(evlist, i); + perf_mmap__consume(md, false); if (ret < 0) return ret; } + perf_mmap__read_done(md); } return 0; } diff --git a/tools/perf/tests/keep-tracking.c b/tools/perf/tests/keep-tracking.c index c465309189384a0c2b999879ff7323156a54b91c..4590d8fb91ab86c4f8beceadb945e4d47355ec04 100644 --- a/tools/perf/tests/keep-tracking.c +++ b/tools/perf/tests/keep-tracking.c @@ -27,18 +27,24 @@ static int find_comm(struct perf_evlist *evlist, const char *comm) { union perf_event *event; + struct perf_mmap *md; + u64 end, start; int i, found; found = 0; for (i = 0; i < evlist->nr_mmaps; i++) { - while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) { + md = &evlist->mmap[i]; + if (perf_mmap__read_init(md, false, &start, &end) < 0) + continue; + while ((event = perf_mmap__read_event(md, false, &start, end)) != NULL) { if (event->header.type == PERF_RECORD_COMM && (pid_t)event->comm.pid == getpid() && (pid_t)event->comm.tid == getpid() && strcmp(event->comm.comm, comm) == 0) found += 1; - perf_evlist__mmap_consume(evlist, i); + perf_mmap__consume(md, false); } + perf_mmap__read_done(md); } return found; } diff --git a/tools/perf/tests/mmap-basic.c b/tools/perf/tests/mmap-basic.c index c0e971da965cc9bb6f7d816996bc06c87b85dc05..44c58d69cd87e4e54a2b584aa63e6ea6fe760061 100644 --- a/tools/perf/tests/mmap-basic.c +++ b/tools/perf/tests/mmap-basic.c @@ -38,6 +38,8 @@ int test__basic_mmap(struct test *test __maybe_unused, int subtest __maybe_unuse expected_nr_events[nsyscalls], i, j; struct perf_evsel *evsels[nsyscalls], *evsel; char sbuf[STRERR_BUFSIZE]; + struct perf_mmap *md; + u64 end, start; threads = thread_map__new(-1, getpid(), UINT_MAX); if (threads == NULL) { @@ -106,7 +108,11 @@ int test__basic_mmap(struct test *test __maybe_unused, int subtest __maybe_unuse ++foo; } - while ((event = perf_evlist__mmap_read(evlist, 0)) != NULL) { + md = &evlist->mmap[0]; + if (perf_mmap__read_init(md, false, &start, &end) < 0) + goto out_init; + + while ((event = perf_mmap__read_event(md, false, &start, end)) != NULL) { struct perf_sample sample; if (event->header.type != PERF_RECORD_SAMPLE) { @@ -129,9 +135,11 @@ int test__basic_mmap(struct test *test __maybe_unused, int subtest __maybe_unuse goto out_delete_evlist; } nr_events[evsel->idx]++; - perf_evlist__mmap_consume(evlist, 0); + perf_mmap__consume(md, false); } + perf_mmap__read_done(md); +out_init: err = 0; evlist__for_each_entry(evlist, evsel) { if (nr_events[evsel->idx] != expected_nr_events[evsel->idx]) { diff --git a/tools/perf/tests/openat-syscall-tp-fields.c b/tools/perf/tests/openat-syscall-tp-fields.c index 43519267b93badf3426ecee4dae79c63b19e5e5c..620b21023f722e6e3ee34e8b3918501bde95e521 100644 --- a/tools/perf/tests/openat-syscall-tp-fields.c +++ b/tools/perf/tests/openat-syscall-tp-fields.c @@ -86,8 +86,14 @@ int test__syscall_openat_tp_fields(struct test *test __maybe_unused, int subtest for (i = 0; i < evlist->nr_mmaps; i++) { union perf_event *event; + struct perf_mmap *md; + u64 end, start; - while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) { + md = &evlist->mmap[i]; + if (perf_mmap__read_init(md, false, &start, &end) < 0) + continue; + + while ((event = perf_mmap__read_event(md, false, &start, end)) != NULL) { const u32 type = event->header.type; int tp_flags; struct perf_sample sample; @@ -95,7 +101,7 @@ int test__syscall_openat_tp_fields(struct test *test __maybe_unused, int subtest ++nr_events; if (type != PERF_RECORD_SAMPLE) { - perf_evlist__mmap_consume(evlist, i); + perf_mmap__consume(md, false); continue; } @@ -115,6 +121,7 @@ int test__syscall_openat_tp_fields(struct test *test __maybe_unused, int subtest goto out_ok; } + perf_mmap__read_done(md); } if (nr_events == before) diff --git a/tools/perf/tests/perf-record.c b/tools/perf/tests/perf-record.c index 0afafab852382527d587b2393b1a0c4d519101f3..31f3f70adca62b52bfd30a9d54e9e4cd77656375 100644 --- a/tools/perf/tests/perf-record.c +++ b/tools/perf/tests/perf-record.c @@ -164,8 +164,14 @@ int test__PERF_RECORD(struct test *test __maybe_unused, int subtest __maybe_unus for (i = 0; i < evlist->nr_mmaps; i++) { union perf_event *event; + struct perf_mmap *md; + u64 end, start; - while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) { + md = &evlist->mmap[i]; + if (perf_mmap__read_init(md, false, &start, &end) < 0) + continue; + + while ((event = perf_mmap__read_event(md, false, &start, end)) != NULL) { const u32 type = event->header.type; const char *name = perf_event__name(type); @@ -266,8 +272,9 @@ int test__PERF_RECORD(struct test *test __maybe_unused, int subtest __maybe_unus ++errs; } - perf_evlist__mmap_consume(evlist, i); + perf_mmap__consume(md, false); } + perf_mmap__read_done(md); } /* diff --git a/tools/perf/tests/shell/trace+probe_libc_inet_pton.sh b/tools/perf/tests/shell/record+probe_libc_inet_pton.sh similarity index 61% rename from tools/perf/tests/shell/trace+probe_libc_inet_pton.sh rename to tools/perf/tests/shell/record+probe_libc_inet_pton.sh index 8c4ab0b390c06ada5298dcb17233bd7398619820..52c3ee701a8957dbca5f4b67fc13064a54d0571f 100755 --- a/tools/perf/tests/shell/trace+probe_libc_inet_pton.sh +++ b/tools/perf/tests/shell/record+probe_libc_inet_pton.sh @@ -15,30 +15,28 @@ nm -g $libc 2>/dev/null | fgrep -q inet_pton || exit 254 trace_libc_inet_pton_backtrace() { idx=0 - expected[0]="PING.*bytes" - expected[1]="64 bytes from ::1.*" - expected[2]=".*ping statistics.*" - expected[3]=".*packets transmitted.*" - expected[4]="rtt min.*" - expected[5]="[0-9]+\.[0-9]+[[:space:]]+probe_libc:inet_pton:\([[:xdigit:]]+\)" - expected[6]=".*inet_pton[[:space:]]\($libc|inlined\)$" + expected[0]="ping[][0-9 \.:]+probe_libc:inet_pton: \([[:xdigit:]]+\)" + expected[1]=".*inet_pton[[:space:]]\($libc\)$" case "$(uname -m)" in s390x) eventattr='call-graph=dwarf' - expected[7]="gaih_inet.*[[:space:]]\($libc|inlined\)$" - expected[8]="__GI_getaddrinfo[[:space:]]\($libc|inlined\)$" - expected[9]="main[[:space:]]\(.*/bin/ping.*\)$" - expected[10]="__libc_start_main[[:space:]]\($libc\)$" - expected[11]="_start[[:space:]]\(.*/bin/ping.*\)$" + expected[2]="gaih_inet.*[[:space:]]\($libc|inlined\)$" + expected[3]="__GI_getaddrinfo[[:space:]]\($libc|inlined\)$" + expected[4]="main[[:space:]]\(.*/bin/ping.*\)$" + expected[5]="__libc_start_main[[:space:]]\($libc\)$" + expected[6]="_start[[:space:]]\(.*/bin/ping.*\)$" ;; *) eventattr='max-stack=3' - expected[7]="getaddrinfo[[:space:]]\($libc\)$" - expected[8]=".*\(.*/bin/ping.*\)$" + expected[2]="getaddrinfo[[:space:]]\($libc\)$" + expected[3]=".*\(.*/bin/ping.*\)$" ;; esac - perf trace --no-syscalls -e probe_libc:inet_pton/$eventattr/ ping -6 -c 1 ::1 2>&1 | grep -v ^$ | while read line ; do + file=`mktemp -u /tmp/perf.data.XXX` + + perf record -e probe_libc:inet_pton/$eventattr/ -o $file ping -6 -c 1 ::1 > /dev/null 2>&1 + perf script -i $file | while read line ; do echo $line echo "$line" | egrep -q "${expected[$idx]}" if [ $? -ne 0 ] ; then @@ -48,6 +46,8 @@ trace_libc_inet_pton_backtrace() { let idx+=1 [ -z "${expected[$idx]}" ] && break done + + rm -f $file } # Check for IPv6 interface existence diff --git a/tools/perf/tests/sw-clock.c b/tools/perf/tests/sw-clock.c index f6c72f915d480728aa481fdf86cc526a61dd9eb4..e6320e267ba567149660f268c54c2b7054c40faa 100644 --- a/tools/perf/tests/sw-clock.c +++ b/tools/perf/tests/sw-clock.c @@ -39,6 +39,8 @@ static int __test__sw_clock_freq(enum perf_sw_ids clock_id) }; struct cpu_map *cpus; struct thread_map *threads; + struct perf_mmap *md; + u64 end, start; attr.sample_freq = 500; @@ -93,7 +95,11 @@ static int __test__sw_clock_freq(enum perf_sw_ids clock_id) perf_evlist__disable(evlist); - while ((event = perf_evlist__mmap_read(evlist, 0)) != NULL) { + md = &evlist->mmap[0]; + if (perf_mmap__read_init(md, false, &start, &end) < 0) + goto out_init; + + while ((event = perf_mmap__read_event(md, false, &start, end)) != NULL) { struct perf_sample sample; if (event->header.type != PERF_RECORD_SAMPLE) @@ -108,9 +114,11 @@ static int __test__sw_clock_freq(enum perf_sw_ids clock_id) total_periods += sample.period; nr_samples++; next_event: - perf_evlist__mmap_consume(evlist, 0); + perf_mmap__consume(md, false); } + perf_mmap__read_done(md); +out_init: if ((u64) nr_samples == total_periods) { pr_debug("All (%d) samples have period value of 1!\n", nr_samples); diff --git a/tools/perf/tests/switch-tracking.c b/tools/perf/tests/switch-tracking.c index 33e00295a972aefe305e220d28f49e34a42a93dd..10c4dcdc2324a9a938233be44a73636e76eb9dd4 100644 --- a/tools/perf/tests/switch-tracking.c +++ b/tools/perf/tests/switch-tracking.c @@ -258,16 +258,23 @@ static int process_events(struct perf_evlist *evlist, unsigned pos, cnt = 0; LIST_HEAD(events); struct event_node *events_array, *node; + struct perf_mmap *md; + u64 end, start; int i, ret; for (i = 0; i < evlist->nr_mmaps; i++) { - while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) { + md = &evlist->mmap[i]; + if (perf_mmap__read_init(md, false, &start, &end) < 0) + continue; + + while ((event = perf_mmap__read_event(md, false, &start, end)) != NULL) { cnt += 1; ret = add_event(evlist, &events, event); - perf_evlist__mmap_consume(evlist, i); + perf_mmap__consume(md, false); if (ret < 0) goto out_free_nodes; } + perf_mmap__read_done(md); } events_array = calloc(cnt, sizeof(struct event_node)); diff --git a/tools/perf/tests/task-exit.c b/tools/perf/tests/task-exit.c index 01b62b81751b6458bea6234a0703f919f3f29b77..02b0888b72a33d383d9249d9e6aa98b488fc865c 100644 --- a/tools/perf/tests/task-exit.c +++ b/tools/perf/tests/task-exit.c @@ -47,6 +47,8 @@ int test__task_exit(struct test *test __maybe_unused, int subtest __maybe_unused char sbuf[STRERR_BUFSIZE]; struct cpu_map *cpus; struct thread_map *threads; + struct perf_mmap *md; + u64 end, start; signal(SIGCHLD, sig_handler); @@ -110,13 +112,19 @@ int test__task_exit(struct test *test __maybe_unused, int subtest __maybe_unused perf_evlist__start_workload(evlist); retry: - while ((event = perf_evlist__mmap_read(evlist, 0)) != NULL) { + md = &evlist->mmap[0]; + if (perf_mmap__read_init(md, false, &start, &end) < 0) + goto out_init; + + while ((event = perf_mmap__read_event(md, false, &start, end)) != NULL) { if (event->header.type == PERF_RECORD_EXIT) nr_exit++; - perf_evlist__mmap_consume(evlist, 0); + perf_mmap__consume(md, false); } + perf_mmap__read_done(md); +out_init: if (!exited || !nr_exit) { perf_evlist__poll(evlist, -1); goto retry; diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c index e2f666391ac4775e39d9b9befd97038f5b89708f..618edf96353c40ec998f850251160219afce119d 100644 --- a/tools/perf/ui/browsers/annotate.c +++ b/tools/perf/ui/browsers/annotate.c @@ -328,7 +328,32 @@ static void annotate_browser__draw_current_jump(struct ui_browser *browser) if (!disasm_line__is_valid_jump(cursor, sym)) return; + /* + * This first was seen with a gcc function, _cpp_lex_token, that + * has the usual jumps: + * + * │1159e6c: ↓ jne 115aa32 <_cpp_lex_token@@Base+0xf92> + * + * I.e. jumps to a label inside that function (_cpp_lex_token), and + * those works, but also this kind: + * + * │1159e8b: ↓ jne c469be + * + * I.e. jumps to another function, outside _cpp_lex_token, which + * are not being correctly handled generating as a side effect references + * to ab->offset[] entries that are set to NULL, so to make this code + * more robust, check that here. + * + * A proper fix for will be put in place, looking at the function + * name right after the '<' token and probably treating this like a + * 'call' instruction. + */ target = ab->offsets[cursor->ops.target.offset]; + if (target == NULL) { + ui_helpline__printf("WARN: jump target inconsistency, press 'o', ab->offsets[%#x] = NULL\n", + cursor->ops.target.offset); + return; + } bcursor = browser_line(&cursor->al); btarget = browser_line(target); @@ -543,35 +568,28 @@ static bool annotate_browser__callq(struct annotate_browser *browser, struct map_symbol *ms = browser->b.priv; struct disasm_line *dl = disasm_line(browser->selection); struct annotation *notes; - struct addr_map_symbol target = { - .map = ms->map, - .addr = map__objdump_2mem(ms->map, dl->ops.target.addr), - }; char title[SYM_TITLE_MAX_SIZE]; if (!ins__is_call(&dl->ins)) return false; - if (map_groups__find_ams(&target) || - map__rip_2objdump(target.map, target.map->map_ip(target.map, - target.addr)) != - dl->ops.target.addr) { + if (!dl->ops.target.sym) { ui_helpline__puts("The called function was not found."); return true; } - notes = symbol__annotation(target.sym); + notes = symbol__annotation(dl->ops.target.sym); pthread_mutex_lock(¬es->lock); - if (notes->src == NULL && symbol__alloc_hist(target.sym) < 0) { + if (notes->src == NULL && symbol__alloc_hist(dl->ops.target.sym) < 0) { pthread_mutex_unlock(¬es->lock); ui__warning("Not enough memory for annotating '%s' symbol!\n", - target.sym->name); + dl->ops.target.sym->name); return true; } pthread_mutex_unlock(¬es->lock); - symbol__tui_annotate(target.sym, target.map, evsel, hbt); + symbol__tui_annotate(dl->ops.target.sym, ms->map, evsel, hbt); sym_title(ms->sym, ms->map, title, sizeof(title)); ui_browser__show_title(&browser->b, title); return true; diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index 6495ee55d9c38feb75e207549e5634e36fd2c378..de2bde232cb30b06e8770a1518f30df8cc1e8612 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c @@ -2223,7 +2223,7 @@ static int perf_evsel_browser_title(struct hist_browser *browser, u64 nr_events = hists->stats.total_period; struct perf_evsel *evsel = hists_to_evsel(hists); const char *ev_name = perf_evsel__name(evsel); - char buf[512]; + char buf[512], sample_freq_str[64] = ""; size_t buflen = sizeof(buf); char ref[30] = " show reference callgraph, "; bool enable_ref = false; @@ -2255,10 +2255,14 @@ static int perf_evsel_browser_title(struct hist_browser *browser, if (symbol_conf.show_ref_callgraph && strstr(ev_name, "call-graph=no")) enable_ref = true; + + if (!is_report_browser(hbt)) + scnprintf(sample_freq_str, sizeof(sample_freq_str), " %d Hz,", evsel->attr.sample_freq); + nr_samples = convert_unit(nr_samples, &unit); printed = scnprintf(bf, size, - "Samples: %lu%c of event '%s',%sEvent count (approx.): %" PRIu64, - nr_samples, unit, ev_name, enable_ref ? ref : " ", nr_events); + "Samples: %lu%c of event '%s',%s%sEvent count (approx.): %" PRIu64, + nr_samples, unit, ev_name, sample_freq_str, enable_ref ? ref : " ", nr_events); if (hists->uid_filter_str) diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index 28b233c3dcbe3c5ff89b445b9769061668bdef79..49ff825f745c8f7f87a224610a5e58a494028e94 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -187,6 +187,9 @@ bool ins__is_fused(struct arch *arch, const char *ins1, const char *ins2) static int call__parse(struct arch *arch, struct ins_operands *ops, struct map *map) { char *endptr, *tok, *name; + struct addr_map_symbol target = { + .map = map, + }; ops->target.addr = strtoull(ops->raw, &endptr, 16); @@ -208,28 +211,29 @@ static int call__parse(struct arch *arch, struct ins_operands *ops, struct map * ops->target.name = strdup(name); *tok = '>'; - return ops->target.name == NULL ? -1 : 0; + if (ops->target.name == NULL) + return -1; +find_target: + target.addr = map__objdump_2mem(map, ops->target.addr); -indirect_call: - tok = strchr(endptr, '*'); - if (tok == NULL) { - struct symbol *sym = map__find_symbol(map, map->map_ip(map, ops->target.addr)); - if (sym != NULL) - ops->target.name = strdup(sym->name); - else - ops->target.addr = 0; - return 0; - } + if (map_groups__find_ams(&target) == 0 && + map__rip_2objdump(target.map, map->map_ip(target.map, target.addr)) == ops->target.addr) + ops->target.sym = target.sym; - ops->target.addr = strtoull(tok + 1, NULL, 16); return 0; + +indirect_call: + tok = strchr(endptr, '*'); + if (tok != NULL) + ops->target.addr = strtoull(tok + 1, NULL, 16); + goto find_target; } static int call__scnprintf(struct ins *ins, char *bf, size_t size, struct ins_operands *ops) { - if (ops->target.name) - return scnprintf(bf, size, "%-6s %s", ins->name, ops->target.name); + if (ops->target.sym) + return scnprintf(bf, size, "%-6s %s", ins->name, ops->target.sym->name); if (ops->target.addr == 0) return ins__raw_scnprintf(ins, bf, size, ops); @@ -1283,8 +1287,8 @@ static int symbol__parse_objdump_line(struct symbol *sym, FILE *file, dl->ops.target.offset_avail = true; } - /* kcore has no symbols, so add the call target name */ - if (dl->ins.ops && ins__is_call(&dl->ins) && !dl->ops.target.name) { + /* kcore has no symbols, so add the call target symbol */ + if (dl->ins.ops && ins__is_call(&dl->ins) && !dl->ops.target.sym) { struct addr_map_symbol target = { .map = map, .addr = dl->ops.target.addr, @@ -1292,7 +1296,7 @@ static int symbol__parse_objdump_line(struct symbol *sym, FILE *file, if (!map_groups__find_ams(&target) && target.sym->start == target.al_addr) - dl->ops.target.name = strdup(target.sym->name); + dl->ops.target.sym = target.sym; } annotation_line__add(&dl->al, ¬es->src->source); diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h index ce427445671f5b85278d87b868e048d0d9dbc92c..7e914e834101bd0e3b7cb6b43d2b0705f5cddeb4 100644 --- a/tools/perf/util/annotate.h +++ b/tools/perf/util/annotate.h @@ -24,6 +24,7 @@ struct ins_operands { struct { char *raw; char *name; + struct symbol *sym; u64 addr; s64 offset; bool offset_avail; diff --git a/tools/perf/util/cgroup.c b/tools/perf/util/cgroup.c index 984f69144f87e2dfef2bf5123537d36d4eeeaa28..5dd9b5ea314de756b4a54cbce0543e20a8033a84 100644 --- a/tools/perf/util/cgroup.c +++ b/tools/perf/util/cgroup.c @@ -157,9 +157,11 @@ int parse_cgroups(const struct option *opt __maybe_unused, const char *str, int unset __maybe_unused) { struct perf_evlist *evlist = *(struct perf_evlist **)opt->value; + struct perf_evsel *counter; + struct cgroup_sel *cgrp = NULL; const char *p, *e, *eos = str + strlen(str); char *s; - int ret; + int ret, i; if (list_empty(&evlist->entries)) { fprintf(stderr, "must define events before cgroups\n"); @@ -188,5 +190,18 @@ int parse_cgroups(const struct option *opt __maybe_unused, const char *str, break; str = p+1; } + /* for the case one cgroup combine to multiple events */ + i = 0; + if (nr_cgroups == 1) { + evlist__for_each_entry(evlist, counter) { + if (i == 0) + cgrp = counter->cgrp; + else { + counter->cgrp = cgrp; + refcount_inc(&cgrp->refcnt); + } + i++; + } + } return 0; } diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 7b7d535396f7a766ff45ee9d63bf8b2f76068a74..41a4666f1519b1a1207bf23858cabe7567485822 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -702,29 +702,6 @@ static int perf_evlist__resume(struct perf_evlist *evlist) return perf_evlist__set_paused(evlist, false); } -union perf_event *perf_evlist__mmap_read_forward(struct perf_evlist *evlist, int idx) -{ - struct perf_mmap *md = &evlist->mmap[idx]; - - /* - * Check messup is required for forward overwritable ring buffer: - * memory pointed by md->prev can be overwritten in this case. - * No need for read-write ring buffer: kernel stop outputting when - * it hit md->prev (perf_mmap__consume()). - */ - return perf_mmap__read_forward(md); -} - -union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx) -{ - return perf_evlist__mmap_read_forward(evlist, idx); -} - -void perf_evlist__mmap_consume(struct perf_evlist *evlist, int idx) -{ - perf_mmap__consume(&evlist->mmap[idx], false); -} - static void perf_evlist__munmap_nofree(struct perf_evlist *evlist) { int i; @@ -761,7 +738,7 @@ static struct perf_mmap *perf_evlist__alloc_mmap(struct perf_evlist *evlist) map[i].fd = -1; /* * When the perf_mmap() call is made we grab one refcount, plus - * one extra to let perf_evlist__mmap_consume() get the last + * one extra to let perf_mmap__consume() get the last * events after all real references (perf_mmap__get()) are * dropped. * diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index 336b838e6957e503da3f73276d828067c8484386..6c41b2f7871396ba9e43708c8b1835c01fea72d4 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h @@ -129,10 +129,6 @@ struct perf_sample_id *perf_evlist__id2sid(struct perf_evlist *evlist, u64 id); void perf_evlist__toggle_bkw_mmap(struct perf_evlist *evlist, enum bkw_mmap_state state); -union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx); - -union perf_event *perf_evlist__mmap_read_forward(struct perf_evlist *evlist, - int idx); void perf_evlist__mmap_consume(struct perf_evlist *evlist, int idx); int perf_evlist__open(struct perf_evlist *evlist); diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index ef351688b79798a1c46b95094fb078c6daee14aa..b56e1c2ddaee2e320a7ce437434557f901a8dc47 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -1915,6 +1915,9 @@ int perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, goto fallback_missing_features; } out_close: + if (err) + threads->err_thread = thread; + do { while (--thread >= 0) { close(FD(evsel, cpu, thread)); diff --git a/tools/perf/util/mmap.c b/tools/perf/util/mmap.c index 91531a7c8fbf38606ee52ca0c3fef57bb2a9cff9..4f27c464ce0b90de89d633140d5cae3edefeaf84 100644 --- a/tools/perf/util/mmap.c +++ b/tools/perf/util/mmap.c @@ -63,25 +63,6 @@ static union perf_event *perf_mmap__read(struct perf_mmap *map, return event; } -/* - * legacy interface for mmap read. - * Don't use it. Use perf_mmap__read_event(). - */ -union perf_event *perf_mmap__read_forward(struct perf_mmap *map) -{ - u64 head; - - /* - * Check if event was unmapped due to a POLLHUP/POLLERR. - */ - if (!refcount_read(&map->refcnt)) - return NULL; - - head = perf_mmap__read_head(map); - - return perf_mmap__read(map, &map->prev, head); -} - /* * Read event from ring buffer one by one. * Return one event for each call. @@ -191,7 +172,7 @@ void perf_mmap__munmap(struct perf_mmap *map) int perf_mmap__mmap(struct perf_mmap *map, struct mmap_params *mp, int fd) { /* - * The last one will be done at perf_evlist__mmap_consume(), so that we + * The last one will be done at perf_mmap__consume(), so that we * make sure we don't prevent tools from consuming every last event in * the ring buffer. * diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index 2918cac7a142633c77867fce41898868165b7acc..35fb5ef7d29019cb19d111cefeb26cb956f72328 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -983,13 +983,19 @@ static PyObject *pyrf_evlist__read_on_cpu(struct pyrf_evlist *pevlist, union perf_event *event; int sample_id_all = 1, cpu; static char *kwlist[] = { "cpu", "sample_id_all", NULL }; + struct perf_mmap *md; + u64 end, start; int err; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i|i", kwlist, &cpu, &sample_id_all)) return NULL; - event = perf_evlist__mmap_read(evlist, cpu); + md = &evlist->mmap[cpu]; + if (perf_mmap__read_init(md, false, &start, &end) < 0) + goto end; + + event = perf_mmap__read_event(md, false, &start, end); if (event != NULL) { PyObject *pyevent = pyrf_event__new(event); struct pyrf_event *pevent = (struct pyrf_event *)pyevent; @@ -1007,14 +1013,14 @@ static PyObject *pyrf_evlist__read_on_cpu(struct pyrf_evlist *pevlist, err = perf_evsel__parse_sample(evsel, event, &pevent->sample); /* Consume the even only after we parsed it out. */ - perf_evlist__mmap_consume(evlist, cpu); + perf_mmap__consume(md, false); if (err) return PyErr_Format(PyExc_OSError, "perf: can't parse sample, err=%d", err); return pyevent; } - +end: Py_INCREF(Py_None); return Py_None; } diff --git a/tools/perf/util/record.c b/tools/perf/util/record.c index 1e97937b03a933890e7101ecad982c99e564836c..9cfc7bf165316b30b191593bcf2d27ddd37f017d 100644 --- a/tools/perf/util/record.c +++ b/tools/perf/util/record.c @@ -5,6 +5,7 @@ #include "parse-events.h" #include #include +#include #include "util.h" #include "cloexec.h" @@ -137,6 +138,7 @@ void perf_evlist__config(struct perf_evlist *evlist, struct record_opts *opts, struct perf_evsel *evsel; bool use_sample_identifier = false; bool use_comm_exec; + bool sample_id = opts->sample_id; /* * Set the evsel leader links before we configure attributes, @@ -163,8 +165,7 @@ void perf_evlist__config(struct perf_evlist *evlist, struct record_opts *opts, * match the id. */ use_sample_identifier = perf_can_sample_identifier(); - evlist__for_each_entry(evlist, evsel) - perf_evsel__set_sample_id(evsel, use_sample_identifier); + sample_id = true; } else if (evlist->nr_entries > 1) { struct perf_evsel *first = perf_evlist__first(evlist); @@ -174,6 +175,10 @@ void perf_evlist__config(struct perf_evlist *evlist, struct record_opts *opts, use_sample_identifier = perf_can_sample_identifier(); break; } + sample_id = true; + } + + if (sample_id) { evlist__for_each_entry(evlist, evsel) perf_evsel__set_sample_id(evsel, use_sample_identifier); } @@ -215,11 +220,21 @@ static int record_opts__config_freq(struct record_opts *opts) * User specified frequency is over current maximum. */ if (user_freq && (max_rate < opts->freq)) { - pr_err("Maximum frequency rate (%u) reached.\n" - "Please use -F freq option with lower value or consider\n" - "tweaking /proc/sys/kernel/perf_event_max_sample_rate.\n", - max_rate); - return -1; + if (opts->strict_freq) { + pr_err("error: Maximum frequency rate (%'u Hz) exceeded.\n" + " Please use -F freq option with a lower value or consider\n" + " tweaking /proc/sys/kernel/perf_event_max_sample_rate.\n", + max_rate); + return -1; + } else { + pr_warning("warning: Maximum frequency rate (%'u Hz) exceeded, throttling from %'u Hz to %'u Hz.\n" + " The limit can be raised via /proc/sys/kernel/perf_event_max_sample_rate.\n" + " The kernel will lower it when perf's interrupts take too long.\n" + " Use --strict-freq to disable this throttling, refusing to record.\n", + max_rate, opts->freq, max_rate); + + opts->freq = max_rate; + } } /* @@ -287,3 +302,25 @@ bool perf_evlist__can_select_event(struct perf_evlist *evlist, const char *str) perf_evlist__delete(temp_evlist); return ret; } + +int record__parse_freq(const struct option *opt, const char *str, int unset __maybe_unused) +{ + unsigned int freq; + struct record_opts *opts = opt->value; + + if (!str) + return -EINVAL; + + if (strcasecmp(str, "max") == 0) { + if (get_max_rate(&freq)) { + pr_err("couldn't read /proc/sys/kernel/perf_event_max_sample_rate\n"); + return -1; + } + pr_info("info: Using a maximum frequency rate of %'d Hz\n", freq); + } else { + freq = atoi(str); + } + + opts->user_freq = freq; + return 0; +} diff --git a/tools/perf/util/thread_map.c b/tools/perf/util/thread_map.c index 729dad8f412d62ac700a8801ffc79c67c8025bc6..5d467d8ae9abf9bee19b5cb02b01d1849af5f32d 100644 --- a/tools/perf/util/thread_map.c +++ b/tools/perf/util/thread_map.c @@ -32,6 +32,7 @@ static void thread_map__reset(struct thread_map *map, int start, int nr) size_t size = (nr - start) * sizeof(map->map[0]); memset(&map->map[start], 0, size); + map->err_thread = -1; } static struct thread_map *thread_map__realloc(struct thread_map *map, int nr) diff --git a/tools/perf/util/thread_map.h b/tools/perf/util/thread_map.h index 5ec91cfd18696b9f1efccdf824bfc4a9ec32b122..2f689c90a8c6d59c5e52a7d2627c9c793df55834 100644 --- a/tools/perf/util/thread_map.h +++ b/tools/perf/util/thread_map.h @@ -14,6 +14,7 @@ struct thread_map_data { struct thread_map { refcount_t refcnt; int nr; + int err_thread; struct thread_map_data map[]; };