diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2023-07-08 19:21:51 +0200 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2023-07-08 19:21:51 +0200 |
commit | c206353dfdf026dafd42679cf82d6f0a89781e36 (patch) | |
tree | 47567b5de6e64f615c5547cc137178f3a942cc1c /tools/perf/util | |
parent | Merge tag 'bitmap-6.5-rc1' of https://github.com/norov/linux (diff) | |
parent | perf test: Fix event parsing test when PERF_PMU_CAP_EXTENDED_HW_TYPE isn't su... (diff) | |
download | linux-c206353dfdf026dafd42679cf82d6f0a89781e36.tar.xz linux-c206353dfdf026dafd42679cf82d6f0a89781e36.zip |
Merge tag 'perf-tools-for-v6.5-2-2023-07-06' of git://git.kernel.org/pub/scm/linux/kernel/git/perf/perf-tools-next
Pull more perf tools updates from Namhyung Kim:
"These are remaining changes and fixes for this cycle.
Build:
- Allow generating vmlinux.h from BTF using `make GEN_VMLINUX_H=1`
and skip if the vmlinux has no BTF.
- Replace deprecated clang -target xxx option by --target=xxx.
perf record:
- Print event attributes with well known type and config symbols in
the debug output like below:
# perf record -e cycles,cpu-clock -C0 -vv true
<SNIP>
------------------------------------------------------------
perf_event_attr:
type 0 (PERF_TYPE_HARDWARE)
size 136
config 0 (PERF_COUNT_HW_CPU_CYCLES)
{ sample_period, sample_freq } 4000
sample_type IP|TID|TIME|CPU|PERIOD|IDENTIFIER
read_format ID
disabled 1
inherit 1
freq 1
sample_id_all 1
exclude_guest 1
------------------------------------------------------------
sys_perf_event_open: pid -1 cpu 0 group_fd -1 flags 0x8 = 5
------------------------------------------------------------
perf_event_attr:
type 1 (PERF_TYPE_SOFTWARE)
size 136
config 0 (PERF_COUNT_SW_CPU_CLOCK)
{ sample_period, sample_freq } 4000
sample_type IP|TID|TIME|CPU|PERIOD|IDENTIFIER
read_format ID
disabled 1
inherit 1
freq 1
sample_id_all 1
exclude_guest 1
- Update AMD IBS event error message since it now support per-process
profiling but no priviledge filters.
$ sudo perf record -e ibs_op//k -C 0
Error:
AMD IBS doesn't support privilege filtering. Try again without
the privilege modifiers (like 'k') at the end.
perf lock contention:
- Support CSV style output using -x option
$ sudo perf lock con -ab -x, sleep 1
# output: contended, total wait, max wait, avg wait, type, caller
19, 194232, 21415, 10222, spinlock, process_one_work+0x1f0
15, 162748, 23843, 10849, rwsem:R, do_user_addr_fault+0x40e
4, 86740, 23415, 21685, rwlock:R, ep_poll_callback+0x2d
1, 84281, 84281, 84281, mutex, iwl_mvm_async_handlers_wk+0x135
8, 67608, 27404, 8451, spinlock, __queue_work+0x174
3, 58616, 31125, 19538, rwsem:W, do_mprotect_pkey+0xff
3, 52953, 21172, 17651, rwlock:W, do_epoll_wait+0x248
2, 30324, 19704, 15162, rwsem:R, do_madvise+0x3ad
1, 24619, 24619, 24619, spinlock, rcu_core+0xd4
- Add --output option to save the data to a file not to be interfered
by other debug messages.
Test:
- Fix event parsing test on ARM where there's no raw PMU nor supports
PERF_PMU_CAP_EXTENDED_HW_TYPE.
- Update the lock contention test case for CSV output.
- Fix a segfault in the daemon command test.
Vendor events (JSON):
- Add has_event() to check if the given event is available on system
at runtime. On Intel machines, some transaction events may not be
present when TSC extensions are disabled.
- Update Intel event metrics.
Misc:
- Sort symbols by name using an external array of pointers instead of
a rbtree node in the symbol. This will save 16-bytes or 24-bytes
per symbol whether the sorting is actually requested or not.
- Fix unwinding DWARF callstacks using libdw when --symfs option is
used"
* tag 'perf-tools-for-v6.5-2-2023-07-06' of git://git.kernel.org/pub/scm/linux/kernel/git/perf/perf-tools-next: (38 commits)
perf test: Fix event parsing test when PERF_PMU_CAP_EXTENDED_HW_TYPE isn't supported.
perf test: Fix event parsing test on Arm
perf evsel amd: Fix IBS error message
perf: unwind: Fix symfs with libdw
perf symbol: Fix uninitialized return value in symbols__find_by_name()
perf test: Test perf lock contention CSV output
perf lock contention: Add --output option
perf lock contention: Add -x option for CSV style output
perf lock: Remove stale comments
perf vendor events intel: Update tigerlake to 1.13
perf vendor events intel: Update skylakex to 1.31
perf vendor events intel: Update skylake to 57
perf vendor events intel: Update sapphirerapids to 1.14
perf vendor events intel: Update icelakex to 1.21
perf vendor events intel: Update icelake to 1.19
perf vendor events intel: Update cascadelakex to 1.19
perf vendor events intel: Update meteorlake to 1.03
perf vendor events intel: Add rocketlake events/metrics
perf vendor metrics intel: Make transaction metrics conditional
perf jevents: Support for has_event function
...
Diffstat (limited to 'tools/perf/util')
28 files changed, 407 insertions, 145 deletions
diff --git a/tools/perf/util/bpf_skel/.gitignore b/tools/perf/util/bpf_skel/.gitignore index 7a1c832825de..cd01455e1b53 100644 --- a/tools/perf/util/bpf_skel/.gitignore +++ b/tools/perf/util/bpf_skel/.gitignore @@ -1,3 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only .tmp *.skel.h +vmlinux.h diff --git a/tools/perf/util/bpf_skel/lock_contention.bpf.c b/tools/perf/util/bpf_skel/lock_contention.bpf.c index 1d48226ae75d..8d3cfbb3cc65 100644 --- a/tools/perf/util/bpf_skel/lock_contention.bpf.c +++ b/tools/perf/util/bpf_skel/lock_contention.bpf.c @@ -416,8 +416,6 @@ int contention_end(u64 *ctx) return 0; } -struct rq {}; - extern struct rq runqueues __ksym; struct rq___old { diff --git a/tools/perf/util/bpf_skel/vmlinux.h b/tools/perf/util/bpf_skel/vmlinux/vmlinux.h index c7ed51b0c1ef..ab84a6e1da5e 100644 --- a/tools/perf/util/bpf_skel/vmlinux.h +++ b/tools/perf/util/bpf_skel/vmlinux/vmlinux.h @@ -171,4 +171,14 @@ struct bpf_perf_event_data_kern { struct perf_sample_data *data; struct perf_event *event; } __attribute__((preserve_access_index)); + +/* + * If 'struct rq' isn't defined for lock_contention.bpf.c, for the sake of + * rq___old and rq___new, then the type for the 'runqueue' variable ends up + * being a forward declaration (BTF_KIND_FWD) while the kernel has it defined + * (BTF_KIND_STRUCT). The definition appears in vmlinux.h rather than + * lock_contention.bpf.c for consistency with a generated vmlinux.h. + */ +struct rq {}; + #endif // __VMLINUX_H diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index 046fbfcfdaab..bdfead36b83a 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c @@ -1320,7 +1320,9 @@ struct dso *dso__new_id(const char *name, struct dso_id *id) dso->id = *id; dso__set_long_name_id(dso, dso->name, id, false); dso__set_short_name(dso, dso->name, false); - dso->symbols = dso->symbol_names = RB_ROOT_CACHED; + dso->symbols = RB_ROOT_CACHED; + dso->symbol_names = NULL; + dso->symbol_names_len = 0; dso->data.cache = RB_ROOT; dso->inlined_nodes = RB_ROOT_CACHED; dso->srclines = RB_ROOT_CACHED; @@ -1364,7 +1366,8 @@ void dso__delete(struct dso *dso) inlines__tree_delete(&dso->inlined_nodes); srcline__tree_delete(&dso->srclines); symbols__delete(&dso->symbols); - + dso->symbol_names_len = 0; + zfree(&dso->symbol_names); if (dso->short_name_allocated) { zfree((char **)&dso->short_name); dso->short_name_allocated = false; diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h index b23a157c914d..b41c9782c754 100644 --- a/tools/perf/util/dso.h +++ b/tools/perf/util/dso.h @@ -150,7 +150,8 @@ struct dso { struct rb_node rb_node; /* rbtree node sorted by long name */ struct rb_root *root; /* root of rbtree that rb_node is in */ struct rb_root_cached symbols; - struct rb_root_cached symbol_names; + struct symbol **symbol_names; + size_t symbol_names_len; struct rb_root_cached inlined_nodes; struct rb_root_cached srclines; struct { diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 3860b0c74829..4cbb092e0684 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -581,15 +581,14 @@ struct map *thread__find_map(struct thread *thread, u8 cpumode, u64 addr, maps__zput(al->maps); map__zput(al->map); thread__zput(al->thread); + al->thread = thread__get(thread); al->addr = addr; al->cpumode = cpumode; al->filtered = 0; - if (machine == NULL) { - al->map = NULL; + if (machine == NULL) return NULL; - } if (cpumode == PERF_RECORD_MISC_KERNEL && perf_host) { al->level = 'k'; @@ -605,7 +604,6 @@ struct map *thread__find_map(struct thread *thread, u8 cpumode, u64 addr, al->level = 'u'; } else { al->level = 'H'; - al->map = NULL; if ((cpumode == PERF_RECORD_MISC_GUEST_USER || cpumode == PERF_RECORD_MISC_GUEST_KERNEL) && @@ -619,7 +617,6 @@ struct map *thread__find_map(struct thread *thread, u8 cpumode, u64 addr, return NULL; } al->maps = maps__get(maps); - al->thread = thread__get(thread); al->map = map__get(maps__find(maps, al->addr)); if (al->map != NULL) { /* diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index f607b5bddc76..762e2b2634a5 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -2924,25 +2924,19 @@ static bool find_process(const char *name) return ret ? false : true; } -static bool is_amd(const char *arch, const char *cpuid) +int __weak arch_evsel__open_strerror(struct evsel *evsel __maybe_unused, + char *msg __maybe_unused, + size_t size __maybe_unused) { - return arch && !strcmp("x86", arch) && cpuid && strstarts(cpuid, "AuthenticAMD"); -} - -static bool is_amd_ibs(struct evsel *evsel) -{ - return evsel->core.attr.precise_ip - || (evsel->pmu_name && !strncmp(evsel->pmu_name, "ibs", 3)); + return 0; } int evsel__open_strerror(struct evsel *evsel, struct target *target, int err, char *msg, size_t size) { - struct perf_env *env = evsel__env(evsel); - const char *arch = perf_env__arch(env); - const char *cpuid = perf_env__cpuid(env); char sbuf[STRERR_BUFSIZE]; int printed = 0, enforced = 0; + int ret; switch (err) { case EPERM: @@ -3044,16 +3038,6 @@ int evsel__open_strerror(struct evsel *evsel, struct target *target, return scnprintf(msg, size, "Invalid event (%s) in per-thread mode, enable system wide with '-a'.", evsel__name(evsel)); - if (is_amd(arch, cpuid)) { - if (is_amd_ibs(evsel)) { - if (evsel->core.attr.exclude_kernel) - return scnprintf(msg, size, - "AMD IBS can't exclude kernel events. Try running at a higher privilege level."); - if (!evsel->core.system_wide) - return scnprintf(msg, size, - "AMD IBS may only be available in system-wide/per-cpu mode. Try using -a, or -C and workload affinity"); - } - } break; case ENODATA: @@ -3063,6 +3047,10 @@ int evsel__open_strerror(struct evsel *evsel, struct target *target, break; } + ret = arch_evsel__open_strerror(evsel, msg, size); + if (ret) + return ret; + return scnprintf(msg, size, "The sys_perf_event_open() syscall returned with %d (%s) for event (%s).\n" "/bin/dmesg | grep -i perf may provide additional information.\n", diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 9f06d6cd5379..848534ec74fa 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -311,6 +311,7 @@ void evsel__set_sample_id(struct evsel *evsel, bool use_sample_identifier); void arch_evsel__set_sample_weight(struct evsel *evsel); void arch__post_evsel_config(struct evsel *evsel, struct perf_event_attr *attr); +int arch_evsel__open_strerror(struct evsel *evsel, char *msg, size_t size); int evsel__set_filter(struct evsel *evsel, const char *filter); int evsel__append_tp_filter(struct evsel *evsel, const char *filter); diff --git a/tools/perf/util/expr.c b/tools/perf/util/expr.c index f4e52919324e..4814262e3805 100644 --- a/tools/perf/util/expr.c +++ b/tools/perf/util/expr.c @@ -8,6 +8,7 @@ #include "cpumap.h" #include "cputopo.h" #include "debug.h" +#include "evlist.h" #include "expr.h" #include "expr-bison.h" #include "expr-flex.h" @@ -474,3 +475,23 @@ out: pr_debug2("literal: %s = %f\n", literal, result); return result; } + +/* Does the event 'id' parse? Determine via ctx->ids if possible. */ +double expr__has_event(const struct expr_parse_ctx *ctx, bool compute_ids, const char *id) +{ + struct evlist *tmp; + double ret; + + if (hashmap__find(ctx->ids, id, /*value=*/NULL)) + return 1.0; + + if (!compute_ids) + return 0.0; + + tmp = evlist__new(); + if (!tmp) + return NAN; + ret = parse_event(tmp, id) ? 0 : 1; + evlist__delete(tmp); + return ret; +} diff --git a/tools/perf/util/expr.h b/tools/perf/util/expr.h index eaa44b24c555..3c1e49b3e35d 100644 --- a/tools/perf/util/expr.h +++ b/tools/perf/util/expr.h @@ -54,5 +54,6 @@ int expr__find_ids(const char *expr, const char *one, double expr_id_data__value(const struct expr_id_data *data); double expr_id_data__source_count(const struct expr_id_data *data); double expr__get_literal(const char *literal, const struct expr_scanner_ctx *ctx); +double expr__has_event(const struct expr_parse_ctx *ctx, bool compute_ids, const char *id); #endif diff --git a/tools/perf/util/expr.l b/tools/perf/util/expr.l index 4fbf353e78e7..dbb117414710 100644 --- a/tools/perf/util/expr.l +++ b/tools/perf/util/expr.l @@ -113,6 +113,7 @@ min { return MIN; } if { return IF; } else { return ELSE; } source_count { return SOURCE_COUNT; } +has_event { return HAS_EVENT; } {literal} { return literal(yyscanner, sctx); } {number} { return value(yyscanner); } {symbol} { return str(yyscanner, ID, sctx->runtime); } diff --git a/tools/perf/util/expr.y b/tools/perf/util/expr.y index f04963eb6be0..dd504afd8f36 100644 --- a/tools/perf/util/expr.y +++ b/tools/perf/util/expr.y @@ -37,7 +37,7 @@ } ids; } -%token ID NUMBER MIN MAX IF ELSE LITERAL D_RATIO SOURCE_COUNT EXPR_ERROR +%token ID NUMBER MIN MAX IF ELSE LITERAL D_RATIO SOURCE_COUNT HAS_EVENT EXPR_ERROR %left MIN MAX IF %left '|' %left '^' @@ -199,6 +199,12 @@ expr: NUMBER } | ID { $$ = handle_id(ctx, $1, compute_ids, /*source_count=*/false); } | SOURCE_COUNT '(' ID ')' { $$ = handle_id(ctx, $3, compute_ids, /*source_count=*/true); } +| HAS_EVENT '(' ID ')' +{ + $$.val = expr__has_event(ctx, compute_ids, $3); + $$.ids = NULL; + free($3); +} | expr '|' expr { if (is_const($1.val) && is_const($3.val)) { diff --git a/tools/perf/util/llvm-utils.c b/tools/perf/util/llvm-utils.c index 4e8e243a6e4b..c6c9c2228578 100644 --- a/tools/perf/util/llvm-utils.c +++ b/tools/perf/util/llvm-utils.c @@ -25,7 +25,7 @@ "$CLANG_OPTIONS $PERF_BPF_INC_OPTIONS $KERNEL_INC_OPTIONS " \ "-Wno-unused-value -Wno-pointer-sign " \ "-working-directory $WORKING_DIR " \ - "-c \"$CLANG_SOURCE\" -target bpf $CLANG_EMIT_LLVM -g -O2 -o - $LLVM_OPTIONS_PIPE" + "-c \"$CLANG_SOURCE\" --target=bpf $CLANG_EMIT_LLVM -g -O2 -o - $LLVM_OPTIONS_PIPE" struct llvm_param llvm_param = { .clang_path = "clang", @@ -569,7 +569,7 @@ int llvm__compile_bpf(const char *path, void **p_obj_buf, pr_err("ERROR:\tunable to compile %s\n", path); pr_err("Hint:\tCheck error message shown above.\n"); pr_err("Hint:\tYou can also pre-compile it into .o using:\n"); - pr_err(" \t\tclang -target bpf -O2 -c %s\n", path); + pr_err(" \t\tclang --target=bpf -O2 -c %s\n", path); pr_err(" \twith proper -I and -D options.\n"); goto errout; } diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index f30d34903aa4..f64b83004421 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -390,7 +390,7 @@ struct symbol *map__find_symbol(struct map *map, u64 addr) return dso__find_symbol(map__dso(map), addr); } -struct symbol *map__find_symbol_by_name(struct map *map, const char *name) +struct symbol *map__find_symbol_by_name_idx(struct map *map, const char *name, size_t *idx) { struct dso *dso; @@ -398,10 +398,16 @@ struct symbol *map__find_symbol_by_name(struct map *map, const char *name) return NULL; dso = map__dso(map); - if (!dso__sorted_by_name(dso)) - dso__sort_by_name(dso); + dso__sort_by_name(dso); + + return dso__find_symbol_by_name(dso, name, idx); +} + +struct symbol *map__find_symbol_by_name(struct map *map, const char *name) +{ + size_t idx; - return dso__find_symbol_by_name(dso, name); + return map__find_symbol_by_name_idx(map, name, &idx); } struct map *map__clone(struct map *from) diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index 66a87b3d9965..1b53d53adc86 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h @@ -148,16 +148,17 @@ struct thread; * @map: the 'struct map *' in which symbols are iterated * @sym_name: the symbol name * @pos: the 'struct symbol *' to use as a loop cursor + * @idx: the cursor index in the symbol names array */ -#define __map__for_each_symbol_by_name(map, sym_name, pos) \ - for (pos = map__find_symbol_by_name(map, sym_name); \ +#define __map__for_each_symbol_by_name(map, sym_name, pos, idx) \ + for (pos = map__find_symbol_by_name_idx(map, sym_name, &idx); \ pos && \ !symbol__match_symbol_name(pos->name, sym_name, \ SYMBOL_TAG_INCLUDE__DEFAULT_ONLY); \ - pos = symbol__next_by_name(pos)) + pos = dso__next_symbol_by_name(map__dso(map), &idx)) -#define map__for_each_symbol_by_name(map, sym_name, pos) \ - __map__for_each_symbol_by_name(map, sym_name, (pos)) +#define map__for_each_symbol_by_name(map, sym_name, pos, idx) \ + __map__for_each_symbol_by_name(map, sym_name, (pos), idx) void map__init(struct map *map, u64 start, u64 end, u64 pgoff, struct dso *dso); @@ -202,6 +203,7 @@ int map__fprintf_srcline(struct map *map, u64 addr, const char *prefix, int map__load(struct map *map); struct symbol *map__find_symbol(struct map *map, u64 addr); struct symbol *map__find_symbol_by_name(struct map *map, const char *name); +struct symbol *map__find_symbol_by_name_idx(struct map *map, const char *name, size_t *idx); void map__fixup_start(struct map *map); void map__fixup_end(struct map *map); diff --git a/tools/perf/util/perf_event_attr_fprintf.c b/tools/perf/util/perf_event_attr_fprintf.c index 7e5e7b30510d..2247991451f3 100644 --- a/tools/perf/util/perf_event_attr_fprintf.c +++ b/tools/perf/util/perf_event_attr_fprintf.c @@ -1,11 +1,13 @@ // SPDX-License-Identifier: GPL-2.0 #include <inttypes.h> #include <stdio.h> +#include <stdlib.h> #include <stdbool.h> #include <linux/kernel.h> #include <linux/types.h> #include <linux/perf_event.h> #include "util/evsel_fprintf.h" +#include "trace-event.h" struct bit_names { int bit; @@ -71,6 +73,170 @@ static void __p_read_format(char *buf, size_t size, u64 value) __p_bits(buf, size, value, bits); } +#define ENUM_ID_TO_STR_CASE(x) case x: return (#x); +static const char *stringify_perf_type_id(u64 value) +{ + switch (value) { + ENUM_ID_TO_STR_CASE(PERF_TYPE_HARDWARE) + ENUM_ID_TO_STR_CASE(PERF_TYPE_SOFTWARE) + ENUM_ID_TO_STR_CASE(PERF_TYPE_TRACEPOINT) + ENUM_ID_TO_STR_CASE(PERF_TYPE_HW_CACHE) + ENUM_ID_TO_STR_CASE(PERF_TYPE_RAW) + ENUM_ID_TO_STR_CASE(PERF_TYPE_BREAKPOINT) + default: + return NULL; + } +} + +static const char *stringify_perf_hw_id(u64 value) +{ + switch (value) { + ENUM_ID_TO_STR_CASE(PERF_COUNT_HW_CPU_CYCLES) + ENUM_ID_TO_STR_CASE(PERF_COUNT_HW_INSTRUCTIONS) + ENUM_ID_TO_STR_CASE(PERF_COUNT_HW_CACHE_REFERENCES) + ENUM_ID_TO_STR_CASE(PERF_COUNT_HW_CACHE_MISSES) + ENUM_ID_TO_STR_CASE(PERF_COUNT_HW_BRANCH_INSTRUCTIONS) + ENUM_ID_TO_STR_CASE(PERF_COUNT_HW_BRANCH_MISSES) + ENUM_ID_TO_STR_CASE(PERF_COUNT_HW_BUS_CYCLES) + ENUM_ID_TO_STR_CASE(PERF_COUNT_HW_STALLED_CYCLES_FRONTEND) + ENUM_ID_TO_STR_CASE(PERF_COUNT_HW_STALLED_CYCLES_BACKEND) + ENUM_ID_TO_STR_CASE(PERF_COUNT_HW_REF_CPU_CYCLES) + default: + return NULL; + } +} + +static const char *stringify_perf_hw_cache_id(u64 value) +{ + switch (value) { + ENUM_ID_TO_STR_CASE(PERF_COUNT_HW_CACHE_L1D) + ENUM_ID_TO_STR_CASE(PERF_COUNT_HW_CACHE_L1I) + ENUM_ID_TO_STR_CASE(PERF_COUNT_HW_CACHE_LL) + ENUM_ID_TO_STR_CASE(PERF_COUNT_HW_CACHE_DTLB) + ENUM_ID_TO_STR_CASE(PERF_COUNT_HW_CACHE_ITLB) + ENUM_ID_TO_STR_CASE(PERF_COUNT_HW_CACHE_BPU) + ENUM_ID_TO_STR_CASE(PERF_COUNT_HW_CACHE_NODE) + default: + return NULL; + } +} + +static const char *stringify_perf_hw_cache_op_id(u64 value) +{ + switch (value) { + ENUM_ID_TO_STR_CASE(PERF_COUNT_HW_CACHE_OP_READ) + ENUM_ID_TO_STR_CASE(PERF_COUNT_HW_CACHE_OP_WRITE) + ENUM_ID_TO_STR_CASE(PERF_COUNT_HW_CACHE_OP_PREFETCH) + default: + return NULL; + } +} + +static const char *stringify_perf_hw_cache_op_result_id(u64 value) +{ + switch (value) { + ENUM_ID_TO_STR_CASE(PERF_COUNT_HW_CACHE_RESULT_ACCESS) + ENUM_ID_TO_STR_CASE(PERF_COUNT_HW_CACHE_RESULT_MISS) + default: + return NULL; + } +} + +static const char *stringify_perf_sw_id(u64 value) +{ + switch (value) { + ENUM_ID_TO_STR_CASE(PERF_COUNT_SW_CPU_CLOCK) + ENUM_ID_TO_STR_CASE(PERF_COUNT_SW_TASK_CLOCK) + ENUM_ID_TO_STR_CASE(PERF_COUNT_SW_PAGE_FAULTS) + ENUM_ID_TO_STR_CASE(PERF_COUNT_SW_CONTEXT_SWITCHES) + ENUM_ID_TO_STR_CASE(PERF_COUNT_SW_CPU_MIGRATIONS) + ENUM_ID_TO_STR_CASE(PERF_COUNT_SW_PAGE_FAULTS_MIN) + ENUM_ID_TO_STR_CASE(PERF_COUNT_SW_PAGE_FAULTS_MAJ) + ENUM_ID_TO_STR_CASE(PERF_COUNT_SW_ALIGNMENT_FAULTS) + ENUM_ID_TO_STR_CASE(PERF_COUNT_SW_EMULATION_FAULTS) + ENUM_ID_TO_STR_CASE(PERF_COUNT_SW_DUMMY) + ENUM_ID_TO_STR_CASE(PERF_COUNT_SW_BPF_OUTPUT) + ENUM_ID_TO_STR_CASE(PERF_COUNT_SW_CGROUP_SWITCHES) + default: + return NULL; + } +} +#undef ENUM_ID_TO_STR_CASE + +#define PRINT_ID(_s, _f) \ +do { \ + const char *__s = _s; \ + if (__s == NULL) \ + snprintf(buf, size, _f, value); \ + else \ + snprintf(buf, size, _f" (%s)", value, __s); \ +} while (0) +#define print_id_unsigned(_s) PRINT_ID(_s, "%"PRIu64) +#define print_id_hex(_s) PRINT_ID(_s, "%#"PRIx64) + +static void __p_type_id(char *buf, size_t size, u64 value) +{ + print_id_unsigned(stringify_perf_type_id(value)); +} + +static void __p_config_hw_id(char *buf, size_t size, u64 value) +{ + print_id_hex(stringify_perf_hw_id(value)); +} + +static void __p_config_sw_id(char *buf, size_t size, u64 value) +{ + print_id_hex(stringify_perf_sw_id(value)); +} + +static void __p_config_hw_cache_id(char *buf, size_t size, u64 value) +{ + const char *hw_cache_str = stringify_perf_hw_cache_id(value & 0xff); + const char *hw_cache_op_str = + stringify_perf_hw_cache_op_id((value & 0xff00) >> 8); + const char *hw_cache_op_result_str = + stringify_perf_hw_cache_op_result_id((value & 0xff0000) >> 16); + + if (hw_cache_str == NULL || hw_cache_op_str == NULL || + hw_cache_op_result_str == NULL) { + snprintf(buf, size, "%#"PRIx64, value); + } else { + snprintf(buf, size, "%#"PRIx64" (%s | %s | %s)", value, + hw_cache_op_result_str, hw_cache_op_str, hw_cache_str); + } +} + +#ifdef HAVE_LIBTRACEEVENT +static void __p_config_tracepoint_id(char *buf, size_t size, u64 value) +{ + char *str = tracepoint_id_to_name(value); + + print_id_hex(str); + free(str); +} +#endif + +static void __p_config_id(char *buf, size_t size, u32 type, u64 value) +{ + switch (type) { + case PERF_TYPE_HARDWARE: + return __p_config_hw_id(buf, size, value); + case PERF_TYPE_SOFTWARE: + return __p_config_sw_id(buf, size, value); + case PERF_TYPE_HW_CACHE: + return __p_config_hw_cache_id(buf, size, value); + case PERF_TYPE_TRACEPOINT: +#ifdef HAVE_LIBTRACEEVENT + return __p_config_tracepoint_id(buf, size, value); +#endif + case PERF_TYPE_RAW: + case PERF_TYPE_BREAKPOINT: + default: + snprintf(buf, size, "%#"PRIx64, value); + return; + } +} + #define BUF_SIZE 1024 #define p_hex(val) snprintf(buf, BUF_SIZE, "%#"PRIx64, (uint64_t)(val)) @@ -79,16 +245,18 @@ static void __p_read_format(char *buf, size_t size, u64 value) #define p_sample_type(val) __p_sample_type(buf, BUF_SIZE, val) #define p_branch_sample_type(val) __p_branch_sample_type(buf, BUF_SIZE, val) #define p_read_format(val) __p_read_format(buf, BUF_SIZE, val) +#define p_type_id(val) __p_type_id(buf, BUF_SIZE, val) +#define p_config_id(val) __p_config_id(buf, BUF_SIZE, attr->type, val) -#define PRINT_ATTRn(_n, _f, _p) \ +#define PRINT_ATTRn(_n, _f, _p, _a) \ do { \ - if (attr->_f) { \ + if (_a || attr->_f) { \ _p(attr->_f); \ ret += attr__fprintf(fp, _n, buf, priv);\ } \ } while (0) -#define PRINT_ATTRf(_f, _p) PRINT_ATTRn(#_f, _f, _p) +#define PRINT_ATTRf(_f, _p) PRINT_ATTRn(#_f, _f, _p, false) int perf_event_attr__fprintf(FILE *fp, struct perf_event_attr *attr, attr__fprintf_f attr__fprintf, void *priv) @@ -96,10 +264,10 @@ int perf_event_attr__fprintf(FILE *fp, struct perf_event_attr *attr, char buf[BUF_SIZE]; int ret = 0; - PRINT_ATTRf(type, p_unsigned); + PRINT_ATTRn("type", type, p_type_id, true); PRINT_ATTRf(size, p_unsigned); - PRINT_ATTRf(config, p_hex); - PRINT_ATTRn("{ sample_period, sample_freq }", sample_period, p_unsigned); + PRINT_ATTRn("config", config, p_config_id, true); + PRINT_ATTRn("{ sample_period, sample_freq }", sample_period, p_unsigned, false); PRINT_ATTRf(sample_type, p_sample_type); PRINT_ATTRf(read_format, p_read_format); @@ -141,10 +309,10 @@ int perf_event_attr__fprintf(FILE *fp, struct perf_event_attr *attr, PRINT_ATTRf(remove_on_exec, p_unsigned); PRINT_ATTRf(sigtrap, p_unsigned); - PRINT_ATTRn("{ wakeup_events, wakeup_watermark }", wakeup_events, p_unsigned); + PRINT_ATTRn("{ wakeup_events, wakeup_watermark }", wakeup_events, p_unsigned, false); PRINT_ATTRf(bp_type, p_unsigned); - PRINT_ATTRn("{ bp_addr, config1 }", bp_addr, p_hex); - PRINT_ATTRn("{ bp_len, config2 }", bp_len, p_hex); + PRINT_ATTRn("{ bp_addr, config1 }", bp_addr, p_hex, false); + PRINT_ATTRn("{ bp_len, config2 }", bp_len, p_hex, false); PRINT_ATTRf(branch_sample_type, p_branch_sample_type); PRINT_ATTRf(sample_regs_user, p_hex); PRINT_ATTRf(sample_stack_user, p_unsigned); diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index 64fa568a5426..7f984a7f16ca 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c @@ -928,6 +928,31 @@ err: return NULL; } +/* Creates the PMU when sysfs scanning fails. */ +struct perf_pmu *perf_pmu__create_placeholder_core_pmu(struct list_head *core_pmus) +{ + struct perf_pmu *pmu = zalloc(sizeof(*pmu)); + + if (!pmu) + return NULL; + + pmu->name = strdup("cpu"); + if (!pmu->name) { + free(pmu); + return NULL; + } + + pmu->is_core = true; + pmu->type = PERF_TYPE_RAW; + pmu->cpus = cpu_map__online(); + + INIT_LIST_HEAD(&pmu->format); + INIT_LIST_HEAD(&pmu->aliases); + INIT_LIST_HEAD(&pmu->caps); + list_add_tail(&pmu->list, core_pmus); + return pmu; +} + void perf_pmu__warn_invalid_formats(struct perf_pmu *pmu) { struct perf_pmu_format *format; @@ -1427,7 +1452,7 @@ bool perf_pmu__supports_legacy_cache(const struct perf_pmu *pmu) bool perf_pmu__auto_merge_stats(const struct perf_pmu *pmu) { - return pmu->is_core && perf_pmus__num_core_pmus() == 1; + return !pmu->is_core || perf_pmus__num_core_pmus() == 1; } bool perf_pmu__have_event(const struct perf_pmu *pmu, const char *name) diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h index 8807a624e918..203b92860e3c 100644 --- a/tools/perf/util/pmu.h +++ b/tools/perf/util/pmu.h @@ -286,6 +286,7 @@ int perf_pmu__event_source_devices_fd(void); int perf_pmu__pathname_fd(int dirfd, const char *pmu_name, const char *filename, int flags); struct perf_pmu *perf_pmu__lookup(struct list_head *pmus, int dirfd, const char *lookup_name); +struct perf_pmu *perf_pmu__create_placeholder_core_pmu(struct list_head *core_pmus); void perf_pmu__delete(struct perf_pmu *pmu); #endif /* __PMU_H */ diff --git a/tools/perf/util/pmus.c b/tools/perf/util/pmus.c index 0866dee3fc62..3cd9de42139e 100644 --- a/tools/perf/util/pmus.c +++ b/tools/perf/util/pmus.c @@ -153,7 +153,12 @@ static void pmu_read_sysfs(bool core_only) closedir(dir); if (core_only) { - read_sysfs_core_pmus = true; + if (!list_empty(&core_pmus)) + read_sysfs_core_pmus = true; + else { + if (perf_pmu__create_placeholder_core_pmu(&core_pmus)) + read_sysfs_core_pmus = true; + } } else { read_sysfs_core_pmus = true; read_sysfs_all_pmus = true; diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 6e2110d605fb..16822a8a540f 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -74,7 +74,6 @@ int init_probe_symbol_maps(bool user_only) { int ret; - symbol_conf.sort_by_name = true; symbol_conf.allow_aliases = true; ret = symbol__init(NULL); if (ret < 0) { @@ -382,6 +381,7 @@ static int find_alternative_probe_point(struct debuginfo *dinfo, struct symbol *sym; u64 address = 0; int ret = -ENOENT; + size_t idx; /* This can work only for function-name based one */ if (!pp->function || pp->file) @@ -392,7 +392,7 @@ static int find_alternative_probe_point(struct debuginfo *dinfo, return -EINVAL; /* Find the address of given function */ - map__for_each_symbol_by_name(map, pp->function, sym) { + map__for_each_symbol_by_name(map, pp->function, sym, idx) { if (uprobes) { address = sym->start; if (sym->type == STT_GNU_IFUNC) @@ -3738,7 +3738,6 @@ out: int show_available_funcs(const char *target, struct nsinfo *nsi, struct strfilter *_filter, bool user) { - struct rb_node *nd; struct map *map; struct dso *dso; int ret; @@ -3767,17 +3766,16 @@ int show_available_funcs(const char *target, struct nsinfo *nsi, goto end; } dso = map__dso(map); - if (!dso__sorted_by_name(dso)) - dso__sort_by_name(dso); + dso__sort_by_name(dso); /* Show all (filtered) symbols */ setup_pager(); - for (nd = rb_first_cached(&dso->symbol_names); nd; nd = rb_next(nd)) { - struct symbol_name_rb_node *pos = rb_entry(nd, struct symbol_name_rb_node, rb_node); + for (size_t i = 0; i < dso->symbol_names_len; i++) { + struct symbol *pos = dso->symbol_names[i]; - if (strfilter__compare(_filter, pos->sym.name)) - printf("%s\n", pos->sym.name); + if (strfilter__compare(_filter, pos->name)) + printf("%s\n", pos->name); } end: map__put(map); diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index a7b2cb05dc86..4eed8ec23994 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -124,6 +124,14 @@ int metricgroup__copy_metric_events(struct evlist *evlist, struct cgroup *cgrp, } /* + * Add this one here not to drag util/trace-event-info.c + */ +char *tracepoint_id_to_name(u64 config) +{ + return NULL; +} + +/* * XXX: All these evsel destructors need some better mechanism, like a linked * list of destructors registered when the relevant code indeed is used instead * of having more and more calls in perf_evsel__delete(). -- acme diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index d275d3bef7d5..f849f9ef68e6 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -440,38 +440,35 @@ static struct symbol *symbols__next(struct symbol *sym) return NULL; } -static void symbols__insert_by_name(struct rb_root_cached *symbols, struct symbol *sym) +static int symbols__sort_name_cmp(const void *vlhs, const void *vrhs) { - struct rb_node **p = &symbols->rb_root.rb_node; - struct rb_node *parent = NULL; - struct symbol_name_rb_node *symn, *s; - bool leftmost = true; + const struct symbol *lhs = *((const struct symbol **)vlhs); + const struct symbol *rhs = *((const struct symbol **)vrhs); - symn = container_of(sym, struct symbol_name_rb_node, sym); - - while (*p != NULL) { - parent = *p; - s = rb_entry(parent, struct symbol_name_rb_node, rb_node); - if (strcmp(sym->name, s->sym.name) < 0) - p = &(*p)->rb_left; - else { - p = &(*p)->rb_right; - leftmost = false; - } - } - rb_link_node(&symn->rb_node, parent, p); - rb_insert_color_cached(&symn->rb_node, symbols, leftmost); + return strcmp(lhs->name, rhs->name); } -static void symbols__sort_by_name(struct rb_root_cached *symbols, - struct rb_root_cached *source) +static struct symbol **symbols__sort_by_name(struct rb_root_cached *source, size_t *len) { struct rb_node *nd; + struct symbol **result; + size_t i = 0, size = 0; + + for (nd = rb_first_cached(source); nd; nd = rb_next(nd)) + size++; + + result = malloc(sizeof(*result) * size); + if (!result) + return NULL; for (nd = rb_first_cached(source); nd; nd = rb_next(nd)) { struct symbol *pos = rb_entry(nd, struct symbol, rb_node); - symbols__insert_by_name(symbols, pos); + + result[i++] = pos; } + qsort(result, size, sizeof(*result), symbols__sort_name_cmp); + *len = size; + return result; } int symbol__match_symbol_name(const char *name, const char *str, @@ -491,48 +488,53 @@ int symbol__match_symbol_name(const char *name, const char *str, return arch__compare_symbol_names(name, str); } -static struct symbol *symbols__find_by_name(struct rb_root_cached *symbols, +static struct symbol *symbols__find_by_name(struct symbol *symbols[], + size_t symbols_len, const char *name, - enum symbol_tag_include includes) + enum symbol_tag_include includes, + size_t *found_idx) { - struct rb_node *n; - struct symbol_name_rb_node *s = NULL; + size_t i, lower = 0, upper = symbols_len; + struct symbol *s = NULL; - if (symbols == NULL) - return NULL; + if (found_idx) + *found_idx = SIZE_MAX; - n = symbols->rb_root.rb_node; + if (!symbols_len) + return NULL; - while (n) { + while (lower < upper) { int cmp; - s = rb_entry(n, struct symbol_name_rb_node, rb_node); - cmp = symbol__match_symbol_name(s->sym.name, name, includes); + i = (lower + upper) / 2; + cmp = symbol__match_symbol_name(symbols[i]->name, name, includes); if (cmp > 0) - n = n->rb_left; + upper = i; else if (cmp < 0) - n = n->rb_right; - else + lower = i + 1; + else { + if (found_idx) + *found_idx = i; + s = symbols[i]; break; + } } - - if (n == NULL) - return NULL; - - if (includes != SYMBOL_TAG_INCLUDE__DEFAULT_ONLY) + if (s && includes != SYMBOL_TAG_INCLUDE__DEFAULT_ONLY) { /* return first symbol that has same name (if any) */ - for (n = rb_prev(n); n; n = rb_prev(n)) { - struct symbol_name_rb_node *tmp; - - tmp = rb_entry(n, struct symbol_name_rb_node, rb_node); - if (arch__compare_symbol_names(tmp->sym.name, s->sym.name)) + for (; i > 0; i--) { + struct symbol *tmp = symbols[i - 1]; + + if (!arch__compare_symbol_names(tmp->name, s->name)) { + if (found_idx) + *found_idx = i - 1; + s = tmp; + } else break; - - s = tmp; } - - return &s->sym; + } + assert(!found_idx || !s || s == symbols[*found_idx]); + return s; } void dso__reset_find_symbol_cache(struct dso *dso) @@ -590,31 +592,41 @@ struct symbol *dso__next_symbol(struct symbol *sym) return symbols__next(sym); } -struct symbol *symbol__next_by_name(struct symbol *sym) +struct symbol *dso__next_symbol_by_name(struct dso *dso, size_t *idx) { - struct symbol_name_rb_node *s = container_of(sym, struct symbol_name_rb_node, sym); - struct rb_node *n = rb_next(&s->rb_node); + if (*idx + 1 >= dso->symbol_names_len) + return NULL; - return n ? &rb_entry(n, struct symbol_name_rb_node, rb_node)->sym : NULL; + ++*idx; + return dso->symbol_names[*idx]; } /* * Returns first symbol that matched with @name. */ -struct symbol *dso__find_symbol_by_name(struct dso *dso, const char *name) +struct symbol *dso__find_symbol_by_name(struct dso *dso, const char *name, size_t *idx) { - struct symbol *s = symbols__find_by_name(&dso->symbol_names, name, - SYMBOL_TAG_INCLUDE__NONE); + struct symbol *s = symbols__find_by_name(dso->symbol_names, dso->symbol_names_len, + name, SYMBOL_TAG_INCLUDE__NONE, idx); if (!s) - s = symbols__find_by_name(&dso->symbol_names, name, - SYMBOL_TAG_INCLUDE__DEFAULT_ONLY); + s = symbols__find_by_name(dso->symbol_names, dso->symbol_names_len, + name, SYMBOL_TAG_INCLUDE__DEFAULT_ONLY, idx); return s; } void dso__sort_by_name(struct dso *dso) { - dso__set_sorted_by_name(dso); - return symbols__sort_by_name(&dso->symbol_names, &dso->symbols); + mutex_lock(&dso->lock); + if (!dso__sorted_by_name(dso)) { + size_t len; + + dso->symbol_names = symbols__sort_by_name(&dso->symbols, &len); + if (dso->symbol_names) { + dso->symbol_names_len = len; + dso__set_sorted_by_name(dso); + } + } + mutex_unlock(&dso->lock); } /* @@ -2656,10 +2668,6 @@ int symbol__init(struct perf_env *env) symbol__elf_init(); - if (symbol_conf.sort_by_name) - symbol_conf.priv_size += (sizeof(struct symbol_name_rb_node) - - sizeof(struct symbol)); - if (symbol_conf.try_vmlinux_path && vmlinux_path__init(env) < 0) return -1; diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 5ca8665dd2c1..af87c46b3f89 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -43,8 +43,7 @@ Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep, /** * A symtab entry. When allocated this may be preceded by an annotation (see - * symbol__annotation), a browser_index (see symbol__browser_index) and rb_node - * to sort by name (see struct symbol_name_rb_node). + * symbol__annotation) and/or a browser_index (see symbol__browser_index). */ struct symbol { struct rb_node rb_node; @@ -95,11 +94,6 @@ static inline size_t symbol__size(const struct symbol *sym) struct strlist; struct intlist; -struct symbol_name_rb_node { - struct rb_node rb_node; - struct symbol sym; -}; - static inline int __symbol__join_symfs(char *bf, size_t size, const char *path) { return path__join(bf, size, symbol_conf.symfs, path); @@ -136,9 +130,9 @@ void dso__delete_symbol(struct dso *dso, struct symbol *dso__find_symbol(struct dso *dso, u64 addr); struct symbol *dso__find_symbol_nocache(struct dso *dso, u64 addr); -struct symbol *dso__find_symbol_by_name(struct dso *dso, const char *name); -struct symbol *symbol__next_by_name(struct symbol *sym); +struct symbol *dso__next_symbol_by_name(struct dso *dso, size_t *idx); +struct symbol *dso__find_symbol_by_name(struct dso *dso, const char *name, size_t *idx); struct symbol *dso__first_symbol(struct dso *dso); struct symbol *dso__last_symbol(struct dso *dso); diff --git a/tools/perf/util/symbol_conf.h b/tools/perf/util/symbol_conf.h index f26f81eb8252..0b589570d1d0 100644 --- a/tools/perf/util/symbol_conf.h +++ b/tools/perf/util/symbol_conf.h @@ -18,7 +18,6 @@ struct symbol_conf { show_kernel_path, use_modules, allow_aliases, - sort_by_name, show_nr_samples, show_total_period, use_callchain, diff --git a/tools/perf/util/symbol_fprintf.c b/tools/perf/util/symbol_fprintf.c index d9e5ad040b6a..088f4abf230f 100644 --- a/tools/perf/util/symbol_fprintf.c +++ b/tools/perf/util/symbol_fprintf.c @@ -63,13 +63,11 @@ size_t dso__fprintf_symbols_by_name(struct dso *dso, FILE *fp) { size_t ret = 0; - struct rb_node *nd; - struct symbol_name_rb_node *pos; - for (nd = rb_first_cached(&dso->symbol_names); nd; nd = rb_next(nd)) { - pos = rb_entry(nd, struct symbol_name_rb_node, rb_node); - ret += fprintf(fp, "%s\n", pos->sym.name); - } + for (size_t i = 0; i < dso->symbol_names_len; i++) { + struct symbol *pos = dso->symbol_names[i]; + ret += fprintf(fp, "%s\n", pos->name); + } return ret; } diff --git a/tools/perf/util/trace-event-info.c b/tools/perf/util/trace-event-info.c index c24b3a15e319..319ccf09a435 100644 --- a/tools/perf/util/trace-event-info.c +++ b/tools/perf/util/trace-event-info.c @@ -466,6 +466,18 @@ next: return NULL; } +char *tracepoint_id_to_name(u64 config) +{ + struct tracepoint_path *path = tracepoint_id_to_path(config); + char *buf = NULL; + + if (path && asprintf(&buf, "%s:%s", path->system, path->name) < 0) + buf = NULL; + + put_tracepoints_path(path); + return buf; +} + static struct tracepoint_path *tracepoint_name_to_path(const char *name) { struct tracepoint_path *path = zalloc(sizeof(*path)); diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h index a0cff184b1cd..a69ee29419f3 100644 --- a/tools/perf/util/trace-event.h +++ b/tools/perf/util/trace-event.h @@ -62,6 +62,12 @@ unsigned long long eval_flag(const char *flag); int read_tracing_data(int fd, struct list_head *pattrs); +/* + * Return the tracepoint name in the format "subsystem:event_name", + * callers should free the returned string. + */ +char *tracepoint_id_to_name(u64 config); + struct tracing_data { /* size is only valid if temp is 'true' */ ssize_t size; diff --git a/tools/perf/util/unwind-libdw.c b/tools/perf/util/unwind-libdw.c index 83eea968482e..2a96df4c8d42 100644 --- a/tools/perf/util/unwind-libdw.c +++ b/tools/perf/util/unwind-libdw.c @@ -66,9 +66,13 @@ static int __report_module(struct addr_location *al, u64 ip, mod = 0; } - if (!mod) - mod = dwfl_report_elf(ui->dwfl, dso->short_name, dso->long_name, -1, + if (!mod) { + char filename[PATH_MAX]; + + __symbol__join_symfs(filename, sizeof(filename), dso->long_name); + mod = dwfl_report_elf(ui->dwfl, dso->short_name, filename, -1, map__start(al->map) - map__pgoff(al->map), false); + } if (!mod) { char filename[PATH_MAX]; |