diff options
| author | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2026-07-24 16:17:26 +0200 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2026-07-24 16:17:26 +0200 |
| commit | b62ed4c93ad71374b54d2c3a72cbc67b506f580c (patch) | |
| tree | 6ca6ab974bbce902fc9de300fea6aa056170850f /tools/perf | |
| parent | 5895db67c12464003afd16c08049b73aa09e58ea (diff) | |
| parent | 221fc2f4d0eda59d02af2e751a9282fa013a8e97 (diff) | |
Merge v6.18.40linux-rolling-lts
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'tools/perf')
80 files changed, 1133 insertions, 524 deletions
diff --git a/tools/perf/Documentation/perf-inject.txt b/tools/perf/Documentation/perf-inject.txt index c972032f4ca0..95dfdf39666e 100644 --- a/tools/perf/Documentation/perf-inject.txt +++ b/tools/perf/Documentation/perf-inject.txt @@ -109,6 +109,11 @@ include::itrace.txt[] should be used, and also --buildid-all and --switch-events may be useful. +--convert-callchain:: + Parse DWARF callchains and convert them to usual callchains. This also + discards stack and register data from the samples. This will lose + inlined callchain entries. + :GMEXAMPLECMD: inject :GMEXAMPLESUBCMD: include::guestmount.txt[] diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config index 2dd5f5a60568..8ae13809f68b 100644 --- a/tools/perf/Makefile.config +++ b/tools/perf/Makefile.config @@ -110,6 +110,7 @@ endif ifeq ($(ARCH),s390) CFLAGS += -fPIC + CXXFLAGS += -fPIC endif ifeq ($(ARCH),mips) diff --git a/tools/perf/arch/arm64/util/header.c b/tools/perf/arch/arm64/util/header.c index f445a2dd6293..cbc0ba101636 100644 --- a/tools/perf/arch/arm64/util/header.c +++ b/tools/perf/arch/arm64/util/header.c @@ -1,4 +1,3 @@ -#include <linux/kernel.h> #include <linux/bits.h> #include <linux/bitfield.h> #include <stdio.h> diff --git a/tools/perf/arch/common.c b/tools/perf/arch/common.c index 4908d54dd33b..e9454df5235e 100644 --- a/tools/perf/arch/common.c +++ b/tools/perf/arch/common.c @@ -237,5 +237,7 @@ int perf_env__lookup_objdump(struct perf_env *env, char **path) */ bool perf_env__single_address_space(struct perf_env *env) { - return strcmp(perf_env__arch(env), "sparc"); + const char *arch = perf_env__arch(env); + + return strcmp(arch, "s390") && strcmp(arch, "sparc"); } diff --git a/tools/perf/arch/powerpc/util/auxtrace.c b/tools/perf/arch/powerpc/util/auxtrace.c index 62c6f67f1bbe..57f2910c0b01 100644 --- a/tools/perf/arch/powerpc/util/auxtrace.c +++ b/tools/perf/arch/powerpc/util/auxtrace.c @@ -70,6 +70,12 @@ struct auxtrace_record *auxtrace_record__init(struct evlist *evlist, struct evsel *pos; int found = 0; + /* + * Set err value to zero here. Any fail later + * will set appropriate return code to err. + */ + *err = 0; + evlist__for_each_entry(evlist, pos) { if (strstarts(pos->name, "vpa_dtl")) { found = 1; diff --git a/tools/perf/arch/x86/tests/amd-ibs-period.c b/tools/perf/arch/x86/tests/amd-ibs-period.c index 223e059e04de..986ac34d218f 100644 --- a/tools/perf/arch/x86/tests/amd-ibs-period.c +++ b/tools/perf/arch/x86/tests/amd-ibs-period.c @@ -933,7 +933,7 @@ static bool kernel_v6_15_or_newer(void) endptr++; minor = strtol(endptr, NULL, 10); - return major >= 6 && minor >= 15; + return major > 6 || (major == 6 && minor >= 15); } int test__amd_ibs_period(struct test_suite *test __maybe_unused, diff --git a/tools/perf/arch/x86/tests/bp-modify.c b/tools/perf/arch/x86/tests/bp-modify.c index 0924ccd9e36d..589b43273948 100644 --- a/tools/perf/arch/x86/tests/bp-modify.c +++ b/tools/perf/arch/x86/tests/bp-modify.c @@ -80,26 +80,24 @@ static int bp_modify1(void) */ if (ptrace(PTRACE_POKEUSER, child, offsetof(struct user, u_debugreg[0]), bp_2)) { - pr_debug("failed to set breakpoint, 1st time: %s\n", - strerror(errno)); + pr_debug("failed to set breakpoint, 1st time: %m\n"); goto out; } if (ptrace(PTRACE_POKEUSER, child, offsetof(struct user, u_debugreg[0]), bp_1)) { - pr_debug("failed to set breakpoint, 2nd time: %s\n", - strerror(errno)); + pr_debug("failed to set breakpoint, 2nd time: %m\n"); goto out; } if (ptrace(PTRACE_POKEUSER, child, offsetof(struct user, u_debugreg[7]), dr7)) { - pr_debug("failed to set dr7: %s\n", strerror(errno)); + pr_debug("failed to set dr7: %m\n"); goto out; } if (ptrace(PTRACE_CONT, child, NULL, NULL)) { - pr_debug("failed to PTRACE_CONT: %s\n", strerror(errno)); + pr_debug("failed to PTRACE_CONT: %m\n"); goto out; } @@ -112,19 +110,17 @@ static int bp_modify1(void) rip = ptrace(PTRACE_PEEKUSER, child, offsetof(struct user_regs_struct, rip), NULL); if (rip == (unsigned long) -1) { - pr_debug("failed to PTRACE_PEEKUSER: %s\n", - strerror(errno)); + pr_debug("failed to PTRACE_PEEKUSER: %m\n"); goto out; } pr_debug("rip %lx, bp_1 %p\n", rip, bp_1); - out: if (ptrace(PTRACE_DETACH, child, NULL, NULL)) { - pr_debug("failed to PTRACE_DETACH: %s", strerror(errno)); + pr_debug("failed to PTRACE_DETACH: %m\n"); return TEST_FAIL; - } + } return rip == (unsigned long) bp_1 ? TEST_OK : TEST_FAIL; } @@ -157,14 +153,13 @@ static int bp_modify2(void) */ if (ptrace(PTRACE_POKEUSER, child, offsetof(struct user, u_debugreg[0]), bp_1)) { - pr_debug("failed to set breakpoint: %s\n", - strerror(errno)); + pr_debug("failed to set breakpoint: %m\n"); goto out; } if (ptrace(PTRACE_POKEUSER, child, offsetof(struct user, u_debugreg[7]), dr7)) { - pr_debug("failed to set dr7: %s\n", strerror(errno)); + pr_debug("failed to set dr7: %m\n"); goto out; } @@ -175,7 +170,7 @@ static int bp_modify2(void) } if (ptrace(PTRACE_CONT, child, NULL, NULL)) { - pr_debug("failed to PTRACE_CONT: %s\n", strerror(errno)); + pr_debug("failed to PTRACE_CONT: %m\n"); goto out; } @@ -188,8 +183,7 @@ static int bp_modify2(void) rip = ptrace(PTRACE_PEEKUSER, child, offsetof(struct user_regs_struct, rip), NULL); if (rip == (unsigned long) -1) { - pr_debug("failed to PTRACE_PEEKUSER: %s\n", - strerror(errno)); + pr_debug("failed to PTRACE_PEEKUSER: %m\n"); goto out; } @@ -197,7 +191,7 @@ static int bp_modify2(void) out: if (ptrace(PTRACE_DETACH, child, NULL, NULL)) { - pr_debug("failed to PTRACE_DETACH: %s", strerror(errno)); + pr_debug("failed to PTRACE_DETACH: %m\n"); return TEST_FAIL; } diff --git a/tools/perf/bench/inject-buildid.c b/tools/perf/bench/inject-buildid.c index 12387ea88b9a..aad572a78d7f 100644 --- a/tools/perf/bench/inject-buildid.c +++ b/tools/perf/bench/inject-buildid.c @@ -85,7 +85,7 @@ static int add_dso(const char *fpath, const struct stat *sb __maybe_unused, if (typeflag == FTW_D || typeflag == FTW_SL) return 0; - if (filename__read_build_id(fpath, &bid, /*block=*/true) < 0) + if (filename__read_build_id(fpath, &bid) < 0) return 0; dso->name = realpath(fpath, NULL); diff --git a/tools/perf/bench/uprobe.c b/tools/perf/bench/uprobe.c index 0b90275862e1..c4dac868f1ee 100644 --- a/tools/perf/bench/uprobe.c +++ b/tools/perf/bench/uprobe.c @@ -54,7 +54,7 @@ static const char * const bench_uprobe_usage[] = { /*opts=*/&uprobe_opts); \ if (!skel->links.prog) { \ err = -errno; \ - fprintf(stderr, "Failed to attach bench uprobe \"%s\": %s\n", #prog, strerror(errno)); \ + fprintf(stderr, "Failed to attach bench uprobe \"%s\": %m\n", #prog); \ goto cleanup; \ } diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 646f43b0f7c4..0485bddb0246 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -299,7 +299,8 @@ static int process_sample_event(const struct perf_tool *tool, goto out_put; } - if (ann->cpu_list && !test_bit(sample->cpu, ann->cpu_bitmap)) + if (ann->cpu_list && (sample->cpu >= MAX_NR_CPUS || + !test_bit(sample->cpu, ann->cpu_bitmap))) goto out_put; if (!al.filtered && diff --git a/tools/perf/builtin-buildid-cache.c b/tools/perf/builtin-buildid-cache.c index 2e0f2004696a..539e779e3268 100644 --- a/tools/perf/builtin-buildid-cache.c +++ b/tools/perf/builtin-buildid-cache.c @@ -180,7 +180,7 @@ static int build_id_cache__add_file(const char *filename, struct nsinfo *nsi) struct nscookie nsc; nsinfo__mountns_enter(nsi, &nsc); - err = filename__read_build_id(filename, &bid, /*block=*/true); + err = filename__read_build_id(filename, &bid); nsinfo__mountns_exit(&nsc); if (err < 0) { pr_debug("Couldn't read a build-id in %s\n", filename); @@ -204,7 +204,7 @@ static int build_id_cache__remove_file(const char *filename, struct nsinfo *nsi) int err; nsinfo__mountns_enter(nsi, &nsc); - err = filename__read_build_id(filename, &bid, /*block=*/true); + err = filename__read_build_id(filename, &bid); nsinfo__mountns_exit(&nsc); if (err < 0) { pr_debug("Couldn't read a build-id in %s\n", filename); @@ -276,12 +276,14 @@ static bool dso__missing_buildid_cache(struct dso *dso, int parm __maybe_unused) { char filename[PATH_MAX]; struct build_id bid = { .size = 0, }; + int err; if (!dso__build_id_filename(dso, filename, sizeof(filename), false)) return true; - if (filename__read_build_id(filename, &bid, /*block=*/true) == -1) { - if (errno == ENOENT) + err = filename__read_build_id(filename, &bid); + if (err < 0) { + if (err == -ENOENT) return false; pr_warning("Problems with %s file, consider removing it from the cache\n", @@ -309,7 +311,7 @@ static int build_id_cache__update_file(const char *filename, struct nsinfo *nsi) int err; nsinfo__mountns_enter(nsi, &nsc); - err = filename__read_build_id(filename, &bid, /*block=*/true); + err = filename__read_build_id(filename, &bid); nsinfo__mountns_exit(&nsc); if (err < 0) { pr_debug("Couldn't read a build-id in %s\n", filename); diff --git a/tools/perf/builtin-buildid-list.c b/tools/perf/builtin-buildid-list.c index a91bbb34ac94..e0881b0ac38f 100644 --- a/tools/perf/builtin-buildid-list.c +++ b/tools/perf/builtin-buildid-list.c @@ -61,7 +61,7 @@ static int sysfs__fprintf_build_id(FILE *fp) int ret; ret = sysfs__snprintf_build_id("/", sbuild_id, sizeof(sbuild_id)); - if (ret != sizeof(sbuild_id)) + if (ret + 1 != sizeof(sbuild_id)) return ret < 0 ? ret : -EINVAL; return fprintf(fp, "%s\n", sbuild_id); @@ -73,7 +73,7 @@ static int filename__fprintf_build_id(const char *name, FILE *fp) int ret; ret = filename__snprintf_build_id(name, sbuild_id, sizeof(sbuild_id)); - if (ret != sizeof(sbuild_id)) + if (ret + 1 != sizeof(sbuild_id)) return ret < 0 ? ret : -EINVAL; return fprintf(fp, "%s\n", sbuild_id); diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c index 9e9ff471ddd1..0849092ef5fc 100644 --- a/tools/perf/builtin-c2c.c +++ b/tools/perf/builtin-c2c.c @@ -218,6 +218,7 @@ he__get_c2c_hists(struct hist_entry *he, ret = c2c_hists__init(hists, sort, nr_header_lines, env); if (ret) { + c2c_he->hists = NULL; free(hists); return NULL; } @@ -232,6 +233,10 @@ static void c2c_he__set_cpu(struct c2c_hist_entry *c2c_he, "WARNING: no sample cpu value")) return; + /* cpuset bitmap has c2c.cpus_cnt bits from env->nr_cpus_avail */ + if (sample->cpu >= (unsigned int)c2c.cpus_cnt) + return; + __set_bit(sample->cpu, c2c_he->cpuset); } @@ -249,6 +254,10 @@ static void c2c_he__set_node(struct c2c_hist_entry *c2c_he, if (WARN_ONCE(node < 0, "WARNING: failed to find node\n")) return; + /* nodeset bitmap has c2c.nodes_cnt bits from env->nr_numa_nodes */ + if (node >= c2c.nodes_cnt) + return; + __set_bit(node, c2c_he->nodeset); if (c2c_he->paddr != sample->phys_addr) { @@ -348,7 +357,12 @@ static int process_sample_event(const struct perf_tool *tool __maybe_unused, * Doing node stats only for single callchain data. */ int cpu = sample->cpu == (unsigned int) -1 ? 0 : sample->cpu; - int node = c2c.cpu2node[cpu]; + int node; + + /* cpu2node[] has c2c.cpus_cnt entries; large u32 wraps signed negative */ + if (cpu < 0 || cpu >= c2c.cpus_cnt) + cpu = 0; + node = c2c.cpu2node[cpu]; mi = mi_dup; @@ -365,7 +379,9 @@ static int process_sample_event(const struct perf_tool *tool __maybe_unused, c2c_he = container_of(he, struct c2c_hist_entry, he); c2c_add_stats(&c2c_he->stats, &stats); c2c_add_stats(&c2c_hists->stats, &stats); - c2c_add_stats(&c2c_he->node_stats[node], &stats); + /* node_stats[] has c2c.nodes_cnt entries */ + if (node >= 0 && node < c2c.nodes_cnt) + c2c_add_stats(&c2c_he->node_stats[node], &stats); compute_stats(c2c_he, &stats, sample->weight); @@ -2312,6 +2328,10 @@ static int setup_nodes(struct perf_session *session) nodes[node] = set; perf_cpu_map__for_each_cpu_skip_any(cpu, idx, map) { + /* topology CPU IDs from perf.data may exceed nr_cpus_avail */ + if (cpu.cpu < 0 || cpu.cpu >= c2c.cpus_cnt) + continue; + __set_bit(cpu.cpu, set); if (WARN_ONCE(cpu2node[cpu.cpu] != -1, "node/cpu topology bug")) diff --git a/tools/perf/builtin-daemon.c b/tools/perf/builtin-daemon.c index f0568431fbd5..33473e071392 100644 --- a/tools/perf/builtin-daemon.c +++ b/tools/perf/builtin-daemon.c @@ -265,8 +265,7 @@ static int check_base(struct daemon *daemon) daemon->base); return -EACCES; default: - pr_err("failed: can't access base '%s': %s\n", - daemon->base, strerror(errno)); + pr_err("failed: can't access base '%s': %m\n", daemon->base); return -errno; } } @@ -544,8 +543,7 @@ static int daemon_session__control(struct daemon_session *session, err = writen(control, msg, len); if (err != len) { - pr_err("failed: write to control pipe: %d (%s)\n", - errno, control_path); + pr_err("failed: write to control pipe: %m (%s)\n", control_path); goto out; } @@ -586,7 +584,7 @@ static int setup_server_socket(struct daemon *daemon) int fd = socket(AF_UNIX, SOCK_STREAM, 0); if (fd < 0) { - fprintf(stderr, "socket: %s\n", strerror(errno)); + fprintf(stderr, "socket: %m\n"); return -1; } diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 53d5ea4a6a4f..98fcfe924f5d 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -178,10 +178,9 @@ static struct header_column { } }; -static int setup_compute_opt_wdiff(char *opt) +static int setup_compute_opt_wdiff(const char *opt) { - char *w1_str = opt; - char *w2_str; + const char *w1_str = opt, *w2_str; int ret = -EINVAL; @@ -192,8 +191,7 @@ static int setup_compute_opt_wdiff(char *opt) if (!w2_str) goto out; - *w2_str++ = 0x0; - if (!*w2_str) + if (!*++w2_str) goto out; compute_wdiff_w1 = strtol(w1_str, NULL, 10); @@ -214,7 +212,7 @@ static int setup_compute_opt_wdiff(char *opt) return ret; } -static int setup_compute_opt(char *opt) +static int setup_compute_opt(const char *opt) { if (compute == COMPUTE_WEIGHTED_DIFF) return setup_compute_opt_wdiff(opt); @@ -234,7 +232,7 @@ static int setup_compute(const struct option *opt, const char *str, char *cstr = (char *) str; char buf[50]; unsigned i; - char *option; + const char *option; if (!str) { *cp = COMPUTE_DELTA; @@ -418,7 +416,8 @@ static int diff__process_sample_event(const struct perf_tool *tool, goto out; } - if (cpu_list && !test_bit(sample->cpu, cpu_bitmap)) { + if (cpu_list && (sample->cpu >= MAX_NR_CPUS || + !test_bit(sample->cpu, cpu_bitmap))) { ret = 0; goto out; } diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index a114b3fa1bea..e8278999b12d 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -122,6 +122,7 @@ struct perf_inject { bool in_place_update; bool in_place_update_dry_run; bool copy_kcore_dir; + bool convert_callchain; const char *input_name; struct perf_data output; u64 bytes_written; @@ -133,6 +134,7 @@ struct perf_inject { struct guest_session guest_session; struct strlist *known_build_ids; const struct evsel *mmap_evsel; + struct ip_callchain *raw_callchain; }; struct event_entry { @@ -396,6 +398,91 @@ static int perf_event__repipe_sample(const struct perf_tool *tool, return perf_event__repipe_synth(tool, event); } +static int perf_event__convert_sample_callchain(const struct perf_tool *tool, + union perf_event *event, + struct perf_sample *sample, + struct evsel *evsel, + struct machine *machine) +{ + struct perf_inject *inject = container_of(tool, struct perf_inject, tool); + struct callchain_cursor *cursor = get_tls_callchain_cursor(); + union perf_event *event_copy = (void *)inject->event_copy; + struct callchain_cursor_node *node; + struct thread *thread; + u64 sample_type = evsel->core.attr.sample_type; + u32 sample_size = event->header.size; + u64 i, k; + int ret; + + if (event_copy == NULL) { + inject->event_copy = malloc(PERF_SAMPLE_MAX_SIZE); + if (!inject->event_copy) + return -ENOMEM; + + event_copy = (void *)inject->event_copy; + } + + if (cursor == NULL) + return -ENOMEM; + + callchain_cursor_reset(cursor); + + thread = machine__find_thread(machine, sample->tid, sample->pid); + if (thread == NULL) + goto out; + + /* this will parse DWARF using stack and register data */ + ret = thread__resolve_callchain(thread, cursor, evsel, sample, + /*parent=*/NULL, /*root_al=*/NULL, + PERF_MAX_STACK_DEPTH); + thread__put(thread); + if (ret != 0) + goto out; + + /* copy kernel callchain and context entries */ + for (i = 0; i < sample->callchain->nr; i++) { + inject->raw_callchain->ips[i] = sample->callchain->ips[i]; + if (sample->callchain->ips[i] == PERF_CONTEXT_USER) { + i++; + break; + } + } + if (i == 0 || inject->raw_callchain->ips[i - 1] != PERF_CONTEXT_USER) + inject->raw_callchain->ips[i++] = PERF_CONTEXT_USER; + + node = cursor->first; + for (k = 0; k < cursor->nr && i < PERF_MAX_STACK_DEPTH; k++) { + if (machine->single_address_space && + machine__kernel_ip(machine, node->ip)) + /* kernel IPs were added already */; + else if (node->ms.sym && node->ms.sym->inlined) + /* we can't handle inlined callchains */; + else + inject->raw_callchain->ips[i++] = node->ip; + + node = node->next; + } + + inject->raw_callchain->nr = i; + sample->callchain = inject->raw_callchain; + +out: + memcpy(event_copy, event, sizeof(event->header)); + + /* adjust sample size for stack and regs */ + sample_size -= sample->user_stack.size; + sample_size -= (hweight64(evsel->core.attr.sample_regs_user) + 1) * sizeof(u64); + sample_size += (sample->callchain->nr + 1) * sizeof(u64); + event_copy->header.size = sample_size; + + /* remove sample_type {STACK,REGS}_USER for synthesize */ + sample_type &= ~(PERF_SAMPLE_STACK_USER | PERF_SAMPLE_REGS_USER); + + perf_event__synthesize_sample(event_copy, sample_type, + evsel->core.attr.read_format, sample); + return perf_event__repipe_synth(tool, event_copy); +} + static struct dso *findnew_dso(int pid, int tid, const char *filename, const struct dso_id *id, struct machine *machine) { @@ -680,12 +767,12 @@ static int dso__read_build_id(struct dso *dso) mutex_lock(dso__lock(dso)); nsinfo__mountns_enter(dso__nsinfo(dso), &nsc); - if (filename__read_build_id(dso__long_name(dso), &bid, /*block=*/true) > 0) + if (filename__read_build_id(dso__long_name(dso), &bid) > 0) dso__set_build_id(dso, &bid); else if (dso__nsinfo(dso)) { char *new_name = dso__filename_with_chroot(dso, dso__long_name(dso)); - if (new_name && filename__read_build_id(new_name, &bid, /*block=*/true) > 0) + if (new_name && filename__read_build_id(new_name, &bid) > 0) dso__set_build_id(dso, &bid); free(new_name); } @@ -2280,6 +2367,15 @@ static int __cmd_inject(struct perf_inject *inject) /* Allow space in the header for guest attributes */ output_data_offset += gs->session->header.data_offset; output_data_offset = roundup(output_data_offset, 4096); + } else if (inject->convert_callchain) { + inject->tool.sample = perf_event__convert_sample_callchain; + inject->tool.fork = perf_event__repipe_fork; + inject->tool.comm = perf_event__repipe_comm; + inject->tool.exit = perf_event__repipe_exit; + inject->tool.mmap = perf_event__repipe_mmap; + inject->tool.mmap2 = perf_event__repipe_mmap2; + inject->tool.ordered_events = true; + inject->tool.ordering_requires_timestamps = true; } if (!inject->itrace_synth_opts.set) @@ -2332,6 +2428,23 @@ static int __cmd_inject(struct perf_inject *inject) perf_header__set_feat(&session->header, HEADER_BRANCH_STACK); } + + /* + * The converted data file won't have stack and registers. + * Update the perf_event_attr to remove them before writing. + */ + if (inject->convert_callchain) { + struct evsel *evsel; + + evlist__for_each_entry(session->evlist, evsel) { + evsel__reset_sample_bit(evsel, REGS_USER); + evsel__reset_sample_bit(evsel, STACK_USER); + evsel->core.attr.sample_regs_user = 0; + evsel->core.attr.sample_stack_user = 0; + evsel->core.attr.exclude_callchain_user = 0; + } + } + session->header.data_offset = output_data_offset; session->header.data_size = inject->bytes_written; perf_session__inject_header(session, session->evlist, fd, &inj_fc.fc, @@ -2356,6 +2469,18 @@ static int __cmd_inject(struct perf_inject *inject) return ret; } +static bool evsel__has_dwarf_callchain(struct evsel *evsel) +{ + struct perf_event_attr *attr = &evsel->core.attr; + const u64 dwarf_callchain_flags = + PERF_SAMPLE_STACK_USER | PERF_SAMPLE_REGS_USER | PERF_SAMPLE_CALLCHAIN; + + if (!attr->exclude_callchain_user) + return false; + + return (attr->sample_type & dwarf_callchain_flags) == dwarf_callchain_flags; +} + int cmd_inject(int argc, const char **argv) { struct perf_inject inject = { @@ -2424,6 +2549,8 @@ int cmd_inject(int argc, const char **argv) OPT_STRING(0, "guestmount", &symbol_conf.guestmount, "directory", "guest mount directory under which every guest os" " instance has a subdir"), + OPT_BOOLEAN(0, "convert-callchain", &inject.convert_callchain, + "Generate callchains using DWARF and drop register/stack data"), OPT_END() }; const char * const inject_usage[] = { @@ -2440,6 +2567,9 @@ int cmd_inject(int argc, const char **argv) #ifndef HAVE_JITDUMP set_option_nobuild(options, 'j', "jit", "NO_LIBELF=1", true); #endif +#ifndef HAVE_LIBDW_SUPPORT + set_option_nobuild(options, 0, "convert-callchain", "NO_LIBDW=1", true); +#endif argc = parse_options(argc, argv, options, inject_usage, 0); /* @@ -2597,6 +2727,28 @@ int cmd_inject(int argc, const char **argv) } } + if (inject.convert_callchain) { + struct evsel *evsel; + + if (inject.output.is_pipe || inject.session->data->is_pipe) { + pr_err("--convert-callchain cannot work with pipe\n"); + goto out_delete; + } + + evlist__for_each_entry(inject.session->evlist, evsel) { + if (!evsel__has_dwarf_callchain(evsel) && !evsel__is_dummy_event(evsel)) { + pr_err("--convert-callchain requires DWARF call graph.\n"); + goto out_delete; + } + } + + inject.raw_callchain = calloc(PERF_MAX_STACK_DEPTH, sizeof(u64)); + if (inject.raw_callchain == NULL) { + pr_err("callchain allocation failed\n"); + goto out_delete; + } + } + #ifdef HAVE_JITDUMP if (inject.jit_mode) { inject.tool.mmap2 = perf_event__repipe_mmap2; @@ -2627,5 +2779,6 @@ out_close_output: free(inject.itrace_synth_opts.vm_tm_corr_args); free(inject.event_copy); free(inject.guest_session.ev.event_buf); + free(inject.raw_callchain); return ret; } diff --git a/tools/perf/builtin-list.c b/tools/perf/builtin-list.c index caf42276bd0f..0ad687663d22 100644 --- a/tools/perf/builtin-list.c +++ b/tools/perf/builtin-list.c @@ -619,7 +619,7 @@ int cmd_list(int argc, const char **argv) } for (i = 0; i < argc; ++i) { - char *sep, *s; + char *s; if (strcmp(argv[i], "tracepoint") == 0) { char *old_pmu_glob = default_ps.pmu_glob; @@ -674,7 +674,7 @@ int cmd_list(int argc, const char **argv) else if (strcmp(argv[i], "pfm") == 0) print_libpfm_events(&print_cb, ps); #endif - else if ((sep = strchr(argv[i], ':')) != NULL) { + else if (strchr(argv[i], ':') != NULL) { char *old_pmu_glob = default_ps.pmu_glob; char *old_event_glob = default_ps.event_glob; diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index 69800e4d9530..1b4ba85ee019 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c @@ -211,8 +211,7 @@ static int opt_set_target_ns(const struct option *opt __maybe_unused, ns_pid = (pid_t)strtol(str, NULL, 10); if (errno != 0) { ret = -errno; - pr_warning("Failed to parse %s as a pid: %s\n", str, - strerror(errno)); + pr_warning("Failed to parse %s as a pid: %m\n", str); return ret; } nsip = nsinfo__new(ns_pid); diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index b1fb87016d5a..c82b9720c929 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -867,6 +867,7 @@ static int record__auxtrace_init(struct record *rec) } if (!rec->itr) { + err = -EINVAL; rec->itr = auxtrace_record__init(rec->evlist, &err); if (err) return err; @@ -1323,7 +1324,6 @@ static int record__mmap_evlist(struct record *rec, struct record_opts *opts = &rec->opts; bool auxtrace_overwrite = opts->auxtrace_snapshot_mode || opts->auxtrace_sample_mode; - char msg[512]; if (opts->affinity != PERF_AFFINITY_SYS) cpu__setup_cpunode_map(); @@ -1342,8 +1342,7 @@ static int record__mmap_evlist(struct record *rec, opts->mmap_pages, opts->auxtrace_mmap_pages); return -errno; } else { - pr_err("failed to mmap with %d (%s)\n", errno, - str_error_r(errno, msg, sizeof(msg))); + pr_err("failed to mmap: %m\n"); if (errno) return -errno; else @@ -1361,7 +1360,8 @@ static int record__mmap_evlist(struct record *rec, if (record__threads_enabled(rec)) { ret = perf_data__create_dir(&rec->data, evlist->core.nr_mmaps); if (ret) { - pr_err("Failed to create data directory: %s\n", strerror(-ret)); + errno = -ret; + pr_err("Failed to create data directory: %m\n"); return ret; } for (i = 0; i < evlist->core.nr_mmaps; i++) { @@ -1422,9 +1422,8 @@ try_again: } if (evlist__apply_filters(evlist, &pos, &opts->target)) { - pr_err("failed to set filter \"%s\" on event %s with %d (%s)\n", - pos->filter ?: "BPF", evsel__name(pos), errno, - str_error_r(errno, msg, sizeof(msg))); + pr_err("failed to set filter \"%s\" on event %s: %m\n", + pos->filter ?: "BPF", evsel__name(pos)); rc = -1; goto out; } @@ -1709,8 +1708,7 @@ static void *record__thread(void *arg) err = write(thread->pipes.ack[1], &msg, sizeof(msg)); if (err == -1) - pr_warning("threads[%d]: failed to notify on start: %s\n", - thread->tid, strerror(errno)); + pr_warning("threads[%d]: failed to notify on start: %m\n", thread->tid); pr_debug("threads[%d]: started on cpu%d\n", thread->tid, sched_getcpu()); @@ -1753,8 +1751,7 @@ static void *record__thread(void *arg) err = write(thread->pipes.ack[1], &msg, sizeof(msg)); if (err == -1) - pr_warning("threads[%d]: failed to notify on termination: %s\n", - thread->tid, strerror(errno)); + pr_warning("threads[%d]: failed to notify on termination: %m\n", thread->tid); return NULL; } @@ -2300,7 +2297,7 @@ static int record__start_threads(struct record *rec) sigfillset(&full); if (sigprocmask(SIG_SETMASK, &full, &mask)) { - pr_err("Failed to block signals on threads start: %s\n", strerror(errno)); + pr_err("Failed to block signals on threads start: %m\n"); return -1; } @@ -2318,7 +2315,7 @@ static int record__start_threads(struct record *rec) if (pthread_create(&handle, &attrs, record__thread, &thread_data[t])) { for (tt = 1; tt < t; tt++) record__terminate_thread(&thread_data[t]); - pr_err("Failed to start threads: %s\n", strerror(errno)); + pr_err("Failed to start threads: %m\n"); ret = -1; goto out_err; } @@ -2341,7 +2338,7 @@ out_err: pthread_attr_destroy(&attrs); if (sigprocmask(SIG_SETMASK, &mask, NULL)) { - pr_err("Failed to unblock signals on threads start: %s\n", strerror(errno)); + pr_err("Failed to unblock signals on threads start: %m\n"); ret = -1; } diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 35df04dad2fd..fd5bd3d97686 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -303,7 +303,8 @@ static int process_sample_event(const struct perf_tool *tool, if (symbol_conf.hide_unresolved && al.sym == NULL) goto out_put; - if (rep->cpu_list && !test_bit(sample->cpu, rep->cpu_bitmap)) + if (rep->cpu_list && (sample->cpu >= MAX_NR_CPUS || + !test_bit(sample->cpu, rep->cpu_bitmap))) goto out_put; if (sort__mode == SORT_MODE__BRANCH) { diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index eca3b1c58c4b..f2da112e49f5 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -52,6 +52,7 @@ #define COMM_LEN 20 #define SYM_LEN 129 #define MAX_PID 1024000 +#define PID_MAX_LIMIT 4194304 /* kernel limit on 64-bit */ #define MAX_PRIO 140 static const char *cpu_list; @@ -266,6 +267,7 @@ struct thread_runtime { u64 migrations; int prio; + bool color; }; /* per event run time data */ @@ -357,14 +359,25 @@ get_new_event(struct task_desc *task, u64 timestamp) struct sched_atom *event = zalloc(sizeof(*event)); unsigned long idx = task->nr_events; size_t size; + struct sched_atom **atoms_p; + + if (event == NULL) { + pr_err("ERROR: sched: failed to allocate event\n"); + return NULL; + } event->timestamp = timestamp; event->nr = idx; + size = sizeof(struct sched_atom *) * (task->nr_events + 1); + atoms_p = realloc(task->atoms, size); + if (!atoms_p) { + pr_err("ERROR: sched: failed to grow atoms array\n"); + free(event); + return NULL; + } + task->atoms = atoms_p; task->nr_events++; - size = sizeof(struct sched_atom *) * task->nr_events; - task->atoms = realloc(task->atoms, size); - BUG_ON(!task->atoms); task->atoms[idx] = event; @@ -395,6 +408,8 @@ static void add_sched_event_run(struct perf_sched *sched, struct task_desc *task } event = get_new_event(task, timestamp); + if (event == NULL) + return; event->type = SCHED_EVENT_RUN; event->duration = duration; @@ -408,6 +423,8 @@ static void add_sched_event_wakeup(struct perf_sched *sched, struct task_desc *t struct sched_atom *event, *wakee_event; event = get_new_event(task, timestamp); + if (event == NULL) + return; event->type = SCHED_EVENT_WAKEUP; event->wakee = wakee; @@ -422,6 +439,10 @@ static void add_sched_event_wakeup(struct perf_sched *sched, struct task_desc *t } wakee_event->wait_sem = zalloc(sizeof(*wakee_event->wait_sem)); + if (!wakee_event->wait_sem) { + pr_err("ERROR: sched: failed to allocate semaphore\n"); + return; + } sem_init(wakee_event->wait_sem, 0, 0); event->wait_sem = wakee_event->wait_sem; @@ -433,6 +454,9 @@ static void add_sched_event_sleep(struct perf_sched *sched, struct task_desc *ta { struct sched_atom *event = get_new_event(task, timestamp); + if (event == NULL) + return; + event->type = SCHED_EVENT_SLEEP; sched->nr_sleep_events++; @@ -441,17 +465,28 @@ static void add_sched_event_sleep(struct perf_sched *sched, struct task_desc *ta static struct task_desc *register_pid(struct perf_sched *sched, unsigned long pid, const char *comm) { - struct task_desc *task; + struct task_desc *task, **tasks_p; static int pid_max; + /* perf.data is untrusted — cap pid to prevent overflow in size calculations */ + if (pid >= PID_MAX_LIMIT) { + pr_err("pid %lu exceeds limit %d, skipping\n", pid, PID_MAX_LIMIT); + return NULL; + } + if (sched->pid_to_task == NULL) { if (sysctl__read_int("kernel/pid_max", &pid_max) < 0) pid_max = MAX_PID; - BUG_ON((sched->pid_to_task = calloc(pid_max, sizeof(struct task_desc *))) == NULL); + sched->pid_to_task = calloc(pid_max, sizeof(struct task_desc *)); + if (sched->pid_to_task == NULL) + return NULL; } if (pid >= (unsigned long)pid_max) { - BUG_ON((sched->pid_to_task = realloc(sched->pid_to_task, (pid + 1) * - sizeof(struct task_desc *))) == NULL); + void *p = realloc(sched->pid_to_task, (pid + 1) * sizeof(struct task_desc *)); + + if (p == NULL) + return NULL; + sched->pid_to_task = p; while (pid >= (unsigned long)pid_max) sched->pid_to_task[pid_max++] = NULL; } @@ -462,9 +497,11 @@ static struct task_desc *register_pid(struct perf_sched *sched, return task; task = zalloc(sizeof(*task)); + if (task == NULL) + return NULL; task->pid = pid; - task->nr = sched->nr_tasks; - strcpy(task->comm, comm); + if (comm) + strlcpy(task->comm, comm, sizeof(task->comm)); /* * every task starts in sleeping state - this gets ignored * if there's no wakeup pointing to this sleep state: @@ -472,10 +509,12 @@ static struct task_desc *register_pid(struct perf_sched *sched, add_sched_event_sleep(sched, task, 0); sched->pid_to_task[pid] = task; - sched->nr_tasks++; - sched->tasks = realloc(sched->tasks, sched->nr_tasks * sizeof(struct task_desc *)); - BUG_ON(!sched->tasks); - sched->tasks[task->nr] = task; + tasks_p = realloc(sched->tasks, (sched->nr_tasks + 1) * sizeof(struct task_desc *)); + if (!tasks_p) + return NULL; + sched->tasks = tasks_p; + sched->tasks[sched->nr_tasks] = task; + task->nr = sched->nr_tasks++; if (verbose > 0) printf("registered task #%ld, PID %ld (%s)\n", sched->nr_tasks, pid, comm); @@ -834,6 +873,8 @@ replay_wakeup_event(struct perf_sched *sched, waker = register_pid(sched, sample->tid, "<unknown>"); wakee = register_pid(sched, pid, comm); + if (waker == NULL || wakee == NULL) + return -1; add_sched_event_wakeup(sched, waker, sample->time, wakee); return 0; @@ -875,6 +916,8 @@ static int replay_switch_event(struct perf_sched *sched, prev = register_pid(sched, prev_pid, prev_comm); next = register_pid(sched, next_pid, next_comm); + if (prev == NULL || next == NULL) + return -1; sched->cpu_last_switched[cpu] = timestamp; @@ -1170,7 +1213,7 @@ static int latency_switch_event(struct perf_sched *sched, } } if (add_sched_out_event(out_events, prev_state, timestamp)) - return -1; + goto out_put; in_events = thread_atoms_search(&sched->atom_root, sched_in, &sched->cmp_pid); if (!in_events) { @@ -1534,22 +1577,32 @@ static int process_sched_wakeup_ignore(const struct perf_tool *tool __maybe_unus static bool thread__has_color(struct thread *thread) { - return thread__priv(thread) != NULL; + struct thread_runtime *tr = thread__priv(thread); + + return tr != NULL && tr->color; } static struct thread* map__findnew_thread(struct perf_sched *sched, struct machine *machine, pid_t pid, pid_t tid) { struct thread *thread = machine__findnew_thread(machine, pid, tid); - bool color = false; - if (!sched->map.color_pids || !thread || thread__priv(thread)) + if (!sched->map.color_pids || !thread) return thread; - if (thread_map__has(sched->map.color_pids, tid)) - color = true; + /* + * Always check the color-pids map, even if thread__priv() is + * already set. COMM events processed before the first sched_switch + * allocate a thread_runtime via thread__get_runtime(), so priv is + * non-NULL before we ever get here. Skipping the check on non-NULL + * priv would prevent those threads from being colored. + */ + if (thread_map__has(sched->map.color_pids, tid)) { + struct thread_runtime *tr = thread__get_runtime(thread); - thread__set_priv(thread, color ? ((void*)1) : NULL); + if (tr) + tr->color = true; + } return thread; } @@ -2168,7 +2221,8 @@ static void timehist_print_sample(struct perf_sched *sched, char nstr[30]; u64 wait_time; - if (cpu_list && !test_bit(sample->cpu, cpu_bitmap)) + if (cpu_list && (sample->cpu >= MAX_NR_CPUS || + !test_bit(sample->cpu, cpu_bitmap))) return; timestamp__scnprintf_usec(t, tstr, sizeof(tstr)); @@ -2435,7 +2489,7 @@ static void free_idle_threads(void) if (itr) thread__put(itr->last_thread); - thread__delete(idle); + thread__put(idle); } } @@ -2468,8 +2522,11 @@ static struct thread *get_idle_thread(int cpu) idle_threads[cpu] = thread__new(0, 0); if (idle_threads[cpu]) { - if (init_idle_thread(idle_threads[cpu]) < 0) + if (init_idle_thread(idle_threads[cpu]) < 0) { + /* clean up so next call doesn't find a half-initialized thread */ + thread__zput(idle_threads[cpu]); return NULL; + } } } @@ -2567,7 +2624,9 @@ static bool timehist_skip_sample(struct perf_sched *sched, else if (evsel__name_is(evsel, "sched:sched_switch")) prio = evsel__intval(evsel, sample, "prev_prio"); - if (prio != -1 && !test_bit(prio, sched->prio_bitmap)) { + /* negative prio means no info; out-of-range prio can't match the filter */ + if (prio >= 0 && + (prio >= MAX_PRIO || !test_bit(prio, sched->prio_bitmap))) { rc = true; sched->skipped_samples++; } @@ -2850,7 +2909,8 @@ static int timehist_sched_change_event(const struct perf_tool *tool, } if (!sched->idle_hist || thread__tid(thread) == 0) { - if (!cpu_list || test_bit(sample->cpu, cpu_bitmap)) + if (!cpu_list || (sample->cpu < MAX_NR_CPUS && + test_bit(sample->cpu, cpu_bitmap))) timehist_update_runtime_stats(tr, t, tprev); if (sched->idle_hist) { @@ -3044,7 +3104,8 @@ static size_t timehist_print_idlehist_callchain(struct rb_root_cached *root) size_t ret = 0; FILE *fp = stdout; struct callchain_node *chain; - struct rb_node *rb_node = rb_first_cached(root); + /* sort() uses rb_insert_color() on rb_root, not rb_root_cached */ + struct rb_node *rb_node = rb_first(&root->rb_root); printf(" %16s %8s %s\n", "Idle time (msec)", "Count", "Callchains"); printf(" %.16s %.8s %.50s\n", graph_dotted_line, graph_dotted_line, @@ -3186,7 +3247,9 @@ static int perf_timehist__process_sample(const struct perf_tool *tool, .cpu = sample->cpu, }; - if (this_cpu.cpu > sched->max_cpu.cpu) + /* max_cpu indexes arrays allocated with MAX_CPUS entries */ + if (this_cpu.cpu >= 0 && this_cpu.cpu < MAX_CPUS && + this_cpu.cpu > sched->max_cpu.cpu) sched->max_cpu = this_cpu; if (evsel->handler != NULL) { @@ -3292,6 +3355,7 @@ static int perf_sched__timehist(struct perf_sched *sched) */ sched->tool.sample = perf_timehist__process_sample; sched->tool.mmap = perf_event__process_mmap; + sched->tool.mmap2 = perf_event__process_mmap2; sched->tool.comm = perf_event__process_comm; sched->tool.exit = perf_event__process_exit; sched->tool.fork = perf_event__process_fork; @@ -3355,8 +3419,8 @@ static int perf_sched__timehist(struct perf_sched *sched) perf_session__set_tracepoints_handlers(session, migrate_handlers)) goto out; - /* pre-allocate struct for per-CPU idle stats */ - sched->max_cpu.cpu = env->nr_cpus_online; + /* pre-allocate struct for per-CPU idle stats; cap to array bounds */ + sched->max_cpu.cpu = min(env->nr_cpus_online, MAX_CPUS); if (sched->max_cpu.cpu == 0) sched->max_cpu.cpu = 4; if (init_idle_threads(sched->max_cpu.cpu)) diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 9eb0876633c0..1202ceb425f0 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -868,9 +868,8 @@ static int __run_perf_stat(int argc, const char **argv, int run_idx) } if (evlist__apply_filters(evsel_list, &counter, &target)) { - pr_err("failed to set filter \"%s\" on event %s with %d (%s)\n", - counter->filter, evsel__name(counter), errno, - str_error_r(errno, msg, sizeof(msg))); + pr_err("failed to set filter \"%s\" on event %s: %m\n", + counter->filter, evsel__name(counter)); return -1; } @@ -932,8 +931,8 @@ static int __run_perf_stat(int argc, const char **argv, int run_idx) } if (workload_exec_errno) { - const char *emsg = str_error_r(workload_exec_errno, msg, sizeof(msg)); - pr_err("Workload failed: %s\n", emsg); + errno = workload_exec_errno; + pr_err("Workload failed: %m\n"); err = -1; goto err_out; } @@ -1564,9 +1563,10 @@ static struct aggr_cpu_id perf_env__get_socket_aggr_by_cpu(struct perf_cpu cpu, { struct perf_env *env = data; struct aggr_cpu_id id = aggr_cpu_id__empty(); + struct cpu_topology_map *topo = perf_env__get_cpu_topology(env, cpu); - if (cpu.cpu != -1) - id.socket = env->cpu[cpu.cpu].socket_id; + if (topo) + id.socket = topo->socket_id; return id; } @@ -1575,15 +1575,16 @@ static struct aggr_cpu_id perf_env__get_die_aggr_by_cpu(struct perf_cpu cpu, voi { struct perf_env *env = data; struct aggr_cpu_id id = aggr_cpu_id__empty(); + struct cpu_topology_map *topo = perf_env__get_cpu_topology(env, cpu); - if (cpu.cpu != -1) { + if (topo) { /* * die_id is relative to socket, so start * with the socket ID and then add die to * make a unique ID. */ - id.socket = env->cpu[cpu.cpu].socket_id; - id.die = env->cpu[cpu.cpu].die_id; + id.socket = topo->socket_id; + id.die = topo->die_id; } return id; @@ -1631,12 +1632,13 @@ static struct aggr_cpu_id perf_env__get_cache_aggr_by_cpu(struct perf_cpu cpu, { struct perf_env *env = data; struct aggr_cpu_id id = aggr_cpu_id__empty(); + struct cpu_topology_map *topo = perf_env__get_cpu_topology(env, cpu); - if (cpu.cpu != -1) { + if (topo) { u32 cache_level = (perf_stat.aggr_level) ?: stat_config.aggr_level; - id.socket = env->cpu[cpu.cpu].socket_id; - id.die = env->cpu[cpu.cpu].die_id; + id.socket = topo->socket_id; + id.die = topo->die_id; perf_env__get_cache_id_for_cpu(cpu, env, cache_level, &id); } @@ -1648,11 +1650,12 @@ static struct aggr_cpu_id perf_env__get_cluster_aggr_by_cpu(struct perf_cpu cpu, { struct perf_env *env = data; struct aggr_cpu_id id = aggr_cpu_id__empty(); + struct cpu_topology_map *topo = perf_env__get_cpu_topology(env, cpu); - if (cpu.cpu != -1) { - id.socket = env->cpu[cpu.cpu].socket_id; - id.die = env->cpu[cpu.cpu].die_id; - id.cluster = env->cpu[cpu.cpu].cluster_id; + if (topo) { + id.socket = topo->socket_id; + id.die = topo->die_id; + id.cluster = topo->cluster_id; } return id; @@ -1662,16 +1665,17 @@ static struct aggr_cpu_id perf_env__get_core_aggr_by_cpu(struct perf_cpu cpu, vo { struct perf_env *env = data; struct aggr_cpu_id id = aggr_cpu_id__empty(); + struct cpu_topology_map *topo = perf_env__get_cpu_topology(env, cpu); - if (cpu.cpu != -1) { + if (topo) { /* * core_id is relative to socket, die and cluster, we need a * global id. So we set socket, die id, cluster id and core id. */ - id.socket = env->cpu[cpu.cpu].socket_id; - id.die = env->cpu[cpu.cpu].die_id; - id.cluster = env->cpu[cpu.cpu].cluster_id; - id.core = env->cpu[cpu.cpu].core_id; + id.socket = topo->socket_id; + id.die = topo->die_id; + id.cluster = topo->cluster_id; + id.core = topo->core_id; } return id; @@ -1681,18 +1685,19 @@ static struct aggr_cpu_id perf_env__get_cpu_aggr_by_cpu(struct perf_cpu cpu, voi { struct perf_env *env = data; struct aggr_cpu_id id = aggr_cpu_id__empty(); + struct cpu_topology_map *topo = perf_env__get_cpu_topology(env, cpu); - if (cpu.cpu != -1) { + if (topo) { /* * core_id is relative to socket and die, * we need a global id. So we set * socket, die id and core id */ - id.socket = env->cpu[cpu.cpu].socket_id; - id.die = env->cpu[cpu.cpu].die_id; - id.core = env->cpu[cpu.cpu].core_id; - id.cpu = cpu; + id.socket = topo->socket_id; + id.die = topo->die_id; + id.core = topo->core_id; } + id.cpu = cpu; return id; } diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 96efe1fba854..2be8c3ed93c9 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -2595,12 +2595,10 @@ static struct syscall *trace__syscall_info(struct trace *trace, struct evsel *ev err = syscall__read_info(sc, trace); if (err && verbose > 0) { - char sbuf[STRERR_BUFSIZE]; - - fprintf(trace->output, "Problems reading syscall %d: %d (%s)", id, -err, - str_error_r(-err, sbuf, sizeof(sbuf))); + errno = -err; + fprintf(trace->output, "Problems reading syscall %d: %m", id); if (sc && sc->name) - fprintf(trace->output, "(%s)", sc->name); + fprintf(trace->output, " (%s)", sc->name); fputs(" information\n", trace->output); } return err ? NULL : sc; @@ -4652,9 +4650,8 @@ out_error: out_error_apply_filters: fprintf(trace->output, - "Failed to set filter \"%s\" on event %s with %d (%s)\n", - evsel->filter, evsel__name(evsel), errno, - str_error_r(errno, errbuf, sizeof(errbuf))); + "Failed to set filter \"%s\" on event %s: %m\n", + evsel->filter, evsel__name(evsel)); goto out_delete_evlist; } out_error_mem: @@ -4662,7 +4659,7 @@ out_error_mem: goto out_delete_evlist; out_errno: - fprintf(trace->output, "errno=%d,%s\n", errno, strerror(errno)); + fprintf(trace->output, "%m\n"); goto out_delete_evlist; } @@ -5152,8 +5149,8 @@ static int trace__parse_events_option(const struct option *opt, const char *str, int unset __maybe_unused) { struct trace *trace = (struct trace *)opt->value; - const char *s = str; - char *sep = NULL, *lists[2] = { NULL, NULL, }; + const char *s; + char *strd, *sep = NULL, *lists[2] = { NULL, NULL, }; int len = strlen(str) + 1, err = -1, list, idx; char *strace_groups_dir = system_path(STRACE_GROUPS_DIR); char group_name[PATH_MAX]; @@ -5162,13 +5159,17 @@ static int trace__parse_events_option(const struct option *opt, const char *str, if (strace_groups_dir == NULL) return -1; + s = strd = strdup(str); + if (strd == NULL) + return -1; + if (*s == '!') { ++s; trace->not_ev_qualifier = true; } while (1) { - if ((sep = strchr(s, ',')) != NULL) + if ((sep = strchr((char *)s, ',')) != NULL) *sep = '\0'; list = 0; @@ -5236,8 +5237,7 @@ out: free(strace_groups_dir); free(lists[0]); free(lists[1]); - if (sep) - *sep = ','; + free(strd); return err; } diff --git a/tools/perf/jvmti/libjvmti.c b/tools/perf/jvmti/libjvmti.c index 82514e6532b8..87bfd4781003 100644 --- a/tools/perf/jvmti/libjvmti.c +++ b/tools/perf/jvmti/libjvmti.c @@ -142,7 +142,7 @@ copy_class_filename(const char * class_sign, const char * file_name, char * resu */ if (*class_sign == 'L') { size_t j, i = 0; - char *p = strrchr(class_sign, '/'); + const char *p = strrchr(class_sign, '/'); if (p) { /* drop the 'L' prefix and copy up to the final '/' */ for (i = 0; i < (size_t)(p - class_sign); i++) diff --git a/tools/perf/perf.c b/tools/perf/perf.c index 88c60ecf3395..f475a8664ffc 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c @@ -169,8 +169,8 @@ static int set_debug_file(const char *path) { debug_fp = fopen(path, "w"); if (!debug_fp) { - fprintf(stderr, "Open debug file '%s' failed: %s\n", - path, strerror(errno)); + fprintf(stderr, "Open debug file '%s' failed: %m\n", + path); return -1; } @@ -335,7 +335,6 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv) { int status; struct stat st; - char sbuf[STRERR_BUFSIZE]; if (use_browser == -1) use_browser = check_browser_config(p->cmd); @@ -363,17 +362,15 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv) status = 1; /* Check for ENOSPC and EIO errors.. */ if (fflush(stdout)) { - fprintf(stderr, "write failure on standard output: %s", - str_error_r(errno, sbuf, sizeof(sbuf))); + fprintf(stderr, "write failure on standard output: %m\n"); goto out; } if (ferror(stdout)) { - fprintf(stderr, "unknown write failure on standard output"); + fprintf(stderr, "unknown write failure on standard output\n"); goto out; } if (fclose(stdout)) { - fprintf(stderr, "close failed on standard output: %s", - str_error_r(errno, sbuf, sizeof(sbuf))); + fprintf(stderr, "close failed on standard output: %m\n"); goto out; } status = 0; @@ -459,7 +456,6 @@ int main(int argc, const char **argv) { int err, done_help = 0; const char *cmd; - char sbuf[STRERR_BUFSIZE]; perf_debug_setup(); @@ -573,8 +569,8 @@ int main(int argc, const char **argv) } if (cmd) { - fprintf(stderr, "Failed to run command '%s': %s\n", - cmd, str_error_r(errno, sbuf, sizeof(sbuf))); + fprintf(stderr, "Failed to run command '%s': %m\n", + cmd); } out: if (debug_fp) diff --git a/tools/perf/tests/code-reading.c b/tools/perf/tests/code-reading.c index 4c9fbf6965c4..4e759b898980 100644 --- a/tools/perf/tests/code-reading.c +++ b/tools/perf/tests/code-reading.c @@ -465,8 +465,11 @@ static int read_object_code(u64 addr, size_t len, u8 cpumode, goto out; } - decomp = true; - objdump_name = decomp_name; + /* empty pathname means file wasn't actually compressed */ + if (decomp_name[0] != '\0') { + decomp = true; + objdump_name = decomp_name; + } } /* Read the object code using objdump */ diff --git a/tools/perf/tests/pe-file-parsing.c b/tools/perf/tests/pe-file-parsing.c index 8b31d1d05f90..30c7da79e109 100644 --- a/tools/perf/tests/pe-file-parsing.c +++ b/tools/perf/tests/pe-file-parsing.c @@ -37,7 +37,7 @@ static int run_dir(const char *d) size_t idx; scnprintf(filename, PATH_MAX, "%s/pe-file.exe", d); - ret = filename__read_build_id(filename, &bid, /*block=*/true); + ret = filename__read_build_id(filename, &bid); TEST_ASSERT_VAL("Failed to read build_id", ret == sizeof(expect_build_id)); TEST_ASSERT_VAL("Wrong build_id", !memcmp(bid.data, expect_build_id, @@ -49,7 +49,7 @@ static int run_dir(const char *d) !strcmp(debuglink, expect_debuglink)); scnprintf(debugfile, PATH_MAX, "%s/%s", d, debuglink); - ret = filename__read_build_id(debugfile, &bid, /*block=*/true); + ret = filename__read_build_id(debugfile, &bid); TEST_ASSERT_VAL("Failed to read debug file build_id", ret == sizeof(expect_build_id)); TEST_ASSERT_VAL("Wrong build_id", !memcmp(bid.data, expect_build_id, diff --git a/tools/perf/tests/pmu-events.c b/tools/perf/tests/pmu-events.c index 95fd9f671a22..5adac4f7d94c 100644 --- a/tools/perf/tests/pmu-events.c +++ b/tools/perf/tests/pmu-events.c @@ -15,6 +15,7 @@ #include "util/expr.h" #include "util/hashmap.h" #include "util/parse-events.h" +#include "util/tool_pmu.h" #include "metricgroup.h" #include "stat.h" @@ -837,6 +838,26 @@ struct metric { struct metric_ref metric_ref; }; +static bool is_expected_broken_metric(const struct pmu_metric *pm) +{ + if (!strcmp(pm->metric_name, "M1") || !strcmp(pm->metric_name, "M2") || + !strcmp(pm->metric_name, "M3")) + return true; + +#if defined(__aarch64__) + /* + * Arm64 platforms may return "#slots == 0", which is treated as a + * syntax error by the parser. Don't test these metrics when running + * on such platforms. + */ + if (strstr(pm->metric_expr, "#slots") && + !tool_pmu__cpu_slots_per_cycle()) + return true; +#endif + + return false; +} + static int test__parsing_callback(const struct pmu_metric *pm, const struct pmu_metrics_table *table, void *data) @@ -872,8 +893,7 @@ static int test__parsing_callback(const struct pmu_metric *pm, err = metricgroup__parse_groups_test(evlist, table, pm->metric_name); if (err) { - if (!strcmp(pm->metric_name, "M1") || !strcmp(pm->metric_name, "M2") || - !strcmp(pm->metric_name, "M3")) { + if (is_expected_broken_metric(pm)) { (*failures)--; pr_debug("Expected broken metric %s skipping\n", pm->metric_name); err = 0; diff --git a/tools/perf/tests/sdt.c b/tools/perf/tests/sdt.c index 6132f1af3e22..93baee2eae42 100644 --- a/tools/perf/tests/sdt.c +++ b/tools/perf/tests/sdt.c @@ -31,7 +31,7 @@ static int build_id_cache__add_file(const char *filename) struct build_id bid = { .size = 0, }; int err; - err = filename__read_build_id(filename, &bid, /*block=*/true); + err = filename__read_build_id(filename, &bid); if (err < 0) { pr_debug("Failed to read build id of %s\n", filename); return err; diff --git a/tools/perf/ui/browser.c b/tools/perf/ui/browser.c index dc88427b4ae5..321187b204d3 100644 --- a/tools/perf/ui/browser.c +++ b/tools/perf/ui/browser.c @@ -513,6 +513,9 @@ unsigned int ui_browser__list_head_refresh(struct ui_browser *browser) struct list_head *head = browser->entries; int row = 0; + if (browser->nr_entries == 0) + return 0; + if (browser->top == NULL || browser->top == browser->entries) browser->top = ui_browser__list_head_filter_entries(browser, head->next); diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c index 8fe699f98542..97f92512f136 100644 --- a/tools/perf/ui/browsers/annotate.c +++ b/tools/perf/ui/browsers/annotate.c @@ -449,6 +449,9 @@ static bool annotate_browser__toggle_source(struct annotate_browser *browser, struct annotation_line *al; off_t offset = browser->b.index - browser->b.top_idx; + if (browser->b.nr_entries == 0) + return false; + browser->b.seek(&browser->b, offset, SEEK_CUR); al = list_entry(browser->b.top, struct annotation_line, node); @@ -542,8 +545,8 @@ static void annotate_browser__show_full_location(struct ui_browser *browser) static void ui_browser__init_asm_mode(struct ui_browser *browser) { struct annotation *notes = browser__annotation(browser); - ui_browser__reset_index(browser); browser->nr_entries = notes->src->nr_asm_entries; + ui_browser__reset_index(browser); } static int sym_title(struct symbol *sym, struct map *map, char *title, diff --git a/tools/perf/util/bpf-event.c b/tools/perf/util/bpf-event.c index 2298cd396c42..2294336f6e60 100644 --- a/tools/perf/util/bpf-event.c +++ b/tools/perf/util/bpf-event.c @@ -36,7 +36,7 @@ static int snprintf_hex(char *buf, size_t size, unsigned char *data, size_t len) size_t i; for (i = 0; i < len; i++) - ret += snprintf(buf + ret, size - ret, "%02x", data[i]); + ret += scnprintf(buf + ret, size - ret, "%02x", data[i]); return ret; } @@ -59,6 +59,10 @@ static int machine__process_bpf_event_load(struct machine *machine, return 0; info_linear = info_node->info_linear; + /* jited_ksyms is only valid if bpil_offs_to_addr() converted it */ + if (!(info_linear->arrays & (1UL << PERF_BPIL_JITED_KSYMS))) + return 0; + for (i = 0; i < info_linear->info.nr_jited_ksyms; i++) { u64 *addrs = (u64 *)(uintptr_t)(info_linear->info.jited_ksyms); u64 addr = addrs[i]; @@ -140,22 +144,26 @@ static int synthesize_bpf_prog_name(char *buf, int size, const struct btf_type *t; int name_len; - name_len = snprintf(buf, size, "bpf_prog_"); + name_len = scnprintf(buf, size, "bpf_prog_"); name_len += snprintf_hex(buf + name_len, size - name_len, prog_tags[sub_id], BPF_TAG_SIZE); - if (btf) { + if (btf && + info->func_info_rec_size >= sizeof(*finfo) && + sub_id < info->nr_func_info) { finfo = func_infos + sub_id * info->func_info_rec_size; t = btf__type_by_id(btf, finfo->type_id); - short_name = btf__name_by_offset(btf, t->name_off); + if (t) + short_name = btf__name_by_offset(btf, t->name_off); } else if (sub_id == 0 && sub_prog_cnt == 1) { /* no subprog */ if (info->name[0]) short_name = info->name; } else short_name = "F"; - if (short_name) - name_len += snprintf(buf + name_len, size - name_len, - "_%s", short_name); + if (short_name) { + name_len += scnprintf(buf + name_len, size - name_len, + "_%s", short_name); + } return name_len; } @@ -365,6 +373,15 @@ static struct bpf_metadata *bpf_metadata_alloc(__u32 nr_prog_tags, event_size = sizeof(metadata->event->bpf_metadata) + nr_variables * sizeof(metadata->event->bpf_metadata.entries[0]); + /* + * header.size is __u16. synthesize_perf_record_bpf_metadata() + * adds machine->id_hdr_size (up to ~64 bytes) after this, so + * leave headroom to prevent the final size from wrapping. + */ + if (event_size > UINT16_MAX - 256) { + bpf_metadata_free(metadata); + return NULL; + } metadata->event = zalloc(event_size); if (!metadata->event) { bpf_metadata_free(metadata); @@ -393,8 +410,10 @@ static struct bpf_metadata *bpf_metadata_create(struct bpf_prog_info *info) continue; metadata = bpf_metadata_alloc(info->nr_prog_tags, map.num_vars); - if (!metadata) + if (!metadata) { + bpf_metadata_free_map_data(&map); continue; + } bpf_metadata_fill_event(&map, &metadata->event->bpf_metadata); @@ -733,7 +752,8 @@ kallsyms_process_symbol(void *data, const char *_name, char type __maybe_unused, u64 start) { char disp[KSYM_NAME_LEN]; - char *module, *name; + const char *module; + char *name; unsigned long id; int err = 0; @@ -787,11 +807,10 @@ int perf_event__synthesize_bpf_events(struct perf_session *session, err = 0; break; } - pr_debug("%s: can't get next program: %s%s\n", - __func__, strerror(errno), - errno == EINVAL ? " -- kernel too old?" : ""); /* don't report error on old kernel or EPERM */ err = (errno == EINVAL || errno == EPERM) ? 0 : -1; + pr_debug("%s: can\'t get next program: %m%s\n", + __func__, errno == EINVAL ? " -- kernel too old?" : ""); break; } fd = bpf_prog_get_fd_by_id(id); @@ -824,10 +843,8 @@ int perf_event__synthesize_bpf_events(struct perf_session *session, .tool = session->tool, }; - if (kallsyms__parse(kallsyms_filename, &arg, kallsyms_process_symbol)) { - pr_err("%s: failed to synthesize bpf images: %s\n", - __func__, strerror(errno)); - } + if (kallsyms__parse(kallsyms_filename, &arg, kallsyms_process_symbol)) + pr_err("%s: failed to synthesize bpf images: %m\n", __func__); free(event); return err; @@ -871,6 +888,7 @@ static int perf_env__add_bpf_info(struct perf_env *env, u32 id) if (!perf_env__insert_bpf_prog_info(env, info_node)) { pr_debug("%s: duplicate add bpf info request for id %u\n", __func__, btf_id); + bpf_metadata_free(info_node->metadata); free(info_linear); free(info_node); goto out; @@ -945,12 +963,15 @@ int evlist__add_bpf_sb_event(struct evlist *evlist, struct perf_env *env) return evlist__add_sb_event(evlist, &attr, bpf_event__sb_cb, env); } -void __bpf_event__print_bpf_prog_info(struct bpf_prog_info *info, +void __bpf_event__print_bpf_prog_info(struct perf_bpil *info_linear, struct perf_env *env, FILE *fp) { - __u32 *prog_lens = (__u32 *)(uintptr_t)(info->jited_func_lens); - __u64 *prog_addrs = (__u64 *)(uintptr_t)(info->jited_ksyms); + struct bpf_prog_info *info = &info_linear->info; + __u64 required_arrays = (1UL << PERF_BPIL_JITED_KSYMS) | + (1UL << PERF_BPIL_JITED_FUNC_LENS); + __u32 *prog_lens; + __u64 *prog_addrs; char name[KSYM_NAME_LEN]; struct btf *btf = NULL; u32 sub_prog_cnt, i; @@ -960,6 +981,13 @@ void __bpf_event__print_bpf_prog_info(struct bpf_prog_info *info, sub_prog_cnt != info->nr_jited_func_lens) return; + /* Ensure the arrays were present and converted by bpil_offs_to_addr() */ + if ((info_linear->arrays & required_arrays) != required_arrays) + return; + + prog_lens = (__u32 *)(uintptr_t)(info->jited_func_lens); + prog_addrs = (__u64 *)(uintptr_t)(info->jited_ksyms); + if (info->btf_id) { struct btf_node *node; diff --git a/tools/perf/util/bpf-event.h b/tools/perf/util/bpf-event.h index 60d2c6637af5..da4eeb4a1a73 100644 --- a/tools/perf/util/bpf-event.h +++ b/tools/perf/util/bpf-event.h @@ -40,7 +40,7 @@ struct btf_node { int machine__process_bpf(struct machine *machine, union perf_event *event, struct perf_sample *sample); int evlist__add_bpf_sb_event(struct evlist *evlist, struct perf_env *env); -void __bpf_event__print_bpf_prog_info(struct bpf_prog_info *info, +void __bpf_event__print_bpf_prog_info(struct perf_bpil *info_linear, struct perf_env *env, FILE *fp); void bpf_metadata_free(struct bpf_metadata *metadata); @@ -58,7 +58,7 @@ static inline int evlist__add_bpf_sb_event(struct evlist *evlist __maybe_unused, return 0; } -static inline void __bpf_event__print_bpf_prog_info(struct bpf_prog_info *info __maybe_unused, +static inline void __bpf_event__print_bpf_prog_info(struct perf_bpil *info_linear __maybe_unused, struct perf_env *env __maybe_unused, FILE *fp __maybe_unused) { diff --git a/tools/perf/util/bpf-utils.c b/tools/perf/util/bpf-utils.c index 5a66dc8594aa..69148197b1ef 100644 --- a/tools/perf/util/bpf-utils.c +++ b/tools/perf/util/bpf-utils.c @@ -123,7 +123,7 @@ get_bpf_prog_info_linear(int fd, __u64 arrays) /* step 1: get array dimensions */ err = bpf_obj_get_info_by_fd(fd, &info, &info_len); if (err) { - pr_debug("can't get prog info: %s", strerror(errno)); + pr_debug("can't get prog info: %m\n"); return ERR_PTR(-EFAULT); } if (info.type >= __MAX_BPF_PROG_TYPE) @@ -186,7 +186,7 @@ get_bpf_prog_info_linear(int fd, __u64 arrays) /* step 5: call syscall again to get required arrays */ err = bpf_obj_get_info_by_fd(fd, &info_linear->info, &info_len); if (err) { - pr_debug("can't get prog info: %s", strerror(errno)); + pr_debug("can't get prog info: %m\n"); free(info_linear); return ERR_PTR(-EFAULT); } @@ -264,12 +264,28 @@ void bpil_offs_to_addr(struct perf_bpil *info_linear) for (i = PERF_BPIL_FIRST_ARRAY; i < PERF_BPIL_LAST_ARRAY; ++i) { const struct bpil_array_desc *desc = &bpil_array_desc[i]; __u64 addr, offs; + __u32 count, size; if ((info_linear->arrays & (1UL << i)) == 0) continue; offs = bpf_prog_info_read_offset_u64(&info_linear->info, desc->array_offset); + count = bpf_prog_info_read_offset_u32(&info_linear->info, + desc->count_offset); + size = bpf_prog_info_read_offset_u32(&info_linear->info, + desc->size_offset); + /* offset and extent from perf.data are untrusted — keep within data[] */ + if (offs >= info_linear->data_len || + (u64)count * size > info_linear->data_len - offs) { + bpf_prog_info_set_offset_u64(&info_linear->info, + desc->array_offset, 0); + bpf_prog_info_set_offset_u32(&info_linear->info, + desc->count_offset, 0); + /* clear the bit so bpil_addr_to_offs() won't reverse a zeroed address */ + info_linear->arrays &= ~(1UL << i); + continue; + } addr = offs + ptr_to_u64(info_linear->data); bpf_prog_info_set_offset_u64(&info_linear->info, desc->array_offset, addr); diff --git a/tools/perf/util/bpf_lock_contention.c b/tools/perf/util/bpf_lock_contention.c index 7b5671f13c53..788d30be2058 100644 --- a/tools/perf/util/bpf_lock_contention.c +++ b/tools/perf/util/bpf_lock_contention.c @@ -42,7 +42,7 @@ static void check_slab_cache_iter(struct lock_contention *con) con->btf = btf__load_vmlinux_btf(); if (con->btf == NULL) { - pr_debug("BTF loading failed: %s\n", strerror(errno)); + pr_debug("BTF loading failed: %m\n"); return; } diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index 35505a1ffd11..fdb35133fde4 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c @@ -122,7 +122,7 @@ int filename__snprintf_build_id(const char *pathname, char *sbuild_id, size_t sb struct build_id bid = { .size = 0, }; int ret; - ret = filename__read_build_id(pathname, &bid, /*block=*/true); + ret = filename__read_build_id(pathname, &bid); if (ret < 0) return ret; @@ -848,7 +848,7 @@ static int filename__read_build_id_ns(const char *filename, int ret; nsinfo__mountns_enter(nsi, &nsc); - ret = filename__read_build_id(filename, bid, /*block=*/true); + ret = filename__read_build_id(filename, bid); nsinfo__mountns_exit(&nsc); return ret; diff --git a/tools/perf/util/cap.c b/tools/perf/util/cap.c index 24a0ea7e6d97..ac6d1d9a523d 100644 --- a/tools/perf/util/cap.c +++ b/tools/perf/util/cap.c @@ -28,8 +28,7 @@ bool perf_cap__capable(int cap, bool *used_root) header.version == _LINUX_CAPABILITY_VERSION_1) continue; - pr_debug2("capget syscall failed (%s - %d) fall back on root check\n", - strerror(errno), errno); + pr_debug2("capget syscall failed (%m) fall back on root check\n"); *used_root = true; return geteuid() == 0; } diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c index 89570397a4b3..d243eee11542 100644 --- a/tools/perf/util/cpumap.c +++ b/tools/perf/util/cpumap.c @@ -420,6 +420,12 @@ static int get_max_num(char *path, int *max) buf[num] = '\0'; + /* empty file — nothing to parse */ + if (num == 0) { + err = -1; + goto out; + } + /* start on the right, to find highest node num */ while (--num) { if ((buf[num] == ',') || (buf[num] == '-')) { @@ -466,6 +472,16 @@ static void set_max_cpu_num(void) if (ret) goto out; + /* + * struct perf_cpu.cpu is int16_t (libperf ABI) — clamp to avoid + * truncation to negative. See tools/lib/perf/TODO for the ABI + * widening plan. + */ + if (max > INT16_MAX) { + pr_warning("WARNING: max possible cpus %d exceeds int16_t, clamping to %d\n", + max, INT16_MAX); + max = INT16_MAX; + } max_cpu_num.cpu = max; /* get the highest present cpu number for a sparse allocation */ @@ -478,11 +494,12 @@ static void set_max_cpu_num(void) ret = get_max_num(path, &max); if (!ret && max > INT16_MAX) { - pr_err("Read out of bounds max cpus of %d\n", max); - ret = -1; + pr_warning("WARNING: max present cpus %d exceeds int16_t, clamping to %d\n", + max, INT16_MAX); + max = INT16_MAX; } if (!ret) - max_present_cpu_num.cpu = (int16_t)max; + max_present_cpu_num.cpu = max; out: if (ret) pr_err("Failed to read max cpus, using default of %d\n", max_cpu_num.cpu); @@ -548,6 +565,10 @@ int cpu__get_node(struct perf_cpu cpu) return -1; } + /* cpunode_map allocated for max_cpu_num entries; input may be untrusted */ + if (cpu.cpu < 0 || cpu.cpu >= max_cpu_num.cpu) + return -1; + return cpunode_map[cpu.cpu]; } @@ -615,7 +636,9 @@ int cpu__setup_cpunode_map(void) while ((dent2 = readdir(dir2)) != NULL) { if (dent2->d_type != DT_LNK || sscanf(dent2->d_name, "cpu%u", &cpu) < 1) continue; - cpunode_map[cpu] = mem; + /* cpunode_map allocated for max_cpu_num entries */ + if (cpu < (unsigned int)max_cpu_num.cpu) + cpunode_map[cpu] = mem; } closedir(dir2); } @@ -641,21 +664,21 @@ size_t cpu_map__snprint(struct perf_cpu_map *map, char *buf, size_t size) if (start == -1) { start = i; if (last) { - ret += snprintf(buf + ret, size - ret, - "%s%d", COMMA, - perf_cpu_map__cpu(map, i).cpu); + ret += scnprintf(buf + ret, size - ret, + "%s%d", COMMA, + perf_cpu_map__cpu(map, i).cpu); } } else if (((i - start) != (cpu.cpu - perf_cpu_map__cpu(map, start).cpu)) || last) { int end = i - 1; if (start == end) { - ret += snprintf(buf + ret, size - ret, - "%s%d", COMMA, - perf_cpu_map__cpu(map, start).cpu); + ret += scnprintf(buf + ret, size - ret, + "%s%d", COMMA, + perf_cpu_map__cpu(map, start).cpu); } else { - ret += snprintf(buf + ret, size - ret, - "%s%d-%d", COMMA, - perf_cpu_map__cpu(map, start).cpu, perf_cpu_map__cpu(map, end).cpu); + ret += scnprintf(buf + ret, size - ret, + "%s%d-%d", COMMA, + perf_cpu_map__cpu(map, start).cpu, perf_cpu_map__cpu(map, end).cpu); } first = false; start = i; diff --git a/tools/perf/util/cs-etm-base.c b/tools/perf/util/cs-etm-base.c index 4abe416e3feb..aebef71d3a0a 100644 --- a/tools/perf/util/cs-etm-base.c +++ b/tools/perf/util/cs-etm-base.c @@ -170,7 +170,9 @@ int cs_etm__process_auxtrace_info(union perf_event *event, u64 *ptr = NULL; u64 hdr_version; - if (auxtrace_info->header.size < (event_header_size + INFO_HEADER_SIZE)) + /* Ensure priv[] is large enough for the global header entries */ + if (auxtrace_info->header.size < (event_header_size + INFO_HEADER_SIZE + + CS_ETM_HEADER_SIZE)) return -EINVAL; /* First the global part */ diff --git a/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c b/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c index 43ac711a4e2a..4f66ff9fe7f6 100644 --- a/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c +++ b/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c @@ -399,6 +399,8 @@ cs_etm_decoder__buffer_packet(struct cs_etm_queue *etmq, packet_queue->packet_buffer[et].flags = 0; packet_queue->packet_buffer[et].exception_number = UINT32_MAX; packet_queue->packet_buffer[et].trace_chan_id = trace_chan_id; + packet_queue->packet_buffer[et].el = ocsd_EL_unknown; + packet_queue->packet_buffer[et].tid = -1; if (packet_queue->packet_count == CS_ETM_PACKET_MAX_BUFFER - 1) return OCSD_RESP_WAIT; @@ -446,6 +448,7 @@ cs_etm_decoder__buffer_range(struct cs_etm_queue *etmq, packet->last_instr_type = elem->last_i_type; packet->last_instr_subtype = elem->last_i_subtype; packet->last_instr_cond = elem->last_instr_cond; + packet->el = elem->context.exception_level; if (elem->last_i_type == OCSD_INSTR_BR || elem->last_i_type == OCSD_INSTR_BR_INDIRECT) packet->last_instr_taken_branch = elem->last_instr_exec; @@ -522,7 +525,9 @@ cs_etm_decoder__set_tid(struct cs_etm_queue *etmq, const ocsd_generic_trace_elem *elem, const uint8_t trace_chan_id) { + struct cs_etm_packet *packet; pid_t tid = -1; + int ret; /* * Process the PE_CONTEXT packets if we have a valid contextID or VMID. @@ -543,12 +548,18 @@ cs_etm_decoder__set_tid(struct cs_etm_queue *etmq, break; } - if (cs_etm__etmq_set_tid_el(etmq, tid, trace_chan_id, - elem->context.exception_level)) + if (cs_etm__etmq_update_decode_context(etmq, trace_chan_id, + elem->context.exception_level, tid)) return OCSD_RESP_FATAL_SYS_ERR; - if (tid == -1) - return OCSD_RESP_CONT; + ret = cs_etm_decoder__buffer_packet(etmq, packet_queue, trace_chan_id, + CS_ETM_CONTEXT); + if (ret != OCSD_RESP_CONT && ret != OCSD_RESP_WAIT) + return ret; + + packet = &packet_queue->packet_buffer[packet_queue->tail]; + packet->tid = tid; + packet->el = elem->context.exception_level; /* * A timestamp is generated after a PE_CONTEXT element so make sure @@ -556,7 +567,7 @@ cs_etm_decoder__set_tid(struct cs_etm_queue *etmq, */ cs_etm_decoder__reset_timestamp(packet_queue); - return OCSD_RESP_CONT; + return ret; } static ocsd_datapath_resp_t cs_etm_decoder__gen_trace_elem_printer( diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c index 06eb1a56430c..520670348311 100644 --- a/tools/perf/util/cs-etm.c +++ b/tools/perf/util/cs-etm.c @@ -6,7 +6,7 @@ * Author: Mathieu Poirier <mathieu.poirier@linaro.org> */ -#include <linux/kernel.h> +#include <limits.h> #include <linux/bitfield.h> #include <linux/bitops.h> #include <linux/coresight-pmu.h> @@ -86,15 +86,22 @@ struct cs_etm_traceid_queue { u64 period_instructions; size_t last_branch_pos; union perf_event *event_buf; - struct thread *thread; - struct thread *prev_packet_thread; - ocsd_ex_level prev_packet_el; - ocsd_ex_level el; struct branch_stack *last_branch; struct branch_stack *last_branch_rb; struct cs_etm_packet *prev_packet; struct cs_etm_packet *packet; struct cs_etm_packet_queue packet_queue; + + struct thread *decode_thread; + ocsd_ex_level decode_el; + + /* + * The frontend accesses the EL from '[prev_]packet' because it needs + * previous EL for branch and current EL for instruction samples. It's + * not possible to change thread in a single branch sample so no need to + * store or access the thread through the packet. + */ + struct thread *frontend_thread; }; enum cs_etm_format { @@ -285,8 +292,11 @@ static struct cs_etm_queue *cs_etm__get_queue(struct cs_etm_auxtrace *etm, int c { if (etm->per_thread_decoding) return etm->queues.queue_array[0].priv; - else - return etm->queues.queue_array[cpu].priv; + + if (cpu < 0 || cpu >= (int)etm->queues.nr_queues) + return NULL; + + return etm->queues.queue_array[cpu].priv; } static int cs_etm__map_trace_id_v0(struct cs_etm_auxtrace *etm, u8 trace_chan_id, @@ -299,6 +309,9 @@ static int cs_etm__map_trace_id_v0(struct cs_etm_auxtrace *etm, u8 trace_chan_id * queue associated with that CPU so only one decoder is made. */ etmq = cs_etm__get_queue(etm, cpu_metadata[CS_ETM_CPU]); + if (!etmq) + return -EINVAL; + if (etmq->format == UNFORMATTED) return cs_etm__insert_trace_id_node(etmq, trace_chan_id, cpu_metadata); @@ -311,6 +324,9 @@ static int cs_etm__map_trace_id_v0(struct cs_etm_auxtrace *etm, u8 trace_chan_id int ret; etmq = etm->queues.queue_array[i].priv; + if (!etmq) + continue; + ret = cs_etm__insert_trace_id_node(etmq, trace_chan_id, cpu_metadata); if (ret) @@ -351,6 +367,9 @@ static int cs_etm__process_trace_id_v0_1(struct cs_etm_auxtrace *etm, int cpu, u32 sink_id = FIELD_GET(CS_AUX_HW_ID_SINK_ID_MASK, hw_id); u8 trace_id = FIELD_GET(CS_AUX_HW_ID_TRACE_ID_MASK, hw_id); + if (!etmq) + return -EINVAL; + /* * Check sink id hasn't changed in per-cpu mode. In per-thread mode, * let it pass for now until an actual overlapping trace ID is hit. In @@ -368,6 +387,9 @@ static int cs_etm__process_trace_id_v0_1(struct cs_etm_auxtrace *etm, int cpu, for (unsigned int i = 0; i < etm->queues.nr_queues; ++i) { struct cs_etm_queue *other_etmq = etm->queues.queue_array[i].priv; + if (!other_etmq) + continue; + /* Different sinks, skip */ if (other_etmq->sink_id != etmq->sink_id) continue; @@ -389,6 +411,9 @@ static int cs_etm__process_trace_id_v0_1(struct cs_etm_auxtrace *etm, int cpu, } cpu_data = get_cpu_data(etm, cpu); + if (!cpu_data) + return -EINVAL; + ret = cs_etm__insert_trace_id_node(etmq, trace_id, cpu_data); if (ret) return ret; @@ -615,10 +640,11 @@ static int cs_etm__init_traceid_queue(struct cs_etm_queue *etmq, queue = &etmq->etm->queues.queue_array[etmq->queue_nr]; tidq->trace_chan_id = trace_chan_id; - tidq->el = tidq->prev_packet_el = ocsd_EL_unknown; - tidq->thread = machine__findnew_thread(&etm->session->machines.host, -1, + tidq->decode_el = ocsd_EL_unknown; + tidq->frontend_thread = machine__findnew_thread(&etm->session->machines.host, -1, + queue->tid); + tidq->decode_thread = machine__findnew_thread(&etm->session->machines.host, -1, queue->tid); - tidq->prev_packet_thread = machine__idle_thread(&etm->session->machines.host); tidq->packet = zalloc(sizeof(struct cs_etm_packet)); if (!tidq->packet) @@ -751,21 +777,10 @@ static void cs_etm__packet_swap(struct cs_etm_auxtrace *etm, /* * Swap PACKET with PREV_PACKET: PACKET becomes PREV_PACKET for * the next incoming packet. - * - * Threads and exception levels are also tracked for both the - * previous and current packets. This is because the previous - * packet is used for the 'from' IP for branch samples, so the - * thread at that time must also be assigned to that sample. - * Across discontinuity packets the thread can change, so by - * tracking the thread for the previous packet the branch sample - * will have the correct info. */ tmp = tidq->packet; tidq->packet = tidq->prev_packet; tidq->prev_packet = tmp; - tidq->prev_packet_el = tidq->el; - thread__put(tidq->prev_packet_thread); - tidq->prev_packet_thread = thread__get(tidq->thread); } } @@ -938,8 +953,8 @@ static void cs_etm__free_traceid_queues(struct cs_etm_queue *etmq) /* Free this traceid_queue from the array */ tidq = etmq->traceid_queues[idx]; - thread__zput(tidq->thread); - thread__zput(tidq->prev_packet_thread); + thread__zput(tidq->frontend_thread); + thread__zput(tidq->decode_thread); zfree(&tidq->event_buf); zfree(&tidq->last_branch); zfree(&tidq->last_branch_rb); @@ -1084,47 +1099,43 @@ static u8 cs_etm__cpu_mode(struct cs_etm_queue *etmq, u64 address, } } -static u32 cs_etm__mem_access(struct cs_etm_queue *etmq, u8 trace_chan_id, - u64 address, size_t size, u8 *buffer, - const ocsd_mem_space_acc_t mem_space) +static u32 __cs_etm__mem_access(struct cs_etm_queue *etmq, + u64 address, size_t size, u8 *buffer, + const ocsd_mem_space_acc_t mem_space, + ocsd_ex_level el, struct thread *thread) { u8 cpumode; u64 offset; int len; struct addr_location al; struct dso *dso; - struct cs_etm_traceid_queue *tidq; int ret = 0; if (!etmq) return 0; addr_location__init(&al); - tidq = cs_etm__etmq_get_traceid_queue(etmq, trace_chan_id); - if (!tidq) - goto out; /* - * We've already tracked EL along side the PID in cs_etm__set_thread() - * so double check that it matches what OpenCSD thinks as well. It - * doesn't distinguish between EL0 and EL1 for this mem access callback - * so we had to do the extra tracking. Skip validation if it's any of - * the 'any' values. + * We track EL for the frontend and the backend when receiving context + * and range packets. OpenCSD doesn't distinguish between EL0 and EL1 + * for this mem access callback so we had to do the extra tracking. Skip + * validation if it's any of the 'any' values. */ if (!(mem_space == OCSD_MEM_SPACE_ANY || mem_space == OCSD_MEM_SPACE_N || mem_space == OCSD_MEM_SPACE_S)) { if (mem_space & OCSD_MEM_SPACE_EL1N) { /* Includes both non secure EL1 and EL0 */ - assert(tidq->el == ocsd_EL1 || tidq->el == ocsd_EL0); + assert(el == ocsd_EL1 || el == ocsd_EL0); } else if (mem_space & OCSD_MEM_SPACE_EL2) - assert(tidq->el == ocsd_EL2); + assert(el == ocsd_EL2); else if (mem_space & OCSD_MEM_SPACE_EL3) - assert(tidq->el == ocsd_EL3); + assert(el == ocsd_EL3); } - cpumode = cs_etm__cpu_mode(etmq, address, tidq->el); + cpumode = cs_etm__cpu_mode(etmq, address, el); - if (!thread__find_map(tidq->thread, cpumode, address, &al)) + if (!thread__find_map(thread, cpumode, address, &al)) goto out; dso = map__dso(al.map); @@ -1139,7 +1150,7 @@ static u32 cs_etm__mem_access(struct cs_etm_queue *etmq, u8 trace_chan_id, map__load(al.map); - len = dso__data_read_offset(dso, maps__machine(thread__maps(tidq->thread)), + len = dso__data_read_offset(dso, maps__machine(thread__maps(thread)), offset, buffer, size); if (len <= 0) { @@ -1159,6 +1170,30 @@ out: return ret; } +static u32 cs_etm__frontend_mem_access(struct cs_etm_queue *etmq, + struct cs_etm_traceid_queue *tidq, + struct cs_etm_packet *packet, + u64 address, size_t size, u8 *buffer) +{ + return __cs_etm__mem_access(etmq, address, size, buffer, 0, packet->el, + tidq->frontend_thread); +} + +static u32 cs_etm__decoder_mem_access(struct cs_etm_queue *etmq, u8 trace_chan_id, + u64 address, size_t size, u8 *buffer, + const ocsd_mem_space_acc_t mem_space) +{ + struct cs_etm_traceid_queue *tidq; + + tidq = cs_etm__etmq_get_traceid_queue(etmq, trace_chan_id); + if (!tidq) + return 0; + + return __cs_etm__mem_access(etmq, address, size, buffer, + mem_space, tidq->decode_el, + tidq->decode_thread); +} + static struct cs_etm_queue *cs_etm__alloc_queue(void) { struct cs_etm_queue *etmq = zalloc(sizeof(*etmq)); @@ -1334,12 +1369,13 @@ void cs_etm__reset_last_branch_rb(struct cs_etm_traceid_queue *tidq) } static inline int cs_etm__t32_instr_size(struct cs_etm_queue *etmq, - u8 trace_chan_id, u64 addr) + struct cs_etm_traceid_queue *tidq, + struct cs_etm_packet *packet, u64 addr) { u8 instrBytes[2]; - cs_etm__mem_access(etmq, trace_chan_id, addr, ARRAY_SIZE(instrBytes), - instrBytes, 0); + cs_etm__frontend_mem_access(etmq, tidq, packet, addr, + ARRAY_SIZE(instrBytes), instrBytes); /* * T32 instruction size is indicated by bits[15:11] of the first * 16-bit word of the instruction: 0b11101, 0b11110 and 0b11111 @@ -1372,16 +1408,16 @@ u64 cs_etm__last_executed_instr(const struct cs_etm_packet *packet) } static inline u64 cs_etm__instr_addr(struct cs_etm_queue *etmq, - u64 trace_chan_id, - const struct cs_etm_packet *packet, + struct cs_etm_traceid_queue *tidq, + struct cs_etm_packet *packet, u64 offset) { if (packet->isa == CS_ETM_ISA_T32) { u64 addr = packet->start_addr; while (offset) { - addr += cs_etm__t32_instr_size(etmq, - trace_chan_id, addr); + addr += cs_etm__t32_instr_size(etmq, tidq, packet, + addr); offset--; } return addr; @@ -1473,34 +1509,51 @@ cs_etm__get_trace(struct cs_etm_queue *etmq) return etmq->buf_len; } -static void cs_etm__set_thread(struct cs_etm_queue *etmq, - struct cs_etm_traceid_queue *tidq, pid_t tid, - ocsd_ex_level el) +/* + * Convert a raw thread number to a thread struct and assign it to **thread. + */ +static int cs_etm__etmq_update_thread(struct cs_etm_queue *etmq, + ocsd_ex_level el, pid_t tid, + struct thread **thread) { struct machine *machine = cs_etm__get_machine(etmq, el); + if (!machine || !*thread) + return -EINVAL; + if (tid != -1) { - thread__zput(tidq->thread); - tidq->thread = machine__find_thread(machine, -1, tid); + thread__zput(*thread); + *thread = machine__find_thread(machine, -1, tid); } /* Couldn't find a known thread */ - if (!tidq->thread) - tidq->thread = machine__idle_thread(machine); + if (!*thread) + *thread = machine__idle_thread(machine); - tidq->el = el; + return 0; } -int cs_etm__etmq_set_tid_el(struct cs_etm_queue *etmq, pid_t tid, - u8 trace_chan_id, ocsd_ex_level el) +/* + * Set the thread and EL of the decode context which is ahead in time of the + * frontend context. + */ +int cs_etm__etmq_update_decode_context(struct cs_etm_queue *etmq, + u8 trace_chan_id, + ocsd_ex_level el, pid_t tid) { struct cs_etm_traceid_queue *tidq; + int ret; tidq = cs_etm__etmq_get_traceid_queue(etmq, trace_chan_id); if (!tidq) return -EINVAL; - cs_etm__set_thread(etmq, tidq, tid, el); + ret = cs_etm__etmq_update_thread(etmq, el, tid, + &tidq->decode_thread); + if (ret) + return ret; + + tidq->decode_el = el; return 0; } @@ -1510,8 +1563,8 @@ bool cs_etm__etmq_is_timeless(struct cs_etm_queue *etmq) } static void cs_etm__copy_insn(struct cs_etm_queue *etmq, - u64 trace_chan_id, - const struct cs_etm_packet *packet, + struct cs_etm_traceid_queue *tidq, + struct cs_etm_packet *packet, struct perf_sample *sample) { /* @@ -1528,14 +1581,14 @@ static void cs_etm__copy_insn(struct cs_etm_queue *etmq, * cs_etm__t32_instr_size(). */ if (packet->isa == CS_ETM_ISA_T32) - sample->insn_len = cs_etm__t32_instr_size(etmq, trace_chan_id, + sample->insn_len = cs_etm__t32_instr_size(etmq, tidq, packet, sample->ip); /* Otherwise, A64 and A32 instruction size are always 32-bit. */ else sample->insn_len = 4; - cs_etm__mem_access(etmq, trace_chan_id, sample->ip, sample->insn_len, - (void *)sample->insn, 0); + cs_etm__frontend_mem_access(etmq, tidq, packet, sample->ip, + sample->insn_len, (void *)sample->insn); } u64 cs_etm__convert_sample_time(struct cs_etm_queue *etmq, u64 cs_timestamp) @@ -1562,6 +1615,7 @@ static inline u64 cs_etm__resolve_sample_time(struct cs_etm_queue *etmq, static int cs_etm__synth_instruction_sample(struct cs_etm_queue *etmq, struct cs_etm_traceid_queue *tidq, + struct cs_etm_packet *packet, u64 addr, u64 period) { int ret = 0; @@ -1571,23 +1625,23 @@ static int cs_etm__synth_instruction_sample(struct cs_etm_queue *etmq, perf_sample__init(&sample, /*all=*/true); event->sample.header.type = PERF_RECORD_SAMPLE; - event->sample.header.misc = cs_etm__cpu_mode(etmq, addr, tidq->el); + event->sample.header.misc = cs_etm__cpu_mode(etmq, addr, packet->el); event->sample.header.size = sizeof(struct perf_event_header); /* Set time field based on etm auxtrace config. */ sample.time = cs_etm__resolve_sample_time(etmq, tidq); sample.ip = addr; - sample.pid = thread__pid(tidq->thread); - sample.tid = thread__tid(tidq->thread); + sample.pid = thread__pid(tidq->frontend_thread); + sample.tid = thread__tid(tidq->frontend_thread); sample.id = etmq->etm->instructions_id; sample.stream_id = etmq->etm->instructions_id; sample.period = period; - sample.cpu = tidq->packet->cpu; + sample.cpu = packet->cpu; sample.flags = tidq->prev_packet->flags; sample.cpumode = event->sample.header.misc; - cs_etm__copy_insn(etmq, tidq->trace_chan_id, tidq->packet, &sample); + cs_etm__copy_insn(etmq, tidq, packet, &sample); if (etm->synth_opts.last_branch) sample.branch_stack = tidq->last_branch; @@ -1632,15 +1686,15 @@ static int cs_etm__synth_branch_sample(struct cs_etm_queue *etmq, event->sample.header.type = PERF_RECORD_SAMPLE; event->sample.header.misc = cs_etm__cpu_mode(etmq, ip, - tidq->prev_packet_el); + tidq->prev_packet->el); event->sample.header.size = sizeof(struct perf_event_header); /* Set time field based on etm auxtrace config. */ sample.time = cs_etm__resolve_sample_time(etmq, tidq); sample.ip = ip; - sample.pid = thread__pid(tidq->prev_packet_thread); - sample.tid = thread__tid(tidq->prev_packet_thread); + sample.pid = thread__pid(tidq->frontend_thread); + sample.tid = thread__tid(tidq->frontend_thread); sample.addr = cs_etm__first_executed_instr(tidq->packet); sample.id = etmq->etm->branches_id; sample.stream_id = etmq->etm->branches_id; @@ -1649,8 +1703,7 @@ static int cs_etm__synth_branch_sample(struct cs_etm_queue *etmq, sample.flags = tidq->prev_packet->flags; sample.cpumode = event->sample.header.misc; - cs_etm__copy_insn(etmq, tidq->trace_chan_id, tidq->prev_packet, - &sample); + cs_etm__copy_insn(etmq, tidq, tidq->prev_packet, &sample); /* * perf report cannot handle events without a branch stack @@ -1774,7 +1827,6 @@ static int cs_etm__sample(struct cs_etm_queue *etmq, { struct cs_etm_auxtrace *etm = etmq->etm; int ret; - u8 trace_chan_id = tidq->trace_chan_id; u64 instrs_prev; /* Get instructions remainder from previous packet */ @@ -1860,10 +1912,10 @@ static int cs_etm__sample(struct cs_etm_queue *etmq, * been executed, but PC has not advanced to next * instruction) */ - addr = cs_etm__instr_addr(etmq, trace_chan_id, - tidq->packet, offset - 1); + addr = cs_etm__instr_addr(etmq, tidq, tidq->packet, + offset - 1); ret = cs_etm__synth_instruction_sample( - etmq, tidq, addr, + etmq, tidq, tidq->packet, addr, etm->instructions_sample_period); if (ret) return ret; @@ -1945,7 +1997,7 @@ static int cs_etm__flush(struct cs_etm_queue *etmq, addr = cs_etm__last_executed_instr(tidq->prev_packet); err = cs_etm__synth_instruction_sample( - etmq, tidq, addr, + etmq, tidq, tidq->prev_packet, addr, tidq->period_instructions); if (err) return err; @@ -2000,7 +2052,7 @@ static int cs_etm__end_block(struct cs_etm_queue *etmq, addr = cs_etm__last_executed_instr(tidq->prev_packet); err = cs_etm__synth_instruction_sample( - etmq, tidq, addr, + etmq, tidq, tidq->prev_packet, addr, tidq->period_instructions); if (err) return err; @@ -2037,9 +2089,9 @@ static int cs_etm__get_data_block(struct cs_etm_queue *etmq) return etmq->buf_len; } -static bool cs_etm__is_svc_instr(struct cs_etm_queue *etmq, u8 trace_chan_id, - struct cs_etm_packet *packet, - u64 end_addr) +static bool cs_etm__is_svc_instr(struct cs_etm_queue *etmq, + struct cs_etm_traceid_queue *tidq, + struct cs_etm_packet *packet, u64 end_addr) { /* Initialise to keep compiler happy */ u16 instr16 = 0; @@ -2061,8 +2113,8 @@ static bool cs_etm__is_svc_instr(struct cs_etm_queue *etmq, u8 trace_chan_id, * so below only read 2 bytes as instruction size for T32. */ addr = end_addr - 2; - cs_etm__mem_access(etmq, trace_chan_id, addr, sizeof(instr16), - (u8 *)&instr16, 0); + cs_etm__frontend_mem_access(etmq, tidq, packet, addr, + sizeof(instr16), (u8 *)&instr16); if ((instr16 & 0xFF00) == 0xDF00) return true; @@ -2077,8 +2129,8 @@ static bool cs_etm__is_svc_instr(struct cs_etm_queue *etmq, u8 trace_chan_id, * +---------+---------+-------------------------+ */ addr = end_addr - 4; - cs_etm__mem_access(etmq, trace_chan_id, addr, sizeof(instr32), - (u8 *)&instr32, 0); + cs_etm__frontend_mem_access(etmq, tidq, packet, addr, + sizeof(instr32), (u8 *)&instr32); if ((instr32 & 0x0F000000) == 0x0F000000 && (instr32 & 0xF0000000) != 0xF0000000) return true; @@ -2094,8 +2146,8 @@ static bool cs_etm__is_svc_instr(struct cs_etm_queue *etmq, u8 trace_chan_id, * +-----------------------+---------+-----------+ */ addr = end_addr - 4; - cs_etm__mem_access(etmq, trace_chan_id, addr, sizeof(instr32), - (u8 *)&instr32, 0); + cs_etm__frontend_mem_access(etmq, tidq, packet, addr, + sizeof(instr32), (u8 *)&instr32); if ((instr32 & 0xFFE0001F) == 0xd4000001) return true; @@ -2111,7 +2163,6 @@ static bool cs_etm__is_svc_instr(struct cs_etm_queue *etmq, u8 trace_chan_id, static bool cs_etm__is_syscall(struct cs_etm_queue *etmq, struct cs_etm_traceid_queue *tidq, u64 magic) { - u8 trace_chan_id = tidq->trace_chan_id; struct cs_etm_packet *packet = tidq->packet; struct cs_etm_packet *prev_packet = tidq->prev_packet; @@ -2126,7 +2177,7 @@ static bool cs_etm__is_syscall(struct cs_etm_queue *etmq, */ if (magic == __perf_cs_etmv4_magic) { if (packet->exception_number == CS_ETMV4_EXC_CALL && - cs_etm__is_svc_instr(etmq, trace_chan_id, prev_packet, + cs_etm__is_svc_instr(etmq, tidq, prev_packet, prev_packet->end_addr)) return true; } @@ -2164,7 +2215,6 @@ static bool cs_etm__is_sync_exception(struct cs_etm_queue *etmq, struct cs_etm_traceid_queue *tidq, u64 magic) { - u8 trace_chan_id = tidq->trace_chan_id; struct cs_etm_packet *packet = tidq->packet; struct cs_etm_packet *prev_packet = tidq->prev_packet; @@ -2190,7 +2240,7 @@ static bool cs_etm__is_sync_exception(struct cs_etm_queue *etmq, * (SMC, HVC) are taken as sync exceptions. */ if (packet->exception_number == CS_ETMV4_EXC_CALL && - !cs_etm__is_svc_instr(etmq, trace_chan_id, prev_packet, + !cs_etm__is_svc_instr(etmq, tidq, prev_packet, prev_packet->end_addr)) return true; @@ -2214,7 +2264,6 @@ static int cs_etm__set_sample_flags(struct cs_etm_queue *etmq, { struct cs_etm_packet *packet = tidq->packet; struct cs_etm_packet *prev_packet = tidq->prev_packet; - u8 trace_chan_id = tidq->trace_chan_id; u64 magic; int ret; @@ -2295,11 +2344,11 @@ static int cs_etm__set_sample_flags(struct cs_etm_queue *etmq, if (prev_packet->flags == (PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN | PERF_IP_FLAG_INTERRUPT) && - cs_etm__is_svc_instr(etmq, trace_chan_id, - packet, packet->start_addr)) + cs_etm__is_svc_instr(etmq, tidq, packet, packet->start_addr)) { prev_packet->flags = PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN | PERF_IP_FLAG_SYSCALLRET; + } break; case CS_ETM_DISCONTINUITY: /* @@ -2380,6 +2429,7 @@ static int cs_etm__set_sample_flags(struct cs_etm_queue *etmq, PERF_IP_FLAG_RETURN | PERF_IP_FLAG_INTERRUPT; break; + case CS_ETM_CONTEXT: case CS_ETM_EMPTY: default: break; @@ -2455,6 +2505,19 @@ static int cs_etm__process_traceid_queue(struct cs_etm_queue *etmq, */ cs_etm__sample(etmq, tidq); break; + case CS_ETM_CONTEXT: + /* + * Update context but don't swap packet. Keep the + * previous one for branch source address info, if + * tracing the kernel the context packet will be emitted + * between two ranges. + */ + ret = cs_etm__etmq_update_thread(etmq, tidq->packet->el, + tidq->packet->tid, + &tidq->frontend_thread); + if (ret) + goto out; + break; case CS_ETM_EXCEPTION: case CS_ETM_EXCEPTION_RET: /* @@ -2483,6 +2546,7 @@ static int cs_etm__process_traceid_queue(struct cs_etm_queue *etmq, } } +out: return ret; } @@ -2606,7 +2670,7 @@ static int cs_etm__process_timeless_queues(struct cs_etm_auxtrace *etm, if (!tidq) continue; - if (tid == -1 || thread__tid(tidq->thread) == tid) + if (tid == -1 || thread__tid(tidq->frontend_thread) == tid) cs_etm__run_per_thread_timeless_decoder(etmq); } else cs_etm__run_per_cpu_timeless_decoder(etmq); @@ -3091,6 +3155,9 @@ static int cs_etm__queue_aux_fragment(struct perf_session *session, off_t file_o aux_offset + aux_size <= auxtrace_event->offset + auxtrace_event->size) { struct cs_etm_queue *etmq = cs_etm__get_queue(etm, auxtrace_event->cpu); + if (!etmq) + return -EINVAL; + /* * If this AUX event was inside this buffer somewhere, create a new auxtrace event * based on the sizes of the aux event, and queue that fragment. @@ -3322,7 +3389,7 @@ static int cs_etm__create_queue_decoders(struct cs_etm_queue *etmq) */ if (cs_etm_decoder__add_mem_access_cb(etmq->decoder, 0x0L, ((u64) -1L), - cs_etm__mem_access)) + cs_etm__decoder_mem_access)) goto out_free_decoder; zfree(&t_params); @@ -3378,6 +3445,18 @@ int cs_etm__process_auxtrace_info_full(union perf_event *event, /* First the global part */ ptr = (u64 *) auxtrace_info->priv; num_cpu = ptr[CS_PMU_TYPE_CPUS] & 0xffffffff; + + /* + * Bound num_cpu by the event size: the global header consumes + * CS_ETM_HEADER_SIZE bytes, and each CPU needs at least one u64 + * metadata entry after that. + */ + priv_size = total_size - event_header_size - INFO_HEADER_SIZE - + CS_ETM_HEADER_SIZE; + if (num_cpu <= 0 || priv_size <= 0 || + num_cpu > priv_size / (int)sizeof(u64)) + return -EINVAL; + metadata = zalloc(sizeof(*metadata) * num_cpu); if (!metadata) return -ENOMEM; @@ -3416,7 +3495,13 @@ int cs_etm__process_auxtrace_info_full(union perf_event *event, goto err_free_metadata; } - if ((int) metadata[j][CS_ETM_CPU] > max_cpu) + /* CPU id comes from perf.data and must fit max_cpu + 1 without overflow */ + if (metadata[j][CS_ETM_CPU] >= INT_MAX) { + err = -EINVAL; + goto err_free_metadata; + } + + if ((int)metadata[j][CS_ETM_CPU] > max_cpu) max_cpu = metadata[j][CS_ETM_CPU]; } diff --git a/tools/perf/util/cs-etm.h b/tools/perf/util/cs-etm.h index a8caeea720aa..dfc9aeacfd0b 100644 --- a/tools/perf/util/cs-etm.h +++ b/tools/perf/util/cs-etm.h @@ -158,6 +158,7 @@ enum cs_etm_sample_type { CS_ETM_DISCONTINUITY, CS_ETM_EXCEPTION, CS_ETM_EXCEPTION_RET, + CS_ETM_CONTEXT, }; enum cs_etm_isa { @@ -184,6 +185,8 @@ struct cs_etm_packet { u8 last_instr_size; u8 trace_chan_id; int cpu; + int el; + pid_t tid; }; #define CS_ETM_PACKET_MAX_BUFFER 1024 @@ -244,8 +247,9 @@ enum cs_etm_pid_fmt { #include <opencsd/ocsd_if_types.h> int cs_etm__get_cpu(struct cs_etm_queue *etmq, u8 trace_chan_id, int *cpu); enum cs_etm_pid_fmt cs_etm__get_pid_fmt(struct cs_etm_queue *etmq); -int cs_etm__etmq_set_tid_el(struct cs_etm_queue *etmq, pid_t tid, - u8 trace_chan_id, ocsd_ex_level el); +int cs_etm__etmq_update_decode_context(struct cs_etm_queue *etmq, + u8 trace_chan_id, ocsd_ex_level el, + pid_t tid); bool cs_etm__etmq_is_timeless(struct cs_etm_queue *etmq); void cs_etm__etmq_set_traceid_queue_timestamp(struct cs_etm_queue *etmq, u8 trace_chan_id); diff --git a/tools/perf/util/data.c b/tools/perf/util/data.c index 164eb45a0b36..90df41da1a32 100644 --- a/tools/perf/util/data.c +++ b/tools/perf/util/data.c @@ -213,17 +213,15 @@ static int check_backup(struct perf_data *data) ret = rm_rf_perf_data(oldname); if (ret) { - pr_err("Can't remove old data: %s (%s)\n", - ret == -2 ? - "Unknown file found" : strerror(errno), - oldname); + if (ret == -2) + pr_err("Can't remove old data: Unknown file found (%s)\n", oldname); + else + pr_err("Can't remove old data: %m (%s)\n", oldname); return -1; } if (rename(data->path, oldname)) { - pr_err("Can't move data: %s (%s to %s)\n", - strerror(errno), - data->path, oldname); + pr_err("Can't move data: %m (%s to %s)\n", data->path, oldname); return -1; } } @@ -246,14 +244,12 @@ static int open_file_read(struct perf_data *data) int flags = data->in_place_update ? O_RDWR : O_RDONLY; struct stat st; int fd; - char sbuf[STRERR_BUFSIZE]; fd = open(data->file.path, flags); if (fd < 0) { int err = errno; - pr_err("failed to open %s: %s", data->file.path, - str_error_r(err, sbuf, sizeof(sbuf))); + pr_err("failed to open %s: %m", data->file.path); if (err == ENOENT && !strcmp(data->file.path, "perf.data")) pr_err(" (try 'perf record' first)"); pr_err("\n"); @@ -285,15 +281,10 @@ static int open_file_read(struct perf_data *data) static int open_file_write(struct perf_data *data) { - int fd; - char sbuf[STRERR_BUFSIZE]; - - fd = open(data->file.path, O_CREAT|O_RDWR|O_TRUNC|O_CLOEXEC, - S_IRUSR|S_IWUSR); + int fd = open(data->file.path, O_CREAT|O_RDWR|O_TRUNC|O_CLOEXEC, S_IRUSR|S_IWUSR); if (fd < 0) - pr_err("failed to open %s : %s\n", data->file.path, - str_error_r(errno, sbuf, sizeof(sbuf))); + pr_err("failed to open %s : %m\n", data->file.path); return fd; } @@ -436,8 +427,8 @@ int perf_data__switch(struct perf_data *data, if (lseek(data->file.fd, pos, SEEK_SET) == (off_t)-1) { ret = -errno; - pr_debug("Failed to lseek to %zu: %s", - pos, strerror(errno)); + pr_debug("Failed to lseek to %zu: %m\n", + pos); goto out; } } diff --git a/tools/perf/util/debuginfo.c b/tools/perf/util/debuginfo.c index bb9ebd84ec2d..46fecb7b17e1 100644 --- a/tools/perf/util/debuginfo.c +++ b/tools/perf/util/debuginfo.c @@ -42,6 +42,7 @@ static int debuginfo__init_offline_dwarf(struct debuginfo *dbg, { GElf_Addr dummy; int fd; + bool fd_consumed = false; fd = open(path, O_RDONLY); if (fd < 0) @@ -55,6 +56,7 @@ static int debuginfo__init_offline_dwarf(struct debuginfo *dbg, dbg->mod = dwfl_report_offline(dbg->dwfl, "", "", fd); if (!dbg->mod) goto error; + fd_consumed = true; dbg->dbg = dwfl_module_getdwarf(dbg->mod, &dbg->bias); if (!dbg->dbg) @@ -62,13 +64,14 @@ static int debuginfo__init_offline_dwarf(struct debuginfo *dbg, dwfl_module_build_id(dbg->mod, &dbg->build_id, &dummy); - dwfl_report_end(dbg->dwfl, NULL, NULL); + if (dwfl_report_end(dbg->dwfl, NULL, NULL) != 0) + goto error; return 0; error: if (dbg->dwfl) dwfl_end(dbg->dwfl); - else + if (!fd_consumed) close(fd); memset(dbg, 0, sizeof(*dbg)); @@ -115,7 +118,7 @@ struct debuginfo *debuginfo__new(const char *path) * incase the path isn't for a regular file. */ assert(!dso__has_build_id(dso)); - if (filename__read_build_id(path, &bid, /*block=*/false) > 0) + if (filename__read_build_id(path, &bid) > 0) dso__set_build_id(dso, &bid); for (type = distro_dwarf_types; @@ -168,7 +171,7 @@ int debuginfo__get_text_offset(struct debuginfo *dbg, Dwarf_Addr *offs, /* Search the relocation related .text section */ for (i = 0; i < n; i++) { p = dwfl_module_relocation_info(dbg->mod, i, &shndx); - if (strcmp(p, ".text") == 0) { + if (p && strcmp(p, ".text") == 0) { /* OK, get the section header */ scn = elf_getscn(elf, shndx); if (!scn) diff --git a/tools/perf/util/demangle-java.c b/tools/perf/util/demangle-java.c index ddf33d58bcd3..c3cb327ed562 100644 --- a/tools/perf/util/demangle-java.c +++ b/tools/perf/util/demangle-java.c @@ -158,7 +158,7 @@ char * java_demangle_sym(const char *str, int flags) { char *buf, *ptr; - char *p; + const char *p; size_t len, l1 = 0; if (!str) diff --git a/tools/perf/util/disasm.c b/tools/perf/util/disasm.c index c513db41137f..61a186399035 100644 --- a/tools/perf/util/disasm.c +++ b/tools/perf/util/disasm.c @@ -1684,8 +1684,11 @@ int symbol__disassemble(struct symbol *sym, struct annotate_args *args) if (dso__decompress_kmodule_path(dso, symfs_filename, tmp, sizeof(tmp)) < 0) return -1; - decomp = true; - strcpy(symfs_filename, tmp); + /* empty pathname means file wasn't actually compressed */ + if (tmp[0] != '\0') { + decomp = true; + strcpy(symfs_filename, tmp); + } } /* diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index dc202d494372..6fa104752de2 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c @@ -342,8 +342,14 @@ int filename__decompress(const char *name, char *pathname, * To keep this transparent, we detect this and return the file * descriptor to the uncompressed file. */ - if (!compressions[comp].is_compressed(name)) - return open(name, O_RDONLY); + if (!compressions[comp].is_compressed(name)) { + fd = open(name, O_RDONLY | O_CLOEXEC); + if (fd < 0) + *err = errno; + if (pathname && len > 0) + pathname[0] = '\0'; + return fd; + } fd = mkstemp(tmpbuf); if (fd < 0) { @@ -539,16 +545,13 @@ static void close_first_dso(void); static int do_open(char *name) EXCLUSIVE_LOCKS_REQUIRED(_dso__data_open_lock) { - int fd; - char sbuf[STRERR_BUFSIZE]; - do { - fd = open(name, O_RDONLY|O_CLOEXEC); + int fd = open(name, O_RDONLY|O_CLOEXEC); + if (fd >= 0) return fd; - pr_debug("dso open failed: %s\n", - str_error_r(errno, sbuf, sizeof(sbuf))); + pr_debug("dso open failed: %m\n"); if (!dso__data_open_cnt || errno != EMFILE) break; @@ -596,12 +599,28 @@ static char *dso__get_filename(struct dso *dso, const char *root_dir, size_t len = sizeof(newpath); if (dso__decompress_kmodule_path(dso, name, newpath, len) < 0) { - errno = *dso__load_errno(dso); + /* + * Use a standard errno value, not the negative custom + * DSO_LOAD_ERRNO stored in dso__load_errno(dso): + * __open_dso() computes fd = -errno, so a negative + * errno produces a positive fd that looks valid. + */ + errno = EIO; goto out; } - *decomp = true; - strcpy(name, newpath); + /* empty pathname means file wasn't actually compressed */ + if (newpath[0] != '\0') { + char *tmp = strdup(newpath); + + if (!tmp) { + unlink(newpath); + goto out; + } + free(name); + name = tmp; + *decomp = true; + } } return name; @@ -866,6 +885,12 @@ static ssize_t bpf_read(struct dso *dso, u64 offset, char *data) return -1; } + /* jited_prog_insns is only valid if bpil_offs_to_addr() converted it */ + if (!(node->info_linear->arrays & (1UL << PERF_BPIL_JITED_INSNS))) { + dso__data(dso)->status = DSO_DATA_STATUS_ERROR; + return -1; + } + len = node->info_linear->info.jited_prog_len; buf = (u8 *)(uintptr_t)node->info_linear->info.jited_prog_insns; @@ -1110,7 +1135,6 @@ static int file_size(struct dso *dso, struct machine *machine) { int ret = 0; struct stat st; - char sbuf[STRERR_BUFSIZE]; mutex_lock(dso__data_open_lock()); @@ -1128,8 +1152,7 @@ static int file_size(struct dso *dso, struct machine *machine) if (fstat(dso__data(dso)->fd, &st) < 0) { ret = -errno; - pr_err("dso cache fstat failed: %s\n", - str_error_r(errno, sbuf, sizeof(sbuf))); + pr_err("dso cache fstat failed: %m\n"); dso__data(dso)->status = DSO_DATA_STATUS_ERROR; goto out; } @@ -1703,7 +1726,7 @@ void dso__read_running_kernel_build_id(struct dso *dso, struct machine *machine) if (machine__is_default_guest(machine)) return; - sprintf(path, "%s/sys/kernel/notes", machine->root_dir); + snprintf(path, sizeof(path), "%s/sys/kernel/notes", machine->root_dir); sysfs__read_build_id(path, &bid); dso__set_build_id(dso, &bid); } @@ -1784,10 +1807,8 @@ int dso__strerror_load(struct dso *dso, char *buf, size_t buflen) BUG_ON(buflen == 0); if (errnum >= 0) { - const char *err = str_error_r(errnum, buf, buflen); - - if (err != buf) - scnprintf(buf, buflen, "%s", err); + errno = errnum; + scnprintf(buf, buflen, "%m"); return 0; } @@ -1843,7 +1864,7 @@ static const u8 *__dso__read_symbol(struct dso *dso, const char *symfs_filename, int saved_errno; nsinfo__mountns_enter(dso__nsinfo(dso), &nsc); - fd = open(symfs_filename, O_RDONLY); + fd = open(symfs_filename, O_RDONLY | O_CLOEXEC); saved_errno = errno; nsinfo__mountns_exit(&nsc); if (fd < 0) { @@ -1911,6 +1932,10 @@ const u8 *dso__read_symbol(struct dso *dso, const char *symfs_filename, return NULL; } info_linear = info_node->info_linear; + if (!(info_linear->arrays & (1UL << PERF_BPIL_JITED_INSNS))) { + errno = SYMBOL_ANNOTATE_ERRNO__BPF_MISSING_BTF; + return NULL; + } assert(len <= info_linear->info.jited_prog_len); *out_buf_len = len; return (const u8 *)(uintptr_t)(info_linear->info.jited_prog_insns); diff --git a/tools/perf/util/dsos.c b/tools/perf/util/dsos.c index 64c1d65b0149..0a7645c7fae7 100644 --- a/tools/perf/util/dsos.c +++ b/tools/perf/util/dsos.c @@ -81,13 +81,13 @@ static int dsos__read_build_ids_cb(struct dso *dso, void *data) return 0; } nsinfo__mountns_enter(dso__nsinfo(dso), &nsc); - if (filename__read_build_id(dso__long_name(dso), &bid, /*block=*/true) > 0) { + if (filename__read_build_id(dso__long_name(dso), &bid) > 0) { dso__set_build_id(dso, &bid); args->have_build_id = true; } else if (errno == ENOENT && dso__nsinfo(dso)) { char *new_name = dso__filename_with_chroot(dso, dso__long_name(dso)); - if (new_name && filename__read_build_id(new_name, &bid, /*block=*/true) > 0) { + if (new_name && filename__read_build_id(new_name, &bid) > 0) { dso__set_build_id(dso, &bid); args->have_build_id = true; } diff --git a/tools/perf/util/env.h b/tools/perf/util/env.h index 9977b85523a8..6ca0e223ea40 100644 --- a/tools/perf/util/env.h +++ b/tools/perf/util/env.h @@ -164,6 +164,20 @@ const char *perf_env__pmu_mappings(struct perf_env *env); int perf_env__read_cpu_topology_map(struct perf_env *env); +/* + * Safe accessor for env->cpu[] topology array. env->cpu can be NULL when + * reading old-format perf.data that predates topology information — + * process_cpu_topology() in header.c frees it while nr_cpus_avail remains + * set, so callers must not index env->cpu[] without this check. + */ +static inline struct cpu_topology_map * +perf_env__get_cpu_topology(struct perf_env *env, struct perf_cpu cpu) +{ + if (env->cpu && cpu.cpu >= 0 && cpu.cpu < env->nr_cpus_avail) + return &env->cpu[cpu.cpu]; + return NULL; +} + void cpu_cache_level__free(struct cpu_cache_level *cache); const char *perf_env__arch(struct perf_env *env); diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index fcf44149feb2..f3c331c70247 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -15,6 +15,7 @@ #include <linux/zalloc.h> #include "cpumap.h" #include "dso.h" +#include "env.h" #include "event.h" #include "debug.h" #include "hist.h" @@ -786,8 +787,18 @@ int machine__resolve(struct machine *machine, struct addr_location *al, if (al->cpu >= 0) { struct perf_env *env = machine->env; - if (env && env->cpu) - al->socket = env->cpu[al->cpu].socket_id; + /* + * Bounds-check al->cpu (s32) before casting to struct perf_cpu + * (int16_t): without this, e.g. 65536 truncates to 0 and silently + * returns CPU 0's topology. Can go once perf_cpu.cpu is widened. + */ + if (env && al->cpu < env->nr_cpus_avail) { + struct cpu_topology_map *topo; + + topo = perf_env__get_cpu_topology(env, (struct perf_cpu){ al->cpu }); + if (topo) + al->socket = topo->socket_id; + } } /* Account for possible out-of-order switch events. */ diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 80d8387e6b97..314393a430a3 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -1605,14 +1605,14 @@ int evlist__parse_sample_timestamp(struct evlist *evlist, union perf_event *even int evlist__strerror_open(struct evlist *evlist, int err, char *buf, size_t size) { int printed, value; - char sbuf[STRERR_BUFSIZE], *emsg = str_error_r(err, sbuf, sizeof(sbuf)); switch (err) { case EACCES: case EPERM: + errno = err; printed = scnprintf(buf, size, - "Error:\t%s.\n" - "Hint:\tCheck /proc/sys/kernel/perf_event_paranoid setting.", emsg); + "Error:\t%m.\n" + "Hint:\tCheck /proc/sys/kernel/perf_event_paranoid setting."); value = perf_event_paranoid(); @@ -1639,16 +1639,18 @@ int evlist__strerror_open(struct evlist *evlist, int err, char *buf, size_t size if (first->core.attr.sample_freq < (u64)max_freq) goto out_default; + errno = err; printed = scnprintf(buf, size, - "Error:\t%s.\n" + "Error:\t%m.\n" "Hint:\tCheck /proc/sys/kernel/perf_event_max_sample_rate.\n" "Hint:\tThe current value is %d and %" PRIu64 " is being requested.", - emsg, max_freq, first->core.attr.sample_freq); + max_freq, first->core.attr.sample_freq); break; } default: out_default: - scnprintf(buf, size, "%s", emsg); + errno = err; + scnprintf(buf, size, "%m"); break; } @@ -1657,17 +1659,17 @@ out_default: int evlist__strerror_mmap(struct evlist *evlist, int err, char *buf, size_t size) { - char sbuf[STRERR_BUFSIZE], *emsg = str_error_r(err, sbuf, sizeof(sbuf)); int pages_attempted = evlist->core.mmap_len / 1024, pages_max_per_user, printed = 0; switch (err) { case EPERM: sysctl__read_int("kernel/perf_event_mlock_kb", &pages_max_per_user); + errno = err; printed += scnprintf(buf + printed, size - printed, - "Error:\t%s.\n" + "Error:\t%m.\n" "Hint:\tCheck /proc/sys/kernel/perf_event_mlock_kb (%d kB) setting.\n" "Hint:\tTried using %zd kB.\n", - emsg, pages_max_per_user, pages_attempted); + pages_max_per_user, pages_attempted); if (pages_attempted >= pages_max_per_user) { printed += scnprintf(buf + printed, size - printed, @@ -1679,7 +1681,8 @@ int evlist__strerror_mmap(struct evlist *evlist, int err, char *buf, size_t size "Hint:\tTry using a smaller -m/--mmap-pages value."); break; default: - scnprintf(buf, size, "%s", emsg); + errno = err; + scnprintf(buf, size, "%m"); break; } @@ -1911,8 +1914,8 @@ static int evlist__parse_control_fifo(const char *str, int *ctl_fd, int *ctl_fd_ */ fd = open(s, O_RDWR | O_NONBLOCK | O_CLOEXEC); if (fd < 0) { - pr_err("Failed to open '%s'\n", s); ret = -errno; + pr_err("Failed to open '%s': %m\n", s); goto out_free; } *ctl_fd = fd; @@ -1922,7 +1925,7 @@ static int evlist__parse_control_fifo(const char *str, int *ctl_fd, int *ctl_fd_ /* O_RDWR | O_NONBLOCK means the other end need not be open */ fd = open(p, O_RDWR | O_NONBLOCK | O_CLOEXEC); if (fd < 0) { - pr_err("Failed to open '%s'\n", p); + pr_err("Failed to open '%s': %m\n", p); ret = -errno; goto out_free; } @@ -1936,7 +1939,8 @@ out_free: int evlist__parse_control(const char *str, int *ctl_fd, int *ctl_fd_ack, bool *ctl_fd_close) { - char *comma = NULL, *endptr = NULL; + const char *comma = NULL; + char *endptr = NULL; *ctl_fd_close = false; @@ -2354,7 +2358,7 @@ int evlist__parse_event_enable_time(struct evlist *evlist, struct record_opts *o eet->timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC); if (eet->timerfd == -1) { err = -errno; - pr_err("timerfd_create failed: %s\n", strerror(errno)); + pr_err("timerfd_create failed: %m\n"); goto free_eet_times; } @@ -2389,7 +2393,7 @@ static int event_enable_timer__set_timer(struct event_enable_timer *eet, int ms) if (timerfd_settime(eet->timerfd, 0, &its, NULL) < 0) { err = -errno; - pr_err("timerfd_settime failed: %s\n", strerror(errno)); + pr_err("timerfd_settime failed: %m\n"); } return err; } diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 9df9818e3701..22e43a57dc95 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -648,8 +648,9 @@ struct tep_event *evsel__tp_format(struct evsel *evsel) if (IS_ERR(tp_format)) { int err = -PTR_ERR(evsel->tp_format); - pr_err("Error getting tracepoint format '%s' '%s'(%d)\n", - evsel__name(evsel), strerror(err), err); + errno = err; + pr_err("Error getting tracepoint format '%s': %m\n", + evsel__name(evsel)); return NULL; } evsel->tp_format = tp_format; @@ -2750,8 +2751,8 @@ retry_open: PERF_EVENT_IOC_SET_BPF, bpf_fd); if (err && errno != EEXIST) { - pr_err("failed to attach bpf fd %d: %s\n", - bpf_fd, strerror(errno)); + pr_err("failed to attach bpf fd %d: %m\n", + bpf_fd); err = -EINVAL; goto out_close; } @@ -3815,7 +3816,6 @@ int evsel__open_strerror(struct evsel *evsel, struct target *target, int err, char *msg, size_t size) { struct perf_pmu *pmu; - char sbuf[STRERR_BUFSIZE]; int printed = 0, enforced = 0; int ret; @@ -3948,10 +3948,11 @@ int evsel__open_strerror(struct evsel *evsel, struct target *target, if (ret) return ret; + errno = err; return scnprintf(msg, size, - "The sys_perf_event_open() syscall returned with %d (%s) for event (%s).\n" - "\"dmesg | grep -i perf\" may provide additional information.\n", - err, str_error_r(err, sbuf, sizeof(sbuf)), evsel__name(evsel)); + "The sys_perf_event_open() syscall failed for event (%s): %m\n" + "\"dmesg | grep -i perf\" may provide additional information.\n", + evsel__name(evsel)); } struct perf_session *evsel__session(struct evsel *evsel) diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 4e12be579140..9cb1d54b2452 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -1825,8 +1825,7 @@ static void print_bpf_prog_info(struct feat_fd *ff, FILE *fp) node = rb_entry(next, struct bpf_prog_info_node, rb_node); next = rb_next(&node->rb_node); - __bpf_event__print_bpf_prog_info(&node->info_linear->info, - env, fp); + __bpf_event__print_bpf_prog_info(node->info_linear, env, fp); } up_read(&env->bpf_progs.lock); @@ -1919,9 +1918,28 @@ static struct evsel *read_event_desc(struct feat_fd *ff) if (do_read_u32(ff, &nre)) goto error; + /* Size of each of the nre attributes. */ if (do_read_u32(ff, &sz)) goto error; + /* + * Require at least one event with an attr no smaller than the + * first published struct, and reject sz values where + * sz + sizeof(u32) would overflow size_t (possible on 32-bit) + * or nre == UINT32_MAX where nre + 1 wraps to 0 in the calloc. + * + * The minimum section footprint per event is sz bytes for the + * attr plus a u32 for the id count, check that nre events fit. + */ + if (!nre || sz < PERF_ATTR_SIZE_VER0 || + sz > ff->size || (size_t)sz > SIZE_MAX - sizeof(u32) || + nre == UINT32_MAX || + nre > (ff->size - ff->offset) / (sz + sizeof(u32))) { + pr_err("Invalid HEADER_EVENT_DESC: nre=%u sz=%u (min %d)\n", + nre, sz, PERF_ATTR_SIZE_VER0); + goto error; + } + /* buffer to hold on file attr struct */ buf = malloc(sz); if (!buf) @@ -1937,6 +1955,9 @@ static struct evsel *read_event_desc(struct feat_fd *ff) msz = sz; for (i = 0, evsel = events; i < nre; evsel++, i++) { + struct perf_event_attr *attr = buf; + u32 attr_size; + evsel->core.idx = i; /* @@ -1946,6 +1967,32 @@ static struct evsel *read_event_desc(struct feat_fd *ff) if (__do_read(ff, buf, sz)) goto error; + /* Reject before attr_swap to prevent OOB via bswap_safe() */ + attr_size = ff->ph->needs_swap ? bswap_32(attr->size) : attr->size; + /* ABI0: size == 0 means the producer didn't set it */ + if (!attr_size) { + attr_size = PERF_ATTR_SIZE_VER0; + /* + * Write back so free_event_desc() doesn't + * treat this event as the end-of-array sentinel + * (it iterates while attr.size != 0). + * + * Only for native — the swap path must NOT + * write native-endian VER0 here because + * perf_event__attr_swap() would re-swap it + * to 0x40000000, defeating bswap_safe() bounds. + * perf_event__attr_swap() has its own ABI0 + * fallback that sets VER0 after swapping. + */ + if (!ff->ph->needs_swap) + attr->size = attr_size; + } + if (attr_size < PERF_ATTR_SIZE_VER0 || attr_size > sz) { + pr_err("Event %d attr.size (%u) invalid (min: %d, max: %u)\n", + i, attr_size, PERF_ATTR_SIZE_VER0, sz); + goto error; + } + if (ff->ph->needs_swap) perf_event__attr_swap(buf); @@ -1967,6 +2014,12 @@ static struct evsel *read_event_desc(struct feat_fd *ff) if (!nr) continue; + /* Prevent oversized allocation from crafted nr */ + if (nr > (ff->size - ff->offset) / sizeof(*id)) { + pr_err("Event %d: id count %u exceeds remaining section\n", i, nr); + goto error; + } + id = calloc(nr, sizeof(*id)); if (!id) goto error; diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index ef4b569f7df4..bd894054021b 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -2960,9 +2960,10 @@ int __hists__scnprintf_title(struct hists *hists, char *bf, size_t size, bool sh ev_name, sample_freq_str, enable_ref ? ref : " ", nr_events); - if (hists->uid_filter_str) - printed += snprintf(bf + printed, size - printed, - ", UID: %s", hists->uid_filter_str); + if (hists->uid_filter_str) { + printed += scnprintf(bf + printed, size - printed, + ", UID: %s", hists->uid_filter_str); + } if (thread) { if (hists__has(hists, thread)) { printed += scnprintf(bf + printed, size - printed, diff --git a/tools/perf/util/hwmon_pmu.c b/tools/perf/util/hwmon_pmu.c index 5c27256a220a..b24cdc329944 100644 --- a/tools/perf/util/hwmon_pmu.c +++ b/tools/perf/util/hwmon_pmu.c @@ -161,7 +161,7 @@ bool parse_hwmon_filename(const char *filename, bool *alarm) { char fn_type[24]; - const char **elem; + const char * const *elem; const char *fn_item = NULL; size_t fn_item_len; @@ -202,7 +202,8 @@ bool parse_hwmon_filename(const char *filename, fn_item_len = strlen(fn_item); if (fn_item_len > 6 && !strcmp(&fn_item[fn_item_len - 6], "_alarm")) { assert(strlen(LONGEST_HWMON_ITEM_STR) < sizeof(fn_type)); - strlcpy(fn_type, fn_item, fn_item_len - 5); + /* fn_item_len - 5 strips "_alarm"; clamp to buffer size */ + strlcpy(fn_type, fn_item, min_t(size_t, fn_item_len - 5, sizeof(fn_type))); fn_item = fn_type; *alarm = true; } @@ -289,13 +290,16 @@ static int hwmon_pmu__read_events(struct hwmon_pmu *pmu) if (fd < 0) continue; - read_len = read(fd, buf, sizeof(buf)); + read_len = read(fd, buf, sizeof(buf) - 1); while (read_len > 0 && buf[read_len - 1] == '\n') read_len--; - if (read_len > 0) - buf[read_len] = '\0'; + if (read_len <= 0) { + close(fd); + continue; + } + buf[read_len] = '\0'; if (buf[0] == '\0') { pr_debug("hwmon_pmu: empty label file %s %s\n", @@ -431,8 +435,8 @@ static size_t hwmon_pmu__describe_items(struct hwmon_pmu *hwm, char *out_buf, si hwmon_item_strs[bit], is_alarm ? "_alarm" : ""); fd = openat(dir, buf, O_RDONLY); - if (fd > 0) { - ssize_t read_len = read(fd, buf, sizeof(buf)); + if (fd >= 0) { + ssize_t read_len = read(fd, buf, sizeof(buf) - 1); while (read_len > 0 && buf[read_len - 1] == '\n') read_len--; @@ -514,14 +518,14 @@ int hwmon_pmu__for_each_event(struct perf_pmu *pmu, void *state, pmu_event_callb int ret; size_t len; - len = snprintf(alias_buf, sizeof(alias_buf), "%s%d", - hwmon_type_strs[key.type], key.num); + scnprintf(alias_buf, sizeof(alias_buf), "%s%d", + hwmon_type_strs[key.type], key.num); if (!info.name) { info.name = info.alias; info.alias = NULL; } - len = snprintf(desc_buf, sizeof(desc_buf), "%s in unit %s named %s.", + len = scnprintf(desc_buf, sizeof(desc_buf), "%s in unit %s named %s.", hwmon_desc[key.type], pmu->name + 6, value->label ?: info.name); @@ -816,7 +820,7 @@ int evsel__hwmon_pmu_read(struct evsel *evsel, int cpu_map_idx, int thread) count = perf_counts(evsel->counts, cpu_map_idx, thread); fd = FD(evsel, cpu_map_idx, thread); - len = pread(fd, buf, sizeof(buf), 0); + len = pread(fd, buf, sizeof(buf) - 1, 0); if (len <= 0) { count->lost++; return -EINVAL; diff --git a/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.c b/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.c index 72c7a4e15d61..f90fcbc4302d 100644 --- a/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.c +++ b/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.c @@ -220,7 +220,6 @@ const char *dump_insn(struct perf_insn *x, uint64_t ip __maybe_unused, { struct insn insn; int n, i, ret; - int left; ret = insn_decode(&insn, inbuf, inlen, x->is64bit ? INSN_MODE_64 : INSN_MODE_32); @@ -229,13 +228,9 @@ const char *dump_insn(struct perf_insn *x, uint64_t ip __maybe_unused, return "<bad>"; if (lenp) *lenp = insn.length; - left = sizeof(x->out); - n = snprintf(x->out, left, "insn: "); - left -= n; - for (i = 0; i < insn.length; i++) { - n += snprintf(x->out + n, left, "%02x ", inbuf[i]); - left -= n; - } + n = scnprintf(x->out, sizeof(x->out), "insn: "); + for (i = 0; i < insn.length; i++) + n += scnprintf(x->out + n, sizeof(x->out) - n, "%02x ", inbuf[i]); return x->out; } diff --git a/tools/perf/util/jitdump.c b/tools/perf/util/jitdump.c index b062b1f234b6..2a4c14b003b3 100644 --- a/tools/perf/util/jitdump.c +++ b/tools/perf/util/jitdump.c @@ -90,7 +90,8 @@ jit_emit_elf(struct jit_buf_desc *jd, saved_errno = errno; nsinfo__mountns_exit(&nsc); if (fd == -1) { - pr_warning("cannot create jit ELF %s: %s\n", filename, strerror(saved_errno)); + errno = saved_errno; + pr_warning("cannot create jit ELF %s: %m\n", filename); return -1; } @@ -754,7 +755,7 @@ jit_inject(struct jit_buf_desc *jd, const char *path) static int jit_detect(const char *mmap_name, pid_t pid, struct nsinfo *nsi, bool *in_pidns) { - char *p; + const char *p; char *end = NULL; pid_t pid2; diff --git a/tools/perf/util/libbfd.c b/tools/perf/util/libbfd.c index 2324f6846d51..63ea3fb53e77 100644 --- a/tools/perf/util/libbfd.c +++ b/tools/perf/util/libbfd.c @@ -418,13 +418,20 @@ out_close: return err; } -int libbfd__read_build_id(const char *filename, struct build_id *bid, bool block) +int libbfd__read_build_id(const char *filename, struct build_id *bid) { size_t size = sizeof(bid->data); int err = -1, fd; bfd *abfd; - fd = open(filename, block ? O_RDONLY : (O_RDONLY | O_NONBLOCK)); + if (!filename) + return -EFAULT; + + errno = 0; + if (!is_regular_file(filename)) + return errno == 0 ? -EWOULDBLOCK : -errno; + + fd = open(filename, O_RDONLY); if (fd < 0) return -1; diff --git a/tools/perf/util/libbfd.h b/tools/perf/util/libbfd.h index e300f171d1bd..953886f3d62f 100644 --- a/tools/perf/util/libbfd.h +++ b/tools/perf/util/libbfd.h @@ -25,7 +25,7 @@ void dso__free_a2l_libbfd(struct dso *dso); int symbol__disassemble_libbfd(const char *filename, struct symbol *sym, struct annotate_args *args); -int libbfd__read_build_id(const char *filename, struct build_id *bid, bool block); +int libbfd__read_build_id(const char *filename, struct build_id *bid); int libbfd_filename__read_debuglink(const char *filename, char *debuglink, size_t size); @@ -59,8 +59,7 @@ static inline int symbol__disassemble_libbfd(const char *filename __always_unuse } static inline int libbfd__read_build_id(const char *filename __always_unused, - struct build_id *bid __always_unused, - bool block __always_unused) + struct build_id *bid __always_unused) { return -1; } diff --git a/tools/perf/util/lzma.c b/tools/perf/util/lzma.c index c355757ed391..91b9b5171d1f 100644 --- a/tools/perf/util/lzma.c +++ b/tools/perf/util/lzma.c @@ -59,7 +59,7 @@ int lzma_decompress_stream_to_file(FILE *infile, int output_fd) strm.avail_in = fread(buf_in, 1, sizeof(buf_in), infile); if (ferror(infile)) { - pr_debug("lzma: read error: %s\n", strerror(errno)); + pr_debug("lzma: read error: %m\n"); goto err_lzma_end; } @@ -73,7 +73,7 @@ int lzma_decompress_stream_to_file(FILE *infile, int output_fd) ssize_t write_size = sizeof(buf_out) - strm.avail_out; if (writen(output_fd, buf_out, write_size) != write_size) { - pr_debug("lzma: write error: %s\n", strerror(errno)); + pr_debug("lzma: write error: %m\n"); goto err_lzma_end; } @@ -103,7 +103,7 @@ int lzma_decompress_to_file(const char *input, int output_fd) infile = fopen(input, "rb"); if (!infile) { - pr_debug("lzma: fopen failed on %s: '%s'\n", input, strerror(errno)); + pr_debug("lzma: fopen failed on %s: '%m'\n", input); return -1; } diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index b5dd42588c91..9331b4cc19f1 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -327,7 +327,7 @@ struct machine *machines__findnew(struct machines *machines, pid_t pid) if ((pid != HOST_KERNEL_ID) && (pid != DEFAULT_GUEST_KERNEL_ID) && (symbol_conf.guestmount)) { - sprintf(path, "%s/%d", symbol_conf.guestmount, pid); + snprintf(path, sizeof(path), "%s/%d", symbol_conf.guestmount, pid); if (access(path, R_OK)) { static struct strlist *seen; @@ -1239,9 +1239,9 @@ int machines__create_guest_kernel_maps(struct machines *machines) namelist[i]->d_name); continue; } - sprintf(path, "%s/%s/proc/kallsyms", - symbol_conf.guestmount, - namelist[i]->d_name); + snprintf(path, sizeof(path), "%s/%s/proc/kallsyms", + symbol_conf.guestmount, + namelist[i]->d_name); ret = access(path, R_OK); if (ret) { pr_debug("Can't access file %s\n", path); @@ -1319,7 +1319,7 @@ static char *get_kernel_version(const char *root_dir) char *name, *tmp; const char *prefix = "Linux version "; - sprintf(version, "%s/proc/version", root_dir); + snprintf(version, sizeof(version), "%s/proc/version", root_dir); file = fopen(version, "r"); if (!file) return NULL; diff --git a/tools/perf/util/metricgroup.c b/tools/perf/util/metricgroup.c index 595b83142d2c..238c3e605bfe 100644 --- a/tools/perf/util/metricgroup.c +++ b/tools/perf/util/metricgroup.c @@ -364,7 +364,7 @@ static int setup_metric_events(const char *pmu, struct hashmap *ids, static bool match_metric_or_groups(const char *metric_or_groups, const char *sought) { int len; - char *m; + const char *m; if (!sought) return false; @@ -439,11 +439,10 @@ static const char *code_characters = ",-=@"; static int encode_metric_id(struct strbuf *sb, const char *x) { - char *c; int ret = 0; for (; *x; x++) { - c = strchr(code_characters, *x); + const char *c = strchr(code_characters, *x); if (c) { ret = strbuf_addch(sb, '!'); if (ret) diff --git a/tools/perf/util/mmap.c b/tools/perf/util/mmap.c index a34726219af3..fcc093984683 100644 --- a/tools/perf/util/mmap.c +++ b/tools/perf/util/mmap.c @@ -88,10 +88,10 @@ static int perf_mmap__aio_alloc(struct mmap *map, int idx) static void perf_mmap__aio_free(struct mmap *map, int idx) { - if (map->aio.data[idx]) { - munmap(map->aio.data[idx], mmap__mmap_len(map)); - map->aio.data[idx] = NULL; - } + if (!map->aio.data || !map->aio.data[idx]) + return; + munmap(map->aio.data[idx], mmap__mmap_len(map)); + map->aio.data[idx] = NULL; } static int perf_mmap__aio_bind(struct mmap *map, int idx, struct perf_cpu cpu, int affinity) @@ -103,9 +103,15 @@ static int perf_mmap__aio_bind(struct mmap *map, int idx, struct perf_cpu cpu, i int err = 0; if (affinity != PERF_AFFINITY_SYS && cpu__max_node() > 1) { + int node; + data = map->aio.data[idx]; mmap_len = mmap__mmap_len(map); - node_index = cpu__get_node(cpu); + node = cpu__get_node(cpu); + /* -1 sign-extends to ULONG_MAX, wrapping bitmap_zalloc(0) and OOB __set_bit */ + if (node < 0) + return 0; + node_index = node; node_mask = bitmap_zalloc(node_index + 1); if (!node_mask) { pr_err("Failed to allocate node mask for mbind: error %m\n"); @@ -134,6 +140,8 @@ static int perf_mmap__aio_alloc(struct mmap *map, int idx) static void perf_mmap__aio_free(struct mmap *map, int idx) { + if (!map->aio.data) + return; zfree(&(map->aio.data[idx])); } diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index 3d1f975e8db9..bcdbbaadcbb3 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c @@ -328,7 +328,7 @@ static int perf_pmu__parse_scale(struct perf_pmu *pmu, struct perf_pmu_alias *al goto error; sret = read(fd, scale, sizeof(scale)-1); - if (sret < 0) + if (sret <= 0) goto error; if (scale[sret - 1] == '\n') @@ -360,7 +360,7 @@ static int perf_pmu__parse_unit(struct perf_pmu *pmu, struct perf_pmu_alias *ali return -1; sret = read(fd, alias->unit, UNIT_MAX_LEN); - if (sret < 0) + if (sret <= 0) goto error; close(fd); @@ -856,6 +856,12 @@ static char *pmu_id(const char *name) if (filename__read_str(path, &str, &len) < 0) return NULL; + /* empty identifier file — nothing useful */ + if (len == 0) { + free(str); + return NULL; + } + str[len - 1] = 0; /* remove line feed */ return str; diff --git a/tools/perf/util/print-events.c b/tools/perf/util/print-events.c index 4153124a9948..f5f828d49ef0 100644 --- a/tools/perf/util/print-events.c +++ b/tools/perf/util/print-events.c @@ -86,7 +86,7 @@ void print_sdt_events(const struct print_callbacks *print_cb, void *print_state) strlist__for_each_entry(sdt_name, sdtlist) { bool show_detail = false; - char *bid = strchr(sdt_name->s, '@'); + char *bid = (char *)strchr(sdt_name->s, '@'); char *evt_name = NULL; if (bid) @@ -97,14 +97,9 @@ void print_sdt_events(const struct print_callbacks *print_cb, void *print_state) } else { next_sdt_name = strlist__next(sdt_name); if (next_sdt_name) { - char *bid2 = strchr(next_sdt_name->s, '@'); - - if (bid2) - *bid2 = '\0'; - if (strcmp(sdt_name->s, next_sdt_name->s) == 0) - show_detail = true; - if (bid2) - *bid2 = '@'; + const char *bid2 = strchrnul(next_sdt_name->s, '@'); + + show_detail = strncmp(sdt_name->s, next_sdt_name->s, bid2 - next_sdt_name->s) == 0; } } last_sdt_name = sdt_name->s; @@ -271,7 +266,7 @@ void print_symbol_events(const struct print_callbacks *print_cb, void *print_sta } strlist__for_each_entry(nd, evt_name_list) { - char *alias = strstr(nd->s, " OR "); + char *alias = (char *)strstr(nd->s, " OR "); if (alias) { *alias = '\0'; diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 09af486c83e4..3a37f6df7e78 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -2186,9 +2186,10 @@ reader__read_event(struct reader *rd, struct perf_session *session, if (size < sizeof(struct perf_event_header) || (skip = rd->process(session, event, rd->file_pos, rd->path)) < 0) { - pr_err("%#" PRIx64 " [%#x]: failed to process type: %d [%s]\n", + errno = -skip; + pr_err("%#" PRIx64 " [%#x]: failed to process type: %d [%m]\n", rd->file_offset + rd->head, event->header.size, - event->header.type, strerror(-skip)); + event->header.type); err = skip; goto out; } @@ -2473,7 +2474,7 @@ bool perf_session__has_switch_events(struct perf_session *session) int map__set_kallsyms_ref_reloc_sym(struct map *map, const char *symbol_name, u64 addr) { - char *bracket; + char *bracket, *name; struct ref_reloc_sym *ref; struct kmap *kmap; @@ -2481,13 +2482,13 @@ int map__set_kallsyms_ref_reloc_sym(struct map *map, const char *symbol_name, u6 if (ref == NULL) return -ENOMEM; - ref->name = strdup(symbol_name); + ref->name = name = strdup(symbol_name); if (ref->name == NULL) { free(ref); return -ENOMEM; } - bracket = strchr(ref->name, ']'); + bracket = strchr(name, ']'); if (bracket) *bracket = '\0'; diff --git a/tools/perf/util/strlist.c b/tools/perf/util/strlist.c index 8a868cbeffae..50add72575e0 100644 --- a/tools/perf/util/strlist.c +++ b/tools/perf/util/strlist.c @@ -12,20 +12,16 @@ #include <linux/zalloc.h> static -struct rb_node *strlist__node_new(struct rblist *rblist, const void *entry) +struct rb_node *strlist__node_new(struct rblist *rblist __maybe_unused, const void *entry) { const char *s = entry; struct rb_node *rc = NULL; - struct strlist *strlist = container_of(rblist, struct strlist, rblist); struct str_node *snode = malloc(sizeof(*snode)); if (snode != NULL) { - if (strlist->dupstr) { - s = strdup(s); - if (s == NULL) - goto out_delete; - } - snode->s = s; + snode->s = strdup(s); + if (snode->s == NULL) + goto out_delete; rc = &snode->rb_node; } @@ -36,20 +32,18 @@ out_delete: return NULL; } -static void str_node__delete(struct str_node *snode, bool dupstr) +static void str_node__delete(struct str_node *snode) { - if (dupstr) - zfree((char **)&snode->s); + zfree((char **)&snode->s); free(snode); } static -void strlist__node_delete(struct rblist *rblist, struct rb_node *rb_node) +void strlist__node_delete(struct rblist *rblist __maybe_unused, struct rb_node *rb_node) { - struct strlist *slist = container_of(rblist, struct strlist, rblist); struct str_node *snode = container_of(rb_node, struct str_node, rb_node); - str_node__delete(snode, slist->dupstr); + str_node__delete(snode); } static int strlist__node_cmp(struct rb_node *rb_node, const void *entry) @@ -139,21 +133,25 @@ out: return err; } -static int strlist__parse_list(struct strlist *slist, const char *s, const char *subst_dir) +static int strlist__parse_list(struct strlist *slist, const char *list, const char *subst_dir) { - char *sep; + char *sep, *s = strdup(list), *sdup = s; int err; + if (s == NULL) + return -ENOMEM; + while ((sep = strchr(s, ',')) != NULL) { *sep = '\0'; err = strlist__parse_list_entry(slist, s, subst_dir); - *sep = ','; if (err != 0) return err; s = sep + 1; } - return *s ? strlist__parse_list_entry(slist, s, subst_dir) : 0; + err = *s ? strlist__parse_list_entry(slist, s, subst_dir) : 0; + free(sdup); + return err; } struct strlist *strlist__new(const char *list, const struct strlist_config *config) @@ -161,12 +159,10 @@ struct strlist *strlist__new(const char *list, const struct strlist_config *conf struct strlist *slist = malloc(sizeof(*slist)); if (slist != NULL) { - bool dupstr = true; bool file_only = false; const char *dirname = NULL; if (config) { - dupstr = !config->dont_dupstr; dirname = config->dirname; file_only = config->file_only; } @@ -176,7 +172,6 @@ struct strlist *strlist__new(const char *list, const struct strlist_config *conf slist->rblist.node_new = strlist__node_new; slist->rblist.node_delete = strlist__node_delete; - slist->dupstr = dupstr; slist->file_only = file_only; if (list && strlist__parse_list(slist, list, dirname) != 0) diff --git a/tools/perf/util/strlist.h b/tools/perf/util/strlist.h index 7e82c71dcc42..3e9533e66ca9 100644 --- a/tools/perf/util/strlist.h +++ b/tools/perf/util/strlist.h @@ -14,7 +14,6 @@ struct str_node { struct strlist { struct rblist rblist; - bool dupstr; bool file_only; }; @@ -24,7 +23,6 @@ struct strlist { * found */ struct strlist_config { - bool dont_dupstr; bool file_only; const char *dirname; }; diff --git a/tools/perf/util/svghelper.c b/tools/perf/util/svghelper.c index b1d259f590e9..7c862c33321e 100644 --- a/tools/perf/util/svghelper.c +++ b/tools/perf/util/svghelper.c @@ -47,13 +47,13 @@ static double cpu2slot(int cpu) } static int *topology_map; +static int topology_map_size; static double cpu2y(int cpu) { - if (topology_map) + if (topology_map && cpu >= 0 && cpu < topology_map_size) return cpu2slot(topology_map[cpu]) * SLOT_MULT; - else - return cpu2slot(cpu) * SLOT_MULT; + return cpu2slot(cpu) * SLOT_MULT; } static double time2pixels(u64 __time) @@ -735,7 +735,8 @@ static int str_to_bitmap(char *s, cpumask_t *b, int nr_cpus) return -1; perf_cpu_map__for_each_cpu(cpu, idx, map) { - if (cpu.cpu >= nr_cpus) { + /* perf_cpu_map__new("") returns cpu.cpu == -1 */ + if (cpu.cpu < 0 || cpu.cpu >= nr_cpus) { ret = -1; break; } @@ -793,6 +794,7 @@ int svg_build_topology_map(struct perf_env *env) fprintf(stderr, "topology: no memory\n"); goto exit; } + topology_map_size = nr_cpus; for (i = 0; i < nr_cpus; i++) topology_map[i] = -1; diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index 9602cc51dcc6..140f3d5a7241 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c @@ -217,7 +217,7 @@ bool filename__has_section(const char *filename, const char *sec) GElf_Shdr shdr; bool found = false; - fd = open(filename, O_RDONLY); + fd = open(filename, O_RDONLY | O_CLOEXEC); if (fd < 0) return false; @@ -836,10 +836,24 @@ static int elf_read_build_id(Elf *elf, void *bf, size_t size) ptr = data->d_buf; while (ptr < (data->d_buf + data->d_size)) { GElf_Nhdr *nhdr = ptr; - size_t namesz = NOTE_ALIGN(nhdr->n_namesz), - descsz = NOTE_ALIGN(nhdr->n_descsz); + size_t namesz, descsz, remaining; const char *name; + /* ensure the note header fits within the section */ + if (ptr + sizeof(*nhdr) > data->d_buf + data->d_size) + break; + + namesz = NOTE_ALIGN(nhdr->n_namesz); + descsz = NOTE_ALIGN(nhdr->n_descsz); + + /* validate individually to avoid size_t overflow on 32-bit */ + remaining = data->d_buf + data->d_size - ptr - sizeof(*nhdr); + if (namesz > remaining || descsz > remaining - namesz) { + pr_warning("%s: oversized note: n_namesz=%u, n_descsz=%u\n", + __func__, nhdr->n_namesz, nhdr->n_descsz); + break; + } + ptr += sizeof(*nhdr); name = ptr; ptr += namesz; @@ -860,20 +874,20 @@ out: return err; } -static int read_build_id(const char *filename, struct build_id *bid, bool block) +static int read_build_id(const char *filename, struct build_id *bid) { size_t size = sizeof(bid->data); int fd, err; Elf *elf; - err = libbfd__read_build_id(filename, bid, block); + err = libbfd__read_build_id(filename, bid); if (err >= 0) goto out; if (size < BUILD_ID_SIZE) goto out; - fd = open(filename, block ? O_RDONLY : (O_RDONLY | O_NONBLOCK)); + fd = open(filename, O_RDONLY | O_CLOEXEC); if (fd < 0) goto out; @@ -894,7 +908,7 @@ out: return err; } -int filename__read_build_id(const char *filename, struct build_id *bid, bool block) +int filename__read_build_id(const char *filename, struct build_id *bid) { struct kmod_path m = { .name = NULL, }; char path[PATH_MAX]; @@ -903,6 +917,10 @@ int filename__read_build_id(const char *filename, struct build_id *bid, bool blo if (!filename) return -EFAULT; + errno = 0; + if (!is_regular_file(filename)) + return errno == 0 ? -EWOULDBLOCK : -errno; + err = kmod_path__parse(&m, filename); if (err) return -1; @@ -917,13 +935,14 @@ int filename__read_build_id(const char *filename, struct build_id *bid, bool blo return -1; } close(fd); - filename = path; - block = true; + /* non-empty path means a temp file was created */ + if (path[0] != '\0') + filename = path; } - err = read_build_id(filename, bid, block); + err = read_build_id(filename, bid); - if (m.comp) + if (m.comp && filename == path) unlink(filename); return err; } @@ -933,7 +952,7 @@ int sysfs__read_build_id(const char *filename, struct build_id *bid) size_t size = sizeof(bid->data); int fd, err = -1; - fd = open(filename, O_RDONLY); + fd = open(filename, O_RDONLY | O_CLOEXEC); if (fd < 0) goto out; @@ -959,17 +978,28 @@ int sysfs__read_build_id(const char *filename, struct build_id *bid) err = 0; break; } - } else if (read(fd, bf, descsz) != (ssize_t)descsz) - break; + } else { + /* descsz from untrusted file — clamp to buffer */ + if (descsz > sizeof(bf)) + break; + if (read(fd, bf, descsz) != (ssize_t)descsz) + break; + } } else { - int n = namesz + descsz; + size_t n; - if (n > (int)sizeof(bf)) { + /* int sum of namesz+descsz can overflow negative, bypassing size check */ + if (namesz > sizeof(bf) || descsz > sizeof(bf) - namesz) { n = sizeof(bf); pr_debug("%s: truncating reading of build id in sysfs file %s: n_namesz=%u, n_descsz=%u.\n", __func__, filename, nhdr.n_namesz, nhdr.n_descsz); + } else { + n = namesz + descsz; } - if (read(fd, bf, n) != n) + /* no valid note has both namesz and descsz zero */ + if (n == 0) + break; + if (read(fd, bf, n) != (ssize_t)n) break; } } @@ -993,7 +1023,7 @@ int filename__read_debuglink(const char *filename, char *debuglink, if (err >= 0) goto out; - fd = open(filename, O_RDONLY); + fd = open(filename, O_RDONLY | O_CLOEXEC); if (fd < 0) goto out; @@ -1022,7 +1052,14 @@ int filename__read_debuglink(const char *filename, char *debuglink, goto out_elf_end; /* the start of this section is a zero-terminated string */ - strncpy(debuglink, data->d_buf, size); + if (data->d_size > 0) { + size_t len = min(size - 1, data->d_size); + + memcpy(debuglink, data->d_buf, len); + debuglink[len] = '\0'; + } else { + debuglink[0] = '\0'; + } err = 0; @@ -1102,14 +1139,14 @@ static Elf *read_gnu_debugdata(struct dso *dso, Elf *elf, const char *name, int wrapped = fmemopen(scn_data->d_buf, scn_data->d_size, "r"); if (!wrapped) { - pr_debug("%s: fmemopen: %s\n", __func__, strerror(errno)); + pr_debug("%s: fmemopen: %m\n", __func__); *dso__load_errno(dso) = -errno; return NULL; } temp_fd = mkstemp(temp_filename); if (temp_fd < 0) { - pr_debug("%s: mkstemp: %s\n", __func__, strerror(errno)); + pr_debug("%s: mkstemp: %m\n", __func__); *dso__load_errno(dso) = -errno; fclose(wrapped); return NULL; @@ -1151,7 +1188,7 @@ int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name, type = dso__symtab_type(dso); } else { - fd = open(name, O_RDONLY); + fd = open(name, O_RDONLY | O_CLOEXEC); if (fd < 0) { *dso__load_errno(dso) = errno; return -1; @@ -1941,7 +1978,7 @@ static int kcore__open(struct kcore *kcore, const char *filename) { GElf_Ehdr *ehdr; - kcore->fd = open(filename, O_RDONLY); + kcore->fd = open(filename, O_RDONLY | O_CLOEXEC); if (kcore->fd == -1) return -1; @@ -1974,7 +2011,7 @@ static int kcore__init(struct kcore *kcore, char *filename, int elfclass, if (temp) kcore->fd = mkstemp(filename); else - kcore->fd = open(filename, O_WRONLY | O_CREAT | O_EXCL, 0400); + kcore->fd = open(filename, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, 0400); if (kcore->fd == -1) return -1; @@ -2450,11 +2487,11 @@ static int kcore_copy__compare_files(const char *from_filename, { int from, to, err = -1; - from = open(from_filename, O_RDONLY); + from = open(from_filename, O_RDONLY | O_CLOEXEC); if (from < 0) return -1; - to = open(to_filename, O_RDONLY); + to = open(to_filename, O_RDONLY | O_CLOEXEC); if (to < 0) goto out_close_from; @@ -2872,7 +2909,7 @@ int get_sdt_note_list(struct list_head *head, const char *target) Elf *elf; int fd, ret; - fd = open(target, O_RDONLY); + fd = open(target, O_RDONLY | O_CLOEXEC); if (fd < 0) return -EBADF; diff --git a/tools/perf/util/symbol-minimal.c b/tools/perf/util/symbol-minimal.c index aeb253248895..0a71d1463952 100644 --- a/tools/perf/util/symbol-minimal.c +++ b/tools/perf/util/symbol-minimal.c @@ -1,3 +1,4 @@ +#include "debug.h" #include "dso.h" #include "symbol.h" #include "symsrc.h" @@ -44,7 +45,7 @@ static int read_build_id(void *note_data, size_t note_len, struct build_id *bid, ptr = note_data; while ((ptr + sizeof(*nhdr)) < (note_data + note_len)) { const char *name; - size_t namesz, descsz; + size_t namesz, descsz, remaining; nhdr = ptr; if (need_swap) { @@ -56,6 +57,14 @@ static int read_build_id(void *note_data, size_t note_len, struct build_id *bid, namesz = NOTE_ALIGN(nhdr->n_namesz); descsz = NOTE_ALIGN(nhdr->n_descsz); + /* validate individually to avoid size_t overflow on 32-bit */ + remaining = note_data + note_len - ptr - sizeof(*nhdr); + if (namesz > remaining || descsz > remaining - namesz) { + pr_warning("%s: oversized note: n_namesz=%u, n_descsz=%u\n", + __func__, nhdr->n_namesz, nhdr->n_descsz); + break; + } + ptr += sizeof(*nhdr); name = ptr; ptr += namesz; @@ -85,7 +94,7 @@ int filename__read_debuglink(const char *filename __maybe_unused, /* * Just try PT_NOTE header otherwise fails */ -int filename__read_build_id(const char *filename, struct build_id *bid, bool block) +int filename__read_build_id(const char *filename, struct build_id *bid) { int fd, ret = -1; bool need_swap = false, elf32; @@ -102,7 +111,14 @@ int filename__read_build_id(const char *filename, struct build_id *bid, bool blo void *phdr, *buf = NULL; ssize_t phdr_size, ehdr_size, buf_size = 0; - fd = open(filename, block ? O_RDONLY : (O_RDONLY | O_NONBLOCK)); + if (!filename) + return -EFAULT; + + errno = 0; + if (!is_regular_file(filename)) + return errno == 0 ? -EWOULDBLOCK : -errno; + + fd = open(filename, O_RDONLY); if (fd < 0) return -1; @@ -159,7 +175,7 @@ int filename__read_build_id(const char *filename, struct build_id *bid, bool blo if (elf32) { hdrs.phdr32[i].p_type = bswap_32(hdrs.phdr32[i].p_type); hdrs.phdr32[i].p_offset = bswap_32(hdrs.phdr32[i].p_offset); - hdrs.phdr32[i].p_filesz = bswap_32(hdrs.phdr32[i].p_offset); + hdrs.phdr32[i].p_filesz = bswap_32(hdrs.phdr32[i].p_filesz); } else { hdrs.phdr64[i].p_type = bswap_32(hdrs.phdr64[i].p_type); hdrs.phdr64[i].p_offset = bswap_64(hdrs.phdr64[i].p_offset); @@ -170,6 +186,9 @@ int filename__read_build_id(const char *filename, struct build_id *bid, bool blo continue; p_filesz = elf32 ? hdrs.phdr32[i].p_filesz : hdrs.phdr64[i].p_filesz; + /* ssize_t can go negative with crafted ELF p_filesz values */ + if (p_filesz <= 0) + continue; if (p_filesz > buf_size) { void *tmp; @@ -323,7 +342,7 @@ int dso__load_sym(struct dso *dso, struct map *map __maybe_unused, if (ret >= 0) RC_CHK_ACCESS(dso)->is_64_bit = ret; - if (filename__read_build_id(ss->name, &bid, /*block=*/true) > 0) + if (filename__read_build_id(ss->name, &bid) > 0) dso__set_build_id(dso, &bid); return 0; } diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index f6c268c588a5..af0df841d640 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -1748,14 +1748,13 @@ int dso__load(struct dso *dso, struct map *map) /* * Read the build id if possible. This is required for - * DSO_BINARY_TYPE__BUILDID_DEBUGINFO to work. Don't block in case path - * isn't for a regular file. + * DSO_BINARY_TYPE__BUILDID_DEBUGINFO to work. */ if (!dso__has_build_id(dso)) { struct build_id bid = { .size = 0, }; __symbol__join_symfs(name, PATH_MAX, dso__long_name(dso)); - if (filename__read_build_id(name, &bid, /*block=*/false) > 0) + if (filename__read_build_id(name, &bid) > 0) dso__set_build_id(dso, &bid); } @@ -2159,7 +2158,7 @@ static int dso__load_guest_kernel_sym(struct dso *dso, struct map *map) if (!kallsyms_filename) return -1; } else { - sprintf(path, "%s/proc/kallsyms", machine->root_dir); + snprintf(path, sizeof(path), "%s/proc/kallsyms", machine->root_dir); kallsyms_filename = path; } diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 347106218799..3fb5d146d9b1 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -140,7 +140,7 @@ struct symbol *dso__next_symbol(struct symbol *sym); enum dso_type dso__type_fd(int fd); -int filename__read_build_id(const char *filename, struct build_id *id, bool block); +int filename__read_build_id(const char *filename, struct build_id *id); int sysfs__read_build_id(const char *filename, struct build_id *bid); int modules__parse(const char *filename, void *arg, int (*process_module)(void *arg, const char *name, diff --git a/tools/perf/util/synthetic-events.c b/tools/perf/util/synthetic-events.c index c85d219928d4..c5b0ba775098 100644 --- a/tools/perf/util/synthetic-events.c +++ b/tools/perf/util/synthetic-events.c @@ -401,7 +401,7 @@ static void perf_record_mmap2__read_build_id(struct perf_record_mmap2 *event, nsi = nsinfo__new(event->pid); nsinfo__mountns_enter(nsi, &nc); - rc = filename__read_build_id(event->filename, &bid, /*block=*/false) > 0 ? 0 : -1; + rc = filename__read_build_id(event->filename, &bid) > 0 ? 0 : -1; nsinfo__mountns_exit(&nc); nsinfo__put(nsi); diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index aa9c58bbf9d3..9409d32dce04 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c @@ -294,6 +294,11 @@ int thread__set_comm_from_proc(struct thread *thread) if (!(snprintf(path, sizeof(path), "%d/task/%d/comm", thread__pid(thread), thread__tid(thread)) >= (int)sizeof(path)) && procfs__read_str(path, &comm, &sz) == 0) { + /* sz==0: read got nothing, e.g. race during exit teardown */ + if (sz == 0) { + free(comm); + return -1; + } comm[sz - 1] = '\0'; err = thread__set_comm(thread, comm, 0); } diff --git a/tools/perf/util/thread_map.c b/tools/perf/util/thread_map.c index ca193c1374ed..48c70f149e92 100644 --- a/tools/perf/util/thread_map.c +++ b/tools/perf/util/thread_map.c @@ -164,19 +164,16 @@ static struct perf_thread_map *thread_map__new_by_pid_str(const char *pid_str) struct dirent **namelist = NULL; int i, j = 0; pid_t pid, prev_pid = INT_MAX; - char *end_ptr; struct str_node *pos; - struct strlist_config slist_config = { .dont_dupstr = true, }; - struct strlist *slist = strlist__new(pid_str, &slist_config); + struct strlist *slist = strlist__new(pid_str, NULL); if (!slist) return NULL; strlist__for_each_entry(pos, slist) { - pid = strtol(pos->s, &end_ptr, 10); + pid = strtol(pos->s, NULL, 10); - if (pid == INT_MIN || pid == INT_MAX || - (*end_ptr != '\0' && *end_ptr != ',')) + if (pid == INT_MIN || pid == INT_MAX) goto out_free_threads; if (pid == prev_pid) @@ -223,24 +220,21 @@ struct perf_thread_map *thread_map__new_by_tid_str(const char *tid_str) struct perf_thread_map *threads = NULL, *nt; int ntasks = 0; pid_t tid, prev_tid = INT_MAX; - char *end_ptr; struct str_node *pos; - struct strlist_config slist_config = { .dont_dupstr = true, }; struct strlist *slist; /* perf-stat expects threads to be generated even if tid not given */ if (!tid_str) return perf_thread_map__new_dummy(); - slist = strlist__new(tid_str, &slist_config); + slist = strlist__new(tid_str, NULL); if (!slist) return NULL; strlist__for_each_entry(pos, slist) { - tid = strtol(pos->s, &end_ptr, 10); + tid = strtol(pos->s, NULL, 10); - if (tid == INT_MIN || tid == INT_MAX || - (*end_ptr != '\0' && *end_ptr != ',')) + if (tid == INT_MIN || tid == INT_MAX) goto out_free_threads; if (tid == prev_tid) diff --git a/tools/perf/util/time-utils.c b/tools/perf/util/time-utils.c index 1b91ccd4d523..d43c4577d7eb 100644 --- a/tools/perf/util/time-utils.c +++ b/tools/perf/util/time-utils.c @@ -325,7 +325,7 @@ static int percent_comma_split(struct perf_time_interval *ptime_buf, int num, } static int one_percent_convert(struct perf_time_interval *ptime_buf, - const char *ostr, u64 start, u64 end, char *c) + const char *ostr, u64 start, u64 end, const char *c) { char *str; int len = strlen(ostr), ret; @@ -358,7 +358,7 @@ static int one_percent_convert(struct perf_time_interval *ptime_buf, int perf_time__percent_parse_str(struct perf_time_interval *ptime_buf, int num, const char *ostr, u64 start, u64 end) { - char *c; + const char *c; /* * ostr example: diff --git a/tools/perf/util/tp_pmu.c b/tools/perf/util/tp_pmu.c index eddb9807131a..c2be8c9f9084 100644 --- a/tools/perf/util/tp_pmu.c +++ b/tools/perf/util/tp_pmu.c @@ -192,7 +192,7 @@ bool tp_pmu__have_event(struct perf_pmu *pmu __maybe_unused, const char *name) char *dup_name, *colon; int id; - colon = strchr(name, ':'); + colon = strchr((char *)name, ':'); if (colon == NULL) return false; diff --git a/tools/perf/util/trace-event-info.c b/tools/perf/util/trace-event-info.c index c8755679281e..45774722f249 100644 --- a/tools/perf/util/trace-event-info.c +++ b/tools/perf/util/trace-event-info.c @@ -482,7 +482,7 @@ char *tracepoint_id_to_name(u64 config) static struct tracepoint_path *tracepoint_name_to_path(const char *name) { struct tracepoint_path *path = zalloc(sizeof(*path)); - char *str = strchr(name, ':'); + const char *str = strchr(name, ':'); if (path == NULL || str == NULL) { free(path); diff --git a/tools/perf/util/units.c b/tools/perf/util/units.c index 4c6a86e1cb54..0bbacf5a29aa 100644 --- a/tools/perf/util/units.c +++ b/tools/perf/util/units.c @@ -12,7 +12,7 @@ unsigned long parse_tag_value(const char *str, struct parse_tag *tags) struct parse_tag *i = tags; while (i->tag) { - char *s = strchr(str, i->tag); + const char *s = strchr(str, i->tag); if (s) { unsigned long int value; |
