diff options
Diffstat (limited to 'tools/perf/util/header.c')
| -rw-r--r-- | tools/perf/util/header.c | 1028 |
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, |
