Skip to content

Commit c72bf82

Browse files
tlfalconnamhyung
authored andcommitted
perf top: populate PMU capabilities data in perf_env
Calling perf top with branch filters enabled on Intel CPU's with branch counters logging (A.K.A LBR event logging [1]) support results in a segfault. $ perf top -e '{cpu_core/cpu-cycles/,cpu_core/event=0xc6,umask=0x3,frontend=0x11,name=frontend_retired_dsb_miss/}' -j any,counter ... Thread 27 "perf" received signal SIGSEGV, Segmentation fault. [Switching to Thread 0x7fffafff76c0 (LWP 949003)] perf_env__find_br_cntr_info (env=0xf66dc0 <perf_env>, nr=0x0, width=0x7fffafff62c0) at util/env.c:653 653 *width = env->cpu_pmu_caps ? env->br_cntr_width : (gdb) bt #0 perf_env__find_br_cntr_info (env=0xf66dc0 <perf_env>, nr=0x0, width=0x7fffafff62c0) at util/env.c:653 #1 0x00000000005b1599 in symbol__account_br_cntr (branch=0x7fffcc3db580, evsel=0xfea2d0, offset=12, br_cntr=8) at util/annotate.c:345 #2 0x00000000005b17fb in symbol__account_cycles (addr=5658172, start=5658160, sym=0x7fffcc0ee420, cycles=539, evsel=0xfea2d0, br_cntr=8) at util/annotate.c:389 #3 0x00000000005b1976 in addr_map_symbol__account_cycles (ams=0x7fffcd7b01d0, start=0x7fffcd7b02b0, cycles=539, evsel=0xfea2d0, br_cntr=8) at util/annotate.c:422 #4 0x000000000068d57f in hist__account_cycles (bs=0x110d288, al=0x7fffafff6540, sample=0x7fffafff6760, nonany_branch_mode=false, total_cycles=0x0, evsel=0xfea2d0) at util/hist.c:2850 #5 0x0000000000446216 in hist_iter__top_callback (iter=0x7fffafff6590, al=0x7fffafff6540, single=true, arg=0x7fffffff9e00) at builtin-top.c:737 #6 0x0000000000689787 in hist_entry_iter__add (iter=0x7fffafff6590, al=0x7fffafff6540, max_stack_depth=127, arg=0x7fffffff9e00) at util/hist.c:1359 #7 0x0000000000446710 in perf_event__process_sample (tool=0x7fffffff9e00, event=0x110d250, evsel=0xfea2d0, sample=0x7fffafff6760, machine=0x108c968) at builtin-top.c:845 #8 0x0000000000447735 in deliver_event (qe=0x7fffffffa120, qevent=0x10fc200) at builtin-top.c:1211 #9 0x000000000064ccae in do_flush (oe=0x7fffffffa120, show_progress=false) at util/ordered-events.c:245 #10 0x000000000064d005 in __ordered_events__flush (oe=0x7fffffffa120, how=OE_FLUSH__TOP, timestamp=0) at util/ordered-events.c:324 #11 0x000000000064d0ef in ordered_events__flush (oe=0x7fffffffa120, how=OE_FLUSH__TOP) at util/ordered-events.c:342 #12 0x00000000004472a9 in process_thread (arg=0x7fffffff9e00) at builtin-top.c:1120 #13 0x00007ffff6e7dba8 in start_thread (arg=<optimized out>) at pthread_create.c:448 #14 0x00007ffff6f01b8c in __GI___clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:78 The cause is that perf_env__find_br_cntr_info tries to access a null pointer pmu_caps in the perf_env struct. A similar issue exists for homogeneous core systems which use the cpu_pmu_caps structure. Fix this by populating cpu_pmu_caps and pmu_caps structures with values from sysfs when calling perf top with branch stack sampling enabled. [1], LBR event logging introduced here: https://lore.kernel.org/all/[email protected]/ Reviewed-by: Ian Rogers <[email protected]> Signed-off-by: Thomas Falcon <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Namhyung Kim <[email protected]>
1 parent ac87187 commit c72bf82

File tree

3 files changed

+119
-0
lines changed

3 files changed

+119
-0
lines changed

tools/perf/builtin-top.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1734,6 +1734,14 @@ int cmd_top(int argc, const char **argv)
17341734
if (opts->branch_stack && callchain_param.enabled)
17351735
symbol_conf.show_branchflag_count = true;
17361736

1737+
if (opts->branch_stack) {
1738+
status = perf_env__read_core_pmu_caps(&perf_env);
1739+
if (status) {
1740+
pr_err("PMU capability data is not available\n");
1741+
goto out_delete_evlist;
1742+
}
1743+
}
1744+
17371745
sort__mode = SORT_MODE__TOP;
17381746
/* display thread wants entries to be collapsed in a different tree */
17391747
perf_hpp_list.need_collapse = 1;

tools/perf/util/env.c

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,116 @@ static int perf_env__read_nr_cpus_avail(struct perf_env *env)
433433
return env->nr_cpus_avail ? 0 : -ENOENT;
434434
}
435435

436+
static int __perf_env__read_core_pmu_caps(const struct perf_pmu *pmu,
437+
int *nr_caps, char ***caps,
438+
unsigned int *max_branches,
439+
unsigned int *br_cntr_nr,
440+
unsigned int *br_cntr_width)
441+
{
442+
struct perf_pmu_caps *pcaps = NULL;
443+
char *ptr, **tmp;
444+
int ret = 0;
445+
446+
*nr_caps = 0;
447+
*caps = NULL;
448+
449+
if (!pmu->nr_caps)
450+
return 0;
451+
452+
*caps = calloc(pmu->nr_caps, sizeof(char *));
453+
if (!*caps)
454+
return -ENOMEM;
455+
456+
tmp = *caps;
457+
list_for_each_entry(pcaps, &pmu->caps, list) {
458+
if (asprintf(&ptr, "%s=%s", pcaps->name, pcaps->value) < 0) {
459+
ret = -ENOMEM;
460+
goto error;
461+
}
462+
463+
*tmp++ = ptr;
464+
465+
if (!strcmp(pcaps->name, "branches"))
466+
*max_branches = atoi(pcaps->value);
467+
else if (!strcmp(pcaps->name, "branch_counter_nr"))
468+
*br_cntr_nr = atoi(pcaps->value);
469+
else if (!strcmp(pcaps->name, "branch_counter_width"))
470+
*br_cntr_width = atoi(pcaps->value);
471+
}
472+
*nr_caps = pmu->nr_caps;
473+
return 0;
474+
error:
475+
while (tmp-- != *caps)
476+
zfree(tmp);
477+
zfree(caps);
478+
*nr_caps = 0;
479+
return ret;
480+
}
481+
482+
int perf_env__read_core_pmu_caps(struct perf_env *env)
483+
{
484+
struct pmu_caps *pmu_caps;
485+
struct perf_pmu *pmu = NULL;
486+
int nr_pmu, i = 0, j;
487+
int ret;
488+
489+
nr_pmu = perf_pmus__num_core_pmus();
490+
491+
if (!nr_pmu)
492+
return -ENODEV;
493+
494+
if (nr_pmu == 1) {
495+
pmu = perf_pmus__find_core_pmu();
496+
if (!pmu)
497+
return -ENODEV;
498+
ret = perf_pmu__caps_parse(pmu);
499+
if (ret < 0)
500+
return ret;
501+
return __perf_env__read_core_pmu_caps(pmu, &env->nr_cpu_pmu_caps,
502+
&env->cpu_pmu_caps,
503+
&env->max_branches,
504+
&env->br_cntr_nr,
505+
&env->br_cntr_width);
506+
}
507+
508+
pmu_caps = calloc(nr_pmu, sizeof(*pmu_caps));
509+
if (!pmu_caps)
510+
return -ENOMEM;
511+
512+
while ((pmu = perf_pmus__scan_core(pmu)) != NULL) {
513+
if (perf_pmu__caps_parse(pmu) <= 0)
514+
continue;
515+
ret = __perf_env__read_core_pmu_caps(pmu, &pmu_caps[i].nr_caps,
516+
&pmu_caps[i].caps,
517+
&pmu_caps[i].max_branches,
518+
&pmu_caps[i].br_cntr_nr,
519+
&pmu_caps[i].br_cntr_width);
520+
if (ret)
521+
goto error;
522+
523+
pmu_caps[i].pmu_name = strdup(pmu->name);
524+
if (!pmu_caps[i].pmu_name) {
525+
ret = -ENOMEM;
526+
goto error;
527+
}
528+
i++;
529+
}
530+
531+
env->nr_pmus_with_caps = nr_pmu;
532+
env->pmu_caps = pmu_caps;
533+
534+
return 0;
535+
error:
536+
for (i = 0; i < nr_pmu; i++) {
537+
for (j = 0; j < pmu_caps[i].nr_caps; j++)
538+
zfree(&pmu_caps[i].caps[j]);
539+
zfree(&pmu_caps[i].caps);
540+
zfree(&pmu_caps[i].pmu_name);
541+
}
542+
zfree(&pmu_caps);
543+
return ret;
544+
}
545+
436546
const char *perf_env__raw_arch(struct perf_env *env)
437547
{
438548
return env && !perf_env__read_arch(env) ? env->arch : "unknown";

tools/perf/util/env.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ struct btf_node;
152152

153153
extern struct perf_env perf_env;
154154

155+
int perf_env__read_core_pmu_caps(struct perf_env *env);
155156
void perf_env__exit(struct perf_env *env);
156157

157158
int perf_env__kernel_is_64_bit(struct perf_env *env);

0 commit comments

Comments
 (0)