summaryrefslogtreecommitdiff
path: root/tools/perf
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf')
-rw-r--r--tools/perf/Documentation/perf-inject.txt5
-rw-r--r--tools/perf/Makefile.config1
-rw-r--r--tools/perf/arch/arm64/util/header.c1
-rw-r--r--tools/perf/arch/common.c4
-rw-r--r--tools/perf/arch/powerpc/util/auxtrace.c6
-rw-r--r--tools/perf/arch/x86/tests/amd-ibs-period.c2
-rw-r--r--tools/perf/arch/x86/tests/bp-modify.c30
-rw-r--r--tools/perf/bench/inject-buildid.c2
-rw-r--r--tools/perf/bench/uprobe.c2
-rw-r--r--tools/perf/builtin-annotate.c3
-rw-r--r--tools/perf/builtin-buildid-cache.c12
-rw-r--r--tools/perf/builtin-buildid-list.c4
-rw-r--r--tools/perf/builtin-c2c.c24
-rw-r--r--tools/perf/builtin-daemon.c8
-rw-r--r--tools/perf/builtin-diff.c15
-rw-r--r--tools/perf/builtin-inject.c157
-rw-r--r--tools/perf/builtin-list.c4
-rw-r--r--tools/perf/builtin-probe.c3
-rw-r--r--tools/perf/builtin-record.c25
-rw-r--r--tools/perf/builtin-report.c3
-rw-r--r--tools/perf/builtin-sched.c122
-rw-r--r--tools/perf/builtin-stat.c59
-rw-r--r--tools/perf/builtin-trace.c28
-rw-r--r--tools/perf/jvmti/libjvmti.c2
-rw-r--r--tools/perf/perf.c18
-rw-r--r--tools/perf/tests/code-reading.c7
-rw-r--r--tools/perf/tests/pe-file-parsing.c4
-rw-r--r--tools/perf/tests/pmu-events.c24
-rw-r--r--tools/perf/tests/sdt.c2
-rw-r--r--tools/perf/ui/browser.c3
-rw-r--r--tools/perf/ui/browsers/annotate.c5
-rw-r--r--tools/perf/util/bpf-event.c66
-rw-r--r--tools/perf/util/bpf-event.h4
-rw-r--r--tools/perf/util/bpf-utils.c20
-rw-r--r--tools/perf/util/bpf_lock_contention.c2
-rw-r--r--tools/perf/util/build-id.c4
-rw-r--r--tools/perf/util/cap.c3
-rw-r--r--tools/perf/util/cpumap.c49
-rw-r--r--tools/perf/util/cs-etm-base.c4
-rw-r--r--tools/perf/util/cs-etm-decoder/cs-etm-decoder.c21
-rw-r--r--tools/perf/util/cs-etm.c283
-rw-r--r--tools/perf/util/cs-etm.h8
-rw-r--r--tools/perf/util/data.c29
-rw-r--r--tools/perf/util/debuginfo.c11
-rw-r--r--tools/perf/util/demangle-java.c2
-rw-r--r--tools/perf/util/disasm.c7
-rw-r--r--tools/perf/util/dso.c65
-rw-r--r--tools/perf/util/dsos.c4
-rw-r--r--tools/perf/util/env.h14
-rw-r--r--tools/perf/util/event.c15
-rw-r--r--tools/perf/util/evlist.c34
-rw-r--r--tools/perf/util/evsel.c17
-rw-r--r--tools/perf/util/header.c57
-rw-r--r--tools/perf/util/hist.c7
-rw-r--r--tools/perf/util/hwmon_pmu.c26
-rw-r--r--tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.c11
-rw-r--r--tools/perf/util/jitdump.c5
-rw-r--r--tools/perf/util/libbfd.c11
-rw-r--r--tools/perf/util/libbfd.h5
-rw-r--r--tools/perf/util/lzma.c6
-rw-r--r--tools/perf/util/machine.c10
-rw-r--r--tools/perf/util/metricgroup.c5
-rw-r--r--tools/perf/util/mmap.c18
-rw-r--r--tools/perf/util/pmu.c10
-rw-r--r--tools/perf/util/print-events.c15
-rw-r--r--tools/perf/util/session.c11
-rw-r--r--tools/perf/util/strlist.c37
-rw-r--r--tools/perf/util/strlist.h2
-rw-r--r--tools/perf/util/svghelper.c10
-rw-r--r--tools/perf/util/symbol-elf.c91
-rw-r--r--tools/perf/util/symbol-minimal.c29
-rw-r--r--tools/perf/util/symbol.c7
-rw-r--r--tools/perf/util/symbol.h2
-rw-r--r--tools/perf/util/synthetic-events.c2
-rw-r--r--tools/perf/util/thread.c5
-rw-r--r--tools/perf/util/thread_map.c18
-rw-r--r--tools/perf/util/time-utils.c4
-rw-r--r--tools/perf/util/tp_pmu.c2
-rw-r--r--tools/perf/util/trace-event-info.c2
-rw-r--r--tools/perf/util/units.c2
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;