summaryrefslogtreecommitdiff
path: root/tools/perf/util/header.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf/util/header.c')
-rw-r--r--tools/perf/util/header.c1028
1 files changed, 853 insertions, 175 deletions
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index d06aa86352d3..f30e48eb3fc3 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -44,6 +44,7 @@
#include "build-id.h"
#include "data.h"
#include <api/fs/fs.h>
+#include <api/io_dir.h>
#include "asm/bug.h"
#include "tool.h"
#include "time-utils.h"
@@ -53,6 +54,7 @@
#include "bpf-event.h"
#include "bpf-utils.h"
#include "clockid.h"
+#include "cacheline.h"
#include <linux/ctype.h>
#include <internal/lib.h>
@@ -61,6 +63,15 @@
#include <event-parse.h>
#endif
+#define MAX_BPF_DATA_LEN (256 * 1024 * 1024)
+#define MAX_BPF_PROGS 131072
+#define MAX_CACHE_ENTRIES 32768
+#define MAX_GROUP_DESC 32768
+#define MAX_NUMA_NODES 4096
+#define MAX_PMU_CAPS 512
+#define MAX_PMU_MAPPINGS 4096
+#define MAX_SCHED_DOMAINS 64
+
/*
* magic2 = "PERFILE2"
* must be a numerical value to let the endianness
@@ -75,6 +86,7 @@ static const u64 __perf_magic2 = 0x32454c4946524550ULL;
static const u64 __perf_magic2_sw = 0x50455246494c4532ULL;
#define PERF_MAGIC __perf_magic2
+#define DNAME_LEN 16
const char perf_version_string[] = PERF_VERSION;
@@ -304,16 +316,19 @@ static int do_read_bitmap(struct feat_fd *ff, unsigned long **pset, u64 *psize)
return 0;
}
-#ifdef HAVE_LIBTRACEEVENT
static int write_tracing_data(struct feat_fd *ff,
- struct evlist *evlist)
+ struct evlist *evlist __maybe_unused)
{
if (WARN(ff->buf, "Error: calling %s in pipe-mode.\n", __func__))
return -1;
+#ifdef HAVE_LIBTRACEEVENT
return read_tracing_data(ff->fd, &evlist->core.entries);
-}
+#else
+ pr_err("ERROR: Trying to write tracing data without libtraceevent support.\n");
+ return -1;
#endif
+}
static int write_build_id(struct feat_fd *ff,
struct evlist *evlist __maybe_unused)
@@ -334,7 +349,6 @@ static int write_build_id(struct feat_fd *ff,
pr_debug("failed to write buildid table\n");
return err;
}
- perf_session__cache_build_ids(session);
return 0;
}
@@ -378,6 +392,21 @@ static int write_arch(struct feat_fd *ff,
return do_write_string(ff, uts.machine);
}
+static int write_e_machine(struct feat_fd *ff,
+ struct evlist *evlist __maybe_unused)
+{
+ /* e_machine expanded from 16 to 32-bits for alignment. */
+ uint32_t e_flags;
+ uint32_t e_machine = perf_session__e_machine(evlist->session, &e_flags);
+ int ret;
+
+ ret = do_write(ff, &e_machine, sizeof(e_machine));
+ if (ret)
+ return ret;
+
+ return do_write(ff, &e_flags, sizeof(e_flags));
+}
+
static int write_version(struct feat_fd *ff,
struct evlist *evlist __maybe_unused)
{
@@ -556,6 +585,7 @@ static int write_event_desc(struct feat_fd *ff,
static int write_cmdline(struct feat_fd *ff,
struct evlist *evlist __maybe_unused)
{
+ struct perf_env *env = &ff->ph->env;
char pbuf[MAXPATHLEN], *buf;
int i, ret, n;
@@ -563,7 +593,7 @@ static int write_cmdline(struct feat_fd *ff,
buf = perf_exe(pbuf, MAXPATHLEN);
/* account for binary path */
- n = perf_env.nr_cmdline + 1;
+ n = env->nr_cmdline + 1;
ret = do_write(ff, &n, sizeof(n));
if (ret < 0)
@@ -573,8 +603,8 @@ static int write_cmdline(struct feat_fd *ff,
if (ret < 0)
return ret;
- for (i = 0 ; i < perf_env.nr_cmdline; i++) {
- ret = do_write_string(ff, perf_env.cmdline_argv[i]);
+ for (i = 0 ; i < env->nr_cmdline; i++) {
+ ret = do_write_string(ff, env->cmdline_argv[i]);
if (ret < 0)
return ret;
}
@@ -585,6 +615,7 @@ static int write_cmdline(struct feat_fd *ff,
static int write_cpu_topology(struct feat_fd *ff,
struct evlist *evlist __maybe_unused)
{
+ struct perf_env *env = &ff->ph->env;
struct cpu_topology *tp;
u32 i;
int ret, j;
@@ -612,17 +643,17 @@ static int write_cpu_topology(struct feat_fd *ff,
break;
}
- ret = perf_env__read_cpu_topology_map(&perf_env);
+ ret = perf_env__read_cpu_topology_map(env);
if (ret < 0)
goto done;
- for (j = 0; j < perf_env.nr_cpus_avail; j++) {
- ret = do_write(ff, &perf_env.cpu[j].core_id,
- sizeof(perf_env.cpu[j].core_id));
+ for (j = 0; j < env->nr_cpus_avail; j++) {
+ ret = do_write(ff, &env->cpu[j].core_id,
+ sizeof(env->cpu[j].core_id));
if (ret < 0)
return ret;
- ret = do_write(ff, &perf_env.cpu[j].socket_id,
- sizeof(perf_env.cpu[j].socket_id));
+ ret = do_write(ff, &env->cpu[j].socket_id,
+ sizeof(env->cpu[j].socket_id));
if (ret < 0)
return ret;
}
@@ -640,9 +671,9 @@ static int write_cpu_topology(struct feat_fd *ff,
goto done;
}
- for (j = 0; j < perf_env.nr_cpus_avail; j++) {
- ret = do_write(ff, &perf_env.cpu[j].die_id,
- sizeof(perf_env.cpu[j].die_id));
+ for (j = 0; j < env->nr_cpus_avail; j++) {
+ ret = do_write(ff, &env->cpu[j].die_id,
+ sizeof(env->cpu[j].die_id));
if (ret < 0)
return ret;
}
@@ -1008,20 +1039,20 @@ static int write_dir_format(struct feat_fd *ff,
return do_write(ff, &data->dir.version, sizeof(data->dir.version));
}
-#ifdef HAVE_LIBBPF_SUPPORT
-static int write_bpf_prog_info(struct feat_fd *ff,
+static int write_bpf_prog_info(struct feat_fd *ff __maybe_unused,
struct evlist *evlist __maybe_unused)
{
+#ifdef HAVE_LIBBPF_SUPPORT
struct perf_env *env = &ff->ph->env;
struct rb_root *root;
struct rb_node *next;
- int ret;
+ int ret = 0;
down_read(&env->bpf_progs.lock);
ret = do_write(ff, &env->bpf_progs.infos_cnt,
sizeof(env->bpf_progs.infos_cnt));
- if (ret < 0)
+ if (ret < 0 || env->bpf_progs.infos_cnt == 0)
goto out;
root = &env->bpf_progs.infos;
@@ -1049,22 +1080,27 @@ static int write_bpf_prog_info(struct feat_fd *ff,
out:
up_read(&env->bpf_progs.lock);
return ret;
+#else
+ pr_err("ERROR: Trying to write bpf_prog_info without libbpf support.\n");
+ return -1;
+#endif // HAVE_LIBBPF_SUPPORT
}
-static int write_bpf_btf(struct feat_fd *ff,
+static int write_bpf_btf(struct feat_fd *ff __maybe_unused,
struct evlist *evlist __maybe_unused)
{
+#ifdef HAVE_LIBBPF_SUPPORT
struct perf_env *env = &ff->ph->env;
struct rb_root *root;
struct rb_node *next;
- int ret;
+ int ret = 0;
down_read(&env->bpf_progs.lock);
ret = do_write(ff, &env->bpf_progs.btfs_cnt,
sizeof(env->bpf_progs.btfs_cnt));
- if (ret < 0)
+ if (ret < 0 || env->bpf_progs.btfs_cnt == 0)
goto out;
root = &env->bpf_progs.btfs;
@@ -1082,8 +1118,11 @@ static int write_bpf_btf(struct feat_fd *ff,
out:
up_read(&env->bpf_progs.lock);
return ret;
-}
+#else
+ pr_err("ERROR: Trying to write btf data without libbpf support.\n");
+ return -1;
#endif // HAVE_LIBBPF_SUPPORT
+}
static int cpu_cache_level__sort(const void *a, const void *b)
{
@@ -1286,6 +1325,19 @@ out:
return ret;
}
+static int write_cln_size(struct feat_fd *ff,
+ struct evlist *evlist __maybe_unused)
+{
+ int cln_size = cacheline_size();
+
+ if (!cln_size)
+ cln_size = DEFAULT_CACHELINE_SIZE;
+
+ ff->ph->env.cln_size = cln_size;
+
+ return do_write(ff, &cln_size, sizeof(cln_size));
+}
+
static int write_stat(struct feat_fd *ff __maybe_unused,
struct evlist *evlist __maybe_unused)
{
@@ -1311,11 +1363,11 @@ static int memory_node__read(struct memory_node *n, unsigned long idx)
{
unsigned int phys, size = 0;
char path[PATH_MAX];
- struct dirent *ent;
- DIR *dir;
+ struct io_dirent64 *ent;
+ struct io_dir dir;
#define for_each_memory(mem, dir) \
- while ((ent = readdir(dir))) \
+ while ((ent = io_dir__readdir(&dir)) != NULL) \
if (strcmp(ent->d_name, ".") && \
strcmp(ent->d_name, "..") && \
sscanf(ent->d_name, "memory%u", &mem) == 1)
@@ -1324,9 +1376,9 @@ static int memory_node__read(struct memory_node *n, unsigned long idx)
"%s/devices/system/node/node%lu",
sysfs__mountpoint(), idx);
- dir = opendir(path);
- if (!dir) {
- pr_warning("failed: can't open memory sysfs data\n");
+ io_dir__init(&dir, open(path, O_CLOEXEC | O_DIRECTORY | O_RDONLY));
+ if (dir.dirfd < 0) {
+ pr_warning("failed: can't open memory sysfs data '%s'\n", path);
return -1;
}
@@ -1338,20 +1390,20 @@ static int memory_node__read(struct memory_node *n, unsigned long idx)
n->set = bitmap_zalloc(size);
if (!n->set) {
- closedir(dir);
+ close(dir.dirfd);
return -ENOMEM;
}
n->node = idx;
n->size = size;
- rewinddir(dir);
+ io_dir__rewinddir(&dir);
for_each_memory(phys, dir) {
__set_bit(phys, n->set);
}
- closedir(dir);
+ close(dir.dirfd);
return 0;
}
@@ -1374,8 +1426,8 @@ static int memory_node__sort(const void *a, const void *b)
static int build_mem_topology(struct memory_node **nodesp, u64 *cntp)
{
char path[PATH_MAX];
- struct dirent *ent;
- DIR *dir;
+ struct io_dirent64 *ent;
+ struct io_dir dir;
int ret = 0;
size_t cnt = 0, size = 0;
struct memory_node *nodes = NULL;
@@ -1383,14 +1435,14 @@ static int build_mem_topology(struct memory_node **nodesp, u64 *cntp)
scnprintf(path, PATH_MAX, "%s/devices/system/node/",
sysfs__mountpoint());
- dir = opendir(path);
- if (!dir) {
+ io_dir__init(&dir, open(path, O_CLOEXEC | O_DIRECTORY | O_RDONLY));
+ if (dir.dirfd < 0) {
pr_debug2("%s: couldn't read %s, does this arch have topology information?\n",
__func__, path);
return -1;
}
- while (!ret && (ent = readdir(dir))) {
+ while (!ret && (ent = io_dir__readdir(&dir))) {
unsigned int idx;
int r;
@@ -1419,7 +1471,7 @@ static int build_mem_topology(struct memory_node **nodesp, u64 *cntp)
cnt += 1;
}
out:
- closedir(dir);
+ close(dir.dirfd);
if (!ret) {
*cntp = cnt;
*nodesp = nodes;
@@ -1552,7 +1604,7 @@ static int __write_pmu_caps(struct feat_fd *ff, struct perf_pmu *pmu,
static int write_cpu_pmu_caps(struct feat_fd *ff,
struct evlist *evlist __maybe_unused)
{
- struct perf_pmu *cpu_pmu = perf_pmus__find("cpu");
+ struct perf_pmu *cpu_pmu = perf_pmus__find_core_pmu();
int ret;
if (!cpu_pmu)
@@ -1612,6 +1664,161 @@ static int write_pmu_caps(struct feat_fd *ff,
return 0;
}
+struct cpu_domain_map **build_cpu_domain_map(u32 *schedstat_version, u32 *max_sched_domains, u32 nr)
+{
+ char dname[DNAME_LEN], cpumask[MAX_NR_CPUS];
+ struct domain_info *domain_info;
+ struct cpu_domain_map **cd_map;
+ char cpulist[MAX_NR_CPUS];
+ char *line = NULL;
+ u32 cpu, domain;
+ u32 dcount = 0;
+ size_t len;
+ FILE *fp;
+
+ fp = fopen("/proc/schedstat", "r");
+ if (!fp) {
+ pr_err("Failed to open /proc/schedstat\n");
+ return NULL;
+ }
+
+ cd_map = zalloc(sizeof(*cd_map) * nr);
+ if (!cd_map)
+ goto out;
+
+ while (getline(&line, &len, fp) > 0) {
+ int retval;
+
+ if (strncmp(line, "version", 7) == 0) {
+ retval = sscanf(line, "version %d\n", schedstat_version);
+ if (retval != 1)
+ continue;
+
+ } else if (strncmp(line, "cpu", 3) == 0) {
+ retval = sscanf(line, "cpu%u %*s", &cpu);
+ if (retval == 1) {
+ cd_map[cpu] = zalloc(sizeof(*cd_map[cpu]));
+ if (!cd_map[cpu])
+ goto out_free_line;
+ cd_map[cpu]->cpu = cpu;
+ } else
+ continue;
+
+ dcount = 0;
+ } else if (strncmp(line, "domain", 6) == 0) {
+ struct domain_info **temp_domains;
+
+ dcount++;
+ temp_domains = realloc(cd_map[cpu]->domains, dcount * sizeof(domain_info));
+ if (!temp_domains)
+ goto out_free_line;
+ else
+ cd_map[cpu]->domains = temp_domains;
+
+ domain_info = zalloc(sizeof(*domain_info));
+ if (!domain_info)
+ goto out_free_line;
+
+ cd_map[cpu]->domains[dcount - 1] = domain_info;
+
+ if (*schedstat_version >= 17) {
+ retval = sscanf(line, "domain%u %s %s %*s", &domain, dname,
+ cpumask);
+ if (retval != 3)
+ continue;
+
+ domain_info->dname = strdup(dname);
+ if (!domain_info->dname)
+ goto out_free_line;
+ } else {
+ retval = sscanf(line, "domain%u %s %*s", &domain, cpumask);
+ if (retval != 2)
+ continue;
+ }
+
+ domain_info->domain = domain;
+ if (domain > *max_sched_domains)
+ *max_sched_domains = domain;
+
+ domain_info->cpumask = strdup(cpumask);
+ if (!domain_info->cpumask)
+ goto out_free_line;
+
+ cpumask_to_cpulist(cpumask, cpulist);
+ domain_info->cpulist = strdup(cpulist);
+ if (!domain_info->cpulist)
+ goto out_free_line;
+
+ cd_map[cpu]->nr_domains = dcount;
+ }
+ }
+
+out_free_line:
+ free(line);
+out:
+ fclose(fp);
+ return cd_map;
+}
+
+static int write_cpu_domain_info(struct feat_fd *ff,
+ struct evlist *evlist __maybe_unused)
+{
+ u32 max_sched_domains = 0, schedstat_version = 0;
+ struct cpu_domain_map **cd_map;
+ u32 i, j, nr, ret;
+
+ nr = cpu__max_present_cpu().cpu;
+
+ cd_map = build_cpu_domain_map(&schedstat_version, &max_sched_domains, nr);
+ if (!cd_map)
+ return -1;
+
+ ret = do_write(ff, &schedstat_version, sizeof(u32));
+ if (ret < 0)
+ goto out;
+
+ max_sched_domains += 1;
+ ret = do_write(ff, &max_sched_domains, sizeof(u32));
+ if (ret < 0)
+ goto out;
+
+ for (i = 0; i < nr; i++) {
+ if (!cd_map[i])
+ continue;
+
+ ret = do_write(ff, &cd_map[i]->cpu, sizeof(u32));
+ if (ret < 0)
+ goto out;
+
+ ret = do_write(ff, &cd_map[i]->nr_domains, sizeof(u32));
+ if (ret < 0)
+ goto out;
+
+ for (j = 0; j < cd_map[i]->nr_domains; j++) {
+ ret = do_write(ff, &cd_map[i]->domains[j]->domain, sizeof(u32));
+ if (ret < 0)
+ goto out;
+ if (schedstat_version >= 17) {
+ ret = do_write_string(ff, cd_map[i]->domains[j]->dname);
+ if (ret < 0)
+ goto out;
+ }
+
+ ret = do_write_string(ff, cd_map[i]->domains[j]->cpumask);
+ if (ret < 0)
+ goto out;
+
+ ret = do_write_string(ff, cd_map[i]->domains[j]->cpulist);
+ if (ret < 0)
+ goto out;
+ }
+ }
+
+out:
+ free_cpu_domain_info(cd_map, schedstat_version, nr);
+ return ret;
+}
+
static void print_hostname(struct feat_fd *ff, FILE *fp)
{
fprintf(fp, "# hostname : %s\n", ff->ph->env.hostname);
@@ -1627,6 +1834,12 @@ static void print_arch(struct feat_fd *ff, FILE *fp)
fprintf(fp, "# arch : %s\n", ff->ph->env.arch);
}
+static void print_e_machine(struct feat_fd *ff, FILE *fp)
+{
+ fprintf(fp, "# e_machine : %u\n", ff->ph->env.e_machine);
+ fprintf(fp, "# e_flags : %u\n", ff->ph->env.e_flags);
+}
+
static void print_cpudesc(struct feat_fd *ff, FILE *fp)
{
fprintf(fp, "# cpudesc : %s\n", ff->ph->env.cpu_desc);
@@ -1801,9 +2014,9 @@ static void print_dir_format(struct feat_fd *ff, FILE *fp)
fprintf(fp, "# directory data version : %"PRIu64"\n", data->dir.version);
}
-#ifdef HAVE_LIBBPF_SUPPORT
-static void print_bpf_prog_info(struct feat_fd *ff, FILE *fp)
+static void print_bpf_prog_info(struct feat_fd *ff __maybe_unused, FILE *fp)
{
+#ifdef HAVE_LIBBPF_SUPPORT
struct perf_env *env = &ff->ph->env;
struct rb_root *root;
struct rb_node *next;
@@ -1813,6 +2026,9 @@ static void print_bpf_prog_info(struct feat_fd *ff, FILE *fp)
root = &env->bpf_progs.infos;
next = rb_first(root);
+ if (!next)
+ fprintf(fp, "# bpf_prog_info empty\n");
+
while (next) {
struct bpf_prog_info_node *node;
@@ -1824,10 +2040,14 @@ static void print_bpf_prog_info(struct feat_fd *ff, FILE *fp)
}
up_read(&env->bpf_progs.lock);
+#else
+ fprintf(fp, "# bpf_prog_info missing, no libbpf support\n");
+#endif // HAVE_LIBBPF_SUPPORT
}
-static void print_bpf_btf(struct feat_fd *ff, FILE *fp)
+static void print_bpf_btf(struct feat_fd *ff __maybe_unused, FILE *fp)
{
+#ifdef HAVE_LIBBPF_SUPPORT
struct perf_env *env = &ff->ph->env;
struct rb_root *root;
struct rb_node *next;
@@ -1837,6 +2057,9 @@ static void print_bpf_btf(struct feat_fd *ff, FILE *fp)
root = &env->bpf_progs.btfs;
next = rb_first(root);
+ if (!next)
+ printf("# btf info empty\n");
+
while (next) {
struct btf_node *node;
@@ -1846,8 +2069,10 @@ static void print_bpf_btf(struct feat_fd *ff, FILE *fp)
}
up_read(&env->bpf_progs.lock);
-}
+#else
+ fprintf(fp, "# bpf btf data missing, no libbpf support\n");
#endif // HAVE_LIBBPF_SUPPORT
+}
static void free_event_desc(struct evsel *events)
{
@@ -2076,6 +2301,11 @@ static void print_cache(struct feat_fd *ff, FILE *fp __maybe_unused)
}
}
+static void print_cln_size(struct feat_fd *ff, FILE *fp)
+{
+ fprintf(fp, "# cacheline size: %u\n", ff->ph->env.cln_size);
+}
+
static void print_compressed(struct feat_fd *ff, FILE *fp)
{
fprintf(fp, "# compressed : %s, level = %d, ratio = %d\n",
@@ -2110,17 +2340,18 @@ static void print_cpu_pmu_caps(struct feat_fd *ff, FILE *fp)
static void print_pmu_caps(struct feat_fd *ff, FILE *fp)
{
+ struct perf_env *env = &ff->ph->env;
struct pmu_caps *pmu_caps;
- for (int i = 0; i < ff->ph->env.nr_pmus_with_caps; i++) {
- pmu_caps = &ff->ph->env.pmu_caps[i];
+ for (int i = 0; i < env->nr_pmus_with_caps; i++) {
+ pmu_caps = &env->pmu_caps[i];
__print_pmu_caps(fp, pmu_caps->nr_caps, pmu_caps->caps,
pmu_caps->pmu_name);
}
- if (strcmp(perf_env__arch(&ff->ph->env), "x86") == 0 &&
- perf_env__has_pmu_mapping(&ff->ph->env, "ibs_op")) {
- char *max_precise = perf_env__find_pmu_cap(&ff->ph->env, "cpu", "max_precise");
+ if (strcmp(perf_env__arch(env), "x86") == 0 &&
+ perf_env__has_pmu_mapping(env, "ibs_op")) {
+ char *max_precise = perf_env__find_pmu_cap(env, "cpu", "max_precise");
if (max_precise != NULL && atoi(max_precise) == 0)
fprintf(fp, "# AMD systems uses ibs_op// PMU for some precise events, e.g.: cycles:p, see the 'perf list' man page for further details.\n");
@@ -2129,18 +2360,19 @@ static void print_pmu_caps(struct feat_fd *ff, FILE *fp)
static void print_pmu_mappings(struct feat_fd *ff, FILE *fp)
{
+ struct perf_env *env = &ff->ph->env;
const char *delimiter = "# pmu mappings: ";
char *str, *tmp;
u32 pmu_num;
u32 type;
- pmu_num = ff->ph->env.nr_pmu_mappings;
+ pmu_num = env->nr_pmu_mappings;
if (!pmu_num) {
fprintf(fp, "# pmu mappings: not available\n");
return;
}
- str = ff->ph->env.pmu_mappings;
+ str = env->pmu_mappings;
while (pmu_num) {
type = strtoul(str, &tmp, 0);
@@ -2222,17 +2454,51 @@ static void memory_node__fprintf(struct memory_node *n,
static void print_mem_topology(struct feat_fd *ff, FILE *fp)
{
+ struct perf_env *env = &ff->ph->env;
struct memory_node *nodes;
int i, nr;
- nodes = ff->ph->env.memory_nodes;
- nr = ff->ph->env.nr_memory_nodes;
+ nodes = env->memory_nodes;
+ nr = env->nr_memory_nodes;
fprintf(fp, "# memory nodes (nr %d, block size 0x%llx):\n",
- nr, ff->ph->env.memory_bsize);
+ nr, env->memory_bsize);
for (i = 0; i < nr; i++) {
- memory_node__fprintf(&nodes[i], ff->ph->env.memory_bsize, fp);
+ memory_node__fprintf(&nodes[i], env->memory_bsize, fp);
+ }
+}
+
+static void print_cpu_domain_info(struct feat_fd *ff, FILE *fp)
+{
+ struct cpu_domain_map **cd_map = ff->ph->env.cpu_domain;
+ u32 nr = ff->ph->env.nr_cpus_avail;
+ struct domain_info *d_info;
+ u32 i, j;
+
+ fprintf(fp, "# schedstat version : %u\n", ff->ph->env.schedstat_version);
+ fprintf(fp, "# Maximum sched domains : %u\n", ff->ph->env.max_sched_domains);
+
+ for (i = 0; i < nr; i++) {
+ if (!cd_map[i])
+ continue;
+
+ fprintf(fp, "# cpu : %u\n", cd_map[i]->cpu);
+ fprintf(fp, "# nr_domains : %u\n", cd_map[i]->nr_domains);
+
+ for (j = 0; j < cd_map[i]->nr_domains; j++) {
+ d_info = cd_map[i]->domains[j];
+ if (!d_info)
+ continue;
+
+ fprintf(fp, "# Domain : %u\n", d_info->domain);
+
+ if (ff->ph->env.schedstat_version >= 17)
+ fprintf(fp, "# Domain name : %s\n", d_info->dname);
+
+ fprintf(fp, "# Domain cpu map : %s\n", d_info->cpumask);
+ fprintf(fp, "# Domain cpu list : %s\n", d_info->cpulist);
+ }
}
}
@@ -2290,7 +2556,7 @@ static int __event_process_build_id(struct perf_record_header_build_id *bev,
free(m.name);
}
- build_id__sprintf(dso__bid(dso), sbuild_id);
+ build_id__snprintf(dso__bid(dso), sbuild_id, sizeof(sbuild_id));
pr_debug("build id event received for %s: %s [%zu]\n",
dso__long_name(dso), sbuild_id, size);
dso__put(dso);
@@ -2324,6 +2590,11 @@ static int perf_header__read_build_ids_abi_quirk(struct perf_header *header,
perf_event_header__bswap(&old_bev.header);
len = old_bev.header.size - sizeof(old_bev);
+ if (len < 0 || len >= PATH_MAX) {
+ pr_warning("invalid build_id filename length %zd\n", len);
+ return -1;
+ }
+
if (readn(input, filename, len) != len)
return -1;
@@ -2366,6 +2637,11 @@ static int perf_header__read_build_ids(struct perf_header *header,
perf_event_header__bswap(&bev.header);
len = bev.header.size - sizeof(bev);
+ if (len < 0 || len >= PATH_MAX) {
+ pr_warning("invalid build_id filename length %zd\n", len);
+ goto out;
+ }
+
if (readn(input, filename, len) != len)
goto out;
/*
@@ -2412,14 +2688,28 @@ FEAT_PROCESS_STR_FUN(arch, arch);
FEAT_PROCESS_STR_FUN(cpudesc, cpu_desc);
FEAT_PROCESS_STR_FUN(cpuid, cpuid);
-#ifdef HAVE_LIBTRACEEVENT
-static int process_tracing_data(struct feat_fd *ff, void *data)
+static int process_e_machine(struct feat_fd *ff, void *data __maybe_unused)
{
+ int ret;
+
+ ret = do_read_u32(ff, &ff->ph->env.e_machine);
+ if (ret)
+ return ret;
+
+ return do_read_u32(ff, &ff->ph->env.e_flags);
+}
+
+static int process_tracing_data(struct feat_fd *ff __maybe_unused, void *data __maybe_unused)
+{
+#ifdef HAVE_LIBTRACEEVENT
ssize_t ret = trace_report(ff->fd, data, false);
return ret < 0 ? -1 : 0;
-}
+#else
+ pr_err("ERROR: Trying to read tracing data without libtraceevent support.\n");
+ return -1;
#endif
+}
static int process_build_id(struct feat_fd *ff, void *data __maybe_unused)
{
@@ -2430,6 +2720,7 @@ static int process_build_id(struct feat_fd *ff, void *data __maybe_unused)
static int process_nrcpus(struct feat_fd *ff, void *data __maybe_unused)
{
+ struct perf_env *env = &ff->ph->env;
int ret;
u32 nr_cpus_avail, nr_cpus_online;
@@ -2440,20 +2731,28 @@ static int process_nrcpus(struct feat_fd *ff, void *data __maybe_unused)
ret = do_read_u32(ff, &nr_cpus_online);
if (ret)
return ret;
- ff->ph->env.nr_cpus_avail = (int)nr_cpus_avail;
- ff->ph->env.nr_cpus_online = (int)nr_cpus_online;
+
+ if (nr_cpus_online > nr_cpus_avail) {
+ pr_err("Invalid HEADER_NRCPUS: nr_cpus_online (%u) > nr_cpus_avail (%u)\n",
+ nr_cpus_online, nr_cpus_avail);
+ return -1;
+ }
+
+ env->nr_cpus_avail = (int)nr_cpus_avail;
+ env->nr_cpus_online = (int)nr_cpus_online;
return 0;
}
static int process_total_mem(struct feat_fd *ff, void *data __maybe_unused)
{
+ struct perf_env *env = &ff->ph->env;
u64 total_mem;
int ret;
ret = do_read_u64(ff, &total_mem);
if (ret)
return -1;
- ff->ph->env.total_mem = (unsigned long long)total_mem;
+ env->total_mem = (unsigned long long)total_mem;
return 0;
}
@@ -2512,21 +2811,31 @@ process_event_desc(struct feat_fd *ff, void *data __maybe_unused)
return 0;
}
+/*
+ * Some arbitrary max for the number of command line arguments,
+ * Wildcards can expand and end up with tons of command line args.
+ */
+#define MAX_CMDLINE_NR 1048576
+
static int process_cmdline(struct feat_fd *ff, void *data __maybe_unused)
{
+ struct perf_env *env = &ff->ph->env;
char *str, *cmdline = NULL, **argv = NULL;
u32 nr, i, len = 0;
if (do_read_u32(ff, &nr))
return -1;
- ff->ph->env.nr_cmdline = nr;
+ if (nr > MAX_CMDLINE_NR)
+ return -1;
+
+ env->nr_cmdline = nr;
cmdline = zalloc(ff->size + nr + 1);
if (!cmdline)
return -1;
- argv = zalloc(sizeof(char *) * (nr + 1));
+ argv = calloc(nr + 1, sizeof(char *));
if (!argv)
goto error;
@@ -2540,8 +2849,8 @@ static int process_cmdline(struct feat_fd *ff, void *data __maybe_unused)
len += strlen(str) + 1;
free(str);
}
- ff->ph->env.cmdline = cmdline;
- ff->ph->env.cmdline_argv = (const char **) argv;
+ env->cmdline = cmdline;
+ env->cmdline_argv = (const char **) argv;
return 0;
error:
@@ -2555,19 +2864,29 @@ static int process_cpu_topology(struct feat_fd *ff, void *data __maybe_unused)
u32 nr, i;
char *str = NULL;
struct strbuf sb;
- int cpu_nr = ff->ph->env.nr_cpus_avail;
+ struct perf_env *env = &ff->ph->env;
+ int cpu_nr = env->nr_cpus_avail;
u64 size = 0;
- struct perf_header *ph = ff->ph;
- bool do_core_id_test = true;
- ph->env.cpu = calloc(cpu_nr, sizeof(*ph->env.cpu));
- if (!ph->env.cpu)
+ if (cpu_nr == 0) {
+ pr_err("Invalid HEADER_CPU_TOPOLOGY: missing HEADER_NRCPUS\n");
+ return -1;
+ }
+
+ env->cpu = calloc(cpu_nr, sizeof(*env->cpu));
+ if (!env->cpu)
return -1;
if (do_read_u32(ff, &nr))
goto free_cpu;
- ph->env.nr_sibling_cores = nr;
+ if (nr > (u32)cpu_nr) {
+ pr_err("Invalid HEADER_CPU_TOPOLOGY: nr_sibling_cores (%u) > nr_cpus_avail (%d)\n",
+ nr, cpu_nr);
+ goto free_cpu;
+ }
+
+ env->nr_sibling_cores = nr;
size += sizeof(u32);
if (strbuf_init(&sb, 128) < 0)
goto free_cpu;
@@ -2583,12 +2902,18 @@ static int process_cpu_topology(struct feat_fd *ff, void *data __maybe_unused)
size += string_size(str);
zfree(&str);
}
- ph->env.sibling_cores = strbuf_detach(&sb, NULL);
+ env->sibling_cores = strbuf_detach(&sb, NULL);
if (do_read_u32(ff, &nr))
- return -1;
+ goto free_cpu;
- ph->env.nr_sibling_threads = nr;
+ if (nr > (u32)cpu_nr) {
+ pr_err("Invalid HEADER_CPU_TOPOLOGY: nr_sibling_threads (%u) > nr_cpus_avail (%d)\n",
+ nr, cpu_nr);
+ goto free_cpu;
+ }
+
+ env->nr_sibling_threads = nr;
size += sizeof(u32);
for (i = 0; i < nr; i++) {
@@ -2602,43 +2927,28 @@ static int process_cpu_topology(struct feat_fd *ff, void *data __maybe_unused)
size += string_size(str);
zfree(&str);
}
- ph->env.sibling_threads = strbuf_detach(&sb, NULL);
+ env->sibling_threads = strbuf_detach(&sb, NULL);
/*
* The header may be from old perf,
* which doesn't include core id and socket id information.
*/
if (ff->size <= size) {
- zfree(&ph->env.cpu);
+ zfree(&env->cpu);
return 0;
}
- /* On s390 the socket_id number is not related to the numbers of cpus.
- * The socket_id number might be higher than the numbers of cpus.
- * This depends on the configuration.
- * AArch64 is the same.
- */
- if (ph->env.arch && (!strncmp(ph->env.arch, "s390", 4)
- || !strncmp(ph->env.arch, "aarch64", 7)))
- do_core_id_test = false;
-
for (i = 0; i < (u32)cpu_nr; i++) {
if (do_read_u32(ff, &nr))
goto free_cpu;
- ph->env.cpu[i].core_id = nr;
+ env->cpu[i].core_id = nr;
size += sizeof(u32);
if (do_read_u32(ff, &nr))
goto free_cpu;
- if (do_core_id_test && nr != (u32)-1 && nr > (u32)cpu_nr) {
- pr_debug("socket_id number is too big."
- "You may need to upgrade the perf tool.\n");
- goto free_cpu;
- }
-
- ph->env.cpu[i].socket_id = nr;
+ env->cpu[i].socket_id = nr;
size += sizeof(u32);
}
@@ -2650,9 +2960,15 @@ static int process_cpu_topology(struct feat_fd *ff, void *data __maybe_unused)
return 0;
if (do_read_u32(ff, &nr))
- return -1;
+ goto free_cpu;
- ph->env.nr_sibling_dies = nr;
+ if (nr > (u32)cpu_nr) {
+ pr_err("Invalid HEADER_CPU_TOPOLOGY: nr_sibling_dies (%u) > nr_cpus_avail (%d)\n",
+ nr, cpu_nr);
+ goto free_cpu;
+ }
+
+ env->nr_sibling_dies = nr;
size += sizeof(u32);
for (i = 0; i < nr; i++) {
@@ -2666,13 +2982,13 @@ static int process_cpu_topology(struct feat_fd *ff, void *data __maybe_unused)
size += string_size(str);
zfree(&str);
}
- ph->env.sibling_dies = strbuf_detach(&sb, NULL);
+ env->sibling_dies = strbuf_detach(&sb, NULL);
for (i = 0; i < (u32)cpu_nr; i++) {
if (do_read_u32(ff, &nr))
goto free_cpu;
- ph->env.cpu[i].die_id = nr;
+ env->cpu[i].die_id = nr;
}
return 0;
@@ -2681,12 +2997,13 @@ error:
strbuf_release(&sb);
zfree(&str);
free_cpu:
- zfree(&ph->env.cpu);
+ zfree(&env->cpu);
return -1;
}
static int process_numa_topology(struct feat_fd *ff, void *data __maybe_unused)
{
+ struct perf_env *env = &ff->ph->env;
struct numa_node *nodes, *n;
u32 nr, i;
char *str;
@@ -2695,7 +3012,19 @@ static int process_numa_topology(struct feat_fd *ff, void *data __maybe_unused)
if (do_read_u32(ff, &nr))
return -1;
- nodes = zalloc(sizeof(*nodes) * nr);
+ if (nr > MAX_NUMA_NODES) {
+ pr_err("Invalid HEADER_NUMA_TOPOLOGY: nr_nodes (%u) > %u\n",
+ nr, MAX_NUMA_NODES);
+ return -1;
+ }
+
+ if (ff->size < sizeof(u32) + nr * (sizeof(u32) + 2 * sizeof(u64))) {
+ pr_err("Invalid HEADER_NUMA_TOPOLOGY: section too small (%zu) for %u nodes\n",
+ ff->size, nr);
+ return -1;
+ }
+
+ nodes = calloc(nr, sizeof(*nodes));
if (!nodes)
return -ENOMEM;
@@ -2721,8 +3050,8 @@ static int process_numa_topology(struct feat_fd *ff, void *data __maybe_unused)
if (!n->map)
goto error;
}
- ff->ph->env.nr_numa_nodes = nr;
- ff->ph->env.numa_nodes = nodes;
+ env->nr_numa_nodes = nr;
+ env->numa_nodes = nodes;
return 0;
error:
@@ -2732,6 +3061,7 @@ error:
static int process_pmu_mappings(struct feat_fd *ff, void *data __maybe_unused)
{
+ struct perf_env *env = &ff->ph->env;
char *name;
u32 pmu_num;
u32 type;
@@ -2745,7 +3075,19 @@ static int process_pmu_mappings(struct feat_fd *ff, void *data __maybe_unused)
return 0;
}
- ff->ph->env.nr_pmu_mappings = pmu_num;
+ if (pmu_num > MAX_PMU_MAPPINGS) {
+ pr_err("Invalid HEADER_PMU_MAPPINGS: pmu_num (%u) > %u\n",
+ pmu_num, MAX_PMU_MAPPINGS);
+ return -1;
+ }
+
+ if (ff->size < sizeof(u32) + pmu_num * 2 * sizeof(u32)) {
+ pr_err("Invalid HEADER_PMU_MAPPINGS: section too small (%zu) for %u PMUs\n",
+ ff->size, pmu_num);
+ return -1;
+ }
+
+ env->nr_pmu_mappings = pmu_num;
if (strbuf_init(&sb, 128) < 0)
return -1;
@@ -2764,12 +3106,14 @@ static int process_pmu_mappings(struct feat_fd *ff, void *data __maybe_unused)
goto error;
if (!strcmp(name, "msr"))
- ff->ph->env.msr_pmu_type = type;
+ env->msr_pmu_type = type;
free(name);
pmu_num--;
}
- ff->ph->env.pmu_mappings = strbuf_detach(&sb, NULL);
+ /* AMD may set it by evlist__has_amd_ibs() from perf_session__new() */
+ free(env->pmu_mappings);
+ env->pmu_mappings = strbuf_detach(&sb, NULL);
return 0;
error:
@@ -2779,6 +3123,7 @@ error:
static int process_group_desc(struct feat_fd *ff, void *data __maybe_unused)
{
+ struct perf_env *env = &ff->ph->env;
size_t ret = -1;
u32 i, nr, nr_groups;
struct perf_session *session;
@@ -2792,12 +3137,25 @@ static int process_group_desc(struct feat_fd *ff, void *data __maybe_unused)
if (do_read_u32(ff, &nr_groups))
return -1;
- ff->ph->env.nr_groups = nr_groups;
if (!nr_groups) {
pr_debug("group desc not available\n");
return 0;
}
+ if (nr_groups > MAX_GROUP_DESC) {
+ pr_err("Invalid HEADER_GROUP_DESC: nr_groups (%u) > %u\n",
+ nr_groups, MAX_GROUP_DESC);
+ return -1;
+ }
+
+ if (ff->size < sizeof(u32) + nr_groups * 3 * sizeof(u32)) {
+ pr_err("Invalid HEADER_GROUP_DESC: section too small (%zu) for %u groups\n",
+ ff->size, nr_groups);
+ return -1;
+ }
+
+ env->nr_groups = nr_groups;
+
desc = calloc(nr_groups, sizeof(*desc));
if (!desc)
return -1;
@@ -2876,6 +3234,7 @@ static int process_auxtrace(struct feat_fd *ff, void *data __maybe_unused)
static int process_cache(struct feat_fd *ff, void *data __maybe_unused)
{
+ struct perf_env *env = &ff->ph->env;
struct cpu_cache_level *caches;
u32 cnt, i, version;
@@ -2888,7 +3247,19 @@ static int process_cache(struct feat_fd *ff, void *data __maybe_unused)
if (do_read_u32(ff, &cnt))
return -1;
- caches = zalloc(sizeof(*caches) * cnt);
+ if (cnt > MAX_CACHE_ENTRIES) {
+ pr_err("Invalid HEADER_CACHE: cnt (%u) > %u\n",
+ cnt, MAX_CACHE_ENTRIES);
+ return -1;
+ }
+
+ if (ff->size < 2 * sizeof(u32) + cnt * 7 * sizeof(u32)) {
+ pr_err("Invalid HEADER_CACHE: section too small (%zu) for %u entries\n",
+ ff->size, cnt);
+ return -1;
+ }
+
+ caches = calloc(cnt, sizeof(*caches));
if (!caches)
return -1;
@@ -2916,8 +3287,8 @@ static int process_cache(struct feat_fd *ff, void *data __maybe_unused)
#undef _R
}
- ff->ph->env.caches = caches;
- ff->ph->env.caches_cnt = cnt;
+ env->caches = caches;
+ env->caches_cnt = cnt;
return 0;
out_free_caches:
for (i = 0; i < cnt; i++) {
@@ -2929,6 +3300,16 @@ out_free_caches:
return -1;
}
+static int process_cln_size(struct feat_fd *ff, void *data __maybe_unused)
+{
+ struct perf_env *env = &ff->ph->env;
+
+ if (do_read_u32(ff, &env->cln_size))
+ return -1;
+
+ return 0;
+}
+
static int process_sample_time(struct feat_fd *ff, void *data __maybe_unused)
{
struct perf_session *session;
@@ -2953,6 +3334,7 @@ static int process_sample_time(struct feat_fd *ff, void *data __maybe_unused)
static int process_mem_topology(struct feat_fd *ff,
void *data __maybe_unused)
{
+ struct perf_env *env = &ff->ph->env;
struct memory_node *nodes;
u64 version, i, nr, bsize;
int ret = -1;
@@ -2969,7 +3351,19 @@ static int process_mem_topology(struct feat_fd *ff,
if (do_read_u64(ff, &nr))
return -1;
- nodes = zalloc(sizeof(*nodes) * nr);
+ if (nr > MAX_NUMA_NODES) {
+ pr_err("Invalid HEADER_MEM_TOPOLOGY: nr_nodes (%llu) > %u\n",
+ (unsigned long long)nr, MAX_NUMA_NODES);
+ return -1;
+ }
+
+ if (ff->size < 3 * sizeof(u64) + nr * 2 * sizeof(u64)) {
+ pr_err("Invalid HEADER_MEM_TOPOLOGY: section too small (%zu) for %llu nodes\n",
+ ff->size, (unsigned long long)nr);
+ return -1;
+ }
+
+ nodes = calloc(nr, sizeof(*nodes));
if (!nodes)
return -1;
@@ -2991,9 +3385,9 @@ static int process_mem_topology(struct feat_fd *ff,
nodes[i] = n;
}
- ff->ph->env.memory_bsize = bsize;
- ff->ph->env.memory_nodes = nodes;
- ff->ph->env.nr_memory_nodes = nr;
+ env->memory_bsize = bsize;
+ env->memory_nodes = nodes;
+ env->nr_memory_nodes = nr;
ret = 0;
out:
@@ -3005,7 +3399,9 @@ out:
static int process_clockid(struct feat_fd *ff,
void *data __maybe_unused)
{
- if (do_read_u64(ff, &ff->ph->env.clock.clockid_res_ns))
+ struct perf_env *env = &ff->ph->env;
+
+ if (do_read_u64(ff, &env->clock.clockid_res_ns))
return -1;
return 0;
@@ -3014,6 +3410,7 @@ static int process_clockid(struct feat_fd *ff,
static int process_clock_data(struct feat_fd *ff,
void *_data __maybe_unused)
{
+ struct perf_env *env = &ff->ph->env;
u32 data32;
u64 data64;
@@ -3028,26 +3425,27 @@ static int process_clock_data(struct feat_fd *ff,
if (do_read_u32(ff, &data32))
return -1;
- ff->ph->env.clock.clockid = data32;
+ env->clock.clockid = data32;
/* TOD ref time */
if (do_read_u64(ff, &data64))
return -1;
- ff->ph->env.clock.tod_ns = data64;
+ env->clock.tod_ns = data64;
/* clockid ref time */
if (do_read_u64(ff, &data64))
return -1;
- ff->ph->env.clock.clockid_ns = data64;
- ff->ph->env.clock.enabled = true;
+ env->clock.clockid_ns = data64;
+ env->clock.enabled = true;
return 0;
}
static int process_hybrid_topology(struct feat_fd *ff,
void *data __maybe_unused)
{
+ struct perf_env *env = &ff->ph->env;
struct hybrid_node *nodes, *n;
u32 nr, i;
@@ -3055,7 +3453,19 @@ static int process_hybrid_topology(struct feat_fd *ff,
if (do_read_u32(ff, &nr))
return -1;
- nodes = zalloc(sizeof(*nodes) * nr);
+ if (nr > MAX_PMU_MAPPINGS) {
+ pr_err("Invalid HEADER_HYBRID_TOPOLOGY: nr_nodes (%u) > %u\n",
+ nr, MAX_PMU_MAPPINGS);
+ return -1;
+ }
+
+ if (ff->size < sizeof(u32) + nr * 2 * sizeof(u32)) {
+ pr_err("Invalid HEADER_HYBRID_TOPOLOGY: section too small (%zu) for %u nodes\n",
+ ff->size, nr);
+ return -1;
+ }
+
+ nodes = calloc(nr, sizeof(*nodes));
if (!nodes)
return -ENOMEM;
@@ -3071,8 +3481,8 @@ static int process_hybrid_topology(struct feat_fd *ff,
goto error;
}
- ff->ph->env.nr_hybrid_nodes = nr;
- ff->ph->env.hybrid_nodes = nodes;
+ env->nr_hybrid_nodes = nr;
+ env->hybrid_nodes = nodes;
return 0;
error:
@@ -3100,9 +3510,9 @@ static int process_dir_format(struct feat_fd *ff,
return do_read_u64(ff, &data->dir.version);
}
-#ifdef HAVE_LIBBPF_SUPPORT
-static int process_bpf_prog_info(struct feat_fd *ff, void *data __maybe_unused)
+static int process_bpf_prog_info(struct feat_fd *ff __maybe_unused, void *data __maybe_unused)
{
+#ifdef HAVE_LIBBPF_SUPPORT
struct bpf_prog_info_node *info_node;
struct perf_env *env = &ff->ph->env;
struct perf_bpil *info_linear;
@@ -3117,6 +3527,18 @@ static int process_bpf_prog_info(struct feat_fd *ff, void *data __maybe_unused)
if (do_read_u32(ff, &count))
return -1;
+ if (count > MAX_BPF_PROGS) {
+ pr_err("Invalid HEADER_BPF_PROG_INFO: count (%u) > %u\n",
+ count, MAX_BPF_PROGS);
+ return -1;
+ }
+
+ if (ff->size < sizeof(u32) + count * (2 * sizeof(u32) + sizeof(u64))) {
+ pr_err("Invalid HEADER_BPF_PROG_INFO: section too small (%zu) for %u entries\n",
+ ff->size, count);
+ return -1;
+ }
+
down_write(&env->bpf_progs.lock);
for (i = 0; i < count; ++i) {
@@ -3134,6 +3556,12 @@ static int process_bpf_prog_info(struct feat_fd *ff, void *data __maybe_unused)
goto out;
}
+ if (data_len > MAX_BPF_DATA_LEN) {
+ pr_warning("Invalid HEADER_BPF_PROG_INFO: data_len (%u) too large\n",
+ data_len);
+ goto out;
+ }
+
info_linear = malloc(sizeof(struct perf_bpil) +
data_len);
if (!info_linear)
@@ -3158,6 +3586,7 @@ static int process_bpf_prog_info(struct feat_fd *ff, void *data __maybe_unused)
/* after reading from file, translate offset to address */
bpil_offs_to_addr(info_linear);
info_node->info_linear = info_linear;
+ info_node->metadata = NULL;
if (!__perf_env__insert_bpf_prog_info(env, info_node)) {
free(info_linear);
free(info_node);
@@ -3171,10 +3600,15 @@ out:
free(info_node);
up_write(&env->bpf_progs.lock);
return err;
+#else
+ pr_err("ERROR: Trying to read bpf_prog_info without libbpf support.\n");
+ return -1;
+#endif // HAVE_LIBBPF_SUPPORT
}
-static int process_bpf_btf(struct feat_fd *ff, void *data __maybe_unused)
+static int process_bpf_btf(struct feat_fd *ff __maybe_unused, void *data __maybe_unused)
{
+#ifdef HAVE_LIBBPF_SUPPORT
struct perf_env *env = &ff->ph->env;
struct btf_node *node = NULL;
u32 count, i;
@@ -3188,6 +3622,17 @@ static int process_bpf_btf(struct feat_fd *ff, void *data __maybe_unused)
if (do_read_u32(ff, &count))
return -1;
+ if (count > MAX_BPF_PROGS) {
+ pr_err("bpf btf count %u too large (max %u)\n", count, MAX_BPF_PROGS);
+ return -1;
+ }
+
+ if (ff->size < sizeof(u32) + count * 2 * sizeof(u32)) {
+ pr_err("Invalid HEADER_BPF_BTF: section too small (%zu) for %u entries\n",
+ ff->size, count);
+ return -1;
+ }
+
down_write(&env->bpf_progs.lock);
for (i = 0; i < count; ++i) {
@@ -3198,6 +3643,12 @@ static int process_bpf_btf(struct feat_fd *ff, void *data __maybe_unused)
if (do_read_u32(ff, &data_size))
goto out;
+ if (data_size > MAX_BPF_DATA_LEN) {
+ pr_err("bpf btf data size %u too large (max %u)\n",
+ data_size, MAX_BPF_DATA_LEN);
+ goto out;
+ }
+
node = malloc(sizeof(struct btf_node) + data_size);
if (!node)
goto out;
@@ -3218,25 +3669,30 @@ out:
up_write(&env->bpf_progs.lock);
free(node);
return err;
-}
+#else
+ pr_err("ERROR: Trying to read btf data without libbpf support.\n");
+ return -1;
#endif // HAVE_LIBBPF_SUPPORT
+}
static int process_compressed(struct feat_fd *ff,
void *data __maybe_unused)
{
- if (do_read_u32(ff, &(ff->ph->env.comp_ver)))
+ struct perf_env *env = &ff->ph->env;
+
+ if (do_read_u32(ff, &(env->comp_ver)))
return -1;
- if (do_read_u32(ff, &(ff->ph->env.comp_type)))
+ if (do_read_u32(ff, &(env->comp_type)))
return -1;
- if (do_read_u32(ff, &(ff->ph->env.comp_level)))
+ if (do_read_u32(ff, &(env->comp_level)))
return -1;
- if (do_read_u32(ff, &(ff->ph->env.comp_ratio)))
+ if (do_read_u32(ff, &(env->comp_ratio)))
return -1;
- if (do_read_u32(ff, &(ff->ph->env.comp_mmap_len)))
+ if (do_read_u32(ff, &(env->comp_mmap_len)))
return -1;
return 0;
@@ -3259,7 +3715,13 @@ static int __process_pmu_caps(struct feat_fd *ff, int *nr_caps,
if (!nr_pmu_caps)
return 0;
- *caps = zalloc(sizeof(char *) * nr_pmu_caps);
+ if (nr_pmu_caps > MAX_PMU_CAPS) {
+ pr_err("Invalid pmu caps: nr_pmu_caps (%u) > %u\n",
+ nr_pmu_caps, MAX_PMU_CAPS);
+ return -1;
+ }
+
+ *caps = calloc(nr_pmu_caps, sizeof(char *));
if (!*caps)
return -1;
@@ -3308,19 +3770,21 @@ error:
static int process_cpu_pmu_caps(struct feat_fd *ff,
void *data __maybe_unused)
{
- int ret = __process_pmu_caps(ff, &ff->ph->env.nr_cpu_pmu_caps,
- &ff->ph->env.cpu_pmu_caps,
- &ff->ph->env.max_branches,
- &ff->ph->env.br_cntr_nr,
- &ff->ph->env.br_cntr_width);
+ struct perf_env *env = &ff->ph->env;
+ int ret = __process_pmu_caps(ff, &env->nr_cpu_pmu_caps,
+ &env->cpu_pmu_caps,
+ &env->max_branches,
+ &env->br_cntr_nr,
+ &env->br_cntr_width);
- if (!ret && !ff->ph->env.cpu_pmu_caps)
+ if (!ret && !env->cpu_pmu_caps)
pr_debug("cpu pmu capabilities not available\n");
return ret;
}
static int process_pmu_caps(struct feat_fd *ff, void *data __maybe_unused)
{
+ struct perf_env *env = &ff->ph->env;
struct pmu_caps *pmu_caps;
u32 nr_pmu, i;
int ret;
@@ -3334,7 +3798,19 @@ static int process_pmu_caps(struct feat_fd *ff, void *data __maybe_unused)
return 0;
}
- pmu_caps = zalloc(sizeof(*pmu_caps) * nr_pmu);
+ if (nr_pmu > MAX_PMU_MAPPINGS) {
+ pr_err("Invalid HEADER_PMU_CAPS: nr_pmu (%u) > %u\n",
+ nr_pmu, MAX_PMU_MAPPINGS);
+ return -1;
+ }
+
+ if (ff->size < sizeof(u32) + nr_pmu * sizeof(u32)) {
+ pr_err("Invalid HEADER_PMU_CAPS: section too small (%zu) for %u PMUs\n",
+ ff->size, nr_pmu);
+ return -1;
+ }
+
+ pmu_caps = calloc(nr_pmu, sizeof(*pmu_caps));
if (!pmu_caps)
return -ENOMEM;
@@ -3358,8 +3834,8 @@ static int process_pmu_caps(struct feat_fd *ff, void *data __maybe_unused)
}
}
- ff->ph->env.nr_pmus_with_caps = nr_pmu;
- ff->ph->env.pmu_caps = pmu_caps;
+ env->nr_pmus_with_caps = nr_pmu;
+ env->pmu_caps = pmu_caps;
return 0;
err:
@@ -3374,6 +3850,144 @@ err:
return ret;
}
+static int process_cpu_domain_info(struct feat_fd *ff, void *data __maybe_unused)
+{
+ u32 schedstat_version, max_sched_domains, cpu, domain, nr_domains;
+ struct perf_env *env = &ff->ph->env;
+ char *dname, *cpumask, *cpulist;
+ struct cpu_domain_map **cd_map;
+ struct domain_info *d_info;
+ u32 nra, nr, i, j;
+ int ret;
+
+ nra = env->nr_cpus_avail;
+ nr = env->nr_cpus_online;
+
+ if (nra == 0 || nr == 0) {
+ pr_err("Invalid HEADER_CPU_DOMAIN_INFO: missing HEADER_NRCPUS\n");
+ return -1;
+ }
+
+ if (ff->size < 2 * sizeof(u32) + nr * 2 * sizeof(u32)) {
+ pr_err("Invalid HEADER_CPU_DOMAIN_INFO: section too small (%zu) for %u CPUs\n",
+ (size_t)ff->size, nr);
+ return -1;
+ }
+
+ cd_map = calloc(nra, sizeof(*cd_map));
+ if (!cd_map)
+ return -1;
+
+ env->cpu_domain = cd_map;
+
+ ret = do_read_u32(ff, &schedstat_version);
+ if (ret)
+ return ret;
+
+ env->schedstat_version = schedstat_version;
+
+ ret = do_read_u32(ff, &max_sched_domains);
+ if (ret)
+ return ret;
+
+ /*
+ * Sanity check: real systems have at most ~10 sched domain levels
+ * (SMT, CLS, MC, PKG + NUMA hops). Reject obviously bogus values
+ * from malformed perf.data files before they cause excessive
+ * allocation in the per-CPU loop.
+ */
+ if (max_sched_domains > MAX_SCHED_DOMAINS) {
+ pr_err("Invalid HEADER_CPU_DOMAIN_INFO: max_sched_domains %u > %u\n",
+ max_sched_domains, MAX_SCHED_DOMAINS);
+ return -1;
+ }
+
+ env->max_sched_domains = max_sched_domains;
+
+ for (i = 0; i < nr; i++) {
+ if (do_read_u32(ff, &cpu))
+ return -1;
+
+ if (cpu >= nra) {
+ pr_err("Invalid HEADER_CPU_DOMAIN_INFO: cpu %d >= nr_cpus_avail (%d)\n", cpu, nra);
+ return -1;
+ }
+
+ if (cd_map[cpu]) {
+ pr_err("Invalid HEADER_CPU_DOMAIN_INFO: duplicate cpu %u\n", cpu);
+ return -1;
+ }
+
+ cd_map[cpu] = zalloc(sizeof(*cd_map[cpu]));
+ if (!cd_map[cpu])
+ return -1;
+
+ cd_map[cpu]->cpu = cpu;
+
+ if (do_read_u32(ff, &nr_domains))
+ return -1;
+
+ if (nr_domains > max_sched_domains) {
+ pr_err("Invalid HEADER_CPU_DOMAIN_INFO: nr_domains %u > max_sched_domains (%u)\n",
+ nr_domains, max_sched_domains);
+ return -1;
+ }
+
+ cd_map[cpu]->nr_domains = nr_domains;
+
+ cd_map[cpu]->domains = calloc(max_sched_domains, sizeof(*d_info));
+ if (!cd_map[cpu]->domains)
+ return -1;
+
+ for (j = 0; j < nr_domains; j++) {
+ if (do_read_u32(ff, &domain))
+ return -1;
+
+ if (domain >= max_sched_domains) {
+ pr_err("Invalid HEADER_CPU_DOMAIN_INFO: domain %d >= max_sched_domains (%d)\n",
+ domain, max_sched_domains);
+ return -1;
+ }
+
+ d_info = zalloc(sizeof(*d_info));
+ if (!d_info)
+ return -1;
+
+ if (cd_map[cpu]->domains[domain]) {
+ pr_err("Invalid HEADER_CPU_DOMAIN_INFO: duplicate domain %u for cpu %u\n",
+ domain, cpu);
+ free(d_info);
+ return -1;
+ }
+
+ cd_map[cpu]->domains[domain] = d_info;
+ d_info->domain = domain;
+
+ if (schedstat_version >= 17) {
+ dname = do_read_string(ff);
+ if (!dname)
+ return -1;
+
+ d_info->dname = dname;
+ }
+
+ cpumask = do_read_string(ff);
+ if (!cpumask)
+ return -1;
+
+ d_info->cpumask = cpumask;
+
+ cpulist = do_read_string(ff);
+ if (!cpulist)
+ return -1;
+
+ d_info->cpulist = cpulist;
+ }
+ }
+
+ return ret;
+}
+
#define FEAT_OPR(n, func, __full_only) \
[HEADER_##n] = { \
.name = __stringify(n), \
@@ -3404,9 +4018,7 @@ err:
const struct perf_header_feature_ops feat_ops[HEADER_LAST_FEATURE];
const struct perf_header_feature_ops feat_ops[HEADER_LAST_FEATURE] = {
-#ifdef HAVE_LIBTRACEEVENT
FEAT_OPN(TRACING_DATA, tracing_data, false),
-#endif
FEAT_OPN(BUILD_ID, build_id, false),
FEAT_OPR(HOSTNAME, hostname, false),
FEAT_OPR(OSRELEASE, osrelease, false),
@@ -3430,15 +4042,16 @@ const struct perf_header_feature_ops feat_ops[HEADER_LAST_FEATURE] = {
FEAT_OPR(MEM_TOPOLOGY, mem_topology, true),
FEAT_OPR(CLOCKID, clockid, false),
FEAT_OPN(DIR_FORMAT, dir_format, false),
-#ifdef HAVE_LIBBPF_SUPPORT
FEAT_OPR(BPF_PROG_INFO, bpf_prog_info, false),
FEAT_OPR(BPF_BTF, bpf_btf, false),
-#endif
FEAT_OPR(COMPRESSED, compressed, false),
FEAT_OPR(CPU_PMU_CAPS, cpu_pmu_caps, false),
FEAT_OPR(CLOCK_DATA, clock_data, false),
FEAT_OPN(HYBRID_TOPOLOGY, hybrid_topology, true),
FEAT_OPR(PMU_CAPS, pmu_caps, false),
+ FEAT_OPR(CPU_DOMAIN_INFO, cpu_domain_info, true),
+ FEAT_OPR(E_MACHINE, e_machine, false),
+ FEAT_OPR(CLN_SIZE, cln_size, false),
};
struct header_print_data {
@@ -3446,6 +4059,13 @@ struct header_print_data {
bool full; /* extended list of headers */
};
+const char *header_feat__name(unsigned int id)
+{
+ if (id < HEADER_LAST_FEATURE)
+ return feat_ops[id].name ?: "INVALID";
+ return "INVALID";
+}
+
static int perf_file_section__fprintf_info(struct perf_file_section *section,
struct perf_header *ph,
int feat, int fd, void *data)
@@ -3454,11 +4074,11 @@ static int perf_file_section__fprintf_info(struct perf_file_section *section,
struct feat_fd ff;
if (lseek(fd, section->offset, SEEK_SET) == (off_t)-1) {
- pr_debug("Failed to lseek to %" PRIu64 " offset for feature "
- "%d, continuing...\n", section->offset, feat);
+ pr_debug("Failed to lseek to %" PRIu64 " offset for feature %s (%d), continuing...\n",
+ section->offset, header_feat__name(feat), feat);
return 0;
}
- if (feat >= HEADER_LAST_FEATURE) {
+ if (feat >= ph->last_feat) {
pr_warning("unknown feature %d\n", feat);
return 0;
}
@@ -3510,7 +4130,7 @@ int perf_header__fprintf_info(struct perf_session *session, FILE *fp, bool full)
return 0;
fprintf(fp, "# missing features: ");
- for_each_clear_bit(bit, header->adds_features, HEADER_LAST_FEATURE) {
+ for_each_clear_bit(bit, header->adds_features, header->last_feat) {
if (bit)
fprintf(fp, "%s ", feat_ops[bit].name);
}
@@ -3657,6 +4277,7 @@ static int perf_session__do_write_header(struct perf_session *session,
struct perf_header *header = &session->header;
struct evsel *evsel;
struct feat_fd ff = {
+ .ph = header,
.fd = fd,
};
u64 attr_offset = sizeof(f_header), attr_size = 0;
@@ -3839,7 +4460,7 @@ int perf_header__process_sections(struct perf_header *header, int fd,
if (err < 0)
goto out_free;
- for_each_set_bit(feat, header->adds_features, HEADER_LAST_FEATURE) {
+ for_each_set_bit(feat, header->adds_features, header->last_feat) {
err = process(sec++, header, feat, fd, data);
if (err < 0)
goto out_free;
@@ -4054,6 +4675,7 @@ int perf_file_header__read(struct perf_file_header *header,
ph->data_offset = header->data.offset;
ph->data_size = header->data.size;
ph->feat_offset = header->data.offset + header->data.size;
+ ph->last_feat = HEADER_LAST_FEATURE;
return 0;
}
@@ -4069,8 +4691,8 @@ static int perf_file_section__process(struct perf_file_section *section,
};
if (lseek(fd, section->offset, SEEK_SET) == (off_t)-1) {
- pr_debug("Failed to lseek to %" PRIu64 " offset for feature "
- "%d, continuing...\n", section->offset, feat);
+ pr_debug("Failed to lseek to %" PRIu64 " offset for feature %s (%d), continuing...\n",
+ section->offset, header_feat__name(feat), feat);
return 0;
}
@@ -4103,6 +4725,8 @@ static int perf_file_header__read_pipe(struct perf_pipe_file_header *header,
if (ph->needs_swap)
header->size = bswap_64(header->size);
+ /* The last feature is written out as a 0 sized event and will update this value. */
+ ph->last_feat = 0;
return 0;
}
@@ -4225,7 +4849,7 @@ int perf_session__read_header(struct perf_session *session)
if (session->evlist == NULL)
return -ENOMEM;
- session->evlist->env = &header->env;
+ session->evlist->session = session;
session->machines.host.env = &header->env;
/*
@@ -4335,47 +4959,91 @@ out_delete_evlist:
return -ENOMEM;
}
-int perf_event__process_feature(struct perf_session *session,
+int perf_event__process_feature(const struct perf_tool *tool __maybe_unused,
+ struct perf_session *session,
union perf_event *event)
{
- const struct perf_tool *tool = session->tool;
struct feat_fd ff = { .fd = 0 };
struct perf_record_header_feature *fe = (struct perf_record_header_feature *)event;
+ struct perf_header *header = &session->header;
int type = fe->header.type;
- u64 feat = fe->feat_id;
+ int feat = (int)fe->feat_id;
int ret = 0;
+ bool print = dump_trace;
+ bool last_feature_mark = false;
if (type < 0 || type >= PERF_RECORD_HEADER_MAX) {
pr_warning("invalid record type %d in pipe-mode\n", type);
return 0;
}
- if (feat == HEADER_RESERVED || feat >= HEADER_LAST_FEATURE) {
- pr_warning("invalid record type %d in pipe-mode\n", type);
+ if (feat == HEADER_RESERVED) {
+ pr_warning("invalid reserved record type in pipe-mode\n");
return -1;
}
-
- if (!feat_ops[feat].process)
+ if (feat < 0 || feat == INT_MAX) {
+ pr_warning("invalid value for feature type %x\n", feat);
+ return -1;
+ }
+ if (feat >= header->last_feat) {
+ if (event->header.size == sizeof(*fe)) {
+ /*
+ * Either an unexpected zero size feature or the
+ * HEADER_LAST_FEATURE mark.
+ */
+ if (feat > header->last_feat)
+ header->last_feat = min(feat, HEADER_LAST_FEATURE);
+ last_feature_mark = true;
+ } else {
+ /*
+ * A feature but beyond what is known as in
+ * bounds. Assume the last feature is 1 beyond this
+ * feature.
+ */
+ session->header.last_feat = min(feat + 1, HEADER_LAST_FEATURE);
+ }
+ }
+ if (feat >= HEADER_LAST_FEATURE) {
+ if (!last_feature_mark) {
+ pr_warning("unknown feature %d for data file version (%s) in this version of perf (%s)\n",
+ feat, header->env.version, perf_version_string);
+ }
return 0;
-
+ }
+ if (event->header.size < sizeof(*fe)) {
+ pr_warning("feature header size too small\n");
+ return -1;
+ }
ff.buf = (void *)fe->data;
ff.size = event->header.size - sizeof(*fe);
- ff.ph = &session->header;
+ ff.ph = header;
- if (feat_ops[feat].process(&ff, NULL)) {
- ret = -1;
+ if (feat_ops[feat].process && feat_ops[feat].process(&ff, NULL)) {
+ // Processing failed, ignore when this is the last feature mark.
+ if (!last_feature_mark)
+ ret = -1;
goto out;
}
- if (!feat_ops[feat].print || !tool->show_feat_hdr)
- goto out;
+ if (session->tool->show_feat_hdr) {
+ if (!feat_ops[feat].full_only ||
+ session->tool->show_feat_hdr >= SHOW_FEAT_HEADER_FULL_INFO) {
+ print = true;
+ } else {
+ fprintf(stdout, "# %s info available, use -I to display\n",
+ feat_ops[feat].name);
+ }
+ }
- if (!feat_ops[feat].full_only ||
- tool->show_feat_hdr >= SHOW_FEAT_HEADER_FULL_INFO) {
- feat_ops[feat].print(&ff, stdout);
- } else {
- fprintf(stdout, "# %s info available, use -I to display\n",
- feat_ops[feat].name);
+ if (dump_trace)
+ printf(", ");
+
+ if (print) {
+ if (feat_ops[feat].print)
+ feat_ops[feat].print(&ff, stdout);
+ else
+ printf("# %s", feat_ops[feat].name);
}
+
out:
free_event_desc(ff.events);
return ret;
@@ -4417,6 +5085,11 @@ size_t perf_event__fprintf_event_update(union perf_event *event, FILE *fp)
return ret;
}
+size_t perf_event__fprintf_attr(union perf_event *event, FILE *fp)
+{
+ return perf_event_attr__fprintf(fp, &event->attr.attr, __desc_attr__fprintf, NULL);
+}
+
int perf_event__process_attr(const struct perf_tool *tool __maybe_unused,
union perf_event *event,
struct evlist **pevlist)
@@ -4426,6 +5099,9 @@ int perf_event__process_attr(const struct perf_tool *tool __maybe_unused,
struct evsel *evsel;
struct evlist *evlist = *pevlist;
+ if (dump_trace)
+ perf_event__fprintf_attr(event, stdout);
+
if (evlist == NULL) {
*pevlist = evlist = evlist__new();
if (evlist == NULL)
@@ -4492,8 +5168,8 @@ int perf_event__process_event_update(const struct perf_tool *tool __maybe_unused
case PERF_EVENT_UPDATE__CPUS:
map = cpu_map__new_data(&ev->cpus.cpus);
if (map) {
- perf_cpu_map__put(evsel->core.own_cpus);
- evsel->core.own_cpus = map;
+ perf_cpu_map__put(evsel->core.pmu_cpus);
+ evsel->core.pmu_cpus = map;
} else
pr_err("failed to get event_update cpus\n");
default:
@@ -4504,7 +5180,8 @@ int perf_event__process_event_update(const struct perf_tool *tool __maybe_unused
}
#ifdef HAVE_LIBTRACEEVENT
-int perf_event__process_tracing_data(struct perf_session *session,
+int perf_event__process_tracing_data(const struct perf_tool *tool __maybe_unused,
+ struct perf_session *session,
union perf_event *event)
{
ssize_t size_read, padding, size = event->tracing_data.size;
@@ -4552,7 +5229,8 @@ int perf_event__process_tracing_data(struct perf_session *session,
}
#endif
-int perf_event__process_build_id(struct perf_session *session,
+int perf_event__process_build_id(const struct perf_tool *tool __maybe_unused,
+ struct perf_session *session,
union perf_event *event)
{
__event_process_build_id(&event->build_id,