* [PATCH v1 0/7] perf symbol: Reference counting, flat array storage, and LRU shrinking
@ 2026-09-28 7:52 Ian Rogers
2026-09-28 7:52 ` [PATCH v1 1/7] perf symbol: Add accessor functions for struct symbol fields Ian Rogers
` (9 more replies)
0 siblings, 10 replies; 36+ messages in thread
From: Ian Rogers @ 2026-09-28 7:52 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
linux-perf-users, linux-kernel, Alireza Haghdoost
This series modernizes struct symbol memory management and backing storage in
tools/perf to support reference counting (DECLARE_RC_STRUCT(symbol)) and
periodic LRU memory shrinking during long or large profiling sessions:
1. Field accessors (Patch 1): Introduces inline getters/setters for
struct symbol fields across tools/perf in preparation for wrapping
struct symbol with DECLARE_RC_STRUCT(symbol).
2. Remove negative-offset private allocations (Patch 2): Replaces
symbol_conf.priv_size and symbol__priv() negative-offset allocations in
util/annotate.c and util/db-export.c with hashmaps keyed by symbol
pointer (struct annotated_symbol protected by an rw_semaphore, and
sym_db_ids on struct db_export).
3. Flat sorted array backing storage (Patch 3): Replaces the dual
address-sorted and name-sorted red-black trees (rb_node) in struct dso
with a struct symbols container backed by dynamically sorted flat arrays
protected by struct rw_semaphore (util/rwsem.h) with Clang thread-safety
annotations. Saves 48 bytes per struct symbol (two rb_nodes) and provides
cache-friendly binary search (bsearch) and callback-based iteration.
4. Reference counting and DECLARE_RC_STRUCT(symbol) (Patch 4): Wraps
struct symbol with DECLARE_RC_STRUCT(symbol) and adds refcount_t refcnt
along with symbol__get(), symbol__put(), and symbol__zput(). Lookup
functions return a counted reference acquired under symbols->lock, and
lifetime holders (addr_location, map_symbol, hist_entry, call_path,
block_info, disasm_line, thread_stack, db_export, etc.) properly
manage symbol references.
5. LRU memory shrinking (Patch 5): Adds an accessed bit to struct dso
and implements symbols__shrink(), dso__shrink_symbols(), dsos__shrink(),
machine__shrink(), and machines__shrink(). Unreferenced symbols (refcnt == 1)
in cold DSOs are reclaimed, and shrunken DSOs transparently reload on demand
via dso__load() while preserving live symbols and their RC_STRUCT(symbol)
identity.
6. Periodic session shrinking (Patch 6): Hooks machines__shrink() into
perf_session event processing so long-running sessions periodically reclaim
cold symbols and DSOs.
7. Unit tests (Patch 7): Adds unit tests in tools/perf/tests/symbols.c
covering symbol reference counting, symbols__shrink() retention of active
symbols, dsos__shrink() two-pass LRU clock behavior, and on-demand reload
preserving live symbol identity.
Ian Rogers (7):
perf symbol: Add accessor functions for struct symbol fields
perf symbol: Remove symbol_conf.priv_size and negative-offset
allocations
perf symbol: Switch backing storage from rbtree to struct symbols
array
perf symbol: Add reference counting and DECLARE_RC_STRUCT(symbol)
perf symbol: Add LRU memory shrinking for symbols, DSOs, and machines
perf session: Periodically shrink symbols and DSOs during event
processing
perf test symbols: Add tests for symbol and DSO LRU shrinking
.../arch/powerpc/util/skip-callchain-idx.c | 2 +-
tools/perf/arch/powerpc/util/sym-handling.c | 40 +-
tools/perf/builtin-annotate.c | 15 +-
tools/perf/builtin-c2c.c | 14 -
tools/perf/builtin-diff.c | 5 +-
tools/perf/builtin-kallsyms.c | 10 +-
tools/perf/builtin-kmem.c | 66 +-
tools/perf/builtin-kwork.c | 4 +-
tools/perf/builtin-lock.c | 27 +-
tools/perf/builtin-mem.c | 2 +-
tools/perf/builtin-report.c | 14 -
tools/perf/builtin-sched.c | 6 +-
tools/perf/builtin-script.c | 18 +-
tools/perf/builtin-timechart.c | 2 +-
tools/perf/builtin-top.c | 29 +-
tools/perf/builtin-trace.c | 12 +-
tools/perf/tests/builtin-test.c | 1 -
tools/perf/tests/dlfilter-test.c | 12 +-
tools/perf/tests/dwarf-unwind.c | 2 +-
tools/perf/tests/hists_common.c | 4 +-
tools/perf/tests/hists_cumulate.c | 8 +-
tools/perf/tests/hists_filter.c | 10 +-
tools/perf/tests/hists_link.c | 16 +-
tools/perf/tests/hists_output.c | 9 +-
tools/perf/tests/kallsyms-split.c | 5 +-
tools/perf/tests/pe-file-parsing.c | 1 +
tools/perf/tests/symbols.c | 289 +++++-
tools/perf/tests/vmlinux-kallsyms.c | 191 ++--
tools/perf/ui/browsers/annotate.c | 11 +-
tools/perf/ui/browsers/hists.c | 17 +-
tools/perf/ui/browsers/map.c | 87 +-
tools/perf/ui/gtk/annotate.c | 7 +-
tools/perf/ui/stdio/hist.c | 6 +-
tools/perf/util/addr_location.c | 4 +
.../perf/util/annotate-arch/annotate-alpha.c | 2 +-
.../util/annotate-arch/annotate-loongarch.c | 8 +-
tools/perf/util/annotate-arch/annotate-s390.c | 2 +-
tools/perf/util/annotate-arch/annotate-x86.c | 14 +-
tools/perf/util/annotate-data.c | 14 +-
tools/perf/util/annotate.c | 211 ++++-
tools/perf/util/annotate.h | 6 +-
tools/perf/util/auxtrace.c | 111 ++-
tools/perf/util/block-info.c | 13 +-
tools/perf/util/bpf_lock_contention.c | 39 +-
tools/perf/util/c2c-function.c | 25 +-
tools/perf/util/call-path.c | 15 +-
tools/perf/util/callchain.c | 19 +-
tools/perf/util/capstone.c | 21 +-
tools/perf/util/data-convert-json.c | 4 +-
tools/perf/util/db-export.c | 76 +-
tools/perf/util/db-export.h | 6 +
tools/perf/util/debug.c | 3 +-
tools/perf/util/disasm.c | 57 +-
tools/perf/util/dlfilter.c | 12 +-
tools/perf/util/dso.c | 236 +++--
tools/perf/util/dso.h | 85 +-
tools/perf/util/dsos.c | 100 +++
tools/perf/util/dsos.h | 12 +-
tools/perf/util/event.c | 12 +-
tools/perf/util/evsel_fprintf.c | 2 +-
tools/perf/util/hist.c | 30 +-
tools/perf/util/intel-pt.c | 74 +-
tools/perf/util/libbfd.c | 5 +-
tools/perf/util/libdw.c | 5 +-
tools/perf/util/llvm.c | 6 +-
tools/perf/util/lock-contention.c | 8 +-
tools/perf/util/machine.c | 143 ++-
tools/perf/util/machine.h | 2 +
tools/perf/util/map.c | 53 +-
tools/perf/util/map.h | 18 +-
tools/perf/util/map_symbol.c | 4 +-
tools/perf/util/maps.c | 3 +-
tools/perf/util/probe-event.c | 135 +--
tools/perf/util/python.c | 20 +-
.../util/scripting-engines/trace-event-perl.c | 8 +-
.../scripting-engines/trace-event-python.c | 38 +-
tools/perf/util/session.c | 31 +
tools/perf/util/session.h | 13 +
tools/perf/util/sort.c | 44 +-
tools/perf/util/srcline.c | 19 +-
tools/perf/util/symbol-elf.c | 22 +-
tools/perf/util/symbol.c | 845 ++++++++++++------
tools/perf/util/symbol.h | 203 ++++-
tools/perf/util/symbol_conf.h | 1 -
tools/perf/util/symbol_fprintf.c | 14 +-
tools/perf/util/thread-stack.c | 18 +-
tools/perf/util/threads.c | 17 +-
tools/perf/util/unwind-libdw.c | 4 +-
tools/perf/util/unwind-libunwind.c | 2 +-
89 files changed, 2644 insertions(+), 1192 deletions(-)
--
2.56.0.rc1.315.gc6ed9934b7-goog
^ permalink raw reply [flat|nested] 36+ messages in thread
* [PATCH v1 1/7] perf symbol: Add accessor functions for struct symbol fields
2026-09-28 7:52 [PATCH v1 0/7] perf symbol: Reference counting, flat array storage, and LRU shrinking Ian Rogers
@ 2026-09-28 7:52 ` Ian Rogers
2026-09-28 7:52 ` [PATCH v1 2/7] perf symbol: Remove symbol_conf.priv_size and negative-offset allocations Ian Rogers
` (8 subsequent siblings)
9 siblings, 0 replies; 36+ messages in thread
From: Ian Rogers @ 2026-09-28 7:52 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
linux-perf-users, linux-kernel, Alireza Haghdoost
To prepare for adding reference count checking (DECLARE_RC_STRUCT) to
struct symbol, add inline accessor functions for struct symbol fields
(symbol__start, symbol__set_start, symbol__end, symbol__set_end,
symbol__name, symbol__namelen, symbol__arch_sym, symbol__set_arch_sym)
and convert direct member accesses across tools/perf.
Assisted-by: Antigravity:gemini-3.1-pro
Signed-off-by: Ian Rogers <irogers@google.com>
---
.../arch/powerpc/util/skip-callchain-idx.c | 2 +-
tools/perf/arch/powerpc/util/sym-handling.c | 8 +-
tools/perf/builtin-annotate.c | 4 +-
tools/perf/builtin-diff.c | 5 +-
tools/perf/builtin-kallsyms.c | 8 +-
tools/perf/builtin-kmem.c | 25 ++--
tools/perf/builtin-kwork.c | 4 +-
tools/perf/builtin-lock.c | 12 +-
tools/perf/builtin-mem.c | 2 +-
tools/perf/builtin-sched.c | 6 +-
tools/perf/builtin-script.c | 14 +--
tools/perf/builtin-timechart.c | 2 +-
tools/perf/builtin-top.c | 25 ++--
tools/perf/builtin-trace.c | 11 +-
tools/perf/tests/dlfilter-test.c | 4 +-
tools/perf/tests/dwarf-unwind.c | 2 +-
tools/perf/tests/hists_common.c | 4 +-
tools/perf/tests/hists_cumulate.c | 4 +-
tools/perf/tests/hists_output.c | 2 +-
tools/perf/tests/symbols.c | 4 +-
tools/perf/tests/vmlinux-kallsyms.c | 34 +++---
tools/perf/ui/browsers/annotate.c | 11 +-
tools/perf/ui/browsers/hists.c | 10 +-
tools/perf/ui/browsers/map.c | 9 +-
tools/perf/ui/gtk/annotate.c | 7 +-
tools/perf/ui/stdio/hist.c | 3 +-
.../util/annotate-arch/annotate-loongarch.c | 4 +-
tools/perf/util/annotate-arch/annotate-x86.c | 14 +--
tools/perf/util/annotate-data.c | 14 ++-
tools/perf/util/annotate.c | 73 +++++++-----
tools/perf/util/auxtrace.c | 14 +--
tools/perf/util/block-info.c | 7 +-
tools/perf/util/bpf_lock_contention.c | 20 ++--
tools/perf/util/c2c-function.c | 12 +-
tools/perf/util/callchain.c | 15 ++-
tools/perf/util/capstone.c | 20 ++--
tools/perf/util/data-convert-json.c | 4 +-
tools/perf/util/db-export.c | 2 +-
tools/perf/util/debug.c | 3 +-
tools/perf/util/disasm.c | 45 ++++----
tools/perf/util/dlfilter.c | 12 +-
tools/perf/util/dso.c | 4 +-
tools/perf/util/event.c | 4 +-
tools/perf/util/evsel_fprintf.c | 2 +-
tools/perf/util/hist.c | 17 +--
tools/perf/util/intel-pt.c | 8 +-
tools/perf/util/libbfd.c | 5 +-
tools/perf/util/llvm.c | 6 +-
tools/perf/util/lock-contention.c | 2 +-
tools/perf/util/machine.c | 34 +++---
tools/perf/util/map.c | 6 +-
tools/perf/util/map.h | 2 +-
tools/perf/util/probe-event.c | 40 +++----
tools/perf/util/python.c | 8 +-
.../util/scripting-engines/trace-event-perl.c | 8 +-
.../scripting-engines/trace-event-python.c | 28 ++---
tools/perf/util/sort.c | 42 +++----
tools/perf/util/srcline.c | 13 ++-
tools/perf/util/symbol-elf.c | 11 +-
tools/perf/util/symbol.c | 107 ++++++++++--------
tools/perf/util/symbol.h | 42 ++++++-
tools/perf/util/symbol_fprintf.c | 14 +--
tools/perf/util/thread-stack.c | 8 +-
tools/perf/util/unwind-libdw.c | 2 +-
64 files changed, 504 insertions(+), 390 deletions(-)
diff --git a/tools/perf/arch/powerpc/util/skip-callchain-idx.c b/tools/perf/arch/powerpc/util/skip-callchain-idx.c
index 472714cfad38..ee2dea01c4ea 100644
--- a/tools/perf/arch/powerpc/util/skip-callchain-idx.c
+++ b/tools/perf/arch/powerpc/util/skip-callchain-idx.c
@@ -239,7 +239,7 @@ int arch_skip_callchain_idx(struct thread *thread, struct ip_callchain *chain)
rc = check_return_addr(dso, map__map_ip(al.map, ip));
pr_debug("[DSO %s, sym %s, ip 0x%" PRIx64 "] rc %d\n",
- dso__long_name(dso), al.sym->name, ip, rc);
+ dso__long_name(dso), symbol__name(al.sym), ip, rc);
if (rc == 0) {
/*
diff --git a/tools/perf/arch/powerpc/util/sym-handling.c b/tools/perf/arch/powerpc/util/sym-handling.c
index 947bfad7aa59..d73bfdcdbec1 100644
--- a/tools/perf/arch/powerpc/util/sym-handling.c
+++ b/tools/perf/arch/powerpc/util/sym-handling.c
@@ -13,7 +13,7 @@
int arch__choose_best_symbol(struct symbol *syma,
struct symbol *symb __maybe_unused)
{
- char *sym = syma->name;
+ const char *sym = symbol__name(syma);
#if !defined(_CALL_ELF) || _CALL_ELF != 2
/* Skip over any initial dot */
@@ -69,7 +69,7 @@ const char *arch__normalize_symbol_name(const char *name)
#ifdef HAVE_LIBELF_SUPPORT
void arch__sym_update(struct symbol *s, GElf_Sym *sym)
{
- s->arch_sym = sym->st_other;
+ symbol__set_arch_sym(s, sym->st_other);
}
#endif
@@ -102,7 +102,7 @@ void arch__fix_tev_from_maps(struct perf_probe_event *pev,
return;
}
- lep_offset = PPC64_LOCAL_ENTRY_OFFSET(sym->arch_sym);
+ lep_offset = PPC64_LOCAL_ENTRY_OFFSET(symbol__arch_sym(sym));
if (map__dso(map)->symtab_type == DSO_BINARY_TYPE__KALLSYMS)
tev->point.offset += PPC64LE_LEP_OFFSET;
@@ -131,7 +131,7 @@ void arch__post_process_probe_trace_events(struct perf_probe_event *pev,
for (i = 0; i < ntevs; i++) {
tev = &pev->tevs[i];
map__for_each_symbol(map, sym, tmp) {
- if (map__unmap_ip(map, sym->start) == tev->point.address) {
+ if (map__unmap_ip(map, symbol__start(sym)) == tev->point.address) {
arch__fix_tev_from_maps(pev, tev, map, sym);
break;
}
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c
index 8863f3a570f6..c93b67986334 100644
--- a/tools/perf/builtin-annotate.c
+++ b/tools/perf/builtin-annotate.c
@@ -252,7 +252,7 @@ static int add_sample(struct perf_sample *sample,
if ((!ann->has_br_stack || !has_annotation(ann)) &&
ann->sym_hist_filter != NULL &&
(al->sym == NULL ||
- strcmp(ann->sym_hist_filter, al->sym->name) != 0)) {
+ strcmp(ann->sym_hist_filter, symbol__name(al->sym)) != 0)) {
/* We're only interested in a symbol named sym_hist_filter */
/*
* FIXME: why isn't this done in the symbol_filter when loading
@@ -423,7 +423,7 @@ static void hists__find_annotations(struct hists *hists,
goto find_next;
if (ann->sym_hist_filter &&
- (strcmp(he->ms.sym->name, ann->sym_hist_filter) != 0))
+ (strcmp(symbol__name(he->ms.sym), ann->sym_hist_filter) != 0))
goto find_next;
if (ann->min_percent) {
diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c
index 9fa8e900637b..17decf1e19ff 100644
--- a/tools/perf/builtin-diff.c
+++ b/tools/perf/builtin-diff.c
@@ -1358,10 +1358,11 @@ static int cycles_printf(struct hist_entry *he, struct hist_entry *pair,
bi = block_he->block_info;
- start_line = map__srcline(he->ms.map, bi->sym->start + bi->start,
+ start_line = map__srcline(he->ms.map,
+ symbol__start(bi->sym) + bi->start,
he->ms.sym);
- end_line = map__srcline(he->ms.map, bi->sym->start + bi->end,
+ end_line = map__srcline(he->ms.map, symbol__start(bi->sym) + bi->end,
he->ms.sym);
if (start_line != SRCLINE_UNKNOWN &&
diff --git a/tools/perf/builtin-kallsyms.c b/tools/perf/builtin-kallsyms.c
index 3c4339982b16..eb62bb4ea602 100644
--- a/tools/perf/builtin-kallsyms.c
+++ b/tools/perf/builtin-kallsyms.c
@@ -48,9 +48,11 @@ static int __cmd_kallsyms(int argc, const char **argv)
dso = map__dso(map);
printf("%s: %s %s %#" PRIx64 "-%#" PRIx64 " (%#" PRIx64 "-%#" PRIx64")\n",
- symbol->name, dso__short_name(dso), dso__long_name(dso),
- map__unmap_ip(map, symbol->start), map__unmap_ip(map, symbol->end),
- symbol->start, symbol->end);
+ symbol__name(symbol), dso__short_name(dso),
+ dso__long_name(dso),
+ map__unmap_ip(map, symbol__start(symbol)),
+ map__unmap_ip(map, symbol__end(symbol)),
+ symbol__start(symbol), symbol__end(symbol));
}
out:
machine__delete(machine);
diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c
index 8693c6b135ca..d1883086ed58 100644
--- a/tools/perf/builtin-kmem.c
+++ b/tools/perf/builtin-kmem.c
@@ -310,7 +310,7 @@ static struct rb_root page_caller_sorted;
struct alloc_func {
u64 start;
u64 end;
- char *name;
+ const char *name;
};
static int nr_alloc_funcs;
@@ -368,7 +368,7 @@ static int build_alloc_func_list(void)
}
map__for_each_symbol(kernel_map, sym, node) {
- if (regexec(&alloc_func_regex, sym->name, 0, NULL, 0))
+ if (regexec(&alloc_func_regex, symbol__name(sym), 0, NULL, 0))
continue;
func = realloc(alloc_func_list,
@@ -376,10 +376,10 @@ static int build_alloc_func_list(void)
if (func == NULL)
return -ENOMEM;
- pr_debug("alloc func: %s\n", sym->name);
- func[nr_alloc_funcs].start = sym->start;
- func[nr_alloc_funcs].end = sym->end;
- func[nr_alloc_funcs].name = sym->name;
+ pr_debug("alloc func: %s\n", symbol__name(sym));
+ func[nr_alloc_funcs].start = symbol__start(sym);
+ func[nr_alloc_funcs].end = symbol__end(sym);
+ func[nr_alloc_funcs].name = symbol__name(sym);
alloc_func_list = func;
nr_alloc_funcs++;
@@ -1049,8 +1049,9 @@ static void __print_slab_result(struct rb_root *root,
addr = data->ptr;
if (sym != NULL)
- snprintf(buf, sizeof(buf), "%s+%" PRIx64 "", sym->name,
- addr - map__unmap_ip(map, sym->start));
+ snprintf(buf, sizeof(buf), "%s+%" PRIx64 "",
+ symbol__name(sym),
+ addr - map__unmap_ip(map, symbol__start(sym)));
else
snprintf(buf, sizeof(buf), "%#" PRIx64 "", addr);
printf(" %-34s |", buf);
@@ -1105,12 +1106,12 @@ static void __print_page_alloc_result(struct perf_session *session, int n_lines)
struct symbol *sym;
struct map *map;
char buf[32];
- char *caller = buf;
+ const char *caller = buf;
data = rb_entry(next, struct page_stat, node);
sym = machine__find_kernel_symbol(machine, data->callsite, &map);
if (sym)
- caller = sym->name;
+ caller = symbol__name(sym);
else
scnprintf(buf, sizeof(buf), "%"PRIx64, data->callsite);
@@ -1147,12 +1148,12 @@ static void __print_page_caller_result(struct perf_session *session, int n_lines
struct symbol *sym;
struct map *map;
char buf[32];
- char *caller = buf;
+ const char *caller = buf;
data = rb_entry(next, struct page_stat, node);
sym = machine__find_kernel_symbol(machine, data->callsite, &map);
if (sym)
- caller = sym->name;
+ caller = symbol__name(sym);
else
scnprintf(buf, sizeof(buf), "%"PRIx64, data->callsite);
diff --git a/tools/perf/builtin-kwork.c b/tools/perf/builtin-kwork.c
index fce588441e49..98714f1ed949 100644
--- a/tools/perf/builtin-kwork.c
+++ b/tools/perf/builtin-kwork.c
@@ -768,8 +768,8 @@ static void timehist_save_callchain(struct perf_kwork *kwork,
sym = node->ms.sym;
if (sym) {
- if (!strcmp(sym->name, "__softirqentry_text_start") ||
- !strcmp(sym->name, "__do_softirq"))
+ if (!strcmp(symbol__name(sym), "__softirqentry_text_start") ||
+ !strcmp(symbol__name(sym), "__do_softirq"))
symbol__set_ignore(sym, true);
}
diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c
index d5c0d55cb82d..cd060c984068 100644
--- a/tools/perf/builtin-lock.c
+++ b/tools/perf/builtin-lock.c
@@ -826,12 +826,13 @@ static int get_symbol_name_offset(struct map *map, struct symbol *sym, u64 ip,
return 0;
}
- offset = map__map_ip(map, ip) - sym->start;
+ offset = map__map_ip(map, ip) - symbol__start(sym);
if (offset)
- return scnprintf(buf, size, "%s+%#lx", sym->name, offset);
+ return scnprintf(buf, size, "%s+%#lx", symbol__name(sym),
+ offset);
else
- return strlcpy(buf, sym->name, size);
+ return strlcpy(buf, symbol__name(sym), size);
}
static int lock_contention_caller(struct perf_sample *sample,
char *buf, int size)
@@ -1005,7 +1006,8 @@ static int report_lock_contention_begin_event(struct perf_sample *sample)
return -ENOMEM;
}
- addrs[filters.nr_addrs++] = map__unmap_ip(kmap, sym->start);
+ addrs[filters.nr_addrs++] = map__unmap_ip(kmap,
+ symbol__start(sym));
filters.addrs = addrs;
}
}
@@ -1019,7 +1021,7 @@ static int report_lock_contention_begin_event(struct perf_sample *sample)
case LOCK_AGGR_ADDR:
sym = machine__find_kernel_symbol(machine, key, &kmap);
if (sym)
- name = sym->name;
+ name = symbol__name(sym);
break;
case LOCK_AGGR_CALLER:
name = buf;
diff --git a/tools/perf/builtin-mem.c b/tools/perf/builtin-mem.c
index ade53688ebcd..5a19be73348d 100644
--- a/tools/perf/builtin-mem.c
+++ b/tools/perf/builtin-mem.c
@@ -249,7 +249,7 @@ dump_raw_samples(const struct perf_tool *tool,
sample->data_src,
symbol_conf.field_sep,
dso ? dso__long_name(dso) : "???",
- al.sym ? al.sym->name : "???");
+ al.sym ? symbol__name(al.sym) : "???");
out_put:
addr_location__exit(&al);
return 0;
diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c
index 6e8bf619c86c..e53098b532b4 100644
--- a/tools/perf/builtin-sched.c
+++ b/tools/perf/builtin-sched.c
@@ -2629,9 +2629,9 @@ static void save_task_callchain(struct perf_sched *sched,
sym = node->ms.sym;
if (sym) {
- if (!strcmp(sym->name, "schedule") ||
- !strcmp(sym->name, "__schedule") ||
- !strcmp(sym->name, "preempt_schedule"))
+ if (!strcmp(symbol__name(sym), "schedule") ||
+ !strcmp(symbol__name(sym), "__schedule") ||
+ !strcmp(symbol__name(sym), "preempt_schedule"))
symbol__set_ignore(sym, true);
}
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c
index 565ee8d55f80..f1257a78d975 100644
--- a/tools/perf/builtin-script.c
+++ b/tools/perf/builtin-script.c
@@ -1404,7 +1404,7 @@ static int ip__fprintf_sym(uint64_t addr, struct thread *thread,
addr_location__init(&al);
thread__find_map(thread, cpumode, addr, &al);
- if ((*lastsym) && al.addr >= (*lastsym)->start && al.addr < (*lastsym)->end)
+ if ((*lastsym) && al.addr >= symbol__start((*lastsym)) && al.addr < symbol__end((*lastsym)))
goto out;
al.cpu = cpu;
@@ -1415,11 +1415,11 @@ static int ip__fprintf_sym(uint64_t addr, struct thread *thread,
if (!al.sym)
goto out;
- if (al.addr < al.sym->end)
- off = al.addr - al.sym->start;
+ if (al.addr < symbol__end(al.sym))
+ off = al.addr - symbol__start(al.sym);
else
- off = al.addr - map__start(al.map) - al.sym->start;
- printed += fprintf(fp, "\t%s", al.sym->name);
+ off = al.addr - map__start(al.map) - symbol__start(al.sym);
+ printed += fprintf(fp, "\t%s", symbol__name(al.sym));
if (off)
printed += fprintf(fp, "%+d", off);
printed += fprintf(fp, ":");
@@ -1638,7 +1638,7 @@ static const char *resolve_branch_sym(struct perf_sample *sample,
if (!addr_al->thread)
thread__resolve(thread, addr_al, sample);
if (addr_al->sym)
- name = addr_al->sym->name;
+ name = symbol__name(addr_al->sym);
else
*ip = sample->addr;
} else {
@@ -1646,7 +1646,7 @@ static const char *resolve_branch_sym(struct perf_sample *sample,
}
} else if (sample->flags & (PERF_IP_FLAG_RETURN | PERF_IP_FLAG_TRACE_END)) {
if (al->sym)
- name = al->sym->name;
+ name = symbol__name(al->sym);
else
*ip = sample->ip;
}
diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c
index 3f9153d5ecfb..046bd185318d 100644
--- a/tools/perf/builtin-timechart.c
+++ b/tools/perf/builtin-timechart.c
@@ -565,7 +565,7 @@ static char *cat_backtrace(struct perf_sample *sample,
addr_location__init(&tal);
tal.filtered = 0;
if (thread__find_symbol(al.thread, cpumode, ip, &tal))
- fprintf(f, "..... %016" PRIx64 " %s\n", ip, tal.sym->name);
+ fprintf(f, "..... %016" PRIx64 " %s\n", ip, symbol__name(tal.sym));
else
fprintf(f, "..... %016" PRIx64 "\n", ip);
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
index a3442d2d6edf..19a21348823c 100644
--- a/tools/perf/builtin-top.c
+++ b/tools/perf/builtin-top.c
@@ -133,8 +133,8 @@ static int perf_top__parse_source(struct perf_top *top, struct hist_entry *he)
* We can't annotate with just /proc/kallsyms
*/
if (dso__symtab_type(dso) == DSO_BINARY_TYPE__KALLSYMS && !dso__is_kcore(dso)) {
- pr_err("Can't annotate %s: No vmlinux file was found in the "
- "path\n", sym->name);
+ pr_err("Can't annotate %s: No vmlinux file was found in the path\n",
+ symbol__name(sym));
sleep(1);
return -1;
}
@@ -145,7 +145,7 @@ static int perf_top__parse_source(struct perf_top *top, struct hist_entry *he)
if (!symbol__hists(sym, evlist__nr_entries(top->evlist))) {
annotation__unlock(notes);
pr_err("Not enough memory for annotating '%s' symbol!\n",
- sym->name);
+ symbol__name(sym));
sleep(1);
return err;
}
@@ -156,7 +156,7 @@ static int perf_top__parse_source(struct perf_top *top, struct hist_entry *he)
} else {
char msg[BUFSIZ];
symbol__strerror_disassemble(&he->ms, err, msg, sizeof(msg));
- pr_err("Couldn't annotate %s: %s\n", sym->name, msg);
+ pr_err("Couldn't annotate %s: %s\n", symbol__name(sym), msg);
}
annotation__unlock(notes);
@@ -186,9 +186,11 @@ static void ui__warn_map_erange(struct map *map, struct symbol *sym, u64 ip)
"Not all samples will be on the annotation output.\n\n"
"Please report to linux-kernel@vger.kernel.org\n",
ip, dso__long_name(dso), dso__symtab_origin(dso),
- map__start(map), map__end(map), sym->start, sym->end,
+ map__start(map), map__end(map), symbol__start(sym),
+ symbol__end(sym),
symbol__binding(sym) == STB_GLOBAL ? 'g' :
- symbol__binding(sym) == STB_LOCAL ? 'l' : 'w', sym->name,
+ symbol__binding(sym) == STB_LOCAL ? 'l' : 'w',
+ symbol__name(sym),
err ? "[unknown]" : uts.machine,
err ? "[unknown]" : uts.release, perf_version_string);
if (use_browser <= 0)
@@ -232,7 +234,7 @@ static void perf_top__record_precise_ip(struct perf_top *top,
ui__warn_map_erange(he->ms.map, sym, ip);
else if (err == -ENOMEM) {
pr_err("Not enough memory for annotating '%s' symbol!\n",
- sym->name);
+ symbol__name(sym));
sleep(1);
}
@@ -263,7 +265,8 @@ static void perf_top__show_details(struct perf_top *top)
if (notes->src == NULL)
goto out_unlock;
- printf("Showing %s for %s\n", evsel__name(top->sym_evsel), symbol->name);
+ printf("Showing %s for %s\n", evsel__name(top->sym_evsel),
+ symbol__name(symbol));
printf(" Events Pcnt (>=%d%%)\n", annotate_opts.min_pcnt);
more = hist_entry__annotate_printf(he, top->sym_evsel);
@@ -418,7 +421,7 @@ static void perf_top__prompt_symbol(struct perf_top *top, const char *msg)
next = rb_first_cached(&hists->entries);
while (next) {
n = rb_entry(next, struct hist_entry, rb_node);
- if (n->ms.sym && !strcmp(buf, n->ms.sym->name)) {
+ if (n->ms.sym && !strcmp(buf, symbol__name(n->ms.sym))) {
found = n;
break;
}
@@ -437,11 +440,11 @@ static void perf_top__prompt_symbol(struct perf_top *top, const char *msg)
static void perf_top__print_mapped_keys(struct perf_top *top)
{
- char *name = NULL;
+ const char *name = NULL;
if (top->sym_filter_entry) {
struct symbol *sym = top->sym_filter_entry->ms.sym;
- name = sym->name;
+ name = symbol__name(sym);
}
fprintf(stdout, "\nMapped keys:\n");
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c
index d327603ae454..aaa090912409 100644
--- a/tools/perf/builtin-trace.c
+++ b/tools/perf/builtin-trace.c
@@ -768,15 +768,16 @@ size_t syscall_arg__scnprintf_ksym(char *bf, size_t size, struct syscall_arg *ar
arg->val, &map);
if (sym) {
- u64 start = map__unmap_ip(map, sym->start);
+ u64 start = map__unmap_ip(map, symbol__start(sym));
u64 offset = arg->val - start;
size_t printed;
if (offset == 0)
- printed = scnprintf(bf, size, "%s", sym->name);
+ printed = scnprintf(bf, size, "%s",
+ symbol__name(sym));
else
printed = scnprintf(bf, size, "%s+0x%" PRIx64,
- sym->name, offset);
+ symbol__name(sym), offset);
map__put(map);
return printed;
}
@@ -3652,8 +3653,8 @@ static void print_location(FILE *f, struct perf_sample *sample,
fprintf(f, "%s@", dso__long_name(map__dso(al->map)));
if ((verbose > 0 || print_sym) && al->sym)
- fprintf(f, "%s+0x%" PRIx64, al->sym->name,
- al->addr - al->sym->start);
+ fprintf(f, "%s+0x%" PRIx64, symbol__name(al->sym),
+ al->addr - symbol__start(al->sym));
else if (al->map)
fprintf(f, "0x%" PRIx64, al->addr);
else
diff --git a/tools/perf/tests/dlfilter-test.c b/tools/perf/tests/dlfilter-test.c
index 204663571943..b4649c79c309 100644
--- a/tools/perf/tests/dlfilter-test.c
+++ b/tools/perf/tests/dlfilter-test.c
@@ -274,11 +274,11 @@ static int get_ip_addr(struct test_data *td)
sym = map__find_symbol_by_name(map, "foo");
if (sym)
- td->foo = sym->start;
+ td->foo = symbol__start(sym);
sym = map__find_symbol_by_name(map, "bar");
if (sym)
- td->bar = sym->start;
+ td->bar = symbol__start(sym);
map__put(map);
diff --git a/tools/perf/tests/dwarf-unwind.c b/tools/perf/tests/dwarf-unwind.c
index 9ed78d00fb87..b8b0c430a6b3 100644
--- a/tools/perf/tests/dwarf-unwind.c
+++ b/tools/perf/tests/dwarf-unwind.c
@@ -56,7 +56,7 @@ int test__dwarf_unwind(struct test_suite *test, int subtest);
static int unwind_entry(struct unwind_entry *entry, void *arg)
{
unsigned long *cnt = (unsigned long *) arg;
- char *symbol = entry->ms.sym ? entry->ms.sym->name : NULL;
+ const char *symbol = entry->ms.sym ? symbol__name(entry->ms.sym) : NULL;
static const char *funcs[MAX_STACK] = {
"test__arch_unwind_sample",
"test_dwarf_unwind__thread",
diff --git a/tools/perf/tests/hists_common.c b/tools/perf/tests/hists_common.c
index 187f12f5bc21..0db3ea9cf62e 100644
--- a/tools/perf/tests/hists_common.c
+++ b/tools/perf/tests/hists_common.c
@@ -184,7 +184,7 @@ void print_hists_in(struct hists *hists)
pr_info("%2d: entry: %-8s [%-8s] %20s: period = %"PRIu64"\n",
i, thread__comm_str(he->thread),
dso__short_name(dso),
- he->ms.sym->name, he->stat.period);
+ symbol__name(he->ms.sym), he->stat.period);
}
i++;
@@ -213,7 +213,7 @@ void print_hists_out(struct hists *hists)
pr_info("%2d: entry: %8s:%5d [%-8s] %20s: period = %"PRIu64"/%"PRIu64"\n",
i, thread__comm_str(he->thread), thread__tid(he->thread),
dso__short_name(dso),
- he->ms.sym->name, he->stat.period,
+ symbol__name(he->ms.sym), he->stat.period,
he->stat_acc ? he->stat_acc->period : 0);
}
diff --git a/tools/perf/tests/hists_cumulate.c b/tools/perf/tests/hists_cumulate.c
index c4a11a3255bc..95b9a629e59e 100644
--- a/tools/perf/tests/hists_cumulate.c
+++ b/tools/perf/tests/hists_cumulate.c
@@ -164,11 +164,11 @@ typedef int (*test_fn_t)(struct evsel *, struct machine *);
#define COMM(he) (thread__comm_str(he->thread))
#define DSO(he) (dso__short_name(map__dso(he->ms.map)))
-#define SYM(he) (he->ms.sym->name)
+#define SYM(he) (symbol__name(he->ms.sym))
#define CPU(he) (he->cpu)
#define DEPTH(he) (he->callchain->max_depth)
#define CDSO(cl) (dso__short_name(map__dso(cl->ms.map)))
-#define CSYM(cl) (cl->ms.sym->name)
+#define CSYM(cl) (symbol__name(cl->ms.sym))
struct result {
u64 children;
diff --git a/tools/perf/tests/hists_output.c b/tools/perf/tests/hists_output.c
index 35d230a83e43..77fb3a187139 100644
--- a/tools/perf/tests/hists_output.c
+++ b/tools/perf/tests/hists_output.c
@@ -129,7 +129,7 @@ typedef int (*test_fn_t)(struct evsel *, struct machine *);
#define COMM(he) (thread__comm_str(he->thread))
#define DSO(he) (dso__short_name(map__dso(he->ms.map)))
-#define SYM(he) (he->ms.sym->name)
+#define SYM(he) (symbol__name(he->ms.sym))
#define CPU(he) (he->cpu)
#define PID(he) (thread__tid(he->thread))
diff --git a/tools/perf/tests/symbols.c b/tools/perf/tests/symbols.c
index c09e04f36035..d9796851335e 100644
--- a/tools/perf/tests/symbols.c
+++ b/tools/perf/tests/symbols.c
@@ -129,14 +129,14 @@ static int test_dso(struct dso *dso)
continue;
/* Check for overlapping function symbols */
- if (last_sym && sym->start < last_sym->end) {
+ if (last_sym && symbol__start(sym) < symbol__end(last_sym)) {
pr_debug("Overlapping symbols:\n");
symbol__fprintf(last_sym, stderr);
symbol__fprintf(sym, stderr);
ret = TEST_FAIL;
}
/* Check for zero-length function symbol */
- if (sym->start == sym->end) {
+ if (symbol__start(sym) == symbol__end(sym)) {
pr_debug("Zero-length symbol:\n");
symbol__fprintf(sym, stderr);
ret = TEST_FAIL;
diff --git a/tools/perf/tests/vmlinux-kallsyms.c b/tools/perf/tests/vmlinux-kallsyms.c
index 9396c8a77c86..97950f86ed5f 100644
--- a/tools/perf/tests/vmlinux-kallsyms.c
+++ b/tools/perf/tests/vmlinux-kallsyms.c
@@ -292,18 +292,20 @@ static int test__vmlinux_matches_kallsyms(struct test_suite *test __maybe_unused
sym = rb_entry(nd, struct symbol, rb_node);
- if (sym->start == sym->end)
+ if (symbol__start(sym) == symbol__end(sym))
continue;
- mem_start = map__unmap_ip(args.vmlinux_map, sym->start);
- mem_end = map__unmap_ip(args.vmlinux_map, sym->end);
+ mem_start = map__unmap_ip(args.vmlinux_map,
+ symbol__start(sym));
+ mem_end = map__unmap_ip(args.vmlinux_map, symbol__end(sym));
first_pair = machine__find_kernel_symbol(&args.kallsyms, mem_start, NULL);
pair = first_pair;
- if (pair && UM(pair->start) == mem_start) {
+ if (pair && UM(symbol__start(pair)) == mem_start) {
next_pair:
- if (arch__compare_symbol_names(sym->name, pair->name) == 0) {
+ if (arch__compare_symbol_names(symbol__name(sym),
+ symbol__name(pair)) == 0) {
/*
* kallsyms don't have the symbol end, so we
* set that by using the next symbol start - 1,
@@ -313,11 +315,12 @@ static int test__vmlinux_matches_kallsyms(struct test_suite *test __maybe_unused
* off the real size. More than that and we
* _really_ have a problem.
*/
- s64 skew = mem_end - UM(pair->end);
+ s64 skew = mem_end - UM(symbol__end(pair));
if (llabs(skew) >= page_size)
pr_debug("WARN: %#" PRIx64 ": diff end addr for %s v: %#" PRIx64 " k: %#" PRIx64 "\n",
- mem_start, sym->name, mem_end,
- UM(pair->end));
+ mem_start, symbol__name(sym),
+ mem_end,
+ UM(symbol__end(pair)));
/*
* Do not count this as a failure, because we
@@ -328,16 +331,19 @@ static int test__vmlinux_matches_kallsyms(struct test_suite *test __maybe_unused
continue;
} else {
pair = machine__find_kernel_symbol_by_name(&args.kallsyms,
- sym->name, NULL);
+ symbol__name(sym),
+ NULL);
if (pair) {
- if (UM(pair->start) == mem_start)
+ if (UM(symbol__start(pair)) == mem_start)
goto next_pair;
pr_debug("WARN: %#" PRIx64 ": diff name v: %s k: %s\n",
- mem_start, sym->name, pair->name);
+ mem_start, symbol__name(sym),
+ symbol__name(pair));
} else {
pr_debug("WARN: %#" PRIx64 ": diff name v: %s k: %s\n",
- mem_start, sym->name, first_pair->name);
+ mem_start, symbol__name(sym),
+ symbol__name(first_pair));
}
continue;
@@ -348,14 +354,14 @@ static int test__vmlinux_matches_kallsyms(struct test_suite *test __maybe_unused
* such as __indirect_thunk_end.
*/
continue;
- } else if (is_ignored_symbol(sym->name, symbol__type(sym))) {
+ } else if (is_ignored_symbol(symbol__name(sym), symbol__type(sym))) {
/*
* Ignore hidden symbols, see scripts/kallsyms.c for the details
*/
continue;
} else {
pr_debug("ERR : %#" PRIx64 ": %s not on kallsyms\n",
- mem_start, sym->name);
+ mem_start, symbol__name(sym));
}
err = -1;
diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c
index cf36b5347f72..5ef9a59ebdc1 100644
--- a/tools/perf/ui/browsers/annotate.c
+++ b/tools/perf/ui/browsers/annotate.c
@@ -196,7 +196,7 @@ static void annotate_browser__draw_current_jump(struct ui_browser *browser)
int diff = 0;
/* PLT symbols contain external offsets */
- if (strstr(sym->name, "@plt"))
+ if (strstr(symbol__name(sym), "@plt"))
return;
if (!disasm_line__is_valid_local_jump(cursor, sym))
@@ -554,7 +554,8 @@ static void ui_browser__init_asm_mode(struct ui_browser *browser)
static int sym_title(struct symbol *sym, struct map *map, char *title,
size_t sz, int percent_type)
{
- return snprintf(title, sz, "%s %s [Percent: %s] %s", sym->name,
+ return snprintf(title, sz, "%s %s [Percent: %s] %s",
+ symbol__name(sym),
dso__long_name(map__dso(map)),
percent_type_str(percent_type),
annotate_opts.code_with_type ? "[Type]" : "");
@@ -602,7 +603,7 @@ static bool annotate_browser__callq(struct annotate_browser *browser,
if (!symbol__hists(dl->ops.target.sym, evlist__nr_entries(evsel->evlist))) {
annotation__unlock(notes);
ui__warning("Not enough memory for annotating '%s' symbol!\n",
- dl->ops.target.sym->name);
+ symbol__name(dl->ops.target.sym));
return true;
}
@@ -876,7 +877,7 @@ static void annotate_browser__symbol_annotate_error(struct annotate_browser *bro
dso__set_annotate_warned(dso);
symbol__strerror_disassemble(ms, err, msg, sizeof(msg));
- ui__error("Couldn't annotate %s:\n%s", sym->name, msg);
+ ui__error("Couldn't annotate %s:\n%s", symbol__name(sym), msg);
}
static int annotate_browser__run(struct annotate_browser *browser,
@@ -1255,7 +1256,7 @@ int __hist_entry__tui_annotate(struct hist_entry *he, struct map_symbol *ms,
*/
if (al_addr != NO_ADDR) {
struct annotation_line *al = annotated_source__get_line(notes->src,
- al_addr - sym->start);
+ al_addr - symbol__start(sym));
browser.selection = al;
}
diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c
index 593e1fd5759d..206657039223 100644
--- a/tools/perf/ui/browsers/hists.c
+++ b/tools/perf/ui/browsers/hists.c
@@ -2548,7 +2548,7 @@ add_annotate_opt(struct popup_action *act, char **optstr,
if (ms->sym == NULL || symbol__annotation(ms->sym)->src == NULL)
return 0;
- if (asprintf(optstr, "Annotate %s", ms->sym->name) < 0)
+ if (asprintf(optstr, "Annotate %s", symbol__name(ms->sym)) < 0)
return 0;
map_symbol__copy(&act->ms, ms);
@@ -2748,7 +2748,7 @@ do_run_script(struct hist_browser *browser,
if (act->ms.thread)
len += strlen(thread__comm_str(act->ms.thread));
else if (act->ms.sym)
- len += strlen(act->ms.sym->name);
+ len += strlen(symbol__name(act->ms.sym));
script_opt = malloc(len);
if (!script_opt)
return -1;
@@ -2759,7 +2759,7 @@ do_run_script(struct hist_browser *browser,
thread__comm_str(act->ms.thread));
} else if (act->ms.sym) {
n = scnprintf(script_opt, len, " -S %s ",
- act->ms.sym->name);
+ symbol__name(act->ms.sym));
}
if (act->time) {
@@ -2804,7 +2804,7 @@ add_script_opt_2(struct popup_action *act, char **optstr,
return 0;
} else if (sym) {
if (asprintf(optstr, "Run scripts for samples of symbol [%s]%s",
- sym->name, tstr) < 0)
+ symbol__name(sym), tstr) < 0)
return 0;
} else {
if (asprintf(optstr, "Run scripts for all samples%s", tstr) < 0)
@@ -3208,7 +3208,7 @@ static int evsel__hists_browse(struct evsel *evsel, int nr_events, const char *h
ui_browser__warning(&browser->b, delay_secs * 2,
"No samples for the \"%s\" symbol.\n\n"
"Probably appeared just in a callchain",
- browser->selection->sym->name);
+ symbol__name(browser->selection->sym));
continue;
}
diff --git a/tools/perf/ui/browsers/map.c b/tools/perf/ui/browsers/map.c
index 075a575cdc5d..1f69d7a798e9 100644
--- a/tools/perf/ui/browsers/map.c
+++ b/tools/perf/ui/browsers/map.c
@@ -31,12 +31,13 @@ static void map_browser__write(struct ui_browser *browser, void *nd, int row)
ui_browser__set_percent_color(browser, 0, current_entry);
ui_browser__printf(browser, "%*" PRIx64 " %*" PRIx64 " %c ",
- mb->addrlen, sym->start, mb->addrlen, sym->end,
+ mb->addrlen, symbol__start(sym), mb->addrlen,
+ symbol__end(sym),
symbol__binding(sym) == STB_GLOBAL ? 'g' :
symbol__binding(sym) == STB_LOCAL ? 'l' : 'w');
width = browser->width - ((mb->addrlen * 2) + 4);
if (width > 0)
- ui_browser__write_nstring(browser, sym->name, width);
+ ui_browser__write_nstring(browser, symbol__name(sym), width);
}
/* FIXME uber-kludgy, see comment on cmd_report... */
@@ -122,8 +123,8 @@ int map__browse(struct map *map)
for (nd = rb_first(mb.b.entries); nd; nd = rb_next(nd)) {
struct symbol *pos = rb_entry(nd, struct symbol, rb_node);
- if (maxaddr < pos->end)
- maxaddr = pos->end;
+ if (maxaddr < symbol__end(pos))
+ maxaddr = symbol__end(pos);
if (verbose > 0) {
u32 *idx = symbol__browser_index(pos);
*idx = mb.b.nr_entries;
diff --git a/tools/perf/ui/gtk/annotate.c b/tools/perf/ui/gtk/annotate.c
index 094dfa045b77..136957d475d0 100644
--- a/tools/perf/ui/gtk/annotate.c
+++ b/tools/perf/ui/gtk/annotate.c
@@ -67,7 +67,7 @@ static int perf_gtk__get_percent(char *buf, size_t size, struct symbol *sym,
static int perf_gtk__get_offset(char *buf, size_t size, struct map_symbol *ms,
struct disasm_line *dl)
{
- u64 start = map__rip_2objdump(ms->map, ms->sym->start);
+ u64 start = map__rip_2objdump(ms->map, symbol__start(ms->sym));
strcpy(buf, "");
@@ -192,7 +192,8 @@ static int symbol__gtk_annotate(struct map_symbol *ms, struct evsel *evsel,
dso__set_annotate_warned(dso);
symbol__strerror_disassemble(ms, err, msg, sizeof(msg));
- ui__error("Couldn't annotate %s: %s\n", sym->name, msg);
+ ui__error("Couldn't annotate %s: %s\n", symbol__name(sym),
+ msg);
return -1;
}
@@ -234,7 +235,7 @@ static int symbol__gtk_annotate(struct map_symbol *ms, struct evsel *evsel,
}
scrolled_window = gtk_scrolled_window_new();
- tab_label = gtk_label_new(sym->name);
+ tab_label = gtk_label_new(symbol__name(sym));
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
GTK_POLICY_AUTOMATIC,
diff --git a/tools/perf/ui/stdio/hist.c b/tools/perf/ui/stdio/hist.c
index 8c4c8925df2c..f80cc73909ed 100644
--- a/tools/perf/ui/stdio/hist.c
+++ b/tools/perf/ui/stdio/hist.c
@@ -100,13 +100,12 @@ static struct callchain_list rem_hits;
static void init_rem_hits(void)
{
- rem_sq_bracket = malloc(sizeof(*rem_sq_bracket) + 6);
+ rem_sq_bracket = symbol__new(0, 0, 0, 0, "[...]");
if (!rem_sq_bracket) {
fprintf(stderr, "Not enough memory to display remaining hits\n");
return;
}
- strcpy(rem_sq_bracket->name, "[...]");
rem_hits.ms.sym = rem_sq_bracket;
}
diff --git a/tools/perf/util/annotate-arch/annotate-loongarch.c b/tools/perf/util/annotate-arch/annotate-loongarch.c
index a949b4392dd4..d275730080b7 100644
--- a/tools/perf/util/annotate-arch/annotate-loongarch.c
+++ b/tools/perf/util/annotate-arch/annotate-loongarch.c
@@ -94,8 +94,8 @@ static int loongarch_jump__parse(const struct arch *arch, struct ins_operands *o
ops->target.addr = strtoull(ops->raw, NULL, 16);
target.addr = map__objdump_2mem(map, ops->target.addr);
- start = map__unmap_ip(map, sym->start);
- end = map__unmap_ip(map, sym->end);
+ start = map__unmap_ip(map, symbol__start(sym));
+ end = map__unmap_ip(map, symbol__end(sym));
ops->target.outside = target.addr < start || target.addr >= end;
diff --git a/tools/perf/util/annotate-arch/annotate-x86.c b/tools/perf/util/annotate-arch/annotate-x86.c
index 1acf31a2c759..07b2b9e8fd1d 100644
--- a/tools/perf/util/annotate-arch/annotate-x86.c
+++ b/tools/perf/util/annotate-arch/annotate-x86.c
@@ -239,7 +239,7 @@ static void update_insn_state_x86(struct type_state *state,
/* Try to resolve the call target name */
if (func)
- call_name = func->name;
+ call_name = symbol__name(func);
else
call_name = dl->ops.target.name;
@@ -254,7 +254,7 @@ static void update_insn_state_x86(struct type_state *state,
/* Invalidate caller-saved registers after call */
call_addr = map__rip_2objdump(dloc->ms->map,
- dloc->ms->sym->start + dl->al.offset);
+ symbol__start(dloc->ms->sym) + dl->al.offset);
for (unsigned i = 0; i < ARRAY_SIZE(state->regs); i++) {
struct type_state_reg *reg = &state->regs[i];
@@ -286,7 +286,7 @@ static void update_insn_state_x86(struct type_state *state,
int offset;
const char *var_name = NULL;
struct map_symbol *ms = dloc->ms;
- u64 ip = ms->sym->start + dl->al.offset;
+ u64 ip = symbol__start(ms->sym) + dl->al.offset;
if (!has_reg_type(state, dst->reg1))
return;
@@ -487,7 +487,7 @@ static void update_insn_state_x86(struct type_state *state,
return;
if (dloc->fb_cfa) {
- u64 ip = dloc->ms->sym->start + dl->al.offset;
+ u64 ip = symbol__start(dloc->ms->sym) + dl->al.offset;
u64 pc = map__rip_2objdump(dloc->ms->map, ip);
if (die_get_cfa(dloc->di->dbg, pc, &fbreg, &fboff) < 0)
@@ -504,7 +504,7 @@ static void update_insn_state_x86(struct type_state *state,
if (dso__kernel(map__dso(dloc->ms->map)) &&
src->segment == INSN_SEG_X86_GS && src->imm) {
- u64 ip = dloc->ms->sym->start + dl->al.offset;
+ u64 ip = symbol__start(dloc->ms->sym) + dl->al.offset;
u64 var_addr;
int offset;
@@ -653,7 +653,7 @@ static void update_insn_state_x86(struct type_state *state,
/* Or check if it's a global variable */
else if (sreg == DWARF_REG_PC) {
struct map_symbol *ms = dloc->ms;
- u64 ip = ms->sym->start + dl->al.offset;
+ u64 ip = symbol__start(ms->sym) + dl->al.offset;
u64 addr;
int offset;
@@ -678,7 +678,7 @@ static void update_insn_state_x86(struct type_state *state,
/* And check percpu access with base register */
else if (has_reg_type(state, sreg) &&
state->regs[sreg].kind == TSR_KIND_PERCPU_BASE) {
- u64 ip = dloc->ms->sym->start + dl->al.offset;
+ u64 ip = symbol__start(dloc->ms->sym) + dl->al.offset;
u64 var_addr = src->offset;
int offset;
diff --git a/tools/perf/util/annotate-data.c b/tools/perf/util/annotate-data.c
index 19a6ecd67f28..0b2bd6702f25 100644
--- a/tools/perf/util/annotate-data.c
+++ b/tools/perf/util/annotate-data.c
@@ -851,9 +851,10 @@ bool get_global_var_info(struct data_loc_info *dloc, u64 addr,
sym = thread__find_symbol_fb(dloc->thread, dloc->cpumode,
mem_addr, &al);
if (sym) {
- *var_name = sym->name;
+ *var_name = symbol__name(sym);
/* Calculate type offset from the start of variable */
- *var_offset = mem_addr - map__unmap_ip(al.map, sym->start);
+ *var_offset = mem_addr - map__unmap_ip(al.map,
+ symbol__start(sym));
} else {
*var_name = NULL;
}
@@ -1503,7 +1504,7 @@ static enum type_match_result find_data_type_insn(struct data_loc_info *dloc,
bb->begin->al.offset, bb->end->al.offset);
list_for_each_entry_from(dl, ¬es->src->source, al.node) {
- u64 this_ip = sym->start + dl->al.offset;
+ u64 this_ip = symbol__start(sym) + dl->al.offset;
u64 addr = map__rip_2objdump(dloc->ms->map, this_ip);
/* Skip comment or debug info lines */
@@ -1582,8 +1583,8 @@ static enum type_match_result find_data_type_block(struct data_loc_info *dloc,
}
pr_debug_dtp("cannot find a basic block from %"PRIx64" to %"PRIx64"\n",
- src_ip - dloc->ms->sym->start,
- dst_ip - dloc->ms->sym->start);
+ src_ip - symbol__start(dloc->ms->sym),
+ dst_ip - symbol__start(dloc->ms->sym));
continue;
}
prepend_basic_blocks(&this_blocks, &basic_blocks);
@@ -1662,7 +1663,8 @@ static int find_data_type_die(struct data_loc_info *dloc, Dwarf_Die *type_die)
pr_debug_dtp("-----------------------------------------------------------\n");
pr_debug_dtp("find data type for %s%#x(%s) at %s+%#"PRIx64"\n",
offset_sign, offset, buf,
- dloc->ms->sym->name, dloc->ip - dloc->ms->sym->start);
+ symbol__name(dloc->ms->sym),
+ dloc->ip - symbol__start(dloc->ms->sym));
/*
* IP is a relative instruction address from the start of the map, as
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c
index 86a4beec6528..a1090a4e2e9d 100644
--- a/tools/perf/util/annotate.c
+++ b/tools/perf/util/annotate.c
@@ -237,18 +237,20 @@ static int __symbol__inc_addr_samples(struct map_symbol *ms,
pr_debug3("%s: addr=%#" PRIx64 "\n", __func__, map__unmap_ip(ms->map, addr));
- if ((addr < sym->start || addr >= sym->end) &&
- (addr != sym->end || sym->start != sym->end)) {
+ if ((addr < symbol__start(sym) || addr >= symbol__end(sym)) &&
+ (addr != symbol__end(sym) || symbol__start(sym) != symbol__end(sym))) {
pr_debug("%s(%d): ERANGE! sym->name=%s, start=%#" PRIx64 ", addr=%#" PRIx64 ", end=%#" PRIx64 "\n",
- __func__, __LINE__, sym->name, sym->start, addr, sym->end);
+ __func__, __LINE__, symbol__name(sym),
+ symbol__start(sym), addr, symbol__end(sym));
return -ERANGE;
}
- offset = addr - sym->start;
+ offset = addr - symbol__start(sym);
h = annotated_source__histogram(src, evsel);
if (h == NULL) {
pr_debug("%s(%d): ENOMEM! sym->name=%s, start=%#" PRIx64 ", addr=%#" PRIx64 ", end=%#" PRIx64 ", func: %d\n",
- __func__, __LINE__, sym->name, sym->start, addr, sym->end,
+ __func__, __LINE__, symbol__name(sym),
+ symbol__start(sym), addr, symbol__end(sym),
symbol__type(sym) == STT_FUNC);
return -ENOMEM;
}
@@ -288,7 +290,8 @@ static int __symbol__inc_addr_samples(struct map_symbol *ms,
pr_debug3("%#" PRIx64 " %s: period++ [addr: %#" PRIx64 ", %#" PRIx64
", evidx=%d] => nr_samples: %" PRIu64 ", period: %" PRIu64
" weight %" PRIu64 "\n",
- sym->start, sym->name, addr, addr - sym->start, evsel->core.idx,
+ symbol__start(sym), symbol__name(sym), addr,
+ addr - symbol__start(sym), evsel->core.idx,
entry->nr_samples, entry->period, weight);
return 0;
}
@@ -402,18 +405,18 @@ static int symbol__account_cycles(u64 addr, u64 start, struct symbol *sym,
branch = symbol__find_branch_hist(sym, evlist__nr_br_cntr(evsel->evlist));
if (!branch)
return -ENOMEM;
- if (addr < sym->start || addr >= sym->end)
+ if (addr < symbol__start(sym) || addr >= symbol__end(sym))
return -ERANGE;
if (start) {
- if (start < sym->start || start >= sym->end)
+ if (start < symbol__start(sym) || start >= symbol__end(sym))
return -ERANGE;
if (start >= addr)
start = 0;
}
- offset = addr - sym->start;
+ offset = addr - symbol__start(sym);
ret = __symbol__account_cycles(branch->cycles_hist,
- start ? start - sym->start : 0,
+ start ? start - symbol__start(sym) : 0,
offset, cycles,
!!start);
@@ -445,13 +448,13 @@ int addr_map_symbol__account_cycles(struct addr_map_symbol *ams,
if (start &&
(start->ms.sym == ams->ms.sym ||
(ams->ms.sym &&
- start->addr == ams->ms.sym->start + map__start(ams->ms.map))))
+ start->addr == symbol__start(ams->ms.sym) + map__start(ams->ms.map))))
saddr = start->al_addr;
if (saddr == 0)
pr_debug2("BB with bad start: addr %"PRIx64" start %"PRIx64" sym %"PRIx64" saddr %"PRIx64"\n",
ams->addr,
start ? start->addr : 0,
- ams->ms.sym ? ams->ms.sym->start + map__start(ams->ms.map) : 0,
+ ams->ms.sym ? symbol__start(ams->ms.sym) + map__start(ams->ms.map) : 0,
saddr);
err = symbol__account_cycles(ams->al_addr, saddr, ams->ms.sym, cycles, evsel, br_cntr);
if (err)
@@ -1165,9 +1168,11 @@ int symbol__annotate(struct map_symbol *ms, struct evsel *evsel,
notes->src->nr_events = nr ? nr : 1;
if (annotate_opts.full_addr)
- notes->src->start = map__objdump_2mem(ms->map, ms->sym->start);
+ notes->src->start = map__objdump_2mem(ms->map,
+ symbol__start(ms->sym));
else
- notes->src->start = map__rip_2objdump(ms->map, ms->sym->start);
+ notes->src->start = map__rip_2objdump(ms->map,
+ symbol__start(ms->sym));
return symbol__disassemble(sym, &args);
}
@@ -1309,7 +1314,7 @@ static void symbol__annotate_hits(struct symbol *sym, struct evsel *evsel)
entry = annotated_source__hist_entry(notes->src, evsel, offset);
if (entry && entry->nr_samples != 0)
printf("%*" PRIx64 ": %" PRIu64 "\n", BITS_PER_LONG / 2,
- sym->start + offset, entry->nr_samples);
+ symbol__start(sym) + offset, entry->nr_samples);
}
printf("%*s: %" PRIu64 "\n", BITS_PER_LONG / 2, "h->nr_samples", h->nr_samples);
}
@@ -1521,7 +1526,7 @@ int map_symbol__annotation_dump(struct map_symbol *ms, struct evsel *evsel,
.evsel = evsel,
};
- if (asprintf(&filename, "%s.annotation", ms->sym->name) < 0)
+ if (asprintf(&filename, "%s.annotation", symbol__name(ms->sym)) < 0)
return -1;
fp = fopen(filename, "w");
@@ -1534,7 +1539,8 @@ int map_symbol__annotation_dump(struct map_symbol *ms, struct evsel *evsel,
}
fprintf(fp, "%s() %s\nEvent: %s\n\n",
- ms->sym->name, dso__long_name(map__dso(ms->map)), ev_name);
+ symbol__name(ms->sym), dso__long_name(map__dso(ms->map)),
+ ev_name);
symbol__annotate_fprintf2(ms->sym, fp, &apd);
fclose(fp);
@@ -1629,7 +1635,7 @@ annotation__mark_jump_targets(struct annotation *notes, struct symbol *sym)
struct annotation_line *al;
/* PLT symbols contain external offsets */
- if (strstr(sym->name, "@plt"))
+ if (strstr(symbol__name(sym), "@plt"))
return;
list_for_each_entry(al, ¬es->src->source, node) {
@@ -1708,7 +1714,7 @@ annotation__init_column_widths(struct annotation *notes, struct symbol *sym)
{
notes->src->widths.addr = notes->src->widths.target =
notes->src->widths.min_addr = hex_width(symbol__size(sym));
- notes->src->widths.max_addr = hex_width(sym->end);
+ notes->src->widths.max_addr = hex_width(symbol__end(sym));
notes->src->widths.jumps = width_jumps(notes->src->max_jump_sources);
notes->src->widths.max_ins_name = annotation__max_ins_name(notes);
}
@@ -1733,9 +1739,11 @@ void annotation__toggle_full_addr(struct annotation *notes, struct map_symbol *m
annotate_opts.full_addr = !annotate_opts.full_addr;
if (annotate_opts.full_addr)
- notes->src->start = map__objdump_2mem(ms->map, ms->sym->start);
+ notes->src->start = map__objdump_2mem(ms->map,
+ symbol__start(ms->sym));
else
- notes->src->start = map__rip_2objdump(ms->map, ms->sym->start);
+ notes->src->start = map__rip_2objdump(ms->map,
+ symbol__start(ms->sym));
annotation__update_column_widths(notes);
}
@@ -1764,9 +1772,10 @@ static void annotation__calc_lines(struct annotation *notes, struct map_symbol *
if (percent_max <= 0.5)
continue;
- addr = map__rip_2objdump(ms->map, ms->sym->start);
+ addr = map__rip_2objdump(ms->map, symbol__start(ms->sym));
al->path = get_srcline(map__dso(ms->map), addr + al->offset, NULL,
- false, true, ms->sym->start + al->offset);
+ false, true,
+ symbol__start(ms->sym) + al->offset);
insert_source_line(&tmp_root, al);
}
@@ -1800,7 +1809,7 @@ int hist_entry__tty_annotate2(struct hist_entry *he, struct evsel *evsel)
dso__set_annotate_warned(dso);
symbol__strerror_disassemble(ms, err, msg, sizeof(msg));
- ui__error("Couldn't annotate %s:\n%s", sym->name, msg);
+ ui__error("Couldn't annotate %s:\n%s", symbol__name(sym), msg);
return -1;
}
@@ -1812,7 +1821,8 @@ int hist_entry__tty_annotate2(struct hist_entry *he, struct evsel *evsel)
hists__scnprintf_title(hists, buf, sizeof(buf));
fprintf(stdout, "%s, [percent: %s]\n%s() %s\n",
- buf, percent_type_str(annotate_opts.percent_type), sym->name, dso__long_name(dso));
+ buf, percent_type_str(annotate_opts.percent_type),
+ symbol__name(sym), dso__long_name(dso));
symbol__annotate_fprintf2(sym, stdout, &apd);
annotated_source__purge(symbol__annotation(sym)->src);
@@ -1834,7 +1844,7 @@ int hist_entry__tty_annotate(struct hist_entry *he, struct evsel *evsel)
dso__set_annotate_warned(dso);
symbol__strerror_disassemble(ms, err, msg, sizeof(msg));
- ui__error("Couldn't annotate %s:\n%s", sym->name, msg);
+ ui__error("Couldn't annotate %s:\n%s", symbol__name(sym), msg);
return -1;
}
@@ -2682,7 +2692,7 @@ static struct disasm_line *find_disasm_line(struct symbol *sym, u64 ip,
if (dl->al.offset == -1)
continue;
- if (sym->start + dl->al.offset == ip) {
+ if (symbol__start(sym) + dl->al.offset == ip) {
/*
* llvm-objdump places "lock" in a separate line and
* in that case, we want to get the next line.
@@ -2818,7 +2828,7 @@ u64 annotate_calc_pcrel(struct map_symbol *ms, u64 ip, int offset,
*/
next = annotation__next_asm_line(notes, dl);
if (next == NULL)
- addr = ms->sym->end + offset;
+ addr = symbol__end(ms->sym) + offset;
else
addr = ip + (next->al.offset - dl->al.offset) + offset;
@@ -2880,7 +2890,7 @@ __hist_entry__get_data_type(struct hist_entry *he, const struct arch *arch,
.arch = arch,
.thread = he->thread,
.ms = ms,
- .ip = ms->sym->start + dl->al.offset,
+ .ip = symbol__start(ms->sym) + dl->al.offset,
.cpumode = he->cpumode,
.op = op_loc,
.di = dbg,
@@ -3134,7 +3144,7 @@ static bool process_basic_block(struct basic_block_data *bb_data,
if (dl->al.offset == -1)
continue;
/* Found the target instruction */
- if (sym->start + dl->al.offset == target) {
+ if (symbol__start(sym) + dl->al.offset == target) {
found = true;
break;
}
@@ -3151,7 +3161,8 @@ static bool process_basic_block(struct basic_block_data *bb_data,
if (dl->ops.target.outside)
break;
/* jump instruction creates new basic block(s) */
- next_dl = find_disasm_line(sym, sym->start + dl->ops.target.offset,
+ next_dl = find_disasm_line(sym,
+ symbol__start(sym) + dl->ops.target.offset,
/*allow_update=*/false);
if (next_dl)
add_basic_block(bb_data, link, next_dl);
diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c
index ed2113190785..f4aee90b43a3 100644
--- a/tools/perf/util/auxtrace.c
+++ b/tools/perf/util/auxtrace.c
@@ -2707,7 +2707,7 @@ static bool dso_sym_match(struct symbol *sym, const char *name, int *cnt,
int idx)
{
/* Same name, and global or the n'th found or any */
- return !arch__compare_symbol_names(name, sym->name) &&
+ return !arch__compare_symbol_names(name, symbol__name(sym)) &&
((!idx && symbol__binding(sym) == STB_GLOBAL) ||
(idx > 0 && ++*cnt == idx) ||
idx < 0);
@@ -2725,14 +2725,14 @@ static void print_duplicate_syms(struct dso *dso, const char *sym_name)
while (sym) {
if (dso_sym_match(sym, sym_name, &cnt, -1)) {
pr_err("#%d\t0x%"PRIx64"\t%c\t%s\n",
- ++cnt, sym->start,
+ ++cnt, symbol__start(sym),
symbol__binding(sym) == STB_GLOBAL ? 'g' :
symbol__binding(sym) == STB_LOCAL ? 'l' : 'w',
- sym->name);
+ symbol__name(sym));
near = true;
} else if (near) {
near = false;
- pr_err("\t\twhich is near\t\t%s\n", sym->name);
+ pr_err("\t\twhich is near\t\t%s\n", symbol__name(sym));
}
sym = dso__next_symbol(sym);
}
@@ -2755,7 +2755,7 @@ static int find_dso_sym(struct dso *dso, const char *sym_name, u64 *start,
while (sym) {
if (*start) {
if (!*size)
- *size = sym->start - *start;
+ *size = symbol__start(sym) - *start;
if (idx > 0) {
if (*size)
return 0;
@@ -2764,8 +2764,8 @@ static int find_dso_sym(struct dso *dso, const char *sym_name, u64 *start,
return -EINVAL;
}
} else if (dso_sym_match(sym, sym_name, &cnt, idx)) {
- *start = sym->start;
- *size = sym->end - sym->start;
+ *start = symbol__start(sym);
+ *size = symbol__end(sym) - symbol__start(sym);
}
sym = dso__next_symbol(sym);
}
diff --git a/tools/perf/util/block-info.c b/tools/perf/util/block-info.c
index 1135e54f4c7f..5dec6c9dc1a7 100644
--- a/tools/perf/util/block-info.c
+++ b/tools/perf/util/block-info.c
@@ -84,7 +84,7 @@ int64_t __block_info__cmp(struct hist_entry *left, struct hist_entry *right)
return 1;
}
- cmp = strcmp(bi_l->sym->name, bi_r->sym->name);
+ cmp = strcmp(symbol__name(bi_l->sym), symbol__name(bi_r->sym));
if (cmp)
return cmp;
@@ -305,10 +305,11 @@ static int block_range_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
symbol_conf.addr2line_disable_warn = true;
- start_line = map__srcline(he->ms.map, bi->sym->start + bi->start,
+ start_line = map__srcline(he->ms.map,
+ symbol__start(bi->sym) + bi->start,
he->ms.sym);
- end_line = map__srcline(he->ms.map, bi->sym->start + bi->end,
+ end_line = map__srcline(he->ms.map, symbol__start(bi->sym) + bi->end,
he->ms.sym);
if (start_line != SRCLINE_UNKNOWN &&
diff --git a/tools/perf/util/bpf_lock_contention.c b/tools/perf/util/bpf_lock_contention.c
index c20bd075664e..9832c45857d1 100644
--- a/tools/perf/util/bpf_lock_contention.c
+++ b/tools/perf/util/bpf_lock_contention.c
@@ -143,7 +143,8 @@ static void init_numa_data(struct lock_contention *con)
"contig_page_data",
&kmap);
if (sym) {
- skel->rodata->contig_page_data_addr = map__unmap_ip(kmap, sym->start);
+ skel->rodata->contig_page_data_addr = map__unmap_ip(kmap,
+ symbol__start(sym));
map__put(kmap);
return;
}
@@ -159,7 +160,7 @@ static void init_numa_data(struct lock_contention *con)
if (sym == NULL)
return;
- skel->rodata->node_data_addr = map__unmap_ip(kmap, sym->start);
+ skel->rodata->node_data_addr = map__unmap_ip(kmap, symbol__start(sym));
map__put(kmap);
/* get the number of online nodes using the last node number + 1 */
@@ -266,7 +267,8 @@ int lock_contention_prepare(struct lock_contention *con)
continue;
}
- addrs[con->filters->nr_addrs++] = map__unmap_ip(kmap, sym->start);
+ addrs[con->filters->nr_addrs++] = map__unmap_ip(kmap,
+ symbol__start(sym));
con->filters->addrs = addrs;
}
naddrs = con->filters->nr_addrs ?: has_mmap_lock;
@@ -288,7 +290,8 @@ int lock_contention_prepare(struct lock_contention *con)
continue;
}
- con->delays[i].addr = map__unmap_ip(kmap, sym->start);
+ con->delays[i].addr = map__unmap_ip(kmap,
+ symbol__start(sym));
}
skel->rodata->lock_delay = 1;
bpf_map__set_max_entries(skel->maps.lock_delays, con->nr_delays);
@@ -608,7 +611,7 @@ static const char *lock_contention_get_name(struct lock_contention *con,
/* global locks with symbols */
sym = machine__find_kernel_symbol(machine, key->lock_addr_or_cgroup, &kmap);
if (sym)
- return sym->name;
+ return symbol__name(sym);
/* try semi-global locks collected separately */
if (!bpf_map_lookup_elem(lock_fd, &key->lock_addr_or_cgroup, &flags)) {
@@ -652,12 +655,13 @@ static const char *lock_contention_get_name(struct lock_contention *con,
if (sym) {
unsigned long offset;
- offset = map__map_ip(kmap, addr) - sym->start;
+ offset = map__map_ip(kmap, addr) - symbol__start(sym);
if (offset == 0)
- return sym->name;
+ return symbol__name(sym);
- snprintf(name_buf, sizeof(name_buf), "%s+%#lx", sym->name, offset);
+ snprintf(name_buf, sizeof(name_buf), "%s+%#lx",
+ symbol__name(sym), offset);
} else {
snprintf(name_buf, sizeof(name_buf), "%#lx", (unsigned long)addr);
}
diff --git a/tools/perf/util/c2c-function.c b/tools/perf/util/c2c-function.c
index d410196d0429..0c50d816653f 100644
--- a/tools/perf/util/c2c-function.c
+++ b/tools/perf/util/c2c-function.c
@@ -966,7 +966,7 @@ c2c_function_hists__level1_entry(struct symbol *sym,
* true "function view" rather than a per-code-address view.
*/
u64 sym_start = (sym && detail_he->ms.map) ?
- map__unmap_ip(detail_he->ms.map, sym->start) : detail_he->ip;
+ map__unmap_ip(detail_he->ms.map, symbol__start(sym)) : detail_he->ip;
mi = mem_info__new();
if (!mi)
@@ -1058,8 +1058,8 @@ c2c_function_hists__level2_entry(struct c2c_hist_entry *level1_c2c,
/* Key by the function symbol start so all code addresses collapse. */
level2_c2c = c2c_child_entry__alloc(level1_he, detail_he, 1,
(sym && detail_he->ms.map) ?
- map__unmap_ip(detail_he->ms.map, sym->start) :
- hist_entry__iaddr(detail_he));
+ map__unmap_ip(detail_he->ms.map, symbol__start(sym)) :
+ hist_entry__iaddr(detail_he));
if (!level2_c2c)
return NULL;
@@ -1181,8 +1181,8 @@ static void c2c_he__resort_by_stores(struct hist_entry *parent_he)
if (a != b)
cmp = a < b ? -1 : 1;
else if (he->ms.sym && iter->ms.sym)
- cmp = strcmp(he->ms.sym->name,
- iter->ms.sym->name);
+ cmp = strcmp(symbol__name(he->ms.sym),
+ symbol__name(iter->ms.sym));
else
cmp = (iter->ms.sym ? 1 : 0) - (he->ms.sym ? 1 : 0);
@@ -1407,7 +1407,7 @@ static int c2c_function__ident_len(struct hist_entry *he)
* Match symbol_view_entry(): sort_sym adds the cpumode prefix and, in
* verbose mode, the address and DSO origin before the symbol name.
*/
- size = strlen(he->ms.sym->name) + 64;
+ size = strlen(symbol__name(he->ms.sym)) + 64;
symbuf = malloc(size);
if (!symbuf)
return size - 1;
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c
index 31c675cbab63..993b7c744a89 100644
--- a/tools/perf/util/callchain.c
+++ b/tools/perf/util/callchain.c
@@ -802,13 +802,15 @@ static enum match_result match_chain(struct callchain_cursor_node *node,
* on the symbol start address.
*/
if (symbol__inlined(cnode->ms.sym) || symbol__inlined(node->ms.sym)) {
- match = match_chain_strings(cnode->ms.sym->name,
- node->ms.sym->name);
+ match = match_chain_strings(symbol__name(cnode->ms.sym),
+ symbol__name(node->ms.sym));
if (match != MATCH_ERROR)
break;
} else {
- match = match_chain_dso_addresses(cnode->ms.map, cnode->ms.sym->start,
- node->ms.map, node->ms.sym->start);
+ match = match_chain_dso_addresses(cnode->ms.map,
+ symbol__start(cnode->ms.sym),
+ node->ms.map,
+ symbol__start(node->ms.sym));
break;
}
}
@@ -1249,11 +1251,12 @@ char *callchain_list__sym_name(struct callchain_list *cl,
if (show_srcline && cl->srcline)
printed = scnprintf(bf, bfsize, "%s %s%s",
- cl->ms.sym->name, cl->srcline,
+ symbol__name(cl->ms.sym),
+ cl->srcline,
inlined);
else
printed = scnprintf(bf, bfsize, "%s%s",
- cl->ms.sym->name, inlined);
+ symbol__name(cl->ms.sym), inlined);
} else
printed = scnprintf(bf, bfsize, "%#" PRIx64, cl->ip);
diff --git a/tools/perf/util/capstone.c b/tools/perf/util/capstone.c
index 95881f15324a..422a48385b78 100644
--- a/tools/perf/util/capstone.c
+++ b/tools/perf/util/capstone.c
@@ -333,7 +333,7 @@ static struct symbol *find_symbol_from_addr(struct map *map, u64 addr,
return NULL;
}
- *sym_offset = al_addr - sym->start;
+ *sym_offset = al_addr - symbol__start(sym);
return sym;
}
@@ -368,10 +368,10 @@ static void print_capstone_detail(struct cs_insn *insn, char *buf, size_t len,
if (sym_offset == 0) {
scnprintf(buf, len, "\t# %"PRIx64" <%s>",
- orig_addr, sym->name);
+ orig_addr, symbol__name(sym));
} else {
scnprintf(buf, len, "\t# %"PRIx64" <%s+%#"PRIx64">",
- orig_addr, sym->name, sym_offset);
+ orig_addr, symbol__name(sym), sym_offset);
}
map__put(found_map);
break;
@@ -454,10 +454,10 @@ static void format_capstone_insn_arm64(struct cs_insn *insn, char *buf,
len = len - (last_imm - buf);
if (sym_offset == 0) {
scnprintf(last_imm, len, "%"PRIx64" <%s>",
- addr, sym->name);
+ addr, symbol__name(sym));
} else {
scnprintf(last_imm, len, "%"PRIx64" <%s+%#"PRIx64">",
- addr, sym->name, sym_offset);
+ addr, symbol__name(sym), sym_offset);
}
map__put(found_map);
}
@@ -498,7 +498,7 @@ int symbol__disassemble_capstone(const char *filename, struct symbol *sym,
struct annotation *notes = symbol__annotation(sym);
struct map *map = args->ms->map;
struct dso *dso = map__dso(map);
- u64 start = map__rip_2objdump(map, sym->start);
+ u64 start = map__rip_2objdump(map, symbol__start(sym));
u64 offset;
int i, count, free_count;
bool is_64bit = false;
@@ -526,7 +526,7 @@ int symbol__disassemble_capstone(const char *filename, struct symbol *sym,
/* add the function address and name */
scnprintf(disasm_buf, sizeof(disasm_buf), "%#"PRIx64" <%s>:",
- start, sym->name);
+ start, symbol__name(sym));
args->offset = -1;
args->line = disasm_buf;
@@ -631,8 +631,8 @@ int symbol__disassemble_capstone_powerpc(const char *filename __maybe_unused,
struct map *map = args->ms->map;
struct dso *dso = map__dso(map);
struct nscookie nsc;
- u64 start = map__rip_2objdump(map, sym->start);
- u64 end = map__rip_2objdump(map, sym->end);
+ u64 start = map__rip_2objdump(map, symbol__start(sym));
+ u64 end = map__rip_2objdump(map, symbol__end(sym));
u64 len = end - start;
u64 offset;
int i, fd, count;
@@ -703,7 +703,7 @@ int symbol__disassemble_capstone_powerpc(const char *filename __maybe_unused,
/* add the function address and name */
scnprintf(disasm_buf, sizeof(disasm_buf), "%#"PRIx64" <%s>:",
- start, sym->name);
+ start, symbol__name(sym));
args->offset = -1;
args->line = disasm_buf;
diff --git a/tools/perf/util/data-convert-json.c b/tools/perf/util/data-convert-json.c
index 40888b7c4467..c09bd77f5665 100644
--- a/tools/perf/util/data-convert-json.c
+++ b/tools/perf/util/data-convert-json.c
@@ -138,11 +138,11 @@ static void output_sample_callchain_entry(const struct perf_tool *tool,
output_json_format(out, false, 4, "{");
output_json_key_format(out, false, 5, "ip", "\"0x%" PRIx64 "\"", ip);
- if (al && al->sym && al->sym->namelen) {
+ if (al && al->sym && symbol__namelen(al->sym)) {
struct dso *dso = al->map ? map__dso(al->map) : NULL;
fputc(',', out);
- output_json_key_string(out, false, 5, "symbol", al->sym->name);
+ output_json_key_string(out, false, 5, "symbol", symbol__name(al->sym));
if (dso) {
const char *dso_name = dso__short_name(dso);
diff --git a/tools/perf/util/db-export.c b/tools/perf/util/db-export.c
index ba54b1119ee6..732369fe0046 100644
--- a/tools/perf/util/db-export.c
+++ b/tools/perf/util/db-export.c
@@ -198,7 +198,7 @@ static int db_ids_from_al(struct db_export *dbe, struct addr_location *al,
if (err)
return err;
*sym_db_id = *db_id;
- *offset = al->addr - al->sym->start;
+ *offset = al->addr - symbol__start(al->sym);
}
}
diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c
index 6b5ffe81f141..e43bfb741dd1 100644
--- a/tools/perf/util/debug.c
+++ b/tools/perf/util/debug.c
@@ -342,7 +342,8 @@ void __dump_stack(FILE *file, void **stackdump, size_t stackdump_size)
if (thread && thread__find_map(thread, PERF_RECORD_MISC_USER, addr, &al)) {
al.sym = map__find_symbol(al.map, al.addr);
if (al.sym) {
- fprintf(file, " #%zd %p in %s ", i, stackdump[i], al.sym->name);
+ fprintf(file, " #%zd %p in %s ", i, stackdump[i],
+ symbol__name(al.sym));
printed = true;
}
}
diff --git a/tools/perf/util/disasm.c b/tools/perf/util/disasm.c
index 5478c134e7e3..78b8811dcaa4 100644
--- a/tools/perf/util/disasm.c
+++ b/tools/perf/util/disasm.c
@@ -312,7 +312,8 @@ int call__scnprintf(const struct ins *ins, char *bf, size_t size,
struct ins_operands *ops, int max_ins_name)
{
if (ops->target.sym)
- return scnprintf(bf, size, "%-*s %s", max_ins_name, ins->name, ops->target.sym->name);
+ return scnprintf(bf, size, "%-*s %s", max_ins_name, ins->name,
+ symbol__name(ops->target.sym));
if (ops->target.addr == 0)
return ins__raw_scnprintf(ins, bf, size, ops, max_ins_name);
@@ -399,8 +400,8 @@ static int jump__parse(const struct arch *arch, struct ins_operands *ops, struct
}
target.addr = map__objdump_2mem(map, ops->target.addr);
- start = map__unmap_ip(map, sym->start);
- end = map__unmap_ip(map, sym->end);
+ start = map__unmap_ip(map, symbol__start(sym));
+ end = map__unmap_ip(map, symbol__end(sym));
ops->target.outside = target.addr < start || target.addr >= end;
@@ -445,7 +446,8 @@ int jump__scnprintf(const struct ins *ins, char *bf, size_t size,
return ins__raw_scnprintf(ins, bf, size, ops, max_ins_name);
if (ops->target.outside && ops->target.sym != NULL)
- return scnprintf(bf, size, "%-*s %s", max_ins_name, ins->name, ops->target.sym->name);
+ return scnprintf(bf, size, "%-*s %s", max_ins_name, ins->name,
+ symbol__name(ops->target.sym));
c = strchr(ops->raw, ',');
c = validate_comma(c, ops);
@@ -1050,8 +1052,8 @@ static int symbol__parse_objdump_line(struct symbol *sym,
/* Process hex address followed by ':'. */
line_ip = strtoull(parsed_line, &tmp, 16);
if (parsed_line != tmp && tmp[0] == ':' && tmp[1] != '\0') {
- u64 start = map__rip_2objdump(map, sym->start),
- end = map__rip_2objdump(map, sym->end);
+ u64 start = map__rip_2objdump(map, symbol__start(sym)),
+ end = map__rip_2objdump(map, symbol__end(sym));
offset = line_ip - start;
if ((u64)line_ip < start || (u64)line_ip >= end)
@@ -1074,7 +1076,8 @@ static int symbol__parse_objdump_line(struct symbol *sym,
if (!disasm_line__has_local_offset(dl)) {
dl->ops.target.offset = dl->ops.target.addr -
- map__rip_2objdump(map, sym->start);
+ map__rip_2objdump(map,
+ symbol__start(sym));
dl->ops.target.offset_avail = true;
}
@@ -1086,7 +1089,7 @@ static int symbol__parse_objdump_line(struct symbol *sym,
};
if (!maps__find_ams(thread__maps(args->ms->thread), &target) &&
- target.ms.sym->start == target.al_addr)
+ symbol__start(target.ms.sym) == target.al_addr)
dl->ops.target.sym = target.ms.sym;
addr_map_symbol__exit(&target);
@@ -1260,8 +1263,8 @@ static int symbol__disassemble_raw(char *filename, struct symbol *sym,
struct annotation *notes = symbol__annotation(sym);
struct map *map = args->ms->map;
struct dso *dso = map__dso(map);
- u64 start = map__rip_2objdump(map, sym->start);
- u64 end = map__rip_2objdump(map, sym->end);
+ u64 start = map__rip_2objdump(map, symbol__start(sym));
+ u64 end = map__rip_2objdump(map, symbol__end(sym));
u64 len = end - start;
u64 offset;
int i, count;
@@ -1280,7 +1283,7 @@ static int symbol__disassemble_raw(char *filename, struct symbol *sym,
if (buf == NULL)
goto err;
- count = dso__data_read_offset(dso, NULL, sym->start, buf, len);
+ count = dso__data_read_offset(dso, NULL, symbol__start(sym), buf, len);
line = (u32 *)buf;
@@ -1289,7 +1292,7 @@ static int symbol__disassemble_raw(char *filename, struct symbol *sym,
/* add the function address and name */
scnprintf(disasm_buf, sizeof(disasm_buf), "%#"PRIx64" <%s>:",
- start, sym->name);
+ start, symbol__name(sym));
args->offset = -1;
args->line = disasm_buf;
@@ -1454,8 +1457,8 @@ static int symbol__disassemble_objdump(const char *filename, struct symbol *sym,
opts->objdump_path ?: "objdump",
opts->disassembler_style ? "-M " : "",
opts->disassembler_style ?: "",
- map__rip_2objdump(map, sym->start),
- map__rip_2objdump(map, sym->end),
+ map__rip_2objdump(map, symbol__start(sym)),
+ map__rip_2objdump(map, symbol__end(sym)),
opts->show_linenr ? "-l" : "",
opts->show_asm_raw ? "" : "--no-show-raw-insn",
opts->annotate_src ? "-S" : "",
@@ -1576,18 +1579,20 @@ int symbol__disassemble(struct symbol *sym, struct annotate_args *args)
return err;
pr_debug("%s: filename=%s, sym=%s, start=%#" PRIx64 ", end=%#" PRIx64 "\n", __func__,
- symfs_filename, sym->name, map__unmap_ip(map, sym->start),
- map__unmap_ip(map, sym->end));
+ symfs_filename, symbol__name(sym),
+ map__unmap_ip(map, symbol__start(sym)),
+ map__unmap_ip(map, symbol__end(sym)));
- pr_debug("annotating [%p] %30s : [%p] %30s\n", dso, dso__long_name(dso), sym, sym->name);
+ pr_debug("annotating [%p] %30s : [%p] %30s\n", dso, dso__long_name(dso), sym,
+ symbol__name(sym));
if (dso__binary_type(dso) == DSO_BINARY_TYPE__NOT_FOUND) {
return SYMBOL_ANNOTATE_ERRNO__COULDNT_DETERMINE_FILE_TYPE;
} else if (dso__is_kcore(dso)) {
- kce.addr = map__rip_2objdump(map, sym->start);
+ kce.addr = map__rip_2objdump(map, symbol__start(sym));
kce.kcore_filename = symfs_filename;
- kce.len = sym->end - sym->start;
- kce.offs = sym->start;
+ kce.len = symbol__end(sym) - symbol__start(sym);
+ kce.offs = symbol__start(sym);
if (!kcore_extract__create(&kce)) {
delete_extract = true;
diff --git a/tools/perf/util/dlfilter.c b/tools/perf/util/dlfilter.c
index e11e144af62b..3b72cb7b412e 100644
--- a/tools/perf/util/dlfilter.c
+++ b/tools/perf/util/dlfilter.c
@@ -47,13 +47,13 @@ static void al_to_d_al(struct addr_location *al, struct perf_dlfilter_al *d_al)
d_al->buildid = NULL;
}
if (sym) {
- d_al->sym = sym->name;
- d_al->sym_start = sym->start;
- d_al->sym_end = sym->end;
- if (al->addr < sym->end)
- d_al->symoff = al->addr - sym->start;
+ d_al->sym = symbol__name(sym);
+ d_al->sym_start = symbol__start(sym);
+ d_al->sym_end = symbol__end(sym);
+ if (al->addr < symbol__end(sym))
+ d_al->symoff = al->addr - symbol__start(sym);
else if (al->map)
- d_al->symoff = al->addr - map__start(al->map) - sym->start;
+ d_al->symoff = al->addr - map__start(al->map) - symbol__start(sym);
else
d_al->symoff = 0;
d_al->sym_binding = symbol__binding(sym);
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c
index 11db461f2032..39cb82693596 100644
--- a/tools/perf/util/dso.c
+++ b/tools/perf/util/dso.c
@@ -2027,8 +2027,8 @@ const u8 *dso__read_symbol(struct dso *dso, const char *symfs_filename,
const struct map *map, const struct symbol *sym,
u8 **out_buf, u64 *out_buf_len, bool *is_64bit)
{
- u64 start = map__rip_2objdump(map, sym->start);
- u64 end = map__rip_2objdump(map, sym->end);
+ u64 start = map__rip_2objdump(map, symbol__start(sym));
+ u64 end = map__rip_2objdump(map, symbol__end(sym));
size_t len = end - start;
*out_buf = NULL;
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c
index ea75816d126a..939307796a88 100644
--- a/tools/perf/util/event.c
+++ b/tools/perf/util/event.c
@@ -884,11 +884,11 @@ int machine__resolve(struct machine *machine, struct addr_location *al,
if (al->sym) {
ret = strlist__has_entry(symbol_conf.sym_list,
- al->sym->name);
+ symbol__name(al->sym));
}
if (!ret && al->sym) {
snprintf(al_addr_str, sz, "0x%"PRIx64,
- map__unmap_ip(al->map, al->sym->start));
+ map__unmap_ip(al->map, symbol__start(al->sym)));
ret = strlist__has_entry(symbol_conf.sym_list,
al_addr_str);
}
diff --git a/tools/perf/util/evsel_fprintf.c b/tools/perf/util/evsel_fprintf.c
index 0f7a25500a44..76a0c69e6f45 100644
--- a/tools/perf/util/evsel_fprintf.c
+++ b/tools/perf/util/evsel_fprintf.c
@@ -200,7 +200,7 @@ int sample__fprintf_callchain(struct perf_sample *sample, int left_alignment,
/* Add srccode here too? */
if (bt_stop_list && sym &&
- strlist__has_entry(bt_stop_list, sym->name)) {
+ strlist__has_entry(bt_stop_list, symbol__name(sym))) {
break;
}
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c
index 443694926f1a..dd388169c25b 100644
--- a/tools/perf/util/hist.c
+++ b/tools/perf/util/hist.c
@@ -97,7 +97,7 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
* +3 accounts for ' y ' symtab origin info
*/
if (h->ms.sym) {
- symlen = h->ms.sym->namelen + 4;
+ symlen = symbol__namelen(h->ms.sym) + 4;
if (verbose > 0)
symlen += BITS_PER_LONG / 4 + 2 + 3;
hists__new_col_len(hists, HISTC_SYMBOL, symlen);
@@ -120,11 +120,12 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
}
if (h->parent)
- hists__new_col_len(hists, HISTC_PARENT, h->parent->namelen);
+ hists__new_col_len(hists, HISTC_PARENT,
+ symbol__namelen(h->parent));
if (h->branch_info) {
if (h->branch_info->from.ms.sym) {
- symlen = (int)h->branch_info->from.ms.sym->namelen + 4;
+ symlen = (int) symbol__namelen(h->branch_info->from.ms.sym) + 4;
if (verbose > 0)
symlen += BITS_PER_LONG / 4 + 2 + 3;
hists__new_col_len(hists, HISTC_SYMBOL_FROM, symlen);
@@ -139,7 +140,7 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
}
if (h->branch_info->to.ms.sym) {
- symlen = (int)h->branch_info->to.ms.sym->namelen + 4;
+ symlen = (int) symbol__namelen(h->branch_info->to.ms.sym) + 4;
if (verbose > 0)
symlen += BITS_PER_LONG / 4 + 2 + 3;
hists__new_col_len(hists, HISTC_SYMBOL_TO, symlen);
@@ -163,7 +164,7 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
if (h->mem_info) {
if (mem_info__daddr(h->mem_info)->ms.sym) {
- symlen = (int)mem_info__daddr(h->mem_info)->ms.sym->namelen + 4
+ symlen = (int) symbol__namelen(mem_info__daddr(h->mem_info)->ms.sym) + 4
+ unresolved_col_width + 2;
hists__new_col_len(hists, HISTC_MEM_DADDR_SYMBOL,
symlen);
@@ -178,7 +179,7 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
}
if (mem_info__iaddr(h->mem_info)->ms.sym) {
- symlen = (int)mem_info__iaddr(h->mem_info)->ms.sym->namelen + 4
+ symlen = (int) symbol__namelen(mem_info__iaddr(h->mem_info)->ms.sym) + 4
+ unresolved_col_width + 2;
hists__new_col_len(hists, HISTC_MEM_IADDR_SYMBOL,
symlen);
@@ -725,7 +726,7 @@ static struct hist_entry *hists__findnew_entry(struct hists *hists,
*/
if (hists__has(hists, sym) && he->ms.map != entry->ms.map) {
if (he->ms.sym) {
- u64 addr = he->ms.sym->start;
+ u64 addr = symbol__start(he->ms.sym);
he->ms.sym = map__find_symbol(entry->ms.map, addr);
}
@@ -2298,7 +2299,7 @@ static bool hists__filter_entry_by_symbol(struct hists *hists,
struct hist_entry *he)
{
if (hists->symbol_filter_str != NULL &&
- (!he->ms.sym || strstr(he->ms.sym->name,
+ (!he->ms.sym || strstr(symbol__name(he->ms.sym),
hists->symbol_filter_str) == NULL)) {
he->filtered |= (1 << HIST_FILTER__SYMBOL);
return true;
diff --git a/tools/perf/util/intel-pt.c b/tools/perf/util/intel-pt.c
index 8c21c9f52d57..a9f43ac8d152 100644
--- a/tools/perf/util/intel-pt.c
+++ b/tools/perf/util/intel-pt.c
@@ -3003,8 +3003,8 @@ static u64 intel_pt_switch_ip(struct intel_pt *pt, u64 *ptss_ip)
for (sym = start; sym; sym = dso__next_symbol(sym)) {
if (symbol__binding(sym) == STB_GLOBAL &&
- !strcmp(sym->name, "__switch_to")) {
- ip = map__unmap_ip(map, sym->start);
+ !strcmp(symbol__name(sym), "__switch_to")) {
+ ip = map__unmap_ip(map, symbol__start(sym));
if (ip >= map__start(map) && ip < map__end(map)) {
switch_ip = ip;
break;
@@ -3021,8 +3021,8 @@ static u64 intel_pt_switch_ip(struct intel_pt *pt, u64 *ptss_ip)
ptss = "__perf_event_task_sched_out";
for (sym = start; sym; sym = dso__next_symbol(sym)) {
- if (!strcmp(sym->name, ptss)) {
- ip = map__unmap_ip(map, sym->start);
+ if (!strcmp(symbol__name(sym), ptss)) {
+ ip = map__unmap_ip(map, symbol__start(sym));
if (ip >= map__start(map) && ip < map__end(map)) {
*ptss_ip = ip;
break;
diff --git a/tools/perf/util/libbfd.c b/tools/perf/util/libbfd.c
index a907d7c1ac94..d4b496bb9426 100644
--- a/tools/perf/util/libbfd.c
+++ b/tools/perf/util/libbfd.c
@@ -552,7 +552,7 @@ int symbol__disassemble_bpf_libbfd(struct symbol *sym __maybe_unused,
struct annotation *notes = symbol__annotation(sym);
struct bpf_prog_linfo *prog_linfo = NULL;
struct bpf_prog_info_node *info_node;
- int len = sym->end - sym->start;
+ int len = symbol__end(sym) - symbol__start(sym);
disassembler_ftype disassemble;
struct map *map = args->ms->map;
struct perf_bpil *info_linear;
@@ -572,7 +572,8 @@ int symbol__disassemble_bpf_libbfd(struct symbol *sym __maybe_unused,
return SYMBOL_ANNOTATE_ERRNO__BPF_INVALID_FILE;
pr_debug("%s: handling sym %s addr %" PRIx64 " len %" PRIx64 "\n", __func__,
- sym->name, sym->start, sym->end - sym->start);
+ symbol__name(sym), symbol__start(sym),
+ symbol__end(sym) - symbol__start(sym));
memset(tpath, 0, sizeof(tpath));
perf_exe(tpath, sizeof(tpath));
diff --git a/tools/perf/util/llvm.c b/tools/perf/util/llvm.c
index 722e71aab100..e90b0d55070e 100644
--- a/tools/perf/util/llvm.c
+++ b/tools/perf/util/llvm.c
@@ -132,7 +132,7 @@ int symbol__disassemble_llvm(const char *filename, struct symbol *sym,
struct annotation *notes = symbol__annotation(sym);
struct map *map = args->ms->map;
struct dso *dso = map__dso(map);
- u64 start = map__rip_2objdump(map, sym->start);
+ u64 start = map__rip_2objdump(map, symbol__start(sym));
/* Malloc-ed buffer containing instructions read from disk. */
u8 *code_buf = NULL;
/* Pointer to code to be disassembled. */
@@ -196,7 +196,7 @@ int symbol__disassemble_llvm(const char *filename, struct symbol *sym,
/* add the function address and name */
scnprintf(disasm_buf, sizeof(disasm_buf), "%#"PRIx64" <%s>:",
- start, sym->name);
+ start, symbol__name(sym));
args->offset = -1;
args->line = disasm_buf;
@@ -317,7 +317,7 @@ int symbol__disassemble_llvm(const char *filename, struct symbol *sym,
return ret;
#else // HAVE_LIBLLVM_SUPPORT
pr_debug("The LLVM disassembler isn't linked in for %s in %s\n",
- sym->name, filename);
+ symbol__name(sym), filename);
return -1;
#endif
}
diff --git a/tools/perf/util/lock-contention.c b/tools/perf/util/lock-contention.c
index 119a7206f3cd..8ee1b19a8292 100644
--- a/tools/perf/util/lock-contention.c
+++ b/tools/perf/util/lock-contention.c
@@ -135,7 +135,7 @@ bool match_callstack_filter(struct machine *machine, u64 *callstack, int max_sta
continue;
list_for_each_entry(filter, &callstack_filters, list) {
- if (strstr(sym->name, filter->name))
+ if (strstr(symbol__name(sym), filter->name))
return true;
}
}
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index 1d9d3bf57720..fa2bd8e0e1f1 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -1110,8 +1110,8 @@ static u64 find_entry_trampoline(struct dso *dso)
if (symbol__binding(sym) != STB_GLOBAL)
continue;
for (i = 0; i < ARRAY_SIZE(syms); i++) {
- if (!strcmp(sym->name, syms[i]))
- return sym->start;
+ if (!strcmp(symbol__name(sym), syms[i]))
+ return symbol__start(sym);
}
}
@@ -2056,7 +2056,7 @@ int machine__process_event(struct machine *machine, union perf_event *event,
static bool symbol__match_regex(struct symbol *sym, regex_t *regex)
{
- return regexec(regex, sym->name, 0, NULL, 0) == 0;
+ return regexec(regex, symbol__name(sym), 0, NULL, 0) == 0;
}
static void ip__resolve_ams(struct thread *thread,
@@ -3283,8 +3283,8 @@ char *machine__resolve_kernel_addr(void *vmachine, unsigned long long *addrp, ch
return NULL;
*modp = __map__is_kmodule(map) ? (char *)dso__short_name(map__dso(map)) : NULL;
- *addrp = map__unmap_ip(map, sym->start);
- return sym->name;
+ *addrp = map__unmap_ip(map, symbol__start(sym));
+ return (char *)symbol__name(sym);
}
struct machine__for_each_dso_cb_args {
@@ -3330,27 +3330,35 @@ bool machine__is_lock_function(struct machine *machine, u64 addr)
return false;
}
- machine->sched.text_start = map__unmap_ip(kmap, sym->start);
+ machine->sched.text_start = map__unmap_ip(kmap,
+ symbol__start(sym));
/* should not fail from here */
sym = machine__find_kernel_symbol_by_name(machine, "__sched_text_end", &kmap);
- machine->sched.text_end = map__unmap_ip(kmap, sym->start);
+ machine->sched.text_end = map__unmap_ip(kmap,
+ symbol__start(sym));
sym = machine__find_kernel_symbol_by_name(machine, "__lock_text_start", &kmap);
- machine->lock.text_start = map__unmap_ip(kmap, sym->start);
+ machine->lock.text_start = map__unmap_ip(kmap,
+ symbol__start(sym));
sym = machine__find_kernel_symbol_by_name(machine, "__lock_text_end", &kmap);
- machine->lock.text_end = map__unmap_ip(kmap, sym->start);
+ machine->lock.text_end = map__unmap_ip(kmap,
+ symbol__start(sym));
sym = machine__find_kernel_symbol_by_name(machine, "__traceiter_contention_begin", &kmap);
if (sym) {
- machine->traceiter.text_start = map__unmap_ip(kmap, sym->start);
- machine->traceiter.text_end = map__unmap_ip(kmap, sym->end);
+ machine->traceiter.text_start = map__unmap_ip(kmap,
+ symbol__start(sym));
+ machine->traceiter.text_end = map__unmap_ip(kmap,
+ symbol__end(sym));
}
sym = machine__find_kernel_symbol_by_name(machine, "trace_contention_begin", &kmap);
if (sym) {
- machine->trace.text_start = map__unmap_ip(kmap, sym->start);
- machine->trace.text_end = map__unmap_ip(kmap, sym->end);
+ machine->trace.text_start = map__unmap_ip(kmap,
+ symbol__start(sym));
+ machine->trace.text_end = map__unmap_ip(kmap,
+ symbol__end(sym));
}
}
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c
index 41cdddc987ee..85216fa71f1a 100644
--- a/tools/perf/util/map.c
+++ b/tools/perf/util/map.c
@@ -321,7 +321,7 @@ void map__fixup_start(struct map *map)
if (nd != NULL) {
struct symbol *sym = rb_entry(nd, struct symbol, rb_node);
- map__set_start(map, sym->start);
+ map__set_start(map, symbol__start(sym));
}
}
@@ -333,7 +333,7 @@ void map__fixup_end(struct map *map)
if (nd != NULL) {
struct symbol *sym = rb_entry(nd, struct symbol, rb_node);
- map__set_end(map, sym->end);
+ map__set_end(map, symbol__end(sym));
}
}
@@ -606,7 +606,7 @@ u64 map__objdump_2rip(const struct map *map, u64 ip)
bool map__contains_symbol(const struct map *map, const struct symbol *sym)
{
- u64 ip = map__unmap_ip(map, sym->start);
+ u64 ip = map__unmap_ip(map, symbol__start(sym));
return ip >= map__start(map) && ip < map__end(map);
}
diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h
index fb0279810ae9..f49b4aaacf2f 100644
--- a/tools/perf/util/map.h
+++ b/tools/perf/util/map.h
@@ -164,7 +164,7 @@ struct thread;
#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__match_symbol_name(symbol__name(pos), sym_name, \
SYMBOL_TAG_INCLUDE__DEFAULT_ONLY); \
pos = dso__next_symbol_by_name(map__dso(map), &idx))
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 11ae4a09412c..128076a4b92d 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -149,7 +149,7 @@ static int kernel_get_symbol_address_by_name(const char *name, u64 *addr,
sym = machine__find_kernel_symbol_by_name(host_machine, name, &map);
if (!sym)
return -ENOENT;
- *addr = map__unmap_ip(map, sym->start) -
+ *addr = map__unmap_ip(map, symbol__start(sym)) -
((reloc) ? 0 : map__reloc(map)) -
((reladdr) ? map__start(map) : 0);
}
@@ -415,13 +415,13 @@ static int find_alternative_probe_point(struct debuginfo *dinfo,
/* Find the address of given function */
map__for_each_symbol_by_name(map, pp->function, sym, idx) {
if (uprobes) {
- address = sym->start;
+ address = symbol__start(sym);
if (symbol__type(sym) == STT_GNU_IFUNC)
pr_warning("Warning: The probe function (%s) is a GNU indirect function.\n"
"Consider identifying the final function used at run time and set the probe directly on that.\n",
pp->function);
} else
- address = map__unmap_ip(map, sym->start) - map__reloc(map);
+ address = map__unmap_ip(map, symbol__start(sym)) - map__reloc(map);
break;
}
if (!address) {
@@ -721,17 +721,17 @@ static int post_process_probe_trace_point(struct probe_trace_point *tp,
return (symbol_conf.ignore_vmlinux_buildid) ? 0 : -ENOENT;
}
- if (strcmp(sym->name, tp->symbol)) {
+ if (strcmp(symbol__name(sym), tp->symbol)) {
/* If we have no realname, use symbol for it */
if (!tp->realname)
tp->realname = tp->symbol;
else
free(tp->symbol);
- tp->symbol = strdup(sym->name);
+ tp->symbol = strdup(symbol__name(sym));
if (!tp->symbol)
return -ENOMEM;
}
- tp->offset = addr - sym->start;
+ tp->offset = addr - symbol__start(sym);
tp->address -= offs;
return 0;
@@ -2309,8 +2309,8 @@ static int find_perf_probe_point_from_map(struct probe_trace_point *tp,
goto out;
pp->retprobe = tp->retprobe;
- pp->offset = addr - map__unmap_ip(map, sym->start);
- pp->function = strdup(sym->name);
+ pp->offset = addr - map__unmap_ip(map, symbol__start(sym));
+ pp->function = strdup(symbol__name(sym));
ret = pp->function ? 0 : -ENOMEM;
out:
@@ -3065,7 +3065,7 @@ static int find_probe_functions(struct map *map, char *name,
cut_version = false;
map__for_each_symbol(map, sym, tmp) {
- norm = arch__normalize_symbol_name(sym->name);
+ norm = arch__normalize_symbol_name(symbol__name(sym));
if (!norm)
continue;
@@ -3194,9 +3194,10 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev,
/* There can be duplicated symbols in the map */
for (i = 0; i < j; i++)
- if (sym->start == syms[i]->start) {
+ if (symbol__start(sym) == symbol__start(syms[i])) {
pr_debug("Found duplicated symbol %s @ %" PRIx64 "\n",
- sym->name, sym->start);
+ symbol__name(sym),
+ symbol__start(sym));
break;
}
if (i != j)
@@ -3210,28 +3211,29 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev,
}
ret++;
- if (pp->offset > sym->end - sym->start) {
+ if (pp->offset > symbol__end(sym) - symbol__start(sym)) {
pr_warning("Offset %ld is bigger than the size of %s\n",
- pp->offset, sym->name);
+ pp->offset, symbol__name(sym));
ret = -ENOENT;
goto err_out;
}
/* Add one probe point */
- tp->address = map__unmap_ip(map, sym->start) + pp->offset;
+ tp->address = map__unmap_ip(map, symbol__start(sym)) + pp->offset;
/* Check the kprobe (not in module) is within .text */
if (!pev->uprobes && !pev->target &&
- kprobe_warn_out_range(sym->name, tp->address)) {
+ kprobe_warn_out_range(symbol__name(sym), tp->address)) {
tp->symbol = NULL; /* Skip it */
skipped++;
} else if (reloc_sym) {
tp->symbol = strdup_or_goto(reloc_sym->name, nomem_out);
tp->offset = tp->address - reloc_sym->addr;
} else {
- tp->symbol = strdup_or_goto(sym->name, nomem_out);
+ tp->symbol = strdup_or_goto(symbol__name(sym),
+ nomem_out);
tp->offset = pp->offset;
}
- tp->realname = strdup_or_goto(sym->name, nomem_out);
+ tp->realname = strdup_or_goto(symbol__name(sym), nomem_out);
tp->retprobe = pp->retprobe;
if (pev->target) {
@@ -3813,8 +3815,8 @@ int show_available_funcs(const char *target, struct nsinfo *nsi,
for (size_t i = 0; i < dso__symbol_names_len(dso); i++) {
struct symbol *pos = dso__symbol_names(dso)[i];
- if (strfilter__compare(_filter, pos->name))
- printf("%s\n", pos->name);
+ if (strfilter__compare(_filter, symbol__name(pos)))
+ printf("%s\n", symbol__name(pos));
}
end:
map__put(map);
diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c
index d8a842621cda..d47f6865021e 100644
--- a/tools/perf/util/python.c
+++ b/tools/perf/util/python.c
@@ -759,7 +759,7 @@ static PyObject *pyrf_sample_event__get_symbol(struct pyrf_event *pevent,
if (pyrf_sample_event__resolve_al(pevent) < 0 || !pevent->al.sym)
Py_RETURN_NONE;
- return PyUnicode_FromString(pevent->al.sym->name);
+ return PyUnicode_FromString(symbol__name(pevent->al.sym));
}
static PyObject *pyrf_sample_event__get_sym_start(struct pyrf_event *pevent,
@@ -768,7 +768,7 @@ static PyObject *pyrf_sample_event__get_sym_start(struct pyrf_event *pevent,
if (pyrf_sample_event__resolve_al(pevent) < 0 || !pevent->al.sym)
Py_RETURN_NONE;
- return PyLong_FromUnsignedLongLong(pevent->al.sym->start);
+ return PyLong_FromUnsignedLongLong(symbol__start(pevent->al.sym));
}
static PyObject *pyrf_sample_event__get_sym_end(struct pyrf_event *pevent,
@@ -777,7 +777,7 @@ static PyObject *pyrf_sample_event__get_sym_end(struct pyrf_event *pevent,
if (pyrf_sample_event__resolve_al(pevent) < 0 || !pevent->al.sym)
Py_RETURN_NONE;
- return PyLong_FromUnsignedLongLong(pevent->al.sym->end);
+ return PyLong_FromUnsignedLongLong(symbol__end(pevent->al.sym));
}
static PyObject *pyrf_sample_event__get_raw_buf(struct pyrf_event *pevent,
@@ -890,7 +890,7 @@ static PyObject *pyrf_callchain_node__get_symbol(struct pyrf_callchain_node *pno
void *closure __maybe_unused)
{
if (pnode->sym)
- return PyUnicode_FromString(pnode->sym->name);
+ return PyUnicode_FromString(symbol__name(pnode->sym));
return PyUnicode_FromString("[unknown]");
}
diff --git a/tools/perf/util/scripting-engines/trace-event-perl.c b/tools/perf/util/scripting-engines/trace-event-perl.c
index 410dc4cd0600..bcb4f0f6a173 100644
--- a/tools/perf/util/scripting-engines/trace-event-perl.c
+++ b/tools/perf/util/scripting-engines/trace-event-perl.c
@@ -301,11 +301,11 @@ static SV *perl_process_callchain(struct perf_sample *sample,
hv_undef(elem);
goto exit;
}
- if (!hv_stores(sym, "start", newSVuv(node->ms.sym->start)) ||
- !hv_stores(sym, "end", newSVuv(node->ms.sym->end)) ||
+ if (!hv_stores(sym, "start", newSVuv(symbol__start(node->ms.sym))) ||
+ !hv_stores(sym, "end", newSVuv(symbol__end(node->ms.sym))) ||
!hv_stores(sym, "binding", newSVuv(symbol__binding(node->ms.sym))) ||
- !hv_stores(sym, "name", newSVpvn(node->ms.sym->name,
- node->ms.sym->namelen)) ||
+ !hv_stores(sym, "name", newSVpvn(symbol__name(node->ms.sym),
+ symbol__namelen(node->ms.sym))) ||
!hv_stores(elem, "sym", newRV_noinc((SV*)sym))) {
hv_undef(sym);
hv_undef(elem);
diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c
index 038c4268a429..408b5645b33d 100644
--- a/tools/perf/util/scripting-engines/trace-event-python.c
+++ b/tools/perf/util/scripting-engines/trace-event-python.c
@@ -382,10 +382,10 @@ static unsigned long get_offset(struct symbol *sym, struct addr_location *al)
{
unsigned long offset;
- if (al->addr < sym->end)
- offset = al->addr - sym->start;
+ if (al->addr < symbol__end(sym))
+ offset = al->addr - symbol__start(sym);
else
- offset = al->addr - map__start(al->map) - sym->start;
+ offset = al->addr - map__start(al->map) - symbol__start(sym);
return offset;
}
@@ -433,14 +433,14 @@ static PyObject *python_process_callchain(struct perf_sample *sample,
if (!pysym)
Py_FatalError("couldn't create Python dictionary");
pydict_set_item_string_decref(pysym, "start",
- PyLong_FromUnsignedLongLong(node->ms.sym->start));
+ PyLong_FromUnsignedLongLong(symbol__start(node->ms.sym)));
pydict_set_item_string_decref(pysym, "end",
- PyLong_FromUnsignedLongLong(node->ms.sym->end));
+ PyLong_FromUnsignedLongLong(symbol__end(node->ms.sym)));
pydict_set_item_string_decref(pysym, "binding",
_PyLong_FromLong(symbol__binding(node->ms.sym)));
pydict_set_item_string_decref(pysym, "name",
- _PyUnicode_FromStringAndSize(node->ms.sym->name,
- node->ms.sym->namelen));
+ _PyUnicode_FromStringAndSize(symbol__name(node->ms.sym),
+ symbol__namelen(node->ms.sym)));
pydict_set_item_string_decref(pyelem, "sym", pysym);
if (node->ms.map) {
@@ -547,15 +547,15 @@ static int get_symoff(struct symbol *sym, struct addr_location *al,
{
unsigned long offset;
- if (!sym || !sym->name[0])
+ if (!sym || !symbol__name(sym)[0])
return scnprintf(bf, size, "%s", "[unknown]");
if (!print_off)
- return scnprintf(bf, size, "%s", sym->name);
+ return scnprintf(bf, size, "%s", symbol__name(sym));
offset = get_offset(sym, al);
- return scnprintf(bf, size, "%s+0x%x", sym->name, offset);
+ return scnprintf(bf, size, "%s+0x%x", symbol__name(sym), offset);
}
static int get_br_mspred(struct branch_flags *flags, char *bf, int size)
@@ -908,7 +908,7 @@ static void set_sym_in_dict(PyObject *dict, struct addr_location *al,
}
if (al->sym) {
pydict_set_item_string_decref(dict, sym_field,
- _PyUnicode_FromString(al->sym->name));
+ _PyUnicode_FromString(symbol__name(al->sym)));
pydict_set_item_string_decref(dict, symoff_field,
PyLong_FromUnsignedLong(get_offset(al->sym, al)));
}
@@ -1379,10 +1379,10 @@ static int python_export_symbol(struct db_export *dbe, struct symbol *sym,
tuple_set_d64(t, 0, *sym_db_id);
tuple_set_d64(t, 1, dso__db_id(dso));
- tuple_set_d64(t, 2, sym->start);
- tuple_set_d64(t, 3, sym->end);
+ tuple_set_d64(t, 2, symbol__start(sym));
+ tuple_set_d64(t, 3, symbol__end(sym));
tuple_set_s32(t, 4, symbol__binding(sym));
- tuple_set_string(t, 5, sym->name);
+ tuple_set_string(t, 5, symbol__name(sym));
call_object(tables->symbol_handler, t, "symbol_table");
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c
index 58638ec9ae0e..89e833f35fd2 100644
--- a/tools/perf/util/sort.c
+++ b/tools/perf/util/sort.c
@@ -470,18 +470,19 @@ int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r)
return 0;
if (symbol__inlined(sym_l) || symbol__inlined(sym_r)) {
- int ret = strcmp(sym_l->name, sym_r->name);
+ int ret = strcmp(symbol__name(sym_l), symbol__name(sym_r));
if (ret)
return ret;
- if ((sym_l->start <= sym_r->end) && (sym_l->end >= sym_r->start))
+ if ((symbol__start(sym_l) <= symbol__end(sym_r)) &&
+ (symbol__end(sym_l) >= symbol__start(sym_r)))
return 0;
}
- if (sym_l->start != sym_r->start)
- return (int64_t)(sym_r->start - sym_l->start);
+ if (symbol__start(sym_l) != symbol__start(sym_r))
+ return (int64_t)(symbol__start(sym_r) - symbol__start(sym_l));
- return (int64_t)(sym_r->end - sym_l->end);
+ return (int64_t)(symbol__end(sym_r) - symbol__end(sym_l));
}
static int64_t
@@ -511,7 +512,7 @@ sort__sym_sort(struct hist_entry *left, struct hist_entry *right)
if (!left->ms.sym || !right->ms.sym)
return cmp_null(left->ms.sym, right->ms.sym);
- return strcmp(right->ms.sym->name, left->ms.sym->name);
+ return strcmp(symbol__name(right->ms.sym), symbol__name(left->ms.sym));
}
static int _hist_entry__sym_snprintf(struct map_symbol *ms,
@@ -537,13 +538,14 @@ static int _hist_entry__sym_snprintf(struct map_symbol *ms,
ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", level);
if (sym && map) {
if (symbol__type(sym) == STT_OBJECT) {
- ret += repsep_snprintf(bf + ret, size - ret, "%s", sym->name);
+ ret += repsep_snprintf(bf + ret, size - ret, "%s",
+ symbol__name(sym));
ret += repsep_snprintf(bf + ret, size - ret, "+0x%llx",
- ip - map__unmap_ip(map, sym->start));
+ ip - map__unmap_ip(map, symbol__start(sym)));
} else {
ret += repsep_snprintf(bf + ret, size - ret, "%.*s",
width - ret,
- sym->name);
+ symbol__name(sym));
if (symbol__inlined(sym))
ret += repsep_snprintf(bf + ret, size - ret,
" (inlined)");
@@ -570,7 +572,7 @@ static int hist_entry__sym_filter(struct hist_entry *he, int type, const void *a
if (type != HIST_FILTER__SYMBOL)
return -1;
- return sym && (!he->ms.sym || !strstr(he->ms.sym->name, sym));
+ return sym && (!he->ms.sym || !strstr(symbol__name(he->ms.sym), sym));
}
struct sort_entry sort_sym = {
@@ -616,7 +618,8 @@ hist_entry__symoff_snprintf(struct hist_entry *he, char *bf, size_t size, unsign
if (sym == NULL)
return repsep_snprintf(bf, size, "[%c] %-#.*llx", he->level, width - 4, he->ip);
- return repsep_snprintf(bf, size, "[%c] %s+0x%llx", he->level, sym->name, he->ip - sym->start);
+ return repsep_snprintf(bf, size, "[%c] %s+0x%llx", he->level,
+ symbol__name(sym), he->ip - symbol__start(sym));
}
static struct sort_entry sort_sym_offset = {
@@ -1016,14 +1019,14 @@ sort__parent_cmp(struct hist_entry *left, struct hist_entry *right)
if (!sym_l || !sym_r)
return cmp_null(sym_l, sym_r);
- return strcmp(sym_r->name, sym_l->name);
+ return strcmp(symbol__name(sym_r), symbol__name(sym_l));
}
static int hist_entry__parent_snprintf(struct hist_entry *he, char *bf,
size_t size, unsigned int width)
{
return repsep_snprintf(bf, size, "%-*.*s", width, width,
- he->parent ? he->parent->name : "[other]");
+ he->parent ? symbol__name(he->parent) : "[other]");
}
struct sort_entry sort_parent = {
@@ -1426,7 +1429,7 @@ static int hist_entry__sym_from_filter(struct hist_entry *he, int type,
return -1;
return sym && !(he->branch_info && he->branch_info->from.ms.sym &&
- strstr(he->branch_info->from.ms.sym->name, sym));
+ strstr(symbol__name(he->branch_info->from.ms.sym), sym));
}
static int hist_entry__sym_to_filter(struct hist_entry *he, int type,
@@ -1438,7 +1441,7 @@ static int hist_entry__sym_to_filter(struct hist_entry *he, int type,
return -1;
return sym && !(he->branch_info && he->branch_info->to.ms.sym &&
- strstr(he->branch_info->to.ms.sym->name, sym));
+ strstr(symbol__name(he->branch_info->to.ms.sym), sym));
}
struct sort_entry sort_dso_from = {
@@ -1484,14 +1487,15 @@ static int _hist_entry__addr_snprintf(struct map_symbol *ms,
ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", level);
if (sym && map) {
if (symbol__type(sym) == STT_OBJECT) {
- ret += repsep_snprintf(bf + ret, size - ret, "%s", sym->name);
+ ret += repsep_snprintf(bf + ret, size - ret, "%s",
+ symbol__name(sym));
ret += repsep_snprintf(bf + ret, size - ret, "+0x%llx",
- ip - map__unmap_ip(map, sym->start));
+ ip - map__unmap_ip(map, symbol__start(sym)));
} else {
ret += repsep_snprintf(bf + ret, size - ret, "%.*s",
width - ret,
- sym->name);
- offs = ip - sym->start;
+ symbol__name(sym));
+ offs = ip - symbol__start(sym);
if (offs)
ret += repsep_snprintf(bf + ret, size - ret, "+0x%llx", offs);
}
diff --git a/tools/perf/util/srcline.c b/tools/perf/util/srcline.c
index a8d16106f619..53976de3105c 100644
--- a/tools/perf/util/srcline.c
+++ b/tools/perf/util/srcline.c
@@ -116,7 +116,7 @@ struct symbol *new_inline_sym(struct dso *dso,
funcname = demangled;
}
- if (base_sym && strcmp(funcname, base_sym->name) == 0) {
+ if (base_sym && strcmp(funcname, symbol__name(base_sym)) == 0) {
/* reuse the real, existing symbol */
inline_sym = base_sym;
/* ensure that we don't alias an inlined symbol, which could
@@ -125,8 +125,9 @@ struct symbol *new_inline_sym(struct dso *dso,
assert(!symbol__inlined(base_sym));
} else {
/* create a fake symbol for the inline frame */
- inline_sym = symbol__new(base_sym ? base_sym->start : 0,
- base_sym ? (base_sym->end - base_sym->start) : 0,
+ inline_sym = symbol__new(base_sym ? symbol__start(base_sym) : 0,
+ base_sym ?
+ (symbol__end(base_sym) - symbol__start(base_sym)) : 0,
base_sym ? symbol__binding(base_sym) : 0,
base_sym ? symbol__type(base_sym) : 0,
funcname);
@@ -307,11 +308,11 @@ char *__get_srcline(struct dso *dso, u64 addr, struct symbol *sym,
out:
if (!show_addr)
return (show_sym && sym) ?
- strndup(sym->name, sym->namelen) : SRCLINE_UNKNOWN;
+ strndup(symbol__name(sym), symbol__namelen(sym)) : SRCLINE_UNKNOWN;
if (sym) {
- if (asprintf(&srcline, "%s+%" PRIu64, show_sym ? sym->name : "",
- ip - sym->start) < 0)
+ if (asprintf(&srcline, "%s+%" PRIu64, show_sym ? symbol__name(sym) : "",
+ ip - symbol__start(sym)) < 0)
return SRCLINE_UNKNOWN;
} else if (asprintf(&srcline, "%s[%" PRIx64 "]", dso__short_name(dso), addr) < 0)
return SRCLINE_UNKNOWN;
diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c
index e955c3feddcd..73885413073b 100644
--- a/tools/perf/util/symbol-elf.c
+++ b/tools/perf/util/symbol-elf.c
@@ -350,11 +350,11 @@ static bool get_ifunc_name(Elf *elf, struct dso *dso, GElf_Ehdr *ehdr,
sym = dso__find_symbol_nocache(dso, addr);
/* Expecting the address to be an IFUNC or IFUNC alias */
- if (!sym || sym->start != addr ||
+ if (!sym || symbol__start(sym) != addr ||
(symbol__type(sym) != STT_GNU_IFUNC && !symbol__ifunc_alias(sym)))
return false;
- snprintf(buf, buf_sz, "%s@plt", sym->name);
+ snprintf(buf, buf_sz, "%s@plt", symbol__name(sym));
return true;
}
@@ -629,8 +629,8 @@ int dso__synthesize_plt_symbols(struct dso *dso, struct symsrc *ss)
* a symbol for .plt header.
*/
f = dso__find_symbol_nocache(dso, shdr_plt.sh_offset);
- if (f && f->start < shdr_plt.sh_offset && f->end > shdr_plt.sh_offset)
- f->end = shdr_plt.sh_offset;
+ if (f && symbol__start(f) < shdr_plt.sh_offset && symbol__end(f) > shdr_plt.sh_offset)
+ symbol__set_end(f, shdr_plt.sh_offset);
if (!get_plt_sizes(dso, &ehdr, &shdr_plt, &plt_header_size, &plt_entry_size))
return 0;
@@ -652,7 +652,8 @@ int dso__synthesize_plt_symbols(struct dso *dso, struct symsrc *ss)
if (!get_plt_sizes(dso, &ehdr, &plt_sec_shdr, &plt_header_size, &plt_entry_size))
return 0;
/* Extend .plt symbol to entire .plt */
- plt_sym->end = plt_sym->start + shdr_plt.sh_size;
+ symbol__set_end(plt_sym,
+ symbol__start(plt_sym) + shdr_plt.sh_size);
/* Use .plt.sec offset */
plt_offset = plt_sec_shdr.sh_offset;
lazy_plt = false;
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 163652f071c6..8e264d38b981 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -149,9 +149,9 @@ int __weak arch__choose_best_symbol(struct symbol *syma,
struct symbol *symb __maybe_unused)
{
/* Avoid "SyS" kernel syscall aliases */
- if (strlen(syma->name) >= 3 && !strncmp(syma->name, "SyS", 3))
+ if (strlen(symbol__name(syma)) >= 3 && !strncmp(symbol__name(syma), "SyS", 3))
return SYMBOL_B;
- if (strlen(syma->name) >= 10 && !strncmp(syma->name, "compat_SyS", 10))
+ if (strlen(symbol__name(syma)) >= 10 && !strncmp(symbol__name(syma), "compat_SyS", 10))
return SYMBOL_B;
return SYMBOL_A;
@@ -164,8 +164,8 @@ static int choose_best_symbol(struct symbol *syma, struct symbol *symb)
size_t na, nb;
/* Prefer a symbol with non zero length */
- a = syma->end - syma->start;
- b = symb->end - symb->start;
+ a = symbol__end(syma) - symbol__start(syma);
+ b = symbol__end(symb) - symbol__start(symb);
if ((b == 0) && (a > 0))
return SYMBOL_A;
else if ((a == 0) && (b > 0))
@@ -195,16 +195,16 @@ static int choose_best_symbol(struct symbol *syma, struct symbol *symb)
return SYMBOL_B;
/* Prefer a symbol with less underscores */
- a = prefix_underscores_count(syma->name);
- b = prefix_underscores_count(symb->name);
+ a = prefix_underscores_count(symbol__name(syma));
+ b = prefix_underscores_count(symbol__name(symb));
if (b > a)
return SYMBOL_A;
else if (a > b)
return SYMBOL_B;
/* Choose the symbol with the longest name */
- na = strlen(syma->name);
- nb = strlen(symb->name);
+ na = strlen(symbol__name(syma));
+ nb = strlen(symbol__name(symb));
if (na > nb)
return SYMBOL_A;
else if (na < nb)
@@ -231,7 +231,7 @@ void symbols__fixup_duplicate(struct rb_root_cached *symbols)
break;
next = rb_entry(nd, struct symbol, rb_node);
- if (curr->start != next->start)
+ if (symbol__start(curr) != symbol__start(next))
continue;
if (choose_best_symbol(curr, next) == SYMBOL_A) {
@@ -281,35 +281,39 @@ void symbols__fixup_end(struct rb_root_cached *symbols, bool is_kallsyms)
* like in:
* ffffffffc1937000 T hdmi_driver_init [snd_hda_codec_hdmi]
*/
- if (prev->end == prev->start) {
+ if (symbol__end(prev) == symbol__start(prev)) {
const char *prev_mod;
const char *curr_mod;
if (!is_kallsyms) {
- prev->end = curr->start;
+ symbol__set_end(prev, symbol__start(curr));
continue;
}
- prev_mod = strchr(prev->name, '[');
- curr_mod = strchr(curr->name, '[');
+ prev_mod = strchr(symbol__name(prev), '[');
+ curr_mod = strchr(symbol__name(curr), '[');
/* Last kernel/module symbol mapped to end of page */
if (!prev_mod != !curr_mod)
- prev->end = roundup(prev->end + 4096, 4096);
+ symbol__set_end(prev,
+ roundup(symbol__end(prev) + 4096, 4096));
/* Last symbol in the previous module */
else if (prev_mod && strcmp(prev_mod, curr_mod))
- prev->end = roundup(prev->end + 4096, 4096);
+ symbol__set_end(prev,
+ roundup(symbol__end(prev) + 4096, 4096));
else
- prev->end = curr->start;
+ symbol__set_end(prev, symbol__start(curr));
pr_debug4("%s sym:%s end:%#" PRIx64 "\n",
- __func__, prev->name, prev->end);
+ __func__, symbol__name(prev),
+ symbol__end(prev));
}
}
/* Last entry */
- if (curr->end == curr->start)
- curr->end = roundup(curr->start, 4096) + 4096;
+ if (symbol__end(curr) == symbol__start(curr))
+ symbol__set_end(curr,
+ roundup(symbol__start(curr), 4096) + 4096);
}
struct symbol *symbol__new(u64 start, u64 len, u8 binding, u8 type, const char *name)
@@ -413,14 +417,14 @@ void __symbols__insert(struct rb_root_cached *symbols, struct symbol *sym)
{
struct rb_node **p = &symbols->rb_root.rb_node;
struct rb_node *parent = NULL;
- const u64 ip = sym->start;
+ const u64 ip = symbol__start(sym);
struct symbol *s;
bool leftmost = true;
while (*p != NULL) {
parent = *p;
s = rb_entry(parent, struct symbol, rb_node);
- if (ip < s->start)
+ if (ip < symbol__start(s))
p = &(*p)->rb_left;
else {
p = &(*p)->rb_right;
@@ -448,9 +452,9 @@ static struct symbol *symbols__find(struct rb_root_cached *symbols, u64 ip)
while (n) {
struct symbol *s = rb_entry(n, struct symbol, rb_node);
- if (ip < s->start)
+ if (ip < symbol__start(s))
n = n->rb_left;
- else if (ip > s->end || (ip == s->end && ip != s->start))
+ else if (ip > symbol__end(s) || (ip == symbol__end(s) && ip != symbol__start(s)))
n = n->rb_right;
else
return s;
@@ -494,7 +498,7 @@ static int symbols__sort_name_cmp(const void *vlhs, const void *vrhs)
const struct symbol *lhs = *((const struct symbol **)vlhs);
const struct symbol *rhs = *((const struct symbol **)vrhs);
- return strcmp(lhs->name, rhs->name);
+ return strcmp(symbol__name(lhs), symbol__name(rhs));
}
static struct symbol **symbols__sort_by_name(struct rb_root_cached *source, size_t *len)
@@ -556,7 +560,8 @@ static struct symbol *symbols__find_by_name(struct symbol *symbols[],
int cmp;
i = (lower + upper) / 2;
- cmp = symbol__match_symbol_name(symbols[i]->name, name, includes);
+ cmp = symbol__match_symbol_name(symbol__name(symbols[i]),
+ name, includes);
if (cmp > 0)
upper = i;
@@ -574,7 +579,7 @@ static struct symbol *symbols__find_by_name(struct symbol *symbols[],
for (; i > 0; i--) {
struct symbol *tmp = symbols[i - 1];
- if (!arch__compare_symbol_names(tmp->name, s->name)) {
+ if (!arch__compare_symbol_names(symbol__name(tmp), symbol__name(s))) {
if (found_idx)
*found_idx = i - 1;
s = tmp;
@@ -597,9 +602,9 @@ void dso__insert_symbol(struct dso *dso, struct symbol *sym)
__symbols__insert(dso__symbols(dso), sym);
/* update the symbol cache if necessary */
- if (dso__last_find_result_addr(dso) >= sym->start &&
- (dso__last_find_result_addr(dso) < sym->end ||
- sym->start == sym->end)) {
+ if (dso__last_find_result_addr(dso) >= symbol__start(sym) &&
+ (dso__last_find_result_addr(dso) < symbol__end(sym) ||
+ symbol__start(sym) == symbol__end(sym))) {
dso__set_last_find_result_symbol(dso, sym);
}
}
@@ -796,7 +801,7 @@ bool symbol__is_idle(struct symbol *sym, const struct dso *dso, struct perf_env
"poll_idle",
"pseries_dedicated_idle_sleep",
};
- const char *name = sym->name;
+ const char *name = symbol__name(sym);
uint16_t e_machine;
{
@@ -936,22 +941,25 @@ static int maps__split_kallsyms_for_kcore(struct maps *kmaps, struct dso *dso)
rb_erase_cached(&pos->rb_node, &old_root);
RB_CLEAR_NODE(&pos->rb_node);
- module = strchr(pos->name, '\t');
+ module = strchr((char *)symbol__name(pos), '\t');
if (module)
*module = '\0';
- curr_map = maps__find(kmaps, pos->start);
+ curr_map = maps__find(kmaps, symbol__start(pos));
if (!curr_map) {
symbol__delete(pos);
continue;
}
curr_map_dso = map__dso(curr_map);
- pos->start -= map__start(curr_map) - map__pgoff(curr_map);
- if (pos->end > map__end(curr_map))
- pos->end = map__end(curr_map);
- if (pos->end)
- pos->end -= map__start(curr_map) - map__pgoff(curr_map);
+ symbol__set_start(pos, symbol__start(pos) -
+ (map__start(curr_map) - map__pgoff(curr_map)));
+ if (symbol__end(pos) > map__end(curr_map))
+ symbol__set_end(pos, map__end(curr_map));
+ if (symbol__end(pos)) {
+ symbol__set_end(pos, symbol__end(pos) -
+ (map__start(curr_map) - map__pgoff(curr_map)));
+ }
symbols__insert(dso__symbols(curr_map_dso), pos);
++count;
map__put(curr_map);
@@ -1009,7 +1017,7 @@ static int maps__split_kallsyms(struct maps *kmaps, struct dso *dso, u64 delta,
pos = rb_entry(next, struct symbol, rb_node);
next = rb_next(&pos->rb_node);
- module = strchr(pos->name, '\t');
+ module = strchr((char *)symbol__name(pos), '\t');
if (module) {
struct dso *curr_map_dso;
@@ -1051,9 +1059,11 @@ static int maps__split_kallsyms(struct maps *kmaps, struct dso *dso, u64 delta,
* So that we look just like we get from .ko files,
* i.e. not prelinked, relative to initial_map->start.
*/
- pos->start = map__map_ip(curr_map, pos->start);
- pos->end = map__map_ip(curr_map, pos->end);
- } else if (e_machine == EM_X86_64 && is_entry_trampoline(pos->name)) {
+ symbol__set_start(pos,
+ map__map_ip(curr_map, symbol__start(pos)));
+ symbol__set_end(pos,
+ map__map_ip(curr_map, symbol__end(pos)));
+ } else if (e_machine == EM_X86_64 && is_entry_trampoline(symbol__name(pos))) {
/*
* These symbols are not needed anymore since the
* trampoline maps refer to the text section and it's
@@ -1069,12 +1079,13 @@ static int maps__split_kallsyms(struct maps *kmaps, struct dso *dso, u64 delta,
if (delta) {
/* Kernel was relocated at boot time */
- pos->start -= delta;
- pos->end -= delta;
+ symbol__set_start(pos,
+ symbol__start(pos) - delta);
+ symbol__set_end(pos, symbol__end(pos) - delta);
}
- if (map__start(initial_map) <= (pos->start + delta) &&
- (pos->start + delta) < map__end(initial_map)) {
+ if (map__start(initial_map) <= (symbol__start(pos) + delta) &&
+ (symbol__start(pos) + delta) < map__end(initial_map)) {
map__zput(curr_map);
curr_map = map__get(initial_map);
goto add_symbol;
@@ -1097,7 +1108,7 @@ static int maps__split_kallsyms(struct maps *kmaps, struct dso *dso, u64 delta,
dso__set_kernel(ndso, dso__kernel(dso));
dso__set_loaded(ndso);
- curr_map = map__new2(pos->start, ndso);
+ curr_map = map__new2(symbol__start(pos), ndso);
if (curr_map == NULL) {
dso__put(ndso);
return -1;
@@ -1113,8 +1124,8 @@ static int maps__split_kallsyms(struct maps *kmaps, struct dso *dso, u64 delta,
++kernel_range;
} else if (delta) {
/* Kernel was relocated at boot time */
- pos->start -= delta;
- pos->end -= delta;
+ symbol__set_start(pos, symbol__start(pos) - delta);
+ symbol__set_end(pos, symbol__end(pos) - delta);
}
add_symbol:
if (!RC_CHK_EQUAL(curr_map, initial_map)) {
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index d0bac824c79c..4fb7f6fa8441 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -109,6 +109,46 @@ struct symbol {
void symbol__delete(struct symbol *sym);
void symbols__delete(struct rb_root_cached *symbols);
+static inline u64 symbol__start(const struct symbol *sym)
+{
+ return sym->start;
+}
+
+static inline void symbol__set_start(struct symbol *sym, u64 start)
+{
+ sym->start = start;
+}
+
+static inline u64 symbol__end(const struct symbol *sym)
+{
+ return sym->end;
+}
+
+static inline void symbol__set_end(struct symbol *sym, u64 end)
+{
+ sym->end = end;
+}
+
+static inline const char *symbol__name(const struct symbol *sym)
+{
+ return sym->name;
+}
+
+static inline u16 symbol__namelen(const struct symbol *sym)
+{
+ return sym->namelen;
+}
+
+static inline u8 symbol__arch_sym(const struct symbol *sym)
+{
+ return sym->arch_sym;
+}
+
+static inline void symbol__set_arch_sym(struct symbol *sym, u8 arch_sym)
+{
+ sym->arch_sym = arch_sym;
+}
+
static inline u8 symbol__type(const struct symbol *sym)
{
return (atomic_load_explicit(&sym->flags, memory_order_relaxed) &
@@ -165,7 +205,7 @@ void symbol__set_ifunc_alias(struct symbol *sym, bool ifunc_alias);
static inline size_t symbol__size(const struct symbol *sym)
{
- return sym->end - sym->start;
+ return symbol__end(sym) - symbol__start(sym);
}
struct strlist;
diff --git a/tools/perf/util/symbol_fprintf.c b/tools/perf/util/symbol_fprintf.c
index 4dc8d5761f52..8c131325e989 100644
--- a/tools/perf/util/symbol_fprintf.c
+++ b/tools/perf/util/symbol_fprintf.c
@@ -10,10 +10,10 @@
size_t symbol__fprintf(struct symbol *sym, FILE *fp)
{
return fprintf(fp, " %" PRIx64 "-%" PRIx64 " %c %s\n",
- sym->start, sym->end,
+ symbol__start(sym), symbol__end(sym),
symbol__binding(sym) == STB_GLOBAL ? 'g' :
symbol__binding(sym) == STB_LOCAL ? 'l' : 'w',
- sym->name);
+ symbol__name(sym));
}
size_t __symbol__fprintf_symname_offs(const struct symbol *sym,
@@ -25,12 +25,12 @@ size_t __symbol__fprintf_symname_offs(const struct symbol *sym,
size_t length;
if (sym) {
- length = fprintf(fp, "%s", sym->name);
+ length = fprintf(fp, "%s", symbol__name(sym));
if (al && print_offsets) {
- if (al->addr < sym->end)
- offset = al->addr - sym->start;
+ if (al->addr < symbol__end(sym))
+ offset = al->addr - symbol__start(sym);
else
- offset = al->addr - map__start(al->map) - sym->start;
+ offset = al->addr - map__start(al->map) - symbol__start(sym);
length += fprintf(fp, "+0x%lx", offset);
}
return length;
@@ -67,7 +67,7 @@ size_t dso__fprintf_symbols_by_name(struct dso *dso,
for (size_t i = 0; i < dso__symbol_names_len(dso); i++) {
struct symbol *pos = dso__symbol_names(dso)[i];
- ret += fprintf(fp, "%s\n", pos->name);
+ ret += fprintf(fp, "%s\n", symbol__name(pos));
}
return ret;
}
diff --git a/tools/perf/util/thread-stack.c b/tools/perf/util/thread-stack.c
index 1360f44421ef..76867ba83365 100644
--- a/tools/perf/util/thread-stack.c
+++ b/tools/perf/util/thread-stack.c
@@ -1056,7 +1056,7 @@ static int thread_stack__x86_retpoline(struct thread_stack *ts,
struct symbol *tsym = to_al->sym;
struct call_path *cp;
- if (sym && is_x86_retpoline(sym->name)) {
+ if (sym && is_x86_retpoline(symbol__name(sym))) {
/*
* This is a x86 retpoline fn. It pollutes the call graph by
* showing up everywhere there is an indirect branch, but does
@@ -1069,7 +1069,7 @@ static int thread_stack__x86_retpoline(struct thread_stack *ts,
*/
ts->cnt -= 1;
sym = ts->stack[ts->cnt - 2].cp->sym;
- if (sym && sym == tsym && to_al->addr != tsym->start) {
+ if (sym && sym == tsym && to_al->addr != symbol__start(tsym)) {
/*
* Target is back to the middle of the symbol we came
* from so assume it is an indirect jmp and forget it
@@ -1170,7 +1170,7 @@ int thread_stack__process(struct thread *thread, struct comm *comm,
*/
if (!err && rstate == X86_RETPOLINE_POSSIBLE && to_al->sym &&
from_al->sym == to_al->sym &&
- to_al->addr != to_al->sym->start)
+ to_al->addr != symbol__start(to_al->sym))
ts->rstate = X86_RETPOLINE_DETECTED;
} else if (sample->flags & PERF_IP_FLAG_RETURN) {
@@ -1207,7 +1207,7 @@ int thread_stack__process(struct thread *thread, struct comm *comm,
err = thread_stack__trace_end(ts, sample, ref);
} else if (sample->flags & PERF_IP_FLAG_BRANCH &&
from_al->sym != to_al->sym && to_al->sym &&
- to_al->addr == to_al->sym->start) {
+ to_al->addr == symbol__start(to_al->sym)) {
struct call_path_root *cpr = ts->crp->cpr;
struct call_path *cp;
diff --git a/tools/perf/util/unwind-libdw.c b/tools/perf/util/unwind-libdw.c
index d49901b1571e..87ade098b5c6 100644
--- a/tools/perf/util/unwind-libdw.c
+++ b/tools/perf/util/unwind-libdw.c
@@ -172,7 +172,7 @@ static int entry(u64 ip, struct unwind_info *ui)
e->ms.sym = al.sym;
pr_debug("unwind: %s:ip = 0x%" PRIx64 " (0x%" PRIx64 ")\n",
- al.sym ? al.sym->name : "''",
+ al.sym ? symbol__name(al.sym) : "''",
ip,
al.map ? map__map_ip(al.map, ip) : (u64) 0);
addr_location__exit(&al);
--
2.56.0.rc1.315.gc6ed9934b7-goog
^ permalink raw reply [flat|nested] 36+ messages in thread
* [PATCH v1 2/7] perf symbol: Remove symbol_conf.priv_size and negative-offset allocations
2026-09-28 7:52 [PATCH v1 0/7] perf symbol: Reference counting, flat array storage, and LRU shrinking Ian Rogers
2026-09-28 7:52 ` [PATCH v1 1/7] perf symbol: Add accessor functions for struct symbol fields Ian Rogers
@ 2026-09-28 7:52 ` Ian Rogers
2026-09-28 7:52 ` [PATCH v1 3/7] perf symbol: Switch backing storage from rbtree to struct symbols array Ian Rogers
` (7 subsequent siblings)
9 siblings, 0 replies; 36+ messages in thread
From: Ian Rogers @ 2026-09-28 7:52 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
linux-perf-users, linux-kernel, Alireza Haghdoost
Previously struct symbol allocations could be preceded by
symbol_conf.priv_size bytes (used by annotation, the TUI map browser
index, and db-export symbol IDs). This coupled symbol allocation size to
global configuration, allocated unused space ahead of every symbol in
the system, and complicated symbol lifetime management.
Without adding extra seldom-used fields to struct symbol:
- Move struct annotation storage to an on-demand hashmap in
util/annotate.c keyed by struct symbol pointer, guarded by
annotations_lock and tracked via a new SYMBOL_FLAG_ANNOTATED flag in
sym->flags so unannotated symbols pay zero lookup or teardown
overhead. If allocation fails in symbol__annotation(), return a static
empty_annotation fallback so callers that do not check for NULL remain
safe while mutating callers check for &empty_annotation.
- Remove symbol__browser_index() in ui/browsers/map.c and compute the
TUI map browser index on demand in map_browser__search() by walking
the map browser's symbol entries when the user searches with '/'.
Previously, '/' search in the TUI map browser was gated behind
'verbose > 0' (-v) in builtin-c2c.c, builtin-report.c, builtin-top.c,
and ui/browsers/map.c solely to avoid adding sizeof(u32) to
symbol_conf.priv_size for every struct symbol in normal runs. With on-
demand index computation there is no per-symbol memory overhead, so
remove the verbose > 0 restriction and allow '/' search in the map
browser unconditionally.
- Store exported symbol database IDs in an on-demand sym_db_ids hashmap
in struct db_export instead of symbol__priv().
- Remove symbol_conf.priv_size and symbol__priv().
Assisted-by: Antigravity:gemini-3.1-pro
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/builtin-c2c.c | 14 --
tools/perf/builtin-report.c | 14 --
tools/perf/tests/builtin-test.c | 1 -
tools/perf/ui/browsers/map.c | 35 +++--
tools/perf/util/annotate.c | 128 +++++++++++++++++-
tools/perf/util/annotate.h | 6 +-
tools/perf/util/db-export.c | 49 ++++++-
tools/perf/util/db-export.h | 6 +
.../scripting-engines/trace-event-python.c | 10 +-
tools/perf/util/symbol.c | 34 ++---
tools/perf/util/symbol.h | 16 ++-
tools/perf/util/symbol_conf.h | 1 -
12 files changed, 219 insertions(+), 95 deletions(-)
diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c
index 4222ac784fab..ba2f6cb7b371 100644
--- a/tools/perf/builtin-c2c.c
+++ b/tools/perf/builtin-c2c.c
@@ -3404,20 +3404,6 @@ static int perf_c2c__report(int argc, const char **argv)
if (ret < 0)
goto out_mem2node;
- /*
- * For searching by name on the "Browse map details".
- * providing it only in verbose mode not to bloat too
- * much struct symbol.
- */
- if (verbose > 0) {
- /*
- * XXX: Need to provide a less kludgy way to ask for
- * more space per symbol, the u32 is for the index on
- * the ui browser.
- * See symbol__browser_index.
- */
- symbol_conf.priv_size += sizeof(u32);
- }
annotation_config__init();
}
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index 57225bc87731..b42bf23e5545 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -1876,20 +1876,6 @@ int cmd_report(int argc, const char **argv)
ret = symbol__annotation_init();
if (ret < 0)
goto error;
- /*
- * For searching by name on the "Browse map details".
- * providing it only in verbose mode not to bloat too
- * much struct symbol.
- */
- if (verbose > 0) {
- /*
- * XXX: Need to provide a less kludgy way to ask for
- * more space per symbol, the u32 is for the index on
- * the ui browser.
- * See symbol__browser_index.
- */
- symbol_conf.priv_size += sizeof(u32);
- }
annotation_config__init();
}
diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c
index d2f594921e25..66bfd0841f95 100644
--- a/tools/perf/tests/builtin-test.c
+++ b/tools/perf/tests/builtin-test.c
@@ -1827,7 +1827,6 @@ int cmd_test(int argc, const char **argv)
if (dont_fork)
sequential = true;
- symbol_conf.priv_size = sizeof(int);
symbol_conf.try_vmlinux_path = true;
diff --git a/tools/perf/ui/browsers/map.c b/tools/perf/ui/browsers/map.c
index 1f69d7a798e9..38e123d45a1f 100644
--- a/tools/perf/ui/browsers/map.c
+++ b/tools/perf/ui/browsers/map.c
@@ -40,12 +40,6 @@ static void map_browser__write(struct ui_browser *browser, void *nd, int row)
ui_browser__write_nstring(browser, symbol__name(sym), width);
}
-/* FIXME uber-kludgy, see comment on cmd_report... */
-static u32 *symbol__browser_index(struct symbol *browser)
-{
- return ((void *)browser) - sizeof(struct rb_node) - sizeof(u32);
-}
-
static int map_browser__search(struct map_browser *browser)
{
char target[512];
@@ -63,10 +57,21 @@ static int map_browser__search(struct map_browser *browser)
sym = map__find_symbol_by_name(browser->map, target);
if (sym != NULL) {
- u32 *idx = symbol__browser_index(sym);
-
- browser->b.top = &sym->rb_node;
- browser->b.index = browser->b.top_idx = *idx;
+ struct rb_node *nd;
+ u32 idx = 0;
+
+ /*
+ * Walk the map browser's symbol entries to find the matching
+ * symbol node and its display row index, then position the
+ * browser cursor at that entry.
+ */
+ for (nd = rb_first(browser->b.entries); nd; nd = rb_next(nd), ++idx) {
+ if (&sym->rb_node == nd) {
+ browser->b.top = nd;
+ browser->b.index = browser->b.top_idx = idx;
+ break;
+ }
+ }
} else
ui_helpline__fpush("%s not found!", target);
@@ -78,8 +83,7 @@ static int map_browser__run(struct map_browser *browser)
int key;
if (ui_browser__show(&browser->b, dso__long_name(map__dso(browser->map)),
- "Press ESC to exit, %s / to search",
- verbose > 0 ? "" : "restart with -v to use") < 0)
+ "Press ESC to exit, / to search") < 0)
return -1;
while (1) {
@@ -87,8 +91,7 @@ static int map_browser__run(struct map_browser *browser)
switch (key) {
case '/':
- if (verbose > 0)
- map_browser__search(browser);
+ map_browser__search(browser);
/* fall thru */
default:
ui_browser__warn_unhandled_hotkey(&browser->b, key, 0, NULL);
@@ -125,10 +128,6 @@ int map__browse(struct map *map)
if (maxaddr < symbol__end(pos))
maxaddr = symbol__end(pos);
- if (verbose > 0) {
- u32 *idx = symbol__browser_index(pos);
- *idx = mb.b.nr_entries;
- }
++mb.b.nr_entries;
}
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c
index a1090a4e2e9d..9bb7e0ee1b8e 100644
--- a/tools/perf/util/annotate.c
+++ b/tools/perf/util/annotate.c
@@ -35,6 +35,7 @@
#include "string2.h"
#include "dwarf-regs.h"
#include "util/event.h"
+#include "util/rwsem.h"
#include "util/sharded_mutex.h"
#include "arch/common.h"
#include "namespaces.h"
@@ -156,9 +157,126 @@ annotated_source__hist_entry(struct annotated_source *src, const struct evsel *e
return entry;
}
+/*
+ * Map from struct symbol pointer to struct annotation. Allocated on demand so
+ * unannotated symbols do not pay the memory cost of struct annotation.
+ * empty_annotation is returned as a fallback on allocation failure so callers
+ * that do not check for NULL do not dereference NULL, while mutating callers
+ * check for &empty_annotation and return an error.
+ */
+static struct rw_semaphore _annotations_lock;
+static struct hashmap *annotations GUARDED_BY(_annotations_lock);
+static struct annotation empty_annotation;
+
+static void annotations_lock_init(void)
+{
+ init_rwsem(&_annotations_lock);
+}
+
+static struct rw_semaphore *annotations_lock(void) LOCK_RETURNED(_annotations_lock)
+{
+ static pthread_once_t annotations_lock_once = PTHREAD_ONCE_INIT;
+
+ pthread_once(&annotations_lock_once, annotations_lock_init);
+ return &_annotations_lock;
+}
+
+static size_t sym_ptr_hash(long key, void *ctx __maybe_unused)
+{
+ return hash_bits((size_t)key, 16);
+}
+
+static bool sym_ptr_equal(long key1, long key2, void *ctx __maybe_unused)
+{
+ return key1 == key2;
+}
+
+struct annotation *symbol__annotation(struct symbol *sym)
+{
+ struct annotation *notes = NULL;
+ long val = 0;
+
+ if (!sym)
+ return NULL;
+
+ /* Fast path: look up existing annotation under read lock if flagged. */
+ if (symbol__annotated(sym)) {
+ down_read(annotations_lock());
+ if (annotations && hashmap__find(annotations, (long)sym, &val))
+ notes = (struct annotation *)val;
+ up_read(annotations_lock());
+ if (notes)
+ return notes;
+ }
+
+ down_write(annotations_lock());
+ if (!annotations) {
+ annotations = hashmap__new(sym_ptr_hash, sym_ptr_equal, NULL);
+ if (IS_ERR(annotations)) {
+ annotations = NULL;
+ up_write(annotations_lock());
+ return &empty_annotation;
+ }
+ } else if (hashmap__find(annotations, (long)sym, &val)) {
+ notes = (struct annotation *)val;
+ up_write(annotations_lock());
+ return notes;
+ }
+
+ notes = zalloc(sizeof(*notes));
+ if (!notes) {
+ up_write(annotations_lock());
+ return &empty_annotation;
+ }
+ annotation__init(notes);
+ if (hashmap__add(annotations, (long)sym, notes) < 0) {
+ free(notes);
+ if (hashmap__size(annotations) == 0) {
+ hashmap__free(annotations);
+ annotations = NULL;
+ }
+ up_write(annotations_lock());
+ return &empty_annotation;
+ }
+ symbol__set_annotated(sym, true);
+ up_write(annotations_lock());
+ return notes;
+}
+
+void symbol__annotation_delete(struct symbol *sym)
+{
+ struct annotation *notes = NULL;
+ long val = 0;
+
+ /* Unannotated symbols avoid taking annotations_lock on teardown. */
+ if (!sym || !symbol__annotated(sym))
+ return;
+
+ down_write(annotations_lock());
+ if (annotations && hashmap__delete(annotations, (long)sym, NULL, &val)) {
+ notes = (struct annotation *)val;
+ if (hashmap__size(annotations) == 0) {
+ hashmap__free(annotations);
+ annotations = NULL;
+ }
+ }
+ symbol__set_annotated(sym, false);
+ up_write(annotations_lock());
+
+ if (notes) {
+ annotation__exit(notes);
+ free(notes);
+ }
+}
+
void symbol__annotate_zero_histograms(struct symbol *sym)
{
- struct annotation *notes = symbol__annotation(sym);
+ struct annotation *notes;
+
+ if (!symbol__annotated(sym))
+ return;
+
+ notes = symbol__annotation(sym);
annotation__lock(notes);
if (notes->src != NULL) {
@@ -298,7 +416,7 @@ static int __symbol__inc_addr_samples(struct map_symbol *ms,
struct annotated_branch *annotation__get_branch(struct annotation *notes)
{
- if (notes == NULL)
+ if (notes == NULL || notes == &empty_annotation)
return NULL;
if (notes->branch == NULL)
@@ -337,6 +455,9 @@ struct annotated_source *symbol__hists(struct symbol *sym, int nr_hists)
{
struct annotation *notes = symbol__annotation(sym);
+ if (!notes || notes == &empty_annotation)
+ return NULL;
+
if (notes->src == NULL) {
notes->src = annotated_source__new();
if (notes->src == NULL)
@@ -1142,6 +1263,9 @@ int symbol__annotate(struct map_symbol *ms, struct evsel *evsel,
if (parch)
*parch = arch;
+ if (!notes || notes == &empty_annotation)
+ return -ENOMEM;
+
if (notes->src && !list_empty(¬es->src->source))
return 0;
diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h
index 1b47c1156b9a..d87de0f11a60 100644
--- a/tools/perf/util/annotate.h
+++ b/tools/perf/util/annotate.h
@@ -449,10 +449,8 @@ static inline struct sym_hist *annotation__histogram(struct annotation *notes,
struct sym_hist_entry *
annotated_source__hist_entry(struct annotated_source *src, const struct evsel *evsel, u64 offset);
-static inline struct annotation *symbol__annotation(struct symbol *sym)
-{
- return (void *)sym - symbol_conf.priv_size;
-}
+struct annotation *symbol__annotation(struct symbol *sym);
+void symbol__annotation_delete(struct symbol *sym);
int addr_map_symbol__inc_samples(struct addr_map_symbol *ams, struct perf_sample *sample);
diff --git a/tools/perf/util/db-export.c b/tools/perf/util/db-export.c
index 732369fe0046..897cda3fadd2 100644
--- a/tools/perf/util/db-export.c
+++ b/tools/perf/util/db-export.c
@@ -17,8 +17,20 @@
#include "event.h"
#include "thread-stack.h"
#include "callchain.h"
+#include <linux/err.h>
#include "call-path.h"
#include "db-export.h"
+#include "hashmap.h"
+
+static size_t sym_ptr_hash(long key, void *ctx __maybe_unused)
+{
+ return hash_bits((size_t)key, 16);
+}
+
+static bool sym_ptr_equal(long key1, long key2, void *ctx __maybe_unused)
+{
+ return key1 == key2;
+}
int db_export__init(struct db_export *dbe)
{
@@ -28,6 +40,8 @@ int db_export__init(struct db_export *dbe)
void db_export__exit(struct db_export *dbe)
{
+ hashmap__free(dbe->sym_db_ids);
+ dbe->sym_db_ids = NULL;
call_return_processor__free(dbe->crp);
dbe->crp = NULL;
}
@@ -156,15 +170,40 @@ int db_export__dso(struct db_export *dbe, struct dso *dso,
return 0;
}
+/*
+ * Return the database export ID assigned to @sym in @dbe->sym_db_ids, or 0
+ * if @sym has not been exported yet.
+ */
+u64 db_export__symbol_db_id(struct db_export *dbe, struct symbol *sym)
+{
+ long db_id = 0;
+
+ if (sym && dbe->sym_db_ids)
+ hashmap__find(dbe->sym_db_ids, (long)sym, &db_id);
+ return (u64)db_id;
+}
+
int db_export__symbol(struct db_export *dbe, struct symbol *sym,
struct dso *dso)
{
- u64 *sym_db_id = symbol__priv(sym);
+ long db_id;
- if (*sym_db_id)
+ if (db_export__symbol_db_id(dbe, sym))
return 0;
- *sym_db_id = ++dbe->symbol_last_db_id;
+ if (!dbe->sym_db_ids) {
+ dbe->sym_db_ids = hashmap__new(sym_ptr_hash, sym_ptr_equal, NULL);
+ if (IS_ERR(dbe->sym_db_ids)) {
+ int err = PTR_ERR(dbe->sym_db_ids);
+
+ dbe->sym_db_ids = NULL;
+ return err;
+ }
+ }
+
+ db_id = ++dbe->symbol_last_db_id;
+ if (hashmap__set(dbe->sym_db_ids, (long)sym, db_id, NULL, NULL))
+ return -ENOMEM;
if (dbe->export_symbol)
return dbe->export_symbol(dbe, sym, dso);
@@ -192,12 +231,10 @@ static int db_ids_from_al(struct db_export *dbe, struct addr_location *al,
}
if (al->sym) {
- u64 *db_id = symbol__priv(al->sym);
-
err = db_export__symbol(dbe, al->sym, dso);
if (err)
return err;
- *sym_db_id = *db_id;
+ *sym_db_id = db_export__symbol_db_id(dbe, al->sym);
*offset = al->addr - symbol__start(al->sym);
}
}
diff --git a/tools/perf/util/db-export.h b/tools/perf/util/db-export.h
index 1abbfd398e3a..b5c22095e914 100644
--- a/tools/perf/util/db-export.h
+++ b/tools/perf/util/db-export.h
@@ -63,6 +63,11 @@ struct db_export {
u64 th_in_id, u64 comm_in_id, int flags);
struct call_return_processor *crp;
struct call_path_root *cpr;
+ /**
+ * @sym_db_ids: Hashmap mapping struct symbol pointers to their
+ * exported 64-bit database IDs.
+ */
+ struct hashmap *sym_db_ids;
u64 evsel_last_db_id;
u64 machine_last_db_id;
u64 thread_last_db_id;
@@ -90,6 +95,7 @@ int db_export__comm_thread(struct db_export *dbe, struct comm *comm,
struct thread *thread);
int db_export__dso(struct db_export *dbe, struct dso *dso,
struct machine *machine);
+u64 db_export__symbol_db_id(struct db_export *dbe, struct symbol *sym);
int db_export__symbol(struct db_export *dbe, struct symbol *sym,
struct dso *dso);
int db_export__branch_type(struct db_export *dbe, u32 branch_type,
diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c
index 408b5645b33d..91885381d8b7 100644
--- a/tools/perf/util/scripting-engines/trace-event-python.c
+++ b/tools/perf/util/scripting-engines/trace-event-python.c
@@ -1372,12 +1372,12 @@ static int python_export_symbol(struct db_export *dbe, struct symbol *sym,
struct dso *dso)
{
struct tables *tables = container_of(dbe, struct tables, dbe);
- u64 *sym_db_id = symbol__priv(sym);
+ u64 sym_db_id = db_export__symbol_db_id(dbe, sym);
PyObject *t;
t = tuple_new(6);
- tuple_set_d64(t, 0, *sym_db_id);
+ tuple_set_d64(t, 0, sym_db_id);
tuple_set_d64(t, 1, dso__db_id(dso));
tuple_set_d64(t, 2, symbol__start(sym));
tuple_set_d64(t, 3, symbol__end(sym));
@@ -1487,7 +1487,7 @@ static int python_export_call_path(struct db_export *dbe, struct call_path *cp)
u64 parent_db_id, sym_db_id;
parent_db_id = cp->parent ? cp->parent->db_id : 0;
- sym_db_id = cp->sym ? *(u64 *)symbol__priv(cp->sym) : 0;
+ sym_db_id = db_export__symbol_db_id(dbe, cp->sym);
t = tuple_new(4);
@@ -1993,10 +1993,6 @@ static void set_table_handlers(struct tables *tables)
}
tables->db_export_mode = true;
- /*
- * Reserve per symbol space for symbol->db_id via symbol__priv()
- */
- symbol_conf.priv_size = sizeof(u64);
SET_TABLE_HANDLER(evsel);
SET_TABLE_HANDLER(machine);
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 8e264d38b981..2ec650a7834d 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -319,19 +319,11 @@ void symbols__fixup_end(struct rb_root_cached *symbols, bool is_kallsyms)
struct symbol *symbol__new(u64 start, u64 len, u8 binding, u8 type, const char *name)
{
size_t namelen = strlen(name) + 1;
- struct symbol *sym = calloc(1, (symbol_conf.priv_size +
- sizeof(*sym) + namelen));
+ struct symbol *sym = calloc(1, sizeof(*sym) + namelen);
+
if (sym == NULL)
return NULL;
- if (symbol_conf.priv_size) {
- if (symbol_conf.init_annotation) {
- struct annotation *notes = (void *)sym;
- annotation__init(notes);
- }
- sym = ((void *)sym) + symbol_conf.priv_size;
- }
-
sym->start = start;
sym->end = len ? start + len : start;
atomic_init(&sym->flags, (type << SYMBOL_FLAG_TYPE_SHIFT) |
@@ -347,14 +339,9 @@ struct symbol *symbol__new(u64 start, u64 len, u8 binding, u8 type, const char *
void symbol__delete(struct symbol *sym)
{
- if (symbol_conf.priv_size) {
- if (symbol_conf.init_annotation) {
- struct annotation *notes = symbol__annotation(sym);
-
- annotation__exit(notes);
- }
- }
- free(((void *)sym) - symbol_conf.priv_size);
+ if (symbol__annotated(sym))
+ symbol__annotation_delete(sym);
+ free(sym);
}
void symbol__set_ignore(struct symbol *sym, bool ignore)
@@ -389,6 +376,14 @@ void symbol__set_ifunc_alias(struct symbol *sym, bool ifunc_alias)
atomic_fetch_and(&sym->flags, ~SYMBOL_FLAG_IFUNC_ALIAS);
}
+void symbol__set_annotated(struct symbol *sym, bool annotated)
+{
+ if (annotated)
+ atomic_fetch_or(&sym->flags, SYMBOL_FLAG_ANNOTATED);
+ else
+ atomic_fetch_and(&sym->flags, ~SYMBOL_FLAG_ANNOTATED);
+}
+
static void symbol__set_idle(struct symbol *sym, bool idle)
{
uint16_t old_flags = atomic_load_explicit(&sym->flags, memory_order_relaxed);
@@ -2504,7 +2499,6 @@ int symbol__annotation_init(void)
return -1;
}
- symbol_conf.priv_size += sizeof(struct annotation);
symbol_conf.init_annotation = true;
return 0;
}
@@ -2547,8 +2541,6 @@ int symbol__init(struct perf_env *env)
if (symbol_conf.initialized)
return 0;
- symbol_conf.priv_size = PERF_ALIGN(symbol_conf.priv_size, sizeof(u64));
-
symbol__elf_init();
if (symbol_conf.try_vmlinux_path && vmlinux_path__init(env) < 0)
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index 4fb7f6fa8441..100a7da07834 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -88,9 +88,9 @@ enum symbol_idle_kind {
#define SYMBOL_FLAG_INLINED (1 << 11)
#define SYMBOL_FLAG_ANNOTATE2 (1 << 12)
#define SYMBOL_FLAG_IFUNC_ALIAS (1 << 13)
+#define SYMBOL_FLAG_ANNOTATED (1 << 14)
/**
- * A symtab entry. When allocated this may be preceded by an annotation (see
- * symbol__annotation) and/or a browser_index (see symbol__browser_index).
+ * A symtab entry.
*/
struct symbol {
struct rb_node rb_node;
@@ -185,12 +185,19 @@ static inline bool symbol__ifunc_alias(const struct symbol *sym)
SYMBOL_FLAG_IFUNC_ALIAS) != 0;
}
+static inline bool symbol__annotated(const struct symbol *sym)
+{
+ return (atomic_load_explicit(&sym->flags, memory_order_relaxed) &
+ SYMBOL_FLAG_ANNOTATED) != 0;
+}
+
bool symbol__is_idle(struct symbol *sym, const struct dso *dso, struct perf_env *env);
void symbol__set_ignore(struct symbol *sym, bool ignore);
void symbol__set_annotate2(struct symbol *sym, bool annotate2);
void symbol__set_inlined(struct symbol *sym, bool inlined);
void symbol__set_ifunc_alias(struct symbol *sym, bool ifunc_alias);
+void symbol__set_annotated(struct symbol *sym, bool annotated);
/* symbols__for_each_entry - iterate over symbols (rb_root)
*
@@ -224,11 +231,6 @@ static inline int __symbol__join_symfs(char *bf, size_t size, const char *path)
extern int vmlinux_path__nr_entries;
extern char **vmlinux_path;
-static inline void *symbol__priv(struct symbol *sym)
-{
- return ((void *)sym) - symbol_conf.priv_size;
-}
-
struct ref_reloc_sym {
const char *name;
u64 addr;
diff --git a/tools/perf/util/symbol_conf.h b/tools/perf/util/symbol_conf.h
index 37d35f42dcc1..aaa7148c3ac1 100644
--- a/tools/perf/util/symbol_conf.h
+++ b/tools/perf/util/symbol_conf.h
@@ -42,7 +42,6 @@ struct symbol_conf {
/* Was hybrid_merge given on the command line rather than in a config file? */
bool hybrid_merge_set;
bool nanosecs;
- unsigned short priv_size;
bool try_vmlinux_path,
init_annotation,
force,
--
2.56.0.rc1.315.gc6ed9934b7-goog
^ permalink raw reply [flat|nested] 36+ messages in thread
* [PATCH v1 3/7] perf symbol: Switch backing storage from rbtree to struct symbols array
2026-09-28 7:52 [PATCH v1 0/7] perf symbol: Reference counting, flat array storage, and LRU shrinking Ian Rogers
2026-09-28 7:52 ` [PATCH v1 1/7] perf symbol: Add accessor functions for struct symbol fields Ian Rogers
2026-09-28 7:52 ` [PATCH v1 2/7] perf symbol: Remove symbol_conf.priv_size and negative-offset allocations Ian Rogers
@ 2026-09-28 7:52 ` Ian Rogers
2026-09-28 7:52 ` [PATCH v1 4/7] perf symbol: Add reference counting and DECLARE_RC_STRUCT(symbol) Ian Rogers
` (6 subsequent siblings)
9 siblings, 0 replies; 36+ messages in thread
From: Ian Rogers @ 2026-09-28 7:52 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
linux-perf-users, linux-kernel, Alireza Haghdoost
Replace struct rb_node rb_node in struct symbol (which cost 24 bytes per
symbol on 64-bit) with a sorted array in struct symbols, matching the
design of struct dsos and struct maps:
- Introduce struct symbols containing a rw_semaphore lock, primary
address-sorted symbols array, secondary name-sorted symbols_by_name
array, counts, capacity, and sorted state flags.
- Lazily sort symbols by [start, end) using qsort (symbols__sort_locked)
and use binary search in symbols__find().
- Update symbols__fixup_duplicate(), symbols__fixup_end(),
maps__split_kallsyms_for_kcore(), and maps__split_kallsyms() to
operate on the sorted array and compact entries in place.
- Convert symbols__for_each_entry(), dso__for_each_symbol(), and
map__for_each_symbol() to callback-based iteration under
symbols->lock, matching dsos__for_each_dso() and maps__for_each_map().
- Remove the unsynchronized single-entry dso->last_find_result cache and
dso__reset_find_symbol_cache() since binary search over a contiguous
array is cache-friendly.
- Remove the direct rb_erase_cached() workaround in builtin-annotate.c's
add_sample().
- Update the TUI map browser (ui/browsers/map.c) to snapshot the sorted
symbol array into an entries array and use ui_browser__argv_refresh
and ui_browser__argv_seek instead of ui_browser__rb_tree_refresh.
Assisted-by: Antigravity:gemini-3.1-pro
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/arch/powerpc/util/sym-handling.c | 34 +-
tools/perf/builtin-annotate.c | 11 -
tools/perf/builtin-kmem.c | 51 ++-
tools/perf/tests/symbols.c | 61 ++-
tools/perf/tests/vmlinux-kallsyms.c | 189 ++++----
tools/perf/ui/browsers/map.c | 62 ++-
tools/perf/util/auxtrace.c | 109 +++--
tools/perf/util/dso.c | 50 +-
tools/perf/util/dso.h | 59 +--
tools/perf/util/intel-pt.c | 74 +--
tools/perf/util/machine.c | 28 +-
tools/perf/util/map.c | 25 +-
tools/perf/util/map.h | 8 +-
tools/perf/util/probe-event.c | 82 ++--
tools/perf/util/symbol.c | 478 ++++++++++++--------
tools/perf/util/symbol.h | 70 ++-
16 files changed, 801 insertions(+), 590 deletions(-)
diff --git a/tools/perf/arch/powerpc/util/sym-handling.c b/tools/perf/arch/powerpc/util/sym-handling.c
index d73bfdcdbec1..0ec327930b0a 100644
--- a/tools/perf/arch/powerpc/util/sym-handling.c
+++ b/tools/perf/arch/powerpc/util/sym-handling.c
@@ -115,13 +115,27 @@ void arch__fix_tev_from_maps(struct perf_probe_event *pev,
}
#ifdef HAVE_LIBELF_SUPPORT
+struct post_process_probe_trace_events_args {
+ struct perf_probe_event *pev;
+ struct probe_trace_event *tev;
+ struct map *map;
+};
+
+static int post_process_probe_trace_events_cb(struct symbol *sym, void *data)
+{
+ struct post_process_probe_trace_events_args *args = data;
+
+ if (map__unmap_ip(args->map, symbol__start(sym)) == args->tev->point.address) {
+ arch__fix_tev_from_maps(args->pev, args->tev, args->map, sym);
+ return 1;
+ }
+ return 0;
+}
+
void arch__post_process_probe_trace_events(struct perf_probe_event *pev,
int ntevs)
{
- struct probe_trace_event *tev;
struct map *map;
- struct symbol *sym = NULL;
- struct rb_node *tmp;
int i = 0;
map = get_target_map(pev->target, pev->nsi, pev->uprobes);
@@ -129,13 +143,13 @@ void arch__post_process_probe_trace_events(struct perf_probe_event *pev,
return;
for (i = 0; i < ntevs; i++) {
- tev = &pev->tevs[i];
- map__for_each_symbol(map, sym, tmp) {
- if (map__unmap_ip(map, symbol__start(sym)) == tev->point.address) {
- arch__fix_tev_from_maps(pev, tev, map, sym);
- break;
- }
- }
+ struct post_process_probe_trace_events_args args = {
+ .pev = pev,
+ .tev = &pev->tevs[i],
+ .map = map,
+ };
+
+ map__for_each_symbol(map, post_process_probe_trace_events_cb, &args);
}
}
#endif /* HAVE_LIBELF_SUPPORT */
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c
index c93b67986334..5ee02a25fb9c 100644
--- a/tools/perf/builtin-annotate.c
+++ b/tools/perf/builtin-annotate.c
@@ -254,17 +254,6 @@ static int add_sample(struct perf_sample *sample,
(al->sym == NULL ||
strcmp(ann->sym_hist_filter, symbol__name(al->sym)) != 0)) {
/* We're only interested in a symbol named sym_hist_filter */
- /*
- * FIXME: why isn't this done in the symbol_filter when loading
- * the DSO?
- */
- if (al->sym != NULL) {
- struct dso *dso = map__dso(al->map);
-
- rb_erase_cached(&al->sym->rb_node, dso__symbols(dso));
- symbol__delete(al->sym);
- dso__reset_find_symbol_cache(dso);
- }
return 0;
}
diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c
index d1883086ed58..dd8821d2d05e 100644
--- a/tools/perf/builtin-kmem.c
+++ b/tools/perf/builtin-kmem.c
@@ -341,13 +341,33 @@ static int callcmp(const void *a, const void *b)
return -1;
}
+static int build_alloc_func_list_cb(struct symbol *sym, void *data)
+{
+ regex_t *alloc_func_regex = data;
+ struct alloc_func *func;
+
+ if (regexec(alloc_func_regex, symbol__name(sym), 0, NULL, 0))
+ return 0;
+
+ func = realloc(alloc_func_list,
+ (nr_alloc_funcs + 1) * sizeof(*func));
+ if (func == NULL)
+ return -ENOMEM;
+
+ pr_debug("alloc func: %s\n", symbol__name(sym));
+ func[nr_alloc_funcs].start = symbol__start(sym);
+ func[nr_alloc_funcs].end = symbol__end(sym);
+ func[nr_alloc_funcs].name = symbol__name(sym);
+
+ alloc_func_list = func;
+ nr_alloc_funcs++;
+ return 0;
+}
+
static int build_alloc_func_list(void)
{
int ret;
struct map *kernel_map;
- struct symbol *sym;
- struct rb_node *node;
- struct alloc_func *func;
struct machine *machine = &kmem_session->machines.host;
regex_t alloc_func_regex;
static const char pattern[] = "^_?_?(alloc|get_free|get_zeroed)_pages?";
@@ -364,30 +384,17 @@ static int build_alloc_func_list(void)
kernel_map = machine__kernel_map(machine);
if (map__load(kernel_map) < 0) {
pr_err("cannot load kernel map\n");
+ regfree(&alloc_func_regex);
return -ENOENT;
}
- map__for_each_symbol(kernel_map, sym, node) {
- if (regexec(&alloc_func_regex, symbol__name(sym), 0, NULL, 0))
- continue;
-
- func = realloc(alloc_func_list,
- (nr_alloc_funcs + 1) * sizeof(*func));
- if (func == NULL)
- return -ENOMEM;
-
- pr_debug("alloc func: %s\n", symbol__name(sym));
- func[nr_alloc_funcs].start = symbol__start(sym);
- func[nr_alloc_funcs].end = symbol__end(sym);
- func[nr_alloc_funcs].name = symbol__name(sym);
-
- alloc_func_list = func;
- nr_alloc_funcs++;
- }
+ ret = map__for_each_symbol(kernel_map, build_alloc_func_list_cb, &alloc_func_regex);
+ regfree(&alloc_func_regex);
+ if (ret)
+ return ret;
- qsort(alloc_func_list, nr_alloc_funcs, sizeof(*func), funcmp);
+ qsort(alloc_func_list, nr_alloc_funcs, sizeof(*alloc_func_list), funcmp);
- regfree(&alloc_func_regex);
return 0;
}
diff --git a/tools/perf/tests/symbols.c b/tools/perf/tests/symbols.c
index d9796851335e..440d800d9f4d 100644
--- a/tools/perf/tests/symbols.c
+++ b/tools/perf/tests/symbols.c
@@ -112,39 +112,50 @@ static int create_map(struct test_info *ti, char *filename, struct map **map_p)
return TEST_OK;
}
+struct test_dso_args {
+ struct symbol *last_sym;
+ int ret;
+};
+
+static int test_dso_cb(struct symbol *sym, void *data)
+{
+ struct test_dso_args *args = data;
+ struct symbol *last_sym = args->last_sym;
+
+ if (symbol__type(sym) != STT_FUNC && symbol__type(sym) != STT_GNU_IFUNC)
+ return 0;
+
+ /* Check for overlapping function symbols */
+ if (last_sym && symbol__start(sym) < symbol__end(last_sym)) {
+ pr_debug("Overlapping symbols:\n");
+ symbol__fprintf(last_sym, stderr);
+ symbol__fprintf(sym, stderr);
+ args->ret = TEST_FAIL;
+ }
+ /* Check for zero-length function symbol */
+ if (symbol__start(sym) == symbol__end(sym)) {
+ pr_debug("Zero-length symbol:\n");
+ symbol__fprintf(sym, stderr);
+ args->ret = TEST_FAIL;
+ }
+ args->last_sym = sym;
+ return 0;
+}
+
static int test_dso(struct dso *dso)
{
- struct symbol *last_sym = NULL;
- struct rb_node *nd;
- int ret = TEST_OK;
+ struct test_dso_args args = {
+ .last_sym = NULL,
+ .ret = TEST_OK,
+ };
/* dso__fprintf() prints all the symbols */
if (verbose > 1)
dso__fprintf(dso, stderr);
- for (nd = rb_first_cached(dso__symbols(dso)); nd; nd = rb_next(nd)) {
- struct symbol *sym = rb_entry(nd, struct symbol, rb_node);
+ dso__for_each_symbol(dso, test_dso_cb, &args);
- if (symbol__type(sym) != STT_FUNC && symbol__type(sym) != STT_GNU_IFUNC)
- continue;
-
- /* Check for overlapping function symbols */
- if (last_sym && symbol__start(sym) < symbol__end(last_sym)) {
- pr_debug("Overlapping symbols:\n");
- symbol__fprintf(last_sym, stderr);
- symbol__fprintf(sym, stderr);
- ret = TEST_FAIL;
- }
- /* Check for zero-length function symbol */
- if (symbol__start(sym) == symbol__end(sym)) {
- pr_debug("Zero-length symbol:\n");
- symbol__fprintf(sym, stderr);
- ret = TEST_FAIL;
- }
- last_sym = sym;
- }
-
- return ret;
+ return args.ret;
}
static int subdivided_dso_cb(struct dso *dso, struct machine *machine __maybe_unused, void *d)
diff --git a/tools/perf/tests/vmlinux-kallsyms.c b/tools/perf/tests/vmlinux-kallsyms.c
index 97950f86ed5f..98c2a9437ba7 100644
--- a/tools/perf/tests/vmlinux-kallsyms.c
+++ b/tools/perf/tests/vmlinux-kallsyms.c
@@ -113,6 +113,7 @@ static bool is_ignored_symbol(const char *name, char type)
struct test__vmlinux_matches_kallsyms_cb_args {
struct machine kallsyms;
struct map *vmlinux_map;
+ int err;
bool header_printed;
};
@@ -185,16 +186,103 @@ static int test__vmlinux_matches_kallsyms_cb3(struct map *map, void *data)
return 0;
}
+static int test__vmlinux_matches_kallsyms_sym_cb(struct symbol *sym, void *data)
+{
+ struct test__vmlinux_matches_kallsyms_cb_args *args = data;
+ /*
+ * Step 4:
+ *
+ * kallsyms will be internally on demand sorted by name so that we can
+ * find the reference relocation * symbol, i.e. the symbol we will use
+ * to see if the running kernel was relocated by checking if it has the
+ * same value in the vmlinux file we load.
+ */
+ struct map *kallsyms_map = machine__kernel_map(&args->kallsyms);
+ struct symbol *pair, *first_pair;
+ u64 mem_start, mem_end;
+
+ if (symbol__start(sym) == symbol__end(sym))
+ return 0;
+
+ mem_start = map__unmap_ip(args->vmlinux_map, symbol__start(sym));
+ mem_end = map__unmap_ip(args->vmlinux_map, symbol__end(sym));
+
+ first_pair = machine__find_kernel_symbol(&args->kallsyms, mem_start, NULL);
+ pair = first_pair;
+
+ if (pair && UM(symbol__start(pair)) == mem_start) {
+next_pair:
+ if (arch__compare_symbol_names(symbol__name(sym),
+ symbol__name(pair)) == 0) {
+ /*
+ * kallsyms don't have the symbol end, so we
+ * set that by using the next symbol start - 1,
+ * in some cases we get this up to a page
+ * wrong, trace_kmalloc when I was developing
+ * this code was one such example, 2106 bytes
+ * off the real size. More than that and we
+ * _really_ have a problem.
+ */
+ s64 skew = mem_end - UM(symbol__end(pair));
+
+ if (llabs(skew) >= page_size)
+ pr_debug("WARN: %#" PRIx64 ": diff end addr for %s v: %#" PRIx64 " k: %#" PRIx64 "\n",
+ mem_start, symbol__name(sym),
+ mem_end,
+ UM(symbol__end(pair)));
+
+ /*
+ * Do not count this as a failure, because we
+ * could really find a case where it's not
+ * possible to get proper function end from
+ * kallsyms.
+ */
+ return 0;
+ } else {
+ pair = machine__find_kernel_symbol_by_name(&args->kallsyms,
+ symbol__name(sym),
+ NULL);
+ if (pair) {
+ if (UM(symbol__start(pair)) == mem_start)
+ goto next_pair;
+
+ pr_debug("WARN: %#" PRIx64 ": diff name v: %s k: %s\n",
+ mem_start, symbol__name(sym),
+ symbol__name(pair));
+ } else {
+ pr_debug("WARN: %#" PRIx64 ": diff name v: %s k: %s\n",
+ mem_start, symbol__name(sym),
+ symbol__name(first_pair));
+ }
+
+ return 0;
+ }
+ } else if (mem_start == map__end(args->kallsyms.vmlinux_map)) {
+ /*
+ * Ignore aliases to _etext, i.e. to the end of the kernel text area,
+ * such as __indirect_thunk_end.
+ */
+ return 0;
+ } else if (is_ignored_symbol(symbol__name(sym), symbol__type(sym))) {
+ /*
+ * Ignore hidden symbols, see scripts/kallsyms.c for the details
+ */
+ return 0;
+ } else {
+ pr_debug("ERR : %#" PRIx64 ": %s not on kallsyms\n",
+ mem_start, symbol__name(sym));
+ }
+
+ args->err = -1;
+ return 0;
+}
+
static int test__vmlinux_matches_kallsyms(struct test_suite *test __maybe_unused,
int subtest __maybe_unused)
{
int err = TEST_FAIL;
- struct rb_node *nd;
- struct symbol *sym;
- struct map *kallsyms_map;
struct machine vmlinux = { 0 };
struct maps *maps;
- u64 mem_start, mem_end;
struct test__vmlinux_matches_kallsyms_cb_args args;
/*
@@ -240,16 +328,6 @@ static int test__vmlinux_matches_kallsyms(struct test_suite *test __maybe_unused
goto out;
}
- /*
- * Step 4:
- *
- * kallsyms will be internally on demand sorted by name so that we can
- * find the reference relocation * symbol, i.e. the symbol we will use
- * to see if the running kernel was relocated by checking if it has the
- * same value in the vmlinux file we load.
- */
- kallsyms_map = machine__kernel_map(&args.kallsyms);
-
/*
* Step 5:
*
@@ -279,7 +357,7 @@ static int test__vmlinux_matches_kallsyms(struct test_suite *test __maybe_unused
goto out;
}
- err = 0;
+ args.err = 0;
/*
* Step 7:
*
@@ -287,85 +365,8 @@ static int test__vmlinux_matches_kallsyms(struct test_suite *test __maybe_unused
* in the kallsyms dso. For the ones that are in both, check its names and
* end addresses too.
*/
- map__for_each_symbol(args.vmlinux_map, sym, nd) {
- struct symbol *pair, *first_pair;
-
- sym = rb_entry(nd, struct symbol, rb_node);
-
- if (symbol__start(sym) == symbol__end(sym))
- continue;
-
- mem_start = map__unmap_ip(args.vmlinux_map,
- symbol__start(sym));
- mem_end = map__unmap_ip(args.vmlinux_map, symbol__end(sym));
-
- first_pair = machine__find_kernel_symbol(&args.kallsyms, mem_start, NULL);
- pair = first_pair;
-
- if (pair && UM(symbol__start(pair)) == mem_start) {
-next_pair:
- if (arch__compare_symbol_names(symbol__name(sym),
- symbol__name(pair)) == 0) {
- /*
- * kallsyms don't have the symbol end, so we
- * set that by using the next symbol start - 1,
- * in some cases we get this up to a page
- * wrong, trace_kmalloc when I was developing
- * this code was one such example, 2106 bytes
- * off the real size. More than that and we
- * _really_ have a problem.
- */
- s64 skew = mem_end - UM(symbol__end(pair));
- if (llabs(skew) >= page_size)
- pr_debug("WARN: %#" PRIx64 ": diff end addr for %s v: %#" PRIx64 " k: %#" PRIx64 "\n",
- mem_start, symbol__name(sym),
- mem_end,
- UM(symbol__end(pair)));
-
- /*
- * Do not count this as a failure, because we
- * could really find a case where it's not
- * possible to get proper function end from
- * kallsyms.
- */
- continue;
- } else {
- pair = machine__find_kernel_symbol_by_name(&args.kallsyms,
- symbol__name(sym),
- NULL);
- if (pair) {
- if (UM(symbol__start(pair)) == mem_start)
- goto next_pair;
-
- pr_debug("WARN: %#" PRIx64 ": diff name v: %s k: %s\n",
- mem_start, symbol__name(sym),
- symbol__name(pair));
- } else {
- pr_debug("WARN: %#" PRIx64 ": diff name v: %s k: %s\n",
- mem_start, symbol__name(sym),
- symbol__name(first_pair));
- }
-
- continue;
- }
- } else if (mem_start == map__end(args.kallsyms.vmlinux_map)) {
- /*
- * Ignore aliases to _etext, i.e. to the end of the kernel text area,
- * such as __indirect_thunk_end.
- */
- continue;
- } else if (is_ignored_symbol(symbol__name(sym), symbol__type(sym))) {
- /*
- * Ignore hidden symbols, see scripts/kallsyms.c for the details
- */
- continue;
- } else {
- pr_debug("ERR : %#" PRIx64 ": %s not on kallsyms\n",
- mem_start, symbol__name(sym));
- }
-
- err = -1;
- }
+ map__for_each_symbol(args.vmlinux_map, test__vmlinux_matches_kallsyms_sym_cb, &args);
+ err = args.err;
if (verbose <= 0)
goto out;
diff --git a/tools/perf/ui/browsers/map.c b/tools/perf/ui/browsers/map.c
index 38e123d45a1f..bb35d4965a3a 100644
--- a/tools/perf/ui/browsers/map.c
+++ b/tools/perf/ui/browsers/map.c
@@ -24,7 +24,7 @@ struct map_browser {
static void map_browser__write(struct ui_browser *browser, void *nd, int row)
{
- struct symbol *sym = rb_entry(nd, struct symbol, rb_node);
+ struct symbol *sym = *(struct symbol **)nd;
struct map_browser *mb = container_of(browser, struct map_browser, b);
bool current_entry = ui_browser__is_current_entry(browser, row);
int width;
@@ -57,17 +57,16 @@ static int map_browser__search(struct map_browser *browser)
sym = map__find_symbol_by_name(browser->map, target);
if (sym != NULL) {
- struct rb_node *nd;
- u32 idx = 0;
+ struct symbol **entries = browser->b.entries;
/*
- * Walk the map browser's symbol entries to find the matching
- * symbol node and its display row index, then position the
- * browser cursor at that entry.
+ * Scan the map browser's symbol entries to find the matching
+ * symbol and its display row index, then position the browser
+ * cursor at that entry.
*/
- for (nd = rb_first(browser->b.entries); nd; nd = rb_next(nd), ++idx) {
- if (&sym->rb_node == nd) {
- browser->b.top = nd;
+ for (u32 idx = 0; idx < browser->b.nr_entries; ++idx) {
+ if (entries[idx] == sym) {
+ browser->b.top = &entries[idx];
browser->b.index = browser->b.top_idx = idx;
break;
}
@@ -110,27 +109,48 @@ static int map_browser__run(struct map_browser *browser)
int map__browse(struct map *map)
{
+ struct symbols *symbols = dso__symbols(map__dso(map));
struct map_browser mb = {
.b = {
- .entries = dso__symbols(map__dso(map)),
- .refresh = ui_browser__rb_tree_refresh,
- .seek = ui_browser__rb_tree_seek,
+ .refresh = ui_browser__argv_refresh,
+ .seek = ui_browser__argv_seek,
.write = map_browser__write,
},
.map = map,
};
- struct rb_node *nd;
+ struct symbol **entries = NULL;
+ unsigned int nr_entries = 0;
char tmp[BITS_PER_LONG / 4];
u64 maxaddr = 0;
-
- for (nd = rb_first(mb.b.entries); nd; nd = rb_next(nd)) {
- struct symbol *pos = rb_entry(nd, struct symbol, rb_node);
-
- if (maxaddr < symbol__end(pos))
- maxaddr = symbol__end(pos);
- ++mb.b.nr_entries;
+ int ret;
+
+ /*
+ * Snapshot the DSO's sorted symbol array under symbols->lock for use
+ * with ui_browser__argv_refresh and ui_browser__argv_seek.
+ */
+ symbols__sort_read_lock(symbols);
+ nr_entries = symbols->cnt;
+ if (nr_entries > 0) {
+ entries = malloc(nr_entries * sizeof(*entries));
+ if (entries) {
+ for (unsigned int idx = 0; idx < nr_entries; idx++) {
+ struct symbol *pos = symbols->symbols[idx];
+
+ entries[idx] = pos;
+ if (maxaddr < symbol__end(pos))
+ maxaddr = symbol__end(pos);
+ }
+ } else {
+ nr_entries = 0;
+ }
}
+ up_read(&symbols->lock);
+
+ mb.b.entries = entries;
+ mb.b.nr_entries = nr_entries;
mb.addrlen = snprintf(tmp, sizeof(tmp), "%" PRIx64, maxaddr);
- return map_browser__run(&mb);
+ ret = map_browser__run(&mb);
+ free(entries);
+ return ret;
}
diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c
index f4aee90b43a3..c7bca2acf938 100644
--- a/tools/perf/util/auxtrace.c
+++ b/tools/perf/util/auxtrace.c
@@ -2713,61 +2713,96 @@ static bool dso_sym_match(struct symbol *sym, const char *name, int *cnt,
idx < 0);
}
+struct print_duplicate_syms_args {
+ const char *sym_name;
+ bool near;
+ int cnt;
+};
+
+static int print_duplicate_syms_cb(struct symbol *sym, void *data)
+{
+ struct print_duplicate_syms_args *args = data;
+
+ if (dso_sym_match(sym, args->sym_name, &args->cnt, -1)) {
+ pr_err("#%d\t0x%"PRIx64"\t%c\t%s\n",
+ ++args->cnt, symbol__start(sym),
+ symbol__binding(sym) == STB_GLOBAL ? 'g' :
+ symbol__binding(sym) == STB_LOCAL ? 'l' : 'w',
+ symbol__name(sym));
+ args->near = true;
+ } else if (args->near) {
+ args->near = false;
+ pr_err("\t\twhich is near\t\t%s\n", symbol__name(sym));
+ }
+ return 0;
+}
+
static void print_duplicate_syms(struct dso *dso, const char *sym_name)
{
- struct symbol *sym;
- bool near = false;
- int cnt = 0;
+ struct print_duplicate_syms_args args = {
+ .sym_name = sym_name,
+ .near = false,
+ .cnt = 0,
+ };
pr_err("Multiple symbols with name '%s'\n", sym_name);
- sym = dso__first_symbol(dso);
- while (sym) {
- if (dso_sym_match(sym, sym_name, &cnt, -1)) {
- pr_err("#%d\t0x%"PRIx64"\t%c\t%s\n",
- ++cnt, symbol__start(sym),
- symbol__binding(sym) == STB_GLOBAL ? 'g' :
- symbol__binding(sym) == STB_LOCAL ? 'l' : 'w',
- symbol__name(sym));
- near = true;
- } else if (near) {
- near = false;
- pr_err("\t\twhich is near\t\t%s\n", symbol__name(sym));
- }
- sym = dso__next_symbol(sym);
- }
+ dso__for_each_symbol(dso, print_duplicate_syms_cb, &args);
pr_err("Disambiguate symbol name by inserting #n after the name e.g. %s #2\n",
sym_name);
pr_err("Or select a global symbol by inserting #0 or #g or #G\n");
}
+struct find_dso_sym_args {
+ const char *sym_name;
+ u64 *start;
+ u64 *size;
+ int idx;
+ int cnt;
+ bool duplicate;
+};
+
+static int find_dso_sym_cb(struct symbol *sym, void *data)
+{
+ struct find_dso_sym_args *args = data;
+
+ if (*args->start) {
+ if (!*args->size)
+ *args->size = symbol__start(sym) - *args->start;
+ if (args->idx > 0) {
+ if (*args->size)
+ return 1;
+ } else if (dso_sym_match(sym, args->sym_name, &args->cnt, args->idx)) {
+ args->duplicate = true;
+ return 1;
+ }
+ } else if (dso_sym_match(sym, args->sym_name, &args->cnt, args->idx)) {
+ *args->start = symbol__start(sym);
+ *args->size = symbol__end(sym) - symbol__start(sym);
+ }
+ return 0;
+}
+
static int find_dso_sym(struct dso *dso, const char *sym_name, u64 *start,
u64 *size, int idx)
{
- struct symbol *sym;
- int cnt = 0;
+ struct find_dso_sym_args args = {
+ .sym_name = sym_name,
+ .start = start,
+ .size = size,
+ .idx = idx,
+ .cnt = 0,
+ .duplicate = false,
+ };
*start = 0;
*size = 0;
- sym = dso__first_symbol(dso);
- while (sym) {
- if (*start) {
- if (!*size)
- *size = symbol__start(sym) - *start;
- if (idx > 0) {
- if (*size)
- return 0;
- } else if (dso_sym_match(sym, sym_name, &cnt, idx)) {
- print_duplicate_syms(dso, sym_name);
- return -EINVAL;
- }
- } else if (dso_sym_match(sym, sym_name, &cnt, idx)) {
- *start = symbol__start(sym);
- *size = symbol__end(sym) - symbol__start(sym);
- }
- sym = dso__next_symbol(sym);
+ dso__for_each_symbol(dso, find_dso_sym_cb, &args);
+ if (args.duplicate) {
+ print_duplicate_syms(dso, sym_name);
+ return -EINVAL;
}
if (!*start)
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c
index 39cb82693596..1b96853164be 100644
--- a/tools/perf/util/dso.c
+++ b/tools/perf/util/dso.c
@@ -1687,12 +1687,7 @@ bool dso__loaded(const struct dso *dso)
bool dso__sorted_by_name(const struct dso *dso)
{
- return RC_CHK_ACCESS(dso)->sorted_by_name;
-}
-
-void dso__set_sorted_by_name(struct dso *dso)
-{
- RC_CHK_ACCESS(dso)->sorted_by_name = true;
+ return RC_CHK_ACCESS(dso)->symbols.sorted_by_name;
}
struct dso *dso__new_id(const char *name, const struct dso_id *id)
@@ -1710,9 +1705,7 @@ struct dso *dso__new_id(const char *name, const struct dso_id *id)
dso->id = *id;
dso__set_long_name_id(res, dso->name, false);
dso__set_short_name(res, dso->name, false);
- dso->symbols = RB_ROOT_CACHED;
- dso->symbol_names = NULL;
- dso->symbol_names_len = 0;
+ symbols__init(&dso->symbols);
dso->inlined_nodes = RB_ROOT_CACHED;
dso->srclines = RB_ROOT_CACHED;
dso->data_types = RB_ROOT;
@@ -1724,7 +1717,6 @@ struct dso *dso__new_id(const char *name, const struct dso_id *id)
dso->is_64_bit = (sizeof(void *) == 8);
dso->loaded = 0;
dso->rel = 0;
- dso->sorted_by_name = 0;
dso->has_srcline = 1;
dso->a2l_fails = 1;
dso->kernel = DSO_SPACE__USER;
@@ -1758,9 +1750,7 @@ void dso__delete(struct dso *dso)
/* free inlines first, as they reference symbols */
inlines__tree_delete(&RC_CHK_ACCESS(dso)->inlined_nodes);
srcline__tree_delete(&RC_CHK_ACCESS(dso)->srclines);
- symbols__delete(&RC_CHK_ACCESS(dso)->symbols);
- RC_CHK_ACCESS(dso)->symbol_names_len = 0;
- zfree(&RC_CHK_ACCESS(dso)->symbol_names);
+ symbols__exit(&RC_CHK_ACCESS(dso)->symbols);
annotated_data_type__tree_delete(dso__data_types(dso));
global_var_type__tree_delete(dso__global_vars(dso));
@@ -1883,22 +1873,34 @@ static size_t dso__fprintf_buildid(struct dso *dso, FILE *fp)
return fprintf(fp, "%s", sbuild_id);
}
+struct dso__fprintf_cb_args {
+ FILE *fp;
+ size_t ret;
+};
+
+static int dso__fprintf_cb(struct symbol *pos, void *data)
+{
+ struct dso__fprintf_cb_args *args = data;
+
+ args->ret += symbol__fprintf(pos, args->fp);
+ return 0;
+}
+
size_t dso__fprintf(struct dso *dso, FILE *fp)
{
- struct rb_node *nd;
- size_t ret = fprintf(fp, "dso: %s (", dso__short_name(dso));
+ struct dso__fprintf_cb_args args = {
+ .fp = fp,
+ .ret = fprintf(fp, "dso: %s (", dso__short_name(dso)),
+ };
if (dso__short_name(dso) != dso__long_name(dso))
- ret += fprintf(fp, "%s, ", dso__long_name(dso));
- ret += fprintf(fp, "%sloaded, ", dso__loaded(dso) ? "" : "NOT ");
- ret += dso__fprintf_buildid(dso, fp);
- ret += fprintf(fp, ")\n");
- for (nd = rb_first_cached(dso__symbols(dso)); nd; nd = rb_next(nd)) {
- struct symbol *pos = rb_entry(nd, struct symbol, rb_node);
- ret += symbol__fprintf(pos, fp);
- }
+ args.ret += fprintf(fp, "%s, ", dso__long_name(dso));
+ args.ret += fprintf(fp, "%sloaded, ", dso__loaded(dso) ? "" : "NOT ");
+ args.ret += dso__fprintf_buildid(dso, fp);
+ args.ret += fprintf(fp, ")\n");
+ dso__for_each_symbol(dso, dso__fprintf_cb, &args);
- return ret;
+ return args.ret;
}
enum dso_type dso__type(struct dso *dso, struct machine *machine)
diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h
index e7d5f4bbf894..1de446966f01 100644
--- a/tools/perf/util/dso.h
+++ b/tools/perf/util/dso.h
@@ -13,6 +13,7 @@
#include "build-id.h"
#include "debuginfo.h"
#include "mutex.h"
+#include "symbol.h"
#include <internal/rc_check.h>
struct machine;
@@ -287,18 +288,12 @@ struct auxtrace_cache;
DECLARE_RC_STRUCT(dso) {
struct mutex lock;
struct dsos *dsos;
- struct rb_root_cached symbols;
- struct symbol **symbol_names;
- size_t symbol_names_len;
+ struct symbols symbols;
struct rb_root_cached inlined_nodes;
struct rb_root_cached srclines;
struct rb_root data_types;
struct rb_root global_vars;
- struct {
- u64 addr;
- struct symbol *symbol;
- } last_find_result;
u64 text_offset;
u64 text_end;
const char *short_name;
@@ -343,7 +338,6 @@ DECLARE_RC_STRUCT(dso) {
u8 short_name_allocated:1;
u8 long_name_allocated:1;
u8 is_64_bit:1;
- bool sorted_by_name;
bool loaded;
u8 rel;
char name[];
@@ -357,11 +351,11 @@ int dso_id__cmp(const struct dso_id *a, const struct dso_id *b);
/* dso__for_each_symbol - iterate over the symbols of given type
*
* @dso: the 'struct dso *' in which symbols are iterated
- * @pos: the 'struct symbol *' to use as a loop cursor
- * @n: the 'struct rb_node *' to use as a temporary storage
+ * @cb: callback function invoked for each symbol
+ * @data: opaque data pointer passed to @cb
*/
-#define dso__for_each_symbol(dso, pos, n) \
- symbols__for_each_entry(dso__symbols(dso), pos, n)
+#define dso__for_each_symbol(dso, cb, data) \
+ symbols__for_each_entry(dso__symbols(dso), cb, data)
static inline void *dso__a2l(const struct dso *dso)
{
@@ -590,26 +584,6 @@ static inline void dso__set_kernel(struct dso *dso, enum dso_space_type kernel)
RC_CHK_ACCESS(dso)->kernel = kernel;
}
-static inline u64 dso__last_find_result_addr(const struct dso *dso)
-{
- return RC_CHK_ACCESS(dso)->last_find_result.addr;
-}
-
-static inline void dso__set_last_find_result_addr(struct dso *dso, u64 addr)
-{
- RC_CHK_ACCESS(dso)->last_find_result.addr = addr;
-}
-
-static inline struct symbol *dso__last_find_result_symbol(const struct dso *dso)
-{
- return RC_CHK_ACCESS(dso)->last_find_result.symbol;
-}
-
-static inline void dso__set_last_find_result_symbol(struct dso *dso, struct symbol *symbol)
-{
- RC_CHK_ACCESS(dso)->last_find_result.symbol = symbol;
-}
-
static inline enum dso_load_errno *dso__load_errno(struct dso *dso)
{
return &RC_CHK_ACCESS(dso)->load_errno;
@@ -722,29 +696,19 @@ static inline struct rb_root *dso__global_vars(struct dso *dso)
return &RC_CHK_ACCESS(dso)->global_vars;
}
-static inline struct rb_root_cached *dso__symbols(struct dso *dso)
+static inline struct symbols *dso__symbols(struct dso *dso)
{
return &RC_CHK_ACCESS(dso)->symbols;
}
static inline struct symbol **dso__symbol_names(struct dso *dso)
{
- return RC_CHK_ACCESS(dso)->symbol_names;
-}
-
-static inline void dso__set_symbol_names(struct dso *dso, struct symbol **names)
-{
- RC_CHK_ACCESS(dso)->symbol_names = names;
+ return RC_CHK_ACCESS(dso)->symbols.symbols_by_name;
}
static inline size_t dso__symbol_names_len(struct dso *dso)
{
- return RC_CHK_ACCESS(dso)->symbol_names_len;
-}
-
-static inline void dso__set_symbol_names_len(struct dso *dso, size_t len)
-{
- RC_CHK_ACCESS(dso)->symbol_names_len = len;
+ return RC_CHK_ACCESS(dso)->symbols.num_by_name;
}
static inline const char *dso__symsrc_filename(const struct dso *dso)
@@ -815,13 +779,12 @@ bool dso__loaded(const struct dso *dso);
static inline bool dso__has_symbols(const struct dso *dso)
{
- return !RB_EMPTY_ROOT(&RC_CHK_ACCESS(dso)->symbols.rb_root);
+ return RC_CHK_ACCESS(dso)->symbols.cnt > 0;
}
char *dso__filename_with_chroot(const struct dso *dso, const char *filename);
bool dso__sorted_by_name(const struct dso *dso);
-void dso__set_sorted_by_name(struct dso *dso);
void dso__sort_by_name(struct dso *dso);
int dso__swap_init(struct dso *dso, unsigned char eidata);
@@ -941,8 +904,6 @@ struct map *dso__new_map(const char *name);
struct dso *machine__findnew_kernel(struct machine *machine, const char *name,
const char *short_name, int dso_type);
-void dso__reset_find_symbol_cache(struct dso *dso);
-
size_t dso__fprintf_symbols_by_name(struct dso *dso, FILE *fp);
size_t dso__fprintf(struct dso *dso, FILE *fp);
diff --git a/tools/perf/util/intel-pt.c b/tools/perf/util/intel-pt.c
index a9f43ac8d152..2e64b3faabc9 100644
--- a/tools/perf/util/intel-pt.c
+++ b/tools/perf/util/intel-pt.c
@@ -2981,13 +2981,37 @@ static int intel_pt_sample(struct intel_pt_queue *ptq)
return 0;
}
+struct intel_pt_switch_ip_args {
+ struct map *map;
+ const char *name;
+ bool check_global;
+ u64 ip;
+};
+
+static int intel_pt_switch_ip_cb(struct symbol *sym, void *data)
+{
+ struct intel_pt_switch_ip_args *args = data;
+ u64 ip;
+
+ if (args->check_global && symbol__binding(sym) != STB_GLOBAL)
+ return 0;
+
+ if (!strcmp(symbol__name(sym), args->name)) {
+ ip = map__unmap_ip(args->map, symbol__start(sym));
+ if (ip >= map__start(args->map) && ip < map__end(args->map)) {
+ args->ip = ip;
+ return 1;
+ }
+ }
+ return 0;
+}
+
static u64 intel_pt_switch_ip(struct intel_pt *pt, u64 *ptss_ip)
{
struct machine *machine = pt->machine;
struct map *map;
- struct symbol *sym, *start;
- u64 ip, switch_ip = 0;
- const char *ptss;
+ struct intel_pt_switch_ip_args args;
+ u64 switch_ip = 0;
if (ptss_ip)
*ptss_ip = 0;
@@ -2999,36 +3023,28 @@ static u64 intel_pt_switch_ip(struct intel_pt *pt, u64 *ptss_ip)
if (map__load(map))
return 0;
- start = dso__first_symbol(map__dso(map));
-
- for (sym = start; sym; sym = dso__next_symbol(sym)) {
- if (symbol__binding(sym) == STB_GLOBAL &&
- !strcmp(symbol__name(sym), "__switch_to")) {
- ip = map__unmap_ip(map, symbol__start(sym));
- if (ip >= map__start(map) && ip < map__end(map)) {
- switch_ip = ip;
- break;
- }
- }
- }
+ args = (struct intel_pt_switch_ip_args) {
+ .map = map,
+ .name = "__switch_to",
+ .check_global = true,
+ .ip = 0,
+ };
+ map__for_each_symbol(map, intel_pt_switch_ip_cb, &args);
+ switch_ip = args.ip;
if (!switch_ip || !ptss_ip)
return 0;
- if (pt->have_sched_switch == 1)
- ptss = "perf_trace_sched_switch";
- else
- ptss = "__perf_event_task_sched_out";
-
- for (sym = start; sym; sym = dso__next_symbol(sym)) {
- if (!strcmp(symbol__name(sym), ptss)) {
- ip = map__unmap_ip(map, symbol__start(sym));
- if (ip >= map__start(map) && ip < map__end(map)) {
- *ptss_ip = ip;
- break;
- }
- }
- }
+ args = (struct intel_pt_switch_ip_args) {
+ .map = map,
+ .name = pt->have_sched_switch == 1 ?
+ "perf_trace_sched_switch" :
+ "__perf_event_task_sched_out",
+ .check_global = false,
+ .ip = 0,
+ };
+ map__for_each_symbol(map, intel_pt_switch_ip_cb, &args);
+ *ptss_ip = args.ip;
return switch_ip;
}
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index fa2bd8e0e1f1..cb30d858f62d 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -1095,29 +1095,35 @@ int machine__create_extra_kernel_map(struct machine *machine,
return err;
}
-static u64 find_entry_trampoline(struct dso *dso)
+static int find_entry_trampoline_cb(struct symbol *sym, void *data)
{
/* Duplicates are removed so lookup all aliases */
- const char *syms[] = {
+ static const char * const syms[] = {
"_entry_trampoline",
"__entry_trampoline_start",
"entry_SYSCALL_64_trampoline",
};
- struct symbol *sym = dso__first_symbol(dso);
- unsigned int i;
+ u64 *addr = data;
- for (; sym; sym = dso__next_symbol(sym)) {
- if (symbol__binding(sym) != STB_GLOBAL)
- continue;
- for (i = 0; i < ARRAY_SIZE(syms); i++) {
- if (!strcmp(symbol__name(sym), syms[i]))
- return symbol__start(sym);
+ if (symbol__binding(sym) != STB_GLOBAL)
+ return 0;
+ for (unsigned int i = 0; i < ARRAY_SIZE(syms); i++) {
+ if (!strcmp(symbol__name(sym), syms[i])) {
+ *addr = symbol__start(sym);
+ return 1;
}
}
-
return 0;
}
+static u64 find_entry_trampoline(struct dso *dso)
+{
+ u64 addr = 0;
+
+ dso__for_each_symbol(dso, find_entry_trampoline_cb, &addr);
+ return addr;
+}
+
/*
* These values can be used for kernels that do not have symbols for the entry
* trampolines in kallsyms.
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c
index 85216fa71f1a..19afe676adf2 100644
--- a/tools/perf/util/map.c
+++ b/tools/perf/util/map.c
@@ -314,27 +314,22 @@ void map__put(struct map *map)
void map__fixup_start(struct map *map)
{
- struct dso *dso = map__dso(map);
- struct rb_root_cached *symbols = dso__symbols(dso);
- struct rb_node *nd = rb_first_cached(symbols);
-
- if (nd != NULL) {
- struct symbol *sym = rb_entry(nd, struct symbol, rb_node);
+ struct symbols *symbols = dso__symbols(map__dso(map));
- map__set_start(map, symbol__start(sym));
- }
+ symbols__sort_read_lock(symbols);
+ if (symbols->cnt > 0)
+ map__set_start(map, symbol__start(symbols->symbols[0]));
+ up_read(&symbols->lock);
}
void map__fixup_end(struct map *map)
{
- struct dso *dso = map__dso(map);
- struct rb_root_cached *symbols = dso__symbols(dso);
- struct rb_node *nd = rb_last(&symbols->rb_root);
+ struct symbols *symbols = dso__symbols(map__dso(map));
- if (nd != NULL) {
- struct symbol *sym = rb_entry(nd, struct symbol, rb_node);
- map__set_end(map, symbol__end(sym));
- }
+ symbols__sort_read_lock(symbols);
+ if (symbols->cnt > 0)
+ map__set_end(map, symbol__end(symbols->symbols[symbols->cnt - 1]));
+ up_read(&symbols->lock);
}
#define DSO__DELETED "(deleted)"
diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h
index f49b4aaacf2f..3d8f72e70d49 100644
--- a/tools/perf/util/map.h
+++ b/tools/perf/util/map.h
@@ -146,12 +146,12 @@ struct thread;
/* map__for_each_symbol - iterate over the symbols in the given map
*
* @map: the 'struct map *' in which symbols are iterated
- * @pos: the 'struct symbol *' to use as a loop cursor
- * @n: the 'struct rb_node *' to use as a temporary storage
+ * @cb: callback function invoked for each symbol
+ * @data: opaque data pointer passed to @cb
* Note: caller must ensure map->dso is not NULL (map is loaded).
*/
-#define map__for_each_symbol(map, pos, n) \
- dso__for_each_symbol(map__dso(map), pos, n)
+#define map__for_each_symbol(map, cb, data) \
+ dso__for_each_symbol(map__dso(map), cb, data)
/* map__for_each_symbol_with_name - iterate over the symbols in the given map
* that have the given name
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 128076a4b92d..47dbe865c2f9 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -3047,49 +3047,63 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
return ret;
}
-static int find_probe_functions(struct map *map, char *name,
- struct symbol **syms)
+struct find_probe_functions_args {
+ const char *name;
+ struct symbol **syms;
+ int found;
+ bool cut_version;
+};
+
+static int find_probe_functions_cb(struct symbol *sym, void *data)
{
- int found = 0;
- struct symbol *sym;
- struct rb_node *tmp;
+ struct find_probe_functions_args *args = data;
const char *norm, *ver;
char *buf = NULL;
- bool cut_version = true;
-
- if (map__load(map) < 0)
- return -EACCES; /* Possible permission error to load symbols */
- /* If user gives a version, don't cut off the version from symbols */
- if (strchr(name, '@'))
- cut_version = false;
-
- map__for_each_symbol(map, sym, tmp) {
- norm = arch__normalize_symbol_name(symbol__name(sym));
- if (!norm)
- continue;
+ norm = arch__normalize_symbol_name(symbol__name(sym));
+ if (!norm)
+ return 0;
- if (cut_version) {
- /* We don't care about default symbol or not */
- ver = strchr(norm, '@');
- if (ver) {
- buf = strndup(norm, ver - norm);
- if (!buf)
- return -ENOMEM;
- norm = buf;
- }
+ if (args->cut_version) {
+ /* We don't care about default symbol or not */
+ ver = strchr(norm, '@');
+ if (ver) {
+ buf = strndup(norm, ver - norm);
+ if (!buf)
+ return -ENOMEM;
+ norm = buf;
}
+ }
- if (strglobmatch(norm, name)) {
- found++;
- if (syms && found < probe_conf.max_probes)
- syms[found - 1] = sym;
- }
- if (buf)
- zfree(&buf);
+ if (strglobmatch(norm, args->name)) {
+ args->found++;
+ if (args->syms && args->found < probe_conf.max_probes)
+ args->syms[args->found - 1] = sym;
}
+ free(buf);
+ return 0;
+}
+
+static int find_probe_functions(struct map *map, char *name,
+ struct symbol **syms)
+{
+ struct find_probe_functions_args args = {
+ .name = name,
+ .syms = syms,
+ /* If user gives a version, don't cut off the version from symbols */
+ .cut_version = strchr(name, '@') == NULL,
+ .found = 0,
+ };
+ int ret;
+
+ if (map__load(map) < 0)
+ return -EACCES; /* Possible permission error to load symbols */
+
+ ret = map__for_each_symbol(map, find_probe_functions_cb, &args);
+ if (ret < 0)
+ return ret;
- return found;
+ return args.found;
}
void __weak arch__fix_tev_from_maps(struct perf_probe_event *pev __maybe_unused,
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 2ec650a7834d..d25d8c96ee66 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -213,57 +213,157 @@ static int choose_best_symbol(struct symbol *syma, struct symbol *symb)
return arch__choose_best_symbol(syma, symb);
}
-void symbols__fixup_duplicate(struct rb_root_cached *symbols)
+void symbols__init(struct symbols *symbols)
{
- struct rb_node *nd;
- struct symbol *curr, *next;
+ init_rwsem(&symbols->lock);
+ symbols->symbols = NULL;
+ symbols->symbols_by_name = NULL;
+ symbols->cnt = 0;
+ symbols->allocated = 0;
+ symbols->num_by_name = 0;
+ symbols->sorted = true;
+ symbols->sorted_by_name = false;
+}
+
+static void symbols__delete_locked(struct symbols *symbols)
+{
+ for (unsigned int i = 0; i < symbols->cnt; i++)
+ symbol__delete(symbols->symbols[i]);
+
+ zfree(&symbols->symbols);
+ zfree(&symbols->symbols_by_name);
+ symbols->cnt = 0;
+ symbols->allocated = 0;
+ symbols->num_by_name = 0;
+ symbols->sorted = true;
+ symbols->sorted_by_name = false;
+}
+
+void symbols__delete(struct symbols *symbols)
+{
+ down_write(&symbols->lock);
+ symbols__delete_locked(symbols);
+ up_write(&symbols->lock);
+}
+
+void symbols__exit(struct symbols *symbols)
+{
+ symbols__delete(symbols);
+ exit_rwsem(&symbols->lock);
+}
+
+static inline bool symbol__less_or_equal(const struct symbol *a, const struct symbol *b)
+{
+ if (symbol__start(a) != symbol__start(b))
+ return symbol__start(a) < symbol__start(b);
+ return symbol__end(a) <= symbol__end(b);
+}
+
+static int symbols__sort_cmp(const void *vlhs, const void *vrhs)
+{
+ const struct symbol *lhs = *((const struct symbol **)vlhs);
+ const struct symbol *rhs = *((const struct symbol **)vrhs);
+
+ if (symbol__start(lhs) != symbol__start(rhs))
+ return symbol__start(lhs) < symbol__start(rhs) ? -1 : 1;
+ if (symbol__end(lhs) != symbol__end(rhs))
+ return symbol__end(lhs) < symbol__end(rhs) ? -1 : 1;
+ return 0;
+}
+
+static void symbols__sort_locked(struct symbols *symbols)
+{
+ if (symbols->sorted)
+ return;
+
+ if (symbols->cnt > 1) {
+ qsort(symbols->symbols, symbols->cnt, sizeof(struct symbol *),
+ symbols__sort_cmp);
+ }
+ symbols->sorted = true;
+}
+
+/*
+ * Acquire @symbols->lock for reading after ensuring @symbols->symbols is
+ * sorted.
+ */
+void symbols__sort_read_lock(struct symbols *symbols)
+ NO_THREAD_SAFETY_ANALYSIS
+{
+ down_read(&symbols->lock);
+ while (!symbols->sorted) {
+ up_read(&symbols->lock);
+ down_write(&symbols->lock);
+ symbols__sort_locked(symbols);
+ up_write(&symbols->lock);
+ down_read(&symbols->lock);
+ }
+}
+
+/*
+ * Deduplicate symbols sharing the same start address by sorting @symbols and
+ * compacting the array in place, keeping the best symbol at each address as
+ * chosen by choose_best_symbol().
+ */
+void symbols__fixup_duplicate(struct symbols *symbols)
+{
+ unsigned int write_idx = 0;
if (symbol_conf.allow_aliases)
return;
- nd = rb_first_cached(symbols);
+ down_write(&symbols->lock);
+ symbols__sort_locked(symbols);
+ if (symbols->cnt <= 1) {
+ up_write(&symbols->lock);
+ return;
+ }
- while (nd) {
- curr = rb_entry(nd, struct symbol, rb_node);
-again:
- nd = rb_next(&curr->rb_node);
- if (!nd)
- break;
+ for (unsigned int read_idx = 1; read_idx < symbols->cnt; read_idx++) {
+ struct symbol *curr = symbols->symbols[write_idx];
+ struct symbol *next = symbols->symbols[read_idx];
- next = rb_entry(nd, struct symbol, rb_node);
- if (symbol__start(curr) != symbol__start(next))
+ if (symbol__start(curr) != symbol__start(next)) {
+ symbols->symbols[++write_idx] = next;
continue;
+ }
if (choose_best_symbol(curr, next) == SYMBOL_A) {
if (symbol__type(next) == STT_GNU_IFUNC)
symbol__set_ifunc_alias(curr, true);
- rb_erase_cached(&next->rb_node, symbols);
symbol__delete(next);
- goto again;
} else {
if (symbol__type(curr) == STT_GNU_IFUNC)
symbol__set_ifunc_alias(next, true);
- nd = rb_next(&curr->rb_node);
- rb_erase_cached(&curr->rb_node, symbols);
symbol__delete(curr);
+ symbols->symbols[write_idx] = next;
}
}
+ symbols->cnt = write_idx + 1;
+ zfree(&symbols->symbols_by_name);
+ symbols->num_by_name = 0;
+ symbols->sorted_by_name = false;
+ up_write(&symbols->lock);
}
/* Update zero-sized symbols using the address of the next symbol */
-void symbols__fixup_end(struct rb_root_cached *symbols, bool is_kallsyms)
+void symbols__fixup_end(struct symbols *symbols, bool is_kallsyms)
{
- struct rb_node *nd, *prevnd = rb_first_cached(symbols);
struct symbol *curr, *prev;
- if (prevnd == NULL)
+ down_write(&symbols->lock);
+ symbols__sort_locked(symbols);
+
+ if (symbols->cnt == 0) {
+ up_write(&symbols->lock);
return;
+ }
- curr = rb_entry(prevnd, struct symbol, rb_node);
+ curr = symbols->symbols[0];
- for (nd = rb_next(prevnd); nd; nd = rb_next(nd)) {
+ for (unsigned int i = 1; i < symbols->cnt; i++) {
prev = curr;
- curr = rb_entry(nd, struct symbol, rb_node);
+ curr = symbols->symbols[i];
/*
* On some architecture kernel text segment start is located at
@@ -314,6 +414,8 @@ void symbols__fixup_end(struct rb_root_cached *symbols, bool is_kallsyms)
if (symbol__end(curr) == symbol__start(curr))
symbol__set_end(curr,
roundup(symbol__start(curr), 4096) + 4096);
+
+ up_write(&symbols->lock);
}
struct symbol *symbol__new(u64 start, u64 len, u8 binding, u8 type, const char *name)
@@ -395,97 +497,92 @@ static void symbol__set_idle(struct symbol *sym, bool idle)
new_flags |= (idle_val << SYMBOL_FLAG_IDLE_SHIFT);
} while (!atomic_compare_exchange_weak(&sym->flags, &old_flags, new_flags));
}
-void symbols__delete(struct rb_root_cached *symbols)
-{
- struct symbol *pos;
- struct rb_node *next = rb_first_cached(symbols);
- while (next) {
- pos = rb_entry(next, struct symbol, rb_node);
- next = rb_next(&pos->rb_node);
- rb_erase_cached(&pos->rb_node, symbols);
- symbol__delete(pos);
- }
-}
-
-void __symbols__insert(struct rb_root_cached *symbols, struct symbol *sym)
+int __symbols__insert(struct symbols *symbols, struct symbol *sym)
{
- struct rb_node **p = &symbols->rb_root.rb_node;
- struct rb_node *parent = NULL;
- const u64 ip = symbol__start(sym);
- struct symbol *s;
- bool leftmost = true;
+ if (symbols->cnt == symbols->allocated) {
+ unsigned int to_allocate = symbols->allocated ? symbols->allocated * 2 : 32;
+ struct symbol **temp = realloc(symbols->symbols,
+ sizeof(struct symbol *) * to_allocate);
- while (*p != NULL) {
- parent = *p;
- s = rb_entry(parent, struct symbol, rb_node);
- if (ip < symbol__start(s))
- p = &(*p)->rb_left;
- else {
- p = &(*p)->rb_right;
- leftmost = false;
+ if (!temp) {
+ symbol__delete(sym);
+ return -ENOMEM;
}
+ symbols->symbols = temp;
+ symbols->allocated = to_allocate;
}
- rb_link_node(&sym->rb_node, parent, p);
- rb_insert_color_cached(&sym->rb_node, symbols, leftmost);
+
+ if (symbols->sorted && symbols->cnt > 0 &&
+ !symbol__less_or_equal(symbols->symbols[symbols->cnt - 1], sym))
+ symbols->sorted = false;
+
+ symbols->symbols[symbols->cnt++] = sym;
+ symbols->sorted_by_name = false;
+ return 0;
}
-void symbols__insert(struct rb_root_cached *symbols, struct symbol *sym)
+int symbols__insert(struct symbols *symbols, struct symbol *sym)
{
- __symbols__insert(symbols, sym);
+ int ret;
+
+ down_write(&symbols->lock);
+ ret = __symbols__insert(symbols, sym);
+ up_write(&symbols->lock);
+ return ret;
}
-static struct symbol *symbols__find(struct rb_root_cached *symbols, u64 ip)
+/*
+ * Binary search @symbols->symbols for a symbol covering @ip. Lazily sorts
+ * @symbols under @symbols->lock if unsorted.
+ */
+static struct symbol *symbols__find(struct symbols *symbols, u64 ip)
{
- struct rb_node *n;
+ int low, high;
+ struct symbol *res = NULL;
if (symbols == NULL)
return NULL;
- n = symbols->rb_root.rb_node;
+ symbols__sort_read_lock(symbols);
- while (n) {
- struct symbol *s = rb_entry(n, struct symbol, rb_node);
+ low = 0;
+ high = (int)symbols->cnt - 1;
+ while (low <= high) {
+ int mid = low + (high - low) / 2;
+ struct symbol *s = symbols->symbols[mid];
+ u64 start = symbol__start(s);
+ u64 end = symbol__end(s);
- if (ip < symbol__start(s))
- n = n->rb_left;
- else if (ip > symbol__end(s) || (ip == symbol__end(s) && ip != symbol__start(s)))
- n = n->rb_right;
- else
- return s;
+ if (ip < start)
+ high = mid - 1;
+ else if (ip > end || (ip == end && ip != start))
+ low = mid + 1;
+ else {
+ res = s;
+ break;
+ }
}
+ up_read(&symbols->lock);
- return NULL;
-}
-
-static struct symbol *symbols__first(struct rb_root_cached *symbols)
-{
- struct rb_node *n = rb_first_cached(symbols);
-
- if (n)
- return rb_entry(n, struct symbol, rb_node);
-
- return NULL;
+ return res;
}
-static struct symbol *symbols__last(struct rb_root_cached *symbols)
+int symbols__for_each_entry(struct symbols *symbols,
+ int (*cb)(struct symbol *sym, void *data),
+ void *data)
{
- struct rb_node *n = rb_last(&symbols->rb_root);
-
- if (n)
- return rb_entry(n, struct symbol, rb_node);
-
- return NULL;
-}
-
-static struct symbol *symbols__next(struct symbol *sym)
-{
- struct rb_node *n = rb_next(&sym->rb_node);
+ int err = 0;
- if (n)
- return rb_entry(n, struct symbol, rb_node);
+ symbols__sort_read_lock(symbols);
+ for (unsigned int i = 0; i < symbols->cnt; i++) {
+ err = cb(symbols->symbols[i], data);
+ if (err)
+ break;
+ }
+ up_read(&symbols->lock);
- return NULL;
+ return err;
}
static int symbols__sort_name_cmp(const void *vlhs, const void *vrhs)
@@ -496,27 +593,31 @@ static int symbols__sort_name_cmp(const void *vlhs, const void *vrhs)
return strcmp(symbol__name(lhs), symbol__name(rhs));
}
-static struct symbol **symbols__sort_by_name(struct rb_root_cached *source, size_t *len)
+static void symbols__sort_by_name_locked(struct symbols *symbols)
{
- struct rb_node *nd;
struct symbol **result;
- size_t i = 0, size = 0;
+ unsigned int size = symbols->cnt;
- for (nd = rb_first_cached(source); nd; nd = rb_next(nd))
- size++;
+ if (symbols->sorted_by_name)
+ return;
+
+ zfree(&symbols->symbols_by_name);
+ symbols->num_by_name = 0;
+
+ if (size == 0) {
+ symbols->sorted_by_name = true;
+ return;
+ }
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);
+ return;
- result[i++] = pos;
- }
+ memcpy(result, symbols->symbols, sizeof(*result) * size);
qsort(result, size, sizeof(*result), symbols__sort_name_cmp);
- *len = size;
- return result;
+ symbols->symbols_by_name = result;
+ symbols->num_by_name = size;
+ symbols->sorted_by_name = true;
}
int symbol__match_symbol_name(const char *name, const char *str,
@@ -586,39 +687,34 @@ static struct symbol *symbols__find_by_name(struct symbol *symbols[],
return s;
}
-void dso__reset_find_symbol_cache(struct dso *dso)
+int dso__insert_symbol(struct dso *dso, struct symbol *sym)
{
- dso__set_last_find_result_addr(dso, 0);
- dso__set_last_find_result_symbol(dso, NULL);
-}
-
-void dso__insert_symbol(struct dso *dso, struct symbol *sym)
-{
- __symbols__insert(dso__symbols(dso), sym);
-
- /* update the symbol cache if necessary */
- if (dso__last_find_result_addr(dso) >= symbol__start(sym) &&
- (dso__last_find_result_addr(dso) < symbol__end(sym) ||
- symbol__start(sym) == symbol__end(sym))) {
- dso__set_last_find_result_symbol(dso, sym);
- }
+ return symbols__insert(dso__symbols(dso), sym);
}
void dso__delete_symbol(struct dso *dso, struct symbol *sym)
{
- rb_erase_cached(&sym->rb_node, dso__symbols(dso));
- symbol__delete(sym);
- dso__reset_find_symbol_cache(dso);
+ struct symbols *symbols = dso__symbols(dso);
+
+ down_write(&symbols->lock);
+ for (unsigned int i = 0; i < symbols->cnt; i++) {
+ if (symbols->symbols[i] == sym) {
+ memmove(&symbols->symbols[i], &symbols->symbols[i + 1],
+ (symbols->cnt - i - 1) * sizeof(struct symbol *));
+ symbols->cnt--;
+ zfree(&symbols->symbols_by_name);
+ symbols->num_by_name = 0;
+ symbols->sorted_by_name = false;
+ symbol__delete(sym);
+ break;
+ }
+ }
+ up_write(&symbols->lock);
}
struct symbol *dso__find_symbol(struct dso *dso, u64 addr)
{
- if (dso__last_find_result_addr(dso) != addr || dso__last_find_result_symbol(dso) == NULL) {
- dso__set_last_find_result_addr(dso, addr);
- dso__set_last_find_result_symbol(dso, symbols__find(dso__symbols(dso), addr));
- }
-
- return dso__last_find_result_symbol(dso);
+ return symbols__find(dso__symbols(dso), addr);
}
struct symbol *dso__find_symbol_nocache(struct dso *dso, u64 addr)
@@ -626,21 +722,6 @@ struct symbol *dso__find_symbol_nocache(struct dso *dso, u64 addr)
return symbols__find(dso__symbols(dso), addr);
}
-struct symbol *dso__first_symbol(struct dso *dso)
-{
- return symbols__first(dso__symbols(dso));
-}
-
-struct symbol *dso__last_symbol(struct dso *dso)
-{
- return symbols__last(dso__symbols(dso));
-}
-
-struct symbol *dso__next_symbol(struct symbol *sym)
-{
- return symbols__next(sym);
-}
-
struct symbol *dso__next_symbol_by_name(struct dso *dso, size_t *idx)
{
if (*idx + 1 >= dso__symbol_names_len(dso))
@@ -655,9 +736,12 @@ 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 *s = symbols__find_by_name(dso__symbol_names(dso),
- dso__symbol_names_len(dso),
- name, SYMBOL_TAG_INCLUDE__NONE, idx);
+ struct symbol *s;
+
+ dso__sort_by_name(dso);
+ s = symbols__find_by_name(dso__symbol_names(dso),
+ dso__symbol_names_len(dso),
+ name, SYMBOL_TAG_INCLUDE__NONE, idx);
if (!s) {
s = symbols__find_by_name(dso__symbol_names(dso), dso__symbol_names_len(dso),
name, SYMBOL_TAG_INCLUDE__DEFAULT_ONLY, idx);
@@ -667,17 +751,17 @@ struct symbol *dso__find_symbol_by_name(struct dso *dso, const char *name, size_
void dso__sort_by_name(struct dso *dso)
{
- mutex_lock(dso__lock(dso));
- if (!dso__sorted_by_name(dso)) {
- size_t len = 0;
+ struct symbols *symbols = dso__symbols(dso);
- dso__set_symbol_names(dso, symbols__sort_by_name(dso__symbols(dso), &len));
- if (dso__symbol_names(dso)) {
- dso__set_symbol_names_len(dso, len);
- dso__set_sorted_by_name(dso);
- }
+ down_read(&symbols->lock);
+ if (!symbols->sorted_by_name) {
+ up_read(&symbols->lock);
+ down_write(&symbols->lock);
+ symbols__sort_by_name_locked(symbols);
+ up_write(&symbols->lock);
+ return;
}
- mutex_unlock(dso__lock(dso));
+ up_read(&symbols->lock);
}
/*
@@ -877,7 +961,7 @@ static int map__process_kallsym_symbol(void *arg, const char *name,
{
struct symbol *sym;
struct dso *dso = arg;
- struct rb_root_cached *root = dso__symbols(dso);
+ struct symbols *root = dso__symbols(dso);
if (!symbol_type__filter(type))
return 0;
@@ -898,9 +982,7 @@ static int map__process_kallsym_symbol(void *arg, const char *name,
* We will pass the symbols to the filter later, in
* map__split_kallsyms, when we have split the maps per module
*/
- __symbols__insert(root, sym);
-
- return 0;
+ return __symbols__insert(root, sym);
}
/*
@@ -917,25 +999,32 @@ static int maps__split_kallsyms_for_kcore(struct maps *kmaps, struct dso *dso)
{
struct symbol *pos;
int count = 0;
- struct rb_root_cached *root = dso__symbols(dso);
- struct rb_root_cached old_root = *root;
- struct rb_node *next = rb_first_cached(root);
+ struct symbols *root = dso__symbols(dso);
+ struct symbol **old_symbols;
+ unsigned int old_cnt;
if (!kmaps)
return -1;
- *root = RB_ROOT_CACHED;
-
- while (next) {
+ down_write(&root->lock);
+ symbols__sort_locked(root);
+ old_symbols = root->symbols;
+ old_cnt = root->cnt;
+ root->symbols = NULL;
+ root->cnt = 0;
+ root->allocated = 0;
+ root->sorted = true;
+ zfree(&root->symbols_by_name);
+ root->num_by_name = 0;
+ root->sorted_by_name = false;
+ up_write(&root->lock);
+
+ for (unsigned int i = 0; i < old_cnt; i++) {
struct map *curr_map;
struct dso *curr_map_dso;
char *module;
- pos = rb_entry(next, struct symbol, rb_node);
- next = rb_next(&pos->rb_node);
-
- rb_erase_cached(&pos->rb_node, &old_root);
- RB_CLEAR_NODE(&pos->rb_node);
+ pos = old_symbols[i];
module = strchr((char *)symbol__name(pos), '\t');
if (module)
*module = '\0';
@@ -955,11 +1044,13 @@ static int maps__split_kallsyms_for_kcore(struct maps *kmaps, struct dso *dso)
symbol__set_end(pos, symbol__end(pos) -
(map__start(curr_map) - map__pgoff(curr_map)));
}
- symbols__insert(dso__symbols(curr_map_dso), pos);
- ++count;
+ if (symbols__insert(dso__symbols(curr_map_dso), pos) == 0)
+ ++count;
map__put(curr_map);
}
+ free(old_symbols);
+
/* Symbols have been adjusted */
dso__set_adjust_symbols(dso, true);
@@ -995,8 +1086,8 @@ static int maps__split_kallsyms(struct maps *kmaps, struct dso *dso, u64 delta,
struct map *curr_map = map__get(initial_map);
struct symbol *pos;
int count = 0, moved = 0;
- struct rb_root_cached *root = dso__symbols(dso);
- struct rb_node *next = rb_first_cached(root);
+ struct symbols *root = dso__symbols(dso);
+ unsigned int i, write_idx = 0;
int kernel_range = 0;
uint16_t e_machine = EM_NONE;
@@ -1006,11 +1097,13 @@ static int maps__split_kallsyms(struct maps *kmaps, struct dso *dso, u64 delta,
machine = maps__machine(kmaps);
e_machine = machine_or_dso_e_machine(machine, dso);
- while (next) {
+ down_write(&root->lock);
+ symbols__sort_locked(root);
+
+ for (i = 0; i < root->cnt; i++) {
char *module;
- pos = rb_entry(next, struct symbol, rb_node);
- next = rb_next(&pos->rb_node);
+ pos = root->symbols[i];
module = strchr((char *)symbol__name(pos), '\t');
if (module) {
@@ -1098,7 +1191,7 @@ static int maps__split_kallsyms(struct maps *kmaps, struct dso *dso, u64 delta,
ndso = dso__new(dso_name);
map__zput(curr_map);
if (ndso == NULL)
- return -1;
+ goto out_err;
dso__set_kernel(ndso, dso__kernel(dso));
dso__set_loaded(ndso);
@@ -1106,14 +1199,14 @@ static int maps__split_kallsyms(struct maps *kmaps, struct dso *dso, u64 delta,
curr_map = map__new2(symbol__start(pos), ndso);
if (curr_map == NULL) {
dso__put(ndso);
- return -1;
+ goto out_err;
}
map__set_mapping_type(curr_map, MAPPING_TYPE__IDENTITY);
if (maps__insert(kmaps, curr_map)) {
map__zput(curr_map);
dso__put(ndso);
- return -1;
+ goto out_err;
}
dso__put(ndso);
++kernel_range;
@@ -1126,18 +1219,24 @@ static int maps__split_kallsyms(struct maps *kmaps, struct dso *dso, u64 delta,
if (!RC_CHK_EQUAL(curr_map, initial_map)) {
struct dso *curr_map_dso = map__dso(curr_map);
- rb_erase_cached(&pos->rb_node, root);
- symbols__insert(dso__symbols(curr_map_dso), pos);
- ++moved;
- } else
+ if (symbols__insert(dso__symbols(curr_map_dso), pos) == 0)
+ ++moved;
+ } else {
+ root->symbols[write_idx++] = pos;
++count;
+ }
continue;
discard_symbol:
- rb_erase_cached(&pos->rb_node, root);
symbol__delete(pos);
}
+ root->cnt = write_idx;
+ zfree(&root->symbols_by_name);
+ root->num_by_name = 0;
+ root->sorted_by_name = false;
+ up_write(&root->lock);
+
if (!RC_CHK_EQUAL(curr_map, initial_map) &&
dso__kernel(dso) == DSO_SPACE__KERNEL_GUEST &&
machine__is_default_guest(maps__machine(kmaps))) {
@@ -1145,6 +1244,16 @@ static int maps__split_kallsyms(struct maps *kmaps, struct dso *dso, u64 delta,
}
map__put(curr_map);
return count + moved;
+
+out_err:
+ memmove(&root->symbols[write_idx], &root->symbols[i],
+ (root->cnt - i) * sizeof(struct symbol *));
+ root->cnt = write_idx + (root->cnt - i);
+ zfree(&root->symbols_by_name);
+ root->num_by_name = 0;
+ root->sorted_by_name = false;
+ up_write(&root->lock);
+ return -1;
}
bool symbol__restricted_filename(const char *filename,
@@ -1705,7 +1814,8 @@ static int dso__load_perf_map(const char *map_path, struct dso *dso)
if (sym == NULL)
goto out_delete_line;
- symbols__insert(dso__symbols(dso), sym);
+ if (symbols__insert(dso__symbols(dso), sym))
+ goto out_delete_line;
nr_syms++;
}
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index 100a7da07834..52cd6b43696c 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -15,6 +15,7 @@
#include <errno.h>
#include "addr_location.h"
#include "path.h"
+#include "rwsem.h"
#include "symbol_conf.h"
#include "spark.h"
#include "util.h"
@@ -93,7 +94,6 @@ enum symbol_idle_kind {
* A symtab entry.
*/
struct symbol {
- struct rb_node rb_node;
/** Range of symbol [start, end). */
u64 start;
u64 end;
@@ -106,8 +106,42 @@ struct symbol {
char name[];
};
+/**
+ * struct symbols - A locked collection of symbols stored in a sorted array.
+ */
+struct symbols {
+ /** @lock: Read/write semaphore synchronizing access to the arrays. */
+ struct rw_semaphore lock;
+ /**
+ * @symbols: Dynamically allocated array of symbol pointers, sorted by
+ * address range [start, end) when @sorted is true.
+ */
+ struct symbol **symbols;
+ /**
+ * @symbols_by_name: Optional dynamically allocated array of symbol
+ * pointers sorted lexicographically by symbol__name().
+ */
+ struct symbol **symbols_by_name;
+ /** @cnt: Number of valid entries in @symbols. */
+ unsigned int cnt;
+ /** @allocated: Capacity (in entries) of the @symbols allocation. */
+ unsigned int allocated;
+ /** @num_by_name: Number of valid entries in @symbols_by_name. */
+ unsigned int num_by_name;
+ /** @sorted: True when @symbols is sorted by address range. */
+ bool sorted;
+ /**
+ * @sorted_by_name: True when @symbols_by_name is populated and sorted
+ * by name.
+ */
+ bool sorted_by_name;
+};
+
void symbol__delete(struct symbol *sym);
-void symbols__delete(struct rb_root_cached *symbols);
+void symbols__init(struct symbols *symbols);
+void symbols__exit(struct symbols *symbols);
+void symbols__delete(struct symbols *symbols);
+void symbols__sort_read_lock(struct symbols *symbols) SHARED_LOCK_FUNCTION(symbols->lock);
static inline u64 symbol__start(const struct symbol *sym)
{
@@ -199,16 +233,16 @@ void symbol__set_inlined(struct symbol *sym, bool inlined);
void symbol__set_ifunc_alias(struct symbol *sym, bool ifunc_alias);
void symbol__set_annotated(struct symbol *sym, bool annotated);
-/* symbols__for_each_entry - iterate over symbols (rb_root)
+/*
+ * symbols__for_each_entry - iterate over symbols calling @cb for each entry
*
- * @symbols: the rb_root of symbols
- * @pos: the 'struct symbol *' to use as a loop cursor
- * @nd: the 'struct rb_node *' to use as a temporary storage
+ * @symbols: the 'struct symbols *' of symbols
+ * @cb: callback function invoked under @symbols->lock
+ * @data: opaque data pointer passed to @cb
*/
-#define symbols__for_each_entry(symbols, pos, nd) \
- for (nd = rb_first_cached(symbols); \
- nd && (pos = rb_entry(nd, struct symbol, rb_node)); \
- nd = rb_next(nd))
+int symbols__for_each_entry(struct symbols *symbols,
+ int (*cb)(struct symbol *sym, void *data),
+ void *data);
static inline size_t symbol__size(const struct symbol *sym)
{
@@ -245,8 +279,8 @@ int __dso__load_kallsyms(struct dso *dso, const char *filename, struct map *map,
bool no_kcore);
int dso__load_kallsyms(struct dso *dso, const char *filename, struct map *map);
-void dso__insert_symbol(struct dso *dso,
- struct symbol *sym);
+int dso__insert_symbol(struct dso *dso,
+ struct symbol *sym);
void dso__delete_symbol(struct dso *dso,
struct symbol *sym);
@@ -256,10 +290,6 @@ struct symbol *dso__find_symbol_nocache(struct dso *dso, u64 addr);
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);
-struct symbol *dso__next_symbol(struct symbol *sym);
-
enum dso_type dso__type_fd(int fd);
int filename__read_build_id(const char *filename, struct build_id *id);
@@ -310,10 +340,10 @@ int dso__synthesize_plt_symbols(struct dso *dso, struct symsrc *ss);
char *dso__demangle_sym(struct dso *dso, int kmodule, const char *elf_name);
-void __symbols__insert(struct rb_root_cached *symbols, struct symbol *sym);
-void symbols__insert(struct rb_root_cached *symbols, struct symbol *sym);
-void symbols__fixup_duplicate(struct rb_root_cached *symbols);
-void symbols__fixup_end(struct rb_root_cached *symbols, bool is_kallsyms);
+int __symbols__insert(struct symbols *symbols, struct symbol *sym);
+int symbols__insert(struct symbols *symbols, struct symbol *sym);
+void symbols__fixup_duplicate(struct symbols *symbols);
+void symbols__fixup_end(struct symbols *symbols, bool is_kallsyms);
typedef int (*mapfn_t)(u64 start, u64 len, u64 pgoff, void *data);
int file__read_maps(int fd, bool exe, mapfn_t mapfn, void *data,
--
2.56.0.rc1.315.gc6ed9934b7-goog
^ permalink raw reply [flat|nested] 36+ messages in thread
* [PATCH v1 4/7] perf symbol: Add reference counting and DECLARE_RC_STRUCT(symbol)
2026-09-28 7:52 [PATCH v1 0/7] perf symbol: Reference counting, flat array storage, and LRU shrinking Ian Rogers
` (2 preceding siblings ...)
2026-09-28 7:52 ` [PATCH v1 3/7] perf symbol: Switch backing storage from rbtree to struct symbols array Ian Rogers
@ 2026-09-28 7:52 ` Ian Rogers
2026-09-28 7:52 ` [PATCH v1 5/7] perf symbol: Add LRU memory shrinking for symbols, DSOs, and machines Ian Rogers
` (5 subsequent siblings)
9 siblings, 0 replies; 36+ messages in thread
From: Ian Rogers @ 2026-09-28 7:52 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
linux-perf-users, linux-kernel, Alireza Haghdoost
Wrap struct symbol with DECLARE_RC_STRUCT(symbol) and add a refcount_t
refcnt field along with symbol__get(), symbol__put(), and symbol__zput()
helpers:
- Return a counted symbol reference acquired under symbols->lock from
symbol lookup functions (symbols__find(), dso__find_symbol(),
map__find_symbol(), dso__find_symbol_by_name(),
map__find_symbol_by_name(), dso__first_symbol(), dso__last_symbol())
and iteration helpers (symbols__first_by_idx(),
symbols__next_by_idx(), symbols__for_each_entry()).
- Update util/annotate.c and util/db-export.c to hash and compare the
underlying RC_STRUCT(symbol) pointer via RC_CHK_ACCESS(sym) and
RC_CHK_EQUAL() so REFCNT_CHECKING wrapper pointers for the same
symbol resolve to the same hashmap entry, and hold a symbol reference
in struct db_export's sym_db_ids hashmap.
- Update struct addr_location, struct map_symbol, struct call_path,
struct block_info, struct disasm_line, struct annotated_op_loc, struct
c2c_sym_Key, inline_list, srcline, thread_stack, and hist/sort/unwind
consumers to acquire and release symbol references and use
RC_CHK_EQUAL() for pointer identity comparisons.
Assisted-by: Antigravity:gemini-3.1-pro
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/builtin-kallsyms.c | 2 +
tools/perf/builtin-lock.c | 17 ++-
tools/perf/builtin-script.c | 4 +-
tools/perf/builtin-top.c | 2 +-
tools/perf/builtin-trace.c | 1 +
tools/perf/tests/dlfilter-test.c | 8 +-
tools/perf/tests/hists_cumulate.c | 4 +-
tools/perf/tests/hists_filter.c | 10 +-
tools/perf/tests/hists_link.c | 16 ++-
tools/perf/tests/hists_output.c | 7 +-
tools/perf/tests/kallsyms-split.c | 5 +-
tools/perf/tests/pe-file-parsing.c | 1 +
tools/perf/tests/vmlinux-kallsyms.c | 12 +-
tools/perf/ui/browsers/hists.c | 7 +-
tools/perf/ui/browsers/map.c | 7 +-
tools/perf/ui/stdio/hist.c | 3 +-
tools/perf/util/addr_location.c | 4 +
.../perf/util/annotate-arch/annotate-alpha.c | 2 +-
.../util/annotate-arch/annotate-loongarch.c | 4 +-
tools/perf/util/annotate-arch/annotate-s390.c | 2 +-
tools/perf/util/annotate.c | 22 ++-
tools/perf/util/block-info.c | 6 +-
tools/perf/util/bpf_lock_contention.c | 27 +++-
tools/perf/util/c2c-function.c | 13 +-
tools/perf/util/call-path.c | 15 +-
tools/perf/util/callchain.c | 4 +-
tools/perf/util/capstone.c | 1 +
tools/perf/util/db-export.c | 35 ++++-
tools/perf/util/disasm.c | 12 +-
tools/perf/util/event.c | 8 +-
tools/perf/util/hist.c | 13 +-
tools/perf/util/libdw.c | 5 +-
tools/perf/util/lock-contention.c | 8 +-
tools/perf/util/machine.c | 48 +++++--
tools/perf/util/map.h | 10 +-
tools/perf/util/map_symbol.c | 4 +-
tools/perf/util/maps.c | 3 +-
tools/perf/util/probe-event.c | 17 ++-
tools/perf/util/python.c | 12 +-
tools/perf/util/sort.c | 2 +-
tools/perf/util/srcline.c | 6 +-
tools/perf/util/symbol-elf.c | 6 +-
tools/perf/util/symbol.c | 129 +++++++++++-------
tools/perf/util/symbol.h | 60 +++++---
tools/perf/util/thread-stack.c | 12 +-
tools/perf/util/threads.c | 17 ++-
tools/perf/util/unwind-libdw.c | 2 +-
tools/perf/util/unwind-libunwind.c | 2 +-
48 files changed, 427 insertions(+), 190 deletions(-)
diff --git a/tools/perf/builtin-kallsyms.c b/tools/perf/builtin-kallsyms.c
index eb62bb4ea602..5b1b87cd54d4 100644
--- a/tools/perf/builtin-kallsyms.c
+++ b/tools/perf/builtin-kallsyms.c
@@ -53,6 +53,8 @@ static int __cmd_kallsyms(int argc, const char **argv)
map__unmap_ip(map, symbol__start(symbol)),
map__unmap_ip(map, symbol__end(symbol)),
symbol__start(symbol), symbol__end(symbol));
+ symbol__put(symbol);
+ map__put(map);
}
out:
machine__delete(machine);
diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c
index cd060c984068..9b495703cf0c 100644
--- a/tools/perf/builtin-lock.c
+++ b/tools/perf/builtin-lock.c
@@ -1003,12 +1003,16 @@ static int report_lock_contention_begin_event(struct perf_sample *sample)
(filters.nr_addrs + 1) * sizeof(*addrs));
if (addrs == NULL) {
pr_warning("memory allocation failure\n");
+ symbol__put(sym);
+ map__put(kmap);
return -ENOMEM;
}
addrs[filters.nr_addrs++] = map__unmap_ip(kmap,
symbol__start(sym));
filters.addrs = addrs;
+ symbol__put(sym);
+ map__put(kmap);
}
}
@@ -1019,9 +1023,12 @@ static int report_lock_contention_begin_event(struct perf_sample *sample)
switch (aggr_mode) {
case LOCK_AGGR_ADDR:
- sym = machine__find_kernel_symbol(machine, key, &kmap);
- if (sym)
- name = symbol__name(sym);
+ sym = machine__find_kernel_symbol(machine, key, NULL);
+ if (sym) {
+ scnprintf(buf, sizeof(buf), "%s", symbol__name(sym));
+ name = buf;
+ symbol__put(sym);
+ }
break;
case LOCK_AGGR_CALLER:
name = buf;
@@ -1668,6 +1675,8 @@ static void print_lock_stat_stdio(struct lock_contention *con, struct lock_stat
ip = st->callstack[i];
sym = machine__find_kernel_symbol(con->machine, ip, &kmap);
get_symbol_name_offset(kmap, sym, ip, buf, sizeof(buf));
+ symbol__put(sym);
+ map__put(kmap);
fprintf(lock_output, "\t\t\t%#lx %s\n", (unsigned long)ip, buf);
}
}
@@ -1721,6 +1730,8 @@ static void print_lock_stat_csv(struct lock_contention *con, struct lock_stat *s
ip = st->callstack[i];
sym = machine__find_kernel_symbol(con->machine, ip, &kmap);
get_symbol_name_offset(kmap, sym, ip, buf, sizeof(buf));
+ symbol__put(sym);
+ map__put(kmap);
fprintf(lock_output, "%s %#lx %s", i ? ":" : sep, (unsigned long) ip, buf);
}
fprintf(lock_output, "\n");
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c
index f1257a78d975..4b6157fa87b9 100644
--- a/tools/perf/builtin-script.c
+++ b/tools/perf/builtin-script.c
@@ -1426,7 +1426,8 @@ static int ip__fprintf_sym(uint64_t addr, struct thread *thread,
if (PRINT_FIELD(SRCLINE))
printed += map__fprintf_srcline(al.map, al.addr, "\t", fp);
printed += fprintf(fp, "\n");
- *lastsym = al.sym;
+ symbol__put(*lastsym);
+ *lastsym = symbol__get(al.sym);
ret = printed;
out:
@@ -1593,6 +1594,7 @@ static int perf_sample__fprintf_brstackinsn(struct perf_sample *sample,
print_srccode(thread, x.cpumode, start + off);
}
out:
+ symbol__put(lastsym);
return printed;
}
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
index 19a21348823c..dd1e475bb4dd 100644
--- a/tools/perf/builtin-top.c
+++ b/tools/perf/builtin-top.c
@@ -211,7 +211,7 @@ static void perf_top__record_precise_ip(struct perf_top *top,
if (sym == NULL || (use_browser == 0 &&
(top->sym_filter_entry == NULL ||
- top->sym_filter_entry->ms.sym != sym)))
+ !RC_CHK_EQUAL(top->sym_filter_entry->ms.sym, sym))))
return;
notes = symbol__annotation(sym);
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c
index aaa090912409..ab970fd45e26 100644
--- a/tools/perf/builtin-trace.c
+++ b/tools/perf/builtin-trace.c
@@ -778,6 +778,7 @@ size_t syscall_arg__scnprintf_ksym(char *bf, size_t size, struct syscall_arg *ar
else
printed = scnprintf(bf, size, "%s+0x%" PRIx64,
symbol__name(sym), offset);
+ symbol__put(sym);
map__put(map);
return printed;
}
diff --git a/tools/perf/tests/dlfilter-test.c b/tools/perf/tests/dlfilter-test.c
index b4649c79c309..1b7f400761bf 100644
--- a/tools/perf/tests/dlfilter-test.c
+++ b/tools/perf/tests/dlfilter-test.c
@@ -273,12 +273,16 @@ static int get_ip_addr(struct test_data *td)
return -1;
sym = map__find_symbol_by_name(map, "foo");
- if (sym)
+ if (sym) {
td->foo = symbol__start(sym);
+ symbol__put(sym);
+ }
sym = map__find_symbol_by_name(map, "bar");
- if (sym)
+ if (sym) {
td->bar = symbol__start(sym);
+ symbol__put(sym);
+ }
map__put(map);
diff --git a/tools/perf/tests/hists_cumulate.c b/tools/perf/tests/hists_cumulate.c
index 95b9a629e59e..16637f1e1e23 100644
--- a/tools/perf/tests/hists_cumulate.c
+++ b/tools/perf/tests/hists_cumulate.c
@@ -114,7 +114,8 @@ static int add_hist_entries(struct hists *hists, struct machine *machine)
fake_samples[i].thread = thread__get(al.thread);
map__put(fake_samples[i].map);
fake_samples[i].map = map__get(al.map);
- fake_samples[i].sym = al.sym;
+ symbol__put(fake_samples[i].sym);
+ fake_samples[i].sym = symbol__get(al.sym);
}
addr_location__exit(&al);
@@ -155,6 +156,7 @@ static void put_fake_samples(void)
size_t i;
for (i = 0; i < ARRAY_SIZE(fake_samples); i++) {
+ symbol__zput(fake_samples[i].sym);
map__zput(fake_samples[i].map);
thread__zput(fake_samples[i].thread);
}
diff --git a/tools/perf/tests/hists_filter.c b/tools/perf/tests/hists_filter.c
index 6c7e4cdc71a7..3bd0b0b270f5 100644
--- a/tools/perf/tests/hists_filter.c
+++ b/tools/perf/tests/hists_filter.c
@@ -93,7 +93,8 @@ static int add_hist_entries(struct evlist *evlist,
fake_samples[i].thread = thread__get(al.thread);
map__put(fake_samples[i].map);
fake_samples[i].map = map__get(al.map);
- fake_samples[i].sym = al.sym;
+ symbol__put(fake_samples[i].sym);
+ fake_samples[i].sym = symbol__get(al.sym);
}
}
addr_location__exit(&al);
@@ -109,8 +110,11 @@ static void put_fake_samples(void)
{
size_t i;
- for (i = 0; i < ARRAY_SIZE(fake_samples); i++)
- map__put(fake_samples[i].map);
+ for (i = 0; i < ARRAY_SIZE(fake_samples); i++) {
+ symbol__zput(fake_samples[i].sym);
+ map__zput(fake_samples[i].map);
+ thread__zput(fake_samples[i].thread);
+ }
}
static int test__hists_filter(struct test_suite *test __maybe_unused, int subtest __maybe_unused)
diff --git a/tools/perf/tests/hists_link.c b/tools/perf/tests/hists_link.c
index cd32c8277627..b91a75a8fa23 100644
--- a/tools/perf/tests/hists_link.c
+++ b/tools/perf/tests/hists_link.c
@@ -99,7 +99,8 @@ static int add_hist_entries(struct evlist *evlist, struct machine *machine)
fake_common_samples[k].thread = thread__get(al.thread);
map__put(fake_common_samples[k].map);
fake_common_samples[k].map = map__get(al.map);
- fake_common_samples[k].sym = al.sym;
+ symbol__put(fake_common_samples[k].sym);
+ fake_common_samples[k].sym = symbol__get(al.sym);
}
for (k = 0; k < ARRAY_SIZE(fake_samples[i]); k++) {
@@ -119,7 +120,8 @@ static int add_hist_entries(struct evlist *evlist, struct machine *machine)
fake_samples[i][k].thread = thread__get(al.thread);
map__put(fake_samples[i][k].map);
fake_samples[i][k].map = map__get(al.map);
- fake_samples[i][k].sym = al.sym;
+ symbol__put(fake_samples[i][k].sym);
+ fake_samples[i][k].sym = symbol__get(al.sym);
}
i++;
}
@@ -136,11 +138,15 @@ static void put_fake_samples(void)
{
size_t i, j;
- for (i = 0; i < ARRAY_SIZE(fake_common_samples); i++)
+ for (i = 0; i < ARRAY_SIZE(fake_common_samples); i++) {
map__put(fake_common_samples[i].map);
+ symbol__put(fake_common_samples[i].sym);
+ }
for (i = 0; i < ARRAY_SIZE(fake_samples); i++) {
- for (j = 0; j < ARRAY_SIZE(fake_samples[0]); j++)
+ for (j = 0; j < ARRAY_SIZE(fake_samples[0]); j++) {
map__put(fake_samples[i][j].map);
+ symbol__put(fake_samples[i][j].sym);
+ }
}
}
@@ -150,7 +156,7 @@ static int find_sample(struct sample *samples, size_t nr_samples,
while (nr_samples--) {
if (RC_CHK_EQUAL(samples->thread, t) &&
RC_CHK_EQUAL(samples->map, m) &&
- samples->sym == s)
+ RC_CHK_EQUAL(samples->sym, s))
return 1;
samples++;
}
diff --git a/tools/perf/tests/hists_output.c b/tools/perf/tests/hists_output.c
index 77fb3a187139..63b9e48ada63 100644
--- a/tools/perf/tests/hists_output.c
+++ b/tools/perf/tests/hists_output.c
@@ -79,7 +79,8 @@ static int add_hist_entries(struct hists *hists, struct machine *machine)
fake_samples[i].thread = al.thread;
map__put(fake_samples[i].map);
fake_samples[i].map = map__get(al.map);
- fake_samples[i].sym = al.sym;
+ symbol__put(fake_samples[i].sym);
+ fake_samples[i].sym = symbol__get(al.sym);
}
addr_location__exit(&al);
@@ -120,8 +121,8 @@ static void put_fake_samples(void)
size_t i;
for (i = 0; i < ARRAY_SIZE(fake_samples); i++) {
- map__put(fake_samples[i].map);
- fake_samples[i].map = NULL;
+ symbol__zput(fake_samples[i].sym);
+ map__zput(fake_samples[i].map);
}
}
diff --git a/tools/perf/tests/kallsyms-split.c b/tools/perf/tests/kallsyms-split.c
index 244daa01bd5d..0e5c029a583e 100644
--- a/tools/perf/tests/kallsyms-split.c
+++ b/tools/perf/tests/kallsyms-split.c
@@ -99,6 +99,7 @@ static int test__kallsyms_split(struct test_suite *test __maybe_unused,
{
struct machine m = { 0 };
struct map *map = NULL;
+ struct symbol *sym;
int ret = TEST_FAIL;
pr_debug("try to create fake root directory\n");
@@ -139,10 +140,12 @@ static int test__kallsyms_split(struct test_suite *test __maybe_unused,
goto out;
}
- if (machine__find_kernel_symbol_by_name(&m, "main_symbol3", &map) == NULL) {
+ sym = machine__find_kernel_symbol_by_name(&m, "main_symbol3", &map);
+ if (sym == NULL) {
pr_debug("FAIL: failed to find a symbol\n");
goto out;
}
+ symbol__put(sym);
if (!RC_CHK_EQUAL(map, machine__kernel_map(&m))) {
pr_debug("FAIL: the symbol is not in the kernel map\n");
diff --git a/tools/perf/tests/pe-file-parsing.c b/tools/perf/tests/pe-file-parsing.c
index 30c7da79e109..c9a2f418bdc5 100644
--- a/tools/perf/tests/pe-file-parsing.c
+++ b/tools/perf/tests/pe-file-parsing.c
@@ -64,6 +64,7 @@ static int run_dir(const char *d)
dso__sort_by_name(dso);
sym = dso__find_symbol_by_name(dso, "main", &idx);
TEST_ASSERT_VAL("Failed to find main", sym);
+ symbol__put(sym);
dso__delete(dso);
return TEST_OK;
diff --git a/tools/perf/tests/vmlinux-kallsyms.c b/tools/perf/tests/vmlinux-kallsyms.c
index 98c2a9437ba7..53f7b44af843 100644
--- a/tools/perf/tests/vmlinux-kallsyms.c
+++ b/tools/perf/tests/vmlinux-kallsyms.c
@@ -208,7 +208,7 @@ static int test__vmlinux_matches_kallsyms_sym_cb(struct symbol *sym, void *data)
mem_end = map__unmap_ip(args->vmlinux_map, symbol__end(sym));
first_pair = machine__find_kernel_symbol(&args->kallsyms, mem_start, NULL);
- pair = first_pair;
+ pair = symbol__get(first_pair);
if (pair && UM(symbol__start(pair)) == mem_start) {
next_pair:
@@ -237,8 +237,11 @@ static int test__vmlinux_matches_kallsyms_sym_cb(struct symbol *sym, void *data)
* possible to get proper function end from
* kallsyms.
*/
+ symbol__put(pair);
+ symbol__put(first_pair);
return 0;
} else {
+ symbol__put(pair);
pair = machine__find_kernel_symbol_by_name(&args->kallsyms,
symbol__name(sym),
NULL);
@@ -249,15 +252,20 @@ static int test__vmlinux_matches_kallsyms_sym_cb(struct symbol *sym, void *data)
pr_debug("WARN: %#" PRIx64 ": diff name v: %s k: %s\n",
mem_start, symbol__name(sym),
symbol__name(pair));
+ symbol__put(pair);
} else {
pr_debug("WARN: %#" PRIx64 ": diff name v: %s k: %s\n",
mem_start, symbol__name(sym),
symbol__name(first_pair));
}
+ symbol__put(first_pair);
return 0;
}
- } else if (mem_start == map__end(args->kallsyms.vmlinux_map)) {
+ }
+ symbol__put(pair);
+ symbol__put(first_pair);
+ if (mem_start == map__end(args->kallsyms.vmlinux_map)) {
/*
* Ignore aliases to _etext, i.e. to the end of the kernel text area,
* such as __indirect_thunk_end.
diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c
index 206657039223..72b071f2604f 100644
--- a/tools/perf/ui/browsers/hists.c
+++ b/tools/perf/ui/browsers/hists.c
@@ -2522,11 +2522,11 @@ static struct symbol *symbol__new_unresolved(u64 addr, struct map *map)
if (sym) {
src = symbol__hists(sym, 1);
if (!src) {
- symbol__delete(sym);
+ symbol__put(sym);
return NULL;
}
- dso__insert_symbol(map__dso(map), sym);
+ dso__insert_symbol(map__dso(map), symbol__get(sym));
}
return sym;
@@ -2812,7 +2812,7 @@ add_script_opt_2(struct popup_action *act, char **optstr,
}
act->ms.thread = thread__get(thread);
- act->ms.sym = sym;
+ act->ms.sym = symbol__get(sym);
act->fn = do_run_script;
return 1;
}
@@ -3201,6 +3201,7 @@ static int evsel__hists_browse(struct evsel *evsel, int nr_events, const char *h
memset(&hotkey_act, 0, sizeof(hotkey_act));
map_symbol__copy(&hotkey_act.ms, &source_ms);
+ symbol__zput(source_ms.sym);
do_annotate(browser, &hotkey_act);
map_symbol__exit(&hotkey_act.ms);
} else {
diff --git a/tools/perf/ui/browsers/map.c b/tools/perf/ui/browsers/map.c
index bb35d4965a3a..2310fbf1ac6d 100644
--- a/tools/perf/ui/browsers/map.c
+++ b/tools/perf/ui/browsers/map.c
@@ -65,12 +65,13 @@ static int map_browser__search(struct map_browser *browser)
* cursor at that entry.
*/
for (u32 idx = 0; idx < browser->b.nr_entries; ++idx) {
- if (entries[idx] == sym) {
+ if (RC_CHK_EQUAL(entries[idx], sym)) {
browser->b.top = &entries[idx];
browser->b.index = browser->b.top_idx = idx;
break;
}
}
+ symbol__put(sym);
} else
ui_helpline__fpush("%s not found!", target);
@@ -136,7 +137,7 @@ int map__browse(struct map *map)
for (unsigned int idx = 0; idx < nr_entries; idx++) {
struct symbol *pos = symbols->symbols[idx];
- entries[idx] = pos;
+ entries[idx] = symbol__get(pos);
if (maxaddr < symbol__end(pos))
maxaddr = symbol__end(pos);
}
@@ -151,6 +152,8 @@ int map__browse(struct map *map)
mb.addrlen = snprintf(tmp, sizeof(tmp), "%" PRIx64, maxaddr);
ret = map_browser__run(&mb);
+ for (unsigned int idx = 0; idx < nr_entries; idx++)
+ symbol__put(entries[idx]);
free(entries);
return ret;
}
diff --git a/tools/perf/ui/stdio/hist.c b/tools/perf/ui/stdio/hist.c
index f80cc73909ed..aedc5eb8b1a4 100644
--- a/tools/perf/ui/stdio/hist.c
+++ b/tools/perf/ui/stdio/hist.c
@@ -908,7 +908,8 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,
free(line);
out:
- zfree(&rem_sq_bracket);
+ symbol__zput(rem_sq_bracket);
+ rem_hits.ms.sym = NULL;
return ret;
}
diff --git a/tools/perf/util/addr_location.c b/tools/perf/util/addr_location.c
index 57e8217a00f9..0f21a01a3843 100644
--- a/tools/perf/util/addr_location.c
+++ b/tools/perf/util/addr_location.c
@@ -2,6 +2,7 @@
#include "addr_location.h"
#include "map.h"
#include "maps.h"
+#include "symbol.h"
#include "thread.h"
void addr_location__init(struct addr_location *al)
@@ -29,13 +30,16 @@ void addr_location__exit(struct addr_location *al)
{
map__zput(al->map);
thread__zput(al->thread);
+ symbol__zput(al->sym);
}
void addr_location__copy(struct addr_location *dst, struct addr_location *src)
{
thread__put(dst->thread);
map__put(dst->map);
+ symbol__put(dst->sym);
*dst = *src;
dst->thread = thread__get(src->thread);
dst->map = map__get(src->map);
+ dst->sym = symbol__get(src->sym);
}
diff --git a/tools/perf/util/annotate-arch/annotate-alpha.c b/tools/perf/util/annotate-arch/annotate-alpha.c
index ccb13e2338a2..e95f7628a8fe 100644
--- a/tools/perf/util/annotate-arch/annotate-alpha.c
+++ b/tools/perf/util/annotate-arch/annotate-alpha.c
@@ -89,7 +89,7 @@ static int alpha_call__parse(const struct arch *arch, struct ins_operands *ops,
if (maps__find_ams(thread__maps(ms->thread), &target) == 0 &&
map__rip_2objdump(target.ms.map,
map__map_ip(target.ms.map, target.addr)) == ops->target.addr)
- ops->target.sym = target.ms.sym;
+ ops->target.sym = symbol__get(target.ms.sym);
addr_map_symbol__exit(&target);
return 0;
diff --git a/tools/perf/util/annotate-arch/annotate-loongarch.c b/tools/perf/util/annotate-arch/annotate-loongarch.c
index d275730080b7..7fa1739966df 100644
--- a/tools/perf/util/annotate-arch/annotate-loongarch.c
+++ b/tools/perf/util/annotate-arch/annotate-loongarch.c
@@ -57,7 +57,7 @@ static int loongarch_call__parse(const struct arch *arch, struct ins_operands *o
if (maps__find_ams(thread__maps(ms->thread), &target) == 0 &&
map__rip_2objdump(target.ms.map, map__map_ip(target.ms.map, target.addr)) == ops->target.addr)
- ops->target.sym = target.ms.sym;
+ ops->target.sym = symbol__get(target.ms.sym);
addr_map_symbol__exit(&target);
return 0;
@@ -101,7 +101,7 @@ static int loongarch_jump__parse(const struct arch *arch, struct ins_operands *o
if (maps__find_ams(thread__maps(ms->thread), &target) == 0 &&
map__rip_2objdump(target.ms.map, map__map_ip(target.ms.map, target.addr)) == ops->target.addr)
- ops->target.sym = target.ms.sym;
+ ops->target.sym = symbol__get(target.ms.sym);
if (!ops->target.outside) {
ops->target.offset = target.addr - start;
diff --git a/tools/perf/util/annotate-arch/annotate-s390.c b/tools/perf/util/annotate-arch/annotate-s390.c
index af9cabd0a586..7c8050e04964 100644
--- a/tools/perf/util/annotate-arch/annotate-s390.c
+++ b/tools/perf/util/annotate-arch/annotate-s390.c
@@ -52,7 +52,7 @@ static int s390_call__parse(const struct arch *arch, struct ins_operands *ops,
if (maps__find_ams(thread__maps(ms->thread), &target) == 0 &&
map__rip_2objdump(target.ms.map, map__map_ip(target.ms.map, target.addr)) == ops->target.addr)
- ops->target.sym = target.ms.sym;
+ ops->target.sym = symbol__get(target.ms.sym);
addr_map_symbol__exit(&target);
return 0;
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c
index 9bb7e0ee1b8e..61a5b13c4777 100644
--- a/tools/perf/util/annotate.c
+++ b/tools/perf/util/annotate.c
@@ -194,15 +194,22 @@ static bool sym_ptr_equal(long key1, long key2, void *ctx __maybe_unused)
struct annotation *symbol__annotation(struct symbol *sym)
{
struct annotation *notes = NULL;
- long val = 0;
+ long key, val = 0;
if (!sym)
return NULL;
+ /*
+ * Key on the underlying RC_STRUCT(symbol) pointer so all
+ * REFCNT_CHECKING wrapper pointers for the same symbol map to the
+ * same annotation entry.
+ */
+ key = (long)RC_CHK_ACCESS(sym);
+
/* Fast path: look up existing annotation under read lock if flagged. */
if (symbol__annotated(sym)) {
down_read(annotations_lock());
- if (annotations && hashmap__find(annotations, (long)sym, &val))
+ if (annotations && hashmap__find(annotations, key, &val))
notes = (struct annotation *)val;
up_read(annotations_lock());
if (notes)
@@ -217,7 +224,7 @@ struct annotation *symbol__annotation(struct symbol *sym)
up_write(annotations_lock());
return &empty_annotation;
}
- } else if (hashmap__find(annotations, (long)sym, &val)) {
+ } else if (hashmap__find(annotations, key, &val)) {
notes = (struct annotation *)val;
up_write(annotations_lock());
return notes;
@@ -229,7 +236,7 @@ struct annotation *symbol__annotation(struct symbol *sym)
return &empty_annotation;
}
annotation__init(notes);
- if (hashmap__add(annotations, (long)sym, notes) < 0) {
+ if (hashmap__add(annotations, key, notes) < 0) {
free(notes);
if (hashmap__size(annotations) == 0) {
hashmap__free(annotations);
@@ -246,14 +253,15 @@ struct annotation *symbol__annotation(struct symbol *sym)
void symbol__annotation_delete(struct symbol *sym)
{
struct annotation *notes = NULL;
- long val = 0;
+ long key, val = 0;
/* Unannotated symbols avoid taking annotations_lock on teardown. */
if (!sym || !symbol__annotated(sym))
return;
+ key = (long)RC_CHK_ACCESS(sym);
down_write(annotations_lock());
- if (annotations && hashmap__delete(annotations, (long)sym, NULL, &val)) {
+ if (annotations && hashmap__delete(annotations, key, NULL, &val)) {
notes = (struct annotation *)val;
if (hashmap__size(annotations) == 0) {
hashmap__free(annotations);
@@ -567,7 +575,7 @@ int addr_map_symbol__account_cycles(struct addr_map_symbol *ams,
* it starts on the function start.
*/
if (start &&
- (start->ms.sym == ams->ms.sym ||
+ (RC_CHK_EQUAL(start->ms.sym, ams->ms.sym) ||
(ams->ms.sym &&
start->addr == symbol__start(ams->ms.sym) + map__start(ams->ms.map))))
saddr = start->al_addr;
diff --git a/tools/perf/util/block-info.c b/tools/perf/util/block-info.c
index 5dec6c9dc1a7..6bf13482da80 100644
--- a/tools/perf/util/block-info.c
+++ b/tools/perf/util/block-info.c
@@ -64,8 +64,10 @@ static struct block_info *block_info__new(unsigned int br_cntr_nr)
void block_info__delete(struct block_info *bi)
{
- if (bi)
+ if (bi) {
+ symbol__zput(bi->sym);
free(bi->br_cntr);
+ }
free(bi);
}
@@ -105,7 +107,7 @@ static void init_block_info(struct block_info *bi, struct symbol *sym,
u64 total_cycles, unsigned int br_cntr_nr,
u64 *br_cntr, struct evsel *evsel)
{
- bi->sym = sym;
+ bi->sym = symbol__get(sym);
bi->start = ch->start;
bi->end = offset;
bi->cycles = ch->cycles;
diff --git a/tools/perf/util/bpf_lock_contention.c b/tools/perf/util/bpf_lock_contention.c
index 9832c45857d1..bf9b4dc3c3bb 100644
--- a/tools/perf/util/bpf_lock_contention.c
+++ b/tools/perf/util/bpf_lock_contention.c
@@ -145,6 +145,7 @@ static void init_numa_data(struct lock_contention *con)
if (sym) {
skel->rodata->contig_page_data_addr = map__unmap_ip(kmap,
symbol__start(sym));
+ symbol__put(sym);
map__put(kmap);
return;
}
@@ -161,6 +162,7 @@ static void init_numa_data(struct lock_contention *con)
return;
skel->rodata->node_data_addr = map__unmap_ip(kmap, symbol__start(sym));
+ symbol__put(sym);
map__put(kmap);
/* get the number of online nodes using the last node number + 1 */
@@ -264,12 +266,16 @@ int lock_contention_prepare(struct lock_contention *con)
(con->filters->nr_addrs + 1) * sizeof(*addrs));
if (addrs == NULL) {
pr_warning("memory allocation failure\n");
+ symbol__put(sym);
+ map__put(kmap);
continue;
}
addrs[con->filters->nr_addrs++] = map__unmap_ip(kmap,
symbol__start(sym));
con->filters->addrs = addrs;
+ symbol__put(sym);
+ map__put(kmap);
}
naddrs = con->filters->nr_addrs ?: has_mmap_lock;
skel->rodata->has_addr = 1;
@@ -292,6 +298,8 @@ int lock_contention_prepare(struct lock_contention *con)
con->delays[i].addr = map__unmap_ip(kmap,
symbol__start(sym));
+ symbol__put(sym);
+ map__put(kmap);
}
skel->rodata->lock_delay = 1;
bpf_map__set_max_entries(skel->maps.lock_delays, con->nr_delays);
@@ -609,9 +617,12 @@ static const char *lock_contention_get_name(struct lock_contention *con,
return "siglock";
/* global locks with symbols */
- sym = machine__find_kernel_symbol(machine, key->lock_addr_or_cgroup, &kmap);
- if (sym)
- return symbol__name(sym);
+ sym = machine__find_kernel_symbol(machine, key->lock_addr_or_cgroup, NULL);
+ if (sym) {
+ snprintf(name_buf, sizeof(name_buf), "%s", symbol__name(sym));
+ symbol__put(sym);
+ return name_buf;
+ }
/* try semi-global locks collected separately */
if (!bpf_map_lookup_elem(lock_fd, &key->lock_addr_or_cgroup, &flags)) {
@@ -658,13 +669,15 @@ static const char *lock_contention_get_name(struct lock_contention *con,
offset = map__map_ip(kmap, addr) - symbol__start(sym);
if (offset == 0)
- return symbol__name(sym);
-
- snprintf(name_buf, sizeof(name_buf), "%s+%#lx",
- symbol__name(sym), offset);
+ snprintf(name_buf, sizeof(name_buf), "%s", symbol__name(sym));
+ else
+ snprintf(name_buf, sizeof(name_buf), "%s+%#lx",
+ symbol__name(sym), offset);
+ symbol__put(sym);
} else {
snprintf(name_buf, sizeof(name_buf), "%#lx", (unsigned long)addr);
}
+ map__put(kmap);
return name_buf;
}
diff --git a/tools/perf/util/c2c-function.c b/tools/perf/util/c2c-function.c
index 0c50d816653f..5bd7e0364263 100644
--- a/tools/perf/util/c2c-function.c
+++ b/tools/perf/util/c2c-function.c
@@ -976,13 +976,13 @@ c2c_function_hists__level1_entry(struct symbol *sym,
/* mem_info__put() will map_symbol__exit() these, so take refs. */
mem_info__iaddr(mi)->ms.thread = thread__get(detail_he->ms.thread);
mem_info__iaddr(mi)->ms.map = map__get(detail_he->ms.map);
- mem_info__iaddr(mi)->ms.sym = sym;
+ mem_info__iaddr(mi)->ms.sym = symbol__get(sym);
mem_info__daddr(mi)->addr = 0;
addr_location__init(&al);
al.thread = thread__get(synthetic_thread);
al.map = map__get(detail_he->ms.map);
- al.sym = sym;
+ al.sym = symbol__get(sym);
al.addr = sym_start;
al.level = detail_he->level;
al.cpumode = detail_he->cpumode;
@@ -1064,9 +1064,12 @@ c2c_function_hists__level2_entry(struct c2c_hist_entry *level1_c2c,
return NULL;
/* Key this level by the looked-up symbol, not detail_he's. */
- level2_c2c->he.ms.sym = sym;
- if (level2_c2c->he.mem_info)
- mem_info__iaddr(level2_c2c->he.mem_info)->ms.sym = sym;
+ symbol__put(level2_c2c->he.ms.sym);
+ level2_c2c->he.ms.sym = symbol__get(sym);
+ if (level2_c2c->he.mem_info) {
+ symbol__put(mem_info__iaddr(level2_c2c->he.mem_info)->ms.sym);
+ mem_info__iaddr(level2_c2c->he.mem_info)->ms.sym = symbol__get(sym);
+ }
c2c_child_entry__insert(level1_he, &level2_c2c->he, p, parent, leftmost);
diff --git a/tools/perf/util/call-path.c b/tools/perf/util/call-path.c
index 5c60b8be1cf6..5927068fd9b6 100644
--- a/tools/perf/util/call-path.c
+++ b/tools/perf/util/call-path.c
@@ -10,12 +10,13 @@
#include <stdlib.h>
#include "call-path.h"
+#include "symbol.h"
static void call_path__init(struct call_path *cp, struct call_path *parent,
struct symbol *sym, u64 ip, bool in_kernel)
{
cp->parent = parent;
- cp->sym = sym;
+ cp->sym = symbol__get(sym);
cp->ip = sym ? 0 : ip;
cp->db_id = 0;
cp->in_kernel = in_kernel;
@@ -38,8 +39,15 @@ struct call_path_root *call_path_root__new(void)
void call_path_root__free(struct call_path_root *cpr)
{
struct call_path_block *pos, *n;
+ size_t i = 0;
+ if (!cpr)
+ return;
+
+ symbol__zput(cpr->call_path.sym);
list_for_each_entry_safe(pos, n, &cpr->blocks, node) {
+ for (size_t j = 0; j < CALL_PATH_BLOCK_SIZE && i < cpr->next; j++, i++)
+ symbol__zput(pos->cp[j].sym);
list_del_init(&pos->node);
free(pos);
}
@@ -94,10 +102,11 @@ struct call_path *call_path__findnew(struct call_path_root *cpr,
node_parent = *p;
cp = rb_entry(node_parent, struct call_path, rb_node);
- if (cp->sym == sym && cp->ip == ip)
+ if (RC_CHK_EQUAL(cp->sym, sym) && cp->ip == ip)
return cp;
- if (sym < cp->sym || (sym == cp->sym && ip < cp->ip))
+ if ((sym ? RC_CHK_ACCESS(sym) : NULL) < (cp->sym ? RC_CHK_ACCESS(cp->sym) : NULL) ||
+ (RC_CHK_EQUAL(sym, cp->sym) && ip < cp->ip))
p = &(*p)->rb_left;
else
p = &(*p)->rb_right;
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c
index 993b7c744a89..7f4543ea2ab5 100644
--- a/tools/perf/util/callchain.c
+++ b/tools/perf/util/callchain.c
@@ -1094,6 +1094,7 @@ merge_chain_branch(struct callchain_cursor *cursor,
struct map_symbol ms = {
.thread = thread__get(list->ms.thread),
.map = map__get(list->ms.map),
+ .sym = symbol__get(list->ms.sym),
};
callchain_cursor_append(cursor, list->ip, &ms, false, NULL, 0, 0, 0, list->srcline);
list_del_init(&list->list);
@@ -1204,7 +1205,8 @@ int fill_callchain_info(struct addr_location *al, struct callchain_cursor_node *
map__put(al->map);
al->map = map__get(node->ms.map);
- al->sym = node->ms.sym;
+ symbol__put(al->sym);
+ al->sym = symbol__get(node->ms.sym);
al->srcline = node->srcline;
al->addr = node->ip;
diff --git a/tools/perf/util/capstone.c b/tools/perf/util/capstone.c
index 422a48385b78..69667f41a7fe 100644
--- a/tools/perf/util/capstone.c
+++ b/tools/perf/util/capstone.c
@@ -373,6 +373,7 @@ static void print_capstone_detail(struct cs_insn *insn, char *buf, size_t len,
scnprintf(buf, len, "\t# %"PRIx64" <%s+%#"PRIx64">",
orig_addr, symbol__name(sym), sym_offset);
}
+ symbol__put(sym);
map__put(found_map);
break;
}
diff --git a/tools/perf/util/db-export.c b/tools/perf/util/db-export.c
index 897cda3fadd2..1029f23be428 100644
--- a/tools/perf/util/db-export.c
+++ b/tools/perf/util/db-export.c
@@ -22,14 +22,24 @@
#include "db-export.h"
#include "hashmap.h"
+/*
+ * Hash and compare the underlying RC_STRUCT(symbol) pointer so all
+ * REFCNT_CHECKING wrapper pointers for the same symbol resolve to the same
+ * exported database ID.
+ */
static size_t sym_ptr_hash(long key, void *ctx __maybe_unused)
{
- return hash_bits((size_t)key, 16);
+ const struct symbol *sym = (const struct symbol *)key;
+
+ return hash_bits((size_t)RC_CHK_ACCESS(sym), 16);
}
static bool sym_ptr_equal(long key1, long key2, void *ctx __maybe_unused)
{
- return key1 == key2;
+ const struct symbol *sym1 = (const struct symbol *)key1;
+ const struct symbol *sym2 = (const struct symbol *)key2;
+
+ return RC_CHK_EQUAL(sym1, sym2);
}
int db_export__init(struct db_export *dbe)
@@ -40,8 +50,15 @@ int db_export__init(struct db_export *dbe)
void db_export__exit(struct db_export *dbe)
{
- hashmap__free(dbe->sym_db_ids);
- dbe->sym_db_ids = NULL;
+ if (dbe->sym_db_ids) {
+ struct hashmap_entry *cur;
+ size_t bkt;
+
+ hashmap__for_each_entry(dbe->sym_db_ids, cur, bkt)
+ symbol__put((struct symbol *)cur->pkey);
+ hashmap__free(dbe->sym_db_ids);
+ dbe->sym_db_ids = NULL;
+ }
call_return_processor__free(dbe->crp);
dbe->crp = NULL;
}
@@ -187,6 +204,7 @@ int db_export__symbol(struct db_export *dbe, struct symbol *sym,
struct dso *dso)
{
long db_id;
+ struct symbol *sym_key;
if (db_export__symbol_db_id(dbe, sym))
return 0;
@@ -202,8 +220,11 @@ int db_export__symbol(struct db_export *dbe, struct symbol *sym,
}
db_id = ++dbe->symbol_last_db_id;
- if (hashmap__set(dbe->sym_db_ids, (long)sym, db_id, NULL, NULL))
+ sym_key = symbol__get(sym);
+ if (hashmap__set(dbe->sym_db_ids, (long)sym_key, db_id, NULL, NULL)) {
+ symbol__put(sym_key);
return -ENOMEM;
+ }
if (dbe->export_symbol)
return dbe->export_symbol(dbe, sym, dso);
@@ -227,7 +248,7 @@ static int db_ids_from_al(struct db_export *dbe, struct addr_location *al,
if (!al->sym) {
al->sym = symbol__new(al->addr, 0, 0, 0, "unknown");
if (al->sym)
- dso__insert_symbol(dso, al->sym);
+ dso__insert_symbol(dso, symbol__get(al->sym));
}
if (al->sym) {
@@ -287,7 +308,7 @@ static struct call_path *call_path_from_sample(struct db_export *dbe,
* db_ids_from_al() to perform the export.
*/
addr_location__init(&al);
- al.sym = node->ms.sym;
+ al.sym = symbol__get(node->ms.sym);
al.map = map__get(node->ms.map);
al.addr = node->ip;
al.thread = thread__get(thread);
diff --git a/tools/perf/util/disasm.c b/tools/perf/util/disasm.c
index 78b8811dcaa4..cc9865ac6623 100644
--- a/tools/perf/util/disasm.c
+++ b/tools/perf/util/disasm.c
@@ -228,6 +228,7 @@ static void ins_ops__delete(struct ins_operands *ops)
zfree(&ops->source.name);
zfree(&ops->target.raw);
zfree(&ops->target.name);
+ symbol__zput(ops->target.sym);
}
int ins__raw_scnprintf(const struct ins *ins, char *bf, size_t size,
@@ -290,7 +291,7 @@ static int call__parse(const struct arch *arch, struct ins_operands *ops, struct
if (maps__find_ams(thread__maps(ms->thread), &target) == 0 &&
map__rip_2objdump(target.ms.map, map__map_ip(target.ms.map, target.addr)) == ops->target.addr)
- ops->target.sym = target.ms.sym;
+ ops->target.sym = symbol__get(target.ms.sym);
addr_map_symbol__exit(&target);
return 0;
@@ -425,7 +426,7 @@ static int jump__parse(const struct arch *arch, struct ins_operands *ops, struct
*/
if (maps__find_ams(thread__maps(ms->thread), &target) == 0 &&
map__rip_2objdump(target.ms.map, map__map_ip(target.ms.map, target.addr)) == ops->target.addr)
- ops->target.sym = target.ms.sym;
+ ops->target.sym = symbol__get(target.ms.sym);
if (!ops->target.outside) {
ops->target.offset = target.addr - start;
@@ -471,12 +472,14 @@ int jump__scnprintf(const struct ins *ins, char *bf, size_t size,
ops->target.offset);
}
-void jump__delete(struct ins_operands *ops __maybe_unused)
+void jump__delete(struct ins_operands *ops)
{
/*
* The ops->jump.raw_comment and ops->jump.raw_func_start belong to the
* raw string, don't free them.
*/
+ if (ops)
+ symbol__zput(ops->target.sym);
}
const struct ins_ops jump_ops = {
@@ -570,6 +573,7 @@ static void lock__delete(struct ins_operands *ops)
zfree(&ops->locked.ins.name);
zfree(&ops->target.raw);
zfree(&ops->target.name);
+ symbol__zput(ops->target.sym);
}
const struct ins_ops lock_ops = {
@@ -1090,7 +1094,7 @@ static int symbol__parse_objdump_line(struct symbol *sym,
if (!maps__find_ams(thread__maps(args->ms->thread), &target) &&
symbol__start(target.ms.sym) == target.al_addr)
- dl->ops.target.sym = target.ms.sym;
+ dl->ops.target.sym = symbol__get(target.ms.sym);
addr_map_symbol__exit(&target);
}
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c
index 939307796a88..a71267af4f3c 100644
--- a/tools/perf/util/event.c
+++ b/tools/perf/util/event.c
@@ -770,7 +770,7 @@ struct map *thread__find_map_fb(struct thread *thread, u8 cpumode, u64 addr,
struct symbol *thread__find_symbol(struct thread *thread, u8 cpumode,
u64 addr, struct addr_location *al)
{
- al->sym = NULL;
+ symbol__zput(al->sym);
if (thread__find_map(thread, cpumode, addr, al))
al->sym = map__find_symbol(al->map, al->addr);
return al->sym;
@@ -779,7 +779,7 @@ struct symbol *thread__find_symbol(struct thread *thread, u8 cpumode,
struct symbol *thread__find_symbol_fb(struct thread *thread, u8 cpumode,
u64 addr, struct addr_location *al)
{
- al->sym = NULL;
+ symbol__zput(al->sym);
if (thread__find_map_fb(thread, cpumode, addr, al))
al->sym = map__find_symbol(al->map, al->addr);
return al->sym;
@@ -829,7 +829,7 @@ int machine__resolve(struct machine *machine, struct addr_location *al,
thread__put(thread);
thread = NULL;
- al->sym = NULL;
+ symbol__zput(al->sym);
al->cpu = sample->cpu;
al->socket = -1;
al->srcline = NULL;
@@ -937,7 +937,7 @@ void thread__resolve(struct thread *thread, struct addr_location *al,
thread__find_map_fb(thread, sample->cpumode, sample->addr, al);
al->cpu = sample->cpu;
- al->sym = NULL;
+ symbol__zput(al->sym);
if (al->map)
al->sym = map__find_symbol(al->map, al->addr);
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c
index dd388169c25b..7ae4e7664ba7 100644
--- a/tools/perf/util/hist.c
+++ b/tools/perf/util/hist.c
@@ -542,6 +542,7 @@ static int hist_entry__init(struct hist_entry *he,
he->ms.thread = thread__get(he->ms.thread);
he->ms.map = map__get(he->ms.map);
+ he->ms.sym = symbol__get(he->ms.sym);
if (he->branch_info) {
/*
@@ -558,8 +559,10 @@ static int hist_entry__init(struct hist_entry *he,
he->branch_info->from.ms.thread = thread__get(he->branch_info->from.ms.thread);
he->branch_info->from.ms.map = map__get(he->branch_info->from.ms.map);
+ he->branch_info->from.ms.sym = symbol__get(he->branch_info->from.ms.sym);
he->branch_info->to.ms.thread = thread__get(he->branch_info->to.ms.thread);
he->branch_info->to.ms.map = map__get(he->branch_info->to.ms.map);
+ he->branch_info->to.ms.sym = symbol__get(he->branch_info->to.ms.sym);
}
if (he->mem_info) {
@@ -592,6 +595,7 @@ static int hist_entry__init(struct hist_entry *he,
INIT_LIST_HEAD(&he->pairs.node);
he->thread = thread__get(he->thread);
+ he->parent = symbol__get(he->parent);
he->hroot_in = RB_ROOT_CACHED;
he->hroot_out = RB_ROOT_CACHED;
@@ -727,6 +731,8 @@ static struct hist_entry *hists__findnew_entry(struct hists *hists,
if (hists__has(hists, sym) && he->ms.map != entry->ms.map) {
if (he->ms.sym) {
u64 addr = symbol__start(he->ms.sym);
+
+ symbol__put(he->ms.sym);
he->ms.sym = map__find_symbol(entry->ms.map, addr);
}
@@ -1028,7 +1034,8 @@ iter_next_branch_entry(struct hist_entry_iter *iter, struct addr_location *al)
al->thread = thread__get(bi[i].to.ms.thread);
map__put(al->map);
al->map = map__get(bi[i].to.ms.map);
- al->sym = bi[i].to.ms.sym;
+ symbol__put(al->sym);
+ al->sym = symbol__get(bi[i].to.ms.sym);
al->addr = bi[i].to.addr;
return 1;
}
@@ -1342,9 +1349,11 @@ int hist_entry_iter__add(struct hist_entry_iter *iter, struct addr_location *al,
if (al)
alm = map__get(al->map);
+ iter->parent = NULL;
err = sample__resolve_callchain(iter->sample, get_tls_callchain_cursor(), &iter->parent,
al, max_stack_depth);
if (err) {
+ symbol__zput(iter->parent);
map__put(alm);
return err;
}
@@ -1380,6 +1389,7 @@ int hist_entry_iter__add(struct hist_entry_iter *iter, struct addr_location *al,
if (!err)
err = err2;
+ symbol__zput(iter->parent);
map__put(alm);
return err;
@@ -1466,6 +1476,7 @@ void hist_entry__delete(struct hist_entry *he)
}
thread__zput(he->thread);
+ symbol__zput(he->parent);
map_symbol__exit(&he->ms);
if (he->branch_info) {
diff --git a/tools/perf/util/libdw.c b/tools/perf/util/libdw.c
index c4504cceb313..ca66c9afdcd6 100644
--- a/tools/perf/util/libdw.c
+++ b/tools/perf/util/libdw.c
@@ -98,7 +98,7 @@ static int libdw_a2l_cb(Dwarf_Die *die, void *_args)
* while DWARF keeps the pre-clone linkage name, which
* left the outer frame spuriously tagged "(inlined)".
*/
- inline_sym = args->sym;
+ inline_sym = symbol__get(args->sym);
} else {
/*
* Prefer DW_AT_linkage_name so C++ inline frames keep
@@ -156,8 +156,7 @@ static int libdw_a2l_cb(Dwarf_Die *die, void *_args)
return 0;
abort_delete_sym:
- if (symbol__inlined(inline_sym))
- symbol__delete(inline_sym);
+ symbol__put(inline_sym);
abort_enomem:
args->err = -ENOMEM;
return DWARF_CB_ABORT;
diff --git a/tools/perf/util/lock-contention.c b/tools/perf/util/lock-contention.c
index 8ee1b19a8292..367666028eb2 100644
--- a/tools/perf/util/lock-contention.c
+++ b/tools/perf/util/lock-contention.c
@@ -101,7 +101,6 @@ struct lock_stat *lock_stat_findnew(u64 addr, const char *name, int flags)
bool match_callstack_filter(struct machine *machine, u64 *callstack, int max_stack_depth)
{
- struct map *kmap;
struct symbol *sym;
u64 ip;
uint16_t e_machine = perf_env__e_machine(machine->env, /*e_flags=*/NULL);
@@ -130,14 +129,17 @@ bool match_callstack_filter(struct machine *machine, u64 *callstack, int max_sta
break;
ip = callstack[i];
- sym = machine__find_kernel_symbol(machine, ip, &kmap);
+ sym = machine__find_kernel_symbol(machine, ip, NULL);
if (sym == NULL)
continue;
list_for_each_entry(filter, &callstack_filters, list) {
- if (strstr(symbol__name(sym), filter->name))
+ if (strstr(symbol__name(sym), filter->name)) {
+ symbol__put(sym);
return true;
+ }
}
+ symbol__put(sym);
}
return false;
}
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index cb30d858f62d..f1155c608a95 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -824,8 +824,10 @@ static int machine__process_ksymbol_unregister(struct machine *machine,
struct dso *dso = map__dso(map);
sym = dso__find_symbol(dso, map__map_ip(map, map__start(map)));
- if (sym)
+ if (sym) {
dso__delete_symbol(dso, sym);
+ symbol__put(sym);
+ }
}
map__put(map);
return 0;
@@ -2085,7 +2087,7 @@ static void ip__resolve_ams(struct thread *thread,
ams->al_addr = al.addr;
ams->al_level = al.level;
ams->ms.thread = thread__get(al.thread);
- ams->ms.sym = al.sym;
+ ams->ms.sym = symbol__get(al.sym);
ams->ms.map = map__get(al.map);
ams->phys_addr = 0;
ams->data_page_size = 0;
@@ -2106,7 +2108,7 @@ static void ip__resolve_data(struct thread *thread,
ams->al_addr = al.addr;
ams->al_level = al.level;
ams->ms.thread = thread__get(al.thread);
- ams->ms.sym = al.sym;
+ ams->ms.sym = symbol__get(al.sym);
ams->ms.map = map__get(al.map);
ams->phys_addr = phys_addr;
ams->data_page_size = daddr_page_size;
@@ -2192,7 +2194,8 @@ static int append_inlines(struct callchain_cursor *cursor, struct map_symbol *ms
.map = map__get(map),
};
list_for_each_entry(ilist, &inline_node->val, list) {
- ilist_ms.sym = ilist->symbol;
+ symbol__put(ilist_ms.sym);
+ ilist_ms.sym = symbol__get(ilist->symbol);
if (first) {
ret = callchain_cursor_append(cursor, ip, &ilist_ms,
branch, flags, nr_loop_iter,
@@ -2204,7 +2207,7 @@ static int append_inlines(struct callchain_cursor *cursor, struct map_symbol *ms
first = false;
if (ret != 0)
- return ret;
+ break;
}
map_symbol__exit(&ilist_ms);
@@ -2270,7 +2273,7 @@ static int add_callchain_ip(struct thread *thread,
if (al.sym != NULL) {
if (perf_hpp_list.parent && !*parent &&
symbol__match_regex(al.sym, &parent_regex))
- *parent = al.sym;
+ *parent = symbol__get(al.sym);
else if (have_ignore_callees && root_al &&
symbol__match_regex(al.sym, &ignore_callees_regex)) {
/* Treat this symbol as the root,
@@ -2290,7 +2293,7 @@ static int add_callchain_ip(struct thread *thread,
ms.thread = thread__get(al.thread);
ms.map = map__get(al.map);
- ms.sym = al.sym;
+ ms.sym = symbol__get(al.sym);
if (append_inlines(cursor, &ms, ip, branch, flags, nr_loop_iter,
iter_cycles, branch_from) == 0)
@@ -2451,8 +2454,7 @@ static void save_lbr_cursor_node(struct thread *thread,
map_symbol__exit(&lbr_stitch->prev_lbr_cursor[idx].ms);
memcpy(&lbr_stitch->prev_lbr_cursor[idx], cursor->curr,
sizeof(struct callchain_cursor_node));
- lbr_stitch->prev_lbr_cursor[idx].ms.thread = thread__get(cursor->curr->ms.thread);
- lbr_stitch->prev_lbr_cursor[idx].ms.map = map__get(cursor->curr->ms.map);
+ map_symbol__copy(&lbr_stitch->prev_lbr_cursor[idx].ms, &cursor->curr->ms);
lbr_stitch->prev_lbr_cursor[idx].valid = true;
cursor->pos++;
@@ -2676,9 +2678,7 @@ static bool has_stitched_lbr(struct thread *thread,
memcpy(&stitch_node->cursor, &lbr_stitch->prev_lbr_cursor[i],
sizeof(struct callchain_cursor_node));
- stitch_node->cursor.ms.thread =
- thread__get(lbr_stitch->prev_lbr_cursor[i].ms.thread);
- stitch_node->cursor.ms.map = map__get(lbr_stitch->prev_lbr_cursor[i].ms.map);
+ map_symbol__copy(&stitch_node->cursor.ms, &lbr_stitch->prev_lbr_cursor[i].ms);
if (callee)
list_add(&stitch_node->node, &lbr_stitch->lists);
@@ -3282,15 +3282,21 @@ struct dso *machine__findnew_dso(struct machine *machine, const char *filename)
char *machine__resolve_kernel_addr(void *vmachine, unsigned long long *addrp, char **modp)
{
struct machine *machine = vmachine;
- struct map *map;
+ struct map *map = NULL;
struct symbol *sym = machine__find_kernel_symbol(machine, *addrp, &map);
+ char *name;
- if (sym == NULL)
+ if (sym == NULL) {
+ map__put(map);
return NULL;
+ }
*modp = __map__is_kmodule(map) ? (char *)dso__short_name(map__dso(map)) : NULL;
*addrp = map__unmap_ip(map, symbol__start(sym));
- return (char *)symbol__name(sym);
+ name = (char *)symbol__name(sym);
+ symbol__put(sym);
+ map__put(map);
+ return name;
}
struct machine__for_each_dso_cb_args {
@@ -3338,19 +3344,27 @@ bool machine__is_lock_function(struct machine *machine, u64 addr)
machine->sched.text_start = map__unmap_ip(kmap,
symbol__start(sym));
+ symbol__put(sym);
+ map__put(kmap);
/* should not fail from here */
sym = machine__find_kernel_symbol_by_name(machine, "__sched_text_end", &kmap);
machine->sched.text_end = map__unmap_ip(kmap,
symbol__start(sym));
+ symbol__put(sym);
+ map__put(kmap);
sym = machine__find_kernel_symbol_by_name(machine, "__lock_text_start", &kmap);
machine->lock.text_start = map__unmap_ip(kmap,
symbol__start(sym));
+ symbol__put(sym);
+ map__put(kmap);
sym = machine__find_kernel_symbol_by_name(machine, "__lock_text_end", &kmap);
machine->lock.text_end = map__unmap_ip(kmap,
symbol__start(sym));
+ symbol__put(sym);
+ map__put(kmap);
sym = machine__find_kernel_symbol_by_name(machine, "__traceiter_contention_begin", &kmap);
if (sym) {
@@ -3358,6 +3372,8 @@ bool machine__is_lock_function(struct machine *machine, u64 addr)
symbol__start(sym));
machine->traceiter.text_end = map__unmap_ip(kmap,
symbol__end(sym));
+ symbol__put(sym);
+ map__put(kmap);
}
sym = machine__find_kernel_symbol_by_name(machine, "trace_contention_begin", &kmap);
if (sym) {
@@ -3365,6 +3381,8 @@ bool machine__is_lock_function(struct machine *machine, u64 addr)
symbol__start(sym));
machine->trace.text_end = map__unmap_ip(kmap,
symbol__end(sym));
+ symbol__put(sym);
+ map__put(kmap);
}
}
diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h
index 3d8f72e70d49..745eefa79c7f 100644
--- a/tools/perf/util/map.h
+++ b/tools/perf/util/map.h
@@ -160,13 +160,15 @@ struct thread;
* @sym_name: the symbol name
* @pos: the 'struct symbol *' to use as a loop cursor
* @idx: the cursor index in the symbol names array
+ * Note: caller must call symbol__put(pos) if breaking out of the loop early.
*/
#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(symbol__name(pos), sym_name, \
- SYMBOL_TAG_INCLUDE__DEFAULT_ONLY); \
- pos = dso__next_symbol_by_name(map__dso(map), &idx))
+ pos && \
+ (!symbol__match_symbol_name(symbol__name(pos), sym_name, \
+ SYMBOL_TAG_INCLUDE__DEFAULT_ONLY) || \
+ (symbol__zput(pos), false)); \
+ symbol__put(pos), pos = dso__next_symbol_by_name(map__dso(map), &idx))
#define map__for_each_symbol_by_name(map, sym_name, pos, idx) \
__map__for_each_symbol_by_name(map, sym_name, (pos), idx)
diff --git a/tools/perf/util/map_symbol.c b/tools/perf/util/map_symbol.c
index 11bc0a7f704c..d1ee4e05564e 100644
--- a/tools/perf/util/map_symbol.c
+++ b/tools/perf/util/map_symbol.c
@@ -2,12 +2,14 @@
#include "map_symbol.h"
#include "maps.h"
#include "map.h"
+#include "symbol.h"
#include "thread.h"
void map_symbol__exit(struct map_symbol *ms)
{
thread__zput(ms->thread);
map__zput(ms->map);
+ symbol__zput(ms->sym);
}
void addr_map_symbol__exit(struct addr_map_symbol *ams)
@@ -19,7 +21,7 @@ void map_symbol__copy(struct map_symbol *dst, struct map_symbol *src)
{
dst->thread = thread__get(src->thread);
dst->map = map__get(src->map);
- dst->sym = src->sym;
+ dst->sym = symbol__get(src->sym);
}
void addr_map_symbol__copy(struct addr_map_symbol *dst, struct addr_map_symbol *src)
diff --git a/tools/perf/util/maps.c b/tools/perf/util/maps.c
index f808df2fe77b..a6cbe65cc5ca 100644
--- a/tools/perf/util/maps.c
+++ b/tools/perf/util/maps.c
@@ -788,7 +788,7 @@ struct symbol *maps__find_symbol_by_name(struct maps *maps, const char *name, st
*mapp = map__get(map);
break;
}
- sym = NULL;
+ symbol__zput(sym);
}
for (unsigned int i = 0; i < nr_maps; i++)
@@ -810,6 +810,7 @@ int maps__find_ams(struct maps *maps, struct addr_map_symbol *ams)
}
ams->al_addr = map__map_ip(ams->ms.map, ams->addr);
+ symbol__put(ams->ms.sym);
ams->ms.sym = map__find_symbol(ams->ms.map, ams->al_addr);
return ams->ms.sym ? 0 : -1;
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 47dbe865c2f9..4b2d4db364b8 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -152,6 +152,8 @@ static int kernel_get_symbol_address_by_name(const char *name, u64 *addr,
*addr = map__unmap_ip(map, symbol__start(sym)) -
((reloc) ? 0 : map__reloc(map)) -
((reladdr) ? map__start(map) : 0);
+ symbol__put(sym);
+ map__put(map);
}
return 0;
}
@@ -422,6 +424,7 @@ static int find_alternative_probe_point(struct debuginfo *dinfo,
pp->function);
} else
address = map__unmap_ip(map, symbol__start(sym)) - map__reloc(map);
+ symbol__put(sym);
break;
}
if (!address) {
@@ -728,11 +731,14 @@ static int post_process_probe_trace_point(struct probe_trace_point *tp,
else
free(tp->symbol);
tp->symbol = strdup(symbol__name(sym));
- if (!tp->symbol)
+ if (!tp->symbol) {
+ symbol__put(sym);
return -ENOMEM;
+ }
}
tp->offset = addr - symbol__start(sym);
tp->address -= offs;
+ symbol__put(sym);
return 0;
}
@@ -2314,6 +2320,7 @@ static int find_perf_probe_point_from_map(struct probe_trace_point *tp,
ret = pp->function ? 0 : -ENOMEM;
out:
+ symbol__put(sym);
map__put(map);
return ret;
@@ -3078,7 +3085,7 @@ static int find_probe_functions_cb(struct symbol *sym, void *data)
if (strglobmatch(norm, args->name)) {
args->found++;
if (args->syms && args->found < probe_conf.max_probes)
- args->syms[args->found - 1] = sym;
+ args->syms[args->found - 1] = symbol__get(sym);
}
free(buf);
return 0;
@@ -3146,7 +3153,7 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev,
goto out;
}
- syms = malloc(sizeof(struct symbol *) * probe_conf.max_probes);
+ syms = calloc(probe_conf.max_probes, sizeof(struct symbol *));
if (!syms) {
ret = -ENOMEM;
goto out;
@@ -3291,6 +3298,10 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev,
}
out:
+ if (syms) {
+ for (i = 0; i < probe_conf.max_probes; i++)
+ symbol__put(syms[i]);
+ }
map__put(map);
free(syms);
return ret;
diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c
index d47f6865021e..6c8bce6c795e 100644
--- a/tools/perf/util/python.c
+++ b/tools/perf/util/python.c
@@ -814,7 +814,7 @@ static PyObject *pyrf_sample_event__srccode(PyObject *self, PyObject *args)
addr_location__init(&al);
al.thread = thread__get(pevent->al.thread);
al.map = map__get(pevent->al.map);
- al.sym = pevent->al.sym;
+ al.sym = symbol__get(pevent->al.sym);
al.addr = pevent->al.addr;
}
@@ -877,6 +877,7 @@ struct pyrf_callchain_node {
static void pyrf_callchain_node__delete(struct pyrf_callchain_node *pnode)
{
map__put(pnode->map);
+ symbol__put(pnode->sym);
Py_TYPE(pnode)->tp_free((PyObject *)pnode);
}
@@ -944,8 +945,10 @@ struct pyrf_callchain {
static void pyrf_callchain__delete(struct pyrf_callchain *pchain)
{
if (pchain->frames) {
- for (u64 i = 0; i < pchain->nr_frames; i++)
+ for (u64 i = 0; i < pchain->nr_frames; i++) {
map__put(pchain->frames[i].map);
+ symbol__put(pchain->frames[i].sym);
+ }
free(pchain->frames);
}
Py_TYPE(pchain)->tp_free((PyObject *)pchain);
@@ -974,7 +977,7 @@ static PyObject *pyrf_callchain__item(PyObject *obj, Py_ssize_t i)
pnode->ip = pchain->frames[i].ip;
pnode->map = map__get(pchain->frames[i].map);
- pnode->sym = pchain->frames[i].sym;
+ pnode->sym = symbol__get(pchain->frames[i].sym);
return (PyObject *)pnode;
}
@@ -1486,7 +1489,8 @@ static PyObject *pyrf_event__new(const union perf_event *event, struct evsel *ev
pchain->frames[i].ip = node->ip;
pchain->frames[i].map =
map__get(node->ms.map);
- pchain->frames[i].sym = node->ms.sym;
+ pchain->frames[i].sym =
+ symbol__get(node->ms.sym);
callchain_cursor_advance(cursor);
}
pevent->callchain = (PyObject *)pchain;
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c
index 89e833f35fd2..79ea71aab13c 100644
--- a/tools/perf/util/sort.c
+++ b/tools/perf/util/sort.c
@@ -466,7 +466,7 @@ int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r)
if (!sym_l || !sym_r)
return cmp_null(sym_l, sym_r);
- if (sym_l == sym_r)
+ if (RC_CHK_EQUAL(sym_l, sym_r))
return 0;
if (symbol__inlined(sym_l) || symbol__inlined(sym_r)) {
diff --git a/tools/perf/util/srcline.c b/tools/perf/util/srcline.c
index 53976de3105c..4151107ccae1 100644
--- a/tools/perf/util/srcline.c
+++ b/tools/perf/util/srcline.c
@@ -118,7 +118,7 @@ struct symbol *new_inline_sym(struct dso *dso,
if (base_sym && strcmp(funcname, symbol__name(base_sym)) == 0) {
/* reuse the real, existing symbol */
- inline_sym = base_sym;
+ inline_sym = symbol__get(base_sym);
/* ensure that we don't alias an inlined symbol, which could
* lead to double frees in inline_node__delete
*/
@@ -468,9 +468,7 @@ void inline_node__clear_frames(struct inline_node *node)
list_for_each_entry_safe(ilist, tmp, &node->val, list) {
list_del_init(&ilist->list);
zfree_srcline(&ilist->srcline);
- /* only the inlined symbols are owned by the list */
- if (ilist->symbol && symbol__inlined(ilist->symbol))
- symbol__delete(ilist->symbol);
+ symbol__zput(ilist->symbol);
free(ilist);
}
}
diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c
index 73885413073b..90542fb643e9 100644
--- a/tools/perf/util/symbol-elf.c
+++ b/tools/perf/util/symbol-elf.c
@@ -351,10 +351,13 @@ static bool get_ifunc_name(Elf *elf, struct dso *dso, GElf_Ehdr *ehdr,
/* Expecting the address to be an IFUNC or IFUNC alias */
if (!sym || symbol__start(sym) != addr ||
- (symbol__type(sym) != STT_GNU_IFUNC && !symbol__ifunc_alias(sym)))
+ (symbol__type(sym) != STT_GNU_IFUNC && !symbol__ifunc_alias(sym))) {
+ symbol__put(sym);
return false;
+ }
snprintf(buf, buf_sz, "%s@plt", symbol__name(sym));
+ symbol__put(sym);
return true;
}
@@ -631,6 +634,7 @@ int dso__synthesize_plt_symbols(struct dso *dso, struct symsrc *ss)
f = dso__find_symbol_nocache(dso, shdr_plt.sh_offset);
if (f && symbol__start(f) < shdr_plt.sh_offset && symbol__end(f) > shdr_plt.sh_offset)
symbol__set_end(f, shdr_plt.sh_offset);
+ symbol__put(f);
if (!get_plt_sizes(dso, &ehdr, &shdr_plt, &plt_header_size, &plt_entry_size))
return 0;
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index d25d8c96ee66..593fccfe3240 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
#include <dirent.h>
#include <errno.h>
+#include <stdatomic.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
@@ -228,7 +229,7 @@ void symbols__init(struct symbols *symbols)
static void symbols__delete_locked(struct symbols *symbols)
{
for (unsigned int i = 0; i < symbols->cnt; i++)
- symbol__delete(symbols->symbols[i]);
+ symbol__put(symbols->symbols[i]);
zfree(&symbols->symbols);
zfree(&symbols->symbols_by_name);
@@ -331,11 +332,11 @@ void symbols__fixup_duplicate(struct symbols *symbols)
if (choose_best_symbol(curr, next) == SYMBOL_A) {
if (symbol__type(next) == STT_GNU_IFUNC)
symbol__set_ifunc_alias(curr, true);
- symbol__delete(next);
+ symbol__put(next);
} else {
if (symbol__type(curr) == STT_GNU_IFUNC)
symbol__set_ifunc_alias(next, true);
- symbol__delete(curr);
+ symbol__put(curr);
symbols->symbols[write_idx] = next;
}
}
@@ -421,81 +422,96 @@ void symbols__fixup_end(struct symbols *symbols, bool is_kallsyms)
struct symbol *symbol__new(u64 start, u64 len, u8 binding, u8 type, const char *name)
{
size_t namelen = strlen(name) + 1;
- struct symbol *sym = calloc(1, sizeof(*sym) + namelen);
-
- if (sym == NULL)
- return NULL;
-
- sym->start = start;
- sym->end = len ? start + len : start;
- atomic_init(&sym->flags, (type << SYMBOL_FLAG_TYPE_SHIFT) |
- (binding << SYMBOL_FLAG_BINDING_SHIFT));
- sym->namelen = namelen - 1;
-
- pr_debug4("%s: %s %#" PRIx64 "-%#" PRIx64 "\n",
- __func__, name, start, sym->end);
- memcpy(sym->name, name, namelen);
+ RC_STRUCT(symbol) *sym;
+ struct symbol *res;
+
+ sym = calloc(1, sizeof(*sym) + namelen);
+
+ if (ADD_RC_CHK(res, sym)) {
+ refcount_set(&sym->refcnt, 1);
+ sym->start = start;
+ sym->end = len ? start + len : start;
+ atomic_init(&sym->flags, (type << SYMBOL_FLAG_TYPE_SHIFT) |
+ (binding << SYMBOL_FLAG_BINDING_SHIFT));
+ sym->namelen = namelen - 1;
+
+ pr_debug4("%s: %s %#" PRIx64 "-%#" PRIx64 "\n",
+ __func__, name, start, sym->end);
+ memcpy(sym->name, name, namelen);
+ } else {
+ free(sym);
+ }
- return sym;
+ return res;
}
void symbol__delete(struct symbol *sym)
{
+ if (!sym)
+ return;
if (symbol__annotated(sym))
symbol__annotation_delete(sym);
- free(sym);
+ RC_CHK_FREE(sym);
+}
+
+void symbol__put(struct symbol *sym)
+{
+ if (sym && refcount_dec_and_test(symbol__refcnt(sym)))
+ symbol__delete(sym);
+ else
+ RC_CHK_PUT(sym);
}
void symbol__set_ignore(struct symbol *sym, bool ignore)
{
if (ignore)
- atomic_fetch_or(&sym->flags, SYMBOL_FLAG_IGNORE);
+ atomic_fetch_or(&RC_CHK_ACCESS(sym)->flags, SYMBOL_FLAG_IGNORE);
else
- atomic_fetch_and(&sym->flags, ~SYMBOL_FLAG_IGNORE);
+ atomic_fetch_and(&RC_CHK_ACCESS(sym)->flags, ~SYMBOL_FLAG_IGNORE);
}
void symbol__set_annotate2(struct symbol *sym, bool annotate2)
{
if (annotate2)
- atomic_fetch_or(&sym->flags, SYMBOL_FLAG_ANNOTATE2);
+ atomic_fetch_or(&RC_CHK_ACCESS(sym)->flags, SYMBOL_FLAG_ANNOTATE2);
else
- atomic_fetch_and(&sym->flags, ~SYMBOL_FLAG_ANNOTATE2);
+ atomic_fetch_and(&RC_CHK_ACCESS(sym)->flags, ~SYMBOL_FLAG_ANNOTATE2);
}
void symbol__set_inlined(struct symbol *sym, bool inlined)
{
if (inlined)
- atomic_fetch_or(&sym->flags, SYMBOL_FLAG_INLINED);
+ atomic_fetch_or(&RC_CHK_ACCESS(sym)->flags, SYMBOL_FLAG_INLINED);
else
- atomic_fetch_and(&sym->flags, ~SYMBOL_FLAG_INLINED);
+ atomic_fetch_and(&RC_CHK_ACCESS(sym)->flags, ~SYMBOL_FLAG_INLINED);
}
void symbol__set_ifunc_alias(struct symbol *sym, bool ifunc_alias)
{
if (ifunc_alias)
- atomic_fetch_or(&sym->flags, SYMBOL_FLAG_IFUNC_ALIAS);
+ atomic_fetch_or(&RC_CHK_ACCESS(sym)->flags, SYMBOL_FLAG_IFUNC_ALIAS);
else
- atomic_fetch_and(&sym->flags, ~SYMBOL_FLAG_IFUNC_ALIAS);
+ atomic_fetch_and(&RC_CHK_ACCESS(sym)->flags, ~SYMBOL_FLAG_IFUNC_ALIAS);
}
void symbol__set_annotated(struct symbol *sym, bool annotated)
{
if (annotated)
- atomic_fetch_or(&sym->flags, SYMBOL_FLAG_ANNOTATED);
+ atomic_fetch_or(&RC_CHK_ACCESS(sym)->flags, SYMBOL_FLAG_ANNOTATED);
else
- atomic_fetch_and(&sym->flags, ~SYMBOL_FLAG_ANNOTATED);
+ atomic_fetch_and(&RC_CHK_ACCESS(sym)->flags, ~SYMBOL_FLAG_ANNOTATED);
}
static void symbol__set_idle(struct symbol *sym, bool idle)
{
- uint16_t old_flags = atomic_load_explicit(&sym->flags, memory_order_relaxed);
+ uint16_t old_flags = atomic_load_explicit(&RC_CHK_ACCESS(sym)->flags, memory_order_relaxed);
uint16_t new_flags;
uint16_t idle_val = idle ? SYMBOL_IDLE__IDLE : SYMBOL_IDLE__NOT_IDLE;
do {
new_flags = old_flags & ~SYMBOL_FLAG_IDLE_MASK;
new_flags |= (idle_val << SYMBOL_FLAG_IDLE_SHIFT);
- } while (!atomic_compare_exchange_weak(&sym->flags, &old_flags, new_flags));
+ } while (!atomic_compare_exchange_weak(&RC_CHK_ACCESS(sym)->flags, &old_flags, new_flags));
}
int __symbols__insert(struct symbols *symbols, struct symbol *sym)
@@ -506,7 +522,7 @@ int __symbols__insert(struct symbols *symbols, struct symbol *sym)
sizeof(struct symbol *) * to_allocate);
if (!temp) {
- symbol__delete(sym);
+ symbol__put(sym);
return -ENOMEM;
}
symbols->symbols = temp;
@@ -559,7 +575,7 @@ static struct symbol *symbols__find(struct symbols *symbols, u64 ip)
else if (ip > end || (ip == end && ip != start))
low = mid + 1;
else {
- res = s;
+ res = symbol__get(s);
break;
}
}
@@ -684,7 +700,7 @@ static struct symbol *symbols__find_by_name(struct symbol *symbols[],
}
}
assert(!found_idx || !s || s == symbols[*found_idx]);
- return s;
+ return symbol__get(s);
}
int dso__insert_symbol(struct dso *dso, struct symbol *sym)
@@ -698,14 +714,16 @@ void dso__delete_symbol(struct dso *dso, struct symbol *sym)
down_write(&symbols->lock);
for (unsigned int i = 0; i < symbols->cnt; i++) {
- if (symbols->symbols[i] == sym) {
+ if (RC_CHK_EQUAL(symbols->symbols[i], sym)) {
+ struct symbol *old = symbols->symbols[i];
+
memmove(&symbols->symbols[i], &symbols->symbols[i + 1],
(symbols->cnt - i - 1) * sizeof(struct symbol *));
symbols->cnt--;
zfree(&symbols->symbols_by_name);
symbols->num_by_name = 0;
symbols->sorted_by_name = false;
- symbol__delete(sym);
+ symbol__put(old);
break;
}
}
@@ -724,11 +742,17 @@ struct symbol *dso__find_symbol_nocache(struct dso *dso, u64 addr)
struct symbol *dso__next_symbol_by_name(struct dso *dso, size_t *idx)
{
- if (*idx + 1 >= dso__symbol_names_len(dso))
- return NULL;
+ struct symbols *symbols = dso__symbols(dso);
+ struct symbol *res = NULL;
+
+ down_read(&symbols->lock);
+ if (*idx + 1 < symbols->num_by_name) {
+ ++*idx;
+ res = symbol__get(symbols->symbols_by_name[*idx]);
+ }
+ up_read(&symbols->lock);
- ++*idx;
- return dso__symbol_names(dso)[*idx];
+ return res;
}
/*
@@ -736,16 +760,26 @@ 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 symbols *symbols;
struct symbol *s;
- dso__sort_by_name(dso);
- s = symbols__find_by_name(dso__symbol_names(dso),
- dso__symbol_names_len(dso),
+ symbols = dso__symbols(dso);
+ down_read(&symbols->lock);
+ while (!symbols->sorted_by_name) {
+ up_read(&symbols->lock);
+ dso__sort_by_name(dso);
+ down_read(&symbols->lock);
+ if (!symbols->sorted_by_name && !symbols->symbols_by_name)
+ break;
+ }
+ s = symbols__find_by_name(symbols->symbols_by_name,
+ symbols->num_by_name,
name, SYMBOL_TAG_INCLUDE__NONE, idx);
if (!s) {
- s = symbols__find_by_name(dso__symbol_names(dso), dso__symbol_names_len(dso),
+ s = symbols__find_by_name(symbols->symbols_by_name, symbols->num_by_name,
name, SYMBOL_TAG_INCLUDE__DEFAULT_ONLY, idx);
}
+ up_read(&symbols->lock);
return s;
}
@@ -884,7 +918,8 @@ bool symbol__is_idle(struct symbol *sym, const struct dso *dso, struct perf_env
uint16_t e_machine;
{
- uint16_t flags = atomic_load_explicit(&sym->flags, memory_order_relaxed);
+ uint16_t flags = atomic_load_explicit(&RC_CHK_ACCESS(sym)->flags,
+ memory_order_relaxed);
uint16_t idle_val = (flags & SYMBOL_FLAG_IDLE_MASK) >> SYMBOL_FLAG_IDLE_SHIFT;
if (idle_val != SYMBOL_IDLE__UNKNOWN)
@@ -1032,7 +1067,7 @@ static int maps__split_kallsyms_for_kcore(struct maps *kmaps, struct dso *dso)
curr_map = maps__find(kmaps, symbol__start(pos));
if (!curr_map) {
- symbol__delete(pos);
+ symbol__put(pos);
continue;
}
curr_map_dso = map__dso(curr_map);
@@ -1228,7 +1263,7 @@ static int maps__split_kallsyms(struct maps *kmaps, struct dso *dso, u64 delta,
continue;
discard_symbol:
- symbol__delete(pos);
+ symbol__put(pos);
}
root->cnt = write_idx;
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index 52cd6b43696c..a3c88b5224ec 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -13,6 +13,7 @@
#include <linux/string.h>
#include <stdio.h>
#include <errno.h>
+#include <internal/rc_check.h>
#include "addr_location.h"
#include "path.h"
#include "rwsem.h"
@@ -93,10 +94,12 @@ enum symbol_idle_kind {
/**
* A symtab entry.
*/
-struct symbol {
+DECLARE_RC_STRUCT(symbol) {
/** Range of symbol [start, end). */
u64 start;
u64 end;
+ /** Reference count. */
+ refcount_t refcnt;
/** Length of the string name. */
u16 namelen;
_Atomic uint16_t flags;
@@ -143,85 +146,110 @@ void symbols__exit(struct symbols *symbols);
void symbols__delete(struct symbols *symbols);
void symbols__sort_read_lock(struct symbols *symbols) SHARED_LOCK_FUNCTION(symbols->lock);
+static inline refcount_t *symbol__refcnt(struct symbol *sym)
+{
+ return &RC_CHK_ACCESS(sym)->refcnt;
+}
+
+static inline struct symbol *symbol__get(struct symbol *sym)
+{
+ struct symbol *result;
+
+ if (RC_CHK_GET(result, sym))
+ refcount_inc(symbol__refcnt(sym));
+
+ return result;
+}
+
+void symbol__put(struct symbol *sym);
+
+static inline void __symbol__zput(struct symbol **sym)
+{
+ symbol__put(*sym);
+ *sym = NULL;
+}
+
+#define symbol__zput(sym) __symbol__zput(&sym)
+
static inline u64 symbol__start(const struct symbol *sym)
{
- return sym->start;
+ return RC_CHK_ACCESS(sym)->start;
}
static inline void symbol__set_start(struct symbol *sym, u64 start)
{
- sym->start = start;
+ RC_CHK_ACCESS(sym)->start = start;
}
static inline u64 symbol__end(const struct symbol *sym)
{
- return sym->end;
+ return RC_CHK_ACCESS(sym)->end;
}
static inline void symbol__set_end(struct symbol *sym, u64 end)
{
- sym->end = end;
+ RC_CHK_ACCESS(sym)->end = end;
}
static inline const char *symbol__name(const struct symbol *sym)
{
- return sym->name;
+ return RC_CHK_ACCESS(sym)->name;
}
static inline u16 symbol__namelen(const struct symbol *sym)
{
- return sym->namelen;
+ return RC_CHK_ACCESS(sym)->namelen;
}
static inline u8 symbol__arch_sym(const struct symbol *sym)
{
- return sym->arch_sym;
+ return RC_CHK_ACCESS(sym)->arch_sym;
}
static inline void symbol__set_arch_sym(struct symbol *sym, u8 arch_sym)
{
- sym->arch_sym = arch_sym;
+ RC_CHK_ACCESS(sym)->arch_sym = arch_sym;
}
static inline u8 symbol__type(const struct symbol *sym)
{
- return (atomic_load_explicit(&sym->flags, memory_order_relaxed) &
+ return (atomic_load_explicit(&RC_CHK_ACCESS(sym)->flags, memory_order_relaxed) &
SYMBOL_FLAG_TYPE_MASK) >> SYMBOL_FLAG_TYPE_SHIFT;
}
static inline u8 symbol__binding(const struct symbol *sym)
{
- return (atomic_load_explicit(&sym->flags, memory_order_relaxed) &
+ return (atomic_load_explicit(&RC_CHK_ACCESS(sym)->flags, memory_order_relaxed) &
SYMBOL_FLAG_BINDING_MASK) >> SYMBOL_FLAG_BINDING_SHIFT;
}
static inline bool symbol__ignore(const struct symbol *sym)
{
- return (atomic_load_explicit(&sym->flags, memory_order_relaxed) &
+ return (atomic_load_explicit(&RC_CHK_ACCESS(sym)->flags, memory_order_relaxed) &
SYMBOL_FLAG_IGNORE) != 0;
}
static inline bool symbol__inlined(const struct symbol *sym)
{
- return (atomic_load_explicit(&sym->flags, memory_order_relaxed) &
+ return (atomic_load_explicit(&RC_CHK_ACCESS(sym)->flags, memory_order_relaxed) &
SYMBOL_FLAG_INLINED) != 0;
}
static inline bool symbol__is_annotate2(const struct symbol *sym)
{
- return (atomic_load_explicit(&sym->flags, memory_order_relaxed) &
+ return (atomic_load_explicit(&RC_CHK_ACCESS(sym)->flags, memory_order_relaxed) &
SYMBOL_FLAG_ANNOTATE2) != 0;
}
static inline bool symbol__ifunc_alias(const struct symbol *sym)
{
- return (atomic_load_explicit(&sym->flags, memory_order_relaxed) &
+ return (atomic_load_explicit(&RC_CHK_ACCESS(sym)->flags, memory_order_relaxed) &
SYMBOL_FLAG_IFUNC_ALIAS) != 0;
}
static inline bool symbol__annotated(const struct symbol *sym)
{
- return (atomic_load_explicit(&sym->flags, memory_order_relaxed) &
+ return (atomic_load_explicit(&RC_CHK_ACCESS(sym)->flags, memory_order_relaxed) &
SYMBOL_FLAG_ANNOTATED) != 0;
}
diff --git a/tools/perf/util/thread-stack.c b/tools/perf/util/thread-stack.c
index 76867ba83365..e2c73e890158 100644
--- a/tools/perf/util/thread-stack.c
+++ b/tools/perf/util/thread-stack.c
@@ -823,7 +823,7 @@ static int thread_stack__pop_cp(struct thread *thread, struct thread_stack *ts,
if (ts->cnt == 1) {
struct thread_stack_entry *tse = &ts->stack[0];
- if (tse->cp->sym == sym)
+ if (RC_CHK_EQUAL(tse->cp->sym, sym))
return thread_stack__call_return(thread, ts, --ts->cnt,
timestamp, ref, false);
}
@@ -941,7 +941,7 @@ static int thread_stack__no_call_return(struct thread *thread,
else
parent = root;
- if (parent->sym == from_al->sym) {
+ if (RC_CHK_EQUAL(parent->sym, from_al->sym)) {
/*
* At the bottom of the stack, assume the missing 'call' was
* before the trace started. So, pop the current symbol and push
@@ -1069,7 +1069,7 @@ static int thread_stack__x86_retpoline(struct thread_stack *ts,
*/
ts->cnt -= 1;
sym = ts->stack[ts->cnt - 2].cp->sym;
- if (sym && sym == tsym && to_al->addr != symbol__start(tsym)) {
+ if (sym && RC_CHK_EQUAL(sym, tsym) && to_al->addr != symbol__start(tsym)) {
/*
* Target is back to the middle of the symbol we came
* from so assume it is an indirect jmp and forget it
@@ -1078,7 +1078,7 @@ static int thread_stack__x86_retpoline(struct thread_stack *ts,
ts->cnt -= 1;
return 0;
}
- } else if (sym && sym == tsym) {
+ } else if (sym && RC_CHK_EQUAL(sym, tsym)) {
/*
* Target is back to the symbol we came from so assume it is an
* indirect jmp and forget it altogether.
@@ -1169,7 +1169,7 @@ int thread_stack__process(struct thread *thread, struct comm *comm,
* may be the start of a x86 retpoline.
*/
if (!err && rstate == X86_RETPOLINE_POSSIBLE && to_al->sym &&
- from_al->sym == to_al->sym &&
+ RC_CHK_EQUAL(from_al->sym, to_al->sym) &&
to_al->addr != symbol__start(to_al->sym))
ts->rstate = X86_RETPOLINE_DETECTED;
@@ -1206,7 +1206,7 @@ int thread_stack__process(struct thread *thread, struct comm *comm,
} else if (sample->flags & PERF_IP_FLAG_TRACE_END) {
err = thread_stack__trace_end(ts, sample, ref);
} else if (sample->flags & PERF_IP_FLAG_BRANCH &&
- from_al->sym != to_al->sym && to_al->sym &&
+ !RC_CHK_EQUAL(from_al->sym, to_al->sym) && to_al->sym &&
to_al->addr == symbol__start(to_al->sym)) {
struct call_path_root *cpr = ts->crp->cpr;
struct call_path *cp;
diff --git a/tools/perf/util/threads.c b/tools/perf/util/threads.c
index 6ca0b178fb6c..b3ec954fe7ab 100644
--- a/tools/perf/util/threads.c
+++ b/tools/perf/util/threads.c
@@ -142,10 +142,13 @@ void threads__remove_all_threads(struct threads *threads)
down_write(&table->lock);
__threads_table_entry__set_last_match(table, NULL);
hashmap__for_each_entry_safe(&table->shard, cur, tmp, bkt) {
- struct thread *old_value;
+ struct thread *old_value = NULL;
- hashmap__delete(&table->shard, cur->key, /*old_key=*/NULL, &old_value);
- thread__put(old_value);
+ if (hashmap__delete(&table->shard, cur->key,
+ /*old_key=*/NULL, &old_value)) {
+ thread__free_stitch_list(old_value);
+ thread__put(old_value);
+ }
}
up_write(&table->lock);
}
@@ -154,14 +157,16 @@ void threads__remove_all_threads(struct threads *threads)
void threads__remove(struct threads *threads, struct thread *thread)
{
struct threads_table_entry *table = threads__table(threads, thread__tid(thread));
- struct thread *old_value;
+ struct thread *old_value = NULL;
down_write(&table->lock);
if (table->last_match && RC_CHK_EQUAL(table->last_match, thread))
__threads_table_entry__set_last_match(table, NULL);
- hashmap__delete(&table->shard, thread__tid(thread), /*old_key=*/NULL, &old_value);
- thread__put(old_value);
+ if (hashmap__delete(&table->shard, thread__tid(thread), /*old_key=*/NULL, &old_value)) {
+ thread__free_stitch_list(old_value);
+ thread__put(old_value);
+ }
up_write(&table->lock);
}
diff --git a/tools/perf/util/unwind-libdw.c b/tools/perf/util/unwind-libdw.c
index 87ade098b5c6..eb4327aa9e7e 100644
--- a/tools/perf/util/unwind-libdw.c
+++ b/tools/perf/util/unwind-libdw.c
@@ -169,7 +169,7 @@ static int entry(u64 ip, struct unwind_info *ui)
e->ip = ip;
e->ms.thread = thread__get(al.thread);
e->ms.map = map__get(al.map);
- e->ms.sym = al.sym;
+ e->ms.sym = symbol__get(al.sym);
pr_debug("unwind: %s:ip = 0x%" PRIx64 " (0x%" PRIx64 ")\n",
al.sym ? symbol__name(al.sym) : "''",
diff --git a/tools/perf/util/unwind-libunwind.c b/tools/perf/util/unwind-libunwind.c
index 6052383e862b..42872c7b6dcd 100644
--- a/tools/perf/util/unwind-libunwind.c
+++ b/tools/perf/util/unwind-libunwind.c
@@ -632,7 +632,7 @@ static int entry(uint64_t ip, struct thread *thread, unwind_entry_cb_t cb, void
e.ms.thread = thread__get(al.thread);
pr_debug("unwind: %s:ip = 0x%" PRIx64 " (0x%" PRIx64 ")\n",
- al.sym ? al.sym->name : "''",
+ al.sym ? symbol__name(al.sym) : "''",
ip,
al.map ? map__map_ip(al.map, ip) : (u64) 0);
--
2.56.0.rc1.315.gc6ed9934b7-goog
^ permalink raw reply [flat|nested] 36+ messages in thread
* [PATCH v1 5/7] perf symbol: Add LRU memory shrinking for symbols, DSOs, and machines
2026-09-28 7:52 [PATCH v1 0/7] perf symbol: Reference counting, flat array storage, and LRU shrinking Ian Rogers
` (3 preceding siblings ...)
2026-09-28 7:52 ` [PATCH v1 4/7] perf symbol: Add reference counting and DECLARE_RC_STRUCT(symbol) Ian Rogers
@ 2026-09-28 7:52 ` Ian Rogers
2026-09-28 7:52 ` [PATCH v1 6/7] perf session: Periodically shrink symbols and DSOs during event processing Ian Rogers
` (4 subsequent siblings)
9 siblings, 0 replies; 36+ messages in thread
From: Ian Rogers @ 2026-09-28 7:52 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
linux-perf-users, linux-kernel, Alireza Haghdoost
Add Clock/Second-Chance LRU access tracking (SYMBOL_FLAG_ACCESSED on
struct symbol, accessed on struct dso, and shrunk on struct symbols) and
memory-shrinking helpers (symbols__shrink, dso__can_shrink_symbols,
dso__shrink, dsos__shrink, machine__shrink, machines__shrink, and
perf_session__shrink):
- symbols__shrink() scans a symbol table under symbols->lock: symbols
with refcnt > 1 are retained, symbols with SYMBOL_FLAG_ACCESSED set
have their accessed bit cleared (second chance), and unreferenced
symbols with SYMBOL_FLAG_ACCESSED clear are evicted. When symbols are
evicted, symbols->shrunk is set under symbols->lock and the backing
array is compacted when occupancy drops below 50%.
- dso__shrink() closes any cached file descriptor, frees cached 4KB DSO
data pages (__dso_cache__free()) and addr2line/libbfd/libdw state
under dso__lock(dso), and shrinks dso->symbols if the DSO supports
idempotent on-demand reloading (dso__can_shrink_symbols()). Protect
dso_cache lookup and memcpy under dso__lock(dso) so concurrent
shrinking cannot free a cache page while it is being read.
- dsos__shrink() sweeps a DSO collection using Clock/Second-Chance LRU:
accessed DSOs have their symbol tables shrunk and their accessed bit
cleared; unaccessed DSOs are fully shrunk via dso__shrink() and
evicted from struct dsos if unreferenced outside the collection
(refcnt == 1) and free of unreloadable state.
- When a DSO's symbol table has been shrunk (dso__symbols_shrunk(dso) is
true) and a subsequent symbol lookup misses in map__find_symbol() or
map__find_symbol_by_name_idx(), transparently reload the DSO on demand
while preferring actively referenced (refcnt > 1) symbols in
choose_best_symbol() and deduplicating identical (start, name) pairs
in symbols__fixup_duplicate() even when symbol_conf.allow_aliases is
enabled.
Assisted-by: Antigravity:gemini-3.1-pro
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/dso.c | 182 +++++++++++++++++++++++++++--------
tools/perf/util/dso.h | 46 +++++++++
tools/perf/util/dsos.c | 100 +++++++++++++++++++
tools/perf/util/dsos.h | 12 ++-
tools/perf/util/machine.c | 39 ++++++++
tools/perf/util/machine.h | 2 +
tools/perf/util/map.c | 26 ++++-
tools/perf/util/session.c | 15 +++
tools/perf/util/session.h | 3 +
tools/perf/util/symbol-elf.c | 5 +-
tools/perf/util/symbol.c | 142 +++++++++++++++++++++++++--
tools/perf/util/symbol.h | 36 +++++--
12 files changed, 551 insertions(+), 57 deletions(-)
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c
index 1b96853164be..330e43cc325b 100644
--- a/tools/perf/util/dso.c
+++ b/tools/perf/util/dso.c
@@ -877,6 +877,7 @@ bool dso__data_get_fd(struct dso *dso, struct machine *machine, int *fd)
if (dso__data(dso)->status == DSO_DATA_STATUS_ERROR)
return false;
+ dso__set_accessed(dso, true);
mutex_lock(dso__data_open_lock());
try_to_open_dso(dso, machine);
@@ -955,12 +956,11 @@ static int bpf_size(struct dso *dso)
#endif // HAVE_LIBBPF_SUPPORT
static void
-dso_cache__free(struct dso *dso)
+__dso_cache__free(struct dso *dso)
{
struct rb_root *root = &dso__data(dso)->cache;
struct rb_node *next = rb_first(root);
- mutex_lock(dso__lock(dso));
while (next) {
struct dso_cache *cache;
@@ -969,6 +969,13 @@ dso_cache__free(struct dso *dso)
rb_erase(&cache->rb_node, root);
free(cache);
}
+}
+
+static void
+dso_cache__free(struct dso *dso)
+{
+ mutex_lock(dso__lock(dso));
+ __dso_cache__free(dso);
mutex_unlock(dso__lock(dso));
}
@@ -998,7 +1005,7 @@ static struct dso_cache *__dso_cache__find(struct dso *dso, u64 offset)
}
static struct dso_cache *
-dso_cache__insert(struct dso *dso, struct dso_cache *new)
+__dso_cache__insert(struct dso *dso, struct dso_cache *new)
{
struct rb_root *root = &dso__data(dso)->cache;
struct rb_node **p = &root->rb_node;
@@ -1006,7 +1013,6 @@ dso_cache__insert(struct dso *dso, struct dso_cache *new)
struct dso_cache *cache;
u64 offset = new->offset;
- mutex_lock(dso__lock(dso));
while (*p != NULL) {
u64 end;
@@ -1019,16 +1025,13 @@ dso_cache__insert(struct dso *dso, struct dso_cache *new)
else if (offset >= end)
p = &(*p)->rb_right;
else
- goto out;
+ return cache;
}
rb_link_node(&new->rb_node, parent, p);
rb_insert_color(&new->rb_node, root);
- cache = NULL;
-out:
- mutex_unlock(dso__lock(dso));
- return cache;
+ return NULL;
}
static ssize_t dso_cache__memcpy(struct dso_cache *cache, u64 offset, u8 *data,
@@ -1082,68 +1085,69 @@ static ssize_t file_read(struct dso *dso, struct machine *machine,
return ret;
}
-static struct dso_cache *dso_cache__populate(struct dso *dso,
- struct machine *machine,
- u64 offset, ssize_t *ret)
+static ssize_t dso_cache__populate(struct dso *dso, struct machine *machine,
+ u64 offset, u8 *data, ssize_t size, bool out)
{
u64 cache_offset = offset & DSO__DATA_CACHE_MASK;
struct dso_cache *cache;
struct dso_cache *old;
+ ssize_t ret;
cache = zalloc(sizeof(*cache) + DSO__DATA_CACHE_SIZE);
- if (!cache) {
- *ret = -ENOMEM;
- return NULL;
- }
+ if (!cache)
+ return -ENOMEM;
#ifdef HAVE_LIBBPF_SUPPORT
if (dso__binary_type(dso) == DSO_BINARY_TYPE__BPF_PROG_INFO)
- *ret = bpf_read(dso, cache_offset, cache->data);
+ ret = bpf_read(dso, cache_offset, cache->data);
else
#endif
if (dso__binary_type(dso) == DSO_BINARY_TYPE__OOL)
- *ret = DSO__DATA_CACHE_SIZE;
+ ret = DSO__DATA_CACHE_SIZE;
else
- *ret = file_read(dso, machine, cache_offset, cache->data);
+ ret = file_read(dso, machine, cache_offset, cache->data);
- if (*ret <= 0) {
+ if (ret <= 0) {
free(cache);
- return NULL;
+ return ret;
}
cache->offset = cache_offset;
- cache->size = *ret;
+ cache->size = ret;
- old = dso_cache__insert(dso, cache);
+ mutex_lock(dso__lock(dso));
+ old = __dso_cache__insert(dso, cache);
if (old) {
/* we lose the race */
free(cache);
cache = old;
}
+ ret = dso_cache__memcpy(cache, offset, data, size, out);
+ mutex_unlock(dso__lock(dso));
- return cache;
-}
-
-static struct dso_cache *dso_cache__find(struct dso *dso,
- struct machine *machine,
- u64 offset,
- ssize_t *ret)
-{
- struct dso_cache *cache = __dso_cache__find(dso, offset);
-
- return cache ? cache : dso_cache__populate(dso, machine, offset, ret);
+ return ret;
}
+/*
+ * Look up and copy cached DSO data while holding dso__lock(dso) so a
+ * concurrent dso__shrink() / __dso_cache__free() cannot free the cache node
+ * while it is being read.
+ */
static ssize_t dso_cache_io(struct dso *dso, struct machine *machine,
u64 offset, u8 *data, ssize_t size, bool out)
{
struct dso_cache *cache;
- ssize_t ret = 0;
+ ssize_t ret;
- cache = dso_cache__find(dso, machine, offset, &ret);
- if (!cache)
+ mutex_lock(dso__lock(dso));
+ cache = __dso_cache__find(dso, offset);
+ if (cache) {
+ ret = dso_cache__memcpy(cache, offset, data, size, out);
+ mutex_unlock(dso__lock(dso));
return ret;
+ }
+ mutex_unlock(dso__lock(dso));
- return dso_cache__memcpy(cache, offset, data, size, out);
+ return dso_cache__populate(dso, machine, offset, data, size, out);
}
/*
@@ -1157,6 +1161,7 @@ static ssize_t cached_io(struct dso *dso, struct machine *machine,
ssize_t r = 0;
u8 *p = data;
+ dso__set_accessed(dso, true);
do {
ssize_t ret;
@@ -1723,6 +1728,7 @@ struct dso *dso__new_id(const char *name, const struct dso_id *id)
dso->is_kmod = 0;
dso->needs_swap = DSO_SWAP__UNSET;
dso->comp = COMP_ID__NONE;
+ atomic_init(&dso->accessed, false);
mutex_init(&dso->lock);
refcount_set(&dso->refcnt, 1);
data = &dso->data;
@@ -1776,6 +1782,106 @@ void dso__delete(struct dso *dso)
RC_CHK_FREE(dso);
}
+/**
+ * dso__can_shrink_symbols - Check if a DSO's symbol table can be shrunk and
+ * reloaded on demand.
+ * @dso: DSO to check.
+ *
+ * Shrinking evicts unreferenced symbols from @dso->symbols and relies on
+ * dso__load() being able to reload the missing symbols idempotently if a future
+ * lookup misses in map__find_symbol(). Not all DSO types support idempotent
+ * reloading:
+ *
+ * - User-space ELF binaries, debuginfo files, build-id cache files, and
+ * perf-<pid>.map JIT files (dso__kernel(dso) == DSO_SPACE__USER), as well as
+ * standalone kernel module ELF files (dso__is_kmod(dso)), load their symbols
+ * directly into @dso->symbols from a backing file on disk and can be safely
+ * reloaded on demand.
+ *
+ * - Main kernel and guest kernel DSOs (vmlinux / kallsyms where
+ * dso__kernel(dso) != DSO_SPACE__USER && !dso__is_kmod(dso)) split their
+ * loaded symbols across multiple per-section or per-module maps and DSOs in
+ * machine->kmaps (via maps__split_kallsyms()). Re-running dso__load() on one
+ * of the split kernel DSOs would either fail to find a backing file or
+ * re-split and duplicate kernel maps, so they are excluded.
+ *
+ * - KCORE / GUEST_KCORE DSOs replace and remap kernel maps in machine->kmaps
+ * when loaded from /proc/kcore, so they cannot be reloaded in place.
+ *
+ * - BPF_PROG_INFO, BPF_IMAGE, and OOL DSOs have their symbols synthesized in
+ * memory from PERF_RECORD_KSYMBOL / PERF_RECORD_BPF_EVENT meta-events rather
+ * than loaded from disk by dso__load(), so evicting their symbols would lose
+ * them permanently.
+ *
+ * Return: True if @dso->symbols can be safely shrunk and reloaded on demand.
+ */
+bool dso__can_shrink_symbols(const struct dso *dso)
+{
+ switch (dso__symtab_type(dso)) {
+ case DSO_BINARY_TYPE__KALLSYMS:
+ case DSO_BINARY_TYPE__GUEST_KALLSYMS:
+ case DSO_BINARY_TYPE__VMLINUX:
+ case DSO_BINARY_TYPE__GUEST_VMLINUX:
+ case DSO_BINARY_TYPE__JAVA_JIT:
+ case DSO_BINARY_TYPE__DEBUGLINK:
+ case DSO_BINARY_TYPE__BUILD_ID_CACHE:
+ case DSO_BINARY_TYPE__BUILD_ID_CACHE_DEBUGINFO:
+ case DSO_BINARY_TYPE__FEDORA_DEBUGINFO:
+ case DSO_BINARY_TYPE__UBUNTU_DEBUGINFO:
+ case DSO_BINARY_TYPE__MIXEDUP_UBUNTU_DEBUGINFO:
+ case DSO_BINARY_TYPE__BUILDID_DEBUGINFO:
+ case DSO_BINARY_TYPE__SYSTEM_PATH_DSO:
+ case DSO_BINARY_TYPE__GUEST_KMODULE:
+ case DSO_BINARY_TYPE__GUEST_KMODULE_COMP:
+ case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE:
+ case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP:
+ case DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO:
+ case DSO_BINARY_TYPE__GNU_DEBUGDATA:
+ return dso__kernel(dso) == DSO_SPACE__USER || dso__is_kmod(dso);
+ case DSO_BINARY_TYPE__KCORE:
+ case DSO_BINARY_TYPE__GUEST_KCORE:
+ case DSO_BINARY_TYPE__BPF_PROG_INFO:
+ case DSO_BINARY_TYPE__BPF_IMAGE:
+ case DSO_BINARY_TYPE__OOL:
+ case DSO_BINARY_TYPE__NOT_FOUND:
+ default:
+ return false;
+ }
+}
+
+/**
+ * dso__shrink - Reclaim cached file data, debuginfo, and unreferenced symbols
+ * from a DSO.
+ * @dso: DSO to shrink.
+ *
+ * Closes any cached file descriptor, frees the 4KB page cache (@dso->data.cache),
+ * releases cached addr2line / libbfd / libdw handles, and shrinks @dso->symbols
+ * if supported by dso__can_shrink_symbols().
+ *
+ * Return: Number of symbols evicted from @dso->symbols.
+ */
+size_t dso__shrink(struct dso *dso)
+{
+ size_t removed = 0;
+
+ if (!dso)
+ return 0;
+
+ dso__data_close(dso);
+
+ mutex_lock(dso__lock(dso));
+ __dso_cache__free(dso);
+ dso__free_a2l(dso);
+ dso__free_a2l_libbfd(dso);
+ dso__free_libdw(dso);
+
+ if (dso__can_shrink_symbols(dso))
+ removed = symbols__shrink(dso__symbols(dso));
+ mutex_unlock(dso__lock(dso));
+
+ return removed;
+}
+
struct dso *dso__get(struct dso *dso)
{
struct dso *result;
diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h
index 1de446966f01..3f27cb1a76df 100644
--- a/tools/perf/util/dso.h
+++ b/tools/perf/util/dso.h
@@ -285,13 +285,24 @@ struct dso_bpf_prog {
struct auxtrace_cache;
+/**
+ * struct dso - Dynamic Shared Object (executable, shared library, kernel, or
+ * module) metadata, symbol table, and cached file state.
+ */
DECLARE_RC_STRUCT(dso) {
+ /** @lock: Mutex serializing symbol loading and DSO state updates. */
struct mutex lock;
+ /** @dsos: Owning collection of DSOs, or NULL if not in a struct dsos. */
struct dsos *dsos;
+ /** @symbols: Sorted array of symbols belonging to this DSO. */
struct symbols symbols;
+ /** @inlined_nodes: Cached DWARF inline call-tree nodes for addresses. */
struct rb_root_cached inlined_nodes;
+ /** @srclines: Cached address-to-source-line mappings. */
struct rb_root_cached srclines;
+ /** @data_types: Cached DWARF annotated data type definitions. */
struct rb_root data_types;
+ /** @global_vars: Cached DWARF global variable locations. */
struct rb_root global_vars;
u64 text_offset;
@@ -319,6 +330,7 @@ DECLARE_RC_STRUCT(dso) {
struct dso_id id;
unsigned int a2l_fails;
int comp;
+ /** @refcnt: Reference count tracking active holders of the DSO. */
refcount_t refcnt;
enum dso_load_errno load_errno;
u16 long_name_len;
@@ -338,6 +350,12 @@ DECLARE_RC_STRUCT(dso) {
u8 short_name_allocated:1;
u8 long_name_allocated:1;
u8 is_64_bit:1;
+ /**
+ * @accessed: Clock/Second-Chance LRU flag set when the DSO or its
+ * symbols/data are looked up or read, and cleared by dsos__shrink().
+ */
+ _Atomic bool accessed;
+ /** @loaded: True once dso__load() has attempted to load symbols. */
bool loaded;
u8 rel;
char name[];
@@ -589,9 +607,30 @@ static inline enum dso_load_errno *dso__load_errno(struct dso *dso)
return &RC_CHK_ACCESS(dso)->load_errno;
}
+static inline bool dso__accessed(const struct dso *dso)
+{
+ return atomic_load_explicit(&RC_CHK_ACCESS(dso)->accessed, memory_order_relaxed);
+}
+
+static inline void dso__set_accessed(struct dso *dso, bool val)
+{
+ atomic_store_explicit(&RC_CHK_ACCESS(dso)->accessed, val, memory_order_relaxed);
+}
+
+static inline bool dso__symbols_shrunk(const struct dso *dso)
+{
+ return atomic_load_explicit(&RC_CHK_ACCESS(dso)->symbols.shrunk, memory_order_acquire);
+}
+
+static inline void dso__set_symbols_shrunk(struct dso *dso, bool val)
+{
+ atomic_store_explicit(&RC_CHK_ACCESS(dso)->symbols.shrunk, val, memory_order_release);
+}
+
static inline void dso__set_loaded(struct dso *dso)
{
RC_CHK_ACCESS(dso)->loaded = true;
+ dso__set_symbols_shrunk(dso, false);
}
static inline struct mutex *dso__lock(struct dso *dso)
@@ -661,6 +700,11 @@ static inline void dso__set_rel(struct dso *dso, u8 rel)
RC_CHK_ACCESS(dso)->rel = rel;
}
+static inline refcount_t *dso__refcnt(struct dso *dso)
+{
+ return &RC_CHK_ACCESS(dso)->refcnt;
+}
+
static inline const char *dso__short_name(const struct dso *dso)
{
return RC_CHK_ACCESS(dso)->short_name;
@@ -756,6 +800,8 @@ static inline void dso__set_text_offset(struct dso *dso, u64 val)
struct dso *dso__new_id(const char *name, const struct dso_id *id);
struct dso *dso__new(const char *name);
void dso__delete(struct dso *dso);
+bool dso__can_shrink_symbols(const struct dso *dso);
+size_t dso__shrink(struct dso *dso);
int dso__cmp_id(struct dso *a, struct dso *b);
void dso__set_short_name(struct dso *dso, const char *name, bool name_allocated);
diff --git a/tools/perf/util/dsos.c b/tools/perf/util/dsos.c
index e927e707abac..f64551ec2728 100644
--- a/tools/perf/util/dsos.c
+++ b/tools/perf/util/dsos.c
@@ -278,9 +278,13 @@ static struct dso *__dsos__find_id(struct dsos *dsos, const char *name, const st
};
__dsos__for_each_dso(dsos, dsos__find_id_cb, &args);
+ if (args.res)
+ dso__set_accessed(args.res, true);
return args.res;
}
res = __dsos__find_by_longname_id(dsos, name, id, write_locked);
+ if (res)
+ dso__set_accessed(res, true);
return res;
}
@@ -324,6 +328,7 @@ static struct dso *__dsos__addnew_id(struct dsos *dsos, const char *name, const
* the array isn't sorted.
*/
dso__set_basename(dso);
+ dso__set_accessed(dso, true);
__dsos__add(dsos, dso);
}
return dso;
@@ -446,6 +451,7 @@ struct dso *dsos__findnew_module_dso(struct dsos *dsos,
dso__set_module_info(dso, m, machine);
dso__set_long_name(dso, strdup(filename), true);
dso__set_kernel(dso, DSO_SPACE__KERNEL);
+ dso__set_accessed(dso, true);
__dsos__add(dsos, dso);
up_write(&dsos->lock);
@@ -493,3 +499,97 @@ int dsos__for_each_dso(struct dsos *dsos, int (*cb)(struct dso *dso, void *data)
up_read(&dsos->lock);
return err;
}
+
+/**
+ * dsos__shrink - Reclaim cold symbols, DSO caches, and unreferenced DSOs.
+ * @dsos: Collection of DSOs to shrink.
+ *
+ * Implements a two-tier Clock/Second-Chance LRU eviction pass:
+ * 1. Snapshot all DSOs under the read lock and release @dsos->lock to respect
+ * the lock ordering (dso__lock() -> dsos->lock).
+ * - If a DSO was accessed since the previous shrink pass (dso__accessed(dso)
+ * is true), its file-level caches (fd, data cache, srclines, inlines,
+ * addr2line/libdw handles) get a second chance, while its unreferenced
+ * cold symbols are still shrunk via symbols__shrink().
+ * - If a DSO was not accessed since the previous shrink pass, dso__shrink()
+ * is called to release its file descriptor, data cache, debuginfo caches,
+ * and unreferenced symbols.
+ * 2. Re-acquire @dsos->lock for writing:
+ * - Clear @dso->accessed on DSOs that were accessed during the interval.
+ * - Evict and release cold user-space DSOs that have no remaining symbols,
+ * no header build-id, and refcount == 1 (referenced only by @dsos->dsos).
+ *
+ * Return: Number of DSOs evicted from @dsos.
+ */
+size_t dsos__shrink(struct dsos *dsos)
+{
+ struct dso **snapshot = NULL;
+ unsigned int cnt = 0, dst = 0;
+ size_t removed = 0;
+
+ if (!dsos)
+ return 0;
+
+ down_read(&dsos->lock);
+ if (dsos->cnt > 0) {
+ snapshot = calloc(dsos->cnt, sizeof(*snapshot));
+ if (snapshot) {
+ cnt = dsos->cnt;
+ for (unsigned int i = 0; i < cnt; i++)
+ snapshot[i] = dso__get(dsos->dsos[i]);
+ }
+ }
+ up_read(&dsos->lock);
+
+ if (!snapshot)
+ return 0;
+
+ for (unsigned int i = 0; i < cnt; i++) {
+ struct dso *dso = snapshot[i];
+
+ if (dso__accessed(dso)) {
+ mutex_lock(dso__lock(dso));
+ if (dso__can_shrink_symbols(dso))
+ symbols__shrink(dso__symbols(dso));
+ mutex_unlock(dso__lock(dso));
+ } else {
+ dso__shrink(dso);
+ }
+ dso__put(dso);
+ }
+ free(snapshot);
+
+ down_write(&dsos->lock);
+ for (unsigned int src = 0; src < dsos->cnt; src++) {
+ struct dso *dso = dsos->dsos[src];
+
+ if (dso__accessed(dso)) {
+ dso__set_accessed(dso, false);
+ dsos->dsos[dst++] = dso;
+ } else if (!dso__has_symbols(dso) &&
+ !dso__header_build_id(dso) &&
+ !dso__kernel(dso) &&
+ refcount_read(dso__refcnt(dso)) == 1) {
+ dso__set_dsos(dso, NULL);
+ dso__put(dso);
+ removed++;
+ } else {
+ dsos->dsos[dst++] = dso;
+ }
+ }
+ dsos->cnt = dst;
+ if (dst == 0) {
+ zfree(&dsos->dsos);
+ dsos->allocated = 0;
+ } else if (dst < dsos->allocated / 2) {
+ struct dso **temp = realloc(dsos->dsos, sizeof(*temp) * dst);
+
+ if (temp) {
+ dsos->dsos = temp;
+ dsos->allocated = dst;
+ }
+ }
+ up_write(&dsos->lock);
+
+ return removed;
+}
diff --git a/tools/perf/util/dsos.h b/tools/perf/util/dsos.h
index a26774950866..5c27f5efb74b 100644
--- a/tools/perf/util/dsos.h
+++ b/tools/perf/util/dsos.h
@@ -13,15 +13,20 @@ struct dso_id;
struct kmod_path;
struct machine;
-/*
- * Collection of DSOs as an array for iteration speed, but sorted for O(n)
- * lookup.
+/**
+ * struct dsos - Collection of DSOs stored as a sorted array for iteration speed
+ * and O(log n) lookup.
*/
struct dsos {
+ /** @lock: Read/write semaphore synchronizing access to @dsos. */
struct rw_semaphore lock;
+ /** @dsos: Dynamically allocated array of struct dso pointers. */
struct dso **dsos;
+ /** @cnt: Number of valid entries in @dsos. */
unsigned int cnt;
+ /** @allocated: Capacity (in entries) of the @dsos allocation. */
unsigned int allocated;
+ /** @sorted: True when @dsos is sorted by long name, dso_id, and short name. */
bool sorted;
};
@@ -48,5 +53,6 @@ struct dso *dsos__findnew_module_dso(struct dsos *dsos, struct machine *machine,
struct dso *dsos__find_kernel_dso(struct dsos *dsos);
int dsos__for_each_dso(struct dsos *dsos, int (*cb)(struct dso *dso, void *data), void *data);
+size_t dsos__shrink(struct dsos *dsos);
#endif /* __PERF_DSOS */
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index f1155c608a95..ba0b982828c2 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -962,6 +962,45 @@ size_t machines__fprintf_dsos_buildid(struct machines *machines, FILE *fp,
return ret;
}
+/**
+ * machine__shrink - Reclaim unreferenced symbols, DSO caches, and cold DSOs
+ * for a machine.
+ * @machine: Machine to shrink.
+ *
+ * Return: Number of DSOs evicted from @machine->dsos.
+ */
+size_t machine__shrink(struct machine *machine)
+{
+ if (!machine)
+ return 0;
+
+ return dsos__shrink(&machine->dsos);
+}
+
+/**
+ * machines__shrink - Reclaim unreferenced symbols, DSO caches, and cold DSOs
+ * across the host and all guest machines.
+ * @machines: Collection of host and guest machines to shrink.
+ *
+ * Return: Total number of DSOs evicted across all machines.
+ */
+size_t machines__shrink(struct machines *machines)
+{
+ struct rb_node *nd;
+ size_t ret;
+
+ if (!machines)
+ return 0;
+
+ ret = machine__shrink(&machines->host);
+ for (nd = rb_first_cached(&machines->guests); nd; nd = rb_next(nd)) {
+ struct machine *pos = rb_entry(nd, struct machine, rb_node);
+
+ ret += machine__shrink(pos);
+ }
+ return ret;
+}
+
struct machine_fprintf_cb_args {
FILE *fp;
size_t printed;
diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h
index 973a59f7c26a..115e60979ec6 100644
--- a/tools/perf/util/machine.h
+++ b/tools/perf/util/machine.h
@@ -268,6 +268,8 @@ size_t machine__fprintf_dsos_buildid(struct machine *machine, FILE *fp,
size_t machines__fprintf_dsos(struct machines *machines, FILE *fp);
size_t machines__fprintf_dsos_buildid(struct machines *machines, FILE *fp,
bool (skip)(struct dso *dso, int parm), int parm);
+size_t machine__shrink(struct machine *machine);
+size_t machines__shrink(struct machines *machines);
void machine__destroy_kernel_maps(struct machine *machine);
int machine__create_kernel_maps(struct machine *machine);
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c
index 19afe676adf2..633989615cf6 100644
--- a/tools/perf/util/map.c
+++ b/tools/perf/util/map.c
@@ -377,15 +377,30 @@ int map__load(struct map *map)
struct symbol *map__find_symbol(struct map *map, u64 addr)
{
+ struct dso *dso;
+ struct symbol *sym;
+
if (map__load(map) < 0)
return NULL;
- return dso__find_symbol(map__dso(map), addr);
+ dso = map__dso(map);
+ sym = dso__find_symbol(dso, addr);
+ /*
+ * If the DSO's symbol table was previously shrunk by symbols__shrink()
+ * and the lookup missed, transparently reload the DSO so evicted
+ * symbols are restored on demand rather than reported as [unknown].
+ */
+ if (!sym && dso__symbols_shrunk(dso)) {
+ if (dso__load(dso, map) >= 0)
+ sym = dso__find_symbol(dso, addr);
+ }
+ return sym;
}
struct symbol *map__find_symbol_by_name_idx(struct map *map, const char *name, size_t *idx)
{
struct dso *dso;
+ struct symbol *sym;
if (map__load(map) < 0)
return NULL;
@@ -393,7 +408,14 @@ struct symbol *map__find_symbol_by_name_idx(struct map *map, const char *name, s
dso = map__dso(map);
dso__sort_by_name(dso);
- return dso__find_symbol_by_name(dso, name, idx);
+ sym = dso__find_symbol_by_name(dso, name, idx);
+ if (!sym && dso__symbols_shrunk(dso)) {
+ if (dso__load(dso, map) >= 0) {
+ dso__sort_by_name(dso);
+ sym = dso__find_symbol_by_name(dso, name, idx);
+ }
+ }
+ return sym;
}
struct symbol *map__find_symbol_by_name(struct map *map, const char *name)
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
index 7fea9e72726c..555d940a866a 100644
--- a/tools/perf/util/session.c
+++ b/tools/perf/util/session.c
@@ -3933,6 +3933,21 @@ size_t perf_session__fprintf_dsos_buildid(struct perf_session *session, FILE *fp
return machines__fprintf_dsos_buildid(&session->machines, fp, skip, parm);
}
+/**
+ * perf_session__shrink - Reclaim unreferenced symbols, DSO caches, and cold
+ * DSOs across all machines in the session.
+ * @session: Session to shrink.
+ *
+ * Return: Number of DSOs evicted across the session's machines.
+ */
+size_t perf_session__shrink(struct perf_session *session)
+{
+ if (!session)
+ return 0;
+
+ return machines__shrink(&session->machines);
+}
+
size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp)
{
size_t ret;
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h
index ac5803d5fb4e..4b223c2cee7f 100644
--- a/tools/perf/util/session.h
+++ b/tools/perf/util/session.h
@@ -91,7 +91,9 @@ struct perf_session {
u64 bytes_compressed;
/** @zstd_data: Owner of global compression state, buffers, etc. */
struct zstd_data zstd_data;
+ /** @decomp_data: Decompression state for the session data file. */
struct decomp_data decomp_data;
+ /** @active_decomp: Currently active decompression context. */
struct decomp_data *active_decomp;
};
@@ -175,6 +177,7 @@ size_t perf_session__fprintf_dsos(struct perf_session *session, FILE *fp);
size_t perf_session__fprintf_dsos_buildid(struct perf_session *session, FILE *fp,
bool (fn)(struct dso *dso, int parm), int parm);
+size_t perf_session__shrink(struct perf_session *session);
size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp);
diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c
index 90542fb643e9..00d088b43f94 100644
--- a/tools/perf/util/symbol-elf.c
+++ b/tools/perf/util/symbol-elf.c
@@ -1826,9 +1826,10 @@ int dso__load_sym(struct dso *dso, struct map *map, struct symsrc *syms_ss,
/*
* Modules may already have symbols from kallsyms, but those symbols
- * have the wrong values for the dso maps, so remove them.
+ * have the wrong values for the dso maps, so remove them unless we are
+ * reloading a previously shrunk module.
*/
- if (kmodule && syms_ss->symtab)
+ if (kmodule && syms_ss->symtab && !dso__symbols_shrunk(dso))
symbols__delete(dso__symbols(dso));
if (!syms_ss->symtab) {
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 593fccfe3240..33cfd86156d2 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -164,6 +164,14 @@ static int choose_best_symbol(struct symbol *syma, struct symbol *symb)
s64 b;
size_t na, nb;
+ /* Prefer a symbol that is actively referenced */
+ a = refcount_read(symbol__refcnt(syma)) > 1;
+ b = refcount_read(symbol__refcnt(symb)) > 1;
+ if (a && !b)
+ return SYMBOL_A;
+ if (b && !a)
+ return SYMBOL_B;
+
/* Prefer a symbol with non zero length */
a = symbol__end(syma) - symbol__start(syma);
b = symbol__end(symb) - symbol__start(symb);
@@ -224,6 +232,7 @@ void symbols__init(struct symbols *symbols)
symbols->num_by_name = 0;
symbols->sorted = true;
symbols->sorted_by_name = false;
+ atomic_init(&symbols->shrunk, false);
}
static void symbols__delete_locked(struct symbols *symbols)
@@ -238,6 +247,7 @@ static void symbols__delete_locked(struct symbols *symbols)
symbols->num_by_name = 0;
symbols->sorted = true;
symbols->sorted_by_name = false;
+ atomic_store_explicit(&symbols->shrunk, false, memory_order_release);
}
void symbols__delete(struct symbols *symbols)
@@ -253,6 +263,68 @@ void symbols__exit(struct symbols *symbols)
exit_rwsem(&symbols->lock);
}
+/**
+ * symbols__shrink - Evict unreferenced, unaccessed symbols using Clock LRU.
+ * @symbols: Symbol table to shrink.
+ *
+ * Scans @symbols in place:
+ * - Symbols with refcount > 1 (held by an external owner such as a hist_entry,
+ * callchain_cursor_node, or inline_list) are always retained.
+ * - Symbols with refcount == 1 that have SYMBOL_FLAG_ACCESSED set are given a
+ * second chance: their accessed bit is cleared and they remain in @symbols.
+ * - Symbols with refcount == 1 and SYMBOL_FLAG_ACCESSED clear are evicted and
+ * released via symbol__put().
+ *
+ * If any symbols are evicted, the secondary @symbols_by_name index is freed so
+ * it will be rebuilt lazily if needed, and the backing @symbols array is
+ * compacted when occupancy drops below 50%.
+ *
+ * Return: Number of symbols evicted and freed.
+ */
+size_t symbols__shrink(struct symbols *symbols)
+{
+ unsigned int write_idx = 0;
+ size_t freed = 0;
+
+ if (!symbols)
+ return 0;
+
+ down_write(&symbols->lock);
+ for (unsigned int read_idx = 0; read_idx < symbols->cnt; read_idx++) {
+ struct symbol *sym = symbols->symbols[read_idx];
+
+ if (refcount_read(symbol__refcnt(sym)) > 1 || symbol__accessed(sym)) {
+ symbol__set_accessed(sym, false);
+ symbols->symbols[write_idx++] = sym;
+ } else {
+ symbol__put(sym);
+ freed++;
+ }
+ }
+ if (freed > 0) {
+ symbols->cnt = write_idx;
+ zfree(&symbols->symbols_by_name);
+ symbols->num_by_name = 0;
+ symbols->sorted_by_name = false;
+ atomic_store_explicit(&symbols->shrunk, true, memory_order_release);
+ if (symbols->cnt == 0) {
+ zfree(&symbols->symbols);
+ symbols->allocated = 0;
+ } else if (symbols->cnt < symbols->allocated / 2 && symbols->allocated > 32) {
+ unsigned int new_alloc = max(symbols->cnt, 32U);
+ struct symbol **temp = realloc(symbols->symbols,
+ sizeof(struct symbol *) * new_alloc);
+
+ if (temp) {
+ symbols->symbols = temp;
+ symbols->allocated = new_alloc;
+ }
+ }
+ }
+ up_write(&symbols->lock);
+ return freed;
+}
+
static inline bool symbol__less_or_equal(const struct symbol *a, const struct symbol *b)
{
if (symbol__start(a) != symbol__start(b))
@@ -310,9 +382,6 @@ void symbols__fixup_duplicate(struct symbols *symbols)
{
unsigned int write_idx = 0;
- if (symbol_conf.allow_aliases)
- return;
-
down_write(&symbols->lock);
symbols__sort_locked(symbols);
if (symbols->cnt <= 1) {
@@ -329,6 +398,38 @@ void symbols__fixup_duplicate(struct symbols *symbols)
continue;
}
+ /*
+ * When aliases are allowed, distinct symbol names at the same
+ * address are kept, but identical (start, name) pairs (such as
+ * when reloading a shrunk DSO that still holds referenced
+ * symbols) are deduplicated using choose_best_symbol().
+ */
+ if (symbol_conf.allow_aliases) {
+ bool dup = false;
+
+ for (unsigned int k = write_idx; ; k--) {
+ struct symbol *existing = symbols->symbols[k];
+
+ if (symbol__start(existing) != symbol__start(next))
+ break;
+ if (!strcmp(symbol__name(existing), symbol__name(next))) {
+ if (choose_best_symbol(existing, next) == SYMBOL_A) {
+ symbol__put(next);
+ } else {
+ symbol__put(existing);
+ symbols->symbols[k] = next;
+ }
+ dup = true;
+ break;
+ }
+ if (k == 0)
+ break;
+ }
+ if (!dup)
+ symbols->symbols[++write_idx] = next;
+ continue;
+ }
+
if (choose_best_symbol(curr, next) == SYMBOL_A) {
if (symbol__type(next) == STT_GNU_IFUNC)
symbol__set_ifunc_alias(curr, true);
@@ -502,6 +603,14 @@ void symbol__set_annotated(struct symbol *sym, bool annotated)
atomic_fetch_and(&RC_CHK_ACCESS(sym)->flags, ~SYMBOL_FLAG_ANNOTATED);
}
+void symbol__set_accessed(struct symbol *sym, bool accessed)
+{
+ if (accessed)
+ atomic_fetch_or(&RC_CHK_ACCESS(sym)->flags, SYMBOL_FLAG_ACCESSED);
+ else
+ atomic_fetch_and(&RC_CHK_ACCESS(sym)->flags, ~SYMBOL_FLAG_ACCESSED);
+}
+
static void symbol__set_idle(struct symbol *sym, bool idle)
{
uint16_t old_flags = atomic_load_explicit(&RC_CHK_ACCESS(sym)->flags, memory_order_relaxed);
@@ -576,6 +685,7 @@ static struct symbol *symbols__find(struct symbols *symbols, u64 ip)
low = mid + 1;
else {
res = symbol__get(s);
+ symbol__set_accessed(res, true);
break;
}
}
@@ -700,7 +810,11 @@ static struct symbol *symbols__find_by_name(struct symbol *symbols[],
}
}
assert(!found_idx || !s || s == symbols[*found_idx]);
- return symbol__get(s);
+ if (s) {
+ s = symbol__get(s);
+ symbol__set_accessed(s, true);
+ }
+ return s;
}
int dso__insert_symbol(struct dso *dso, struct symbol *sym)
@@ -732,11 +846,17 @@ void dso__delete_symbol(struct dso *dso, struct symbol *sym)
struct symbol *dso__find_symbol(struct dso *dso, u64 addr)
{
+ if (!dso)
+ return NULL;
+ dso__set_accessed(dso, true);
return symbols__find(dso__symbols(dso), addr);
}
struct symbol *dso__find_symbol_nocache(struct dso *dso, u64 addr)
{
+ if (!dso)
+ return NULL;
+ dso__set_accessed(dso, true);
return symbols__find(dso__symbols(dso), addr);
}
@@ -763,6 +883,9 @@ struct symbol *dso__find_symbol_by_name(struct dso *dso, const char *name, size_
struct symbols *symbols;
struct symbol *s;
+ if (!dso)
+ return NULL;
+ dso__set_accessed(dso, true);
symbols = dso__symbols(dso);
down_read(&symbols->lock);
while (!symbols->sorted_by_name) {
@@ -1857,6 +1980,9 @@ static int dso__load_perf_map(const char *map_path, struct dso *dso)
free(line);
fclose(file);
+ if (nr_syms > 0)
+ symbols__fixup_duplicate(dso__symbols(dso));
+
return nr_syms;
out_delete_line:
@@ -1967,6 +2093,7 @@ int dso__load(struct dso *dso, struct map *map)
char newmapname[PATH_MAX];
const char *map_path = dso__long_name(dso);
+ dso__set_accessed(dso, true);
mutex_lock(dso__lock(dso));
perfmap = is_perf_pid_map_name(map_path);
@@ -1981,7 +2108,7 @@ int dso__load(struct dso *dso, struct map *map)
nsinfo__mountns_enter(dso__nsinfo(dso), &nsc);
/* check again under the dso->lock */
- if (dso__loaded(dso)) {
+ if (dso__loaded(dso) && !dso__symbols_shrunk(dso)) {
ret = 1;
goto out;
}
@@ -2140,8 +2267,10 @@ int dso__load(struct dso *dso, struct map *map)
int nr_plt;
nr_plt = dso__synthesize_plt_symbols(dso, runtime_ss);
- if (nr_plt > 0)
+ if (nr_plt > 0) {
+ symbols__fixup_duplicate(dso__symbols(dso));
ret += nr_plt;
+ }
}
for (; ss_pos > 0; ss_pos--)
@@ -2151,6 +2280,7 @@ int dso__load(struct dso *dso, struct map *map)
if (ret < 0 && strstr(dso__name(dso), " (deleted)") != NULL)
ret = 0;
out:
+ dso__set_symbols_shrunk(dso, false);
dso__set_loaded(dso);
mutex_unlock(dso__lock(dso));
nsinfo__mountns_exit(&nsc);
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index a3c88b5224ec..a76613c43ba9 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -91,21 +91,32 @@ enum symbol_idle_kind {
#define SYMBOL_FLAG_ANNOTATE2 (1 << 12)
#define SYMBOL_FLAG_IFUNC_ALIAS (1 << 13)
#define SYMBOL_FLAG_ANNOTATED (1 << 14)
+#define SYMBOL_FLAG_ACCESSED (1U << 15)
+
/**
- * A symtab entry.
+ * struct symbol - A symtab entry.
*/
DECLARE_RC_STRUCT(symbol) {
- /** Range of symbol [start, end). */
+ /** @start: Start address of symbol range [start, end). */
u64 start;
+ /** @end: End address (exclusive) of symbol range [start, end). */
u64 end;
- /** Reference count. */
+ /** @refcnt: Reference count tracking active holders of the symbol. */
refcount_t refcnt;
- /** Length of the string name. */
+ /** @namelen: Length of the string @name (excluding trailing NUL). */
u16 namelen;
+ /**
+ * @flags: Atomic bitfield of SYMBOL_FLAG_* values encoding symbol type,
+ * binding, idle classification, ignore/inlined/annotate/ifunc flags,
+ * and Clock LRU access state (SYMBOL_FLAG_ACCESSED).
+ */
_Atomic uint16_t flags;
- /** Architecture specific. Unused except on PPC where it holds st_other. */
+ /**
+ * @arch_sym: Architecture specific value. Unused except on PPC where it
+ * holds st_other.
+ */
u8 arch_sym;
- /** The name of length namelen associated with the symbol. */
+ /** @name: NUL-terminated symbol name of length @namelen. */
char name[];
};
@@ -138,6 +149,11 @@ struct symbols {
* by name.
*/
bool sorted_by_name;
+ /**
+ * @shrunk: Set under @lock when symbols__shrink() evicts one or more
+ * symbols so a subsequent lookup miss knows to reload the DSO on demand.
+ */
+ _Atomic bool shrunk;
};
void symbol__delete(struct symbol *sym);
@@ -145,6 +161,7 @@ void symbols__init(struct symbols *symbols);
void symbols__exit(struct symbols *symbols);
void symbols__delete(struct symbols *symbols);
void symbols__sort_read_lock(struct symbols *symbols) SHARED_LOCK_FUNCTION(symbols->lock);
+size_t symbols__shrink(struct symbols *symbols);
static inline refcount_t *symbol__refcnt(struct symbol *sym)
{
@@ -253,6 +270,12 @@ static inline bool symbol__annotated(const struct symbol *sym)
SYMBOL_FLAG_ANNOTATED) != 0;
}
+static inline bool symbol__accessed(const struct symbol *sym)
+{
+ return (atomic_load_explicit(&RC_CHK_ACCESS(sym)->flags, memory_order_relaxed) &
+ SYMBOL_FLAG_ACCESSED) != 0;
+}
+
bool symbol__is_idle(struct symbol *sym, const struct dso *dso, struct perf_env *env);
void symbol__set_ignore(struct symbol *sym, bool ignore);
@@ -260,6 +283,7 @@ void symbol__set_annotate2(struct symbol *sym, bool annotate2);
void symbol__set_inlined(struct symbol *sym, bool inlined);
void symbol__set_ifunc_alias(struct symbol *sym, bool ifunc_alias);
void symbol__set_annotated(struct symbol *sym, bool annotated);
+void symbol__set_accessed(struct symbol *sym, bool accessed);
/*
* symbols__for_each_entry - iterate over symbols calling @cb for each entry
--
2.56.0.rc1.315.gc6ed9934b7-goog
^ permalink raw reply [flat|nested] 36+ messages in thread
* [PATCH v1 6/7] perf session: Periodically shrink symbols and DSOs during event processing
2026-09-28 7:52 [PATCH v1 0/7] perf symbol: Reference counting, flat array storage, and LRU shrinking Ian Rogers
` (4 preceding siblings ...)
2026-09-28 7:52 ` [PATCH v1 5/7] perf symbol: Add LRU memory shrinking for symbols, DSOs, and machines Ian Rogers
@ 2026-09-28 7:52 ` Ian Rogers
2026-09-28 7:52 ` [PATCH v1 7/7] perf test symbols: Add tests for symbol and DSO LRU shrinking Ian Rogers
` (3 subsequent siblings)
9 siblings, 0 replies; 36+ messages in thread
From: Ian Rogers @ 2026-09-28 7:52 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
linux-perf-users, linux-kernel, Alireza Haghdoost
Add periodic calls to perf_session__shrink() so that both live profiling
and trace-processing commands reclaim unused symbol and DSO memory:
- In perf_session__deliver_event(), trigger perf_session__shrink()
every 16,384 events, or after at least 512 events when at least
65,536 new symbols have been inserted across DSOs (tracked via
symbols__inserted_cnt()). This bounds memory usage for session-based
commands such as 'perf script' and 'perf report' even on short traces
that load many large binaries, while giving hot symbols in newly
loaded DSOs a window to be marked SYMBOL_FLAG_ACCESSED.
- In perf_top__resort_hists(), call perf_session__shrink() on each
display refresh cycle so 'perf top' periodically ages out unused
symbols and DSOs.
Assisted-by: Antigravity:gemini-3.1-pro
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/builtin-top.c | 2 ++
tools/perf/util/session.c | 16 ++++++++++++++++
tools/perf/util/session.h | 10 ++++++++++
tools/perf/util/symbol.c | 15 +++++++++++++++
tools/perf/util/symbol.h | 1 +
5 files changed, 44 insertions(+)
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
index dd1e475bb4dd..c96530f33fe4 100644
--- a/tools/perf/builtin-top.c
+++ b/tools/perf/builtin-top.c
@@ -320,6 +320,8 @@ static void perf_top__resort_hists(struct perf_top *t)
evlist__for_each_entry(evlist, pos) {
evsel__output_resort(pos, NULL);
}
+
+ perf_session__shrink(t->session);
}
static void perf_top__print_sym_table(struct perf_top *top)
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
index 555d940a866a..74a564c6589b 100644
--- a/tools/perf/util/session.c
+++ b/tools/perf/util/session.c
@@ -164,6 +164,7 @@ struct perf_session *__perf_session__new(struct perf_data *data,
session->tool = tool;
session->decomp_data.zstd_decomp = &session->zstd_data;
session->active_decomp = &session->decomp_data;
+ session->last_symbols_inserted = symbols__inserted_cnt();
INIT_LIST_HEAD(&session->auxtrace_index);
perf_env__init(&session->header.env);
if (machines__init(&session->machines, session))
@@ -2351,6 +2352,19 @@ static int perf_session__deliver_event(struct perf_session *session,
if (dump_trace && sample.aux_sample.size)
auxtrace__dump_auxtrace_sample(session, &sample);
+
+ /*
+ * Periodically reclaim unreferenced symbols, DSO caches, and cold DSOs
+ * during event processing (e.g., perf script, perf report). Trigger
+ * either every 16,384 events, or after at least 512 events when more
+ * than 65,536 symbols have been newly loaded across DSOs.
+ */
+ session->events_since_shrink++;
+ if (session->events_since_shrink >= 16384 ||
+ (session->events_since_shrink >= 512 &&
+ symbols__inserted_cnt() - session->last_symbols_inserted >= 65536)) {
+ perf_session__shrink(session);
+ }
out:
perf_sample__exit(&sample);
return ret;
@@ -3945,6 +3959,8 @@ size_t perf_session__shrink(struct perf_session *session)
if (!session)
return 0;
+ session->events_since_shrink = 0;
+ session->last_symbols_inserted = symbols__inserted_cnt();
return machines__shrink(&session->machines);
}
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h
index 4b223c2cee7f..030d5e931160 100644
--- a/tools/perf/util/session.h
+++ b/tools/perf/util/session.h
@@ -95,6 +95,16 @@ struct perf_session {
struct decomp_data decomp_data;
/** @active_decomp: Currently active decompression context. */
struct decomp_data *active_decomp;
+ /**
+ * @events_since_shrink: Number of events delivered via
+ * perf_session__deliver_event() since the last perf_session__shrink().
+ */
+ u64 events_since_shrink;
+ /**
+ * @last_symbols_inserted: Snapshot of symbols__inserted_cnt() taken at
+ * the last perf_session__shrink() pass.
+ */
+ unsigned long last_symbols_inserted;
};
struct decomp {
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 33cfd86156d2..57894c3dc5b5 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -623,6 +623,20 @@ static void symbol__set_idle(struct symbol *sym, bool idle)
} while (!atomic_compare_exchange_weak(&RC_CHK_ACCESS(sym)->flags, &old_flags, new_flags));
}
+static atomic_ulong symbols_inserted_cnt;
+
+/**
+ * symbols__inserted_cnt - Return cumulative count of symbols inserted.
+ *
+ * Monotonically increasing counter incremented each time a symbol is inserted
+ * into a struct symbols collection. Used by perf_session__deliver_event() to
+ * detect burst symbol loading across large DSOs and trigger periodic shrinking.
+ */
+unsigned long symbols__inserted_cnt(void)
+{
+ return atomic_load_explicit(&symbols_inserted_cnt, memory_order_relaxed);
+}
+
int __symbols__insert(struct symbols *symbols, struct symbol *sym)
{
if (symbols->cnt == symbols->allocated) {
@@ -644,6 +658,7 @@ int __symbols__insert(struct symbols *symbols, struct symbol *sym)
symbols->symbols[symbols->cnt++] = sym;
symbols->sorted_by_name = false;
+ atomic_fetch_add_explicit(&symbols_inserted_cnt, 1, memory_order_relaxed);
return 0;
}
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index a76613c43ba9..783d9735aa3f 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -162,6 +162,7 @@ void symbols__exit(struct symbols *symbols);
void symbols__delete(struct symbols *symbols);
void symbols__sort_read_lock(struct symbols *symbols) SHARED_LOCK_FUNCTION(symbols->lock);
size_t symbols__shrink(struct symbols *symbols);
+unsigned long symbols__inserted_cnt(void);
static inline refcount_t *symbol__refcnt(struct symbol *sym)
{
--
2.56.0.rc1.315.gc6ed9934b7-goog
^ permalink raw reply [flat|nested] 36+ messages in thread
* [PATCH v1 7/7] perf test symbols: Add tests for symbol and DSO LRU shrinking
2026-09-28 7:52 [PATCH v1 0/7] perf symbol: Reference counting, flat array storage, and LRU shrinking Ian Rogers
` (5 preceding siblings ...)
2026-09-28 7:52 ` [PATCH v1 6/7] perf session: Periodically shrink symbols and DSOs during event processing Ian Rogers
@ 2026-09-28 7:52 ` Ian Rogers
2026-09-28 15:37 ` [PATCH v1 0/7] perf symbol: Reference counting, flat array storage, and " Ian Rogers
` (2 subsequent siblings)
9 siblings, 0 replies; 36+ messages in thread
From: Ian Rogers @ 2026-09-28 7:52 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
linux-perf-users, linux-kernel, Alireza Haghdoost
Add unit tests in tools/perf/tests/symbols.c covering:
- symbols__shrink(): Second-Chance / Clock eviction of unaccessed
symbols, clearing of SYMBOL_FLAG_ACCESSED on survival, and retention
of symbols with external reference counts (refcnt > 1).
- dsos__shrink(): Second-Chance / Clock eviction of cold DSOs from
struct dsos while retaining accessed DSOs and externally referenced
DSOs.
- dso__shrink() and map__find_symbol(): Shrinking a loaded DSO's symbol
table and verifying that a subsequent map__find_symbol() transparently
reloads the symbol table on demand.
Assisted-by: Antigravity:gemini-3.1-pro
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/tests/symbols.c | 228 +++++++++++++++++++++++++++++++++++++
1 file changed, 228 insertions(+)
diff --git a/tools/perf/tests/symbols.c b/tools/perf/tests/symbols.c
index 440d800d9f4d..6113acbaf602 100644
--- a/tools/perf/tests/symbols.c
+++ b/tools/perf/tests/symbols.c
@@ -3,6 +3,8 @@
#include <linux/string.h>
#include <sys/mman.h>
#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
#include "debug.h"
#include "dso.h"
#include "env.h"
@@ -178,6 +180,221 @@ static int process_subdivided_dso(struct machine *machine, struct dso *dso)
return ret < 0 ? TEST_FAIL : TEST_OK;
}
+static int test_symbols_shrink(void)
+{
+ struct symbols symbols;
+ struct symbol *sym1, *sym2, *sym3, *held;
+ int ret = TEST_FAIL;
+
+ symbols__init(&symbols);
+
+ sym1 = symbol__new(0x100, 0x50, STB_GLOBAL, STT_FUNC, "sym1");
+ sym2 = symbol__new(0x200, 0x50, STB_GLOBAL, STT_FUNC, "sym2");
+ sym3 = symbol__new(0x300, 0x50, STB_GLOBAL, STT_FUNC, "sym3");
+ if (!sym1 || !sym2 || !sym3) {
+ symbol__put(sym1);
+ symbol__put(sym2);
+ symbol__put(sym3);
+ goto out;
+ }
+
+ /* Hold an active reference on sym1, and mark sym2 accessed. */
+ held = symbol__get(sym1);
+ symbol__set_accessed(sym2, true);
+
+ symbols__insert(&symbols, sym1);
+ symbols__insert(&symbols, sym2);
+ symbols__insert(&symbols, sym3);
+
+ /*
+ * First shrink: sym3 (unreferenced) is evicted; sym2 gets a second
+ * chance because SYMBOL_FLAG_ACCESSED was set; sym1 has refcnt > 1.
+ */
+ if (symbols__shrink(&symbols) != 1 || symbols.cnt != 2) {
+ pr_debug("First symbols__shrink failed (cnt=%u)!\n", symbols.cnt);
+ symbol__put(held);
+ goto out;
+ }
+
+ /*
+ * Second shrink: sym2 was not accessed since first shrink, so it is now
+ * evicted; sym1 still has refcnt > 1.
+ */
+ if (symbols__shrink(&symbols) != 1 || symbols.cnt != 1) {
+ pr_debug("Second symbols__shrink failed (cnt=%u)!\n", symbols.cnt);
+ symbol__put(held);
+ goto out;
+ }
+
+ symbol__set_accessed(held, false);
+ symbol__put(held);
+ if (symbols__shrink(&symbols) != 1 || symbols.cnt != 0) {
+ pr_debug("Third symbols__shrink failed (cnt=%u)!\n", symbols.cnt);
+ goto out;
+ }
+
+ ret = TEST_OK;
+out:
+ symbols__exit(&symbols);
+ return ret;
+}
+
+static int test_dsos_shrink(void)
+{
+ struct dsos dsos;
+ struct dso *dso1, *dso2;
+ int ret = TEST_FAIL;
+
+ dsos__init(&dsos);
+
+ dso1 = dsos__findnew_id(&dsos, "/tmp/perf_test_dso1", NULL);
+ dso2 = dsos__findnew_id(&dsos, "/tmp/perf_test_dso2", NULL);
+ if (!dso1 || !dso2) {
+ dso__put(dso1);
+ dso__put(dso2);
+ goto out;
+ }
+
+ /* Keep the caller's active reference on dso1; drop caller reference on dso2. */
+ dso__put(dso2);
+
+ /*
+ * First shrink: both DSOs had accessed=true from creation, so neither
+ * is evicted on the first pass (Clock second chance), and accessed is
+ * cleared.
+ */
+ if (dsos__shrink(&dsos) != 0 || dsos.cnt != 2) {
+ pr_debug("First dsos__shrink failed (cnt=%u)!\n", dsos.cnt);
+ dso__put(dso1);
+ goto out;
+ }
+
+ /*
+ * Second shrink: dso2 has refcnt == 1 and accessed == false, so it is
+ * evicted; dso1 has refcnt == 2, so it is kept.
+ */
+ if (dsos__shrink(&dsos) != 1 || dsos.cnt != 1) {
+ pr_debug("Second dsos__shrink failed (cnt=%u)!\n", dsos.cnt);
+ dso__put(dso1);
+ goto out;
+ }
+
+ dso__put(dso1);
+ if (dsos__shrink(&dsos) != 1 || dsos.cnt != 0) {
+ pr_debug("Third dsos__shrink failed (cnt=%u)!\n", dsos.cnt);
+ goto out;
+ }
+
+ ret = TEST_OK;
+out:
+ dsos__exit(&dsos);
+ return ret;
+}
+
+struct shrink_reload_find_args {
+ struct symbol *kept;
+ u64 first_start;
+ u64 second_start;
+ char *second_name;
+};
+
+static int shrink_reload_find_cb(struct symbol *sym, void *data)
+{
+ struct shrink_reload_find_args *args = data;
+
+ if (!args->kept) {
+ args->kept = symbol__get(sym);
+ args->first_start = symbol__start(sym);
+ return 0;
+ }
+ if (symbol__start(sym) != args->first_start) {
+ args->second_start = symbol__start(sym);
+ args->second_name = strdup(symbol__name(sym));
+ return args->second_name ? 1 : -ENOMEM;
+ }
+ return 0;
+}
+
+static int test_dso_shrink_and_reload(struct dso *dso, struct map *map)
+{
+ struct shrink_reload_find_args args = {
+ .kept = NULL,
+ .first_start = 0,
+ .second_start = 0,
+ .second_name = NULL,
+ };
+ struct symbol *sym, *reloaded;
+ size_t removed;
+
+ if (!dso__can_shrink_symbols(dso) || dso__kernel(dso))
+ return TEST_OK;
+
+ if (dso__for_each_symbol(dso, shrink_reload_find_cb, &args) < 0) {
+ symbol__put(args.kept);
+ return TEST_FAIL;
+ }
+ if (!args.second_name) {
+ symbol__put(args.kept);
+ return TEST_OK;
+ }
+
+ /* Unreferenced symbols have SYMBOL_FLAG_ACCESSED == false and are evicted immediately. */
+ removed = dso__shrink(dso);
+ if (removed == 0 || !dso__symbols_shrunk(dso)) {
+ pr_debug("dso__shrink did not evict any symbols!\n");
+ free(args.second_name);
+ symbol__put(args.kept);
+ return TEST_FAIL;
+ }
+
+ /* Kept symbol is still found without reloading. */
+ sym = dso__find_symbol(dso, args.first_start);
+ if (!RC_CHK_EQUAL(sym, args.kept)) {
+ pr_debug("Kept symbol not found after dso__shrink!\n");
+ symbol__put(sym);
+ free(args.second_name);
+ symbol__put(args.kept);
+ return TEST_FAIL;
+ }
+ symbol__put(sym);
+
+ /* Evicted symbol is no longer in dso->symbols before reload. */
+ sym = dso__find_symbol(dso, args.second_start);
+ if (sym != NULL) {
+ pr_debug("Evicted symbol still present after dso__shrink!\n");
+ symbol__put(sym);
+ free(args.second_name);
+ symbol__put(args.kept);
+ return TEST_FAIL;
+ }
+
+ /* Lookup via map__find_symbol transparently reloads the DSO. */
+ reloaded = map__find_symbol(map, args.second_start);
+ if (!reloaded || strcmp(symbol__name(reloaded), args.second_name) != 0) {
+ pr_debug("Failed to reload evicted symbol %s!\n", args.second_name);
+ symbol__put(reloaded);
+ free(args.second_name);
+ symbol__put(args.kept);
+ return TEST_FAIL;
+ }
+ symbol__put(reloaded);
+
+ /* Kept symbol pointer was preserved across reload deduplication. */
+ sym = dso__find_symbol(dso, args.first_start);
+ if (!RC_CHK_EQUAL(sym, args.kept)) {
+ pr_debug("Kept symbol pointer was not preserved across reload!\n");
+ symbol__put(sym);
+ free(args.second_name);
+ symbol__put(args.kept);
+ return TEST_FAIL;
+ }
+ symbol__put(sym);
+
+ free(args.second_name);
+ symbol__put(args.kept);
+ return TEST_OK;
+}
+
static int test_file(struct test_info *ti, char *filename)
{
struct map *map = NULL;
@@ -209,6 +426,9 @@ static int test_file(struct test_info *ti, char *filename)
/* Module dso is split into many dsos by section */
if (ret == TEST_OK && dso__kernel(dso) != DSO_SPACE__USER)
ret = process_subdivided_dso(ti->machine, dso);
+
+ if (ret == TEST_OK)
+ ret = test_dso_shrink_and_reload(dso, map);
out_put:
map__put(map);
@@ -221,6 +441,14 @@ static int test__symbols(struct test_suite *test __maybe_unused, int subtest __m
struct test_info ti;
int ret;
+ ret = test_symbols_shrink();
+ if (ret != TEST_OK)
+ return ret;
+
+ ret = test_dsos_shrink();
+ if (ret != TEST_OK)
+ return ret;
+
ret = init_test_info(&ti);
if (ret != TEST_OK)
return ret;
--
2.56.0.rc1.315.gc6ed9934b7-goog
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH v1 0/7] perf symbol: Reference counting, flat array storage, and LRU shrinking
2026-09-28 7:52 [PATCH v1 0/7] perf symbol: Reference counting, flat array storage, and LRU shrinking Ian Rogers
` (6 preceding siblings ...)
2026-09-28 7:52 ` [PATCH v1 7/7] perf test symbols: Add tests for symbol and DSO LRU shrinking Ian Rogers
@ 2026-09-28 15:37 ` Ian Rogers
2026-09-28 19:45 ` Alireza Haghdoost
2026-10-27 23:14 ` [PATCH v2 00/18] perf symbol: Reference counting, sorted arrays, and memory shrinking Ian Rogers
9 siblings, 0 replies; 36+ messages in thread
From: Ian Rogers @ 2026-09-28 15:37 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
linux-perf-users, linux-kernel, Alireza Haghdoost
On Mon, Sep 28, 2026 at 12:52 AM Ian Rogers <irogers@google.com> wrote:
>
> This series modernizes struct symbol memory management and backing storage in
> tools/perf to support reference counting (DECLARE_RC_STRUCT(symbol)) and
> periodic LRU memory shrinking during long or large profiling sessions:
>
> 1. Field accessors (Patch 1): Introduces inline getters/setters for
> struct symbol fields across tools/perf in preparation for wrapping
> struct symbol with DECLARE_RC_STRUCT(symbol).
> 2. Remove negative-offset private allocations (Patch 2): Replaces
> symbol_conf.priv_size and symbol__priv() negative-offset allocations in
> util/annotate.c and util/db-export.c with hashmaps keyed by symbol
> pointer (struct annotated_symbol protected by an rw_semaphore, and
> sym_db_ids on struct db_export).
> 3. Flat sorted array backing storage (Patch 3): Replaces the dual
> address-sorted and name-sorted red-black trees (rb_node) in struct dso
> with a struct symbols container backed by dynamically sorted flat arrays
> protected by struct rw_semaphore (util/rwsem.h) with Clang thread-safety
> annotations. Saves 48 bytes per struct symbol (two rb_nodes) and provides
> cache-friendly binary search (bsearch) and callback-based iteration.
> 4. Reference counting and DECLARE_RC_STRUCT(symbol) (Patch 4): Wraps
> struct symbol with DECLARE_RC_STRUCT(symbol) and adds refcount_t refcnt
> along with symbol__get(), symbol__put(), and symbol__zput(). Lookup
> functions return a counted reference acquired under symbols->lock, and
> lifetime holders (addr_location, map_symbol, hist_entry, call_path,
> block_info, disasm_line, thread_stack, db_export, etc.) properly
> manage symbol references.
> 5. LRU memory shrinking (Patch 5): Adds an accessed bit to struct dso
> and implements symbols__shrink(), dso__shrink_symbols(), dsos__shrink(),
> machine__shrink(), and machines__shrink(). Unreferenced symbols (refcnt == 1)
> in cold DSOs are reclaimed, and shrunken DSOs transparently reload on demand
> via dso__load() while preserving live symbols and their RC_STRUCT(symbol)
> identity.
> 6. Periodic session shrinking (Patch 6): Hooks machines__shrink() into
> perf_session event processing so long-running sessions periodically reclaim
> cold symbols and DSOs.
> 7. Unit tests (Patch 7): Adds unit tests in tools/perf/tests/symbols.c
> covering symbol reference counting, symbols__shrink() retention of active
> symbols, dsos__shrink() two-pass LRU clock behavior, and on-demand reload
> preserving live symbol identity.
This needs a rebase to after the changes to the python integration,
hence no Sashiko reviews. I'll do that asap.
Thanks,
Ian
> Ian Rogers (7):
> perf symbol: Add accessor functions for struct symbol fields
> perf symbol: Remove symbol_conf.priv_size and negative-offset
> allocations
> perf symbol: Switch backing storage from rbtree to struct symbols
> array
> perf symbol: Add reference counting and DECLARE_RC_STRUCT(symbol)
> perf symbol: Add LRU memory shrinking for symbols, DSOs, and machines
> perf session: Periodically shrink symbols and DSOs during event
> processing
> perf test symbols: Add tests for symbol and DSO LRU shrinking
>
> .../arch/powerpc/util/skip-callchain-idx.c | 2 +-
> tools/perf/arch/powerpc/util/sym-handling.c | 40 +-
> tools/perf/builtin-annotate.c | 15 +-
> tools/perf/builtin-c2c.c | 14 -
> tools/perf/builtin-diff.c | 5 +-
> tools/perf/builtin-kallsyms.c | 10 +-
> tools/perf/builtin-kmem.c | 66 +-
> tools/perf/builtin-kwork.c | 4 +-
> tools/perf/builtin-lock.c | 27 +-
> tools/perf/builtin-mem.c | 2 +-
> tools/perf/builtin-report.c | 14 -
> tools/perf/builtin-sched.c | 6 +-
> tools/perf/builtin-script.c | 18 +-
> tools/perf/builtin-timechart.c | 2 +-
> tools/perf/builtin-top.c | 29 +-
> tools/perf/builtin-trace.c | 12 +-
> tools/perf/tests/builtin-test.c | 1 -
> tools/perf/tests/dlfilter-test.c | 12 +-
> tools/perf/tests/dwarf-unwind.c | 2 +-
> tools/perf/tests/hists_common.c | 4 +-
> tools/perf/tests/hists_cumulate.c | 8 +-
> tools/perf/tests/hists_filter.c | 10 +-
> tools/perf/tests/hists_link.c | 16 +-
> tools/perf/tests/hists_output.c | 9 +-
> tools/perf/tests/kallsyms-split.c | 5 +-
> tools/perf/tests/pe-file-parsing.c | 1 +
> tools/perf/tests/symbols.c | 289 +++++-
> tools/perf/tests/vmlinux-kallsyms.c | 191 ++--
> tools/perf/ui/browsers/annotate.c | 11 +-
> tools/perf/ui/browsers/hists.c | 17 +-
> tools/perf/ui/browsers/map.c | 87 +-
> tools/perf/ui/gtk/annotate.c | 7 +-
> tools/perf/ui/stdio/hist.c | 6 +-
> tools/perf/util/addr_location.c | 4 +
> .../perf/util/annotate-arch/annotate-alpha.c | 2 +-
> .../util/annotate-arch/annotate-loongarch.c | 8 +-
> tools/perf/util/annotate-arch/annotate-s390.c | 2 +-
> tools/perf/util/annotate-arch/annotate-x86.c | 14 +-
> tools/perf/util/annotate-data.c | 14 +-
> tools/perf/util/annotate.c | 211 ++++-
> tools/perf/util/annotate.h | 6 +-
> tools/perf/util/auxtrace.c | 111 ++-
> tools/perf/util/block-info.c | 13 +-
> tools/perf/util/bpf_lock_contention.c | 39 +-
> tools/perf/util/c2c-function.c | 25 +-
> tools/perf/util/call-path.c | 15 +-
> tools/perf/util/callchain.c | 19 +-
> tools/perf/util/capstone.c | 21 +-
> tools/perf/util/data-convert-json.c | 4 +-
> tools/perf/util/db-export.c | 76 +-
> tools/perf/util/db-export.h | 6 +
> tools/perf/util/debug.c | 3 +-
> tools/perf/util/disasm.c | 57 +-
> tools/perf/util/dlfilter.c | 12 +-
> tools/perf/util/dso.c | 236 +++--
> tools/perf/util/dso.h | 85 +-
> tools/perf/util/dsos.c | 100 +++
> tools/perf/util/dsos.h | 12 +-
> tools/perf/util/event.c | 12 +-
> tools/perf/util/evsel_fprintf.c | 2 +-
> tools/perf/util/hist.c | 30 +-
> tools/perf/util/intel-pt.c | 74 +-
> tools/perf/util/libbfd.c | 5 +-
> tools/perf/util/libdw.c | 5 +-
> tools/perf/util/llvm.c | 6 +-
> tools/perf/util/lock-contention.c | 8 +-
> tools/perf/util/machine.c | 143 ++-
> tools/perf/util/machine.h | 2 +
> tools/perf/util/map.c | 53 +-
> tools/perf/util/map.h | 18 +-
> tools/perf/util/map_symbol.c | 4 +-
> tools/perf/util/maps.c | 3 +-
> tools/perf/util/probe-event.c | 135 +--
> tools/perf/util/python.c | 20 +-
> .../util/scripting-engines/trace-event-perl.c | 8 +-
> .../scripting-engines/trace-event-python.c | 38 +-
> tools/perf/util/session.c | 31 +
> tools/perf/util/session.h | 13 +
> tools/perf/util/sort.c | 44 +-
> tools/perf/util/srcline.c | 19 +-
> tools/perf/util/symbol-elf.c | 22 +-
> tools/perf/util/symbol.c | 845 ++++++++++++------
> tools/perf/util/symbol.h | 203 ++++-
> tools/perf/util/symbol_conf.h | 1 -
> tools/perf/util/symbol_fprintf.c | 14 +-
> tools/perf/util/thread-stack.c | 18 +-
> tools/perf/util/threads.c | 17 +-
> tools/perf/util/unwind-libdw.c | 4 +-
> tools/perf/util/unwind-libunwind.c | 2 +-
> 89 files changed, 2644 insertions(+), 1192 deletions(-)
>
> --
> 2.56.0.rc1.315.gc6ed9934b7-goog
>
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH v1 0/7] perf symbol: Reference counting, flat array storage, and LRU shrinking
2026-09-28 7:52 [PATCH v1 0/7] perf symbol: Reference counting, flat array storage, and LRU shrinking Ian Rogers
` (7 preceding siblings ...)
2026-09-28 15:37 ` [PATCH v1 0/7] perf symbol: Reference counting, flat array storage, and " Ian Rogers
@ 2026-09-28 19:45 ` Alireza Haghdoost
2026-09-28 20:55 ` Ian Rogers
2026-10-27 23:14 ` [PATCH v2 00/18] perf symbol: Reference counting, sorted arrays, and memory shrinking Ian Rogers
9 siblings, 1 reply; 36+ messages in thread
From: Alireza Haghdoost @ 2026-09-28 19:45 UTC (permalink / raw)
To: Ian Rogers
Cc: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Jiri Olsa, Adrian Hunter, James Clark,
linux-perf-users, linux-kernel
On Mon, Sep 28, 2026 at 12:52 AM Ian Rogers <irogers@google.com> wrote:
> annotations. Saves 48 bytes per struct symbol (two rb_nodes) and provides
> cache-friendly binary search (bsearch) and callback-based iteration.
Hi Ian,
Thanks for posting this and for Cc'ing me. I built it on edd8a9fe2eca0,
the same base as the v3 series I sent last week, and compared it with
v3 in eager mode (today's behavior) and with --lazy-load-symbols.
All runs use perf script --no-inline --max-stack 127
-F comm,tid,time,ip,sym,dso, with one warm-up and three runs; the
median is shown below. Peak anon is the maximum RssAnon from
/proc/PID/status, sampled every 10 ms.
1) Production: 120 s cgroup profile of a storage service on an 80-CPU
host (perf record -a -g -F 99 -G <cgroup>). 54k samples, 11 DSOs,
5% of lines [unknown].
time max RSS peak anon
v3 eager 4.5 s 386 MiB 314 MiB
v3 lazy 3.7 s 151 MiB 80 MiB
this series 95.9 s 309 MiB 275 MiB
2) A sample fixture from my v3 cover letter, a worst case with 73% of
lines [unknown]:
time max RSS
v3 eager 3.2 s 334 MiB
v3 lazy 1.9 s 106 MiB
this series 205.3 s 449 MiB
Most of the time goes to reloading. On the fixture, a profile shows it
under map__find_symbol() -> dso__load() -> dso__load_sym(). After each
shrink, the first miss in a shrunk DSO re-parses its whole symtab, and
addresses that resolve to [unknown] miss every time.
The peak is still set by dso__load(), since the whole symtab is
materialized before a shrink can run. That is the main blocker for us
running perf script at scale: we can't bound how much memory it will
use, so we have to contain it with memory.max, and then the profiling
job fails with an OOM kill instead of degrading.
For continuous profiling, a degraded profile is still useful. We build
our view statistically, aggregating many profiles across hosts and
time, so some frames reported as [unknown] in the DSOs that hit the cap
only add a little noise, and the cap reports when it was hit. A lost
profile is worse: the OOM kill removes all its samples and wastes the CPU
and memory already spent. A bounded, reported degradation, like perf record
--max-size, is the trade-off I'd recommend.
The bound also matters before a profile starts. perf runs next to
production workloads, so we have to plan its memory up front: pick a
host with enough headroom and size the job's reservation, without
pushing those sensitive workloads into memory reclaim. Without a bound
we can't do that.
The output of your series matches current eager loading except for a few
frames, and there I think your series is right. For example, a sample inside
nft_do_chain [nf_tables] is printed as dca_sysfs_exit [dca].
symbols__fixup_end() extends the last symbol of a module to
roundup(end + 4096, 4096), which overlaps the next module when it starts
in the next page. The rb-tree lookup can then return the stretched symbol,
while your bsearch picks the right one. This comes from bacefe0c7b77b;
I can send a separate fix that clamps the end to the next symbol's start.
A few questions:
- Which workloads did you have in mind? I'd expect the shrinking to
help most with perf top and long perf report sessions, which I
haven't measured. I'm happy to run those on the same hosts if you
have a setup you'd like me to use.
- Would you consider a finer-grained reload? The index in my patch 5/6
keeps a 24-byte entry per symbol, so a miss after a shrink could
materialize one symbol instead of re-parsing the symtab. The pieces
could fit together: your container and refcounting, the index for
reload, and a byte cap for the peak. If the maintainers prefer your
container first, I'm happy to rebase my v4 onto struct symbols.
- The cover letter says patch 3 saves 48 bytes per symbol (two
rb_nodes). At edd8a9fe2eca0, struct symbol has one rb_node, and the
name index is already a struct symbol ** array on the DSO. Am I
missing a second node?
Thanks,
Alireza
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH v1 0/7] perf symbol: Reference counting, flat array storage, and LRU shrinking
2026-09-28 19:45 ` Alireza Haghdoost
@ 2026-09-28 20:55 ` Ian Rogers
2026-09-28 21:51 ` Namhyung Kim
0 siblings, 1 reply; 36+ messages in thread
From: Ian Rogers @ 2026-09-28 20:55 UTC (permalink / raw)
To: Alireza Haghdoost
Cc: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Jiri Olsa, Adrian Hunter, James Clark,
linux-perf-users, linux-kernel
On Mon, Sep 28, 2026 at 12:45 PM Alireza Haghdoost <haghdoost@uber.com> wrote:
>
> On Mon, Sep 28, 2026 at 12:52 AM Ian Rogers <irogers@google.com> wrote:
> > annotations. Saves 48 bytes per struct symbol (two rb_nodes) and provides
> > cache-friendly binary search (bsearch) and callback-based iteration.
>
> Hi Ian,
>
> Thanks for posting this and for Cc'ing me. I built it on edd8a9fe2eca0,
> the same base as the v3 series I sent last week, and compared it with
> v3 in eager mode (today's behavior) and with --lazy-load-symbols.
>
> All runs use perf script --no-inline --max-stack 127
> -F comm,tid,time,ip,sym,dso, with one warm-up and three runs; the
> median is shown below. Peak anon is the maximum RssAnon from
> /proc/PID/status, sampled every 10 ms.
>
> 1) Production: 120 s cgroup profile of a storage service on an 80-CPU
> host (perf record -a -g -F 99 -G <cgroup>). 54k samples, 11 DSOs,
> 5% of lines [unknown].
>
> time max RSS peak anon
> v3 eager 4.5 s 386 MiB 314 MiB
> v3 lazy 3.7 s 151 MiB 80 MiB
> this series 95.9 s 309 MiB 275 MiB
>
> 2) A sample fixture from my v3 cover letter, a worst case with 73% of
> lines [unknown]:
>
> time max RSS
> v3 eager 3.2 s 334 MiB
> v3 lazy 1.9 s 106 MiB
> this series 205.3 s 449 MiB
>
> Most of the time goes to reloading. On the fixture, a profile shows it
> under map__find_symbol() -> dso__load() -> dso__load_sym(). After each
> shrink, the first miss in a shrunk DSO re-parses its whole symtab, and
> addresses that resolve to [unknown] miss every time.
>
> The peak is still set by dso__load(), since the whole symtab is
> materialized before a shrink can run. That is the main blocker for us
> running perf script at scale: we can't bound how much memory it will
> use, so we have to contain it with memory.max, and then the profiling
> job fails with an OOM kill instead of degrading.
Hi Alireza, and thanks for the feedback! I'll see what can be done
about dso__load in v2.
> For continuous profiling, a degraded profile is still useful. We build
> our view statistically, aggregating many profiles across hosts and
> time, so some frames reported as [unknown] in the DSOs that hit the cap
> only add a little noise, and the cap reports when it was hit. A lost
> profile is worse: the OOM kill removes all its samples and wastes the CPU
> and memory already spent. A bounded, reported degradation, like perf record
> --max-size, is the trade-off I'd recommend.
I don't disagree. Fwiw, a C++ memory allocation failure inside my
company's code base is a runtime abort. In perf, many code paths
return -ENOMEM or NULL. I wanted to see how far we could get with a
"shrunk" set of data inside perf as even if we fix memory allocation
around symbols, other code paths can still fail.
> The bound also matters before a profile starts. perf runs next to
> production workloads, so we have to plan its memory up front: pick a
> host with enough headroom and size the job's reservation, without
> pushing those sensitive workloads into memory reclaim. Without a bound
> we can't do that.
I'm still not massively sold on having explicit bounds. It reminds me
of old and often seemingly arbitrary numbers like the number of rows
or columns in a spreadsheet. That said, perf has a similar problem
with exhausting file descriptors. As long as the default is
"unlimited," I'm okay with some attempts to limit size, but we should
implement obvious garbage collection as I hope this series introduces.
> The output of your series matches current eager loading except for a few
> frames, and there I think your series is right. For example, a sample inside
> nft_do_chain [nf_tables] is printed as dca_sysfs_exit [dca].
> symbols__fixup_end() extends the last symbol of a module to
> roundup(end + 4096, 4096), which overlaps the next module when it starts
> in the next page. The rb-tree lookup can then return the stretched symbol,
> while your bsearch picks the right one. This comes from bacefe0c7b77b;
> I can send a separate fix that clamps the end to the next symbol's start.
That's surprising as no behavioral change was intended. That then
leads to the question of which symbol is right and whether a latent
bug is being fixed or a new one introduced. Oh what fun :-)
> A few questions:
>
> - Which workloads did you have in mind? I'd expect the shrinking to
> help most with perf top and long perf report sessions, which I
> haven't measured. I'm happy to run those on the same hosts if you
> have a setup you'd like me to use.
So most of my experience with excessive memory usage has come from
perf top, which seems to leak memory and consume all your RAM before
long. That is expected since no DSO is currently unloaded. I'm hoping
the shrinking process can be generic.
> - Would you consider a finer-grained reload? The index in my patch 5/6
> keeps a 24-byte entry per symbol, so a miss after a shrink could
> materialize one symbol instead of re-parsing the symtab. The pieces
> could fit together: your container and refcounting, the index for
> reload, and a byte cap for the peak. If the maintainers prefer your
> container first, I'm happy to rebase my v4 onto struct symbols.
Definitely. I was more focused on the reference counting aspect.
Things like the policy around shrinking can be amended as needed, I
just wanted to establish a policy so the patches made sense. I think
lazier loading, shrinking, .. all make sense for things to be faster
and use less RAM.
> - The cover letter says patch 3 saves 48 bytes per symbol (two
> rb_nodes). At edd8a9fe2eca0, struct symbol has one rb_node, and the
> name index is already a struct symbol ** array on the DSO. Am I
> missing a second node?
No, I'll fix this mistake.
Thanks,
Ian
> Thanks,
> Alireza
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH v1 0/7] perf symbol: Reference counting, flat array storage, and LRU shrinking
2026-09-28 20:55 ` Ian Rogers
@ 2026-09-28 21:51 ` Namhyung Kim
2026-09-28 22:04 ` Ian Rogers
0 siblings, 1 reply; 36+ messages in thread
From: Namhyung Kim @ 2026-09-28 21:51 UTC (permalink / raw)
To: Ian Rogers
Cc: Alireza Haghdoost, Peter Zijlstra, Ingo Molnar,
Arnaldo Carvalho de Melo, Jiri Olsa, Adrian Hunter, James Clark,
linux-perf-users, linux-kernel
Hello,
On Mon, Sep 28, 2026 at 01:55:26PM -0700, Ian Rogers wrote:
> On Mon, Sep 28, 2026 at 12:45 PM Alireza Haghdoost <haghdoost@uber.com> wrote:
> >
> > On Mon, Sep 28, 2026 at 12:52 AM Ian Rogers <irogers@google.com> wrote:
> > > annotations. Saves 48 bytes per struct symbol (two rb_nodes) and provides
> > > cache-friendly binary search (bsearch) and callback-based iteration.
> >
> > Hi Ian,
> >
> > Thanks for posting this and for Cc'ing me. I built it on edd8a9fe2eca0,
> > the same base as the v3 series I sent last week, and compared it with
> > v3 in eager mode (today's behavior) and with --lazy-load-symbols.
> >
> > All runs use perf script --no-inline --max-stack 127
> > -F comm,tid,time,ip,sym,dso, with one warm-up and three runs; the
> > median is shown below. Peak anon is the maximum RssAnon from
> > /proc/PID/status, sampled every 10 ms.
> >
> > 1) Production: 120 s cgroup profile of a storage service on an 80-CPU
> > host (perf record -a -g -F 99 -G <cgroup>). 54k samples, 11 DSOs,
> > 5% of lines [unknown].
> >
> > time max RSS peak anon
> > v3 eager 4.5 s 386 MiB 314 MiB
> > v3 lazy 3.7 s 151 MiB 80 MiB
> > this series 95.9 s 309 MiB 275 MiB
> >
> > 2) A sample fixture from my v3 cover letter, a worst case with 73% of
> > lines [unknown]:
> >
> > time max RSS
> > v3 eager 3.2 s 334 MiB
> > v3 lazy 1.9 s 106 MiB
> > this series 205.3 s 449 MiB
> >
> > Most of the time goes to reloading. On the fixture, a profile shows it
> > under map__find_symbol() -> dso__load() -> dso__load_sym(). After each
> > shrink, the first miss in a shrunk DSO re-parses its whole symtab, and
> > addresses that resolve to [unknown] miss every time.
> >
> > The peak is still set by dso__load(), since the whole symtab is
> > materialized before a shrink can run. That is the main blocker for us
> > running perf script at scale: we can't bound how much memory it will
> > use, so we have to contain it with memory.max, and then the profiling
> > job fails with an OOM kill instead of degrading.
>
> Hi Alireza, and thanks for the feedback! I'll see what can be done
> about dso__load in v2.
Thanks for the patches and the analysis.
I think many of this memory overhead come from the symbol string.
Probably we don't use most of them. Then would it be nice if we can
lazy-load the strings? Then it'd have an union of a pointer and a file
offset for symbol strings. The find-by-name API is used rarely for
user DSOs and the kernel symbols should be fully loaded anyway.
Without the string (and the priv part), now it has a fixed size so it
should be saved in an array directly. Probably we can add a limit there
and print it with offset when it doesn't have the name.
I'm a bit skeptical about the refcount approach here. It may be hard to
determine when it reclaims memory. I guess the lazy-load strings with
an array would give similar savings like Alireza's work.
Thanks,
Namhyung
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH v1 0/7] perf symbol: Reference counting, flat array storage, and LRU shrinking
2026-09-28 21:51 ` Namhyung Kim
@ 2026-09-28 22:04 ` Ian Rogers
2026-09-29 0:12 ` Alireza Haghdoost
2026-09-29 18:06 ` Namhyung Kim
0 siblings, 2 replies; 36+ messages in thread
From: Ian Rogers @ 2026-09-28 22:04 UTC (permalink / raw)
To: Namhyung Kim
Cc: Alireza Haghdoost, Peter Zijlstra, Ingo Molnar,
Arnaldo Carvalho de Melo, Jiri Olsa, Adrian Hunter, James Clark,
linux-perf-users, linux-kernel
On Mon, Sep 28, 2026 at 2:51 PM Namhyung Kim <namhyung@kernel.org> wrote:
>
> Hello,
>
> On Mon, Sep 28, 2026 at 01:55:26PM -0700, Ian Rogers wrote:
> > On Mon, Sep 28, 2026 at 12:45 PM Alireza Haghdoost <haghdoost@uber.com> wrote:
> > >
> > > On Mon, Sep 28, 2026 at 12:52 AM Ian Rogers <irogers@google.com> wrote:
> > > > annotations. Saves 48 bytes per struct symbol (two rb_nodes) and provides
> > > > cache-friendly binary search (bsearch) and callback-based iteration.
> > >
> > > Hi Ian,
> > >
> > > Thanks for posting this and for Cc'ing me. I built it on edd8a9fe2eca0,
> > > the same base as the v3 series I sent last week, and compared it with
> > > v3 in eager mode (today's behavior) and with --lazy-load-symbols.
> > >
> > > All runs use perf script --no-inline --max-stack 127
> > > -F comm,tid,time,ip,sym,dso, with one warm-up and three runs; the
> > > median is shown below. Peak anon is the maximum RssAnon from
> > > /proc/PID/status, sampled every 10 ms.
> > >
> > > 1) Production: 120 s cgroup profile of a storage service on an 80-CPU
> > > host (perf record -a -g -F 99 -G <cgroup>). 54k samples, 11 DSOs,
> > > 5% of lines [unknown].
> > >
> > > time max RSS peak anon
> > > v3 eager 4.5 s 386 MiB 314 MiB
> > > v3 lazy 3.7 s 151 MiB 80 MiB
> > > this series 95.9 s 309 MiB 275 MiB
> > >
> > > 2) A sample fixture from my v3 cover letter, a worst case with 73% of
> > > lines [unknown]:
> > >
> > > time max RSS
> > > v3 eager 3.2 s 334 MiB
> > > v3 lazy 1.9 s 106 MiB
> > > this series 205.3 s 449 MiB
> > >
> > > Most of the time goes to reloading. On the fixture, a profile shows it
> > > under map__find_symbol() -> dso__load() -> dso__load_sym(). After each
> > > shrink, the first miss in a shrunk DSO re-parses its whole symtab, and
> > > addresses that resolve to [unknown] miss every time.
> > >
> > > The peak is still set by dso__load(), since the whole symtab is
> > > materialized before a shrink can run. That is the main blocker for us
> > > running perf script at scale: we can't bound how much memory it will
> > > use, so we have to contain it with memory.max, and then the profiling
> > > job fails with an OOM kill instead of degrading.
> >
> > Hi Alireza, and thanks for the feedback! I'll see what can be done
> > about dso__load in v2.
>
> Thanks for the patches and the analysis.
>
> I think many of this memory overhead come from the symbol string.
> Probably we don't use most of them. Then would it be nice if we can
> lazy-load the strings? Then it'd have an union of a pointer and a file
> offset for symbol strings. The find-by-name API is used rarely for
> user DSOs and the kernel symbols should be fully loaded anyway.
>
> Without the string (and the priv part), now it has a fixed size so it
> should be saved in an array directly. Probably we can add a limit there
> and print it with offset when it doesn't have the name.
>
> I'm a bit skeptical about the refcount approach here. It may be hard to
> determine when it reclaims memory. I guess the lazy-load strings with
> an array would give similar savings like Alireza's work.
Thanks Namhyung. Without reference counting we lack a good way to
discard symbols; we must either assume they always exist as in the
current code or implement reference counting. I don't see a way around
it. Alireza's patches introduced an "unknown" state for when a symbol
limit was hit, but I think that'd be frustrating in practice. We could
use it after trying to shrink memory use, in my opinion.
Fwiw, I never like switching the rbtree to an array. The issue is that
the rbtree has references that really should be managed by a reference
count. Doing that is a challenge in the current code and a sorted
array offers similar performance while simplifying the reference
counting.
We load all symbols instead of lazily because we need the end of a
symbol, which we can only determine by processing the entire symbol
table as the end of one symbol is the start of the next. We generally
translate one address in a DSO into a symbol, so loading all symbols
is overkill. I think we can do better by loading only symbols within a
certain address window rather than loading all symbols in a DSO. We
can then expand this window as more symbols are needed. This at least
bounds the number of symbols but isn't quite lazy loading.
Alireza also made good points about how symbols not being found or
being shrunk leads to thrashing patterns. I think we can fix this by
maintaining extra state in the DSO. I'm working to add this into v2.
Thanks,
Ian
> Thanks,
> Namhyung
>
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH v1 0/7] perf symbol: Reference counting, flat array storage, and LRU shrinking
2026-09-28 22:04 ` Ian Rogers
@ 2026-09-29 0:12 ` Alireza Haghdoost
2026-09-29 18:09 ` Namhyung Kim
2026-09-29 18:06 ` Namhyung Kim
1 sibling, 1 reply; 36+ messages in thread
From: Alireza Haghdoost @ 2026-09-29 0:12 UTC (permalink / raw)
To: Ian Rogers
Cc: Namhyung Kim, Peter Zijlstra, Ingo Molnar,
Arnaldo Carvalho de Melo, Jiri Olsa, Adrian Hunter, James Clark,
linux-perf-users, linux-kernel
On Mon, Sep 28, 2026 at 3:05 PM Ian Rogers <irogers@google.com> wrote:
> > I think many of this memory overhead come from the symbol string.
> > Probably we don't use most of them. Then would it be nice if we can
> > lazy-load the strings?
Namhyung, your guess is right. I counted live symbol allocations in eager mode
on the sample fixture. At the peak, 986,688 symbols take 258 MiB:
names (demangled, avg 210 bytes) 197 MiB 77%
struct symbol (48 bytes each, 45 MiB 18%
24 of them the rb_node)
malloc overhead 15 MiB 6%
That is most of the 335 MiB max RSS. With --lazy-load-symbols, only
2,928 symbols are created (0.5 MiB), with identical output, and max
RSS is 108 MiB.
> > Without the string (and the priv part), now it has a fixed size so it
> > should be saved in an array directly.
That is close to what patch 5/6 of my v3 does. Its index is a sorted
array of fixed-size entries:
struct sym_idx {
u64 start;
u64 end;
u32 name_off;
u8 binding;
u8 type;
u8 flags;
};
That is 24 bytes per symbol. The name is read from the string table
and demangled only when a sample lands in the symbol. The difference
from your description is that v3 then creates a regular struct symbol
for the hit and inserts it in the rb-tree, because the rest of perf
holds struct symbol pointers. If the entry itself were the symbol,
with the name as a pointer/offset union, that extra step would go
away. Ian's patch 1 would help: once every name access goes through
symbol__name(), that accessor is the only place that has to resolve
an offset.
> > Probably we can add a limit there
> > and print it with offset when it doesn't have the name.
Agreed, that is better than [unknown]. The symbol boundaries are still
known, so the frame can be printed as an offset and resolved offline.
I can do the same for the byte cap in v4.
>
> We load all symbols instead of lazily because we need the end of a
> symbol, which we can only determine by processing the entire symbol
> table as the end of one symbol is the start of the next.
>
Ian, I think most symbols don't need the next one for their end. ELF
symbols have st_size, so the end is start + st_size. Only zero-size
symbols take the next symbol's start. The whole table still has to be
scanned to find the symbol for an address, because .symtab isn't
sorted by address, so an address window would scan it again each time
it grows. The v3 index scans it once, sorts the entries and fixes up
the ends there, but keeps only the 24-byte entries, not the names.
On which kernel symbol is right in the nf_tables example: the sample
is at 0xffffffffc0c4b280, and /proc/kallsyms has nft_do_chain at
0xffffffffc0c4b0b0 and the next nf_tables symbol at
0xffffffffc0c4b4e0, so your series is right. The last dca symbol
starts at 0xffffffffc0c4aa50, and symbols__fixup_end() extends it to
0xffffffffc0c4c000, over the first page of nf_tables. I've sent the
fix separately:
https://lore.kernel.org/all/20260928-haghdoost-perf-symbols-fixup-module-end-v1-1-0a70d1edd401@uber.com/
Thanks
Alireza
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH v1 0/7] perf symbol: Reference counting, flat array storage, and LRU shrinking
2026-09-28 22:04 ` Ian Rogers
2026-09-29 0:12 ` Alireza Haghdoost
@ 2026-09-29 18:06 ` Namhyung Kim
1 sibling, 0 replies; 36+ messages in thread
From: Namhyung Kim @ 2026-09-29 18:06 UTC (permalink / raw)
To: Ian Rogers
Cc: Alireza Haghdoost, Peter Zijlstra, Ingo Molnar,
Arnaldo Carvalho de Melo, Jiri Olsa, Adrian Hunter, James Clark,
linux-perf-users, linux-kernel
On Mon, Sep 28, 2026 at 03:04:59PM -0700, Ian Rogers wrote:
> On Mon, Sep 28, 2026 at 2:51 PM Namhyung Kim <namhyung@kernel.org> wrote:
> >
> > Hello,
> >
> > On Mon, Sep 28, 2026 at 01:55:26PM -0700, Ian Rogers wrote:
> > > On Mon, Sep 28, 2026 at 12:45 PM Alireza Haghdoost <haghdoost@uber.com> wrote:
> > > >
> > > > On Mon, Sep 28, 2026 at 12:52 AM Ian Rogers <irogers@google.com> wrote:
> > > > > annotations. Saves 48 bytes per struct symbol (two rb_nodes) and provides
> > > > > cache-friendly binary search (bsearch) and callback-based iteration.
> > > >
> > > > Hi Ian,
> > > >
> > > > Thanks for posting this and for Cc'ing me. I built it on edd8a9fe2eca0,
> > > > the same base as the v3 series I sent last week, and compared it with
> > > > v3 in eager mode (today's behavior) and with --lazy-load-symbols.
> > > >
> > > > All runs use perf script --no-inline --max-stack 127
> > > > -F comm,tid,time,ip,sym,dso, with one warm-up and three runs; the
> > > > median is shown below. Peak anon is the maximum RssAnon from
> > > > /proc/PID/status, sampled every 10 ms.
> > > >
> > > > 1) Production: 120 s cgroup profile of a storage service on an 80-CPU
> > > > host (perf record -a -g -F 99 -G <cgroup>). 54k samples, 11 DSOs,
> > > > 5% of lines [unknown].
> > > >
> > > > time max RSS peak anon
> > > > v3 eager 4.5 s 386 MiB 314 MiB
> > > > v3 lazy 3.7 s 151 MiB 80 MiB
> > > > this series 95.9 s 309 MiB 275 MiB
> > > >
> > > > 2) A sample fixture from my v3 cover letter, a worst case with 73% of
> > > > lines [unknown]:
> > > >
> > > > time max RSS
> > > > v3 eager 3.2 s 334 MiB
> > > > v3 lazy 1.9 s 106 MiB
> > > > this series 205.3 s 449 MiB
> > > >
> > > > Most of the time goes to reloading. On the fixture, a profile shows it
> > > > under map__find_symbol() -> dso__load() -> dso__load_sym(). After each
> > > > shrink, the first miss in a shrunk DSO re-parses its whole symtab, and
> > > > addresses that resolve to [unknown] miss every time.
> > > >
> > > > The peak is still set by dso__load(), since the whole symtab is
> > > > materialized before a shrink can run. That is the main blocker for us
> > > > running perf script at scale: we can't bound how much memory it will
> > > > use, so we have to contain it with memory.max, and then the profiling
> > > > job fails with an OOM kill instead of degrading.
> > >
> > > Hi Alireza, and thanks for the feedback! I'll see what can be done
> > > about dso__load in v2.
> >
> > Thanks for the patches and the analysis.
> >
> > I think many of this memory overhead come from the symbol string.
> > Probably we don't use most of them. Then would it be nice if we can
> > lazy-load the strings? Then it'd have an union of a pointer and a file
> > offset for symbol strings. The find-by-name API is used rarely for
> > user DSOs and the kernel symbols should be fully loaded anyway.
> >
> > Without the string (and the priv part), now it has a fixed size so it
> > should be saved in an array directly. Probably we can add a limit there
> > and print it with offset when it doesn't have the name.
> >
> > I'm a bit skeptical about the refcount approach here. It may be hard to
> > determine when it reclaims memory. I guess the lazy-load strings with
> > an array would give similar savings like Alireza's work.
>
> Thanks Namhyung. Without reference counting we lack a good way to
> discard symbols; we must either assume they always exist as in the
> current code or implement reference counting. I don't see a way around
> it. Alireza's patches introduced an "unknown" state for when a symbol
> limit was hit, but I think that'd be frustrating in practice. We could
> use it after trying to shrink memory use, in my opinion.
>
> Fwiw, I never like switching the rbtree to an array. The issue is that
> the rbtree has references that really should be managed by a reference
> count. Doing that is a challenge in the current code and a sorted
> array offers similar performance while simplifying the reference
> counting.
Once the symbol becomes small enough, we may not need to shrink memory.
Alireze shows the lazy loading works fine and IIUC it maintains symbol
index which is 24 bytes. Without rbtree we can make it 32 bytes
(also without the name string), then I think it'd be enough.
Thanks,
Namhyung
>
> We load all symbols instead of lazily because we need the end of a
> symbol, which we can only determine by processing the entire symbol
> table as the end of one symbol is the start of the next. We generally
> translate one address in a DSO into a symbol, so loading all symbols
> is overkill. I think we can do better by loading only symbols within a
> certain address window rather than loading all symbols in a DSO. We
> can then expand this window as more symbols are needed. This at least
> bounds the number of symbols but isn't quite lazy loading.
>
> Alireza also made good points about how symbols not being found or
> being shrunk leads to thrashing patterns. I think we can fix this by
> maintaining extra state in the DSO. I'm working to add this into v2.
>
> Thanks,
> Ian
>
> > Thanks,
> > Namhyung
> >
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH v1 0/7] perf symbol: Reference counting, flat array storage, and LRU shrinking
2026-09-29 0:12 ` Alireza Haghdoost
@ 2026-09-29 18:09 ` Namhyung Kim
2026-09-29 21:24 ` Alireza Haghdoost
0 siblings, 1 reply; 36+ messages in thread
From: Namhyung Kim @ 2026-09-29 18:09 UTC (permalink / raw)
To: Alireza Haghdoost
Cc: Ian Rogers, Peter Zijlstra, Ingo Molnar,
Arnaldo Carvalho de Melo, Jiri Olsa, Adrian Hunter, James Clark,
linux-perf-users, linux-kernel
On Mon, Sep 28, 2026 at 05:12:35PM -0700, Alireza Haghdoost wrote:
> On Mon, Sep 28, 2026 at 3:05 PM Ian Rogers <irogers@google.com> wrote:
> > > I think many of this memory overhead come from the symbol string.
> > > Probably we don't use most of them. Then would it be nice if we can
> > > lazy-load the strings?
>
> Namhyung, your guess is right. I counted live symbol allocations in eager mode
> on the sample fixture. At the peak, 986,688 symbols take 258 MiB:
>
> names (demangled, avg 210 bytes) 197 MiB 77%
> struct symbol (48 bytes each, 45 MiB 18%
> 24 of them the rb_node)
> malloc overhead 15 MiB 6%
>
> That is most of the 335 MiB max RSS. With --lazy-load-symbols, only
> 2,928 symbols are created (0.5 MiB), with identical output, and max
> RSS is 108 MiB.
Thanks for checking this!
>
> > > Without the string (and the priv part), now it has a fixed size so it
> > > should be saved in an array directly.
>
> That is close to what patch 5/6 of my v3 does. Its index is a sorted
> array of fixed-size entries:
>
> struct sym_idx {
> u64 start;
> u64 end;
> u32 name_off;
> u8 binding;
> u8 type;
> u8 flags;
> };
>
> That is 24 bytes per symbol. The name is read from the string table
> and demangled only when a sample lands in the symbol. The difference
> from your description is that v3 then creates a regular struct symbol
> for the hit and inserts it in the rb-tree, because the rest of perf
> holds struct symbol pointers. If the entry itself were the symbol,
> with the name as a pointer/offset union, that extra step would go
> away. Ian's patch 1 would help: once every name access goes through
> symbol__name(), that accessor is the only place that has to resolve
> an offset.
I'm curious if name_off would work well for PLT symbols which come from
the dynamic symbol table. Probably you need to handle them differently.
>
> > > Probably we can add a limit there
> > > and print it with offset when it doesn't have the name.
>
> Agreed, that is better than [unknown]. The symbol boundaries are still
> known, so the frame can be printed as an offset and resolved offline.
> I can do the same for the byte cap in v4.
>
> >
> > We load all symbols instead of lazily because we need the end of a
> > symbol, which we can only determine by processing the entire symbol
> > table as the end of one symbol is the start of the next.
> >
>
> Ian, I think most symbols don't need the next one for their end. ELF
> symbols have st_size, so the end is start + st_size. Only zero-size
> symbols take the next symbol's start. The whole table still has to be
> scanned to find the symbol for an address, because .symtab isn't
> sorted by address, so an address window would scan it again each time
> it grows. The v3 index scans it once, sorts the entries and fixes up
> the ends there, but keeps only the 24-byte entries, not the names.
>
> On which kernel symbol is right in the nf_tables example: the sample
> is at 0xffffffffc0c4b280, and /proc/kallsyms has nft_do_chain at
> 0xffffffffc0c4b0b0 and the next nf_tables symbol at
> 0xffffffffc0c4b4e0, so your series is right. The last dca symbol
> starts at 0xffffffffc0c4aa50, and symbols__fixup_end() extends it to
> 0xffffffffc0c4c000, over the first page of nf_tables. I've sent the
> fix separately:
> https://lore.kernel.org/all/20260928-haghdoost-perf-symbols-fixup-module-end-v1-1-0a70d1edd401@uber.com/
Thanks for the fix!
Namhyung
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH v1 0/7] perf symbol: Reference counting, flat array storage, and LRU shrinking
2026-09-29 18:09 ` Namhyung Kim
@ 2026-09-29 21:24 ` Alireza Haghdoost
0 siblings, 0 replies; 36+ messages in thread
From: Alireza Haghdoost @ 2026-09-29 21:24 UTC (permalink / raw)
To: Namhyung Kim
Cc: Ian Rogers, Peter Zijlstra, Ingo Molnar,
Arnaldo Carvalho de Melo, Jiri Olsa, Adrian Hunter, James Clark,
linux-perf-users, linux-kernel
> > That is close to what patch 5/6 of my v3 does. Its index is a sorted
> > array of fixed-size entries:
> >
> > struct sym_idx {
> > u64 start;
> > u64 end;
> > u32 name_off;
> > u8 binding;
> > u8 type;
> > u8 flags;
> > };
> >
> > That is 24 bytes per symbol. The name is read from the string table
> > and demangled only when a sample lands in the symbol. The difference
> > from your description is that v3 then creates a regular struct symbol
> > for the hit and inserts it in the rb-tree, because the rest of perf
> > holds struct symbol pointers. If the entry itself were the symbol,
> > with the name as a pointer/offset union, that extra step would go
> > away. Ian's patch 1 would help: once every name access goes through
> > symbol__name(), that accessor is the only place that has to resolve
> > an offset.
>
> I'm curious if name_off would work well for PLT symbols which come from
> the dynamic symbol table. Probably you need to handle them differently.
>
Hi Namhyung,
Yes, synthesized PLT symbols need separate handling, but current
upstream already has that path and my v3 series keeps it.
For a regular ELF symbol, name_off is its st_name: a byte offset into
.strtab for .symtab, or .dynstr for .dynsym. So the same index format
works for both tables. v3 uses .dynsym when a DSO has no .symtab.
An @plt symbol is synthesized rather than read directly from either
symbol table. Upstream's dso__synthesize_plt_symbols() gets its address
from the .plt layout, finds its base name through .rela.plt and
.dynsym, and appends "@plt". The existing code also handles .plt.got
and .plt.sec this way.
Since name_off only identifies the base string, it cannot represent
the added "@plt" suffix by itself. I think we should keep synthesizing
these as regular symbols, as upstream does now. v3 only makes the byte
cap apply to them and, in lazy mode, clips the index at .plt so a
preceding zero-size symbol cannot overlap the PLT entries. With the cap
disabled, which is the default, PLT synthesis is unchanged.
There aren't many of them. On the sample fixture, has 381
.rela.plt entries against about 319,000 .symtab symbols, and the other
binary has 100 against about 637,000. Therefore, I think keeping the existing
upstream path has little memory cost compared with the main symbol table.
Thanks,
Alireza
^ permalink raw reply [flat|nested] 36+ messages in thread
* [PATCH v2 00/18] perf symbol: Reference counting, sorted arrays, and memory shrinking
2026-09-28 7:52 [PATCH v1 0/7] perf symbol: Reference counting, flat array storage, and LRU shrinking Ian Rogers
` (8 preceding siblings ...)
2026-09-28 19:45 ` Alireza Haghdoost
@ 2026-10-27 23:14 ` Ian Rogers
2026-10-27 23:14 ` [PATCH v2 01/18] perf bench internals: Add symbols and maps benchmarks Ian Rogers
` (17 more replies)
9 siblings, 18 replies; 36+ messages in thread
From: Ian Rogers @ 2026-10-27 23:14 UTC (permalink / raw)
To: irogers, acme, namhyung
Cc: adrian.hunter, haghdoost, james.clark, jolsa, linux-kernel,
linux-perf-users, mingo, peterz
When profiling large binaries or long-running workloads with many DSOs and
threads, symbol and DSO tables frequently dominate perf's memory footprint
and startup latency. Currently `struct symbol` has several structural
limitations:
1. Every `struct symbol` embeds a 24-byte `struct rb_node` for the
`dso->symbols` address rbtree, plus an optional negative-offset
allocation prefix (`symbol_conf.priv_size`) for TUI browser indices,
`db-export` IDs, or `struct annotation` state even when only a tiny
fraction of symbols are ever annotated or exported.
2. Unlike `struct map`, `struct dso`, and `struct thread`, `struct symbol`
is not reference-counted: once loaded into a `struct dso`, symbols can
never be safely evicted while `hist_entry`, `callchain_cursor_node`, or
`map_symbol` references exist, and `dso__symbols()` has no internal
synchronization against concurrent readers/writers.
3. ELF symbol table loading (`dso__load_sym_internal()`) repeatedly
invokes `libelf` section/program header lookups per symbol, calls
`elf_sym__name()` before filtering out non-function/non-allocated
symbols, runs Rust/Java demanglers on plain C symbols without fast-path
prefix checks, and resorts `dso->symbols` with `qsort()` even when ELF
symbol tables are already largely ordered in ascending address runs.
This series modernizes `struct symbol` to match the rest of `tools/perf/util`
(`DECLARE_RC_STRUCT`, `struct rw_semaphore`, sorted pointer arrays) and
introduces symbol/DSO memory shrinking and faster ELF symbol loading:
- Patch 1 adds `perf bench internals symbols` and `perf bench internals maps`
to benchmark ELF symbol loading, bulk symbol insertion/sorting/lookup,
incremental symbol appends, ascending map insertion, and incremental map
appends.
- Patch 2 adds fast-path prefix checks in `dso__demangle_sym()` (before
calling `rust_demangle_demangle()`, keeping `demangle-rust-v0.c`
unmodified relative to upstream `rustc-demangle`) and in
`java_demangle_sym()`.
- Patches 3-6 encapsulate `struct symbol` field access behind accessor
functions (annotated with `__attribute__((nonnull))`) and replace the
negative-offset `symbol_conf.priv_size` allocation prefix with on-demand
browser index calculation and per-DSO/per-exporter hashmaps for
`db-export` and `struct annotation`.
- Patch 7 replaces `dso->symbols` (`rb_root_cached`) and `dso->symbol_names`
with a locked `struct symbols` container backed by sorted pointer arrays
(`symbols` and `symbols_by_name`) protected by a `struct rw_semaphore`,
removing `struct rb_node` from `struct symbol`.
- Patch 8 wraps `struct symbol` in `DECLARE_RC_STRUCT(symbol)` with
`refcount_t` (`symbol__get()` / `symbol__put()` / `symbol__zput()`) for
sanitizer-checked reference counting across `map_symbol`, `addr_location`,
`hist_entry`, `callchain`, `thread_stack`, and `db-export`.
- Patch 9 replaces `qsort()` in `struct symbols` with a natural-run merge
sort and galloping merge (taking advantage of pre-ordered runs in
`.symtab`/`.dynsym` and `kallsyms`) plus an in-place binary insertion
fast path for small incremental appends.
- Patch 10 applies the same incremental binary-insertion sort and an O(1)
tail-insertion fast path to `struct maps` for `/proc/<pid>/maps` synthesis
and runtime `PERF_RECORD_MMAP2` / JIT map insertions.
- Patch 11 caches ELF program and section headers during
`dso__load_sym_internal()`, defers `elf_sym__name()` string table lookups
until after symbol filtering, and runs `symbols__fixup_end()` /
`symbols__fixup_duplicate()` once per DSO load.
- Patches 12-17 add `down_read_trylock()` / `down_write_trylock()`, protect
`machines->guests` with a `rw_semaphore`, implement `symbols__shrink()`,
`dso__shrink()`, `dsos__shrink()`, and `perf_session__shrink()` (with
transparent on-demand reload in `dso__load()` that preserves pointer
identity for retained symbols), and provide `perf_session__malloc()` /
`zalloc()` / `calloc()` / `realloc()` / `strdup()` wrappers that
automatically shrink unreferenced symbols and DSOs on allocation failure.
- Patch 18 adds unit tests in `perf test symbols` covering sorted symbol
insertion, symbol/DSO shrinking, transparent DSO reload after shrink, and
session allocation wrappers.
End-to-end benchmark results across the series:
perf bench internals symbols (perf binary):
Average DSO load + sort: 7.348 msec -> 5.324 msec (-27.5%)
Average DSO memory usage: 1401 KB -> 1308 KB ( -6.6%)
Average bulk insert + lookup: 1453.95 us -> 1161.00 us (-20.1%)
Average append + lookup (200 interior inserts into 10,000 symbols):
rbtree: 42.85 us -> array+qsort: 40429.75 us -> array+binary-insert: 152.70 us
perf bench internals symbols -b libLLVM.so.21.1:
Average DSO load + sort: 81.196 msec -> 62.582 msec (-22.9%)
Average DSO memory usage: 9530 KB -> 9164 KB ( -3.8%)
perf bench internals symbols -b libLLVM.so.21.1 --no-demangle:
Average DSO load + sort: 32.675 msec -> 19.122 msec (-41.5%)
Average DSO memory usage: 7589 KB -> 7229 KB ( -4.7%)
perf bench internals maps:
Average ascending insert + lookup: 63.37 us -> 58.97 us ( -6.9%)
Average append + lookup: 7477.45 us -> 96.67 us (-98.7%, 77.3x faster)
Net performance and memory trade-offs:
- Replacing the per-symbol rbtree (`struct rb_node`) and negative-offset
`symbol_conf.priv_size` allocation prefix with a contiguous sorted pointer
array reduces per-symbol heap overhead by 16 to 24+ bytes (saving 3.8% to
6.6% of total DSO symbol table heap memory unconditionally, and allowing
unreferenced symbols and DSOs to be shrunk on demand under memory
pressure).
- For the dominant symbol workloads—loading ELF binaries (`-22.9%` to
`-41.5%`) and bulk symbol insertion, sorting, and lookup (`-20.1%`)—the
contiguous array, natural-run merge sort, ELF header caching, and
demangler fast paths yield substantial speedups over the rbtree baseline.
- For synthetic single-symbol interior insertions interleaved with immediate
lookups into an already-large 10,000-symbol DSO (`dso__insert_symbol()`),
an rbtree rotation takes ~0.21 us/op (`42.85 us` for 200 ops) whereas
shifting the contiguous pointer array tail with `memmove()` after binary
search takes ~0.76 us/op (`152.70 us` for 200 ops, down from `40429.75 us`
with naive `qsort()`). In real workloads, symbols are either loaded in
bulk (`dso__load()`) or inserted into small per-prog BPF/JIT DSOs where
the array fits in a few cache lines, making this sub-microsecond
difference negligible in practice.
- Overall, the series reduces memory consumption across all workloads while
significantly improving—or remaining neutral on—runtime performance.
v2:
- Rebased onto perf-tools-next (dropping "perf symbols: Fix broken
ELF_C_READ_MMAP fallback guard", which was merged upstream, and splitting
"perf libdw: Optimize fallback logic for missing DWARF" into a separate
fixes series).
- Added 'perf bench internals symbols' and 'perf bench internals maps'
benchmarks (Patch 1) and included before/after numbers in the optimization
patches.
- Added __attribute__((nonnull)) annotations to struct symbol accessor
functions (Patch 3).
- Split the symbol_conf.priv_size removal patch into three separate patches
for map_browser (Patch 4), db-export (Patch 5), and annotate (Patch 6).
- Added dedicated optimization patches for Rust/Java demangler prefix checks
(Patch 2), natural-run merge sort and binary insertion in struct symbols
(Patch 9), tail-insertion and incremental binary-insertion sort in struct
maps (Patch 10), and ELF program/section header caching and deferred
symbol name lookup (Patch 11).
- Replaced periodic heuristic LRU shrinking and windowed ELF symbol loading
with on-demand shrinking (symbols__shrink(), dso__shrink(),
dsos__shrink(), perf_session__shrink()) triggered on allocation failure via
session-scoped allocation wrappers, plus transparent DSO reload preserving
retained symbol pointers (Patches 12-18).
v1: https://lore.kernel.org/lkml/20260928075237.3055101-1-irogers@google.com/
Assisted-by: Antigravity:gemini-3.1-pro
Signed-off-by: Ian Rogers <irogers@google.com>
Ian Rogers (18):
perf bench internals: Add symbols and maps benchmarks
perf demangle: Add fast-path prefix checks for Rust and Java
demanglers
perf symbol: Add accessor functions for struct symbol fields
perf map_browser: Compute symbol browser index on demand
perf db-export: Store symbol database IDs in a hashmap
perf annotate: Move struct annotation to a hashmap and remove
symbol_conf.priv_size
perf symbol: Switch backing storage from rbtree to struct symbols
array
perf symbol: Add reference counting and DECLARE_RC_STRUCT(symbol)
perf symbol: Optimize symbol sorting with natural-run merge sort and
binary insertion
perf maps: Optimize tail insertion and small-append sorting
perf symbol-elf: Cache ELF program/section headers and defer symbol
name lookup
perf rwsem: Add down_read_trylock() and down_write_trylock()
perf vdso: Constify dso parameter in dso__is_vdso()
perf machine: Protect machines->guests rbtree with a rw_semaphore
perf symbol: Add memory shrinking for symbols, DSOs, and machines
perf session: Add dso__session() and session-scoped allocation
wrappers
perf symbol: Use session allocation wrappers in symbol, DSO, thread,
and hist paths
perf test symbols: Add tests for symbol shrinking and session alloc
wrappers
tools/perf/Documentation/perf-bench.txt | 6 +
.../arch/powerpc/util/skip-callchain-idx.c | 2 +-
tools/perf/arch/powerpc/util/sym-handling.c | 40 +-
tools/perf/bench/Build | 2 +
tools/perf/bench/bench.h | 2 +
tools/perf/bench/maps.c | 186 +++
tools/perf/bench/symbols.c | 239 +++
tools/perf/builtin-annotate.c | 22 +-
tools/perf/builtin-bench.c | 2 +
tools/perf/builtin-c2c.c | 14 -
tools/perf/builtin-diff.c | 4 +-
tools/perf/builtin-kallsyms.c | 9 +-
tools/perf/builtin-kmem.c | 82 +-
tools/perf/builtin-kwork.c | 4 +-
tools/perf/builtin-lock.c | 25 +-
tools/perf/builtin-mem.c | 2 +-
tools/perf/builtin-report.c | 14 -
tools/perf/builtin-sched.c | 6 +-
tools/perf/builtin-script.c | 18 +-
tools/perf/builtin-timechart.c | 2 +-
tools/perf/builtin-top.c | 24 +-
tools/perf/builtin-trace.c | 23 +-
tools/perf/tests/builtin-test.c | 1 -
tools/perf/tests/dlfilter-test.c | 12 +-
tools/perf/tests/dwarf-unwind.c | 2 +-
tools/perf/tests/hists_common.c | 9 +-
tools/perf/tests/hists_cumulate.c | 8 +-
tools/perf/tests/hists_filter.c | 10 +-
tools/perf/tests/hists_link.c | 16 +-
tools/perf/tests/hists_output.c | 9 +-
tools/perf/tests/kallsyms-split.c | 5 +-
tools/perf/tests/maps.c | 58 +
tools/perf/tests/pe-file-parsing.c | 1 +
tools/perf/tests/symbols.c | 454 +++++-
tools/perf/tests/vmlinux-kallsyms.c | 187 ++-
tools/perf/ui/browser.c | 3 +
tools/perf/ui/browsers/annotate.c | 10 +-
tools/perf/ui/browsers/hists.c | 35 +-
tools/perf/ui/browsers/map.c | 88 +-
tools/perf/ui/gtk/annotate.c | 6 +-
tools/perf/ui/stdio/hist.c | 6 +-
tools/perf/util/addr2line.c | 8 +-
tools/perf/util/addr_location.c | 4 +
.../perf/util/annotate-arch/annotate-alpha.c | 5 +-
.../perf/util/annotate-arch/annotate-arm64.c | 12 +-
.../util/annotate-arch/annotate-loongarch.c | 15 +-
tools/perf/util/annotate-arch/annotate-s390.c | 6 +-
tools/perf/util/annotate-arch/annotate-x86.c | 14 +-
tools/perf/util/annotate-data.c | 12 +-
tools/perf/util/annotate.c | 211 ++-
tools/perf/util/annotate.h | 6 +-
tools/perf/util/auxtrace.c | 111 +-
tools/perf/util/block-info.c | 12 +-
tools/perf/util/bpf_lock_contention.c | 35 +-
tools/perf/util/c2c-function.c | 32 +-
tools/perf/util/call-path.c | 15 +-
tools/perf/util/callchain.c | 23 +-
tools/perf/util/capstone.c | 22 +-
tools/perf/util/data-convert-json.c | 4 +-
tools/perf/util/db-export.c | 99 +-
tools/perf/util/db-export.h | 8 +
tools/perf/util/debug.c | 3 +-
tools/perf/util/demangle-java.c | 23 +-
tools/perf/util/disasm.c | 69 +-
tools/perf/util/dlfilter.c | 12 +-
tools/perf/util/dso.c | 257 ++-
tools/perf/util/dso.h | 94 +-
tools/perf/util/dsos.c | 76 +
tools/perf/util/dsos.h | 3 +
tools/perf/util/event.c | 12 +-
tools/perf/util/evsel_fprintf.c | 2 +-
tools/perf/util/hist.c | 70 +-
tools/perf/util/intel-pt.c | 74 +-
tools/perf/util/libbfd.c | 14 +-
tools/perf/util/libdw.c | 5 +-
tools/perf/util/llvm.c | 8 +-
tools/perf/util/lock-contention.c | 8 +-
tools/perf/util/machine.c | 189 ++-
tools/perf/util/machine.h | 9 +
tools/perf/util/map.c | 27 +-
tools/perf/util/map.h | 18 +-
tools/perf/util/map_symbol.c | 4 +-
tools/perf/util/maps.c | 284 +++-
tools/perf/util/mutex.h | 3 +
tools/perf/util/probe-event.c | 148 +-
tools/perf/util/python.c | 36 +-
tools/perf/util/rwsem.c | 20 +
tools/perf/util/rwsem.h | 2 +
tools/perf/util/session.c | 78 +
tools/perf/util/session.h | 8 +
tools/perf/util/sort.c | 47 +-
tools/perf/util/srcline.c | 22 +-
tools/perf/util/symbol-elf.c | 311 +++-
tools/perf/util/symbol.c | 1438 +++++++++++++----
tools/perf/util/symbol.h | 187 ++-
tools/perf/util/symbol_conf.h | 1 -
tools/perf/util/symbol_fprintf.c | 37 +-
tools/perf/util/thread-stack.c | 18 +-
tools/perf/util/threads.c | 17 +-
tools/perf/util/unwind-libdw.c | 4 +-
tools/perf/util/unwind-libunwind.c | 2 +-
tools/perf/util/vdso.c | 2 +-
tools/perf/util/vdso.h | 2 +-
103 files changed, 4557 insertions(+), 1389 deletions(-)
create mode 100644 tools/perf/bench/maps.c
create mode 100644 tools/perf/bench/symbols.c
--
2.56.0.313.g2479322e68-goog
^ permalink raw reply [flat|nested] 36+ messages in thread
* [PATCH v2 01/18] perf bench internals: Add symbols and maps benchmarks
2026-10-27 23:14 ` [PATCH v2 00/18] perf symbol: Reference counting, sorted arrays, and memory shrinking Ian Rogers
@ 2026-10-27 23:14 ` Ian Rogers
2026-10-27 23:14 ` [PATCH v2 02/18] perf demangle: Add fast-path prefix checks for Rust and Java demanglers Ian Rogers
` (16 subsequent siblings)
17 siblings, 0 replies; 36+ messages in thread
From: Ian Rogers @ 2026-10-27 23:14 UTC (permalink / raw)
To: irogers, acme, namhyung
Cc: adrian.hunter, haghdoost, james.clark, jolsa, linux-kernel,
linux-perf-users, mingo, peterz
Add two new 'perf bench internals' subcommands to measure symbol and map
operations directly:
- 'perf bench internals symbols':
1. Loads symbols from an ELF binary (defaulting to the running perf
executable, or specified via -b/--binary) and sorts them by name,
reporting both average runtime and heap memory consumption.
2. Inserts synthetic symbols in two ascending runs (simulating .symtab
and .dynsym), fixes up symbol ends and duplicates, and queries each
symbol by address.
3. Incrementally appends symbols at interior addresses and immediately
queries each appended symbol by address.
- 'perf bench internals maps':
1. Inserts non-overlapping maps in ascending address order (simulating
/proc/<pid>/maps synthesis) and queries each map by address.
2. Incrementally appends maps at interior addresses (simulating runtime
PERF_RECORD_MMAP2 / JIT events) and immediately queries each map by
address and by name.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/Documentation/perf-bench.txt | 6 +
tools/perf/bench/Build | 2 +
tools/perf/bench/bench.h | 2 +
tools/perf/bench/maps.c | 186 +++++++++++++++++++
tools/perf/bench/symbols.c | 236 ++++++++++++++++++++++++
tools/perf/builtin-bench.c | 2 +
6 files changed, 434 insertions(+)
create mode 100644 tools/perf/bench/maps.c
create mode 100644 tools/perf/bench/symbols.c
diff --git a/tools/perf/Documentation/perf-bench.txt b/tools/perf/Documentation/perf-bench.txt
index 7f820f69740f..c65dd8f4131f 100644
--- a/tools/perf/Documentation/perf-bench.txt
+++ b/tools/perf/Documentation/perf-bench.txt
@@ -351,6 +351,12 @@ SUITES FOR 'internals'
*synthesize*::
Suite for evaluating perf's event synthesis performance.
+*symbols*::
+Suite for evaluating ELF symbol loading, sorting, and lookup performance.
+
+*maps*::
+Suite for evaluating maps insertion, sorting, and lookup performance.
+
SEE ALSO
--------
linkperf:perf[1]
diff --git a/tools/perf/bench/Build b/tools/perf/bench/Build
index c64c52468d3d..d51e52d3742a 100644
--- a/tools/perf/bench/Build
+++ b/tools/perf/bench/Build
@@ -20,6 +20,8 @@ perf-bench-y += evlist-open-close.o
perf-bench-y += breakpoint.o
perf-bench-y += pmu-scan.o
perf-bench-y += uprobe.o
+perf-bench-y += symbols.o
+perf-bench-y += maps.o
perf-bench-$(CONFIG_X86_64) += mem-memcpy-x86-64-asm.o
perf-bench-$(CONFIG_X86_64) += mem-memset-x86-64-asm.o
diff --git a/tools/perf/bench/bench.h b/tools/perf/bench/bench.h
index 85e3ac471875..c988d236e0f0 100644
--- a/tools/perf/bench/bench.h
+++ b/tools/perf/bench/bench.h
@@ -52,6 +52,8 @@ int bench_uprobe_trace_printk(int argc, const char **argv);
int bench_uprobe_empty_ret(int argc, const char **argv);
int bench_uprobe_trace_printk_ret(int argc, const char **argv);
int bench_pmu_scan(int argc, const char **argv);
+int bench_symbols(int argc, const char **argv);
+int bench_maps(int argc, const char **argv);
#define BENCH_FORMAT_DEFAULT_STR "default"
#define BENCH_FORMAT_DEFAULT 0
diff --git a/tools/perf/bench/maps.c b/tools/perf/bench/maps.c
new file mode 100644
index 000000000000..ed5a5be3cbf0
--- /dev/null
+++ b/tools/perf/bench/maps.c
@@ -0,0 +1,186 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Benchmark maps insertion, sorting, and address/name lookup.
+ */
+#include <errno.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <linux/compiler.h>
+#include <linux/kernel.h>
+#include <linux/time64.h>
+#include <subcmd/parse-options.h>
+#include "bench.h"
+#include "../util/debug.h"
+#include "../util/dso.h"
+#include "../util/map.h"
+#include "../util/maps.h"
+#include "../util/stat.h"
+
+static unsigned int iterations = 100;
+static unsigned int nr_maps = 1000;
+static unsigned int nr_appends = 200;
+
+static const struct option options[] = {
+ OPT_UINTEGER('i', "iterations", &iterations,
+ "Number of iterations used to compute average (default: 100)"),
+ OPT_UINTEGER('m', "nr-maps", &nr_maps,
+ "Number of ascending maps per iteration (default: 1000)"),
+ OPT_UINTEGER('a', "nr-appends", &nr_appends,
+ "Number of incremental map appends per iteration (default: 200)"),
+ OPT_END()
+};
+
+static const char *const bench_usage[] = {
+ "perf bench internals maps <options>",
+ NULL
+};
+
+static int do_maps_bench(void)
+{
+ struct stats asc_stats, append_stats;
+ unsigned int total_dsos = nr_maps + nr_appends;
+ struct dso **dsos;
+ struct map **all_maps;
+ unsigned int i, j;
+ int err = 0;
+
+ if (total_dsos == 0)
+ return 0;
+
+ dsos = calloc(total_dsos, sizeof(*dsos));
+ all_maps = calloc(total_dsos, sizeof(*all_maps));
+ if (!dsos || !all_maps) {
+ free(dsos);
+ free(all_maps);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < total_dsos; i++) {
+ char name[32];
+ u64 addr;
+
+ scnprintf(name, sizeof(name), "/tmp/bench_dso_%06u.so", i);
+ dsos[i] = dso__new(name);
+ if (!dsos[i]) {
+ err = -ENOMEM;
+ goto out_free;
+ }
+ if (i < nr_maps) {
+ addr = 0x100000ULL + (u64)i * 0x2000ULL;
+ all_maps[i] = map__new2(addr, dsos[i]);
+ if (!all_maps[i]) {
+ err = -ENOMEM;
+ goto out_free;
+ }
+ map__set_end(all_maps[i], addr + 0x1000ULL);
+ } else {
+ u64 k = i - nr_maps;
+ u64 slot = nr_maps ? (k * 197ULL) % nr_maps : k;
+
+ addr = 0x101000ULL + slot * 0x2000ULL +
+ (nr_maps ? (k / nr_maps) * 0x100ULL : 0);
+ all_maps[i] = map__new2(addr, dsos[i]);
+ if (!all_maps[i]) {
+ err = -ENOMEM;
+ goto out_free;
+ }
+ map__set_end(all_maps[i], addr + 0x80ULL);
+ }
+ }
+
+ init_stats(&asc_stats);
+ init_stats(&append_stats);
+
+ for (i = 0; i < iterations; i++) {
+ struct timeval start, end, diff;
+ struct maps *maps = maps__new(NULL);
+ u64 runtime_us;
+
+ if (!maps) {
+ err = -ENOMEM;
+ goto out_free;
+ }
+
+ /*
+ * Phase 1: Insert maps in ascending address order (simulating
+ * /proc/<pid>/maps synthesis) and look up each map by address.
+ */
+ gettimeofday(&start, NULL);
+ for (j = 0; j < nr_maps; j++) {
+ err = maps__fixup_overlap_and_insert(maps, all_maps[j]);
+ if (err) {
+ maps__zput(maps);
+ goto out_free;
+ }
+ }
+ for (j = 0; j < nr_maps; j++) {
+ u64 addr = 0x100000ULL + (u64)j * 0x2000ULL + 0x100ULL;
+ struct map *found = maps__find(maps, addr);
+
+ map__put(found);
+ }
+ gettimeofday(&end, NULL);
+ timersub(&end, &start, &diff);
+ runtime_us = diff.tv_sec * USEC_PER_SEC + diff.tv_usec;
+ update_stats(&asc_stats, runtime_us);
+
+ /*
+ * Phase 2: Incrementally insert maps at interior addresses
+ * (simulating runtime MMAP2 / JIT code events) and immediately
+ * query by address and by name.
+ */
+ gettimeofday(&start, NULL);
+ for (j = 0; j < nr_appends; j++) {
+ struct map *map = all_maps[nr_maps + j];
+ struct dso *dso = dsos[nr_maps + j];
+ u64 addr = map__start(map);
+ struct map *found;
+
+ err = maps__fixup_overlap_and_insert(maps, map);
+ if (err) {
+ maps__zput(maps);
+ goto out_free;
+ }
+
+ found = maps__find(maps, addr + 0x10ULL);
+ map__put(found);
+
+ found = maps__find_by_name(maps, dso__short_name(dso));
+ map__put(found);
+ }
+ gettimeofday(&end, NULL);
+ timersub(&end, &start, &diff);
+ runtime_us = diff.tv_sec * USEC_PER_SEC + diff.tv_usec;
+ update_stats(&append_stats, runtime_us);
+
+ maps__zput(maps);
+ }
+
+ printf(" Average ascending insert + lookup: %.3f usec (+- %.3f usec)\n",
+ avg_stats(&asc_stats), stddev_stats(&asc_stats));
+ printf(" Average append + lookup: %.3f usec (+- %.3f usec)\n",
+ avg_stats(&append_stats), stddev_stats(&append_stats));
+
+out_free:
+ for (i = 0; i < total_dsos; i++) {
+ map__put(all_maps[i]);
+ dso__put(dsos[i]);
+ }
+ free(all_maps);
+ free(dsos);
+ return err;
+}
+
+int bench_maps(int argc, const char **argv)
+{
+ argc = parse_options(argc, argv, options, bench_usage, 0);
+ if (argc) {
+ usage_with_options(bench_usage, options);
+ exit(EXIT_FAILURE);
+ }
+
+ return do_maps_bench();
+}
diff --git a/tools/perf/bench/symbols.c b/tools/perf/bench/symbols.c
new file mode 100644
index 000000000000..5ab71d440e67
--- /dev/null
+++ b/tools/perf/bench/symbols.c
@@ -0,0 +1,236 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Benchmark symbol loading, sorting, and address/name lookup.
+ */
+#include <elf.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <malloc.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <linux/compiler.h>
+#include <linux/kernel.h>
+#include <linux/time64.h>
+#include <subcmd/parse-options.h>
+#include "bench.h"
+#include "../util/debug.h"
+#include "../util/dso.h"
+#include "../util/map.h"
+#include "../util/stat.h"
+#include "../util/symbol.h"
+#include "../util/util.h"
+
+static unsigned int iterations = 20;
+static unsigned int nr_symbols = 10000;
+static unsigned int nr_appends = 200;
+static const char *binary_path;
+static bool no_demangle;
+
+static const struct option options[] = {
+ OPT_UINTEGER('i', "iterations", &iterations,
+ "Number of iterations used to compute average (default: 20)"),
+ OPT_STRING('b', "binary", &binary_path, "path",
+ "Binary path for ELF symbol load benchmark (default: perf binary)"),
+ OPT_UINTEGER('n', "nr-symbols", &nr_symbols,
+ "Number of synthetic symbols for bulk benchmark (default: 10000)"),
+ OPT_UINTEGER('a', "nr-appends", &nr_appends,
+ "Number of incremental symbol appends per iteration (default: 200)"),
+ OPT_BOOLEAN(0, "no-demangle", &no_demangle,
+ "Disable symbol demangling during ELF load"),
+ OPT_END()
+};
+
+static const char *const bench_usage[] = {
+ "perf bench internals symbols <options>",
+ NULL
+};
+
+static int do_dso_load_bench(const char *path)
+{
+ struct stats time_stats;
+#if defined(__GLIBC__) && __GLIBC_PREREQ(2, 33)
+ struct stats mem_stats;
+#endif
+ double time_average, time_stddev;
+ unsigned int i;
+
+ init_stats(&time_stats);
+#if defined(__GLIBC__) && __GLIBC_PREREQ(2, 33)
+ init_stats(&mem_stats);
+#endif
+
+ for (i = 0; i < iterations; i++) {
+ struct timeval start, end, diff;
+ struct map *map;
+ struct dso *dso;
+ u64 runtime_us;
+#if defined(__GLIBC__) && __GLIBC_PREREQ(2, 33)
+ size_t mem_before = mallinfo2().uordblks;
+ size_t mem_after;
+#endif
+
+ gettimeofday(&start, NULL);
+ map = dso__new_map(path);
+ if (!map) {
+ pr_err("Failed to create map for %s\n", path);
+ return -ENOMEM;
+ }
+ if (map__load(map) < 0) {
+ pr_err("Failed to load symbols from %s\n", path);
+ map__put(map);
+ return -EINVAL;
+ }
+ dso = map__dso(map);
+ dso__sort_by_name(dso);
+ gettimeofday(&end, NULL);
+
+#if defined(__GLIBC__) && __GLIBC_PREREQ(2, 33)
+ mem_after = mallinfo2().uordblks;
+ if (mem_after > mem_before)
+ update_stats(&mem_stats, (mem_after - mem_before) / 1024);
+#endif
+ map__put(map);
+
+ timersub(&end, &start, &diff);
+ runtime_us = diff.tv_sec * USEC_PER_SEC + diff.tv_usec;
+ update_stats(&time_stats, runtime_us);
+ }
+
+ time_average = avg_stats(&time_stats) / USEC_PER_MSEC;
+ time_stddev = stddev_stats(&time_stats) / USEC_PER_MSEC;
+ printf(" Average DSO load + sort: %.3f msec (+- %.3f msec)\n",
+ time_average, time_stddev);
+
+#if defined(__GLIBC__) && __GLIBC_PREREQ(2, 33)
+ printf(" Average DSO memory usage: %.0f KB (+- %.0f KB)\n",
+ avg_stats(&mem_stats), stddev_stats(&mem_stats));
+#endif
+ return 0;
+}
+
+static int do_synthetic_symbols_bench(void)
+{
+ struct stats bulk_stats, append_stats;
+ unsigned int i, j, half = nr_symbols / 2;
+
+ if (half == 0)
+ return 0;
+
+ init_stats(&bulk_stats);
+ init_stats(&append_stats);
+
+ for (i = 0; i < iterations; i++) {
+ struct timeval start, end, diff;
+ struct dso *dso = dso__new("[bench_symbols]");
+ u64 runtime_us;
+ char name[32];
+
+ if (!dso)
+ return -ENOMEM;
+
+ /*
+ * Phase 1: Insert symbols in two ascending runs (simulating
+ * .symtab + .dynsym), fixup ends and duplicates, and look up
+ * all symbols by address.
+ */
+ gettimeofday(&start, NULL);
+ for (j = 0; j < nr_symbols; j++) {
+ u64 addr = (j < half) ?
+ (0x100000ULL + (u64)j * 0x40ULL) :
+ (0x100020ULL + (u64)(j - half) * 0x40ULL);
+ struct symbol *sym;
+
+ scnprintf(name, sizeof(name), "bench_sym_%06u", j);
+ sym = symbol__new(addr, 0x18, STB_GLOBAL, STT_FUNC, name);
+ if (!sym) {
+ dso__put(dso);
+ return -ENOMEM;
+ }
+ symbols__insert(dso__symbols(dso), sym);
+ }
+ symbols__fixup_end(dso__symbols(dso), false);
+ symbols__fixup_duplicate(dso__symbols(dso));
+
+ for (j = 0; j < nr_symbols; j++) {
+ u64 addr = (j < half) ?
+ (0x100000ULL + (u64)j * 0x40ULL + 4) :
+ (0x100020ULL + (u64)(j - half) * 0x40ULL + 4);
+
+ (void)dso__find_symbol_nocache(dso, addr);
+ }
+ gettimeofday(&end, NULL);
+ timersub(&end, &start, &diff);
+ runtime_us = diff.tv_sec * USEC_PER_SEC + diff.tv_usec;
+ update_stats(&bulk_stats, runtime_us);
+
+ /*
+ * Phase 2: Incrementally append one symbol at a time at an
+ * interior address and immediately query by address.
+ */
+ gettimeofday(&start, NULL);
+ for (j = 0; j < nr_appends; j++) {
+ u64 addr = 0x100010ULL + ((u64)(j * 197 % half)) * 0x40ULL;
+ struct symbol *sym;
+
+ scnprintf(name, sizeof(name), "jit_sym_%06u", j);
+ sym = symbol__new(addr, 0x08, STB_GLOBAL, STT_FUNC, name);
+ if (!sym) {
+ dso__put(dso);
+ return -ENOMEM;
+ }
+ dso__insert_symbol(dso, sym);
+ (void)dso__find_symbol_nocache(dso, addr + 2);
+ }
+ gettimeofday(&end, NULL);
+ timersub(&end, &start, &diff);
+ runtime_us = diff.tv_sec * USEC_PER_SEC + diff.tv_usec;
+ update_stats(&append_stats, runtime_us);
+
+ dso__put(dso);
+ }
+
+ printf(" Average bulk insert + lookup: %.3f usec (+- %.3f usec)\n",
+ avg_stats(&bulk_stats), stddev_stats(&bulk_stats));
+ printf(" Average append + lookup: %.3f usec (+- %.3f usec)\n",
+ avg_stats(&append_stats), stddev_stats(&append_stats));
+ return 0;
+}
+
+int bench_symbols(int argc, const char **argv)
+{
+ char exe_buf[PATH_MAX];
+ const char *path;
+ bool orig_demangle;
+ int err;
+
+ argc = parse_options(argc, argv, options, bench_usage, 0);
+ if (argc) {
+ usage_with_options(bench_usage, options);
+ exit(EXIT_FAILURE);
+ }
+
+ if (symbol__init(NULL) < 0)
+ return -1;
+
+ orig_demangle = symbol_conf.demangle;
+ if (no_demangle)
+ symbol_conf.demangle = false;
+
+ if (binary_path) {
+ path = binary_path;
+ } else {
+ perf_exe(exe_buf, sizeof(exe_buf));
+ path = exe_buf;
+ }
+
+ err = do_dso_load_bench(path);
+ if (!err)
+ err = do_synthetic_symbols_bench();
+
+ symbol_conf.demangle = orig_demangle;
+ return err;
+}
diff --git a/tools/perf/builtin-bench.c b/tools/perf/builtin-bench.c
index 62bebdedd3b8..57a4199a058c 100644
--- a/tools/perf/builtin-bench.c
+++ b/tools/perf/builtin-bench.c
@@ -97,6 +97,8 @@ static const struct bench internals_benchmarks[] = {
{ "inject-build-id", "Benchmark build-id injection", bench_inject_build_id },
{ "evlist-open-close", "Benchmark evlist open and close", bench_evlist_open_close },
{ "pmu-scan", "Benchmark sysfs PMU info scanning", bench_pmu_scan },
+ { "symbols", "Benchmark symbol loading, sorting and lookup", bench_symbols },
+ { "maps", "Benchmark maps insertion, sorting and lookup", bench_maps },
{ NULL, NULL, NULL }
};
--
2.56.0.313.g2479322e68-goog
^ permalink raw reply [flat|nested] 36+ messages in thread
* [PATCH v2 02/18] perf demangle: Add fast-path prefix checks for Rust and Java demanglers
2026-10-27 23:14 ` [PATCH v2 00/18] perf symbol: Reference counting, sorted arrays, and memory shrinking Ian Rogers
2026-10-27 23:14 ` [PATCH v2 01/18] perf bench internals: Add symbols and maps benchmarks Ian Rogers
@ 2026-10-27 23:14 ` Ian Rogers
2026-10-27 23:14 ` [PATCH v2 03/18] perf symbol: Add accessor functions for struct symbol fields Ian Rogers
` (15 subsequent siblings)
17 siblings, 0 replies; 36+ messages in thread
From: Ian Rogers @ 2026-10-27 23:14 UTC (permalink / raw)
To: irogers, acme, namhyung
Cc: adrian.hunter, haghdoost, james.clark, jolsa, linux-kernel,
linux-perf-users, mingo, peterz
During DSO symbol loading in dso__load_sym_internal(), dso__demangle_sym()
is called for every ELF symbol. Both rust_demangle_demangle() and
java_demangle_sym() previously performed string searches or buffer
allocations before checking whether the symbol name could possibly be a
mangled Rust or Java symbol, adding unnecessary overhead on plain C
symbols.
Add fast-path prefix checks in dso__demangle_sym() before calling
rust_demangle_demangle() (using strstarts() for '_ZN', 'ZN', '__ZN',
'_R', 'R', or '__R', keeping demangle-rust-v0.c unmodified relative to
upstream rustc-demangle) and in java_demangle_sym() (requiring 'L', ';',
'(', and ')') to reject non-Rust and non-Java symbol names immediately.
Using 'perf bench internals symbols':
Before (perf binary):
Average DSO load + sort: 7.348 msec (+- 0.069 msec)
After (perf binary, -4.2%):
Average DSO load + sort: 7.041 msec (+- 0.051 msec)
Before (libLLVM.so.21.1):
Average DSO load + sort: 81.196 msec (+- 0.780 msec)
After (libLLVM.so.21.1, -3.2%):
Average DSO load + sort: 78.599 msec (+- 0.857 msec)
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/demangle-java.c | 23 +++++++++++++++++++++--
tools/perf/util/symbol.c | 15 ++++++++++++++-
2 files changed, 35 insertions(+), 3 deletions(-)
diff --git a/tools/perf/util/demangle-java.c b/tools/perf/util/demangle-java.c
index c3cb327ed562..5625c4de39eb 100644
--- a/tools/perf/util/demangle-java.c
+++ b/tools/perf/util/demangle-java.c
@@ -158,14 +158,33 @@ char *
java_demangle_sym(const char *str, int flags)
{
char *buf, *ptr;
- const char *p;
+ const char *l, *semi, *open, *p;
size_t len, l1 = 0;
if (!str)
return NULL;
+ /*
+ * OpenJDK JIT symbols have the form:
+ * [prefix]L<class>;<method>(<args>)<return_type>
+ * where the return type (such as 'I' for int or 'V' for void) is at the
+ * end after ')'. In __demangle_java_sym(), the parser starts in
+ * MODE_PREFIX and only reaches MODE_TYPE (required when ')' is reached)
+ * via 'L' (MODE_CLASS) -> ';' (MODE_FUNC) -> '(' (MODE_TYPE). Verify
+ * those delimiters appear in order before allocating the output buffer.
+ */
+ l = strchr(str, 'L');
+ if (!l)
+ return NULL;
+ semi = strchr(l + 1, ';');
+ if (!semi)
+ return NULL;
+ open = strchr(semi + 1, '(');
+ if (!open)
+ return NULL;
+
/* find start of return type */
- p = strrchr(str, ')');
+ p = strrchr(open + 1, ')');
if (!p)
return NULL;
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index b61a574ce9a3..dc70bbe8514f 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -2738,7 +2738,20 @@ char *dso__demangle_sym(struct dso *dso, int kmodule, const char *elf_name)
if (!want_demangle((dso && dso__kernel(dso)) || kmodule))
return demangled;
- rust_demangle_demangle(elf_name, &rust_demangle);
+ /*
+ * Rust mangled symbols start with one of the prefixes checked by
+ * rust_demangle_legacy_demangle() ("_ZN", Windows dbghelp "ZN", or
+ * macOS "__ZN") or rust_demangle_v0_demangle() ("_R", Windows dbghelp
+ * "R", or macOS "__R") in demangle-rust-v0.c. Check the prefix here so
+ * we avoid calling rust_demangle_demangle() (which scans the entire
+ * symbol name with strstr() and strlen() for ThinLTO ".llvm." suffixes)
+ * on non-Rust symbols, while keeping demangle-rust-v0.c unmodified
+ * relative to upstream rustc-demangle.
+ */
+ if (strstarts(elf_name, "_ZN") || strstarts(elf_name, "ZN") ||
+ strstarts(elf_name, "__ZN") || strstarts(elf_name, "_R") ||
+ strstarts(elf_name, "R") || strstarts(elf_name, "__R"))
+ rust_demangle_demangle(elf_name, &rust_demangle);
if (rust_demangle_is_known(&rust_demangle)) {
/* A rust mangled name. */
if (rust_demangle.mangled_len == 0)
--
2.56.0.313.g2479322e68-goog
^ permalink raw reply [flat|nested] 36+ messages in thread
* [PATCH v2 03/18] perf symbol: Add accessor functions for struct symbol fields
2026-10-27 23:14 ` [PATCH v2 00/18] perf symbol: Reference counting, sorted arrays, and memory shrinking Ian Rogers
2026-10-27 23:14 ` [PATCH v2 01/18] perf bench internals: Add symbols and maps benchmarks Ian Rogers
2026-10-27 23:14 ` [PATCH v2 02/18] perf demangle: Add fast-path prefix checks for Rust and Java demanglers Ian Rogers
@ 2026-10-27 23:14 ` Ian Rogers
2026-10-27 23:14 ` [PATCH v2 04/18] perf map_browser: Compute symbol browser index on demand Ian Rogers
` (14 subsequent siblings)
17 siblings, 0 replies; 36+ messages in thread
From: Ian Rogers @ 2026-10-27 23:14 UTC (permalink / raw)
To: irogers, acme, namhyung
Cc: adrian.hunter, haghdoost, james.clark, jolsa, linux-kernel,
linux-perf-users, mingo, peterz
To track symbol lifetimes across DSO reloads, histogram decay, and
on-demand memory shrinking, struct symbol needs to be reference-counted
and wrapped with DECLARE_RC_STRUCT(symbol) so REFCNT_CHECKING builds can
detect missing or leaked reference counts.
To prepare for wrapping struct symbol in DECLARE_RC_STRUCT(symbol), add
inline accessor functions for struct symbol fields (symbol__start,
symbol__set_start, symbol__end, symbol__set_end, symbol__name,
symbol__namelen, symbol__arch_sym, symbol__set_arch_sym) and convert
direct member accesses across tools/perf.
Signed-off-by: Ian Rogers <irogers@google.com>
---
.../arch/powerpc/util/skip-callchain-idx.c | 2 +-
tools/perf/arch/powerpc/util/sym-handling.c | 8 +-
tools/perf/builtin-annotate.c | 4 +-
tools/perf/builtin-diff.c | 4 +-
tools/perf/builtin-kallsyms.c | 7 +-
tools/perf/builtin-kmem.c | 29 ++---
tools/perf/builtin-kwork.c | 4 +-
tools/perf/builtin-lock.c | 10 +-
tools/perf/builtin-mem.c | 2 +-
tools/perf/builtin-sched.c | 6 +-
tools/perf/builtin-script.c | 14 +--
tools/perf/builtin-timechart.c | 2 +-
tools/perf/builtin-top.c | 22 ++--
tools/perf/builtin-trace.c | 22 ++--
tools/perf/tests/dlfilter-test.c | 4 +-
tools/perf/tests/dwarf-unwind.c | 2 +-
tools/perf/tests/hists_common.c | 4 +-
tools/perf/tests/hists_cumulate.c | 4 +-
tools/perf/tests/hists_output.c | 2 +-
tools/perf/tests/symbols.c | 12 +-
tools/perf/tests/vmlinux-kallsyms.c | 34 +++---
tools/perf/ui/browsers/annotate.c | 10 +-
tools/perf/ui/browsers/hists.c | 10 +-
tools/perf/ui/browsers/map.c | 8 +-
tools/perf/ui/gtk/annotate.c | 6 +-
tools/perf/ui/stdio/hist.c | 3 +-
.../perf/util/annotate-arch/annotate-arm64.c | 12 +-
.../util/annotate-arch/annotate-loongarch.c | 4 +-
tools/perf/util/annotate-arch/annotate-x86.c | 14 +--
tools/perf/util/annotate-data.c | 12 +-
tools/perf/util/annotate.c | 69 ++++++------
tools/perf/util/auxtrace.c | 14 +--
tools/perf/util/block-info.c | 6 +-
tools/perf/util/bpf_lock_contention.c | 16 +--
tools/perf/util/c2c-function.c | 19 ++--
tools/perf/util/callchain.c | 19 ++--
tools/perf/util/capstone.c | 20 ++--
tools/perf/util/data-convert-json.c | 4 +-
tools/perf/util/db-export.c | 2 +-
tools/perf/util/debug.c | 3 +-
tools/perf/util/disasm.c | 49 +++++----
tools/perf/util/dlfilter.c | 12 +-
tools/perf/util/dso.c | 4 +-
tools/perf/util/event.c | 4 +-
tools/perf/util/evsel_fprintf.c | 2 +-
tools/perf/util/hist.c | 16 +--
tools/perf/util/intel-pt.c | 8 +-
tools/perf/util/libbfd.c | 4 +-
tools/perf/util/llvm.c | 6 +-
tools/perf/util/lock-contention.c | 2 +-
tools/perf/util/machine.c | 26 ++---
tools/perf/util/map.c | 6 +-
tools/perf/util/map.h | 2 +-
tools/perf/util/probe-event.c | 41 +++----
tools/perf/util/python.c | 18 +--
tools/perf/util/sort.c | 40 +++----
tools/perf/util/srcline.c | 16 +--
tools/perf/util/symbol-elf.c | 10 +-
tools/perf/util/symbol.c | 103 +++++++++---------
tools/perf/util/symbol.h | 43 +++++++-
tools/perf/util/symbol_fprintf.c | 14 +--
tools/perf/util/thread-stack.c | 8 +-
tools/perf/util/unwind-libdw.c | 2 +-
63 files changed, 478 insertions(+), 407 deletions(-)
diff --git a/tools/perf/arch/powerpc/util/skip-callchain-idx.c b/tools/perf/arch/powerpc/util/skip-callchain-idx.c
index 472714cfad38..94854f9fa440 100644
--- a/tools/perf/arch/powerpc/util/skip-callchain-idx.c
+++ b/tools/perf/arch/powerpc/util/skip-callchain-idx.c
@@ -239,7 +239,7 @@ int arch_skip_callchain_idx(struct thread *thread, struct ip_callchain *chain)
rc = check_return_addr(dso, map__map_ip(al.map, ip));
pr_debug("[DSO %s, sym %s, ip 0x%" PRIx64 "] rc %d\n",
- dso__long_name(dso), al.sym->name, ip, rc);
+ dso__long_name(dso), al.sym ? symbol__name(al.sym) : "unknown", ip, rc);
if (rc == 0) {
/*
diff --git a/tools/perf/arch/powerpc/util/sym-handling.c b/tools/perf/arch/powerpc/util/sym-handling.c
index 947bfad7aa59..d73bfdcdbec1 100644
--- a/tools/perf/arch/powerpc/util/sym-handling.c
+++ b/tools/perf/arch/powerpc/util/sym-handling.c
@@ -13,7 +13,7 @@
int arch__choose_best_symbol(struct symbol *syma,
struct symbol *symb __maybe_unused)
{
- char *sym = syma->name;
+ const char *sym = symbol__name(syma);
#if !defined(_CALL_ELF) || _CALL_ELF != 2
/* Skip over any initial dot */
@@ -69,7 +69,7 @@ const char *arch__normalize_symbol_name(const char *name)
#ifdef HAVE_LIBELF_SUPPORT
void arch__sym_update(struct symbol *s, GElf_Sym *sym)
{
- s->arch_sym = sym->st_other;
+ symbol__set_arch_sym(s, sym->st_other);
}
#endif
@@ -102,7 +102,7 @@ void arch__fix_tev_from_maps(struct perf_probe_event *pev,
return;
}
- lep_offset = PPC64_LOCAL_ENTRY_OFFSET(sym->arch_sym);
+ lep_offset = PPC64_LOCAL_ENTRY_OFFSET(symbol__arch_sym(sym));
if (map__dso(map)->symtab_type == DSO_BINARY_TYPE__KALLSYMS)
tev->point.offset += PPC64LE_LEP_OFFSET;
@@ -131,7 +131,7 @@ void arch__post_process_probe_trace_events(struct perf_probe_event *pev,
for (i = 0; i < ntevs; i++) {
tev = &pev->tevs[i];
map__for_each_symbol(map, sym, tmp) {
- if (map__unmap_ip(map, sym->start) == tev->point.address) {
+ if (map__unmap_ip(map, symbol__start(sym)) == tev->point.address) {
arch__fix_tev_from_maps(pev, tev, map, sym);
break;
}
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c
index 8863f3a570f6..c93b67986334 100644
--- a/tools/perf/builtin-annotate.c
+++ b/tools/perf/builtin-annotate.c
@@ -252,7 +252,7 @@ static int add_sample(struct perf_sample *sample,
if ((!ann->has_br_stack || !has_annotation(ann)) &&
ann->sym_hist_filter != NULL &&
(al->sym == NULL ||
- strcmp(ann->sym_hist_filter, al->sym->name) != 0)) {
+ strcmp(ann->sym_hist_filter, symbol__name(al->sym)) != 0)) {
/* We're only interested in a symbol named sym_hist_filter */
/*
* FIXME: why isn't this done in the symbol_filter when loading
@@ -423,7 +423,7 @@ static void hists__find_annotations(struct hists *hists,
goto find_next;
if (ann->sym_hist_filter &&
- (strcmp(he->ms.sym->name, ann->sym_hist_filter) != 0))
+ (strcmp(symbol__name(he->ms.sym), ann->sym_hist_filter) != 0))
goto find_next;
if (ann->min_percent) {
diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c
index 9fa8e900637b..c263b848153b 100644
--- a/tools/perf/builtin-diff.c
+++ b/tools/perf/builtin-diff.c
@@ -1358,10 +1358,10 @@ static int cycles_printf(struct hist_entry *he, struct hist_entry *pair,
bi = block_he->block_info;
- start_line = map__srcline(he->ms.map, bi->sym->start + bi->start,
+ start_line = map__srcline(he->ms.map, symbol__start(bi->sym) + bi->start,
he->ms.sym);
- end_line = map__srcline(he->ms.map, bi->sym->start + bi->end,
+ end_line = map__srcline(he->ms.map, symbol__start(bi->sym) + bi->end,
he->ms.sym);
if (start_line != SRCLINE_UNKNOWN &&
diff --git a/tools/perf/builtin-kallsyms.c b/tools/perf/builtin-kallsyms.c
index 3c4339982b16..e99f61c3653e 100644
--- a/tools/perf/builtin-kallsyms.c
+++ b/tools/perf/builtin-kallsyms.c
@@ -48,9 +48,10 @@ static int __cmd_kallsyms(int argc, const char **argv)
dso = map__dso(map);
printf("%s: %s %s %#" PRIx64 "-%#" PRIx64 " (%#" PRIx64 "-%#" PRIx64")\n",
- symbol->name, dso__short_name(dso), dso__long_name(dso),
- map__unmap_ip(map, symbol->start), map__unmap_ip(map, symbol->end),
- symbol->start, symbol->end);
+ symbol__name(symbol), dso__short_name(dso), dso__long_name(dso),
+ map__unmap_ip(map, symbol__start(symbol)),
+ map__unmap_ip(map, symbol__end(symbol)),
+ symbol__start(symbol), symbol__end(symbol));
}
out:
machine__delete(machine);
diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c
index 8693c6b135ca..7af3fd9868a1 100644
--- a/tools/perf/builtin-kmem.c
+++ b/tools/perf/builtin-kmem.c
@@ -310,7 +310,7 @@ static struct rb_root page_caller_sorted;
struct alloc_func {
u64 start;
u64 end;
- char *name;
+ const char *name;
};
static int nr_alloc_funcs;
@@ -368,7 +368,7 @@ static int build_alloc_func_list(void)
}
map__for_each_symbol(kernel_map, sym, node) {
- if (regexec(&alloc_func_regex, sym->name, 0, NULL, 0))
+ if (regexec(&alloc_func_regex, symbol__name(sym), 0, NULL, 0))
continue;
func = realloc(alloc_func_list,
@@ -376,10 +376,10 @@ static int build_alloc_func_list(void)
if (func == NULL)
return -ENOMEM;
- pr_debug("alloc func: %s\n", sym->name);
- func[nr_alloc_funcs].start = sym->start;
- func[nr_alloc_funcs].end = sym->end;
- func[nr_alloc_funcs].name = sym->name;
+ pr_debug("alloc func: %s\n", symbol__name(sym));
+ func[nr_alloc_funcs].start = symbol__start(sym);
+ func[nr_alloc_funcs].end = symbol__end(sym);
+ func[nr_alloc_funcs].name = symbol__name(sym);
alloc_func_list = func;
nr_alloc_funcs++;
@@ -1048,11 +1048,12 @@ static void __print_slab_result(struct rb_root *root,
} else
addr = data->ptr;
- if (sym != NULL)
- snprintf(buf, sizeof(buf), "%s+%" PRIx64 "", sym->name,
- addr - map__unmap_ip(map, sym->start));
- else
+ if (sym != NULL) {
+ snprintf(buf, sizeof(buf), "%s+%" PRIx64 "", symbol__name(sym),
+ addr - map__unmap_ip(map, symbol__start(sym)));
+ } else {
snprintf(buf, sizeof(buf), "%#" PRIx64 "", addr);
+ }
printf(" %-34s |", buf);
printf(" %9llu/%-5lu | %9llu/%-5lu | %8lu | %9lu | %6.3f%%\n",
@@ -1105,12 +1106,12 @@ static void __print_page_alloc_result(struct perf_session *session, int n_lines)
struct symbol *sym;
struct map *map;
char buf[32];
- char *caller = buf;
+ const char *caller = buf;
data = rb_entry(next, struct page_stat, node);
sym = machine__find_kernel_symbol(machine, data->callsite, &map);
if (sym)
- caller = sym->name;
+ caller = symbol__name(sym);
else
scnprintf(buf, sizeof(buf), "%"PRIx64, data->callsite);
@@ -1147,12 +1148,12 @@ static void __print_page_caller_result(struct perf_session *session, int n_lines
struct symbol *sym;
struct map *map;
char buf[32];
- char *caller = buf;
+ const char *caller = buf;
data = rb_entry(next, struct page_stat, node);
sym = machine__find_kernel_symbol(machine, data->callsite, &map);
if (sym)
- caller = sym->name;
+ caller = symbol__name(sym);
else
scnprintf(buf, sizeof(buf), "%"PRIx64, data->callsite);
diff --git a/tools/perf/builtin-kwork.c b/tools/perf/builtin-kwork.c
index fce588441e49..98714f1ed949 100644
--- a/tools/perf/builtin-kwork.c
+++ b/tools/perf/builtin-kwork.c
@@ -768,8 +768,8 @@ static void timehist_save_callchain(struct perf_kwork *kwork,
sym = node->ms.sym;
if (sym) {
- if (!strcmp(sym->name, "__softirqentry_text_start") ||
- !strcmp(sym->name, "__do_softirq"))
+ if (!strcmp(symbol__name(sym), "__softirqentry_text_start") ||
+ !strcmp(symbol__name(sym), "__do_softirq"))
symbol__set_ignore(sym, true);
}
diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c
index 778923c92f42..e52e3785b657 100644
--- a/tools/perf/builtin-lock.c
+++ b/tools/perf/builtin-lock.c
@@ -826,12 +826,12 @@ static int get_symbol_name_offset(struct map *map, struct symbol *sym, u64 ip,
return 0;
}
- offset = map__map_ip(map, ip) - sym->start;
+ offset = map__map_ip(map, ip) - symbol__start(sym);
if (offset)
- return scnprintf(buf, size, "%s+%#lx", sym->name, offset);
+ return scnprintf(buf, size, "%s+%#lx", symbol__name(sym), offset);
else
- return strlcpy(buf, sym->name, size);
+ return strlcpy(buf, symbol__name(sym), size);
}
static int lock_contention_caller(struct perf_sample *sample,
char *buf, int size)
@@ -1006,7 +1006,7 @@ static int report_lock_contention_begin_event(struct perf_sample *sample)
return -ENOMEM;
}
- addrs[filters.nr_addrs++] = map__unmap_ip(kmap, sym->start);
+ addrs[filters.nr_addrs++] = map__unmap_ip(kmap, symbol__start(sym));
filters.addrs = addrs;
}
}
@@ -1059,7 +1059,7 @@ static int report_lock_contention_begin_event(struct perf_sample *sample)
case LOCK_AGGR_ADDR:
sym = machine__find_kernel_symbol(machine, key, &kmap);
if (sym)
- name = sym->name;
+ name = symbol__name(sym);
break;
case LOCK_AGGR_CALLER:
name = buf;
diff --git a/tools/perf/builtin-mem.c b/tools/perf/builtin-mem.c
index ade53688ebcd..5a19be73348d 100644
--- a/tools/perf/builtin-mem.c
+++ b/tools/perf/builtin-mem.c
@@ -249,7 +249,7 @@ dump_raw_samples(const struct perf_tool *tool,
sample->data_src,
symbol_conf.field_sep,
dso ? dso__long_name(dso) : "???",
- al.sym ? al.sym->name : "???");
+ al.sym ? symbol__name(al.sym) : "???");
out_put:
addr_location__exit(&al);
return 0;
diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c
index 39571f03eda2..fba0b9194eee 100644
--- a/tools/perf/builtin-sched.c
+++ b/tools/perf/builtin-sched.c
@@ -2629,9 +2629,9 @@ static void save_task_callchain(struct perf_sched *sched,
sym = node->ms.sym;
if (sym) {
- if (!strcmp(sym->name, "schedule") ||
- !strcmp(sym->name, "__schedule") ||
- !strcmp(sym->name, "preempt_schedule"))
+ if (!strcmp(symbol__name(sym), "schedule") ||
+ !strcmp(symbol__name(sym), "__schedule") ||
+ !strcmp(symbol__name(sym), "preempt_schedule"))
symbol__set_ignore(sym, true);
}
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c
index b6691ebb1b4e..7559d4d230f2 100644
--- a/tools/perf/builtin-script.c
+++ b/tools/perf/builtin-script.c
@@ -1408,7 +1408,7 @@ static int ip__fprintf_sym(uint64_t addr, struct thread *thread,
addr_location__init(&al);
thread__find_map(thread, cpumode, addr, &al);
- if ((*lastsym) && al.addr >= (*lastsym)->start && al.addr < (*lastsym)->end)
+ if ((*lastsym) && al.addr >= symbol__start((*lastsym)) && al.addr < symbol__end((*lastsym)))
goto out;
al.cpu = cpu;
@@ -1419,11 +1419,11 @@ static int ip__fprintf_sym(uint64_t addr, struct thread *thread,
if (!al.sym)
goto out;
- if (al.addr < al.sym->end)
- off = al.addr - al.sym->start;
+ if (al.addr < symbol__end(al.sym))
+ off = al.addr - symbol__start(al.sym);
else
- off = al.addr - map__start(al.map) - al.sym->start;
- printed += fprintf(fp, "\t%s", al.sym->name);
+ off = al.addr - map__start(al.map) - symbol__start(al.sym);
+ printed += fprintf(fp, "\t%s", symbol__name(al.sym));
if (off)
printed += fprintf(fp, "%+d", off);
printed += fprintf(fp, ":");
@@ -1642,7 +1642,7 @@ static const char *resolve_branch_sym(struct perf_sample *sample,
if (!addr_al->thread)
thread__resolve(thread, addr_al, sample);
if (addr_al->sym)
- name = addr_al->sym->name;
+ name = symbol__name(addr_al->sym);
else
*ip = sample->addr;
} else {
@@ -1650,7 +1650,7 @@ static const char *resolve_branch_sym(struct perf_sample *sample,
}
} else if (sample->flags & (PERF_IP_FLAG_RETURN | PERF_IP_FLAG_TRACE_END)) {
if (al->sym)
- name = al->sym->name;
+ name = symbol__name(al->sym);
else
*ip = sample->ip;
}
diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c
index 9eb335d9db16..52db3522aa77 100644
--- a/tools/perf/builtin-timechart.c
+++ b/tools/perf/builtin-timechart.c
@@ -563,7 +563,7 @@ static char *cat_backtrace(struct perf_sample *sample,
addr_location__init(&tal);
tal.filtered = 0;
if (thread__find_symbol(al.thread, cpumode, ip, &tal))
- fprintf(f, "..... %016" PRIx64 " %s\n", ip, tal.sym->name);
+ fprintf(f, "..... %016" PRIx64 " %s\n", ip, symbol__name(tal.sym));
else
fprintf(f, "..... %016" PRIx64 "\n", ip);
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
index a3442d2d6edf..d85a49d546eb 100644
--- a/tools/perf/builtin-top.c
+++ b/tools/perf/builtin-top.c
@@ -133,8 +133,8 @@ static int perf_top__parse_source(struct perf_top *top, struct hist_entry *he)
* We can't annotate with just /proc/kallsyms
*/
if (dso__symtab_type(dso) == DSO_BINARY_TYPE__KALLSYMS && !dso__is_kcore(dso)) {
- pr_err("Can't annotate %s: No vmlinux file was found in the "
- "path\n", sym->name);
+ pr_err("Can't annotate %s: No vmlinux file was found in the path\n",
+ symbol__name(sym));
sleep(1);
return -1;
}
@@ -145,7 +145,7 @@ static int perf_top__parse_source(struct perf_top *top, struct hist_entry *he)
if (!symbol__hists(sym, evlist__nr_entries(top->evlist))) {
annotation__unlock(notes);
pr_err("Not enough memory for annotating '%s' symbol!\n",
- sym->name);
+ symbol__name(sym));
sleep(1);
return err;
}
@@ -156,7 +156,7 @@ static int perf_top__parse_source(struct perf_top *top, struct hist_entry *he)
} else {
char msg[BUFSIZ];
symbol__strerror_disassemble(&he->ms, err, msg, sizeof(msg));
- pr_err("Couldn't annotate %s: %s\n", sym->name, msg);
+ pr_err("Couldn't annotate %s: %s\n", symbol__name(sym), msg);
}
annotation__unlock(notes);
@@ -186,9 +186,9 @@ static void ui__warn_map_erange(struct map *map, struct symbol *sym, u64 ip)
"Not all samples will be on the annotation output.\n\n"
"Please report to linux-kernel@vger.kernel.org\n",
ip, dso__long_name(dso), dso__symtab_origin(dso),
- map__start(map), map__end(map), sym->start, sym->end,
+ map__start(map), map__end(map), symbol__start(sym), symbol__end(sym),
symbol__binding(sym) == STB_GLOBAL ? 'g' :
- symbol__binding(sym) == STB_LOCAL ? 'l' : 'w', sym->name,
+ symbol__binding(sym) == STB_LOCAL ? 'l' : 'w', symbol__name(sym),
err ? "[unknown]" : uts.machine,
err ? "[unknown]" : uts.release, perf_version_string);
if (use_browser <= 0)
@@ -232,7 +232,7 @@ static void perf_top__record_precise_ip(struct perf_top *top,
ui__warn_map_erange(he->ms.map, sym, ip);
else if (err == -ENOMEM) {
pr_err("Not enough memory for annotating '%s' symbol!\n",
- sym->name);
+ symbol__name(sym));
sleep(1);
}
@@ -263,7 +263,7 @@ static void perf_top__show_details(struct perf_top *top)
if (notes->src == NULL)
goto out_unlock;
- printf("Showing %s for %s\n", evsel__name(top->sym_evsel), symbol->name);
+ printf("Showing %s for %s\n", evsel__name(top->sym_evsel), symbol__name(symbol));
printf(" Events Pcnt (>=%d%%)\n", annotate_opts.min_pcnt);
more = hist_entry__annotate_printf(he, top->sym_evsel);
@@ -418,7 +418,7 @@ static void perf_top__prompt_symbol(struct perf_top *top, const char *msg)
next = rb_first_cached(&hists->entries);
while (next) {
n = rb_entry(next, struct hist_entry, rb_node);
- if (n->ms.sym && !strcmp(buf, n->ms.sym->name)) {
+ if (n->ms.sym && !strcmp(buf, symbol__name(n->ms.sym))) {
found = n;
break;
}
@@ -437,11 +437,11 @@ static void perf_top__prompt_symbol(struct perf_top *top, const char *msg)
static void perf_top__print_mapped_keys(struct perf_top *top)
{
- char *name = NULL;
+ const char *name = NULL;
if (top->sym_filter_entry) {
struct symbol *sym = top->sym_filter_entry->ms.sym;
- name = sym->name;
+ name = symbol__name(sym);
}
fprintf(stdout, "\nMapped keys:\n");
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c
index 4094141461f9..7683b362783a 100644
--- a/tools/perf/builtin-trace.c
+++ b/tools/perf/builtin-trace.c
@@ -789,15 +789,16 @@ size_t syscall_arg__scnprintf_ksym(char *bf, size_t size, struct syscall_arg *ar
arg->val, &map);
if (sym) {
- u64 start = map__unmap_ip(map, sym->start);
+ u64 start = map__unmap_ip(map, symbol__start(sym));
u64 offset = arg->val - start;
size_t printed;
- if (offset == 0)
- printed = scnprintf(bf, size, "%s", sym->name);
- else
+ if (offset == 0) {
+ printed = scnprintf(bf, size, "%s", symbol__name(sym));
+ } else {
printed = scnprintf(bf, size, "%s+0x%" PRIx64,
- sym->name, offset);
+ symbol__name(sym), offset);
+ }
map__put(map);
return printed;
}
@@ -3669,13 +3670,14 @@ static void print_location(FILE *f, struct perf_sample *sample,
if ((verbose > 0 || print_dso) && al->map)
fprintf(f, "%s@", dso__long_name(map__dso(al->map)));
- if ((verbose > 0 || print_sym) && al->sym)
- fprintf(f, "%s+0x%" PRIx64, al->sym->name,
- al->addr - al->sym->start);
- else if (al->map)
+ if ((verbose > 0 || print_sym) && al->sym) {
+ fprintf(f, "%s+0x%" PRIx64, symbol__name(al->sym),
+ al->addr - symbol__start(al->sym));
+ } else if (al->map) {
fprintf(f, "0x%" PRIx64, al->addr);
- else
+ } else {
fprintf(f, "0x%" PRIx64, sample->addr);
+ }
}
static int trace__pgfault(struct trace *trace,
diff --git a/tools/perf/tests/dlfilter-test.c b/tools/perf/tests/dlfilter-test.c
index 204663571943..b4649c79c309 100644
--- a/tools/perf/tests/dlfilter-test.c
+++ b/tools/perf/tests/dlfilter-test.c
@@ -274,11 +274,11 @@ static int get_ip_addr(struct test_data *td)
sym = map__find_symbol_by_name(map, "foo");
if (sym)
- td->foo = sym->start;
+ td->foo = symbol__start(sym);
sym = map__find_symbol_by_name(map, "bar");
if (sym)
- td->bar = sym->start;
+ td->bar = symbol__start(sym);
map__put(map);
diff --git a/tools/perf/tests/dwarf-unwind.c b/tools/perf/tests/dwarf-unwind.c
index 9ed78d00fb87..b8b0c430a6b3 100644
--- a/tools/perf/tests/dwarf-unwind.c
+++ b/tools/perf/tests/dwarf-unwind.c
@@ -56,7 +56,7 @@ int test__dwarf_unwind(struct test_suite *test, int subtest);
static int unwind_entry(struct unwind_entry *entry, void *arg)
{
unsigned long *cnt = (unsigned long *) arg;
- char *symbol = entry->ms.sym ? entry->ms.sym->name : NULL;
+ const char *symbol = entry->ms.sym ? symbol__name(entry->ms.sym) : NULL;
static const char *funcs[MAX_STACK] = {
"test__arch_unwind_sample",
"test_dwarf_unwind__thread",
diff --git a/tools/perf/tests/hists_common.c b/tools/perf/tests/hists_common.c
index 187f12f5bc21..0db3ea9cf62e 100644
--- a/tools/perf/tests/hists_common.c
+++ b/tools/perf/tests/hists_common.c
@@ -184,7 +184,7 @@ void print_hists_in(struct hists *hists)
pr_info("%2d: entry: %-8s [%-8s] %20s: period = %"PRIu64"\n",
i, thread__comm_str(he->thread),
dso__short_name(dso),
- he->ms.sym->name, he->stat.period);
+ symbol__name(he->ms.sym), he->stat.period);
}
i++;
@@ -213,7 +213,7 @@ void print_hists_out(struct hists *hists)
pr_info("%2d: entry: %8s:%5d [%-8s] %20s: period = %"PRIu64"/%"PRIu64"\n",
i, thread__comm_str(he->thread), thread__tid(he->thread),
dso__short_name(dso),
- he->ms.sym->name, he->stat.period,
+ symbol__name(he->ms.sym), he->stat.period,
he->stat_acc ? he->stat_acc->period : 0);
}
diff --git a/tools/perf/tests/hists_cumulate.c b/tools/perf/tests/hists_cumulate.c
index c4a11a3255bc..95b9a629e59e 100644
--- a/tools/perf/tests/hists_cumulate.c
+++ b/tools/perf/tests/hists_cumulate.c
@@ -164,11 +164,11 @@ typedef int (*test_fn_t)(struct evsel *, struct machine *);
#define COMM(he) (thread__comm_str(he->thread))
#define DSO(he) (dso__short_name(map__dso(he->ms.map)))
-#define SYM(he) (he->ms.sym->name)
+#define SYM(he) (symbol__name(he->ms.sym))
#define CPU(he) (he->cpu)
#define DEPTH(he) (he->callchain->max_depth)
#define CDSO(cl) (dso__short_name(map__dso(cl->ms.map)))
-#define CSYM(cl) (cl->ms.sym->name)
+#define CSYM(cl) (symbol__name(cl->ms.sym))
struct result {
u64 children;
diff --git a/tools/perf/tests/hists_output.c b/tools/perf/tests/hists_output.c
index 35d230a83e43..77fb3a187139 100644
--- a/tools/perf/tests/hists_output.c
+++ b/tools/perf/tests/hists_output.c
@@ -129,7 +129,7 @@ typedef int (*test_fn_t)(struct evsel *, struct machine *);
#define COMM(he) (thread__comm_str(he->thread))
#define DSO(he) (dso__short_name(map__dso(he->ms.map)))
-#define SYM(he) (he->ms.sym->name)
+#define SYM(he) (symbol__name(he->ms.sym))
#define CPU(he) (he->cpu)
#define PID(he) (thread__tid(he->thread))
diff --git a/tools/perf/tests/symbols.c b/tools/perf/tests/symbols.c
index b644d95c6be9..456ef7a70e28 100644
--- a/tools/perf/tests/symbols.c
+++ b/tools/perf/tests/symbols.c
@@ -130,14 +130,14 @@ static int test_dso(struct dso *dso)
continue;
/* Check for overlapping function symbols */
- if (last_sym && sym->start < last_sym->end) {
+ if (last_sym && symbol__start(sym) < symbol__end(last_sym)) {
pr_debug("Overlapping symbols:\n");
symbol__fprintf(last_sym, stderr);
symbol__fprintf(sym, stderr);
ret = TEST_FAIL;
}
/* Check for zero-length function symbol */
- if (sym->start == sym->end) {
+ if (symbol__start(sym) == symbol__end(sym)) {
pr_debug("Zero-length symbol:\n");
symbol__fprintf(sym, stderr);
ret = TEST_FAIL;
@@ -250,14 +250,14 @@ static int check_symbol(struct dso *dso, u64 addr, const char *name, u64 end)
{
struct symbol *sym = dso__find_symbol_nocache(dso, addr);
- if (!sym || strcmp(sym->name, name)) {
+ if (!sym || strcmp(symbol__name(sym), name)) {
pr_debug("%#" PRIx64 ": expected %s, got %s\n", addr, name,
- sym ? sym->name : "no symbol");
+ sym ? symbol__name(sym) : "no symbol");
return TEST_FAIL;
}
- if (sym->end != end) {
+ if (symbol__end(sym) != end) {
pr_debug("%s: expected end %#" PRIx64 ", got %#" PRIx64 "\n",
- name, end, sym->end);
+ name, end, symbol__end(sym));
return TEST_FAIL;
}
return TEST_OK;
diff --git a/tools/perf/tests/vmlinux-kallsyms.c b/tools/perf/tests/vmlinux-kallsyms.c
index 9396c8a77c86..9b6f2d836d8f 100644
--- a/tools/perf/tests/vmlinux-kallsyms.c
+++ b/tools/perf/tests/vmlinux-kallsyms.c
@@ -292,18 +292,19 @@ static int test__vmlinux_matches_kallsyms(struct test_suite *test __maybe_unused
sym = rb_entry(nd, struct symbol, rb_node);
- if (sym->start == sym->end)
+ if (symbol__start(sym) == symbol__end(sym))
continue;
- mem_start = map__unmap_ip(args.vmlinux_map, sym->start);
- mem_end = map__unmap_ip(args.vmlinux_map, sym->end);
+ mem_start = map__unmap_ip(args.vmlinux_map, symbol__start(sym));
+ mem_end = map__unmap_ip(args.vmlinux_map, symbol__end(sym));
first_pair = machine__find_kernel_symbol(&args.kallsyms, mem_start, NULL);
pair = first_pair;
- if (pair && UM(pair->start) == mem_start) {
+ if (pair && UM(symbol__start(pair)) == mem_start) {
next_pair:
- if (arch__compare_symbol_names(sym->name, pair->name) == 0) {
+ if (arch__compare_symbol_names(symbol__name(sym),
+ symbol__name(pair)) == 0) {
/*
* kallsyms don't have the symbol end, so we
* set that by using the next symbol start - 1,
@@ -313,11 +314,13 @@ static int test__vmlinux_matches_kallsyms(struct test_suite *test __maybe_unused
* off the real size. More than that and we
* _really_ have a problem.
*/
- s64 skew = mem_end - UM(pair->end);
- if (llabs(skew) >= page_size)
+ s64 skew = mem_end - UM(symbol__end(pair));
+
+ if (llabs(skew) >= page_size) {
pr_debug("WARN: %#" PRIx64 ": diff end addr for %s v: %#" PRIx64 " k: %#" PRIx64 "\n",
- mem_start, sym->name, mem_end,
- UM(pair->end));
+ mem_start, symbol__name(sym), mem_end,
+ UM(symbol__end(pair)));
+ }
/*
* Do not count this as a failure, because we
@@ -328,16 +331,17 @@ static int test__vmlinux_matches_kallsyms(struct test_suite *test __maybe_unused
continue;
} else {
pair = machine__find_kernel_symbol_by_name(&args.kallsyms,
- sym->name, NULL);
+ symbol__name(sym), NULL);
if (pair) {
- if (UM(pair->start) == mem_start)
+ if (UM(symbol__start(pair)) == mem_start)
goto next_pair;
pr_debug("WARN: %#" PRIx64 ": diff name v: %s k: %s\n",
- mem_start, sym->name, pair->name);
+ mem_start, symbol__name(sym), symbol__name(pair));
} else {
pr_debug("WARN: %#" PRIx64 ": diff name v: %s k: %s\n",
- mem_start, sym->name, first_pair->name);
+ mem_start, symbol__name(sym),
+ symbol__name(first_pair));
}
continue;
@@ -348,14 +352,14 @@ static int test__vmlinux_matches_kallsyms(struct test_suite *test __maybe_unused
* such as __indirect_thunk_end.
*/
continue;
- } else if (is_ignored_symbol(sym->name, symbol__type(sym))) {
+ } else if (is_ignored_symbol(symbol__name(sym), symbol__type(sym))) {
/*
* Ignore hidden symbols, see scripts/kallsyms.c for the details
*/
continue;
} else {
pr_debug("ERR : %#" PRIx64 ": %s not on kallsyms\n",
- mem_start, sym->name);
+ mem_start, symbol__name(sym));
}
err = -1;
diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c
index cf36b5347f72..66b7b42ea197 100644
--- a/tools/perf/ui/browsers/annotate.c
+++ b/tools/perf/ui/browsers/annotate.c
@@ -196,7 +196,7 @@ static void annotate_browser__draw_current_jump(struct ui_browser *browser)
int diff = 0;
/* PLT symbols contain external offsets */
- if (strstr(sym->name, "@plt"))
+ if (strstr(symbol__name(sym), "@plt"))
return;
if (!disasm_line__is_valid_local_jump(cursor, sym))
@@ -554,7 +554,7 @@ static void ui_browser__init_asm_mode(struct ui_browser *browser)
static int sym_title(struct symbol *sym, struct map *map, char *title,
size_t sz, int percent_type)
{
- return snprintf(title, sz, "%s %s [Percent: %s] %s", sym->name,
+ return snprintf(title, sz, "%s %s [Percent: %s] %s", symbol__name(sym),
dso__long_name(map__dso(map)),
percent_type_str(percent_type),
annotate_opts.code_with_type ? "[Type]" : "");
@@ -602,7 +602,7 @@ static bool annotate_browser__callq(struct annotate_browser *browser,
if (!symbol__hists(dl->ops.target.sym, evlist__nr_entries(evsel->evlist))) {
annotation__unlock(notes);
ui__warning("Not enough memory for annotating '%s' symbol!\n",
- dl->ops.target.sym->name);
+ symbol__name(dl->ops.target.sym));
return true;
}
@@ -876,7 +876,7 @@ static void annotate_browser__symbol_annotate_error(struct annotate_browser *bro
dso__set_annotate_warned(dso);
symbol__strerror_disassemble(ms, err, msg, sizeof(msg));
- ui__error("Couldn't annotate %s:\n%s", sym->name, msg);
+ ui__error("Couldn't annotate %s:\n%s", symbol__name(sym), msg);
}
static int annotate_browser__run(struct annotate_browser *browser,
@@ -1255,7 +1255,7 @@ int __hist_entry__tui_annotate(struct hist_entry *he, struct map_symbol *ms,
*/
if (al_addr != NO_ADDR) {
struct annotation_line *al = annotated_source__get_line(notes->src,
- al_addr - sym->start);
+ al_addr - symbol__start(sym));
browser.selection = al;
}
diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c
index 593e1fd5759d..206657039223 100644
--- a/tools/perf/ui/browsers/hists.c
+++ b/tools/perf/ui/browsers/hists.c
@@ -2548,7 +2548,7 @@ add_annotate_opt(struct popup_action *act, char **optstr,
if (ms->sym == NULL || symbol__annotation(ms->sym)->src == NULL)
return 0;
- if (asprintf(optstr, "Annotate %s", ms->sym->name) < 0)
+ if (asprintf(optstr, "Annotate %s", symbol__name(ms->sym)) < 0)
return 0;
map_symbol__copy(&act->ms, ms);
@@ -2748,7 +2748,7 @@ do_run_script(struct hist_browser *browser,
if (act->ms.thread)
len += strlen(thread__comm_str(act->ms.thread));
else if (act->ms.sym)
- len += strlen(act->ms.sym->name);
+ len += strlen(symbol__name(act->ms.sym));
script_opt = malloc(len);
if (!script_opt)
return -1;
@@ -2759,7 +2759,7 @@ do_run_script(struct hist_browser *browser,
thread__comm_str(act->ms.thread));
} else if (act->ms.sym) {
n = scnprintf(script_opt, len, " -S %s ",
- act->ms.sym->name);
+ symbol__name(act->ms.sym));
}
if (act->time) {
@@ -2804,7 +2804,7 @@ add_script_opt_2(struct popup_action *act, char **optstr,
return 0;
} else if (sym) {
if (asprintf(optstr, "Run scripts for samples of symbol [%s]%s",
- sym->name, tstr) < 0)
+ symbol__name(sym), tstr) < 0)
return 0;
} else {
if (asprintf(optstr, "Run scripts for all samples%s", tstr) < 0)
@@ -3208,7 +3208,7 @@ static int evsel__hists_browse(struct evsel *evsel, int nr_events, const char *h
ui_browser__warning(&browser->b, delay_secs * 2,
"No samples for the \"%s\" symbol.\n\n"
"Probably appeared just in a callchain",
- browser->selection->sym->name);
+ symbol__name(browser->selection->sym));
continue;
}
diff --git a/tools/perf/ui/browsers/map.c b/tools/perf/ui/browsers/map.c
index 075a575cdc5d..47f2743cae26 100644
--- a/tools/perf/ui/browsers/map.c
+++ b/tools/perf/ui/browsers/map.c
@@ -31,12 +31,12 @@ static void map_browser__write(struct ui_browser *browser, void *nd, int row)
ui_browser__set_percent_color(browser, 0, current_entry);
ui_browser__printf(browser, "%*" PRIx64 " %*" PRIx64 " %c ",
- mb->addrlen, sym->start, mb->addrlen, sym->end,
+ mb->addrlen, symbol__start(sym), mb->addrlen, symbol__end(sym),
symbol__binding(sym) == STB_GLOBAL ? 'g' :
symbol__binding(sym) == STB_LOCAL ? 'l' : 'w');
width = browser->width - ((mb->addrlen * 2) + 4);
if (width > 0)
- ui_browser__write_nstring(browser, sym->name, width);
+ ui_browser__write_nstring(browser, symbol__name(sym), width);
}
/* FIXME uber-kludgy, see comment on cmd_report... */
@@ -122,8 +122,8 @@ int map__browse(struct map *map)
for (nd = rb_first(mb.b.entries); nd; nd = rb_next(nd)) {
struct symbol *pos = rb_entry(nd, struct symbol, rb_node);
- if (maxaddr < pos->end)
- maxaddr = pos->end;
+ if (maxaddr < symbol__end(pos))
+ maxaddr = symbol__end(pos);
if (verbose > 0) {
u32 *idx = symbol__browser_index(pos);
*idx = mb.b.nr_entries;
diff --git a/tools/perf/ui/gtk/annotate.c b/tools/perf/ui/gtk/annotate.c
index 094dfa045b77..2307a7f609f8 100644
--- a/tools/perf/ui/gtk/annotate.c
+++ b/tools/perf/ui/gtk/annotate.c
@@ -67,7 +67,7 @@ static int perf_gtk__get_percent(char *buf, size_t size, struct symbol *sym,
static int perf_gtk__get_offset(char *buf, size_t size, struct map_symbol *ms,
struct disasm_line *dl)
{
- u64 start = map__rip_2objdump(ms->map, ms->sym->start);
+ u64 start = map__rip_2objdump(ms->map, symbol__start(ms->sym));
strcpy(buf, "");
@@ -192,7 +192,7 @@ static int symbol__gtk_annotate(struct map_symbol *ms, struct evsel *evsel,
dso__set_annotate_warned(dso);
symbol__strerror_disassemble(ms, err, msg, sizeof(msg));
- ui__error("Couldn't annotate %s: %s\n", sym->name, msg);
+ ui__error("Couldn't annotate %s: %s\n", symbol__name(sym), msg);
return -1;
}
@@ -234,7 +234,7 @@ static int symbol__gtk_annotate(struct map_symbol *ms, struct evsel *evsel,
}
scrolled_window = gtk_scrolled_window_new();
- tab_label = gtk_label_new(sym->name);
+ tab_label = gtk_label_new(symbol__name(sym));
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
GTK_POLICY_AUTOMATIC,
diff --git a/tools/perf/ui/stdio/hist.c b/tools/perf/ui/stdio/hist.c
index 8c4c8925df2c..f80cc73909ed 100644
--- a/tools/perf/ui/stdio/hist.c
+++ b/tools/perf/ui/stdio/hist.c
@@ -100,13 +100,12 @@ static struct callchain_list rem_hits;
static void init_rem_hits(void)
{
- rem_sq_bracket = malloc(sizeof(*rem_sq_bracket) + 6);
+ rem_sq_bracket = symbol__new(0, 0, 0, 0, "[...]");
if (!rem_sq_bracket) {
fprintf(stderr, "Not enough memory to display remaining hits\n");
return;
}
- strcpy(rem_sq_bracket->name, "[...]");
rem_hits.ms.sym = rem_sq_bracket;
}
diff --git a/tools/perf/util/annotate-arch/annotate-arm64.c b/tools/perf/util/annotate-arch/annotate-arm64.c
index 81369ae47290..5ad03c519a5d 100644
--- a/tools/perf/util/annotate-arch/annotate-arm64.c
+++ b/tools/perf/util/annotate-arch/annotate-arm64.c
@@ -477,12 +477,12 @@ static void update_call_insn_state(struct type_state *state,
Dwarf_Die type_die;
u32 insn_offset = dl->al.offset;
- call_name = func ? func->name : dl->ops.target.name;
+ call_name = func ? symbol__name(func) : dl->ops.target.name;
pr_debug_dtp("call [%x] %s\n", insn_offset, call_name ?: "<unknown>");
/* Invalidate caller-saved registers after call */
call_addr = map__rip_2objdump(dloc->ms->map,
- dloc->ms->sym->start + dl->al.offset);
+ symbol__start(dloc->ms->sym) + dl->al.offset);
for (unsigned int i = 0; i < ARRAY_SIZE(state->regs); i++) {
struct type_state_reg *reg = &state->regs[i];
@@ -640,7 +640,7 @@ static int propagate_load_reg_state(struct type_state *state,
tsr->lifetime_end = 0;
if (dloc->fb_cfa) {
- u64 ip = dloc->ms->sym->start + dl->al.offset;
+ u64 ip = symbol__start(dloc->ms->sym) + dl->al.offset;
u64 pc = map__rip_2objdump(dloc->ms->map, ip);
if (die_get_cfa(dloc->di->dbg, pc, &fbreg, &fboff) < 0)
@@ -727,7 +727,7 @@ static int propagate_load_reg_state(struct type_state *state,
/* Or check if it's a global variable */
if (src_tsr->kind == TSR_KIND_GLOBAL_ADDR) {
- u64 ip = dloc->ms->sym->start + dl->al.offset;
+ u64 ip = symbol__start(dloc->ms->sym) + dl->al.offset;
u64 addr = src_tsr->imm_value + reg_offset;
int offset;
u8 kind;
@@ -875,7 +875,7 @@ static void propagate_store_reg_state(struct type_state *state,
reg_offset += mem_spacing;
if (dloc->fb_cfa) {
- u64 ip = dloc->ms->sym->start + dl->al.offset;
+ u64 ip = symbol__start(dloc->ms->sym) + dl->al.offset;
u64 pc = map__rip_2objdump(dloc->ms->map, ip);
if (die_get_cfa(dloc->di->dbg, pc, &fbreg, &fboff) < 0)
@@ -1144,7 +1144,7 @@ static void update_mrs_insn_state(struct type_state *state,
return;
}
- ip = dloc->ms->sym->start + dl->al.offset;
+ ip = symbol__start(dloc->ms->sym) + dl->al.offset;
pc = map__rip_2objdump(dloc->ms->map, ip);
/*
diff --git a/tools/perf/util/annotate-arch/annotate-loongarch.c b/tools/perf/util/annotate-arch/annotate-loongarch.c
index a949b4392dd4..d275730080b7 100644
--- a/tools/perf/util/annotate-arch/annotate-loongarch.c
+++ b/tools/perf/util/annotate-arch/annotate-loongarch.c
@@ -94,8 +94,8 @@ static int loongarch_jump__parse(const struct arch *arch, struct ins_operands *o
ops->target.addr = strtoull(ops->raw, NULL, 16);
target.addr = map__objdump_2mem(map, ops->target.addr);
- start = map__unmap_ip(map, sym->start);
- end = map__unmap_ip(map, sym->end);
+ start = map__unmap_ip(map, symbol__start(sym));
+ end = map__unmap_ip(map, symbol__end(sym));
ops->target.outside = target.addr < start || target.addr >= end;
diff --git a/tools/perf/util/annotate-arch/annotate-x86.c b/tools/perf/util/annotate-arch/annotate-x86.c
index 3a6ea7e978e0..194fa9aff885 100644
--- a/tools/perf/util/annotate-arch/annotate-x86.c
+++ b/tools/perf/util/annotate-arch/annotate-x86.c
@@ -230,7 +230,7 @@ static void update_insn_state_x86(struct type_state *state,
/* Try to resolve the call target name */
if (func)
- call_name = func->name;
+ call_name = symbol__name(func);
else
call_name = dl->ops.target.name;
@@ -245,7 +245,7 @@ static void update_insn_state_x86(struct type_state *state,
/* Invalidate caller-saved registers after call */
call_addr = map__rip_2objdump(dloc->ms->map,
- dloc->ms->sym->start + dl->al.offset);
+ symbol__start(dloc->ms->sym) + dl->al.offset);
for (unsigned i = 0; i < ARRAY_SIZE(state->regs); i++) {
struct type_state_reg *reg = &state->regs[i];
@@ -277,7 +277,7 @@ static void update_insn_state_x86(struct type_state *state,
int offset;
const char *var_name = NULL;
struct map_symbol *ms = dloc->ms;
- u64 ip = ms->sym->start + dl->al.offset;
+ u64 ip = symbol__start(ms->sym) + dl->al.offset;
if (!has_reg_type(state, dst->reg1))
return;
@@ -478,7 +478,7 @@ static void update_insn_state_x86(struct type_state *state,
return;
if (dloc->fb_cfa) {
- u64 ip = dloc->ms->sym->start + dl->al.offset;
+ u64 ip = symbol__start(dloc->ms->sym) + dl->al.offset;
u64 pc = map__rip_2objdump(dloc->ms->map, ip);
if (die_get_cfa(dloc->di->dbg, pc, &fbreg, &fboff) < 0)
@@ -495,7 +495,7 @@ static void update_insn_state_x86(struct type_state *state,
if (dso__kernel(map__dso(dloc->ms->map)) &&
src->segment == INSN_SEG_X86_GS && src->imm) {
- u64 ip = dloc->ms->sym->start + dl->al.offset;
+ u64 ip = symbol__start(dloc->ms->sym) + dl->al.offset;
u64 var_addr;
int offset;
@@ -646,7 +646,7 @@ static void update_insn_state_x86(struct type_state *state,
/* Or check if it's a global variable */
else if (sreg == DWARF_REG_PC) {
struct map_symbol *ms = dloc->ms;
- u64 ip = ms->sym->start + dl->al.offset;
+ u64 ip = symbol__start(ms->sym) + dl->al.offset;
u64 addr;
int offset;
@@ -671,7 +671,7 @@ static void update_insn_state_x86(struct type_state *state,
/* And check percpu access with base register */
else if (has_reg_type(state, sreg) &&
state->regs[sreg].kind == TSR_KIND_PERCPU_BASE) {
- u64 ip = dloc->ms->sym->start + dl->al.offset;
+ u64 ip = symbol__start(dloc->ms->sym) + dl->al.offset;
u64 var_addr = src->offset;
int offset;
diff --git a/tools/perf/util/annotate-data.c b/tools/perf/util/annotate-data.c
index b3de1ca688a2..e430bb0ea043 100644
--- a/tools/perf/util/annotate-data.c
+++ b/tools/perf/util/annotate-data.c
@@ -887,9 +887,9 @@ bool get_global_var_info(struct data_loc_info *dloc, u64 addr,
sym = thread__find_symbol_fb(dloc->thread, dloc->cpumode,
mem_addr, &al);
if (sym) {
- *var_name = sym->name;
+ *var_name = symbol__name(sym);
/* Calculate type offset from the start of variable */
- *var_offset = mem_addr - map__unmap_ip(al.map, sym->start);
+ *var_offset = mem_addr - map__unmap_ip(al.map, symbol__start(sym));
} else {
*var_name = NULL;
}
@@ -1657,7 +1657,7 @@ static enum type_match_result find_data_type_insn(struct data_loc_info *dloc,
bb->begin->al.offset, bb->end->al.offset);
list_for_each_entry_from(dl, ¬es->src->source, al.node) {
- u64 this_ip = sym->start + dl->al.offset;
+ u64 this_ip = symbol__start(sym) + dl->al.offset;
u64 addr = map__rip_2objdump(dloc->ms->map, this_ip);
/* Skip comment or debug info lines */
@@ -1737,8 +1737,8 @@ static enum type_match_result find_data_type_block(struct data_loc_info *dloc,
}
pr_debug_dtp("cannot find a basic block from %"PRIx64" to %"PRIx64"\n",
- src_ip - dloc->ms->sym->start,
- dst_ip - dloc->ms->sym->start);
+ src_ip - symbol__start(dloc->ms->sym),
+ dst_ip - symbol__start(dloc->ms->sym));
continue;
}
prepend_basic_blocks(&this_blocks, &basic_blocks);
@@ -1817,7 +1817,7 @@ static int find_data_type_die(struct data_loc_info *dloc, Dwarf_Die *type_die)
pr_debug_dtp("-----------------------------------------------------------\n");
pr_debug_dtp("find data type for %s%#x(%s) at %s+%#"PRIx64"\n",
offset_sign, offset, buf,
- dloc->ms->sym->name, dloc->ip - dloc->ms->sym->start);
+ symbol__name(dloc->ms->sym), dloc->ip - symbol__start(dloc->ms->sym));
/*
* IP is a relative instruction address from the start of the map, as
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c
index 86a4beec6528..4163689b6b2c 100644
--- a/tools/perf/util/annotate.c
+++ b/tools/perf/util/annotate.c
@@ -237,18 +237,20 @@ static int __symbol__inc_addr_samples(struct map_symbol *ms,
pr_debug3("%s: addr=%#" PRIx64 "\n", __func__, map__unmap_ip(ms->map, addr));
- if ((addr < sym->start || addr >= sym->end) &&
- (addr != sym->end || sym->start != sym->end)) {
+ if ((addr < symbol__start(sym) || addr >= symbol__end(sym)) &&
+ (addr != symbol__end(sym) || symbol__start(sym) != symbol__end(sym))) {
pr_debug("%s(%d): ERANGE! sym->name=%s, start=%#" PRIx64 ", addr=%#" PRIx64 ", end=%#" PRIx64 "\n",
- __func__, __LINE__, sym->name, sym->start, addr, sym->end);
+ __func__, __LINE__, symbol__name(sym),
+ symbol__start(sym), addr, symbol__end(sym));
return -ERANGE;
}
- offset = addr - sym->start;
+ offset = addr - symbol__start(sym);
h = annotated_source__histogram(src, evsel);
if (h == NULL) {
pr_debug("%s(%d): ENOMEM! sym->name=%s, start=%#" PRIx64 ", addr=%#" PRIx64 ", end=%#" PRIx64 ", func: %d\n",
- __func__, __LINE__, sym->name, sym->start, addr, sym->end,
+ __func__, __LINE__, symbol__name(sym),
+ symbol__start(sym), addr, symbol__end(sym),
symbol__type(sym) == STT_FUNC);
return -ENOMEM;
}
@@ -288,7 +290,8 @@ static int __symbol__inc_addr_samples(struct map_symbol *ms,
pr_debug3("%#" PRIx64 " %s: period++ [addr: %#" PRIx64 ", %#" PRIx64
", evidx=%d] => nr_samples: %" PRIu64 ", period: %" PRIu64
" weight %" PRIu64 "\n",
- sym->start, sym->name, addr, addr - sym->start, evsel->core.idx,
+ symbol__start(sym), symbol__name(sym), addr,
+ addr - symbol__start(sym), evsel->core.idx,
entry->nr_samples, entry->period, weight);
return 0;
}
@@ -402,18 +405,18 @@ static int symbol__account_cycles(u64 addr, u64 start, struct symbol *sym,
branch = symbol__find_branch_hist(sym, evlist__nr_br_cntr(evsel->evlist));
if (!branch)
return -ENOMEM;
- if (addr < sym->start || addr >= sym->end)
+ if (addr < symbol__start(sym) || addr >= symbol__end(sym))
return -ERANGE;
if (start) {
- if (start < sym->start || start >= sym->end)
+ if (start < symbol__start(sym) || start >= symbol__end(sym))
return -ERANGE;
if (start >= addr)
start = 0;
}
- offset = addr - sym->start;
+ offset = addr - symbol__start(sym);
ret = __symbol__account_cycles(branch->cycles_hist,
- start ? start - sym->start : 0,
+ start ? start - symbol__start(sym) : 0,
offset, cycles,
!!start);
@@ -445,13 +448,13 @@ int addr_map_symbol__account_cycles(struct addr_map_symbol *ams,
if (start &&
(start->ms.sym == ams->ms.sym ||
(ams->ms.sym &&
- start->addr == ams->ms.sym->start + map__start(ams->ms.map))))
+ start->addr == symbol__start(ams->ms.sym) + map__start(ams->ms.map))))
saddr = start->al_addr;
if (saddr == 0)
pr_debug2("BB with bad start: addr %"PRIx64" start %"PRIx64" sym %"PRIx64" saddr %"PRIx64"\n",
ams->addr,
start ? start->addr : 0,
- ams->ms.sym ? ams->ms.sym->start + map__start(ams->ms.map) : 0,
+ ams->ms.sym ? symbol__start(ams->ms.sym) + map__start(ams->ms.map) : 0,
saddr);
err = symbol__account_cycles(ams->al_addr, saddr, ams->ms.sym, cycles, evsel, br_cntr);
if (err)
@@ -1165,9 +1168,9 @@ int symbol__annotate(struct map_symbol *ms, struct evsel *evsel,
notes->src->nr_events = nr ? nr : 1;
if (annotate_opts.full_addr)
- notes->src->start = map__objdump_2mem(ms->map, ms->sym->start);
+ notes->src->start = map__objdump_2mem(ms->map, symbol__start(ms->sym));
else
- notes->src->start = map__rip_2objdump(ms->map, ms->sym->start);
+ notes->src->start = map__rip_2objdump(ms->map, symbol__start(ms->sym));
return symbol__disassemble(sym, &args);
}
@@ -1307,9 +1310,10 @@ static void symbol__annotate_hits(struct symbol *sym, struct evsel *evsel)
struct sym_hist_entry *entry;
entry = annotated_source__hist_entry(notes->src, evsel, offset);
- if (entry && entry->nr_samples != 0)
+ if (entry && entry->nr_samples != 0) {
printf("%*" PRIx64 ": %" PRIu64 "\n", BITS_PER_LONG / 2,
- sym->start + offset, entry->nr_samples);
+ symbol__start(sym) + offset, entry->nr_samples);
+ }
}
printf("%*s: %" PRIu64 "\n", BITS_PER_LONG / 2, "h->nr_samples", h->nr_samples);
}
@@ -1521,7 +1525,7 @@ int map_symbol__annotation_dump(struct map_symbol *ms, struct evsel *evsel,
.evsel = evsel,
};
- if (asprintf(&filename, "%s.annotation", ms->sym->name) < 0)
+ if (asprintf(&filename, "%s.annotation", symbol__name(ms->sym)) < 0)
return -1;
fp = fopen(filename, "w");
@@ -1534,7 +1538,7 @@ int map_symbol__annotation_dump(struct map_symbol *ms, struct evsel *evsel,
}
fprintf(fp, "%s() %s\nEvent: %s\n\n",
- ms->sym->name, dso__long_name(map__dso(ms->map)), ev_name);
+ symbol__name(ms->sym), dso__long_name(map__dso(ms->map)), ev_name);
symbol__annotate_fprintf2(ms->sym, fp, &apd);
fclose(fp);
@@ -1629,7 +1633,7 @@ annotation__mark_jump_targets(struct annotation *notes, struct symbol *sym)
struct annotation_line *al;
/* PLT symbols contain external offsets */
- if (strstr(sym->name, "@plt"))
+ if (strstr(symbol__name(sym), "@plt"))
return;
list_for_each_entry(al, ¬es->src->source, node) {
@@ -1708,7 +1712,7 @@ annotation__init_column_widths(struct annotation *notes, struct symbol *sym)
{
notes->src->widths.addr = notes->src->widths.target =
notes->src->widths.min_addr = hex_width(symbol__size(sym));
- notes->src->widths.max_addr = hex_width(sym->end);
+ notes->src->widths.max_addr = hex_width(symbol__end(sym));
notes->src->widths.jumps = width_jumps(notes->src->max_jump_sources);
notes->src->widths.max_ins_name = annotation__max_ins_name(notes);
}
@@ -1733,9 +1737,9 @@ void annotation__toggle_full_addr(struct annotation *notes, struct map_symbol *m
annotate_opts.full_addr = !annotate_opts.full_addr;
if (annotate_opts.full_addr)
- notes->src->start = map__objdump_2mem(ms->map, ms->sym->start);
+ notes->src->start = map__objdump_2mem(ms->map, symbol__start(ms->sym));
else
- notes->src->start = map__rip_2objdump(ms->map, ms->sym->start);
+ notes->src->start = map__rip_2objdump(ms->map, symbol__start(ms->sym));
annotation__update_column_widths(notes);
}
@@ -1764,9 +1768,9 @@ static void annotation__calc_lines(struct annotation *notes, struct map_symbol *
if (percent_max <= 0.5)
continue;
- addr = map__rip_2objdump(ms->map, ms->sym->start);
+ addr = map__rip_2objdump(ms->map, symbol__start(ms->sym));
al->path = get_srcline(map__dso(ms->map), addr + al->offset, NULL,
- false, true, ms->sym->start + al->offset);
+ false, true, symbol__start(ms->sym) + al->offset);
insert_source_line(&tmp_root, al);
}
@@ -1800,7 +1804,7 @@ int hist_entry__tty_annotate2(struct hist_entry *he, struct evsel *evsel)
dso__set_annotate_warned(dso);
symbol__strerror_disassemble(ms, err, msg, sizeof(msg));
- ui__error("Couldn't annotate %s:\n%s", sym->name, msg);
+ ui__error("Couldn't annotate %s:\n%s", symbol__name(sym), msg);
return -1;
}
@@ -1812,7 +1816,8 @@ int hist_entry__tty_annotate2(struct hist_entry *he, struct evsel *evsel)
hists__scnprintf_title(hists, buf, sizeof(buf));
fprintf(stdout, "%s, [percent: %s]\n%s() %s\n",
- buf, percent_type_str(annotate_opts.percent_type), sym->name, dso__long_name(dso));
+ buf, percent_type_str(annotate_opts.percent_type),
+ symbol__name(sym), dso__long_name(dso));
symbol__annotate_fprintf2(sym, stdout, &apd);
annotated_source__purge(symbol__annotation(sym)->src);
@@ -1834,7 +1839,7 @@ int hist_entry__tty_annotate(struct hist_entry *he, struct evsel *evsel)
dso__set_annotate_warned(dso);
symbol__strerror_disassemble(ms, err, msg, sizeof(msg));
- ui__error("Couldn't annotate %s:\n%s", sym->name, msg);
+ ui__error("Couldn't annotate %s:\n%s", symbol__name(sym), msg);
return -1;
}
@@ -2682,7 +2687,7 @@ static struct disasm_line *find_disasm_line(struct symbol *sym, u64 ip,
if (dl->al.offset == -1)
continue;
- if (sym->start + dl->al.offset == ip) {
+ if (symbol__start(sym) + dl->al.offset == ip) {
/*
* llvm-objdump places "lock" in a separate line and
* in that case, we want to get the next line.
@@ -2818,7 +2823,7 @@ u64 annotate_calc_pcrel(struct map_symbol *ms, u64 ip, int offset,
*/
next = annotation__next_asm_line(notes, dl);
if (next == NULL)
- addr = ms->sym->end + offset;
+ addr = symbol__end(ms->sym) + offset;
else
addr = ip + (next->al.offset - dl->al.offset) + offset;
@@ -2880,7 +2885,7 @@ __hist_entry__get_data_type(struct hist_entry *he, const struct arch *arch,
.arch = arch,
.thread = he->thread,
.ms = ms,
- .ip = ms->sym->start + dl->al.offset,
+ .ip = symbol__start(ms->sym) + dl->al.offset,
.cpumode = he->cpumode,
.op = op_loc,
.di = dbg,
@@ -3134,7 +3139,7 @@ static bool process_basic_block(struct basic_block_data *bb_data,
if (dl->al.offset == -1)
continue;
/* Found the target instruction */
- if (sym->start + dl->al.offset == target) {
+ if (symbol__start(sym) + dl->al.offset == target) {
found = true;
break;
}
@@ -3151,7 +3156,7 @@ static bool process_basic_block(struct basic_block_data *bb_data,
if (dl->ops.target.outside)
break;
/* jump instruction creates new basic block(s) */
- next_dl = find_disasm_line(sym, sym->start + dl->ops.target.offset,
+ next_dl = find_disasm_line(sym, symbol__start(sym) + dl->ops.target.offset,
/*allow_update=*/false);
if (next_dl)
add_basic_block(bb_data, link, next_dl);
diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c
index 8bb491d389a2..b3cc7b17aca4 100644
--- a/tools/perf/util/auxtrace.c
+++ b/tools/perf/util/auxtrace.c
@@ -2707,7 +2707,7 @@ static bool dso_sym_match(struct symbol *sym, const char *name, int *cnt,
int idx)
{
/* Same name, and global or the n'th found or any */
- return !arch__compare_symbol_names(name, sym->name) &&
+ return !arch__compare_symbol_names(name, symbol__name(sym)) &&
((!idx && symbol__binding(sym) == STB_GLOBAL) ||
(idx > 0 && ++*cnt == idx) ||
idx < 0);
@@ -2725,14 +2725,14 @@ static void print_duplicate_syms(struct dso *dso, const char *sym_name)
while (sym) {
if (dso_sym_match(sym, sym_name, &cnt, -1)) {
pr_err("#%d\t0x%"PRIx64"\t%c\t%s\n",
- ++cnt, sym->start,
+ ++cnt, symbol__start(sym),
symbol__binding(sym) == STB_GLOBAL ? 'g' :
symbol__binding(sym) == STB_LOCAL ? 'l' : 'w',
- sym->name);
+ symbol__name(sym));
near = true;
} else if (near) {
near = false;
- pr_err("\t\twhich is near\t\t%s\n", sym->name);
+ pr_err("\t\twhich is near\t\t%s\n", symbol__name(sym));
}
sym = dso__next_symbol(sym);
}
@@ -2755,7 +2755,7 @@ static int find_dso_sym(struct dso *dso, const char *sym_name, u64 *start,
while (sym) {
if (*start) {
if (!*size)
- *size = sym->start - *start;
+ *size = symbol__start(sym) - *start;
if (idx > 0) {
if (*size)
return 0;
@@ -2764,8 +2764,8 @@ static int find_dso_sym(struct dso *dso, const char *sym_name, u64 *start,
return -EINVAL;
}
} else if (dso_sym_match(sym, sym_name, &cnt, idx)) {
- *start = sym->start;
- *size = sym->end - sym->start;
+ *start = symbol__start(sym);
+ *size = symbol__end(sym) - symbol__start(sym);
}
sym = dso__next_symbol(sym);
}
diff --git a/tools/perf/util/block-info.c b/tools/perf/util/block-info.c
index 1135e54f4c7f..f6ac71a74e6d 100644
--- a/tools/perf/util/block-info.c
+++ b/tools/perf/util/block-info.c
@@ -84,7 +84,7 @@ int64_t __block_info__cmp(struct hist_entry *left, struct hist_entry *right)
return 1;
}
- cmp = strcmp(bi_l->sym->name, bi_r->sym->name);
+ cmp = strcmp(symbol__name(bi_l->sym), symbol__name(bi_r->sym));
if (cmp)
return cmp;
@@ -305,10 +305,10 @@ static int block_range_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
symbol_conf.addr2line_disable_warn = true;
- start_line = map__srcline(he->ms.map, bi->sym->start + bi->start,
+ start_line = map__srcline(he->ms.map, symbol__start(bi->sym) + bi->start,
he->ms.sym);
- end_line = map__srcline(he->ms.map, bi->sym->start + bi->end,
+ end_line = map__srcline(he->ms.map, symbol__start(bi->sym) + bi->end,
he->ms.sym);
if (start_line != SRCLINE_UNKNOWN &&
diff --git a/tools/perf/util/bpf_lock_contention.c b/tools/perf/util/bpf_lock_contention.c
index c20bd075664e..2b799bf7a9a2 100644
--- a/tools/perf/util/bpf_lock_contention.c
+++ b/tools/perf/util/bpf_lock_contention.c
@@ -143,7 +143,7 @@ static void init_numa_data(struct lock_contention *con)
"contig_page_data",
&kmap);
if (sym) {
- skel->rodata->contig_page_data_addr = map__unmap_ip(kmap, sym->start);
+ skel->rodata->contig_page_data_addr = map__unmap_ip(kmap, symbol__start(sym));
map__put(kmap);
return;
}
@@ -159,7 +159,7 @@ static void init_numa_data(struct lock_contention *con)
if (sym == NULL)
return;
- skel->rodata->node_data_addr = map__unmap_ip(kmap, sym->start);
+ skel->rodata->node_data_addr = map__unmap_ip(kmap, symbol__start(sym));
map__put(kmap);
/* get the number of online nodes using the last node number + 1 */
@@ -266,7 +266,7 @@ int lock_contention_prepare(struct lock_contention *con)
continue;
}
- addrs[con->filters->nr_addrs++] = map__unmap_ip(kmap, sym->start);
+ addrs[con->filters->nr_addrs++] = map__unmap_ip(kmap, symbol__start(sym));
con->filters->addrs = addrs;
}
naddrs = con->filters->nr_addrs ?: has_mmap_lock;
@@ -288,7 +288,7 @@ int lock_contention_prepare(struct lock_contention *con)
continue;
}
- con->delays[i].addr = map__unmap_ip(kmap, sym->start);
+ con->delays[i].addr = map__unmap_ip(kmap, symbol__start(sym));
}
skel->rodata->lock_delay = 1;
bpf_map__set_max_entries(skel->maps.lock_delays, con->nr_delays);
@@ -608,7 +608,7 @@ static const char *lock_contention_get_name(struct lock_contention *con,
/* global locks with symbols */
sym = machine__find_kernel_symbol(machine, key->lock_addr_or_cgroup, &kmap);
if (sym)
- return sym->name;
+ return symbol__name(sym);
/* try semi-global locks collected separately */
if (!bpf_map_lookup_elem(lock_fd, &key->lock_addr_or_cgroup, &flags)) {
@@ -652,12 +652,12 @@ static const char *lock_contention_get_name(struct lock_contention *con,
if (sym) {
unsigned long offset;
- offset = map__map_ip(kmap, addr) - sym->start;
+ offset = map__map_ip(kmap, addr) - symbol__start(sym);
if (offset == 0)
- return sym->name;
+ return symbol__name(sym);
- snprintf(name_buf, sizeof(name_buf), "%s+%#lx", sym->name, offset);
+ snprintf(name_buf, sizeof(name_buf), "%s+%#lx", symbol__name(sym), offset);
} else {
snprintf(name_buf, sizeof(name_buf), "%#lx", (unsigned long)addr);
}
diff --git a/tools/perf/util/c2c-function.c b/tools/perf/util/c2c-function.c
index d410196d0429..b548c76e3016 100644
--- a/tools/perf/util/c2c-function.c
+++ b/tools/perf/util/c2c-function.c
@@ -966,7 +966,7 @@ c2c_function_hists__level1_entry(struct symbol *sym,
* true "function view" rather than a per-code-address view.
*/
u64 sym_start = (sym && detail_he->ms.map) ?
- map__unmap_ip(detail_he->ms.map, sym->start) : detail_he->ip;
+ map__unmap_ip(detail_he->ms.map, symbol__start(sym)) : detail_he->ip;
mi = mem_info__new();
if (!mi)
@@ -1058,8 +1058,8 @@ c2c_function_hists__level2_entry(struct c2c_hist_entry *level1_c2c,
/* Key by the function symbol start so all code addresses collapse. */
level2_c2c = c2c_child_entry__alloc(level1_he, detail_he, 1,
(sym && detail_he->ms.map) ?
- map__unmap_ip(detail_he->ms.map, sym->start) :
- hist_entry__iaddr(detail_he));
+ map__unmap_ip(detail_he->ms.map, symbol__start(sym)) :
+ hist_entry__iaddr(detail_he));
if (!level2_c2c)
return NULL;
@@ -1178,13 +1178,14 @@ static void c2c_he__resort_by_stores(struct hist_entry *parent_he)
/* Stable tie-break: instruction address, name, then cacheline. */
u64 a = hist_entry__iaddr(he), b = hist_entry__iaddr(iter);
- if (a != b)
+ if (a != b) {
cmp = a < b ? -1 : 1;
- else if (he->ms.sym && iter->ms.sym)
- cmp = strcmp(he->ms.sym->name,
- iter->ms.sym->name);
- else
+ } else if (he->ms.sym && iter->ms.sym) {
+ cmp = strcmp(symbol__name(he->ms.sym),
+ symbol__name(iter->ms.sym));
+ } else {
cmp = (iter->ms.sym ? 1 : 0) - (he->ms.sym ? 1 : 0);
+ }
if (!cmp) {
struct c2c_hist_entry *he_c2c;
@@ -1407,7 +1408,7 @@ static int c2c_function__ident_len(struct hist_entry *he)
* Match symbol_view_entry(): sort_sym adds the cpumode prefix and, in
* verbose mode, the address and DSO origin before the symbol name.
*/
- size = strlen(he->ms.sym->name) + 64;
+ size = strlen(symbol__name(he->ms.sym)) + 64;
symbuf = malloc(size);
if (!symbuf)
return size - 1;
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c
index 31c675cbab63..24c176905c0e 100644
--- a/tools/perf/util/callchain.c
+++ b/tools/perf/util/callchain.c
@@ -802,13 +802,15 @@ static enum match_result match_chain(struct callchain_cursor_node *node,
* on the symbol start address.
*/
if (symbol__inlined(cnode->ms.sym) || symbol__inlined(node->ms.sym)) {
- match = match_chain_strings(cnode->ms.sym->name,
- node->ms.sym->name);
+ match = match_chain_strings(symbol__name(cnode->ms.sym),
+ symbol__name(node->ms.sym));
if (match != MATCH_ERROR)
break;
} else {
- match = match_chain_dso_addresses(cnode->ms.map, cnode->ms.sym->start,
- node->ms.map, node->ms.sym->start);
+ match = match_chain_dso_addresses(cnode->ms.map,
+ symbol__start(cnode->ms.sym),
+ node->ms.map,
+ symbol__start(node->ms.sym));
break;
}
}
@@ -1247,13 +1249,14 @@ char *callchain_list__sym_name(struct callchain_list *cl,
if (cl->ms.sym) {
const char *inlined = symbol__inlined(cl->ms.sym) ? " (inlined)" : "";
- if (show_srcline && cl->srcline)
+ if (show_srcline && cl->srcline) {
printed = scnprintf(bf, bfsize, "%s %s%s",
- cl->ms.sym->name, cl->srcline,
+ symbol__name(cl->ms.sym), cl->srcline,
inlined);
- else
+ } else {
printed = scnprintf(bf, bfsize, "%s%s",
- cl->ms.sym->name, inlined);
+ symbol__name(cl->ms.sym), inlined);
+ }
} else
printed = scnprintf(bf, bfsize, "%#" PRIx64, cl->ip);
diff --git a/tools/perf/util/capstone.c b/tools/perf/util/capstone.c
index 95881f15324a..422a48385b78 100644
--- a/tools/perf/util/capstone.c
+++ b/tools/perf/util/capstone.c
@@ -333,7 +333,7 @@ static struct symbol *find_symbol_from_addr(struct map *map, u64 addr,
return NULL;
}
- *sym_offset = al_addr - sym->start;
+ *sym_offset = al_addr - symbol__start(sym);
return sym;
}
@@ -368,10 +368,10 @@ static void print_capstone_detail(struct cs_insn *insn, char *buf, size_t len,
if (sym_offset == 0) {
scnprintf(buf, len, "\t# %"PRIx64" <%s>",
- orig_addr, sym->name);
+ orig_addr, symbol__name(sym));
} else {
scnprintf(buf, len, "\t# %"PRIx64" <%s+%#"PRIx64">",
- orig_addr, sym->name, sym_offset);
+ orig_addr, symbol__name(sym), sym_offset);
}
map__put(found_map);
break;
@@ -454,10 +454,10 @@ static void format_capstone_insn_arm64(struct cs_insn *insn, char *buf,
len = len - (last_imm - buf);
if (sym_offset == 0) {
scnprintf(last_imm, len, "%"PRIx64" <%s>",
- addr, sym->name);
+ addr, symbol__name(sym));
} else {
scnprintf(last_imm, len, "%"PRIx64" <%s+%#"PRIx64">",
- addr, sym->name, sym_offset);
+ addr, symbol__name(sym), sym_offset);
}
map__put(found_map);
}
@@ -498,7 +498,7 @@ int symbol__disassemble_capstone(const char *filename, struct symbol *sym,
struct annotation *notes = symbol__annotation(sym);
struct map *map = args->ms->map;
struct dso *dso = map__dso(map);
- u64 start = map__rip_2objdump(map, sym->start);
+ u64 start = map__rip_2objdump(map, symbol__start(sym));
u64 offset;
int i, count, free_count;
bool is_64bit = false;
@@ -526,7 +526,7 @@ int symbol__disassemble_capstone(const char *filename, struct symbol *sym,
/* add the function address and name */
scnprintf(disasm_buf, sizeof(disasm_buf), "%#"PRIx64" <%s>:",
- start, sym->name);
+ start, symbol__name(sym));
args->offset = -1;
args->line = disasm_buf;
@@ -631,8 +631,8 @@ int symbol__disassemble_capstone_powerpc(const char *filename __maybe_unused,
struct map *map = args->ms->map;
struct dso *dso = map__dso(map);
struct nscookie nsc;
- u64 start = map__rip_2objdump(map, sym->start);
- u64 end = map__rip_2objdump(map, sym->end);
+ u64 start = map__rip_2objdump(map, symbol__start(sym));
+ u64 end = map__rip_2objdump(map, symbol__end(sym));
u64 len = end - start;
u64 offset;
int i, fd, count;
@@ -703,7 +703,7 @@ int symbol__disassemble_capstone_powerpc(const char *filename __maybe_unused,
/* add the function address and name */
scnprintf(disasm_buf, sizeof(disasm_buf), "%#"PRIx64" <%s>:",
- start, sym->name);
+ start, symbol__name(sym));
args->offset = -1;
args->line = disasm_buf;
diff --git a/tools/perf/util/data-convert-json.c b/tools/perf/util/data-convert-json.c
index 40888b7c4467..c09bd77f5665 100644
--- a/tools/perf/util/data-convert-json.c
+++ b/tools/perf/util/data-convert-json.c
@@ -138,11 +138,11 @@ static void output_sample_callchain_entry(const struct perf_tool *tool,
output_json_format(out, false, 4, "{");
output_json_key_format(out, false, 5, "ip", "\"0x%" PRIx64 "\"", ip);
- if (al && al->sym && al->sym->namelen) {
+ if (al && al->sym && symbol__namelen(al->sym)) {
struct dso *dso = al->map ? map__dso(al->map) : NULL;
fputc(',', out);
- output_json_key_string(out, false, 5, "symbol", al->sym->name);
+ output_json_key_string(out, false, 5, "symbol", symbol__name(al->sym));
if (dso) {
const char *dso_name = dso__short_name(dso);
diff --git a/tools/perf/util/db-export.c b/tools/perf/util/db-export.c
index ba54b1119ee6..732369fe0046 100644
--- a/tools/perf/util/db-export.c
+++ b/tools/perf/util/db-export.c
@@ -198,7 +198,7 @@ static int db_ids_from_al(struct db_export *dbe, struct addr_location *al,
if (err)
return err;
*sym_db_id = *db_id;
- *offset = al->addr - al->sym->start;
+ *offset = al->addr - symbol__start(al->sym);
}
}
diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c
index b7095519419e..eec3fc14df98 100644
--- a/tools/perf/util/debug.c
+++ b/tools/perf/util/debug.c
@@ -342,7 +342,8 @@ void __dump_stack(FILE *file, void **stackdump, size_t stackdump_size)
if (thread && thread__find_map(thread, PERF_RECORD_MISC_USER, addr, &al)) {
al.sym = map__find_symbol(al.map, al.addr);
if (al.sym) {
- fprintf(file, " #%zd %p in %s ", i, stackdump[i], al.sym->name);
+ fprintf(file, " #%zd %p in %s ", i, stackdump[i],
+ symbol__name(al.sym));
printed = true;
}
}
diff --git a/tools/perf/util/disasm.c b/tools/perf/util/disasm.c
index 7c289b67b5bd..9b3e476bab6d 100644
--- a/tools/perf/util/disasm.c
+++ b/tools/perf/util/disasm.c
@@ -311,8 +311,10 @@ static int call__parse(const struct arch *arch, struct ins_operands *ops, struct
int call__scnprintf(const struct ins *ins, char *bf, size_t size,
struct ins_operands *ops, int max_ins_name)
{
- if (ops->target.sym)
- return scnprintf(bf, size, "%-*s %s", max_ins_name, ins->name, ops->target.sym->name);
+ if (ops->target.sym) {
+ return scnprintf(bf, size, "%-*s %s", max_ins_name, ins->name,
+ symbol__name(ops->target.sym));
+ }
if (ops->target.addr == 0)
return ins__raw_scnprintf(ins, bf, size, ops, max_ins_name);
@@ -399,8 +401,8 @@ static int jump__parse(const struct arch *arch, struct ins_operands *ops, struct
}
target.addr = map__objdump_2mem(map, ops->target.addr);
- start = map__unmap_ip(map, sym->start);
- end = map__unmap_ip(map, sym->end);
+ start = map__unmap_ip(map, symbol__start(sym));
+ end = map__unmap_ip(map, symbol__end(sym));
ops->target.outside = target.addr < start || target.addr >= end;
@@ -444,8 +446,10 @@ int jump__scnprintf(const struct ins *ins, char *bf, size_t size,
if (!ops->target.addr || ops->target.offset < 0)
return ins__raw_scnprintf(ins, bf, size, ops, max_ins_name);
- if (ops->target.outside && ops->target.sym != NULL)
- return scnprintf(bf, size, "%-*s %s", max_ins_name, ins->name, ops->target.sym->name);
+ if (ops->target.outside && ops->target.sym != NULL) {
+ return scnprintf(bf, size, "%-*s %s", max_ins_name, ins->name,
+ symbol__name(ops->target.sym));
+ }
c = strchr(ops->raw, ',');
c = validate_comma(c, ops);
@@ -1050,8 +1054,8 @@ static int symbol__parse_objdump_line(struct symbol *sym,
/* Process hex address followed by ':'. */
line_ip = strtoull(parsed_line, &tmp, 16);
if (parsed_line != tmp && tmp[0] == ':' && tmp[1] != '\0') {
- u64 start = map__rip_2objdump(map, sym->start),
- end = map__rip_2objdump(map, sym->end);
+ u64 start = map__rip_2objdump(map, symbol__start(sym)),
+ end = map__rip_2objdump(map, symbol__end(sym));
offset = line_ip - start;
if ((u64)line_ip < start || (u64)line_ip >= end)
@@ -1074,7 +1078,7 @@ static int symbol__parse_objdump_line(struct symbol *sym,
if (!disasm_line__has_local_offset(dl)) {
dl->ops.target.offset = dl->ops.target.addr -
- map__rip_2objdump(map, sym->start);
+ map__rip_2objdump(map, symbol__start(sym));
dl->ops.target.offset_avail = true;
}
@@ -1086,7 +1090,7 @@ static int symbol__parse_objdump_line(struct symbol *sym,
};
if (!maps__find_ams(thread__maps(args->ms->thread), &target) &&
- target.ms.sym->start == target.al_addr)
+ symbol__start(target.ms.sym) == target.al_addr)
dl->ops.target.sym = target.ms.sym;
addr_map_symbol__exit(&target);
@@ -1182,8 +1186,8 @@ static int symbol__disassemble_raw(char *filename, struct symbol *sym,
struct annotation *notes = symbol__annotation(sym);
struct map *map = args->ms->map;
struct dso *dso = map__dso(map);
- u64 start = map__rip_2objdump(map, sym->start);
- u64 end = map__rip_2objdump(map, sym->end);
+ u64 start = map__rip_2objdump(map, symbol__start(sym));
+ u64 end = map__rip_2objdump(map, symbol__end(sym));
u64 len = end - start;
u64 offset;
int i, count;
@@ -1202,7 +1206,7 @@ static int symbol__disassemble_raw(char *filename, struct symbol *sym,
if (buf == NULL)
goto err;
- count = dso__data_read_offset(dso, NULL, sym->start, buf, len);
+ count = dso__data_read_offset(dso, NULL, symbol__start(sym), buf, len);
line = (u32 *)buf;
@@ -1211,7 +1215,7 @@ static int symbol__disassemble_raw(char *filename, struct symbol *sym,
/* add the function address and name */
scnprintf(disasm_buf, sizeof(disasm_buf), "%#"PRIx64" <%s>:",
- start, sym->name);
+ start, symbol__name(sym));
args->offset = -1;
args->line = disasm_buf;
@@ -1376,8 +1380,8 @@ static int symbol__disassemble_objdump(const char *filename, struct symbol *sym,
opts->objdump_path ?: "objdump",
opts->disassembler_style ? "-M " : "",
opts->disassembler_style ?: "",
- map__rip_2objdump(map, sym->start),
- map__rip_2objdump(map, sym->end),
+ map__rip_2objdump(map, symbol__start(sym)),
+ map__rip_2objdump(map, symbol__end(sym)),
opts->show_linenr ? "-l" : "",
opts->show_asm_raw ? "" : "--no-show-raw-insn",
opts->annotate_src ? "-S" : "",
@@ -1513,16 +1517,17 @@ int symbol__disassemble(struct symbol *sym, struct annotate_args *args)
}
pr_debug("%s: filename=%s, sym=%s, start=%#" PRIx64 ", end=%#" PRIx64 "\n", __func__,
- symfs_filename, sym->name, map__unmap_ip(map, sym->start),
- map__unmap_ip(map, sym->end));
+ symfs_filename, symbol__name(sym), map__unmap_ip(map, symbol__start(sym)),
+ map__unmap_ip(map, symbol__end(sym)));
- pr_debug("annotating [%p] %30s : [%p] %30s\n", dso, dso__long_name(dso), sym, sym->name);
+ pr_debug("annotating [%p] %30s : [%p] %30s\n", dso, dso__long_name(dso), sym,
+ symbol__name(sym));
if (dso__is_kcore(dso)) {
- kce.addr = map__rip_2objdump(map, sym->start);
+ kce.addr = map__rip_2objdump(map, symbol__start(sym));
kce.kcore_filename = symfs_filename;
- kce.len = sym->end - sym->start;
- kce.offs = sym->start;
+ kce.len = symbol__end(sym) - symbol__start(sym);
+ kce.offs = symbol__start(sym);
if (!kcore_extract__create(&kce)) {
delete_extract = true;
diff --git a/tools/perf/util/dlfilter.c b/tools/perf/util/dlfilter.c
index e11e144af62b..3b72cb7b412e 100644
--- a/tools/perf/util/dlfilter.c
+++ b/tools/perf/util/dlfilter.c
@@ -47,13 +47,13 @@ static void al_to_d_al(struct addr_location *al, struct perf_dlfilter_al *d_al)
d_al->buildid = NULL;
}
if (sym) {
- d_al->sym = sym->name;
- d_al->sym_start = sym->start;
- d_al->sym_end = sym->end;
- if (al->addr < sym->end)
- d_al->symoff = al->addr - sym->start;
+ d_al->sym = symbol__name(sym);
+ d_al->sym_start = symbol__start(sym);
+ d_al->sym_end = symbol__end(sym);
+ if (al->addr < symbol__end(sym))
+ d_al->symoff = al->addr - symbol__start(sym);
else if (al->map)
- d_al->symoff = al->addr - map__start(al->map) - sym->start;
+ d_al->symoff = al->addr - map__start(al->map) - symbol__start(sym);
else
d_al->symoff = 0;
d_al->sym_binding = symbol__binding(sym);
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c
index ce98613868e7..39a1eb9cb73f 100644
--- a/tools/perf/util/dso.c
+++ b/tools/perf/util/dso.c
@@ -2147,8 +2147,8 @@ const u8 *dso__read_symbol(struct dso *dso, const char *symfs_filename,
const struct map *map, const struct symbol *sym,
u8 **out_buf, u64 *out_buf_len, bool *is_64bit)
{
- u64 start = map__rip_2objdump(map, sym->start);
- u64 end = map__rip_2objdump(map, sym->end);
+ u64 start = map__rip_2objdump(map, symbol__start(sym));
+ u64 end = map__rip_2objdump(map, symbol__end(sym));
size_t len = end - start;
*out_buf = NULL;
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c
index ea75816d126a..939307796a88 100644
--- a/tools/perf/util/event.c
+++ b/tools/perf/util/event.c
@@ -884,11 +884,11 @@ int machine__resolve(struct machine *machine, struct addr_location *al,
if (al->sym) {
ret = strlist__has_entry(symbol_conf.sym_list,
- al->sym->name);
+ symbol__name(al->sym));
}
if (!ret && al->sym) {
snprintf(al_addr_str, sz, "0x%"PRIx64,
- map__unmap_ip(al->map, al->sym->start));
+ map__unmap_ip(al->map, symbol__start(al->sym)));
ret = strlist__has_entry(symbol_conf.sym_list,
al_addr_str);
}
diff --git a/tools/perf/util/evsel_fprintf.c b/tools/perf/util/evsel_fprintf.c
index 0f7a25500a44..76a0c69e6f45 100644
--- a/tools/perf/util/evsel_fprintf.c
+++ b/tools/perf/util/evsel_fprintf.c
@@ -200,7 +200,7 @@ int sample__fprintf_callchain(struct perf_sample *sample, int left_alignment,
/* Add srccode here too? */
if (bt_stop_list && sym &&
- strlist__has_entry(bt_stop_list, sym->name)) {
+ strlist__has_entry(bt_stop_list, symbol__name(sym))) {
break;
}
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c
index e75a4fcce975..8a1a5e36d7cd 100644
--- a/tools/perf/util/hist.c
+++ b/tools/perf/util/hist.c
@@ -97,7 +97,7 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
* +3 accounts for ' y ' symtab origin info
*/
if (h->ms.sym) {
- symlen = h->ms.sym->namelen + 4;
+ symlen = symbol__namelen(h->ms.sym) + 4;
if (verbose > 0)
symlen += BITS_PER_LONG / 4 + 2 + 3;
hists__new_col_len(hists, HISTC_SYMBOL, symlen);
@@ -120,11 +120,11 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
}
if (h->parent)
- hists__new_col_len(hists, HISTC_PARENT, h->parent->namelen);
+ hists__new_col_len(hists, HISTC_PARENT, symbol__namelen(h->parent));
if (h->branch_info) {
if (h->branch_info->from.ms.sym) {
- symlen = (int)h->branch_info->from.ms.sym->namelen + 4;
+ symlen = (int) symbol__namelen(h->branch_info->from.ms.sym) + 4;
if (verbose > 0)
symlen += BITS_PER_LONG / 4 + 2 + 3;
hists__new_col_len(hists, HISTC_SYMBOL_FROM, symlen);
@@ -139,7 +139,7 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
}
if (h->branch_info->to.ms.sym) {
- symlen = (int)h->branch_info->to.ms.sym->namelen + 4;
+ symlen = (int) symbol__namelen(h->branch_info->to.ms.sym) + 4;
if (verbose > 0)
symlen += BITS_PER_LONG / 4 + 2 + 3;
hists__new_col_len(hists, HISTC_SYMBOL_TO, symlen);
@@ -163,7 +163,7 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
if (h->mem_info) {
if (mem_info__daddr(h->mem_info)->ms.sym) {
- symlen = (int)mem_info__daddr(h->mem_info)->ms.sym->namelen + 4
+ symlen = (int) symbol__namelen(mem_info__daddr(h->mem_info)->ms.sym) + 4
+ unresolved_col_width + 2;
hists__new_col_len(hists, HISTC_MEM_DADDR_SYMBOL,
symlen);
@@ -178,7 +178,7 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
}
if (mem_info__iaddr(h->mem_info)->ms.sym) {
- symlen = (int)mem_info__iaddr(h->mem_info)->ms.sym->namelen + 4
+ symlen = (int) symbol__namelen(mem_info__iaddr(h->mem_info)->ms.sym) + 4
+ unresolved_col_width + 2;
hists__new_col_len(hists, HISTC_MEM_IADDR_SYMBOL,
symlen);
@@ -727,7 +727,7 @@ static struct hist_entry *hists__findnew_entry(struct hists *hists,
*/
if (hists__has(hists, sym) && he->ms.map != entry->ms.map) {
if (he->ms.sym) {
- u64 addr = he->ms.sym->start;
+ u64 addr = symbol__start(he->ms.sym);
he->ms.sym = map__find_symbol(entry->ms.map, addr);
}
@@ -2300,7 +2300,7 @@ static bool hists__filter_entry_by_symbol(struct hists *hists,
struct hist_entry *he)
{
if (hists->symbol_filter_str != NULL &&
- (!he->ms.sym || strstr(he->ms.sym->name,
+ (!he->ms.sym || strstr(symbol__name(he->ms.sym),
hists->symbol_filter_str) == NULL)) {
he->filtered |= (1 << HIST_FILTER__SYMBOL);
return true;
diff --git a/tools/perf/util/intel-pt.c b/tools/perf/util/intel-pt.c
index 8c21c9f52d57..a9f43ac8d152 100644
--- a/tools/perf/util/intel-pt.c
+++ b/tools/perf/util/intel-pt.c
@@ -3003,8 +3003,8 @@ static u64 intel_pt_switch_ip(struct intel_pt *pt, u64 *ptss_ip)
for (sym = start; sym; sym = dso__next_symbol(sym)) {
if (symbol__binding(sym) == STB_GLOBAL &&
- !strcmp(sym->name, "__switch_to")) {
- ip = map__unmap_ip(map, sym->start);
+ !strcmp(symbol__name(sym), "__switch_to")) {
+ ip = map__unmap_ip(map, symbol__start(sym));
if (ip >= map__start(map) && ip < map__end(map)) {
switch_ip = ip;
break;
@@ -3021,8 +3021,8 @@ static u64 intel_pt_switch_ip(struct intel_pt *pt, u64 *ptss_ip)
ptss = "__perf_event_task_sched_out";
for (sym = start; sym; sym = dso__next_symbol(sym)) {
- if (!strcmp(sym->name, ptss)) {
- ip = map__unmap_ip(map, sym->start);
+ if (!strcmp(symbol__name(sym), ptss)) {
+ ip = map__unmap_ip(map, symbol__start(sym));
if (ip >= map__start(map) && ip < map__end(map)) {
*ptss_ip = ip;
break;
diff --git a/tools/perf/util/libbfd.c b/tools/perf/util/libbfd.c
index 992e2c2a7198..1a5d38b8e5f4 100644
--- a/tools/perf/util/libbfd.c
+++ b/tools/perf/util/libbfd.c
@@ -552,7 +552,7 @@ int symbol__disassemble_bpf_libbfd(struct symbol *sym __maybe_unused,
struct annotation *notes = symbol__annotation(sym);
struct bpf_prog_linfo *prog_linfo = NULL;
struct bpf_prog_info_node *info_node;
- int len = sym->end - sym->start;
+ int len = symbol__end(sym) - symbol__start(sym);
disassembler_ftype disassemble;
struct map *map = args->ms->map;
struct perf_bpil *info_linear;
@@ -572,7 +572,7 @@ int symbol__disassemble_bpf_libbfd(struct symbol *sym __maybe_unused,
return SYMBOL_ANNOTATE_ERRNO__BPF_INVALID_FILE;
pr_debug("%s: handling sym %s addr %" PRIx64 " len %" PRIx64 "\n", __func__,
- sym->name, sym->start, sym->end - sym->start);
+ symbol__name(sym), symbol__start(sym), symbol__end(sym) - symbol__start(sym));
memset(tpath, 0, sizeof(tpath));
perf_exe(tpath, sizeof(tpath));
diff --git a/tools/perf/util/llvm.c b/tools/perf/util/llvm.c
index 0ecb1a53359e..7429e8ef6cce 100644
--- a/tools/perf/util/llvm.c
+++ b/tools/perf/util/llvm.c
@@ -138,7 +138,7 @@ int symbol__disassemble_llvm(const char *filename, struct symbol *sym,
struct annotation *notes = symbol__annotation(sym);
struct map *map = args->ms->map;
struct dso *dso = map__dso(map);
- u64 start = map__rip_2objdump(map, sym->start);
+ u64 start = map__rip_2objdump(map, symbol__start(sym));
/* Malloc-ed buffer containing instructions read from disk. */
u8 *code_buf = NULL;
/* Pointer to code to be disassembled. */
@@ -202,7 +202,7 @@ int symbol__disassemble_llvm(const char *filename, struct symbol *sym,
/* add the function address and name */
scnprintf(disasm_buf, sizeof(disasm_buf), "%#"PRIx64" <%s>:",
- start, sym->name);
+ start, symbol__name(sym));
args->offset = -1;
args->line = disasm_buf;
@@ -323,7 +323,7 @@ int symbol__disassemble_llvm(const char *filename, struct symbol *sym,
return ret;
#else // HAVE_LIBLLVM_SUPPORT
pr_debug("The LLVM disassembler isn't linked in for %s in %s\n",
- sym->name, filename);
+ symbol__name(sym), filename);
return -1;
#endif
}
diff --git a/tools/perf/util/lock-contention.c b/tools/perf/util/lock-contention.c
index 119a7206f3cd..8ee1b19a8292 100644
--- a/tools/perf/util/lock-contention.c
+++ b/tools/perf/util/lock-contention.c
@@ -135,7 +135,7 @@ bool match_callstack_filter(struct machine *machine, u64 *callstack, int max_sta
continue;
list_for_each_entry(filter, &callstack_filters, list) {
- if (strstr(sym->name, filter->name))
+ if (strstr(symbol__name(sym), filter->name))
return true;
}
}
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index 1d9d3bf57720..6a10c3036383 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -1110,8 +1110,8 @@ static u64 find_entry_trampoline(struct dso *dso)
if (symbol__binding(sym) != STB_GLOBAL)
continue;
for (i = 0; i < ARRAY_SIZE(syms); i++) {
- if (!strcmp(sym->name, syms[i]))
- return sym->start;
+ if (!strcmp(symbol__name(sym), syms[i]))
+ return symbol__start(sym);
}
}
@@ -2056,7 +2056,7 @@ int machine__process_event(struct machine *machine, union perf_event *event,
static bool symbol__match_regex(struct symbol *sym, regex_t *regex)
{
- return regexec(regex, sym->name, 0, NULL, 0) == 0;
+ return regexec(regex, symbol__name(sym), 0, NULL, 0) == 0;
}
static void ip__resolve_ams(struct thread *thread,
@@ -3283,8 +3283,8 @@ char *machine__resolve_kernel_addr(void *vmachine, unsigned long long *addrp, ch
return NULL;
*modp = __map__is_kmodule(map) ? (char *)dso__short_name(map__dso(map)) : NULL;
- *addrp = map__unmap_ip(map, sym->start);
- return sym->name;
+ *addrp = map__unmap_ip(map, symbol__start(sym));
+ return (char *)symbol__name(sym);
}
struct machine__for_each_dso_cb_args {
@@ -3330,27 +3330,27 @@ bool machine__is_lock_function(struct machine *machine, u64 addr)
return false;
}
- machine->sched.text_start = map__unmap_ip(kmap, sym->start);
+ machine->sched.text_start = map__unmap_ip(kmap, symbol__start(sym));
/* should not fail from here */
sym = machine__find_kernel_symbol_by_name(machine, "__sched_text_end", &kmap);
- machine->sched.text_end = map__unmap_ip(kmap, sym->start);
+ machine->sched.text_end = map__unmap_ip(kmap, symbol__start(sym));
sym = machine__find_kernel_symbol_by_name(machine, "__lock_text_start", &kmap);
- machine->lock.text_start = map__unmap_ip(kmap, sym->start);
+ machine->lock.text_start = map__unmap_ip(kmap, symbol__start(sym));
sym = machine__find_kernel_symbol_by_name(machine, "__lock_text_end", &kmap);
- machine->lock.text_end = map__unmap_ip(kmap, sym->start);
+ machine->lock.text_end = map__unmap_ip(kmap, symbol__start(sym));
sym = machine__find_kernel_symbol_by_name(machine, "__traceiter_contention_begin", &kmap);
if (sym) {
- machine->traceiter.text_start = map__unmap_ip(kmap, sym->start);
- machine->traceiter.text_end = map__unmap_ip(kmap, sym->end);
+ machine->traceiter.text_start = map__unmap_ip(kmap, symbol__start(sym));
+ machine->traceiter.text_end = map__unmap_ip(kmap, symbol__end(sym));
}
sym = machine__find_kernel_symbol_by_name(machine, "trace_contention_begin", &kmap);
if (sym) {
- machine->trace.text_start = map__unmap_ip(kmap, sym->start);
- machine->trace.text_end = map__unmap_ip(kmap, sym->end);
+ machine->trace.text_start = map__unmap_ip(kmap, symbol__start(sym));
+ machine->trace.text_end = map__unmap_ip(kmap, symbol__end(sym));
}
}
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c
index 41cdddc987ee..85216fa71f1a 100644
--- a/tools/perf/util/map.c
+++ b/tools/perf/util/map.c
@@ -321,7 +321,7 @@ void map__fixup_start(struct map *map)
if (nd != NULL) {
struct symbol *sym = rb_entry(nd, struct symbol, rb_node);
- map__set_start(map, sym->start);
+ map__set_start(map, symbol__start(sym));
}
}
@@ -333,7 +333,7 @@ void map__fixup_end(struct map *map)
if (nd != NULL) {
struct symbol *sym = rb_entry(nd, struct symbol, rb_node);
- map__set_end(map, sym->end);
+ map__set_end(map, symbol__end(sym));
}
}
@@ -606,7 +606,7 @@ u64 map__objdump_2rip(const struct map *map, u64 ip)
bool map__contains_symbol(const struct map *map, const struct symbol *sym)
{
- u64 ip = map__unmap_ip(map, sym->start);
+ u64 ip = map__unmap_ip(map, symbol__start(sym));
return ip >= map__start(map) && ip < map__end(map);
}
diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h
index fb0279810ae9..f49b4aaacf2f 100644
--- a/tools/perf/util/map.h
+++ b/tools/perf/util/map.h
@@ -164,7 +164,7 @@ struct thread;
#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__match_symbol_name(symbol__name(pos), sym_name, \
SYMBOL_TAG_INCLUDE__DEFAULT_ONLY); \
pos = dso__next_symbol_by_name(map__dso(map), &idx))
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 1534280c0c1d..2d554fed0c3f 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -149,7 +149,7 @@ static int kernel_get_symbol_address_by_name(const char *name, u64 *addr,
sym = machine__find_kernel_symbol_by_name(host_machine, name, &map);
if (!sym)
return -ENOENT;
- *addr = map__unmap_ip(map, sym->start) -
+ *addr = map__unmap_ip(map, symbol__start(sym)) -
((reloc) ? 0 : map__reloc(map)) -
((reladdr) ? map__start(map) : 0);
}
@@ -415,13 +415,13 @@ static int find_alternative_probe_point(struct debuginfo *dinfo,
/* Find the address of given function */
map__for_each_symbol_by_name(map, pp->function, sym, idx) {
if (uprobes) {
- address = sym->start;
+ address = symbol__start(sym);
if (symbol__type(sym) == STT_GNU_IFUNC)
pr_warning("Warning: The probe function (%s) is a GNU indirect function.\n"
"Consider identifying the final function used at run time and set the probe directly on that.\n",
pp->function);
} else
- address = map__unmap_ip(map, sym->start) - map__reloc(map);
+ address = map__unmap_ip(map, symbol__start(sym)) - map__reloc(map);
break;
}
if (!address) {
@@ -721,17 +721,17 @@ static int post_process_probe_trace_point(struct probe_trace_point *tp,
return (symbol_conf.ignore_vmlinux_buildid) ? 0 : -ENOENT;
}
- if (strcmp(sym->name, tp->symbol)) {
+ if (strcmp(symbol__name(sym), tp->symbol)) {
/* If we have no realname, use symbol for it */
if (!tp->realname)
tp->realname = tp->symbol;
else
free(tp->symbol);
- tp->symbol = strdup(sym->name);
+ tp->symbol = strdup(symbol__name(sym));
if (!tp->symbol)
return -ENOMEM;
}
- tp->offset = addr - sym->start;
+ tp->offset = addr - symbol__start(sym);
tp->address -= offs;
return 0;
@@ -2309,8 +2309,8 @@ static int find_perf_probe_point_from_map(struct probe_trace_point *tp,
goto out;
pp->retprobe = tp->retprobe;
- pp->offset = addr - map__unmap_ip(map, sym->start);
- pp->function = strdup(sym->name);
+ pp->offset = addr - map__unmap_ip(map, symbol__start(sym));
+ pp->function = strdup(symbol__name(sym));
ret = pp->function ? 0 : -ENOMEM;
out:
@@ -3065,7 +3065,7 @@ static int find_probe_functions(struct map *map, char *name,
cut_version = false;
map__for_each_symbol(map, sym, tmp) {
- norm = arch__normalize_symbol_name(sym->name);
+ norm = arch__normalize_symbol_name(symbol__name(sym));
if (!norm)
continue;
@@ -3193,12 +3193,13 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev,
continue;
/* There can be duplicated symbols in the map */
- for (i = 0; i < j; i++)
- if (sym->start == syms[i]->start) {
+ for (i = 0; i < j; i++) {
+ if (symbol__start(sym) == symbol__start(syms[i])) {
pr_debug("Found duplicated symbol %s @ %" PRIx64 "\n",
- sym->name, sym->start);
+ symbol__name(sym), symbol__start(sym));
break;
}
+ }
if (i != j)
continue;
@@ -3210,28 +3211,28 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev,
}
ret++;
- if (pp->offset > sym->end - sym->start) {
+ if (pp->offset > symbol__end(sym) - symbol__start(sym)) {
pr_warning("Offset %ld is bigger than the size of %s\n",
- pp->offset, sym->name);
+ pp->offset, symbol__name(sym));
ret = -ENOENT;
goto err_out;
}
/* Add one probe point */
- tp->address = map__unmap_ip(map, sym->start) + pp->offset;
+ tp->address = map__unmap_ip(map, symbol__start(sym)) + pp->offset;
/* Check the kprobe (not in module) is within .text */
if (!pev->uprobes && !pev->target &&
- kprobe_warn_out_range(sym->name, tp->address)) {
+ kprobe_warn_out_range(symbol__name(sym), tp->address)) {
tp->symbol = NULL; /* Skip it */
skipped++;
} else if (reloc_sym) {
tp->symbol = strdup_or_goto(reloc_sym->name, nomem_out);
tp->offset = tp->address - reloc_sym->addr;
} else {
- tp->symbol = strdup_or_goto(sym->name, nomem_out);
+ tp->symbol = strdup_or_goto(symbol__name(sym), nomem_out);
tp->offset = pp->offset;
}
- tp->realname = strdup_or_goto(sym->name, nomem_out);
+ tp->realname = strdup_or_goto(symbol__name(sym), nomem_out);
tp->retprobe = pp->retprobe;
if (pev->target) {
@@ -3813,8 +3814,8 @@ int show_available_funcs(const char *target, struct nsinfo *nsi,
for (size_t i = 0; i < dso__symbol_names_len(dso); i++) {
struct symbol *pos = dso__symbol_names(dso)[i];
- if (strfilter__compare(_filter, pos->name))
- printf("%s\n", pos->name);
+ if (strfilter__compare(_filter, symbol__name(pos)))
+ printf("%s\n", symbol__name(pos));
}
end:
map__put(map);
diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c
index 60dbe2dad5eb..71ff233553cc 100644
--- a/tools/perf/util/python.c
+++ b/tools/perf/util/python.c
@@ -812,7 +812,7 @@ static PyObject *pyrf_sample_event__get_symbol(struct pyrf_event *pevent,
if (pyrf_sample_event__resolve_al(pevent) < 0 || !pevent->al.sym)
Py_RETURN_NONE;
- return PyUnicode_FromString(pevent->al.sym->name);
+ return PyUnicode_FromString(symbol__name(pevent->al.sym));
}
static PyObject *pyrf_sample_event__get_sym_start(struct pyrf_event *pevent,
@@ -821,7 +821,7 @@ static PyObject *pyrf_sample_event__get_sym_start(struct pyrf_event *pevent,
if (pyrf_sample_event__resolve_al(pevent) < 0 || !pevent->al.sym)
Py_RETURN_NONE;
- return PyLong_FromUnsignedLongLong(pevent->al.sym->start);
+ return PyLong_FromUnsignedLongLong(symbol__start(pevent->al.sym));
}
static PyObject *pyrf_sample_event__get_sym_offset(struct pyrf_event *pevent,
@@ -830,7 +830,7 @@ static PyObject *pyrf_sample_event__get_sym_offset(struct pyrf_event *pevent,
if (pyrf_sample_event__resolve_al(pevent) < 0 || !pevent->al.sym)
Py_RETURN_NONE;
- return PyLong_FromUnsignedLongLong(pevent->al.addr - pevent->al.sym->start);
+ return PyLong_FromUnsignedLongLong(pevent->al.addr - symbol__start(pevent->al.sym));
}
static PyObject *pyrf_sample_event__get_sym_end(struct pyrf_event *pevent,
@@ -839,7 +839,7 @@ static PyObject *pyrf_sample_event__get_sym_end(struct pyrf_event *pevent,
if (pyrf_sample_event__resolve_al(pevent) < 0 || !pevent->al.sym)
Py_RETURN_NONE;
- return PyLong_FromUnsignedLongLong(pevent->al.sym->end);
+ return PyLong_FromUnsignedLongLong(symbol__end(pevent->al.sym));
}
static PyObject *pyrf_sample_event__get_raw_buf(struct pyrf_event *pevent,
@@ -960,7 +960,7 @@ static PyObject *pyrf_callchain_node__get_symbol(struct pyrf_callchain_node *pno
if (pnode->sym_name)
return PyUnicode_FromString(pnode->sym_name);
if (pnode->sym)
- return PyUnicode_FromString(pnode->sym->name);
+ return PyUnicode_FromString(symbol__name(pnode->sym));
return PyUnicode_FromString("[unknown]");
}
@@ -991,7 +991,7 @@ static PyObject *pyrf_callchain_node__get_sym_offset(struct pyrf_callchain_node
Py_RETURN_NONE;
addr = pnode->map ? map__map_ip(pnode->map, pnode->ip) : pnode->ip;
- return PyLong_FromUnsignedLongLong(addr - pnode->sym->start);
+ return PyLong_FromUnsignedLongLong(addr - symbol__start(pnode->sym));
}
static PyGetSetDef pyrf_callchain_node__getset[] = {
@@ -1330,7 +1330,7 @@ static PyObject *pyrf_sample_event__get_addr_symbol(struct pyrf_event *pevent,
PyObject *ret = Py_None;
if (pyrf_sample_event__resolve_addr_al(pevent, &addr_al) == 0 && addr_al.sym)
- ret = PyUnicode_FromString(addr_al.sym->name);
+ ret = PyUnicode_FromString(symbol__name(addr_al.sym));
else
Py_INCREF(Py_None);
@@ -1345,7 +1345,7 @@ static PyObject *pyrf_sample_event__get_addr_sym_offset(struct pyrf_event *peven
PyObject *ret = Py_None;
if (pyrf_sample_event__resolve_addr_al(pevent, &addr_al) == 0 && addr_al.sym)
- ret = PyLong_FromUnsignedLongLong(addr_al.addr - addr_al.sym->start);
+ ret = PyLong_FromUnsignedLongLong(addr_al.addr - symbol__start(addr_al.sym));
else
Py_INCREF(Py_None);
@@ -4505,7 +4505,7 @@ static PyObject *pyrf_call_path__to_callchain(const struct call_path *cp,
frame->ip = pos->ip;
frame->sym = NULL;
- frame->sym_name = pos->sym ? strdup(pos->sym->name) : NULL;
+ frame->sym_name = pos->sym ? strdup(symbol__name(pos->sym)) : NULL;
frame->map = NULL;
if (thread) {
struct addr_location al;
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c
index 58638ec9ae0e..62cf3d40e4b6 100644
--- a/tools/perf/util/sort.c
+++ b/tools/perf/util/sort.c
@@ -470,18 +470,19 @@ int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r)
return 0;
if (symbol__inlined(sym_l) || symbol__inlined(sym_r)) {
- int ret = strcmp(sym_l->name, sym_r->name);
+ int ret = strcmp(symbol__name(sym_l), symbol__name(sym_r));
if (ret)
return ret;
- if ((sym_l->start <= sym_r->end) && (sym_l->end >= sym_r->start))
+ if ((symbol__start(sym_l) <= symbol__end(sym_r)) &&
+ (symbol__end(sym_l) >= symbol__start(sym_r)))
return 0;
}
- if (sym_l->start != sym_r->start)
- return (int64_t)(sym_r->start - sym_l->start);
+ if (symbol__start(sym_l) != symbol__start(sym_r))
+ return (int64_t)(symbol__start(sym_r) - symbol__start(sym_l));
- return (int64_t)(sym_r->end - sym_l->end);
+ return (int64_t)(symbol__end(sym_r) - symbol__end(sym_l));
}
static int64_t
@@ -511,7 +512,7 @@ sort__sym_sort(struct hist_entry *left, struct hist_entry *right)
if (!left->ms.sym || !right->ms.sym)
return cmp_null(left->ms.sym, right->ms.sym);
- return strcmp(right->ms.sym->name, left->ms.sym->name);
+ return strcmp(symbol__name(right->ms.sym), symbol__name(left->ms.sym));
}
static int _hist_entry__sym_snprintf(struct map_symbol *ms,
@@ -537,13 +538,13 @@ static int _hist_entry__sym_snprintf(struct map_symbol *ms,
ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", level);
if (sym && map) {
if (symbol__type(sym) == STT_OBJECT) {
- ret += repsep_snprintf(bf + ret, size - ret, "%s", sym->name);
+ ret += repsep_snprintf(bf + ret, size - ret, "%s", symbol__name(sym));
ret += repsep_snprintf(bf + ret, size - ret, "+0x%llx",
- ip - map__unmap_ip(map, sym->start));
+ ip - map__unmap_ip(map, symbol__start(sym)));
} else {
ret += repsep_snprintf(bf + ret, size - ret, "%.*s",
width - ret,
- sym->name);
+ symbol__name(sym));
if (symbol__inlined(sym))
ret += repsep_snprintf(bf + ret, size - ret,
" (inlined)");
@@ -570,7 +571,7 @@ static int hist_entry__sym_filter(struct hist_entry *he, int type, const void *a
if (type != HIST_FILTER__SYMBOL)
return -1;
- return sym && (!he->ms.sym || !strstr(he->ms.sym->name, sym));
+ return sym && (!he->ms.sym || !strstr(symbol__name(he->ms.sym), sym));
}
struct sort_entry sort_sym = {
@@ -616,7 +617,8 @@ hist_entry__symoff_snprintf(struct hist_entry *he, char *bf, size_t size, unsign
if (sym == NULL)
return repsep_snprintf(bf, size, "[%c] %-#.*llx", he->level, width - 4, he->ip);
- return repsep_snprintf(bf, size, "[%c] %s+0x%llx", he->level, sym->name, he->ip - sym->start);
+ return repsep_snprintf(bf, size, "[%c] %s+0x%llx", he->level,
+ symbol__name(sym), he->ip - symbol__start(sym));
}
static struct sort_entry sort_sym_offset = {
@@ -1016,14 +1018,14 @@ sort__parent_cmp(struct hist_entry *left, struct hist_entry *right)
if (!sym_l || !sym_r)
return cmp_null(sym_l, sym_r);
- return strcmp(sym_r->name, sym_l->name);
+ return strcmp(symbol__name(sym_r), symbol__name(sym_l));
}
static int hist_entry__parent_snprintf(struct hist_entry *he, char *bf,
size_t size, unsigned int width)
{
return repsep_snprintf(bf, size, "%-*.*s", width, width,
- he->parent ? he->parent->name : "[other]");
+ he->parent ? symbol__name(he->parent) : "[other]");
}
struct sort_entry sort_parent = {
@@ -1426,7 +1428,7 @@ static int hist_entry__sym_from_filter(struct hist_entry *he, int type,
return -1;
return sym && !(he->branch_info && he->branch_info->from.ms.sym &&
- strstr(he->branch_info->from.ms.sym->name, sym));
+ strstr(symbol__name(he->branch_info->from.ms.sym), sym));
}
static int hist_entry__sym_to_filter(struct hist_entry *he, int type,
@@ -1438,7 +1440,7 @@ static int hist_entry__sym_to_filter(struct hist_entry *he, int type,
return -1;
return sym && !(he->branch_info && he->branch_info->to.ms.sym &&
- strstr(he->branch_info->to.ms.sym->name, sym));
+ strstr(symbol__name(he->branch_info->to.ms.sym), sym));
}
struct sort_entry sort_dso_from = {
@@ -1484,14 +1486,14 @@ static int _hist_entry__addr_snprintf(struct map_symbol *ms,
ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", level);
if (sym && map) {
if (symbol__type(sym) == STT_OBJECT) {
- ret += repsep_snprintf(bf + ret, size - ret, "%s", sym->name);
+ ret += repsep_snprintf(bf + ret, size - ret, "%s", symbol__name(sym));
ret += repsep_snprintf(bf + ret, size - ret, "+0x%llx",
- ip - map__unmap_ip(map, sym->start));
+ ip - map__unmap_ip(map, symbol__start(sym)));
} else {
ret += repsep_snprintf(bf + ret, size - ret, "%.*s",
width - ret,
- sym->name);
- offs = ip - sym->start;
+ symbol__name(sym));
+ offs = ip - symbol__start(sym);
if (offs)
ret += repsep_snprintf(bf + ret, size - ret, "+0x%llx", offs);
}
diff --git a/tools/perf/util/srcline.c b/tools/perf/util/srcline.c
index 23951c35fb66..14f2b6dfd086 100644
--- a/tools/perf/util/srcline.c
+++ b/tools/perf/util/srcline.c
@@ -116,7 +116,7 @@ struct symbol *new_inline_sym(struct dso *dso,
funcname = demangled;
}
- if (base_sym && strcmp(funcname, base_sym->name) == 0) {
+ if (base_sym && strcmp(funcname, symbol__name(base_sym)) == 0) {
/* reuse the real, existing symbol */
inline_sym = base_sym;
/* ensure that we don't alias an inlined symbol, which could
@@ -125,8 +125,9 @@ struct symbol *new_inline_sym(struct dso *dso,
assert(!symbol__inlined(base_sym));
} else {
/* create a fake symbol for the inline frame */
- inline_sym = symbol__new(base_sym ? base_sym->start : 0,
- base_sym ? (base_sym->end - base_sym->start) : 0,
+ inline_sym = symbol__new(base_sym ? symbol__start(base_sym) : 0,
+ base_sym ?
+ (symbol__end(base_sym) - symbol__start(base_sym)) : 0,
base_sym ? symbol__binding(base_sym) : 0,
base_sym ? symbol__type(base_sym) : 0,
funcname);
@@ -310,13 +311,14 @@ char *__get_srcline(struct dso *dso, u64 addr, struct symbol *sym,
}
mutex_unlock(dso__lock(dso));
out:
- if (!show_addr)
+ if (!show_addr) {
return (show_sym && sym) ?
- strndup(sym->name, sym->namelen) : SRCLINE_UNKNOWN;
+ strndup(symbol__name(sym), symbol__namelen(sym)) : SRCLINE_UNKNOWN;
+ }
if (sym) {
- if (asprintf(&srcline, "%s+%" PRIu64, show_sym ? sym->name : "",
- ip - sym->start) < 0)
+ if (asprintf(&srcline, "%s+%" PRIu64, show_sym ? symbol__name(sym) : "",
+ ip - symbol__start(sym)) < 0)
return SRCLINE_UNKNOWN;
} else if (asprintf(&srcline, "%s[%" PRIx64 "]", dso__short_name(dso), addr) < 0)
return SRCLINE_UNKNOWN;
diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c
index 1e2716c6af45..817486a4b8f1 100644
--- a/tools/perf/util/symbol-elf.c
+++ b/tools/perf/util/symbol-elf.c
@@ -351,11 +351,11 @@ static bool get_ifunc_name(Elf *elf, struct dso *dso, GElf_Ehdr *ehdr,
sym = dso__find_symbol_nocache(dso, addr);
/* Expecting the address to be an IFUNC or IFUNC alias */
- if (!sym || sym->start != addr ||
+ if (!sym || symbol__start(sym) != addr ||
(symbol__type(sym) != STT_GNU_IFUNC && !symbol__ifunc_alias(sym)))
return false;
- snprintf(buf, buf_sz, "%s@plt", sym->name);
+ snprintf(buf, buf_sz, "%s@plt", symbol__name(sym));
return true;
}
@@ -630,8 +630,8 @@ int dso__synthesize_plt_symbols(struct dso *dso, struct symsrc *ss)
* a symbol for .plt header.
*/
f = dso__find_symbol_nocache(dso, shdr_plt.sh_offset);
- if (f && f->start < shdr_plt.sh_offset && f->end > shdr_plt.sh_offset)
- f->end = shdr_plt.sh_offset;
+ if (f && symbol__start(f) < shdr_plt.sh_offset && symbol__end(f) > shdr_plt.sh_offset)
+ symbol__set_end(f, shdr_plt.sh_offset);
if (!get_plt_sizes(dso, &ehdr, &shdr_plt, &plt_header_size, &plt_entry_size))
return 0;
@@ -653,7 +653,7 @@ int dso__synthesize_plt_symbols(struct dso *dso, struct symsrc *ss)
if (!get_plt_sizes(dso, &ehdr, &plt_sec_shdr, &plt_header_size, &plt_entry_size))
return 0;
/* Extend .plt symbol to entire .plt */
- plt_sym->end = plt_sym->start + shdr_plt.sh_size;
+ symbol__set_end(plt_sym, symbol__start(plt_sym) + shdr_plt.sh_size);
/* Use .plt.sec offset */
plt_offset = plt_sec_shdr.sh_offset;
lazy_plt = false;
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index dc70bbe8514f..991167b2dfa4 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -149,9 +149,9 @@ int __weak arch__choose_best_symbol(struct symbol *syma,
struct symbol *symb __maybe_unused)
{
/* Avoid "SyS" kernel syscall aliases */
- if (strlen(syma->name) >= 3 && !strncmp(syma->name, "SyS", 3))
+ if (strlen(symbol__name(syma)) >= 3 && !strncmp(symbol__name(syma), "SyS", 3))
return SYMBOL_B;
- if (strlen(syma->name) >= 10 && !strncmp(syma->name, "compat_SyS", 10))
+ if (strlen(symbol__name(syma)) >= 10 && !strncmp(symbol__name(syma), "compat_SyS", 10))
return SYMBOL_B;
return SYMBOL_A;
@@ -164,8 +164,8 @@ static int choose_best_symbol(struct symbol *syma, struct symbol *symb)
size_t na, nb;
/* Prefer a symbol with non zero length */
- a = syma->end - syma->start;
- b = symb->end - symb->start;
+ a = symbol__end(syma) - symbol__start(syma);
+ b = symbol__end(symb) - symbol__start(symb);
if ((b == 0) && (a > 0))
return SYMBOL_A;
else if ((a == 0) && (b > 0))
@@ -195,16 +195,16 @@ static int choose_best_symbol(struct symbol *syma, struct symbol *symb)
return SYMBOL_B;
/* Prefer a symbol with less underscores */
- a = prefix_underscores_count(syma->name);
- b = prefix_underscores_count(symb->name);
+ a = prefix_underscores_count(symbol__name(syma));
+ b = prefix_underscores_count(symbol__name(symb));
if (b > a)
return SYMBOL_A;
else if (a > b)
return SYMBOL_B;
/* Choose the symbol with the longest name */
- na = strlen(syma->name);
- nb = strlen(symb->name);
+ na = strlen(symbol__name(syma));
+ nb = strlen(symbol__name(symb));
if (na > nb)
return SYMBOL_A;
else if (na < nb)
@@ -231,7 +231,7 @@ void symbols__fixup_duplicate(struct rb_root_cached *symbols)
break;
next = rb_entry(nd, struct symbol, rb_node);
- if (curr->start != next->start)
+ if (symbol__start(curr) != symbol__start(next))
continue;
if (choose_best_symbol(curr, next) == SYMBOL_A) {
@@ -281,39 +281,39 @@ void symbols__fixup_end(struct rb_root_cached *symbols, bool is_kallsyms)
* like in:
* ffffffffc1937000 T hdmi_driver_init [snd_hda_codec_hdmi]
*/
- if (prev->end == prev->start) {
+ if (symbol__end(prev) == symbol__start(prev)) {
const char *prev_mod;
const char *curr_mod;
if (!is_kallsyms) {
- prev->end = curr->start;
+ symbol__set_end(prev, symbol__start(curr));
continue;
}
- prev_mod = strchr(prev->name, '[');
- curr_mod = strchr(curr->name, '[');
+ prev_mod = strchr(symbol__name(prev), '[');
+ curr_mod = strchr(symbol__name(curr), '[');
/* Last kernel/module symbol mapped to end of page */
if (!prev_mod != !curr_mod)
- prev->end = roundup(prev->end + 4096, 4096);
+ symbol__set_end(prev, roundup(symbol__end(prev) + 4096, 4096));
/* Last symbol in the previous module */
else if (prev_mod && strcmp(prev_mod, curr_mod))
- prev->end = roundup(prev->end + 4096, 4096);
+ symbol__set_end(prev, roundup(symbol__end(prev) + 4096, 4096));
else
- prev->end = curr->start;
+ symbol__set_end(prev, symbol__start(curr));
/* The next module can start within that page */
- if (prev->end > curr->start)
- prev->end = curr->start;
+ if (symbol__end(prev) > symbol__start(curr))
+ symbol__set_end(prev, symbol__start(curr));
pr_debug4("%s sym:%s end:%#" PRIx64 "\n",
- __func__, prev->name, prev->end);
+ __func__, symbol__name(prev), symbol__end(prev));
}
}
/* Last entry */
- if (curr->end == curr->start)
- curr->end = roundup(curr->start, 4096) + 4096;
+ if (symbol__end(curr) == symbol__start(curr))
+ symbol__set_end(curr, roundup(symbol__start(curr), 4096) + 4096);
}
struct symbol *symbol__new(u64 start, u64 len, u8 binding, u8 type, const char *name)
@@ -417,14 +417,14 @@ void __symbols__insert(struct rb_root_cached *symbols, struct symbol *sym)
{
struct rb_node **p = &symbols->rb_root.rb_node;
struct rb_node *parent = NULL;
- const u64 ip = sym->start;
+ const u64 ip = symbol__start(sym);
struct symbol *s;
bool leftmost = true;
while (*p != NULL) {
parent = *p;
s = rb_entry(parent, struct symbol, rb_node);
- if (ip < s->start)
+ if (ip < symbol__start(s))
p = &(*p)->rb_left;
else {
p = &(*p)->rb_right;
@@ -452,9 +452,9 @@ static struct symbol *symbols__find(struct rb_root_cached *symbols, u64 ip)
while (n) {
struct symbol *s = rb_entry(n, struct symbol, rb_node);
- if (ip < s->start)
+ if (ip < symbol__start(s))
n = n->rb_left;
- else if (ip > s->end || (ip == s->end && ip != s->start))
+ else if (ip > symbol__end(s) || (ip == symbol__end(s) && ip != symbol__start(s)))
n = n->rb_right;
else
return s;
@@ -498,7 +498,7 @@ static int symbols__sort_name_cmp(const void *vlhs, const void *vrhs)
const struct symbol *lhs = *((const struct symbol **)vlhs);
const struct symbol *rhs = *((const struct symbol **)vrhs);
- return strcmp(lhs->name, rhs->name);
+ return strcmp(symbol__name(lhs), symbol__name(rhs));
}
static struct symbol **symbols__sort_by_name(struct rb_root_cached *source, size_t *len)
@@ -560,7 +560,7 @@ static struct symbol *symbols__find_by_name(struct symbol *symbols[],
int cmp;
i = (lower + upper) / 2;
- cmp = symbol__match_symbol_name(symbols[i]->name, name, includes);
+ cmp = symbol__match_symbol_name(symbol__name(symbols[i]), name, includes);
if (cmp > 0)
upper = i;
@@ -578,7 +578,7 @@ static struct symbol *symbols__find_by_name(struct symbol *symbols[],
for (; i > 0; i--) {
struct symbol *tmp = symbols[i - 1];
- if (!arch__compare_symbol_names(tmp->name, s->name)) {
+ if (!arch__compare_symbol_names(symbol__name(tmp), symbol__name(s))) {
if (found_idx)
*found_idx = i - 1;
s = tmp;
@@ -601,9 +601,9 @@ void dso__insert_symbol(struct dso *dso, struct symbol *sym)
__symbols__insert(dso__symbols(dso), sym);
/* update the symbol cache if necessary */
- if (dso__last_find_result_addr(dso) >= sym->start &&
- (dso__last_find_result_addr(dso) < sym->end ||
- sym->start == sym->end)) {
+ if (dso__last_find_result_addr(dso) >= symbol__start(sym) &&
+ (dso__last_find_result_addr(dso) < symbol__end(sym) ||
+ symbol__start(sym) == symbol__end(sym))) {
dso__set_last_find_result_symbol(dso, sym);
}
}
@@ -800,7 +800,7 @@ bool symbol__is_idle(struct symbol *sym, const struct dso *dso, struct perf_env
"poll_idle",
"pseries_dedicated_idle_sleep",
};
- const char *name = sym->name;
+ const char *name = symbol__name(sym);
uint16_t e_machine;
{
@@ -940,22 +940,25 @@ static int maps__split_kallsyms_for_kcore(struct maps *kmaps, struct dso *dso)
rb_erase_cached(&pos->rb_node, &old_root);
RB_CLEAR_NODE(&pos->rb_node);
- module = strchr(pos->name, '\t');
+ module = strchr((char *)symbol__name(pos), '\t');
if (module)
*module = '\0';
- curr_map = maps__find(kmaps, pos->start);
+ curr_map = maps__find(kmaps, symbol__start(pos));
if (!curr_map) {
symbol__delete(pos);
continue;
}
curr_map_dso = map__dso(curr_map);
- pos->start -= map__start(curr_map) - map__pgoff(curr_map);
- if (pos->end > map__end(curr_map))
- pos->end = map__end(curr_map);
- if (pos->end)
- pos->end -= map__start(curr_map) - map__pgoff(curr_map);
+ symbol__set_start(pos, symbol__start(pos) -
+ (map__start(curr_map) - map__pgoff(curr_map)));
+ if (symbol__end(pos) > map__end(curr_map))
+ symbol__set_end(pos, map__end(curr_map));
+ if (symbol__end(pos)) {
+ symbol__set_end(pos, symbol__end(pos) -
+ (map__start(curr_map) - map__pgoff(curr_map)));
+ }
symbols__insert(dso__symbols(curr_map_dso), pos);
++count;
map__put(curr_map);
@@ -996,7 +999,7 @@ static int maps__split_kallsyms(struct maps *kmaps, struct dso *dso, u64 delta,
pos = rb_entry(next, struct symbol, rb_node);
next = rb_next(&pos->rb_node);
- module = strchr(pos->name, '\t');
+ module = strchr((char *)symbol__name(pos), '\t');
if (module) {
struct dso *curr_map_dso;
@@ -1038,9 +1041,9 @@ static int maps__split_kallsyms(struct maps *kmaps, struct dso *dso, u64 delta,
* So that we look just like we get from .ko files,
* i.e. not prelinked, relative to initial_map->start.
*/
- pos->start = map__map_ip(curr_map, pos->start);
- pos->end = map__map_ip(curr_map, pos->end);
- } else if (e_machine == EM_X86_64 && is_entry_trampoline(pos->name)) {
+ symbol__set_start(pos, map__map_ip(curr_map, symbol__start(pos)));
+ symbol__set_end(pos, map__map_ip(curr_map, symbol__end(pos)));
+ } else if (e_machine == EM_X86_64 && is_entry_trampoline(symbol__name(pos))) {
/*
* These symbols are not needed anymore since the
* trampoline maps refer to the text section and it's
@@ -1056,12 +1059,12 @@ static int maps__split_kallsyms(struct maps *kmaps, struct dso *dso, u64 delta,
if (delta) {
/* Kernel was relocated at boot time */
- pos->start -= delta;
- pos->end -= delta;
+ symbol__set_start(pos, symbol__start(pos) - delta);
+ symbol__set_end(pos, symbol__end(pos) - delta);
}
- if (map__start(initial_map) <= (pos->start + delta) &&
- (pos->start + delta) < map__end(initial_map)) {
+ if (map__start(initial_map) <= (symbol__start(pos) + delta) &&
+ (symbol__start(pos) + delta) < map__end(initial_map)) {
map__zput(curr_map);
curr_map = map__get(initial_map);
goto add_symbol;
@@ -1091,7 +1094,7 @@ static int maps__split_kallsyms(struct maps *kmaps, struct dso *dso, u64 delta,
}
dso__set_loaded(ndso);
- curr_map = map__new2(pos->start, ndso);
+ curr_map = map__new2(symbol__start(pos), ndso);
if (curr_map == NULL) {
dso__put(ndso);
return -1;
@@ -1107,8 +1110,8 @@ static int maps__split_kallsyms(struct maps *kmaps, struct dso *dso, u64 delta,
++kernel_range;
} else if (delta) {
/* Kernel was relocated at boot time */
- pos->start -= delta;
- pos->end -= delta;
+ symbol__set_start(pos, symbol__start(pos) - delta);
+ symbol__set_end(pos, symbol__end(pos) - delta);
}
add_symbol:
if (!RC_CHK_EQUAL(curr_map, initial_map)) {
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index 46b1649c64fc..780ea3030a6f 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -101,6 +101,47 @@ struct symbol {
void symbol__delete(struct symbol *sym);
void symbols__delete(struct rb_root_cached *symbols);
+static inline __attribute__((nonnull)) u64 symbol__start(const struct symbol *sym)
+{
+ return sym->start;
+}
+
+static inline __attribute__((nonnull)) void symbol__set_start(struct symbol *sym, u64 start)
+{
+ sym->start = start;
+}
+
+static inline __attribute__((nonnull)) u64 symbol__end(const struct symbol *sym)
+{
+ return sym->end;
+}
+
+static inline __attribute__((nonnull)) void symbol__set_end(struct symbol *sym, u64 end)
+{
+ sym->end = end;
+}
+
+static inline __attribute__((nonnull, returns_nonnull))
+const char *symbol__name(const struct symbol *sym)
+{
+ return sym->name;
+}
+
+static inline __attribute__((nonnull)) u16 symbol__namelen(const struct symbol *sym)
+{
+ return sym->namelen;
+}
+
+static inline __attribute__((nonnull)) u8 symbol__arch_sym(const struct symbol *sym)
+{
+ return sym->arch_sym;
+}
+
+static inline __attribute__((nonnull)) void symbol__set_arch_sym(struct symbol *sym, u8 arch_sym)
+{
+ sym->arch_sym = arch_sym;
+}
+
static inline u8 symbol__type(const struct symbol *sym)
{
return (atomic_load_explicit(&sym->flags, memory_order_relaxed) &
@@ -157,7 +198,7 @@ void symbol__set_ifunc_alias(struct symbol *sym, bool ifunc_alias);
static inline size_t symbol__size(const struct symbol *sym)
{
- return sym->end - sym->start;
+ return symbol__end(sym) - symbol__start(sym);
}
struct strlist;
diff --git a/tools/perf/util/symbol_fprintf.c b/tools/perf/util/symbol_fprintf.c
index 4dc8d5761f52..8c131325e989 100644
--- a/tools/perf/util/symbol_fprintf.c
+++ b/tools/perf/util/symbol_fprintf.c
@@ -10,10 +10,10 @@
size_t symbol__fprintf(struct symbol *sym, FILE *fp)
{
return fprintf(fp, " %" PRIx64 "-%" PRIx64 " %c %s\n",
- sym->start, sym->end,
+ symbol__start(sym), symbol__end(sym),
symbol__binding(sym) == STB_GLOBAL ? 'g' :
symbol__binding(sym) == STB_LOCAL ? 'l' : 'w',
- sym->name);
+ symbol__name(sym));
}
size_t __symbol__fprintf_symname_offs(const struct symbol *sym,
@@ -25,12 +25,12 @@ size_t __symbol__fprintf_symname_offs(const struct symbol *sym,
size_t length;
if (sym) {
- length = fprintf(fp, "%s", sym->name);
+ length = fprintf(fp, "%s", symbol__name(sym));
if (al && print_offsets) {
- if (al->addr < sym->end)
- offset = al->addr - sym->start;
+ if (al->addr < symbol__end(sym))
+ offset = al->addr - symbol__start(sym);
else
- offset = al->addr - map__start(al->map) - sym->start;
+ offset = al->addr - map__start(al->map) - symbol__start(sym);
length += fprintf(fp, "+0x%lx", offset);
}
return length;
@@ -67,7 +67,7 @@ size_t dso__fprintf_symbols_by_name(struct dso *dso,
for (size_t i = 0; i < dso__symbol_names_len(dso); i++) {
struct symbol *pos = dso__symbol_names(dso)[i];
- ret += fprintf(fp, "%s\n", pos->name);
+ ret += fprintf(fp, "%s\n", symbol__name(pos));
}
return ret;
}
diff --git a/tools/perf/util/thread-stack.c b/tools/perf/util/thread-stack.c
index 1360f44421ef..76867ba83365 100644
--- a/tools/perf/util/thread-stack.c
+++ b/tools/perf/util/thread-stack.c
@@ -1056,7 +1056,7 @@ static int thread_stack__x86_retpoline(struct thread_stack *ts,
struct symbol *tsym = to_al->sym;
struct call_path *cp;
- if (sym && is_x86_retpoline(sym->name)) {
+ if (sym && is_x86_retpoline(symbol__name(sym))) {
/*
* This is a x86 retpoline fn. It pollutes the call graph by
* showing up everywhere there is an indirect branch, but does
@@ -1069,7 +1069,7 @@ static int thread_stack__x86_retpoline(struct thread_stack *ts,
*/
ts->cnt -= 1;
sym = ts->stack[ts->cnt - 2].cp->sym;
- if (sym && sym == tsym && to_al->addr != tsym->start) {
+ if (sym && sym == tsym && to_al->addr != symbol__start(tsym)) {
/*
* Target is back to the middle of the symbol we came
* from so assume it is an indirect jmp and forget it
@@ -1170,7 +1170,7 @@ int thread_stack__process(struct thread *thread, struct comm *comm,
*/
if (!err && rstate == X86_RETPOLINE_POSSIBLE && to_al->sym &&
from_al->sym == to_al->sym &&
- to_al->addr != to_al->sym->start)
+ to_al->addr != symbol__start(to_al->sym))
ts->rstate = X86_RETPOLINE_DETECTED;
} else if (sample->flags & PERF_IP_FLAG_RETURN) {
@@ -1207,7 +1207,7 @@ int thread_stack__process(struct thread *thread, struct comm *comm,
err = thread_stack__trace_end(ts, sample, ref);
} else if (sample->flags & PERF_IP_FLAG_BRANCH &&
from_al->sym != to_al->sym && to_al->sym &&
- to_al->addr == to_al->sym->start) {
+ to_al->addr == symbol__start(to_al->sym)) {
struct call_path_root *cpr = ts->crp->cpr;
struct call_path *cp;
diff --git a/tools/perf/util/unwind-libdw.c b/tools/perf/util/unwind-libdw.c
index d49901b1571e..87ade098b5c6 100644
--- a/tools/perf/util/unwind-libdw.c
+++ b/tools/perf/util/unwind-libdw.c
@@ -172,7 +172,7 @@ static int entry(u64 ip, struct unwind_info *ui)
e->ms.sym = al.sym;
pr_debug("unwind: %s:ip = 0x%" PRIx64 " (0x%" PRIx64 ")\n",
- al.sym ? al.sym->name : "''",
+ al.sym ? symbol__name(al.sym) : "''",
ip,
al.map ? map__map_ip(al.map, ip) : (u64) 0);
addr_location__exit(&al);
--
2.56.0.313.g2479322e68-goog
^ permalink raw reply [flat|nested] 36+ messages in thread
* [PATCH v2 04/18] perf map_browser: Compute symbol browser index on demand
2026-10-27 23:14 ` [PATCH v2 00/18] perf symbol: Reference counting, sorted arrays, and memory shrinking Ian Rogers
` (2 preceding siblings ...)
2026-10-27 23:14 ` [PATCH v2 03/18] perf symbol: Add accessor functions for struct symbol fields Ian Rogers
@ 2026-10-27 23:14 ` Ian Rogers
2026-10-27 23:14 ` [PATCH v2 05/18] perf db-export: Store symbol database IDs in a hashmap Ian Rogers
` (13 subsequent siblings)
17 siblings, 0 replies; 36+ messages in thread
From: Ian Rogers @ 2026-10-27 23:14 UTC (permalink / raw)
To: irogers, acme, namhyung
Cc: adrian.hunter, haghdoost, james.clark, jolsa, linux-kernel,
linux-perf-users, mingo, peterz
To wrap struct symbol in DECLARE_RC_STRUCT(symbol) and reduce per-symbol
memory overhead, negative-offset allocations ahead of struct symbol via
symbol_conf.priv_size and symbol__priv() need to be eliminated.
Remove symbol__browser_index() in ui/browsers/map.c and compute the TUI
map browser index on demand in map_browser__search() by walking the map
browser's symbol entries when the user searches with '/'.
While this changes finding a symbol's display row index in
map_browser__search() from O(1) to O(N) in the number of symbols in that
single map, map_browser__search() is only invoked interactively when a
user opens the TUI "Browse map details" view for a map, presses '/',
types a query, and hits ENTER. Even for a large DSO such as vmlinux
(~100k symbols), scanning the map's symbols once per interactive search
takes well under a millisecond (and becomes a flat pointer array scan
once symbols are stored in struct symbols).
Previously, '/' search in the TUI map browser was gated behind
'verbose > 0' (-v) in builtin-c2c.c, builtin-report.c, and
ui/browsers/map.c solely to avoid adding sizeof(u32) to
symbol_conf.priv_size for every struct symbol across all DSOs in normal
runs. With on-demand index computation there is no per-symbol memory
overhead, so remove the verbose > 0 restriction and allow '/' search in
the map browser unconditionally.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/builtin-c2c.c | 14 --------------
tools/perf/builtin-report.c | 14 --------------
tools/perf/ui/browsers/map.c | 37 ++++++++++++++++++------------------
3 files changed, 18 insertions(+), 47 deletions(-)
diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c
index f51fa23f3bf1..719f47dac2dc 100644
--- a/tools/perf/builtin-c2c.c
+++ b/tools/perf/builtin-c2c.c
@@ -3412,20 +3412,6 @@ static int perf_c2c__report(int argc, const char **argv)
if (ret < 0)
goto out_mem2node;
- /*
- * For searching by name on the "Browse map details".
- * providing it only in verbose mode not to bloat too
- * much struct symbol.
- */
- if (verbose > 0) {
- /*
- * XXX: Need to provide a less kludgy way to ask for
- * more space per symbol, the u32 is for the index on
- * the ui browser.
- * See symbol__browser_index.
- */
- symbol_conf.priv_size += sizeof(u32);
- }
annotation_config__init();
}
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index 29085fdc91e5..6672a4d16529 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -1885,20 +1885,6 @@ int cmd_report(int argc, const char **argv)
ret = symbol__annotation_init();
if (ret < 0)
goto error;
- /*
- * For searching by name on the "Browse map details".
- * providing it only in verbose mode not to bloat too
- * much struct symbol.
- */
- if (verbose > 0) {
- /*
- * XXX: Need to provide a less kludgy way to ask for
- * more space per symbol, the u32 is for the index on
- * the ui browser.
- * See symbol__browser_index.
- */
- symbol_conf.priv_size += sizeof(u32);
- }
annotation_config__init();
}
diff --git a/tools/perf/ui/browsers/map.c b/tools/perf/ui/browsers/map.c
index 47f2743cae26..2f070a53b5cb 100644
--- a/tools/perf/ui/browsers/map.c
+++ b/tools/perf/ui/browsers/map.c
@@ -39,12 +39,6 @@ static void map_browser__write(struct ui_browser *browser, void *nd, int row)
ui_browser__write_nstring(browser, symbol__name(sym), width);
}
-/* FIXME uber-kludgy, see comment on cmd_report... */
-static u32 *symbol__browser_index(struct symbol *browser)
-{
- return ((void *)browser) - sizeof(struct rb_node) - sizeof(u32);
-}
-
static int map_browser__search(struct map_browser *browser)
{
char target[512];
@@ -62,10 +56,21 @@ static int map_browser__search(struct map_browser *browser)
sym = map__find_symbol_by_name(browser->map, target);
if (sym != NULL) {
- u32 *idx = symbol__browser_index(sym);
-
- browser->b.top = &sym->rb_node;
- browser->b.index = browser->b.top_idx = *idx;
+ struct rb_node *nd;
+ u32 idx = 0;
+
+ /*
+ * Walk the map browser's symbol entries to find the matching
+ * symbol node and its display row index, then position the
+ * browser cursor at that entry.
+ */
+ for (nd = rb_first(browser->b.entries); nd; nd = rb_next(nd), ++idx) {
+ if (&sym->rb_node == nd) {
+ browser->b.top = nd;
+ browser->b.index = browser->b.top_idx = idx;
+ break;
+ }
+ }
} else
ui_helpline__fpush("%s not found!", target);
@@ -77,8 +82,7 @@ static int map_browser__run(struct map_browser *browser)
int key;
if (ui_browser__show(&browser->b, dso__long_name(map__dso(browser->map)),
- "Press ESC to exit, %s / to search",
- verbose > 0 ? "" : "restart with -v to use") < 0)
+ "Press ESC to exit, / to search") < 0)
return -1;
while (1) {
@@ -86,9 +90,8 @@ static int map_browser__run(struct map_browser *browser)
switch (key) {
case '/':
- if (verbose > 0)
- map_browser__search(browser);
- /* fall thru */
+ map_browser__search(browser);
+ continue;
default:
ui_browser__warn_unhandled_hotkey(&browser->b, key, 0, NULL);
continue;
@@ -124,10 +127,6 @@ int map__browse(struct map *map)
if (maxaddr < symbol__end(pos))
maxaddr = symbol__end(pos);
- if (verbose > 0) {
- u32 *idx = symbol__browser_index(pos);
- *idx = mb.b.nr_entries;
- }
++mb.b.nr_entries;
}
--
2.56.0.313.g2479322e68-goog
^ permalink raw reply [flat|nested] 36+ messages in thread
* [PATCH v2 05/18] perf db-export: Store symbol database IDs in a hashmap
2026-10-27 23:14 ` [PATCH v2 00/18] perf symbol: Reference counting, sorted arrays, and memory shrinking Ian Rogers
` (3 preceding siblings ...)
2026-10-27 23:14 ` [PATCH v2 04/18] perf map_browser: Compute symbol browser index on demand Ian Rogers
@ 2026-10-27 23:14 ` Ian Rogers
2026-10-27 23:15 ` [PATCH v2 06/18] perf annotate: Move struct annotation to a hashmap and remove symbol_conf.priv_size Ian Rogers
` (12 subsequent siblings)
17 siblings, 0 replies; 36+ messages in thread
From: Ian Rogers @ 2026-10-27 23:14 UTC (permalink / raw)
To: irogers, acme, namhyung
Cc: adrian.hunter, haghdoost, james.clark, jolsa, linux-kernel,
linux-perf-users, mingo, peterz
Currently, db-export reserves sizeof(u64) bytes ahead of every allocated
struct symbol via symbol_conf.priv_size and accesses the database ID at
a negative offset via symbol__priv(). This adds 8 bytes to every loaded
symbol across all DSOs even when only a subset of symbols are exported,
and negative-offset pointer arithmetic ahead of struct symbol is
incompatible with wrapping struct symbol in DECLARE_RC_STRUCT(symbol).
Store exported symbol database IDs in an on-demand sym_db_ids hashmap in
struct db_export instead of using symbol__priv().
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/db-export.c | 68 ++++++++++++++++++++++++++++++-------
tools/perf/util/db-export.h | 8 +++++
2 files changed, 63 insertions(+), 13 deletions(-)
diff --git a/tools/perf/util/db-export.c b/tools/perf/util/db-export.c
index 732369fe0046..6ebc060bdb8f 100644
--- a/tools/perf/util/db-export.c
+++ b/tools/perf/util/db-export.c
@@ -4,21 +4,37 @@
* Copyright (c) 2014, Intel Corporation.
*/
+#include "db-export.h"
+
#include <errno.h>
+#include <stdbool.h>
+#include <stddef.h>
#include <stdlib.h>
+#include <linux/err.h>
+
+#include "call-path.h"
+#include "callchain.h"
+#include "comm.h"
#include "dso.h"
+#include "event.h"
#include "evsel.h"
+#include "hashmap.h"
#include "machine.h"
-#include "thread.h"
-#include "comm.h"
-#include "symbol.h"
#include "map.h"
-#include "event.h"
+#include "symbol.h"
#include "thread-stack.h"
-#include "callchain.h"
-#include "call-path.h"
-#include "db-export.h"
+#include "thread.h"
+
+static size_t sym_ptr_hash(long key, void *ctx __maybe_unused)
+{
+ return hash_bits((size_t)key, 16);
+}
+
+static bool sym_ptr_equal(long key1, long key2, void *ctx __maybe_unused)
+{
+ return key1 == key2;
+}
int db_export__init(struct db_export *dbe)
{
@@ -28,6 +44,8 @@ int db_export__init(struct db_export *dbe)
void db_export__exit(struct db_export *dbe)
{
+ hashmap__free(dbe->sym_db_ids);
+ dbe->sym_db_ids = NULL;
call_return_processor__free(dbe->crp);
dbe->crp = NULL;
}
@@ -156,15 +174,41 @@ int db_export__dso(struct db_export *dbe, struct dso *dso,
return 0;
}
+/*
+ * Return the database export ID assigned to @sym in @dbe->sym_db_ids, or 0
+ * if @sym has not been exported yet.
+ */
+u64 db_export__symbol_db_id(struct db_export *dbe, struct symbol *sym)
+{
+ long db_id = 0;
+
+ if (sym && dbe->sym_db_ids)
+ hashmap__find(dbe->sym_db_ids, (long)sym, &db_id);
+ return (u64)db_id;
+}
+
int db_export__symbol(struct db_export *dbe, struct symbol *sym,
struct dso *dso)
{
- u64 *sym_db_id = symbol__priv(sym);
+ long db_id;
- if (*sym_db_id)
+ if (db_export__symbol_db_id(dbe, sym))
return 0;
- *sym_db_id = ++dbe->symbol_last_db_id;
+ if (!dbe->sym_db_ids) {
+ dbe->sym_db_ids =
+ hashmap__new(sym_ptr_hash, sym_ptr_equal, NULL);
+ if (IS_ERR(dbe->sym_db_ids)) {
+ int err = PTR_ERR(dbe->sym_db_ids);
+
+ dbe->sym_db_ids = NULL;
+ return err;
+ }
+ }
+
+ db_id = ++dbe->symbol_last_db_id;
+ if (hashmap__set(dbe->sym_db_ids, (long)sym, db_id, NULL, NULL))
+ return -ENOMEM;
if (dbe->export_symbol)
return dbe->export_symbol(dbe, sym, dso);
@@ -192,12 +236,10 @@ static int db_ids_from_al(struct db_export *dbe, struct addr_location *al,
}
if (al->sym) {
- u64 *db_id = symbol__priv(al->sym);
-
err = db_export__symbol(dbe, al->sym, dso);
if (err)
return err;
- *sym_db_id = *db_id;
+ *sym_db_id = db_export__symbol_db_id(dbe, al->sym);
*offset = al->addr - symbol__start(al->sym);
}
}
diff --git a/tools/perf/util/db-export.h b/tools/perf/util/db-export.h
index 1abbfd398e3a..2e0cf2dfa281 100644
--- a/tools/perf/util/db-export.h
+++ b/tools/perf/util/db-export.h
@@ -21,6 +21,8 @@ struct call_return_processor;
struct call_path_root;
struct call_path;
struct call_return;
+struct hashmap;
+struct symbol;
struct export_sample {
union perf_event *event;
@@ -63,6 +65,11 @@ struct db_export {
u64 th_in_id, u64 comm_in_id, int flags);
struct call_return_processor *crp;
struct call_path_root *cpr;
+ /**
+ * @sym_db_ids: Hashmap mapping struct symbol pointers to their
+ * exported 64-bit database IDs.
+ */
+ struct hashmap *sym_db_ids;
u64 evsel_last_db_id;
u64 machine_last_db_id;
u64 thread_last_db_id;
@@ -90,6 +97,7 @@ int db_export__comm_thread(struct db_export *dbe, struct comm *comm,
struct thread *thread);
int db_export__dso(struct db_export *dbe, struct dso *dso,
struct machine *machine);
+u64 db_export__symbol_db_id(struct db_export *dbe, struct symbol *sym);
int db_export__symbol(struct db_export *dbe, struct symbol *sym,
struct dso *dso);
int db_export__branch_type(struct db_export *dbe, u32 branch_type,
--
2.56.0.313.g2479322e68-goog
^ permalink raw reply [flat|nested] 36+ messages in thread
* [PATCH v2 06/18] perf annotate: Move struct annotation to a hashmap and remove symbol_conf.priv_size
2026-10-27 23:14 ` [PATCH v2 00/18] perf symbol: Reference counting, sorted arrays, and memory shrinking Ian Rogers
` (4 preceding siblings ...)
2026-10-27 23:14 ` [PATCH v2 05/18] perf db-export: Store symbol database IDs in a hashmap Ian Rogers
@ 2026-10-27 23:15 ` Ian Rogers
2026-10-27 23:15 ` [PATCH v2 07/18] perf symbol: Switch backing storage from rbtree to struct symbols array Ian Rogers
` (11 subsequent siblings)
17 siblings, 0 replies; 36+ messages in thread
From: Ian Rogers @ 2026-10-27 23:15 UTC (permalink / raw)
To: irogers, acme, namhyung
Cc: adrian.hunter, haghdoost, james.clark, jolsa, linux-kernel,
linux-perf-users, mingo, peterz
Previously, enabling annotation added sizeof(struct annotation) to
symbol_conf.priv_size, bloating every loaded struct symbol in every DSO
even though only a small fraction of symbols are ever annotated. In
addition, allocating private data at a negative offset ahead of struct
symbol via symbol__priv() prevents wrapping struct symbol with
DECLARE_RC_STRUCT(symbol) for reference-count checking.
Move struct annotation storage to an on-demand hashmap in
util/annotate.c keyed by struct symbol pointer, guarded by
annotations_lock and tracked via a new SYMBOL_FLAG_ANNOTATED flag in
sym->flags so unannotated symbols pay zero lookup or teardown overhead.
If allocation fails in symbol__annotation(), return a static
empty_annotation fallback so callers that do not check for NULL remain
safe while mutating callers check for &empty_annotation.
With annotation, TUI map browser indices, and db-export symbol IDs no
longer using negative-offset space ahead of struct symbol, remove
symbol_conf.priv_size and symbol__priv().
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/tests/builtin-test.c | 1 -
tools/perf/ui/browsers/hists.c | 13 +++-
tools/perf/util/annotate.c | 132 +++++++++++++++++++++++++++++++-
tools/perf/util/annotate.h | 6 +-
tools/perf/util/sort.c | 5 +-
tools/perf/util/symbol.c | 34 ++++----
tools/perf/util/symbol.h | 16 ++--
tools/perf/util/symbol_conf.h | 1 -
8 files changed, 167 insertions(+), 41 deletions(-)
diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c
index 0b8897fd42ca..9e52a194f919 100644
--- a/tools/perf/tests/builtin-test.c
+++ b/tools/perf/tests/builtin-test.c
@@ -2050,7 +2050,6 @@ int cmd_test(int argc, const char **argv)
if (dont_fork)
sequential = true;
- symbol_conf.priv_size = sizeof(int);
symbol_conf.try_vmlinux_path = true;
diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c
index 206657039223..1f1a877f667e 100644
--- a/tools/perf/ui/browsers/hists.c
+++ b/tools/perf/ui/browsers/hists.c
@@ -796,7 +796,9 @@ static void hist_browser__show_callchain_entry(struct hist_browser *browser,
{
int color, width;
char folded_sign = callchain_list__folded(chain);
- bool show_annotated = browser->show_dso && chain->ms.sym && symbol__annotation(chain->ms.sym)->src;
+ bool show_annotated = browser->show_dso && chain->ms.sym &&
+ symbol__annotated(chain->ms.sym) &&
+ symbol__annotation(chain->ms.sym)->src;
color = HE_COLORSET_NORMAL;
width = browser->b.width - (offset + 2);
@@ -2486,6 +2488,9 @@ do_annotate(struct hist_browser *browser, struct popup_action *act)
perf_env__lookup_objdump(browser->env, &annotate_opts.objdump_path))
return 0;
+ if (!symbol__annotated(act->ms.sym))
+ return 0;
+
notes = symbol__annotation(act->ms.sym);
if (!notes->src)
return 0;
@@ -2545,7 +2550,8 @@ add_annotate_opt(struct popup_action *act, char **optstr,
if (!ms->sym)
ms->sym = symbol__new_unresolved(addr, ms->map);
- if (ms->sym == NULL || symbol__annotation(ms->sym)->src == NULL)
+ if (ms->sym == NULL || !symbol__annotated(ms->sym) ||
+ symbol__annotation(ms->sym)->src == NULL)
return 0;
if (asprintf(optstr, "Annotate %s", symbol__name(ms->sym)) < 0)
@@ -3204,7 +3210,8 @@ static int evsel__hists_browse(struct evsel *evsel, int nr_events, const char *h
do_annotate(browser, &hotkey_act);
map_symbol__exit(&hotkey_act.ms);
} else {
- if (symbol__annotation(browser->selection->sym)->src == NULL) {
+ if (!symbol__annotated(browser->selection->sym) ||
+ symbol__annotation(browser->selection->sym)->src == NULL) {
ui_browser__warning(&browser->b, delay_secs * 2,
"No samples for the \"%s\" symbol.\n\n"
"Probably appeared just in a callchain",
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c
index 4163689b6b2c..e4332800fda4 100644
--- a/tools/perf/util/annotate.c
+++ b/tools/perf/util/annotate.c
@@ -8,6 +8,9 @@
#include <errno.h>
#include <inttypes.h>
+#include <pthread.h>
+#include <stdbool.h>
+#include <stddef.h>
#include <stdlib.h>
#include "util.h" // hex_width()
#include "ui/ui.h"
@@ -35,6 +38,7 @@
#include "string2.h"
#include "dwarf-regs.h"
#include "util/event.h"
+#include "util/rwsem.h"
#include "util/sharded_mutex.h"
#include "arch/common.h"
#include "namespaces.h"
@@ -118,6 +122,7 @@ static __maybe_unused void annotated_source__delete(struct annotated_source *src
if (src == NULL)
return;
+ annotated_source__purge(src);
if (src->samples) {
hashmap__for_each_entry(src->samples, cur, bkt)
zfree(&cur->pvalue);
@@ -156,9 +161,126 @@ annotated_source__hist_entry(struct annotated_source *src, const struct evsel *e
return entry;
}
+/*
+ * Map from struct symbol pointer to struct annotation. Allocated on demand so
+ * unannotated symbols do not pay the memory cost of struct annotation.
+ * empty_annotation is returned as a fallback on allocation failure so callers
+ * that do not check for NULL do not dereference NULL, while mutating callers
+ * check for &empty_annotation and return an error.
+ */
+static struct rw_semaphore _annotations_lock;
+static struct hashmap *annotations GUARDED_BY(_annotations_lock);
+static struct annotation empty_annotation;
+
+static void annotations_lock_init(void)
+{
+ init_rwsem(&_annotations_lock);
+}
+
+static struct rw_semaphore *annotations_lock(void) LOCK_RETURNED(_annotations_lock)
+{
+ static pthread_once_t annotations_lock_once = PTHREAD_ONCE_INIT;
+
+ pthread_once(&annotations_lock_once, annotations_lock_init);
+ return &_annotations_lock;
+}
+
+static size_t sym_ptr_hash(long key, void *ctx __maybe_unused)
+{
+ return hash_bits((size_t)key, 16);
+}
+
+static bool sym_ptr_equal(long key1, long key2, void *ctx __maybe_unused)
+{
+ return key1 == key2;
+}
+
+struct annotation *symbol__annotation(struct symbol *sym)
+{
+ struct annotation *notes = NULL;
+ long val = 0;
+
+ if (!sym)
+ return NULL;
+
+ /* Fast path: look up existing annotation under read lock if flagged. */
+ if (symbol__annotated(sym)) {
+ down_read(annotations_lock());
+ if (annotations && hashmap__find(annotations, (long)sym, &val))
+ notes = (struct annotation *)val;
+ up_read(annotations_lock());
+ if (notes)
+ return notes;
+ }
+
+ down_write(annotations_lock());
+ if (!annotations) {
+ annotations = hashmap__new(sym_ptr_hash, sym_ptr_equal, NULL);
+ if (IS_ERR(annotations)) {
+ annotations = NULL;
+ up_write(annotations_lock());
+ return &empty_annotation;
+ }
+ } else if (hashmap__find(annotations, (long)sym, &val)) {
+ notes = (struct annotation *)val;
+ up_write(annotations_lock());
+ return notes;
+ }
+
+ notes = zalloc(sizeof(*notes));
+ if (!notes) {
+ up_write(annotations_lock());
+ return &empty_annotation;
+ }
+ annotation__init(notes);
+ if (hashmap__add(annotations, (long)sym, notes) < 0) {
+ free(notes);
+ if (hashmap__size(annotations) == 0) {
+ hashmap__free(annotations);
+ annotations = NULL;
+ }
+ up_write(annotations_lock());
+ return &empty_annotation;
+ }
+ symbol__set_annotated(sym, true);
+ up_write(annotations_lock());
+ return notes;
+}
+
+void symbol__annotation_delete(struct symbol *sym)
+{
+ struct annotation *notes = NULL;
+ long val = 0;
+
+ /* Unannotated symbols avoid taking annotations_lock on teardown. */
+ if (!sym || !symbol__annotated(sym))
+ return;
+
+ down_write(annotations_lock());
+ if (annotations && hashmap__delete(annotations, (long)sym, NULL, &val)) {
+ notes = (struct annotation *)val;
+ if (hashmap__size(annotations) == 0) {
+ hashmap__free(annotations);
+ annotations = NULL;
+ }
+ }
+ symbol__set_annotated(sym, false);
+ up_write(annotations_lock());
+
+ if (notes) {
+ annotation__exit(notes);
+ free(notes);
+ }
+}
+
void symbol__annotate_zero_histograms(struct symbol *sym)
{
- struct annotation *notes = symbol__annotation(sym);
+ struct annotation *notes;
+
+ if (!symbol__annotated(sym))
+ return;
+
+ notes = symbol__annotation(sym);
annotation__lock(notes);
if (notes->src != NULL) {
@@ -298,7 +420,7 @@ static int __symbol__inc_addr_samples(struct map_symbol *ms,
struct annotated_branch *annotation__get_branch(struct annotation *notes)
{
- if (notes == NULL)
+ if (notes == NULL || notes == &empty_annotation)
return NULL;
if (notes->branch == NULL)
@@ -337,6 +459,9 @@ struct annotated_source *symbol__hists(struct symbol *sym, int nr_hists)
{
struct annotation *notes = symbol__annotation(sym);
+ if (!notes || notes == &empty_annotation)
+ return NULL;
+
if (notes->src == NULL) {
notes->src = annotated_source__new();
if (notes->src == NULL)
@@ -1142,6 +1267,9 @@ int symbol__annotate(struct map_symbol *ms, struct evsel *evsel,
if (parch)
*parch = arch;
+ if (!notes || notes == &empty_annotation)
+ return -ENOMEM;
+
if (notes->src && !list_empty(¬es->src->source))
return 0;
diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h
index 1b47c1156b9a..d87de0f11a60 100644
--- a/tools/perf/util/annotate.h
+++ b/tools/perf/util/annotate.h
@@ -449,10 +449,8 @@ static inline struct sym_hist *annotation__histogram(struct annotation *notes,
struct sym_hist_entry *
annotated_source__hist_entry(struct annotated_source *src, const struct evsel *evsel, u64 offset);
-static inline struct annotation *symbol__annotation(struct symbol *sym)
-{
- return (void *)sym - symbol_conf.priv_size;
-}
+struct annotation *symbol__annotation(struct symbol *sym);
+void symbol__annotation_delete(struct symbol *sym);
int addr_map_symbol__inc_samples(struct addr_map_symbol *ams, struct perf_sample *sample);
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c
index 62cf3d40e4b6..c4eca7a98642 100644
--- a/tools/perf/util/sort.c
+++ b/tools/perf/util/sort.c
@@ -795,14 +795,15 @@ static int hist_entry__sym_ipc_snprintf(struct hist_entry *he, char *bf,
{
struct symbol *sym = he->ms.sym;
- struct annotated_branch *branch;
+ struct annotated_branch *branch = NULL;
double ipc = 0.0, coverage = 0.0;
char tmp[64];
if (!sym)
return repsep_snprintf(bf, size, "%-*s", width, "-");
- branch = symbol__annotation(sym)->branch;
+ if (symbol__annotated(sym))
+ branch = symbol__annotation(sym)->branch;
if (branch && branch->hit_cycles)
ipc = branch->hit_insn / ((double)branch->hit_cycles);
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 991167b2dfa4..5b50cca7989d 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -319,19 +319,11 @@ void symbols__fixup_end(struct rb_root_cached *symbols, bool is_kallsyms)
struct symbol *symbol__new(u64 start, u64 len, u8 binding, u8 type, const char *name)
{
size_t namelen = strlen(name) + 1;
- struct symbol *sym = calloc(1, (symbol_conf.priv_size +
- sizeof(*sym) + namelen));
+ struct symbol *sym = calloc(1, sizeof(*sym) + namelen);
+
if (sym == NULL)
return NULL;
- if (symbol_conf.priv_size) {
- if (symbol_conf.init_annotation) {
- struct annotation *notes = (void *)sym;
- annotation__init(notes);
- }
- sym = ((void *)sym) + symbol_conf.priv_size;
- }
-
sym->start = start;
sym->end = len ? start + len : start;
atomic_init(&sym->flags, (type << SYMBOL_FLAG_TYPE_SHIFT) |
@@ -347,14 +339,9 @@ struct symbol *symbol__new(u64 start, u64 len, u8 binding, u8 type, const char *
void symbol__delete(struct symbol *sym)
{
- if (symbol_conf.priv_size) {
- if (symbol_conf.init_annotation) {
- struct annotation *notes = symbol__annotation(sym);
-
- annotation__exit(notes);
- }
- }
- free(((void *)sym) - symbol_conf.priv_size);
+ if (symbol__annotated(sym))
+ symbol__annotation_delete(sym);
+ free(sym);
}
void symbol__set_ignore(struct symbol *sym, bool ignore)
@@ -389,6 +376,14 @@ void symbol__set_ifunc_alias(struct symbol *sym, bool ifunc_alias)
atomic_fetch_and(&sym->flags, ~SYMBOL_FLAG_IFUNC_ALIAS);
}
+void symbol__set_annotated(struct symbol *sym, bool annotated)
+{
+ if (annotated)
+ atomic_fetch_or(&sym->flags, SYMBOL_FLAG_ANNOTATED);
+ else
+ atomic_fetch_and(&sym->flags, ~SYMBOL_FLAG_ANNOTATED);
+}
+
static void symbol__set_idle(struct symbol *sym, bool idle)
{
uint16_t old_flags = atomic_load_explicit(&sym->flags, memory_order_relaxed);
@@ -2482,7 +2477,6 @@ int symbol__annotation_init(void)
return -1;
}
- symbol_conf.priv_size += sizeof(struct annotation);
symbol_conf.init_annotation = true;
return 0;
}
@@ -2525,8 +2519,6 @@ int symbol__init(struct perf_env *env)
if (symbol_conf.initialized)
return 0;
- symbol_conf.priv_size = PERF_ALIGN(symbol_conf.priv_size, sizeof(u64));
-
symbol__elf_init();
if (symbol_conf.try_vmlinux_path && vmlinux_path__init(env) < 0)
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index 780ea3030a6f..37e6ab1478b5 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -80,9 +80,9 @@ enum symbol_idle_kind {
#define SYMBOL_FLAG_INLINED (1 << 11)
#define SYMBOL_FLAG_ANNOTATE2 (1 << 12)
#define SYMBOL_FLAG_IFUNC_ALIAS (1 << 13)
+#define SYMBOL_FLAG_ANNOTATED (1 << 14)
/**
- * A symtab entry. When allocated this may be preceded by an annotation (see
- * symbol__annotation) and/or a browser_index (see symbol__browser_index).
+ * A symtab entry.
*/
struct symbol {
struct rb_node rb_node;
@@ -178,12 +178,19 @@ static inline bool symbol__ifunc_alias(const struct symbol *sym)
SYMBOL_FLAG_IFUNC_ALIAS) != 0;
}
+static inline bool symbol__annotated(const struct symbol *sym)
+{
+ return (atomic_load_explicit(&sym->flags, memory_order_relaxed) &
+ SYMBOL_FLAG_ANNOTATED) != 0;
+}
+
bool symbol__is_idle(struct symbol *sym, const struct dso *dso, struct perf_env *env);
void symbol__set_ignore(struct symbol *sym, bool ignore);
void symbol__set_annotate2(struct symbol *sym, bool annotate2);
void symbol__set_inlined(struct symbol *sym, bool inlined);
void symbol__set_ifunc_alias(struct symbol *sym, bool ifunc_alias);
+void symbol__set_annotated(struct symbol *sym, bool annotated);
/* symbols__for_each_entry - iterate over symbols (rb_root)
*
@@ -217,11 +224,6 @@ static inline int __symbol__join_symfs(char *bf, size_t size, const char *path)
extern int vmlinux_path__nr_entries;
extern char **vmlinux_path;
-static inline void *symbol__priv(struct symbol *sym)
-{
- return ((void *)sym) - symbol_conf.priv_size;
-}
-
struct ref_reloc_sym {
const char *name;
u64 addr;
diff --git a/tools/perf/util/symbol_conf.h b/tools/perf/util/symbol_conf.h
index 37d35f42dcc1..aaa7148c3ac1 100644
--- a/tools/perf/util/symbol_conf.h
+++ b/tools/perf/util/symbol_conf.h
@@ -42,7 +42,6 @@ struct symbol_conf {
/* Was hybrid_merge given on the command line rather than in a config file? */
bool hybrid_merge_set;
bool nanosecs;
- unsigned short priv_size;
bool try_vmlinux_path,
init_annotation,
force,
--
2.56.0.313.g2479322e68-goog
^ permalink raw reply [flat|nested] 36+ messages in thread
* [PATCH v2 07/18] perf symbol: Switch backing storage from rbtree to struct symbols array
2026-10-27 23:14 ` [PATCH v2 00/18] perf symbol: Reference counting, sorted arrays, and memory shrinking Ian Rogers
` (5 preceding siblings ...)
2026-10-27 23:15 ` [PATCH v2 06/18] perf annotate: Move struct annotation to a hashmap and remove symbol_conf.priv_size Ian Rogers
@ 2026-10-27 23:15 ` Ian Rogers
2026-10-27 23:15 ` [PATCH v2 08/18] perf symbol: Add reference counting and DECLARE_RC_STRUCT(symbol) Ian Rogers
` (10 subsequent siblings)
17 siblings, 0 replies; 36+ messages in thread
From: Ian Rogers @ 2026-10-27 23:15 UTC (permalink / raw)
To: irogers, acme, namhyung
Cc: adrian.hunter, haghdoost, james.clark, jolsa, linux-kernel,
linux-perf-users, mingo, peterz
Previously, DSO symbols were stored in an unprotected rb_root_cached
with a 24-byte struct rb_node embedded inside every struct symbol. That
design had three drawbacks:
1. Embedding struct rb_node (3 pointers, 24 bytes on 64-bit) accounted
for nearly 40% of struct symbol's fixed header size and scattered
symbol lookups across non-contiguous heap nodes.
2. Unlike struct dsos and struct maps, the symbol rbtree had no lock
protecting concurrent lookups, sorting, and insertions.
3. Reclaiming unreferenced symbols under memory pressure requires
compacting the symbol container in place under a lock.
Replace struct rb_node in struct symbol with a locked, lazily-sorted
pointer array in struct symbols, matching the design of struct dsos and
struct maps:
- Introduce struct symbols containing a rw_semaphore lock, primary
address-sorted symbols array, secondary name-sorted symbols_by_name
array, counts, capacity, and sorted state flags.
- Lazily sort symbols by [start, end) using qsort (symbols__sort_locked)
and use binary search in symbols__find().
- Update symbols__fixup_duplicate(), symbols__fixup_end(),
maps__split_kallsyms_for_kcore(), and maps__split_kallsyms() to
operate on the sorted array and compact entries in place.
- Convert symbols__for_each_entry(), dso__for_each_symbol(), and
map__for_each_symbol() to callback-based iteration under
symbols->lock, matching dsos__for_each_dso() and maps__for_each_map().
- Remove the unsynchronized single-entry dso->last_find_result cache and
dso__reset_find_symbol_cache() since binary search over a contiguous
array is cache-friendly.
- Remove the direct rb_erase_cached() workaround in builtin-annotate.c's
add_sample().
- Update the TUI map browser (ui/browsers/map.c) to snapshot the sorted
symbol array into an entries array and use ui_browser__argv_refresh
and ui_browser__argv_seek instead of ui_browser__rb_tree_refresh.
Using 'perf bench internals symbols':
Before (perf binary):
Average DSO load + sort: 7.041 msec (+- 0.051 msec)
Average DSO memory usage: 1401 KB (+- 4 KB)
Average bulk insert + lookup: 1453.950 usec (+- 10.895 usec)
After (perf binary, -13.5% load, -14.6% memory, -7.7% bulk):
Average DSO load + sort: 6.089 msec (+- 0.039 msec)
Average DSO memory usage: 1197 KB (+- 3 KB)
Average bulk insert + lookup: 1341.500 usec (+- 21.564 usec)
Before (libLLVM.so.21.1):
Average DSO load + sort: 78.599 msec (+- 0.857 msec)
Average DSO memory usage: 9530 KB (+- 5 KB)
After (libLLVM.so.21.1, -22.3% load, -8.1% memory):
Average DSO load + sort: 61.074 msec (+- 0.236 msec)
Average DSO memory usage: 8755 KB (+- 1 KB)
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/arch/powerpc/util/sym-handling.c | 34 +-
tools/perf/builtin-annotate.c | 11 -
tools/perf/builtin-kmem.c | 51 +-
tools/perf/tests/hists_common.c | 5 +-
tools/perf/tests/symbols.c | 62 ++-
tools/perf/tests/vmlinux-kallsyms.c | 185 +++----
tools/perf/ui/browser.c | 3 +
tools/perf/ui/browsers/hists.c | 3 +-
tools/perf/ui/browsers/map.c | 62 ++-
tools/perf/util/auxtrace.c | 109 ++--
tools/perf/util/db-export.c | 4 +-
tools/perf/util/dso.c | 50 +-
tools/perf/util/dso.h | 68 +--
tools/perf/util/intel-pt.c | 74 ++-
tools/perf/util/libbfd.c | 3 +-
tools/perf/util/machine.c | 30 +-
tools/perf/util/map.c | 25 +-
tools/perf/util/map.h | 8 +-
tools/perf/util/probe-event.c | 98 ++--
tools/perf/util/symbol-elf.c | 12 +-
tools/perf/util/symbol.c | 571 +++++++++++++-------
tools/perf/util/symbol.h | 72 ++-
tools/perf/util/symbol_fprintf.c | 25 +-
23 files changed, 941 insertions(+), 624 deletions(-)
diff --git a/tools/perf/arch/powerpc/util/sym-handling.c b/tools/perf/arch/powerpc/util/sym-handling.c
index d73bfdcdbec1..0ec327930b0a 100644
--- a/tools/perf/arch/powerpc/util/sym-handling.c
+++ b/tools/perf/arch/powerpc/util/sym-handling.c
@@ -115,13 +115,27 @@ void arch__fix_tev_from_maps(struct perf_probe_event *pev,
}
#ifdef HAVE_LIBELF_SUPPORT
+struct post_process_probe_trace_events_args {
+ struct perf_probe_event *pev;
+ struct probe_trace_event *tev;
+ struct map *map;
+};
+
+static int post_process_probe_trace_events_cb(struct symbol *sym, void *data)
+{
+ struct post_process_probe_trace_events_args *args = data;
+
+ if (map__unmap_ip(args->map, symbol__start(sym)) == args->tev->point.address) {
+ arch__fix_tev_from_maps(args->pev, args->tev, args->map, sym);
+ return 1;
+ }
+ return 0;
+}
+
void arch__post_process_probe_trace_events(struct perf_probe_event *pev,
int ntevs)
{
- struct probe_trace_event *tev;
struct map *map;
- struct symbol *sym = NULL;
- struct rb_node *tmp;
int i = 0;
map = get_target_map(pev->target, pev->nsi, pev->uprobes);
@@ -129,13 +143,13 @@ void arch__post_process_probe_trace_events(struct perf_probe_event *pev,
return;
for (i = 0; i < ntevs; i++) {
- tev = &pev->tevs[i];
- map__for_each_symbol(map, sym, tmp) {
- if (map__unmap_ip(map, symbol__start(sym)) == tev->point.address) {
- arch__fix_tev_from_maps(pev, tev, map, sym);
- break;
- }
- }
+ struct post_process_probe_trace_events_args args = {
+ .pev = pev,
+ .tev = &pev->tevs[i],
+ .map = map,
+ };
+
+ map__for_each_symbol(map, post_process_probe_trace_events_cb, &args);
}
}
#endif /* HAVE_LIBELF_SUPPORT */
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c
index c93b67986334..5ee02a25fb9c 100644
--- a/tools/perf/builtin-annotate.c
+++ b/tools/perf/builtin-annotate.c
@@ -254,17 +254,6 @@ static int add_sample(struct perf_sample *sample,
(al->sym == NULL ||
strcmp(ann->sym_hist_filter, symbol__name(al->sym)) != 0)) {
/* We're only interested in a symbol named sym_hist_filter */
- /*
- * FIXME: why isn't this done in the symbol_filter when loading
- * the DSO?
- */
- if (al->sym != NULL) {
- struct dso *dso = map__dso(al->map);
-
- rb_erase_cached(&al->sym->rb_node, dso__symbols(dso));
- symbol__delete(al->sym);
- dso__reset_find_symbol_cache(dso);
- }
return 0;
}
diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c
index 7af3fd9868a1..7070611e3619 100644
--- a/tools/perf/builtin-kmem.c
+++ b/tools/perf/builtin-kmem.c
@@ -341,13 +341,33 @@ static int callcmp(const void *a, const void *b)
return -1;
}
+static int build_alloc_func_list_cb(struct symbol *sym, void *data)
+{
+ regex_t *alloc_func_regex = data;
+ struct alloc_func *func;
+
+ if (regexec(alloc_func_regex, symbol__name(sym), 0, NULL, 0))
+ return 0;
+
+ func = realloc(alloc_func_list,
+ (nr_alloc_funcs + 1) * sizeof(*func));
+ if (func == NULL)
+ return -ENOMEM;
+
+ pr_debug("alloc func: %s\n", symbol__name(sym));
+ func[nr_alloc_funcs].start = symbol__start(sym);
+ func[nr_alloc_funcs].end = symbol__end(sym);
+ func[nr_alloc_funcs].name = symbol__name(sym);
+
+ alloc_func_list = func;
+ nr_alloc_funcs++;
+ return 0;
+}
+
static int build_alloc_func_list(void)
{
int ret;
struct map *kernel_map;
- struct symbol *sym;
- struct rb_node *node;
- struct alloc_func *func;
struct machine *machine = &kmem_session->machines.host;
regex_t alloc_func_regex;
static const char pattern[] = "^_?_?(alloc|get_free|get_zeroed)_pages?";
@@ -364,30 +384,17 @@ static int build_alloc_func_list(void)
kernel_map = machine__kernel_map(machine);
if (map__load(kernel_map) < 0) {
pr_err("cannot load kernel map\n");
+ regfree(&alloc_func_regex);
return -ENOENT;
}
- map__for_each_symbol(kernel_map, sym, node) {
- if (regexec(&alloc_func_regex, symbol__name(sym), 0, NULL, 0))
- continue;
-
- func = realloc(alloc_func_list,
- (nr_alloc_funcs + 1) * sizeof(*func));
- if (func == NULL)
- return -ENOMEM;
-
- pr_debug("alloc func: %s\n", symbol__name(sym));
- func[nr_alloc_funcs].start = symbol__start(sym);
- func[nr_alloc_funcs].end = symbol__end(sym);
- func[nr_alloc_funcs].name = symbol__name(sym);
-
- alloc_func_list = func;
- nr_alloc_funcs++;
- }
+ ret = map__for_each_symbol(kernel_map, build_alloc_func_list_cb, &alloc_func_regex);
+ regfree(&alloc_func_regex);
+ if (ret)
+ return ret;
- qsort(alloc_func_list, nr_alloc_funcs, sizeof(*func), funcmp);
+ qsort(alloc_func_list, nr_alloc_funcs, sizeof(*alloc_func_list), funcmp);
- regfree(&alloc_func_regex);
return 0;
}
diff --git a/tools/perf/tests/hists_common.c b/tools/perf/tests/hists_common.c
index 0db3ea9cf62e..beb147e2a654 100644
--- a/tools/perf/tests/hists_common.c
+++ b/tools/perf/tests/hists_common.c
@@ -146,7 +146,10 @@ struct machine *setup_fake_machine(struct machines *machines)
goto out;
}
- symbols__insert(dso__symbols(dso), sym);
+ if (symbols__insert(dso__symbols(dso), sym)) {
+ dso__put(dso);
+ goto out;
+ }
}
dso__put(dso);
diff --git a/tools/perf/tests/symbols.c b/tools/perf/tests/symbols.c
index 456ef7a70e28..8ccf977aa9f7 100644
--- a/tools/perf/tests/symbols.c
+++ b/tools/perf/tests/symbols.c
@@ -113,39 +113,50 @@ static int create_map(struct test_info *ti, char *filename, struct map **map_p)
return TEST_OK;
}
+struct test_dso_args {
+ struct symbol *last_sym;
+ int ret;
+};
+
+static int test_dso_cb(struct symbol *sym, void *data)
+{
+ struct test_dso_args *args = data;
+ struct symbol *last_sym = args->last_sym;
+
+ if (symbol__type(sym) != STT_FUNC && symbol__type(sym) != STT_GNU_IFUNC)
+ return 0;
+
+ /* Check for overlapping function symbols */
+ if (last_sym && symbol__start(sym) < symbol__end(last_sym)) {
+ pr_debug("Overlapping symbols:\n");
+ symbol__fprintf(last_sym, stderr);
+ symbol__fprintf(sym, stderr);
+ args->ret = TEST_FAIL;
+ }
+ /* Check for zero-length function symbol */
+ if (symbol__start(sym) == symbol__end(sym)) {
+ pr_debug("Zero-length symbol:\n");
+ symbol__fprintf(sym, stderr);
+ args->ret = TEST_FAIL;
+ }
+ args->last_sym = sym;
+ return 0;
+}
+
static int test_dso(struct dso *dso)
{
- struct symbol *last_sym = NULL;
- struct rb_node *nd;
- int ret = TEST_OK;
+ struct test_dso_args args = {
+ .last_sym = NULL,
+ .ret = TEST_OK,
+ };
/* dso__fprintf() prints all the symbols */
if (verbose > 1)
dso__fprintf(dso, stderr);
- for (nd = rb_first_cached(dso__symbols(dso)); nd; nd = rb_next(nd)) {
- struct symbol *sym = rb_entry(nd, struct symbol, rb_node);
-
- if (symbol__type(sym) != STT_FUNC && symbol__type(sym) != STT_GNU_IFUNC)
- continue;
+ dso__for_each_symbol(dso, test_dso_cb, &args);
- /* Check for overlapping function symbols */
- if (last_sym && symbol__start(sym) < symbol__end(last_sym)) {
- pr_debug("Overlapping symbols:\n");
- symbol__fprintf(last_sym, stderr);
- symbol__fprintf(sym, stderr);
- ret = TEST_FAIL;
- }
- /* Check for zero-length function symbol */
- if (symbol__start(sym) == symbol__end(sym)) {
- pr_debug("Zero-length symbol:\n");
- symbol__fprintf(sym, stderr);
- ret = TEST_FAIL;
- }
- last_sym = sym;
- }
-
- return ret;
+ return args.ret;
}
static int subdivided_dso_cb(struct dso *dso, struct machine *machine __maybe_unused, void *d)
@@ -318,3 +329,4 @@ struct test_suite suite__symbols = {
.desc = "Symbols",
.test_cases = tests__symbols,
};
+
diff --git a/tools/perf/tests/vmlinux-kallsyms.c b/tools/perf/tests/vmlinux-kallsyms.c
index 9b6f2d836d8f..bef40c194d70 100644
--- a/tools/perf/tests/vmlinux-kallsyms.c
+++ b/tools/perf/tests/vmlinux-kallsyms.c
@@ -113,6 +113,7 @@ static bool is_ignored_symbol(const char *name, char type)
struct test__vmlinux_matches_kallsyms_cb_args {
struct machine kallsyms;
struct map *vmlinux_map;
+ int err;
bool header_printed;
};
@@ -185,16 +186,101 @@ static int test__vmlinux_matches_kallsyms_cb3(struct map *map, void *data)
return 0;
}
+static int test__vmlinux_matches_kallsyms_sym_cb(struct symbol *sym, void *data)
+{
+ struct test__vmlinux_matches_kallsyms_cb_args *args = data;
+ /*
+ * Step 4:
+ *
+ * kallsyms will be internally on demand sorted by name so that we can
+ * find the reference relocation * symbol, i.e. the symbol we will use
+ * to see if the running kernel was relocated by checking if it has the
+ * same value in the vmlinux file we load.
+ */
+ struct map *kallsyms_map = machine__kernel_map(&args->kallsyms);
+ struct symbol *pair, *first_pair;
+ u64 mem_start, mem_end;
+
+ if (symbol__start(sym) == symbol__end(sym))
+ return 0;
+
+ mem_start = map__unmap_ip(args->vmlinux_map, symbol__start(sym));
+ mem_end = map__unmap_ip(args->vmlinux_map, symbol__end(sym));
+
+ first_pair = machine__find_kernel_symbol(&args->kallsyms, mem_start, NULL);
+ pair = first_pair;
+
+ if (pair && UM(symbol__start(pair)) == mem_start) {
+next_pair:
+ if (arch__compare_symbol_names(symbol__name(sym), symbol__name(pair)) == 0) {
+ /*
+ * kallsyms don't have the symbol end, so we
+ * set that by using the next symbol start - 1,
+ * in some cases we get this up to a page
+ * wrong, trace_kmalloc when I was developing
+ * this code was one such example, 2106 bytes
+ * off the real size. More than that and we
+ * _really_ have a problem.
+ */
+ s64 skew = mem_end - UM(symbol__end(pair));
+
+ if (llabs(skew) >= page_size) {
+ pr_debug("WARN: %#" PRIx64 ": diff end addr for %s v: %#"
+ PRIx64 " k: %#" PRIx64 "\n",
+ mem_start, symbol__name(sym), mem_end,
+ UM(symbol__end(pair)));
+ }
+
+ /*
+ * Do not count this as a failure, because we
+ * could really find a case where it's not
+ * possible to get proper function end from
+ * kallsyms.
+ */
+ return 0;
+ }
+ pair = machine__find_kernel_symbol_by_name(&args->kallsyms,
+ symbol__name(sym), NULL);
+ if (pair) {
+ if (UM(symbol__start(pair)) == mem_start)
+ goto next_pair;
+
+ pr_debug("WARN: %#" PRIx64 ": diff name v: %s k: %s\n",
+ mem_start, symbol__name(sym), symbol__name(pair));
+ } else {
+ pr_debug("WARN: %#" PRIx64 ": diff name v: %s k: %s\n",
+ mem_start, symbol__name(sym), symbol__name(first_pair));
+ }
+
+ return 0;
+ }
+
+ if (mem_start == map__end(args->kallsyms.vmlinux_map)) {
+ /*
+ * Ignore aliases to _etext, i.e. to the end of the kernel text area,
+ * such as __indirect_thunk_end.
+ */
+ return 0;
+ }
+ if (is_ignored_symbol(symbol__name(sym), symbol__type(sym))) {
+ /*
+ * Ignore hidden symbols, see scripts/kallsyms.c for the details
+ */
+ return 0;
+ }
+ pr_debug("ERR : %#" PRIx64 ": %s not on kallsyms\n",
+ mem_start, symbol__name(sym));
+
+ args->err = -1;
+ return 0;
+}
+
static int test__vmlinux_matches_kallsyms(struct test_suite *test __maybe_unused,
int subtest __maybe_unused)
{
int err = TEST_FAIL;
- struct rb_node *nd;
- struct symbol *sym;
- struct map *kallsyms_map;
struct machine vmlinux = { 0 };
struct maps *maps;
- u64 mem_start, mem_end;
struct test__vmlinux_matches_kallsyms_cb_args args;
/*
@@ -240,16 +326,6 @@ static int test__vmlinux_matches_kallsyms(struct test_suite *test __maybe_unused
goto out;
}
- /*
- * Step 4:
- *
- * kallsyms will be internally on demand sorted by name so that we can
- * find the reference relocation * symbol, i.e. the symbol we will use
- * to see if the running kernel was relocated by checking if it has the
- * same value in the vmlinux file we load.
- */
- kallsyms_map = machine__kernel_map(&args.kallsyms);
-
/*
* Step 5:
*
@@ -279,7 +355,7 @@ static int test__vmlinux_matches_kallsyms(struct test_suite *test __maybe_unused
goto out;
}
- err = 0;
+ args.err = 0;
/*
* Step 7:
*
@@ -287,83 +363,8 @@ static int test__vmlinux_matches_kallsyms(struct test_suite *test __maybe_unused
* in the kallsyms dso. For the ones that are in both, check its names and
* end addresses too.
*/
- map__for_each_symbol(args.vmlinux_map, sym, nd) {
- struct symbol *pair, *first_pair;
-
- sym = rb_entry(nd, struct symbol, rb_node);
-
- if (symbol__start(sym) == symbol__end(sym))
- continue;
-
- mem_start = map__unmap_ip(args.vmlinux_map, symbol__start(sym));
- mem_end = map__unmap_ip(args.vmlinux_map, symbol__end(sym));
-
- first_pair = machine__find_kernel_symbol(&args.kallsyms, mem_start, NULL);
- pair = first_pair;
-
- if (pair && UM(symbol__start(pair)) == mem_start) {
-next_pair:
- if (arch__compare_symbol_names(symbol__name(sym),
- symbol__name(pair)) == 0) {
- /*
- * kallsyms don't have the symbol end, so we
- * set that by using the next symbol start - 1,
- * in some cases we get this up to a page
- * wrong, trace_kmalloc when I was developing
- * this code was one such example, 2106 bytes
- * off the real size. More than that and we
- * _really_ have a problem.
- */
- s64 skew = mem_end - UM(symbol__end(pair));
-
- if (llabs(skew) >= page_size) {
- pr_debug("WARN: %#" PRIx64 ": diff end addr for %s v: %#" PRIx64 " k: %#" PRIx64 "\n",
- mem_start, symbol__name(sym), mem_end,
- UM(symbol__end(pair)));
- }
-
- /*
- * Do not count this as a failure, because we
- * could really find a case where it's not
- * possible to get proper function end from
- * kallsyms.
- */
- continue;
- } else {
- pair = machine__find_kernel_symbol_by_name(&args.kallsyms,
- symbol__name(sym), NULL);
- if (pair) {
- if (UM(symbol__start(pair)) == mem_start)
- goto next_pair;
-
- pr_debug("WARN: %#" PRIx64 ": diff name v: %s k: %s\n",
- mem_start, symbol__name(sym), symbol__name(pair));
- } else {
- pr_debug("WARN: %#" PRIx64 ": diff name v: %s k: %s\n",
- mem_start, symbol__name(sym),
- symbol__name(first_pair));
- }
-
- continue;
- }
- } else if (mem_start == map__end(args.kallsyms.vmlinux_map)) {
- /*
- * Ignore aliases to _etext, i.e. to the end of the kernel text area,
- * such as __indirect_thunk_end.
- */
- continue;
- } else if (is_ignored_symbol(symbol__name(sym), symbol__type(sym))) {
- /*
- * Ignore hidden symbols, see scripts/kallsyms.c for the details
- */
- continue;
- } else {
- pr_debug("ERR : %#" PRIx64 ": %s not on kallsyms\n",
- mem_start, symbol__name(sym));
- }
-
- err = -1;
- }
+ map__for_each_symbol(args.vmlinux_map, test__vmlinux_matches_kallsyms_sym_cb, &args);
+ err = args.err;
if (verbose <= 0)
goto out;
diff --git a/tools/perf/ui/browser.c b/tools/perf/ui/browser.c
index 321187b204d3..c7451f640d1d 100644
--- a/tools/perf/ui/browser.c
+++ b/tools/perf/ui/browser.c
@@ -622,6 +622,9 @@ static int ui_browser__color_config(const char *var, const char *value,
void ui_browser__argv_seek(struct ui_browser *browser, off_t offset, int whence)
{
+ if (browser->nr_entries == 0)
+ return;
+
switch (whence) {
case SEEK_SET:
browser->top = browser->entries;
diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c
index 1f1a877f667e..5147b7dca256 100644
--- a/tools/perf/ui/browsers/hists.c
+++ b/tools/perf/ui/browsers/hists.c
@@ -2531,7 +2531,8 @@ static struct symbol *symbol__new_unresolved(u64 addr, struct map *map)
return NULL;
}
- dso__insert_symbol(map__dso(map), sym);
+ if (dso__insert_symbol(map__dso(map), sym))
+ return NULL;
}
return sym;
diff --git a/tools/perf/ui/browsers/map.c b/tools/perf/ui/browsers/map.c
index 2f070a53b5cb..71f6161ed27a 100644
--- a/tools/perf/ui/browsers/map.c
+++ b/tools/perf/ui/browsers/map.c
@@ -24,7 +24,7 @@ struct map_browser {
static void map_browser__write(struct ui_browser *browser, void *nd, int row)
{
- struct symbol *sym = rb_entry(nd, struct symbol, rb_node);
+ struct symbol *sym = *(struct symbol **)nd;
struct map_browser *mb = container_of(browser, struct map_browser, b);
bool current_entry = ui_browser__is_current_entry(browser, row);
int width;
@@ -56,17 +56,16 @@ static int map_browser__search(struct map_browser *browser)
sym = map__find_symbol_by_name(browser->map, target);
if (sym != NULL) {
- struct rb_node *nd;
- u32 idx = 0;
+ struct symbol **entries = browser->b.entries;
/*
- * Walk the map browser's symbol entries to find the matching
- * symbol node and its display row index, then position the
- * browser cursor at that entry.
+ * Scan the map browser's symbol entries to find the matching
+ * symbol and its display row index, then position the browser
+ * cursor at that entry.
*/
- for (nd = rb_first(browser->b.entries); nd; nd = rb_next(nd), ++idx) {
- if (&sym->rb_node == nd) {
- browser->b.top = nd;
+ for (u32 idx = 0; idx < browser->b.nr_entries; ++idx) {
+ if (entries[idx] == sym) {
+ browser->b.top = &entries[idx];
browser->b.index = browser->b.top_idx = idx;
break;
}
@@ -109,27 +108,48 @@ static int map_browser__run(struct map_browser *browser)
int map__browse(struct map *map)
{
+ struct symbols *symbols = dso__symbols(map__dso(map));
struct map_browser mb = {
.b = {
- .entries = dso__symbols(map__dso(map)),
- .refresh = ui_browser__rb_tree_refresh,
- .seek = ui_browser__rb_tree_seek,
+ .refresh = ui_browser__argv_refresh,
+ .seek = ui_browser__argv_seek,
.write = map_browser__write,
},
.map = map,
};
- struct rb_node *nd;
+ struct symbol **entries = NULL;
+ unsigned int nr_entries = 0;
char tmp[BITS_PER_LONG / 4];
u64 maxaddr = 0;
-
- for (nd = rb_first(mb.b.entries); nd; nd = rb_next(nd)) {
- struct symbol *pos = rb_entry(nd, struct symbol, rb_node);
-
- if (maxaddr < symbol__end(pos))
- maxaddr = symbol__end(pos);
- ++mb.b.nr_entries;
+ int ret;
+
+ /*
+ * Snapshot the DSO's sorted symbol array under symbols->lock for use
+ * with ui_browser__argv_refresh and ui_browser__argv_seek.
+ */
+ symbols__sort_read_lock(symbols);
+ nr_entries = symbols->cnt;
+ if (nr_entries > 0) {
+ entries = malloc(nr_entries * sizeof(*entries));
+ if (entries) {
+ for (unsigned int idx = 0; idx < nr_entries; idx++) {
+ struct symbol *pos = symbols->symbols[idx];
+
+ entries[idx] = pos;
+ if (maxaddr < symbol__end(pos))
+ maxaddr = symbol__end(pos);
+ }
+ } else {
+ nr_entries = 0;
+ }
}
+ up_read(&symbols->lock);
+
+ mb.b.entries = entries;
+ mb.b.nr_entries = nr_entries;
mb.addrlen = snprintf(tmp, sizeof(tmp), "%" PRIx64, maxaddr);
- return map_browser__run(&mb);
+ ret = map_browser__run(&mb);
+ free(entries);
+ return ret;
}
diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c
index b3cc7b17aca4..a51c871d730b 100644
--- a/tools/perf/util/auxtrace.c
+++ b/tools/perf/util/auxtrace.c
@@ -2713,61 +2713,96 @@ static bool dso_sym_match(struct symbol *sym, const char *name, int *cnt,
idx < 0);
}
+struct print_duplicate_syms_args {
+ const char *sym_name;
+ bool near;
+ int cnt;
+};
+
+static int print_duplicate_syms_cb(struct symbol *sym, void *data)
+{
+ struct print_duplicate_syms_args *args = data;
+
+ if (dso_sym_match(sym, args->sym_name, &args->cnt, -1)) {
+ pr_err("#%d\t0x%"PRIx64"\t%c\t%s\n",
+ ++args->cnt, symbol__start(sym),
+ symbol__binding(sym) == STB_GLOBAL ? 'g' :
+ symbol__binding(sym) == STB_LOCAL ? 'l' : 'w',
+ symbol__name(sym));
+ args->near = true;
+ } else if (args->near) {
+ args->near = false;
+ pr_err("\t\twhich is near\t\t%s\n", symbol__name(sym));
+ }
+ return 0;
+}
+
static void print_duplicate_syms(struct dso *dso, const char *sym_name)
{
- struct symbol *sym;
- bool near = false;
- int cnt = 0;
+ struct print_duplicate_syms_args args = {
+ .sym_name = sym_name,
+ .near = false,
+ .cnt = 0,
+ };
pr_err("Multiple symbols with name '%s'\n", sym_name);
- sym = dso__first_symbol(dso);
- while (sym) {
- if (dso_sym_match(sym, sym_name, &cnt, -1)) {
- pr_err("#%d\t0x%"PRIx64"\t%c\t%s\n",
- ++cnt, symbol__start(sym),
- symbol__binding(sym) == STB_GLOBAL ? 'g' :
- symbol__binding(sym) == STB_LOCAL ? 'l' : 'w',
- symbol__name(sym));
- near = true;
- } else if (near) {
- near = false;
- pr_err("\t\twhich is near\t\t%s\n", symbol__name(sym));
- }
- sym = dso__next_symbol(sym);
- }
+ dso__for_each_symbol(dso, print_duplicate_syms_cb, &args);
pr_err("Disambiguate symbol name by inserting #n after the name e.g. %s #2\n",
sym_name);
pr_err("Or select a global symbol by inserting #0 or #g or #G\n");
}
+struct find_dso_sym_args {
+ const char *sym_name;
+ u64 *start;
+ u64 *size;
+ int idx;
+ int cnt;
+ bool duplicate;
+};
+
+static int find_dso_sym_cb(struct symbol *sym, void *data)
+{
+ struct find_dso_sym_args *args = data;
+
+ if (*args->start) {
+ if (!*args->size)
+ *args->size = symbol__start(sym) - *args->start;
+ if (args->idx > 0) {
+ if (*args->size)
+ return 1;
+ } else if (dso_sym_match(sym, args->sym_name, &args->cnt, args->idx)) {
+ args->duplicate = true;
+ return 1;
+ }
+ } else if (dso_sym_match(sym, args->sym_name, &args->cnt, args->idx)) {
+ *args->start = symbol__start(sym);
+ *args->size = symbol__end(sym) - symbol__start(sym);
+ }
+ return 0;
+}
+
static int find_dso_sym(struct dso *dso, const char *sym_name, u64 *start,
u64 *size, int idx)
{
- struct symbol *sym;
- int cnt = 0;
+ struct find_dso_sym_args args = {
+ .sym_name = sym_name,
+ .start = start,
+ .size = size,
+ .idx = idx,
+ .cnt = 0,
+ .duplicate = false,
+ };
*start = 0;
*size = 0;
- sym = dso__first_symbol(dso);
- while (sym) {
- if (*start) {
- if (!*size)
- *size = symbol__start(sym) - *start;
- if (idx > 0) {
- if (*size)
- return 0;
- } else if (dso_sym_match(sym, sym_name, &cnt, idx)) {
- print_duplicate_syms(dso, sym_name);
- return -EINVAL;
- }
- } else if (dso_sym_match(sym, sym_name, &cnt, idx)) {
- *start = symbol__start(sym);
- *size = symbol__end(sym) - symbol__start(sym);
- }
- sym = dso__next_symbol(sym);
+ dso__for_each_symbol(dso, find_dso_sym_cb, &args);
+ if (args.duplicate) {
+ print_duplicate_syms(dso, sym_name);
+ return -EINVAL;
}
if (!*start)
diff --git a/tools/perf/util/db-export.c b/tools/perf/util/db-export.c
index 6ebc060bdb8f..97ce6061a77d 100644
--- a/tools/perf/util/db-export.c
+++ b/tools/perf/util/db-export.c
@@ -231,8 +231,8 @@ static int db_ids_from_al(struct db_export *dbe, struct addr_location *al,
if (!al->sym) {
al->sym = symbol__new(al->addr, 0, 0, 0, "unknown");
- if (al->sym)
- dso__insert_symbol(dso, al->sym);
+ if (al->sym && dso__insert_symbol(dso, al->sym))
+ al->sym = NULL;
}
if (al->sym) {
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c
index 39a1eb9cb73f..35a5c65030af 100644
--- a/tools/perf/util/dso.c
+++ b/tools/perf/util/dso.c
@@ -1808,12 +1808,7 @@ bool dso__loaded(const struct dso *dso)
bool dso__sorted_by_name(const struct dso *dso)
{
- return RC_CHK_ACCESS(dso)->sorted_by_name;
-}
-
-void dso__set_sorted_by_name(struct dso *dso)
-{
- RC_CHK_ACCESS(dso)->sorted_by_name = true;
+ return RC_CHK_ACCESS(dso)->symbols.sorted_by_name;
}
struct dso *dso__new_id(const char *name, const struct dso_id *id)
@@ -1831,9 +1826,7 @@ struct dso *dso__new_id(const char *name, const struct dso_id *id)
dso->id = *id;
dso__set_long_name_id(res, dso->name, false);
dso__set_short_name(res, dso->name, false);
- dso->symbols = RB_ROOT_CACHED;
- dso->symbol_names = NULL;
- dso->symbol_names_len = 0;
+ symbols__init(&dso->symbols);
dso->inlined_nodes = RB_ROOT_CACHED;
dso->srclines = RB_ROOT_CACHED;
dso->data_types = RB_ROOT;
@@ -1844,7 +1837,6 @@ struct dso *dso__new_id(const char *name, const struct dso_id *id)
dso->is_64_bit = (sizeof(void *) == 8);
dso->loaded = 0;
dso->rel = 0;
- dso->sorted_by_name = 0;
dso->has_srcline = 1;
dso->a2l_fails = 1;
dso->kernel = DSO_SPACE__USER;
@@ -1878,9 +1870,7 @@ void dso__delete(struct dso *dso)
/* free inlines first, as they reference symbols */
inlines__tree_delete(&RC_CHK_ACCESS(dso)->inlined_nodes);
srcline__tree_delete(&RC_CHK_ACCESS(dso)->srclines);
- symbols__delete(&RC_CHK_ACCESS(dso)->symbols);
- RC_CHK_ACCESS(dso)->symbol_names_len = 0;
- zfree(&RC_CHK_ACCESS(dso)->symbol_names);
+ symbols__exit(&RC_CHK_ACCESS(dso)->symbols);
annotated_data_type__tree_delete(dso__data_types(dso));
global_var_type__tree_delete(dso__global_vars(dso));
@@ -2003,22 +1993,34 @@ static size_t dso__fprintf_buildid(struct dso *dso, FILE *fp)
return fprintf(fp, "%s", sbuild_id);
}
+struct dso__fprintf_cb_args {
+ FILE *fp;
+ size_t ret;
+};
+
+static int dso__fprintf_cb(struct symbol *pos, void *data)
+{
+ struct dso__fprintf_cb_args *args = data;
+
+ args->ret += symbol__fprintf(pos, args->fp);
+ return 0;
+}
+
size_t dso__fprintf(struct dso *dso, FILE *fp)
{
- struct rb_node *nd;
- size_t ret = fprintf(fp, "dso: %s (", dso__short_name(dso));
+ struct dso__fprintf_cb_args args = {
+ .fp = fp,
+ .ret = fprintf(fp, "dso: %s (", dso__short_name(dso)),
+ };
if (dso__short_name(dso) != dso__long_name(dso))
- ret += fprintf(fp, "%s, ", dso__long_name(dso));
- ret += fprintf(fp, "%sloaded, ", dso__loaded(dso) ? "" : "NOT ");
- ret += dso__fprintf_buildid(dso, fp);
- ret += fprintf(fp, ")\n");
- for (nd = rb_first_cached(dso__symbols(dso)); nd; nd = rb_next(nd)) {
- struct symbol *pos = rb_entry(nd, struct symbol, rb_node);
- ret += symbol__fprintf(pos, fp);
- }
+ args.ret += fprintf(fp, "%s, ", dso__long_name(dso));
+ args.ret += fprintf(fp, "%sloaded, ", dso__loaded(dso) ? "" : "NOT ");
+ args.ret += dso__fprintf_buildid(dso, fp);
+ args.ret += fprintf(fp, ")\n");
+ dso__for_each_symbol(dso, dso__fprintf_cb, &args);
- return ret;
+ return args.ret;
}
enum dso_type dso__type(struct dso *dso, struct machine *machine)
diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h
index d5f7382d4e13..f23cf9957aec 100644
--- a/tools/perf/util/dso.h
+++ b/tools/perf/util/dso.h
@@ -13,6 +13,7 @@
#include "build-id.h"
#include "debuginfo.h"
#include "mutex.h"
+#include "symbol.h"
#include <internal/rc_check.h>
struct machine;
@@ -287,18 +288,12 @@ struct auxtrace_cache;
DECLARE_RC_STRUCT(dso) {
struct mutex lock;
struct dsos *dsos;
- struct rb_root_cached symbols;
- struct symbol **symbol_names;
- size_t symbol_names_len;
+ struct symbols symbols;
struct rb_root_cached inlined_nodes;
struct rb_root_cached srclines;
struct rb_root data_types;
struct rb_root global_vars;
- struct {
- u64 addr;
- struct symbol *symbol;
- } last_find_result;
u64 text_offset;
u64 text_end;
const char *short_name;
@@ -351,7 +346,6 @@ DECLARE_RC_STRUCT(dso) {
u8 is_64_bit:1;
u8 bintype_searched:1;
u8 debuginfo_searched:1;
- bool sorted_by_name;
bool loaded;
u8 rel;
char name[];
@@ -365,11 +359,14 @@ int dso_id__cmp(const struct dso_id *a, const struct dso_id *b);
/* dso__for_each_symbol - iterate over the symbols of given type
*
* @dso: the 'struct dso *' in which symbols are iterated
- * @pos: the 'struct symbol *' to use as a loop cursor
- * @n: the 'struct rb_node *' to use as a temporary storage
+ * @cb: callback function invoked for each symbol
+ * @data: opaque data pointer passed to @cb
*/
-#define dso__for_each_symbol(dso, pos, n) \
- symbols__for_each_entry(dso__symbols(dso), pos, n)
+#define dso__for_each_symbol(dso, cb, data) \
+ symbols__for_each_entry(dso__symbols(dso), cb, data)
+
+#define dso__for_each_symbol_by_name(dso, cb, data) \
+ symbols__visit_by_name(dso__symbols(dso), cb, data)
static inline void *dso__a2l(const struct dso *dso)
{
@@ -598,26 +595,6 @@ static inline void dso__set_kernel(struct dso *dso, enum dso_space_type kernel)
RC_CHK_ACCESS(dso)->kernel = kernel;
}
-static inline u64 dso__last_find_result_addr(const struct dso *dso)
-{
- return RC_CHK_ACCESS(dso)->last_find_result.addr;
-}
-
-static inline void dso__set_last_find_result_addr(struct dso *dso, u64 addr)
-{
- RC_CHK_ACCESS(dso)->last_find_result.addr = addr;
-}
-
-static inline struct symbol *dso__last_find_result_symbol(const struct dso *dso)
-{
- return RC_CHK_ACCESS(dso)->last_find_result.symbol;
-}
-
-static inline void dso__set_last_find_result_symbol(struct dso *dso, struct symbol *symbol)
-{
- RC_CHK_ACCESS(dso)->last_find_result.symbol = symbol;
-}
-
static inline enum dso_load_errno *dso__load_errno(struct dso *dso)
{
return &RC_CHK_ACCESS(dso)->load_errno;
@@ -730,31 +707,11 @@ static inline struct rb_root *dso__global_vars(struct dso *dso)
return &RC_CHK_ACCESS(dso)->global_vars;
}
-static inline struct rb_root_cached *dso__symbols(struct dso *dso)
+static inline struct symbols *dso__symbols(struct dso *dso)
{
return &RC_CHK_ACCESS(dso)->symbols;
}
-static inline struct symbol **dso__symbol_names(struct dso *dso)
-{
- return RC_CHK_ACCESS(dso)->symbol_names;
-}
-
-static inline void dso__set_symbol_names(struct dso *dso, struct symbol **names)
-{
- RC_CHK_ACCESS(dso)->symbol_names = names;
-}
-
-static inline size_t dso__symbol_names_len(struct dso *dso)
-{
- return RC_CHK_ACCESS(dso)->symbol_names_len;
-}
-
-static inline void dso__set_symbol_names_len(struct dso *dso, size_t len)
-{
- RC_CHK_ACCESS(dso)->symbol_names_len = len;
-}
-
static inline const char *dso__symsrc_filename(const struct dso *dso)
{
return RC_CHK_ACCESS(dso)->symsrc_filename;
@@ -823,13 +780,12 @@ bool dso__loaded(const struct dso *dso);
static inline bool dso__has_symbols(const struct dso *dso)
{
- return !RB_EMPTY_ROOT(&RC_CHK_ACCESS(dso)->symbols.rb_root);
+ return RC_CHK_ACCESS(dso)->symbols.cnt > 0;
}
char *dso__filename_with_chroot(const struct dso *dso, const char *filename);
bool dso__sorted_by_name(const struct dso *dso);
-void dso__set_sorted_by_name(struct dso *dso);
void dso__sort_by_name(struct dso *dso);
int dso__swap_init(struct dso *dso, unsigned char eidata);
@@ -953,8 +909,6 @@ struct map *dso__new_map(const char *name);
struct dso *machine__findnew_kernel(struct machine *machine, const char *name,
const char *short_name, int dso_type);
-void dso__reset_find_symbol_cache(struct dso *dso);
-
size_t dso__fprintf_symbols_by_name(struct dso *dso, FILE *fp);
size_t dso__fprintf(struct dso *dso, FILE *fp);
diff --git a/tools/perf/util/intel-pt.c b/tools/perf/util/intel-pt.c
index a9f43ac8d152..2e64b3faabc9 100644
--- a/tools/perf/util/intel-pt.c
+++ b/tools/perf/util/intel-pt.c
@@ -2981,13 +2981,37 @@ static int intel_pt_sample(struct intel_pt_queue *ptq)
return 0;
}
+struct intel_pt_switch_ip_args {
+ struct map *map;
+ const char *name;
+ bool check_global;
+ u64 ip;
+};
+
+static int intel_pt_switch_ip_cb(struct symbol *sym, void *data)
+{
+ struct intel_pt_switch_ip_args *args = data;
+ u64 ip;
+
+ if (args->check_global && symbol__binding(sym) != STB_GLOBAL)
+ return 0;
+
+ if (!strcmp(symbol__name(sym), args->name)) {
+ ip = map__unmap_ip(args->map, symbol__start(sym));
+ if (ip >= map__start(args->map) && ip < map__end(args->map)) {
+ args->ip = ip;
+ return 1;
+ }
+ }
+ return 0;
+}
+
static u64 intel_pt_switch_ip(struct intel_pt *pt, u64 *ptss_ip)
{
struct machine *machine = pt->machine;
struct map *map;
- struct symbol *sym, *start;
- u64 ip, switch_ip = 0;
- const char *ptss;
+ struct intel_pt_switch_ip_args args;
+ u64 switch_ip = 0;
if (ptss_ip)
*ptss_ip = 0;
@@ -2999,36 +3023,28 @@ static u64 intel_pt_switch_ip(struct intel_pt *pt, u64 *ptss_ip)
if (map__load(map))
return 0;
- start = dso__first_symbol(map__dso(map));
-
- for (sym = start; sym; sym = dso__next_symbol(sym)) {
- if (symbol__binding(sym) == STB_GLOBAL &&
- !strcmp(symbol__name(sym), "__switch_to")) {
- ip = map__unmap_ip(map, symbol__start(sym));
- if (ip >= map__start(map) && ip < map__end(map)) {
- switch_ip = ip;
- break;
- }
- }
- }
+ args = (struct intel_pt_switch_ip_args) {
+ .map = map,
+ .name = "__switch_to",
+ .check_global = true,
+ .ip = 0,
+ };
+ map__for_each_symbol(map, intel_pt_switch_ip_cb, &args);
+ switch_ip = args.ip;
if (!switch_ip || !ptss_ip)
return 0;
- if (pt->have_sched_switch == 1)
- ptss = "perf_trace_sched_switch";
- else
- ptss = "__perf_event_task_sched_out";
-
- for (sym = start; sym; sym = dso__next_symbol(sym)) {
- if (!strcmp(symbol__name(sym), ptss)) {
- ip = map__unmap_ip(map, symbol__start(sym));
- if (ip >= map__start(map) && ip < map__end(map)) {
- *ptss_ip = ip;
- break;
- }
- }
- }
+ args = (struct intel_pt_switch_ip_args) {
+ .map = map,
+ .name = pt->have_sched_switch == 1 ?
+ "perf_trace_sched_switch" :
+ "__perf_event_task_sched_out",
+ .check_global = false,
+ .ip = 0,
+ };
+ map__for_each_symbol(map, intel_pt_switch_ip_cb, &args);
+ *ptss_ip = args.ip;
return switch_ip;
}
diff --git a/tools/perf/util/libbfd.c b/tools/perf/util/libbfd.c
index 1a5d38b8e5f4..5e947271e388 100644
--- a/tools/perf/util/libbfd.c
+++ b/tools/perf/util/libbfd.c
@@ -453,7 +453,8 @@ int dso__load_bfd_symbols(struct dso *dso, const char *debugfile)
if (!symbol)
goto out_free;
- symbols__insert(dso__symbols(dso), symbol);
+ if (symbols__insert(dso__symbols(dso), symbol))
+ goto out_free;
}
#ifdef bfd_get_section
#undef bfd_asymbol_section
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index 6a10c3036383..14e93e7fb235 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -796,7 +796,7 @@ static int machine__process_ksymbol_register(struct machine *machine,
err = -ENOMEM;
goto out;
}
- dso__insert_symbol(dso, sym);
+ err = dso__insert_symbol(dso, sym);
out:
map__put(map);
dso__put(dso);
@@ -1095,29 +1095,35 @@ int machine__create_extra_kernel_map(struct machine *machine,
return err;
}
-static u64 find_entry_trampoline(struct dso *dso)
+static int find_entry_trampoline_cb(struct symbol *sym, void *data)
{
/* Duplicates are removed so lookup all aliases */
- const char *syms[] = {
+ static const char * const syms[] = {
"_entry_trampoline",
"__entry_trampoline_start",
"entry_SYSCALL_64_trampoline",
};
- struct symbol *sym = dso__first_symbol(dso);
- unsigned int i;
+ u64 *addr = data;
- for (; sym; sym = dso__next_symbol(sym)) {
- if (symbol__binding(sym) != STB_GLOBAL)
- continue;
- for (i = 0; i < ARRAY_SIZE(syms); i++) {
- if (!strcmp(symbol__name(sym), syms[i]))
- return symbol__start(sym);
+ if (symbol__binding(sym) != STB_GLOBAL)
+ return 0;
+ for (unsigned int i = 0; i < ARRAY_SIZE(syms); i++) {
+ if (!strcmp(symbol__name(sym), syms[i])) {
+ *addr = symbol__start(sym);
+ return 1;
}
}
-
return 0;
}
+static u64 find_entry_trampoline(struct dso *dso)
+{
+ u64 addr = 0;
+
+ dso__for_each_symbol(dso, find_entry_trampoline_cb, &addr);
+ return addr;
+}
+
/*
* These values can be used for kernels that do not have symbols for the entry
* trampolines in kallsyms.
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c
index 85216fa71f1a..19afe676adf2 100644
--- a/tools/perf/util/map.c
+++ b/tools/perf/util/map.c
@@ -314,27 +314,22 @@ void map__put(struct map *map)
void map__fixup_start(struct map *map)
{
- struct dso *dso = map__dso(map);
- struct rb_root_cached *symbols = dso__symbols(dso);
- struct rb_node *nd = rb_first_cached(symbols);
-
- if (nd != NULL) {
- struct symbol *sym = rb_entry(nd, struct symbol, rb_node);
+ struct symbols *symbols = dso__symbols(map__dso(map));
- map__set_start(map, symbol__start(sym));
- }
+ symbols__sort_read_lock(symbols);
+ if (symbols->cnt > 0)
+ map__set_start(map, symbol__start(symbols->symbols[0]));
+ up_read(&symbols->lock);
}
void map__fixup_end(struct map *map)
{
- struct dso *dso = map__dso(map);
- struct rb_root_cached *symbols = dso__symbols(dso);
- struct rb_node *nd = rb_last(&symbols->rb_root);
+ struct symbols *symbols = dso__symbols(map__dso(map));
- if (nd != NULL) {
- struct symbol *sym = rb_entry(nd, struct symbol, rb_node);
- map__set_end(map, symbol__end(sym));
- }
+ symbols__sort_read_lock(symbols);
+ if (symbols->cnt > 0)
+ map__set_end(map, symbol__end(symbols->symbols[symbols->cnt - 1]));
+ up_read(&symbols->lock);
}
#define DSO__DELETED "(deleted)"
diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h
index f49b4aaacf2f..3d8f72e70d49 100644
--- a/tools/perf/util/map.h
+++ b/tools/perf/util/map.h
@@ -146,12 +146,12 @@ struct thread;
/* map__for_each_symbol - iterate over the symbols in the given map
*
* @map: the 'struct map *' in which symbols are iterated
- * @pos: the 'struct symbol *' to use as a loop cursor
- * @n: the 'struct rb_node *' to use as a temporary storage
+ * @cb: callback function invoked for each symbol
+ * @data: opaque data pointer passed to @cb
* Note: caller must ensure map->dso is not NULL (map is loaded).
*/
-#define map__for_each_symbol(map, pos, n) \
- dso__for_each_symbol(map__dso(map), pos, n)
+#define map__for_each_symbol(map, cb, data) \
+ dso__for_each_symbol(map__dso(map), cb, data)
/* map__for_each_symbol_with_name - iterate over the symbols in the given map
* that have the given name
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 2d554fed0c3f..bea7903a11e3 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -3047,49 +3047,63 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
return ret;
}
-static int find_probe_functions(struct map *map, char *name,
- struct symbol **syms)
+struct find_probe_functions_args {
+ const char *name;
+ struct symbol **syms;
+ int found;
+ bool cut_version;
+};
+
+static int find_probe_functions_cb(struct symbol *sym, void *data)
{
- int found = 0;
- struct symbol *sym;
- struct rb_node *tmp;
+ struct find_probe_functions_args *args = data;
const char *norm, *ver;
char *buf = NULL;
- bool cut_version = true;
- if (map__load(map) < 0)
- return -EACCES; /* Possible permission error to load symbols */
-
- /* If user gives a version, don't cut off the version from symbols */
- if (strchr(name, '@'))
- cut_version = false;
-
- map__for_each_symbol(map, sym, tmp) {
- norm = arch__normalize_symbol_name(symbol__name(sym));
- if (!norm)
- continue;
+ norm = arch__normalize_symbol_name(symbol__name(sym));
+ if (!norm)
+ return 0;
- if (cut_version) {
- /* We don't care about default symbol or not */
- ver = strchr(norm, '@');
- if (ver) {
- buf = strndup(norm, ver - norm);
- if (!buf)
- return -ENOMEM;
- norm = buf;
- }
+ if (args->cut_version) {
+ /* We don't care about default symbol or not */
+ ver = strchr(norm, '@');
+ if (ver) {
+ buf = strndup(norm, ver - norm);
+ if (!buf)
+ return -ENOMEM;
+ norm = buf;
}
+ }
- if (strglobmatch(norm, name)) {
- found++;
- if (syms && found < probe_conf.max_probes)
- syms[found - 1] = sym;
- }
- if (buf)
- zfree(&buf);
+ if (strglobmatch(norm, args->name)) {
+ args->found++;
+ if (args->syms && args->found < probe_conf.max_probes)
+ args->syms[args->found - 1] = sym;
}
+ free(buf);
+ return 0;
+}
- return found;
+static int find_probe_functions(struct map *map, char *name,
+ struct symbol **syms)
+{
+ struct find_probe_functions_args args = {
+ .name = name,
+ .syms = syms,
+ /* If user gives a version, don't cut off the version from symbols */
+ .cut_version = strchr(name, '@') == NULL,
+ .found = 0,
+ };
+ int ret;
+
+ if (map__load(map) < 0)
+ return -EACCES; /* Possible permission error to load symbols */
+
+ ret = map__for_each_symbol(map, find_probe_functions_cb, &args);
+ if (ret < 0)
+ return ret;
+
+ return args.found;
}
void __weak arch__fix_tev_from_maps(struct perf_probe_event *pev __maybe_unused,
@@ -3775,6 +3789,15 @@ void cleanup_perf_probe_events(struct perf_probe_event *pevs, int npevs)
}
}
+static int show_available_funcs_cb(struct symbol *pos, void *data)
+{
+ struct strfilter *_filter = data;
+
+ if (strfilter__compare(_filter, symbol__name(pos)))
+ printf("%s\n", symbol__name(pos));
+ return 0;
+}
+
int show_available_funcs(const char *target, struct nsinfo *nsi,
struct strfilter *_filter, bool user)
{
@@ -3811,12 +3834,7 @@ int show_available_funcs(const char *target, struct nsinfo *nsi,
/* Show all (filtered) symbols */
setup_pager();
- for (size_t i = 0; i < dso__symbol_names_len(dso); i++) {
- struct symbol *pos = dso__symbol_names(dso)[i];
-
- if (strfilter__compare(_filter, symbol__name(pos)))
- printf("%s\n", symbol__name(pos));
- }
+ dso__for_each_symbol_by_name(dso, show_available_funcs_cb, _filter);
end:
map__put(map);
exit_probe_symbol_maps();
diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c
index 817486a4b8f1..77db7ab5011d 100644
--- a/tools/perf/util/symbol-elf.c
+++ b/tools/perf/util/symbol-elf.c
@@ -586,7 +586,8 @@ static int dso__synthesize_plt_got_symbols(struct dso *dso, Elf *elf,
sym = symbol__new(shdr.sh_offset + i, shdr.sh_entsize, STB_GLOBAL, STT_FUNC, buf);
if (!sym)
goto out;
- symbols__insert(dso__symbols(dso), sym);
+ if (symbols__insert(dso__symbols(dso), sym))
+ goto out;
}
err = 0;
out:
@@ -640,7 +641,8 @@ int dso__synthesize_plt_symbols(struct dso *dso, struct symsrc *ss)
plt_sym = symbol__new(shdr_plt.sh_offset, plt_header_size, STB_GLOBAL, STT_FUNC, ".plt");
if (!plt_sym)
goto out_elf_end;
- symbols__insert(dso__symbols(dso), plt_sym);
+ if (symbols__insert(dso__symbols(dso), plt_sym))
+ goto out_elf_end;
/* Only x86 has .plt.got */
if (machine_is_x86(ehdr.e_machine) &&
@@ -762,7 +764,8 @@ int dso__synthesize_plt_symbols(struct dso *dso, struct symsrc *ss)
goto out_elf_end;
plt_offset += plt_entry_size;
- symbols__insert(dso__symbols(dso), f);
+ if (symbols__insert(dso__symbols(dso), f))
+ goto out_elf_end;
++nr;
}
@@ -1785,7 +1788,8 @@ dso__load_sym_internal(struct dso *dso, struct map *map, struct symsrc *syms_ss,
arch__sym_update(f, &sym);
- __symbols__insert(dso__symbols(curr_dso), f);
+ if (symbols__insert(dso__symbols(curr_dso), f))
+ goto out_elf_end;
nr++;
}
dso__put(curr_dso);
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 5b50cca7989d..0055bfdc980c 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -213,57 +213,157 @@ static int choose_best_symbol(struct symbol *syma, struct symbol *symb)
return arch__choose_best_symbol(syma, symb);
}
-void symbols__fixup_duplicate(struct rb_root_cached *symbols)
+void symbols__init(struct symbols *symbols)
{
- struct rb_node *nd;
- struct symbol *curr, *next;
+ init_rwsem(&symbols->lock);
+ symbols->symbols = NULL;
+ symbols->symbols_by_name = NULL;
+ symbols->cnt = 0;
+ symbols->allocated = 0;
+ symbols->num_by_name = 0;
+ symbols->sorted = true;
+ symbols->sorted_by_name = false;
+}
+
+static void symbols__delete_locked(struct symbols *symbols)
+{
+ for (unsigned int i = 0; i < symbols->cnt; i++)
+ symbol__delete(symbols->symbols[i]);
+
+ zfree(&symbols->symbols);
+ zfree(&symbols->symbols_by_name);
+ symbols->cnt = 0;
+ symbols->allocated = 0;
+ symbols->num_by_name = 0;
+ symbols->sorted = true;
+ symbols->sorted_by_name = false;
+}
+
+void symbols__delete(struct symbols *symbols)
+{
+ down_write(&symbols->lock);
+ symbols__delete_locked(symbols);
+ up_write(&symbols->lock);
+}
+
+void symbols__exit(struct symbols *symbols)
+{
+ symbols__delete(symbols);
+ exit_rwsem(&symbols->lock);
+}
+
+static inline bool symbol__less_or_equal(const struct symbol *a, const struct symbol *b)
+{
+ if (symbol__start(a) != symbol__start(b))
+ return symbol__start(a) < symbol__start(b);
+ return symbol__end(a) <= symbol__end(b);
+}
+
+static int symbols__sort_cmp(const void *vlhs, const void *vrhs)
+{
+ const struct symbol *lhs = *((const struct symbol **)vlhs);
+ const struct symbol *rhs = *((const struct symbol **)vrhs);
+
+ if (symbol__start(lhs) != symbol__start(rhs))
+ return symbol__start(lhs) < symbol__start(rhs) ? -1 : 1;
+ if (symbol__end(lhs) != symbol__end(rhs))
+ return symbol__end(lhs) < symbol__end(rhs) ? -1 : 1;
+ return 0;
+}
+
+static void symbols__sort_locked(struct symbols *symbols)
+{
+ if (symbols->sorted)
+ return;
+
+ if (symbols->cnt > 1) {
+ qsort(symbols->symbols, symbols->cnt, sizeof(struct symbol *),
+ symbols__sort_cmp);
+ }
+ symbols->sorted = true;
+}
+
+/*
+ * Acquire @symbols->lock for reading after ensuring @symbols->symbols is
+ * sorted.
+ */
+void symbols__sort_read_lock(struct symbols *symbols)
+ NO_THREAD_SAFETY_ANALYSIS
+{
+ down_read(&symbols->lock);
+ while (!symbols->sorted) {
+ up_read(&symbols->lock);
+ down_write(&symbols->lock);
+ symbols__sort_locked(symbols);
+ up_write(&symbols->lock);
+ down_read(&symbols->lock);
+ }
+}
+
+/*
+ * Deduplicate symbols sharing the same start address by sorting @symbols and
+ * compacting the array in place, keeping the best symbol at each address as
+ * chosen by choose_best_symbol().
+ */
+void symbols__fixup_duplicate(struct symbols *symbols)
+{
+ unsigned int write_idx = 0;
if (symbol_conf.allow_aliases)
return;
- nd = rb_first_cached(symbols);
+ down_write(&symbols->lock);
+ symbols__sort_locked(symbols);
+ if (symbols->cnt <= 1) {
+ up_write(&symbols->lock);
+ return;
+ }
- while (nd) {
- curr = rb_entry(nd, struct symbol, rb_node);
-again:
- nd = rb_next(&curr->rb_node);
- if (!nd)
- break;
+ for (unsigned int read_idx = 1; read_idx < symbols->cnt; read_idx++) {
+ struct symbol *curr = symbols->symbols[write_idx];
+ struct symbol *next = symbols->symbols[read_idx];
- next = rb_entry(nd, struct symbol, rb_node);
- if (symbol__start(curr) != symbol__start(next))
+ if (symbol__start(curr) != symbol__start(next)) {
+ symbols->symbols[++write_idx] = next;
continue;
+ }
if (choose_best_symbol(curr, next) == SYMBOL_A) {
if (symbol__type(next) == STT_GNU_IFUNC)
symbol__set_ifunc_alias(curr, true);
- rb_erase_cached(&next->rb_node, symbols);
symbol__delete(next);
- goto again;
} else {
if (symbol__type(curr) == STT_GNU_IFUNC)
symbol__set_ifunc_alias(next, true);
- nd = rb_next(&curr->rb_node);
- rb_erase_cached(&curr->rb_node, symbols);
symbol__delete(curr);
+ symbols->symbols[write_idx] = next;
}
}
+ symbols->cnt = write_idx + 1;
+ zfree(&symbols->symbols_by_name);
+ symbols->num_by_name = 0;
+ symbols->sorted_by_name = false;
+ up_write(&symbols->lock);
}
/* Update zero-sized symbols using the address of the next symbol */
-void symbols__fixup_end(struct rb_root_cached *symbols, bool is_kallsyms)
+void symbols__fixup_end(struct symbols *symbols, bool is_kallsyms)
{
- struct rb_node *nd, *prevnd = rb_first_cached(symbols);
struct symbol *curr, *prev;
- if (prevnd == NULL)
+ down_write(&symbols->lock);
+ symbols__sort_locked(symbols);
+
+ if (symbols->cnt == 0) {
+ up_write(&symbols->lock);
return;
+ }
- curr = rb_entry(prevnd, struct symbol, rb_node);
+ curr = symbols->symbols[0];
- for (nd = rb_next(prevnd); nd; nd = rb_next(nd)) {
+ for (unsigned int i = 1; i < symbols->cnt; i++) {
prev = curr;
- curr = rb_entry(nd, struct symbol, rb_node);
+ curr = symbols->symbols[i];
/*
* On some architecture kernel text segment start is located at
@@ -314,6 +414,8 @@ void symbols__fixup_end(struct rb_root_cached *symbols, bool is_kallsyms)
/* Last entry */
if (symbol__end(curr) == symbol__start(curr))
symbol__set_end(curr, roundup(symbol__start(curr), 4096) + 4096);
+
+ up_write(&symbols->lock);
}
struct symbol *symbol__new(u64 start, u64 len, u8 binding, u8 type, const char *name)
@@ -395,97 +497,94 @@ static void symbol__set_idle(struct symbol *sym, bool idle)
new_flags |= (idle_val << SYMBOL_FLAG_IDLE_SHIFT);
} while (!atomic_compare_exchange_weak(&sym->flags, &old_flags, new_flags));
}
-void symbols__delete(struct rb_root_cached *symbols)
-{
- struct symbol *pos;
- struct rb_node *next = rb_first_cached(symbols);
- while (next) {
- pos = rb_entry(next, struct symbol, rb_node);
- next = rb_next(&pos->rb_node);
- rb_erase_cached(&pos->rb_node, symbols);
- symbol__delete(pos);
- }
-}
-
-void __symbols__insert(struct rb_root_cached *symbols, struct symbol *sym)
+static int __symbols__insert(struct symbols *symbols, struct symbol *sym)
{
- struct rb_node **p = &symbols->rb_root.rb_node;
- struct rb_node *parent = NULL;
- const u64 ip = symbol__start(sym);
- struct symbol *s;
- bool leftmost = true;
+ if (symbols->cnt == symbols->allocated) {
+ unsigned int to_allocate = symbols->allocated ? symbols->allocated * 2 : 32;
+ struct symbol **temp = realloc(symbols->symbols,
+ sizeof(struct symbol *) * to_allocate);
- while (*p != NULL) {
- parent = *p;
- s = rb_entry(parent, struct symbol, rb_node);
- if (ip < symbol__start(s))
- p = &(*p)->rb_left;
- else {
- p = &(*p)->rb_right;
- leftmost = false;
+ if (!temp) {
+ symbol__delete(sym);
+ return -ENOMEM;
}
+ symbols->symbols = temp;
+ symbols->allocated = to_allocate;
}
- rb_link_node(&sym->rb_node, parent, p);
- rb_insert_color_cached(&sym->rb_node, symbols, leftmost);
+
+ if (symbols->sorted && symbols->cnt > 0 &&
+ !symbol__less_or_equal(symbols->symbols[symbols->cnt - 1], sym))
+ symbols->sorted = false;
+
+ symbols->symbols[symbols->cnt++] = sym;
+ zfree(&symbols->symbols_by_name);
+ symbols->num_by_name = 0;
+ symbols->sorted_by_name = false;
+ return 0;
}
-void symbols__insert(struct rb_root_cached *symbols, struct symbol *sym)
+int symbols__insert(struct symbols *symbols, struct symbol *sym)
{
- __symbols__insert(symbols, sym);
+ int ret;
+
+ down_write(&symbols->lock);
+ ret = __symbols__insert(symbols, sym);
+ up_write(&symbols->lock);
+ return ret;
}
-static struct symbol *symbols__find(struct rb_root_cached *symbols, u64 ip)
+/*
+ * Binary search @symbols->symbols for a symbol covering @ip. Lazily sorts
+ * @symbols under @symbols->lock if unsorted.
+ */
+static struct symbol *symbols__find(struct symbols *symbols, u64 ip)
{
- struct rb_node *n;
+ int low, high;
+ struct symbol *res = NULL;
if (symbols == NULL)
return NULL;
- n = symbols->rb_root.rb_node;
+ symbols__sort_read_lock(symbols);
- while (n) {
- struct symbol *s = rb_entry(n, struct symbol, rb_node);
+ low = 0;
+ high = (int)symbols->cnt - 1;
+ while (low <= high) {
+ int mid = low + (high - low) / 2;
+ struct symbol *s = symbols->symbols[mid];
+ u64 start = symbol__start(s);
+ u64 end = symbol__end(s);
- if (ip < symbol__start(s))
- n = n->rb_left;
- else if (ip > symbol__end(s) || (ip == symbol__end(s) && ip != symbol__start(s)))
- n = n->rb_right;
- else
- return s;
+ if (ip < start)
+ high = mid - 1;
+ else if (ip > end || (ip == end && ip != start))
+ low = mid + 1;
+ else {
+ res = s;
+ break;
+ }
}
+ up_read(&symbols->lock);
- return NULL;
-}
-
-static struct symbol *symbols__first(struct rb_root_cached *symbols)
-{
- struct rb_node *n = rb_first_cached(symbols);
-
- if (n)
- return rb_entry(n, struct symbol, rb_node);
-
- return NULL;
+ return res;
}
-static struct symbol *symbols__last(struct rb_root_cached *symbols)
+int symbols__for_each_entry(struct symbols *symbols,
+ int (*cb)(struct symbol *sym, void *data),
+ void *data)
{
- struct rb_node *n = rb_last(&symbols->rb_root);
-
- if (n)
- return rb_entry(n, struct symbol, rb_node);
-
- return NULL;
-}
-
-static struct symbol *symbols__next(struct symbol *sym)
-{
- struct rb_node *n = rb_next(&sym->rb_node);
+ int err = 0;
- if (n)
- return rb_entry(n, struct symbol, rb_node);
+ symbols__sort_read_lock(symbols);
+ for (unsigned int i = 0; i < symbols->cnt; i++) {
+ err = cb(symbols->symbols[i], data);
+ if (err)
+ break;
+ }
+ up_read(&symbols->lock);
- return NULL;
+ return err;
}
static int symbols__sort_name_cmp(const void *vlhs, const void *vrhs)
@@ -496,27 +595,31 @@ static int symbols__sort_name_cmp(const void *vlhs, const void *vrhs)
return strcmp(symbol__name(lhs), symbol__name(rhs));
}
-static struct symbol **symbols__sort_by_name(struct rb_root_cached *source, size_t *len)
+static void symbols__sort_by_name_locked(struct symbols *symbols)
{
- struct rb_node *nd;
struct symbol **result;
- size_t i = 0, size = 0;
+ unsigned int size = symbols->cnt;
+
+ if (symbols->sorted_by_name)
+ return;
- for (nd = rb_first_cached(source); nd; nd = rb_next(nd))
- size++;
+ zfree(&symbols->symbols_by_name);
+ symbols->num_by_name = 0;
+
+ if (size == 0) {
+ symbols->sorted_by_name = true;
+ return;
+ }
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);
+ return;
- result[i++] = pos;
- }
+ memcpy(result, symbols->symbols, sizeof(*result) * size);
qsort(result, size, sizeof(*result), symbols__sort_name_cmp);
- *len = size;
- return result;
+ symbols->symbols_by_name = result;
+ symbols->num_by_name = size;
+ symbols->sorted_by_name = true;
}
int symbol__match_symbol_name(const char *name, const char *str,
@@ -585,39 +688,34 @@ static struct symbol *symbols__find_by_name(struct symbol *symbols[],
return s;
}
-void dso__reset_find_symbol_cache(struct dso *dso)
-{
- dso__set_last_find_result_addr(dso, 0);
- dso__set_last_find_result_symbol(dso, NULL);
-}
-
-void dso__insert_symbol(struct dso *dso, struct symbol *sym)
+int dso__insert_symbol(struct dso *dso, struct symbol *sym)
{
- __symbols__insert(dso__symbols(dso), sym);
-
- /* update the symbol cache if necessary */
- if (dso__last_find_result_addr(dso) >= symbol__start(sym) &&
- (dso__last_find_result_addr(dso) < symbol__end(sym) ||
- symbol__start(sym) == symbol__end(sym))) {
- dso__set_last_find_result_symbol(dso, sym);
- }
+ return symbols__insert(dso__symbols(dso), sym);
}
void dso__delete_symbol(struct dso *dso, struct symbol *sym)
{
- rb_erase_cached(&sym->rb_node, dso__symbols(dso));
- symbol__delete(sym);
- dso__reset_find_symbol_cache(dso);
+ struct symbols *symbols = dso__symbols(dso);
+
+ down_write(&symbols->lock);
+ for (unsigned int i = 0; i < symbols->cnt; i++) {
+ if (symbols->symbols[i] == sym) {
+ memmove(&symbols->symbols[i], &symbols->symbols[i + 1],
+ (symbols->cnt - i - 1) * sizeof(struct symbol *));
+ symbols->cnt--;
+ zfree(&symbols->symbols_by_name);
+ symbols->num_by_name = 0;
+ symbols->sorted_by_name = false;
+ symbol__delete(sym);
+ break;
+ }
+ }
+ up_write(&symbols->lock);
}
struct symbol *dso__find_symbol(struct dso *dso, u64 addr)
{
- if (dso__last_find_result_addr(dso) != addr || dso__last_find_result_symbol(dso) == NULL) {
- dso__set_last_find_result_addr(dso, addr);
- dso__set_last_find_result_symbol(dso, symbols__find(dso__symbols(dso), addr));
- }
-
- return dso__last_find_result_symbol(dso);
+ return symbols__find(dso__symbols(dso), addr);
}
struct symbol *dso__find_symbol_nocache(struct dso *dso, u64 addr)
@@ -625,28 +723,19 @@ struct symbol *dso__find_symbol_nocache(struct dso *dso, u64 addr)
return symbols__find(dso__symbols(dso), addr);
}
-struct symbol *dso__first_symbol(struct dso *dso)
-{
- return symbols__first(dso__symbols(dso));
-}
-
-struct symbol *dso__last_symbol(struct dso *dso)
-{
- return symbols__last(dso__symbols(dso));
-}
-
-struct symbol *dso__next_symbol(struct symbol *sym)
-{
- return symbols__next(sym);
-}
-
struct symbol *dso__next_symbol_by_name(struct dso *dso, size_t *idx)
{
- if (*idx + 1 >= dso__symbol_names_len(dso))
- return NULL;
+ struct symbols *symbols = dso__symbols(dso);
+ struct symbol *res = NULL;
- ++*idx;
- return dso__symbol_names(dso)[*idx];
+ down_read(&symbols->lock);
+ if (*idx + 1 < symbols->num_by_name) {
+ ++*idx;
+ res = symbols->symbols_by_name[*idx];
+ }
+ up_read(&symbols->lock);
+
+ return res;
}
/*
@@ -654,29 +743,70 @@ 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 *s = symbols__find_by_name(dso__symbol_names(dso),
- dso__symbol_names_len(dso),
- name, SYMBOL_TAG_INCLUDE__NONE, idx);
+ struct symbols *symbols;
+ struct symbol *s;
+
+ if (!dso)
+ return NULL;
+ symbols = dso__symbols(dso);
+ down_read(&symbols->lock);
+ while (!symbols->sorted_by_name) {
+ up_read(&symbols->lock);
+ dso__sort_by_name(dso);
+ down_read(&symbols->lock);
+ if (!symbols->sorted_by_name && !symbols->symbols_by_name)
+ break;
+ }
+ s = symbols__find_by_name(symbols->symbols_by_name,
+ symbols->num_by_name,
+ name, SYMBOL_TAG_INCLUDE__NONE, idx);
if (!s) {
- s = symbols__find_by_name(dso__symbol_names(dso), dso__symbol_names_len(dso),
+ s = symbols__find_by_name(symbols->symbols_by_name, symbols->num_by_name,
name, SYMBOL_TAG_INCLUDE__DEFAULT_ONLY, idx);
}
+ up_read(&symbols->lock);
return s;
}
void dso__sort_by_name(struct dso *dso)
{
- mutex_lock(dso__lock(dso));
- if (!dso__sorted_by_name(dso)) {
- size_t len = 0;
+ struct symbols *symbols = dso__symbols(dso);
- dso__set_symbol_names(dso, symbols__sort_by_name(dso__symbols(dso), &len));
- if (dso__symbol_names(dso)) {
- dso__set_symbol_names_len(dso, len);
- dso__set_sorted_by_name(dso);
- }
+ down_read(&symbols->lock);
+ if (!symbols->sorted_by_name) {
+ up_read(&symbols->lock);
+ down_write(&symbols->lock);
+ symbols__sort_by_name_locked(symbols);
+ up_write(&symbols->lock);
+ return;
}
- mutex_unlock(dso__lock(dso));
+ up_read(&symbols->lock);
+}
+
+int symbols__visit_by_name(struct symbols *symbols,
+ int (*cb)(struct symbol *sym, void *data),
+ void *data)
+{
+ int err = 0;
+
+ down_read(&symbols->lock);
+ while (!symbols->sorted_by_name) {
+ up_read(&symbols->lock);
+ down_write(&symbols->lock);
+ symbols__sort_by_name_locked(symbols);
+ up_write(&symbols->lock);
+ down_read(&symbols->lock);
+ if (!symbols->sorted_by_name && !symbols->symbols_by_name)
+ break;
+ }
+ for (size_t i = 0; i < symbols->num_by_name; i++) {
+ err = cb(symbols->symbols_by_name[i], data);
+ if (err)
+ break;
+ }
+ up_read(&symbols->lock);
+
+ return err;
}
/*
@@ -876,7 +1006,7 @@ static int map__process_kallsym_symbol(void *arg, const char *name,
{
struct symbol *sym;
struct dso *dso = arg;
- struct rb_root_cached *root = dso__symbols(dso);
+ struct symbols *root = dso__symbols(dso);
if (!symbol_type__filter(type))
return 0;
@@ -897,9 +1027,7 @@ static int map__process_kallsym_symbol(void *arg, const char *name,
* We will pass the symbols to the filter later, in
* map__split_kallsyms, when we have split the maps per module
*/
- __symbols__insert(root, sym);
-
- return 0;
+ return symbols__insert(root, sym);
}
/*
@@ -916,25 +1044,32 @@ static int maps__split_kallsyms_for_kcore(struct maps *kmaps, struct dso *dso)
{
struct symbol *pos;
int count = 0;
- struct rb_root_cached *root = dso__symbols(dso);
- struct rb_root_cached old_root = *root;
- struct rb_node *next = rb_first_cached(root);
+ struct symbols *root = dso__symbols(dso);
+ struct symbol **old_symbols;
+ unsigned int old_cnt;
if (!kmaps)
return -1;
- *root = RB_ROOT_CACHED;
-
- while (next) {
+ down_write(&root->lock);
+ symbols__sort_locked(root);
+ old_symbols = root->symbols;
+ old_cnt = root->cnt;
+ root->symbols = NULL;
+ root->cnt = 0;
+ root->allocated = 0;
+ root->sorted = true;
+ zfree(&root->symbols_by_name);
+ root->num_by_name = 0;
+ root->sorted_by_name = false;
+ up_write(&root->lock);
+
+ for (unsigned int i = 0; i < old_cnt; i++) {
struct map *curr_map;
struct dso *curr_map_dso;
char *module;
- pos = rb_entry(next, struct symbol, rb_node);
- next = rb_next(&pos->rb_node);
-
- rb_erase_cached(&pos->rb_node, &old_root);
- RB_CLEAR_NODE(&pos->rb_node);
+ pos = old_symbols[i];
module = strchr((char *)symbol__name(pos), '\t');
if (module)
*module = '\0';
@@ -954,11 +1089,13 @@ static int maps__split_kallsyms_for_kcore(struct maps *kmaps, struct dso *dso)
symbol__set_end(pos, symbol__end(pos) -
(map__start(curr_map) - map__pgoff(curr_map)));
}
- symbols__insert(dso__symbols(curr_map_dso), pos);
- ++count;
+ if (symbols__insert(dso__symbols(curr_map_dso), pos) == 0)
+ ++count;
map__put(curr_map);
}
+ free(old_symbols);
+
/* Symbols have been adjusted */
dso__set_adjust_symbols(dso, true);
@@ -977,8 +1114,10 @@ static int maps__split_kallsyms(struct maps *kmaps, struct dso *dso, u64 delta,
struct map *curr_map = map__get(initial_map);
struct symbol *pos;
int count = 0, moved = 0;
- struct rb_root_cached *root = dso__symbols(dso);
- struct rb_node *next = rb_first_cached(root);
+ struct symbols *root = dso__symbols(dso);
+ struct symbol **old_symbols;
+ unsigned int old_cnt, old_allocated;
+ unsigned int i, write_idx = 0;
int kernel_range = 0;
uint16_t e_machine = EM_NONE;
@@ -988,11 +1127,24 @@ static int maps__split_kallsyms(struct maps *kmaps, struct dso *dso, u64 delta,
machine = maps__machine(kmaps);
e_machine = perf_env__e_machine(machine->env, /*e_flags=*/NULL);
- while (next) {
+ down_write(&root->lock);
+ symbols__sort_locked(root);
+ old_symbols = root->symbols;
+ old_cnt = root->cnt;
+ old_allocated = root->allocated;
+ root->symbols = NULL;
+ root->cnt = 0;
+ root->allocated = 0;
+ root->sorted = true;
+ zfree(&root->symbols_by_name);
+ root->num_by_name = 0;
+ root->sorted_by_name = false;
+ up_write(&root->lock);
+
+ for (i = 0; i < old_cnt; i++) {
char *module;
- pos = rb_entry(next, struct symbol, rb_node);
- next = rb_next(&pos->rb_node);
+ pos = old_symbols[i];
module = strchr((char *)symbol__name(pos), '\t');
if (module) {
@@ -1077,7 +1229,7 @@ static int maps__split_kallsyms(struct maps *kmaps, struct dso *dso, u64 delta,
ndso = dso__new(dso_name);
map__zput(curr_map);
if (ndso == NULL)
- return -1;
+ goto out_err;
dso__set_kernel(ndso, dso__kernel(dso));
if (dso__kernel(dso) == DSO_SPACE__KERNEL_GUEST) {
@@ -1092,14 +1244,14 @@ static int maps__split_kallsyms(struct maps *kmaps, struct dso *dso, u64 delta,
curr_map = map__new2(symbol__start(pos), ndso);
if (curr_map == NULL) {
dso__put(ndso);
- return -1;
+ goto out_err;
}
map__set_mapping_type(curr_map, MAPPING_TYPE__IDENTITY);
if (maps__insert(kmaps, curr_map)) {
map__zput(curr_map);
dso__put(ndso);
- return -1;
+ goto out_err;
}
dso__put(ndso);
++kernel_range;
@@ -1112,18 +1264,35 @@ static int maps__split_kallsyms(struct maps *kmaps, struct dso *dso, u64 delta,
if (!RC_CHK_EQUAL(curr_map, initial_map)) {
struct dso *curr_map_dso = map__dso(curr_map);
- rb_erase_cached(&pos->rb_node, root);
- symbols__insert(dso__symbols(curr_map_dso), pos);
- ++moved;
- } else
+ if (symbols__insert(dso__symbols(curr_map_dso), pos) == 0)
+ ++moved;
+ } else {
+ old_symbols[write_idx++] = pos;
++count;
+ }
continue;
discard_symbol:
- rb_erase_cached(&pos->rb_node, root);
symbol__delete(pos);
}
+ down_write(&root->lock);
+ if (root->cnt == 0) {
+ root->symbols = old_symbols;
+ root->cnt = write_idx;
+ root->allocated = old_allocated;
+ root->sorted = true;
+ old_symbols = NULL;
+ } else {
+ for (unsigned int j = 0; j < write_idx; j++)
+ __symbols__insert(root, old_symbols[j]);
+ }
+ zfree(&root->symbols_by_name);
+ root->num_by_name = 0;
+ root->sorted_by_name = false;
+ up_write(&root->lock);
+ free(old_symbols);
+
if (!RC_CHK_EQUAL(curr_map, initial_map) &&
dso__kernel(dso) == DSO_SPACE__KERNEL_GUEST &&
machine__is_default_guest(maps__machine(kmaps))) {
@@ -1131,6 +1300,29 @@ static int maps__split_kallsyms(struct maps *kmaps, struct dso *dso, u64 delta,
}
map__put(curr_map);
return count + moved;
+
+out_err:
+ memmove(&old_symbols[write_idx], &old_symbols[i],
+ (old_cnt - i) * sizeof(struct symbol *));
+ write_idx += old_cnt - i;
+ down_write(&root->lock);
+ if (root->cnt == 0) {
+ root->symbols = old_symbols;
+ root->cnt = write_idx;
+ root->allocated = old_allocated;
+ root->sorted = (delta == 0);
+ old_symbols = NULL;
+ } else {
+ for (unsigned int j = 0; j < write_idx; j++)
+ __symbols__insert(root, old_symbols[j]);
+ root->sorted = false;
+ }
+ zfree(&root->symbols_by_name);
+ root->num_by_name = 0;
+ root->sorted_by_name = false;
+ up_write(&root->lock);
+ free(old_symbols);
+ return -1;
}
bool symbol__restricted_filename(const char *filename,
@@ -1691,7 +1883,8 @@ static int dso__load_perf_map(const char *map_path, struct dso *dso)
if (sym == NULL)
goto out_delete_line;
- symbols__insert(dso__symbols(dso), sym);
+ if (symbols__insert(dso__symbols(dso), sym))
+ goto out_delete_line;
nr_syms++;
}
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index 37e6ab1478b5..eed4d388d1fc 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -15,6 +15,7 @@
#include <errno.h>
#include "addr_location.h"
#include "path.h"
+#include "rwsem.h"
#include "symbol_conf.h"
#include "spark.h"
#include "util.h"
@@ -85,7 +86,6 @@ enum symbol_idle_kind {
* A symtab entry.
*/
struct symbol {
- struct rb_node rb_node;
/** Range of symbol [start, end). */
u64 start;
u64 end;
@@ -98,8 +98,42 @@ struct symbol {
char name[];
};
+/**
+ * struct symbols - A locked collection of symbols stored in a sorted array.
+ */
+struct symbols {
+ /** @lock: Read/write semaphore synchronizing access to the arrays. */
+ struct rw_semaphore lock;
+ /**
+ * @symbols: Dynamically allocated array of symbol pointers, sorted by
+ * address range [start, end) when @sorted is true.
+ */
+ struct symbol **symbols;
+ /**
+ * @symbols_by_name: Optional dynamically allocated array of symbol
+ * pointers sorted lexicographically by symbol__name().
+ */
+ struct symbol **symbols_by_name;
+ /** @cnt: Number of valid entries in @symbols. */
+ unsigned int cnt;
+ /** @allocated: Capacity (in entries) of the @symbols allocation. */
+ unsigned int allocated;
+ /** @num_by_name: Number of valid entries in @symbols_by_name. */
+ unsigned int num_by_name;
+ /** @sorted: True when @symbols is sorted by address range. */
+ bool sorted;
+ /**
+ * @sorted_by_name: True when @symbols_by_name is populated and sorted
+ * by name.
+ */
+ bool sorted_by_name;
+};
+
void symbol__delete(struct symbol *sym);
-void symbols__delete(struct rb_root_cached *symbols);
+void symbols__init(struct symbols *symbols);
+void symbols__exit(struct symbols *symbols);
+void symbols__delete(struct symbols *symbols);
+void symbols__sort_read_lock(struct symbols *symbols) SHARED_LOCK_FUNCTION(symbols->lock);
static inline __attribute__((nonnull)) u64 symbol__start(const struct symbol *sym)
{
@@ -192,16 +226,19 @@ void symbol__set_inlined(struct symbol *sym, bool inlined);
void symbol__set_ifunc_alias(struct symbol *sym, bool ifunc_alias);
void symbol__set_annotated(struct symbol *sym, bool annotated);
-/* symbols__for_each_entry - iterate over symbols (rb_root)
+/*
+ * symbols__for_each_entry - iterate over symbols calling @cb for each entry
*
- * @symbols: the rb_root of symbols
- * @pos: the 'struct symbol *' to use as a loop cursor
- * @nd: the 'struct rb_node *' to use as a temporary storage
+ * @symbols: the 'struct symbols *' of symbols
+ * @cb: callback function invoked under @symbols->lock
+ * @data: opaque data pointer passed to @cb
*/
-#define symbols__for_each_entry(symbols, pos, nd) \
- for (nd = rb_first_cached(symbols); \
- nd && (pos = rb_entry(nd, struct symbol, rb_node)); \
- nd = rb_next(nd))
+int symbols__for_each_entry(struct symbols *symbols,
+ int (*cb)(struct symbol *sym, void *data),
+ void *data);
+int symbols__visit_by_name(struct symbols *symbols,
+ int (*cb)(struct symbol *sym, void *data),
+ void *data);
static inline size_t symbol__size(const struct symbol *sym)
{
@@ -238,8 +275,8 @@ int __dso__load_kallsyms(struct dso *dso, const char *filename, struct map *map,
bool no_kcore);
int dso__load_kallsyms(struct dso *dso, const char *filename, struct map *map);
-void dso__insert_symbol(struct dso *dso,
- struct symbol *sym);
+int dso__insert_symbol(struct dso *dso,
+ struct symbol *sym);
void dso__delete_symbol(struct dso *dso,
struct symbol *sym);
@@ -249,10 +286,6 @@ struct symbol *dso__find_symbol_nocache(struct dso *dso, u64 addr);
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);
-struct symbol *dso__next_symbol(struct symbol *sym);
-
enum dso_type dso__type_fd(int fd);
int filename__read_build_id(const char *filename, struct build_id *id);
@@ -303,10 +336,9 @@ int dso__synthesize_plt_symbols(struct dso *dso, struct symsrc *ss);
char *dso__demangle_sym(struct dso *dso, int kmodule, const char *elf_name);
-void __symbols__insert(struct rb_root_cached *symbols, struct symbol *sym);
-void symbols__insert(struct rb_root_cached *symbols, struct symbol *sym);
-void symbols__fixup_duplicate(struct rb_root_cached *symbols);
-void symbols__fixup_end(struct rb_root_cached *symbols, bool is_kallsyms);
+int symbols__insert(struct symbols *symbols, struct symbol *sym);
+void symbols__fixup_duplicate(struct symbols *symbols);
+void symbols__fixup_end(struct symbols *symbols, bool is_kallsyms);
typedef int (*mapfn_t)(u64 start, u64 len, u64 pgoff, void *data);
int file__read_maps(int fd, bool exe, mapfn_t mapfn, void *data,
diff --git a/tools/perf/util/symbol_fprintf.c b/tools/perf/util/symbol_fprintf.c
index 8c131325e989..8453a3554cd4 100644
--- a/tools/perf/util/symbol_fprintf.c
+++ b/tools/perf/util/symbol_fprintf.c
@@ -59,15 +59,26 @@ size_t symbol__fprintf_symname(const struct symbol *sym, FILE *fp)
return __symbol__fprintf_symname_offs(sym, NULL, false, false, fp);
}
+struct fprintf_symbols_by_name_args {
+ FILE *fp;
+ size_t ret;
+};
+
+static int fprintf_symbols_by_name_cb(struct symbol *pos, void *data)
+{
+ struct fprintf_symbols_by_name_args *args = data;
+
+ args->ret += fprintf(args->fp, "%s\n", symbol__name(pos));
+ return 0;
+}
+
size_t dso__fprintf_symbols_by_name(struct dso *dso,
FILE *fp)
{
- size_t ret = 0;
-
- for (size_t i = 0; i < dso__symbol_names_len(dso); i++) {
- struct symbol *pos = dso__symbol_names(dso)[i];
+ struct fprintf_symbols_by_name_args args = {
+ .fp = fp,
+ };
- ret += fprintf(fp, "%s\n", symbol__name(pos));
- }
- return ret;
+ dso__for_each_symbol_by_name(dso, fprintf_symbols_by_name_cb, &args);
+ return args.ret;
}
--
2.56.0.313.g2479322e68-goog
^ permalink raw reply [flat|nested] 36+ messages in thread
* [PATCH v2 08/18] perf symbol: Add reference counting and DECLARE_RC_STRUCT(symbol)
2026-10-27 23:14 ` [PATCH v2 00/18] perf symbol: Reference counting, sorted arrays, and memory shrinking Ian Rogers
` (6 preceding siblings ...)
2026-10-27 23:15 ` [PATCH v2 07/18] perf symbol: Switch backing storage from rbtree to struct symbols array Ian Rogers
@ 2026-10-27 23:15 ` Ian Rogers
2026-10-27 23:15 ` [PATCH v2 09/18] perf symbol: Optimize symbol sorting with natural-run merge sort and binary insertion Ian Rogers
` (9 subsequent siblings)
17 siblings, 0 replies; 36+ messages in thread
From: Ian Rogers @ 2026-10-27 23:15 UTC (permalink / raw)
To: irogers, acme, namhyung
Cc: adrian.hunter, haghdoost, james.clark, jolsa, linux-kernel,
linux-perf-users, mingo, peterz
Previously, struct symbol pointers returned by dso__find_symbol() and
map__find_symbol() were uncounted borrowed pointers into the DSO's
symbol table. Callers stored these raw pointers in struct map_symbol,
struct addr_location, struct hist_entry, struct callchain_list, and
annotation structures without lifetime tracking, making it unsafe to
evict unreferenced symbols from a DSO when reclaiming memory.
Wrap struct symbol with DECLARE_RC_STRUCT(symbol) and add a refcount_t
refcnt field along with symbol__get(), symbol__put(), and symbol__zput()
helpers:
- Return a counted symbol reference acquired under symbols->lock from
symbol lookup functions (symbols__find(), dso__find_symbol(),
dso__find_symbol_nocache(), map__find_symbol(),
dso__find_symbol_by_name(), dso__next_symbol_by_name(),
map__find_symbol_by_name()).
- Update util/annotate.c and util/db-export.c to hash and compare the
underlying RC_STRUCT(symbol) pointer via RC_CHK_ACCESS(sym) and
RC_CHK_EQUAL() so REFCNT_CHECKING wrapper pointers for the same
symbol resolve to the same hashmap entry, and hold a symbol reference
in struct db_export's sym_db_ids hashmap.
- Update struct addr_location, struct map_symbol, struct call_path,
struct block_info, struct disasm_line, struct annotated_op_loc, struct
c2c_sym_Key, inline_list, srcline, thread_stack, and hist/sort/unwind
consumers to acquire and release symbol references and use
RC_CHK_EQUAL() for pointer identity comparisons.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/bench/symbols.c | 7 +-
tools/perf/builtin-annotate.c | 7 +-
tools/perf/builtin-kallsyms.c | 2 +
tools/perf/builtin-kmem.c | 12 +-
tools/perf/builtin-lock.c | 17 ++-
tools/perf/builtin-script.c | 4 +-
tools/perf/builtin-top.c | 2 +-
tools/perf/builtin-trace.c | 1 +
tools/perf/tests/dlfilter-test.c | 8 +-
tools/perf/tests/hists_cumulate.c | 4 +-
tools/perf/tests/hists_filter.c | 10 +-
tools/perf/tests/hists_link.c | 16 ++-
tools/perf/tests/hists_output.c | 7 +-
tools/perf/tests/kallsyms-split.c | 5 +-
tools/perf/tests/pe-file-parsing.c | 1 +
tools/perf/tests/symbols.c | 14 ++-
tools/perf/tests/vmlinux-kallsyms.c | 10 +-
tools/perf/ui/browsers/hists.c | 11 +-
tools/perf/ui/browsers/map.c | 7 +-
tools/perf/ui/stdio/hist.c | 3 +-
tools/perf/util/addr2line.c | 8 +-
tools/perf/util/addr_location.c | 4 +
.../perf/util/annotate-arch/annotate-alpha.c | 5 +-
.../util/annotate-arch/annotate-loongarch.c | 11 +-
tools/perf/util/annotate-arch/annotate-s390.c | 6 +-
tools/perf/util/annotate.c | 22 ++--
tools/perf/util/block-info.c | 6 +-
tools/perf/util/bpf_lock_contention.c | 25 +++-
tools/perf/util/c2c-function.c | 13 ++-
tools/perf/util/call-path.c | 15 ++-
tools/perf/util/callchain.c | 4 +-
tools/perf/util/capstone.c | 2 +
tools/perf/util/db-export.c | 39 +++++--
tools/perf/util/disasm.c | 22 ++--
tools/perf/util/event.c | 8 +-
tools/perf/util/hist.c | 13 ++-
tools/perf/util/libbfd.c | 7 +-
tools/perf/util/libdw.c | 5 +-
tools/perf/util/llvm.c | 2 +
tools/perf/util/lock-contention.c | 8 +-
tools/perf/util/machine.c | 56 ++++++---
tools/perf/util/map.h | 10 +-
tools/perf/util/map_symbol.c | 4 +-
tools/perf/util/maps.c | 3 +-
tools/perf/util/probe-event.c | 17 ++-
tools/perf/util/python.c | 20 +---
tools/perf/util/sort.c | 2 +-
tools/perf/util/srcline.c | 6 +-
tools/perf/util/symbol-elf.c | 6 +-
tools/perf/util/symbol.c | 107 +++++++++++-------
tools/perf/util/symbol.h | 60 +++++++---
tools/perf/util/thread-stack.c | 12 +-
tools/perf/util/threads.c | 17 ++-
tools/perf/util/unwind-libdw.c | 2 +-
tools/perf/util/unwind-libunwind.c | 2 +-
55 files changed, 478 insertions(+), 219 deletions(-)
diff --git a/tools/perf/bench/symbols.c b/tools/perf/bench/symbols.c
index 5ab71d440e67..8e0621deec83 100644
--- a/tools/perf/bench/symbols.c
+++ b/tools/perf/bench/symbols.c
@@ -159,8 +159,9 @@ static int do_synthetic_symbols_bench(void)
u64 addr = (j < half) ?
(0x100000ULL + (u64)j * 0x40ULL + 4) :
(0x100020ULL + (u64)(j - half) * 0x40ULL + 4);
+ struct symbol *sym = dso__find_symbol_nocache(dso, addr);
- (void)dso__find_symbol_nocache(dso, addr);
+ symbol__put(sym);
}
gettimeofday(&end, NULL);
timersub(&end, &start, &diff);
@@ -183,7 +184,9 @@ static int do_synthetic_symbols_bench(void)
return -ENOMEM;
}
dso__insert_symbol(dso, sym);
- (void)dso__find_symbol_nocache(dso, addr + 2);
+
+ sym = dso__find_symbol_nocache(dso, addr + 2);
+ symbol__put(sym);
}
gettimeofday(&end, NULL);
timersub(&end, &start, &diff);
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c
index 5ee02a25fb9c..6bf88aada13d 100644
--- a/tools/perf/builtin-annotate.c
+++ b/tools/perf/builtin-annotate.c
@@ -134,7 +134,8 @@ static void process_basic_block(struct addr_map_symbol *start,
entry = block_range_iter(&iter);
entry->coverage++;
- entry->sym = sym;
+ symbol__put(entry->sym);
+ entry->sym = symbol__get(sym);
if (branch)
branch->max_coverage = max(branch->max_coverage, entry->coverage);
@@ -174,6 +175,10 @@ static void process_branch_stack(struct branch_stack *bs, struct addr_location *
prev = &bi[i].to;
}
+ for (i = 0; i < (int)bs->nr; i++) {
+ map_symbol__exit(&bi[i].to.ms);
+ map_symbol__exit(&bi[i].from.ms);
+ }
free(bi);
}
diff --git a/tools/perf/builtin-kallsyms.c b/tools/perf/builtin-kallsyms.c
index e99f61c3653e..8f0eaca1099b 100644
--- a/tools/perf/builtin-kallsyms.c
+++ b/tools/perf/builtin-kallsyms.c
@@ -52,6 +52,8 @@ static int __cmd_kallsyms(int argc, const char **argv)
map__unmap_ip(map, symbol__start(symbol)),
map__unmap_ip(map, symbol__end(symbol)),
symbol__start(symbol), symbol__end(symbol));
+ symbol__put(symbol);
+ map__put(map);
}
out:
machine__delete(machine);
diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c
index 7070611e3619..e178be48f269 100644
--- a/tools/perf/builtin-kmem.c
+++ b/tools/perf/builtin-kmem.c
@@ -1044,7 +1044,7 @@ static void __print_slab_result(struct rb_root *root,
struct alloc_stat *data = rb_entry(next, struct alloc_stat,
node);
struct symbol *sym = NULL;
- struct map *map;
+ struct map *map = NULL;
char buf[BUFSIZ];
u64 addr;
@@ -1061,6 +1061,8 @@ static void __print_slab_result(struct rb_root *root,
} else {
snprintf(buf, sizeof(buf), "%#" PRIx64 "", addr);
}
+ symbol__put(sym);
+ map__put(map);
printf(" %-34s |", buf);
printf(" %9llu/%-5lu | %9llu/%-5lu | %8lu | %9lu | %6.3f%%\n",
@@ -1111,12 +1113,11 @@ static void __print_page_alloc_result(struct perf_session *session, int n_lines)
while (next && n_lines--) {
struct page_stat *data;
struct symbol *sym;
- struct map *map;
char buf[32];
const char *caller = buf;
data = rb_entry(next, struct page_stat, node);
- sym = machine__find_kernel_symbol(machine, data->callsite, &map);
+ sym = machine__find_kernel_symbol(machine, data->callsite, NULL);
if (sym)
caller = symbol__name(sym);
else
@@ -1127,6 +1128,7 @@ static void __print_page_alloc_result(struct perf_session *session, int n_lines)
data->nr_alloc, data->order,
migrate_type_str[data->migrate_type],
gfp_len, compact_gfp_string(data->gfp_flags), caller);
+ symbol__put(sym);
next = rb_next(next);
}
@@ -1153,12 +1155,11 @@ static void __print_page_caller_result(struct perf_session *session, int n_lines
while (next && n_lines--) {
struct page_stat *data;
struct symbol *sym;
- struct map *map;
char buf[32];
const char *caller = buf;
data = rb_entry(next, struct page_stat, node);
- sym = machine__find_kernel_symbol(machine, data->callsite, &map);
+ sym = machine__find_kernel_symbol(machine, data->callsite, NULL);
if (sym)
caller = symbol__name(sym);
else
@@ -1169,6 +1170,7 @@ static void __print_page_caller_result(struct perf_session *session, int n_lines
data->nr_alloc, data->order,
migrate_type_str[data->migrate_type],
gfp_len, compact_gfp_string(data->gfp_flags), caller);
+ symbol__put(sym);
next = rb_next(next);
}
diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c
index e52e3785b657..147bbc09ab76 100644
--- a/tools/perf/builtin-lock.c
+++ b/tools/perf/builtin-lock.c
@@ -1003,11 +1003,15 @@ static int report_lock_contention_begin_event(struct perf_sample *sample)
(filters.nr_addrs + 1) * sizeof(*addrs));
if (addrs == NULL) {
pr_warning("memory allocation failure\n");
+ symbol__put(sym);
+ map__put(kmap);
return -ENOMEM;
}
addrs[filters.nr_addrs++] = map__unmap_ip(kmap, symbol__start(sym));
filters.addrs = addrs;
+ symbol__put(sym);
+ map__put(kmap);
}
}
@@ -1057,9 +1061,12 @@ static int report_lock_contention_begin_event(struct perf_sample *sample)
switch (aggr_mode) {
case LOCK_AGGR_ADDR:
- sym = machine__find_kernel_symbol(machine, key, &kmap);
- if (sym)
- name = symbol__name(sym);
+ sym = machine__find_kernel_symbol(machine, key, NULL);
+ if (sym) {
+ scnprintf(buf, sizeof(buf), "%s", symbol__name(sym));
+ name = buf;
+ symbol__put(sym);
+ }
break;
case LOCK_AGGR_CALLER:
name = buf;
@@ -1671,6 +1678,8 @@ static void print_lock_stat_stdio(struct lock_contention *con, struct lock_stat
ip = st->callstack[i];
sym = machine__find_kernel_symbol(con->machine, ip, &kmap);
get_symbol_name_offset(kmap, sym, ip, buf, sizeof(buf));
+ symbol__put(sym);
+ map__put(kmap);
fprintf(lock_output, "\t\t\t%#lx %s\n", (unsigned long)ip, buf);
}
}
@@ -1724,6 +1733,8 @@ static void print_lock_stat_csv(struct lock_contention *con, struct lock_stat *s
ip = st->callstack[i];
sym = machine__find_kernel_symbol(con->machine, ip, &kmap);
get_symbol_name_offset(kmap, sym, ip, buf, sizeof(buf));
+ symbol__put(sym);
+ map__put(kmap);
fprintf(lock_output, "%s %#lx %s", i ? ":" : sep, (unsigned long) ip, buf);
}
fprintf(lock_output, "\n");
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c
index 7559d4d230f2..1237b755cfdf 100644
--- a/tools/perf/builtin-script.c
+++ b/tools/perf/builtin-script.c
@@ -1430,7 +1430,8 @@ static int ip__fprintf_sym(uint64_t addr, struct thread *thread,
if (PRINT_FIELD(SRCLINE))
printed += map__fprintf_srcline(al.map, al.addr, "\t", fp);
printed += fprintf(fp, "\n");
- *lastsym = al.sym;
+ symbol__put(*lastsym);
+ *lastsym = symbol__get(al.sym);
ret = printed;
out:
@@ -1597,6 +1598,7 @@ static int perf_sample__fprintf_brstackinsn(struct perf_sample *sample,
print_srccode(thread, x.cpumode, start + off);
}
out:
+ symbol__put(lastsym);
return printed;
}
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
index d85a49d546eb..11c46c38b7fb 100644
--- a/tools/perf/builtin-top.c
+++ b/tools/perf/builtin-top.c
@@ -209,7 +209,7 @@ static void perf_top__record_precise_ip(struct perf_top *top,
if (sym == NULL || (use_browser == 0 &&
(top->sym_filter_entry == NULL ||
- top->sym_filter_entry->ms.sym != sym)))
+ !RC_CHK_EQUAL(top->sym_filter_entry->ms.sym, sym))))
return;
notes = symbol__annotation(sym);
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c
index 7683b362783a..0dcf042f7ac0 100644
--- a/tools/perf/builtin-trace.c
+++ b/tools/perf/builtin-trace.c
@@ -799,6 +799,7 @@ size_t syscall_arg__scnprintf_ksym(char *bf, size_t size, struct syscall_arg *ar
printed = scnprintf(bf, size, "%s+0x%" PRIx64,
symbol__name(sym), offset);
}
+ symbol__put(sym);
map__put(map);
return printed;
}
diff --git a/tools/perf/tests/dlfilter-test.c b/tools/perf/tests/dlfilter-test.c
index b4649c79c309..1b7f400761bf 100644
--- a/tools/perf/tests/dlfilter-test.c
+++ b/tools/perf/tests/dlfilter-test.c
@@ -273,12 +273,16 @@ static int get_ip_addr(struct test_data *td)
return -1;
sym = map__find_symbol_by_name(map, "foo");
- if (sym)
+ if (sym) {
td->foo = symbol__start(sym);
+ symbol__put(sym);
+ }
sym = map__find_symbol_by_name(map, "bar");
- if (sym)
+ if (sym) {
td->bar = symbol__start(sym);
+ symbol__put(sym);
+ }
map__put(map);
diff --git a/tools/perf/tests/hists_cumulate.c b/tools/perf/tests/hists_cumulate.c
index 95b9a629e59e..16637f1e1e23 100644
--- a/tools/perf/tests/hists_cumulate.c
+++ b/tools/perf/tests/hists_cumulate.c
@@ -114,7 +114,8 @@ static int add_hist_entries(struct hists *hists, struct machine *machine)
fake_samples[i].thread = thread__get(al.thread);
map__put(fake_samples[i].map);
fake_samples[i].map = map__get(al.map);
- fake_samples[i].sym = al.sym;
+ symbol__put(fake_samples[i].sym);
+ fake_samples[i].sym = symbol__get(al.sym);
}
addr_location__exit(&al);
@@ -155,6 +156,7 @@ static void put_fake_samples(void)
size_t i;
for (i = 0; i < ARRAY_SIZE(fake_samples); i++) {
+ symbol__zput(fake_samples[i].sym);
map__zput(fake_samples[i].map);
thread__zput(fake_samples[i].thread);
}
diff --git a/tools/perf/tests/hists_filter.c b/tools/perf/tests/hists_filter.c
index 6c7e4cdc71a7..3bd0b0b270f5 100644
--- a/tools/perf/tests/hists_filter.c
+++ b/tools/perf/tests/hists_filter.c
@@ -93,7 +93,8 @@ static int add_hist_entries(struct evlist *evlist,
fake_samples[i].thread = thread__get(al.thread);
map__put(fake_samples[i].map);
fake_samples[i].map = map__get(al.map);
- fake_samples[i].sym = al.sym;
+ symbol__put(fake_samples[i].sym);
+ fake_samples[i].sym = symbol__get(al.sym);
}
}
addr_location__exit(&al);
@@ -109,8 +110,11 @@ static void put_fake_samples(void)
{
size_t i;
- for (i = 0; i < ARRAY_SIZE(fake_samples); i++)
- map__put(fake_samples[i].map);
+ for (i = 0; i < ARRAY_SIZE(fake_samples); i++) {
+ symbol__zput(fake_samples[i].sym);
+ map__zput(fake_samples[i].map);
+ thread__zput(fake_samples[i].thread);
+ }
}
static int test__hists_filter(struct test_suite *test __maybe_unused, int subtest __maybe_unused)
diff --git a/tools/perf/tests/hists_link.c b/tools/perf/tests/hists_link.c
index cd32c8277627..b91a75a8fa23 100644
--- a/tools/perf/tests/hists_link.c
+++ b/tools/perf/tests/hists_link.c
@@ -99,7 +99,8 @@ static int add_hist_entries(struct evlist *evlist, struct machine *machine)
fake_common_samples[k].thread = thread__get(al.thread);
map__put(fake_common_samples[k].map);
fake_common_samples[k].map = map__get(al.map);
- fake_common_samples[k].sym = al.sym;
+ symbol__put(fake_common_samples[k].sym);
+ fake_common_samples[k].sym = symbol__get(al.sym);
}
for (k = 0; k < ARRAY_SIZE(fake_samples[i]); k++) {
@@ -119,7 +120,8 @@ static int add_hist_entries(struct evlist *evlist, struct machine *machine)
fake_samples[i][k].thread = thread__get(al.thread);
map__put(fake_samples[i][k].map);
fake_samples[i][k].map = map__get(al.map);
- fake_samples[i][k].sym = al.sym;
+ symbol__put(fake_samples[i][k].sym);
+ fake_samples[i][k].sym = symbol__get(al.sym);
}
i++;
}
@@ -136,11 +138,15 @@ static void put_fake_samples(void)
{
size_t i, j;
- for (i = 0; i < ARRAY_SIZE(fake_common_samples); i++)
+ for (i = 0; i < ARRAY_SIZE(fake_common_samples); i++) {
map__put(fake_common_samples[i].map);
+ symbol__put(fake_common_samples[i].sym);
+ }
for (i = 0; i < ARRAY_SIZE(fake_samples); i++) {
- for (j = 0; j < ARRAY_SIZE(fake_samples[0]); j++)
+ for (j = 0; j < ARRAY_SIZE(fake_samples[0]); j++) {
map__put(fake_samples[i][j].map);
+ symbol__put(fake_samples[i][j].sym);
+ }
}
}
@@ -150,7 +156,7 @@ static int find_sample(struct sample *samples, size_t nr_samples,
while (nr_samples--) {
if (RC_CHK_EQUAL(samples->thread, t) &&
RC_CHK_EQUAL(samples->map, m) &&
- samples->sym == s)
+ RC_CHK_EQUAL(samples->sym, s))
return 1;
samples++;
}
diff --git a/tools/perf/tests/hists_output.c b/tools/perf/tests/hists_output.c
index 77fb3a187139..63b9e48ada63 100644
--- a/tools/perf/tests/hists_output.c
+++ b/tools/perf/tests/hists_output.c
@@ -79,7 +79,8 @@ static int add_hist_entries(struct hists *hists, struct machine *machine)
fake_samples[i].thread = al.thread;
map__put(fake_samples[i].map);
fake_samples[i].map = map__get(al.map);
- fake_samples[i].sym = al.sym;
+ symbol__put(fake_samples[i].sym);
+ fake_samples[i].sym = symbol__get(al.sym);
}
addr_location__exit(&al);
@@ -120,8 +121,8 @@ static void put_fake_samples(void)
size_t i;
for (i = 0; i < ARRAY_SIZE(fake_samples); i++) {
- map__put(fake_samples[i].map);
- fake_samples[i].map = NULL;
+ symbol__zput(fake_samples[i].sym);
+ map__zput(fake_samples[i].map);
}
}
diff --git a/tools/perf/tests/kallsyms-split.c b/tools/perf/tests/kallsyms-split.c
index 244daa01bd5d..0e5c029a583e 100644
--- a/tools/perf/tests/kallsyms-split.c
+++ b/tools/perf/tests/kallsyms-split.c
@@ -99,6 +99,7 @@ static int test__kallsyms_split(struct test_suite *test __maybe_unused,
{
struct machine m = { 0 };
struct map *map = NULL;
+ struct symbol *sym;
int ret = TEST_FAIL;
pr_debug("try to create fake root directory\n");
@@ -139,10 +140,12 @@ static int test__kallsyms_split(struct test_suite *test __maybe_unused,
goto out;
}
- if (machine__find_kernel_symbol_by_name(&m, "main_symbol3", &map) == NULL) {
+ sym = machine__find_kernel_symbol_by_name(&m, "main_symbol3", &map);
+ if (sym == NULL) {
pr_debug("FAIL: failed to find a symbol\n");
goto out;
}
+ symbol__put(sym);
if (!RC_CHK_EQUAL(map, machine__kernel_map(&m))) {
pr_debug("FAIL: the symbol is not in the kernel map\n");
diff --git a/tools/perf/tests/pe-file-parsing.c b/tools/perf/tests/pe-file-parsing.c
index 190520ffc3ad..fdb8d2a7e4c6 100644
--- a/tools/perf/tests/pe-file-parsing.c
+++ b/tools/perf/tests/pe-file-parsing.c
@@ -64,6 +64,7 @@ static int run_dir(const char *d)
dso__sort_by_name(dso);
sym = dso__find_symbol_by_name(dso, "main", &idx);
TEST_ASSERT_VAL("Failed to find main", sym);
+ symbol__put(sym);
dso__delete(dso);
return TEST_OK;
diff --git a/tools/perf/tests/symbols.c b/tools/perf/tests/symbols.c
index 8ccf977aa9f7..d70adbad3978 100644
--- a/tools/perf/tests/symbols.c
+++ b/tools/perf/tests/symbols.c
@@ -260,18 +260,19 @@ static const struct kallsyms_sym kallsyms_syms[] = {
static int check_symbol(struct dso *dso, u64 addr, const char *name, u64 end)
{
struct symbol *sym = dso__find_symbol_nocache(dso, addr);
+ int ret = TEST_OK;
if (!sym || strcmp(symbol__name(sym), name)) {
pr_debug("%#" PRIx64 ": expected %s, got %s\n", addr, name,
sym ? symbol__name(sym) : "no symbol");
- return TEST_FAIL;
- }
- if (symbol__end(sym) != end) {
+ ret = TEST_FAIL;
+ } else if (symbol__end(sym) != end) {
pr_debug("%s: expected end %#" PRIx64 ", got %#" PRIx64 "\n",
name, end, symbol__end(sym));
- return TEST_FAIL;
+ ret = TEST_FAIL;
}
- return TEST_OK;
+ symbol__put(sym);
+ return ret;
}
static int test__kallsyms_fixup_end(struct test_suite *test __maybe_unused,
@@ -289,7 +290,8 @@ static int test__kallsyms_fixup_end(struct test_suite *test __maybe_unused,
if (!sym)
goto out;
- symbols__insert(dso__symbols(dso), sym);
+ if (symbols__insert(dso__symbols(dso), sym))
+ goto out;
}
symbols__fixup_end(dso__symbols(dso), true);
diff --git a/tools/perf/tests/vmlinux-kallsyms.c b/tools/perf/tests/vmlinux-kallsyms.c
index bef40c194d70..7454f13dba06 100644
--- a/tools/perf/tests/vmlinux-kallsyms.c
+++ b/tools/perf/tests/vmlinux-kallsyms.c
@@ -208,7 +208,7 @@ static int test__vmlinux_matches_kallsyms_sym_cb(struct symbol *sym, void *data)
mem_end = map__unmap_ip(args->vmlinux_map, symbol__end(sym));
first_pair = machine__find_kernel_symbol(&args->kallsyms, mem_start, NULL);
- pair = first_pair;
+ pair = symbol__get(first_pair);
if (pair && UM(symbol__start(pair)) == mem_start) {
next_pair:
@@ -237,8 +237,11 @@ static int test__vmlinux_matches_kallsyms_sym_cb(struct symbol *sym, void *data)
* possible to get proper function end from
* kallsyms.
*/
+ symbol__put(pair);
+ symbol__put(first_pair);
return 0;
}
+ symbol__put(pair);
pair = machine__find_kernel_symbol_by_name(&args->kallsyms,
symbol__name(sym), NULL);
if (pair) {
@@ -247,14 +250,17 @@ static int test__vmlinux_matches_kallsyms_sym_cb(struct symbol *sym, void *data)
pr_debug("WARN: %#" PRIx64 ": diff name v: %s k: %s\n",
mem_start, symbol__name(sym), symbol__name(pair));
+ symbol__put(pair);
} else {
pr_debug("WARN: %#" PRIx64 ": diff name v: %s k: %s\n",
mem_start, symbol__name(sym), symbol__name(first_pair));
}
+ symbol__put(first_pair);
return 0;
}
-
+ symbol__put(pair);
+ symbol__put(first_pair);
if (mem_start == map__end(args->kallsyms.vmlinux_map)) {
/*
* Ignore aliases to _etext, i.e. to the end of the kernel text area,
diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c
index 5147b7dca256..0267a91b06e7 100644
--- a/tools/perf/ui/browsers/hists.c
+++ b/tools/perf/ui/browsers/hists.c
@@ -2527,12 +2527,14 @@ static struct symbol *symbol__new_unresolved(u64 addr, struct map *map)
if (sym) {
src = symbol__hists(sym, 1);
if (!src) {
- symbol__delete(sym);
+ symbol__put(sym);
return NULL;
}
- if (dso__insert_symbol(map__dso(map), sym))
+ if (dso__insert_symbol(map__dso(map), symbol__get(sym))) {
+ symbol__put(sym);
return NULL;
+ }
}
return sym;
@@ -2819,7 +2821,7 @@ add_script_opt_2(struct popup_action *act, char **optstr,
}
act->ms.thread = thread__get(thread);
- act->ms.sym = sym;
+ act->ms.sym = symbol__get(sym);
act->fn = do_run_script;
return 1;
}
@@ -3208,6 +3210,7 @@ static int evsel__hists_browse(struct evsel *evsel, int nr_events, const char *h
memset(&hotkey_act, 0, sizeof(hotkey_act));
map_symbol__copy(&hotkey_act.ms, &source_ms);
+ symbol__zput(source_ms.sym);
do_annotate(browser, &hotkey_act);
map_symbol__exit(&hotkey_act.ms);
} else {
@@ -3410,7 +3413,7 @@ static int evsel__hists_browse(struct evsel *evsel, int nr_events, const char *h
&options[nr_options],
&bi->from.ms,
bi->from.al_addr);
- if (bi->to.ms.sym != bi->from.ms.sym)
+ if (!RC_CHK_EQUAL(bi->to.ms.sym, bi->from.ms.sym))
nr_options += add_annotate_opt(&actions[nr_options],
&options[nr_options],
&bi->to.ms,
diff --git a/tools/perf/ui/browsers/map.c b/tools/perf/ui/browsers/map.c
index 71f6161ed27a..3ef62c512355 100644
--- a/tools/perf/ui/browsers/map.c
+++ b/tools/perf/ui/browsers/map.c
@@ -64,12 +64,13 @@ static int map_browser__search(struct map_browser *browser)
* cursor at that entry.
*/
for (u32 idx = 0; idx < browser->b.nr_entries; ++idx) {
- if (entries[idx] == sym) {
+ if (RC_CHK_EQUAL(entries[idx], sym)) {
browser->b.top = &entries[idx];
browser->b.index = browser->b.top_idx = idx;
break;
}
}
+ symbol__put(sym);
} else
ui_helpline__fpush("%s not found!", target);
@@ -135,7 +136,7 @@ int map__browse(struct map *map)
for (unsigned int idx = 0; idx < nr_entries; idx++) {
struct symbol *pos = symbols->symbols[idx];
- entries[idx] = pos;
+ entries[idx] = symbol__get(pos);
if (maxaddr < symbol__end(pos))
maxaddr = symbol__end(pos);
}
@@ -150,6 +151,8 @@ int map__browse(struct map *map)
mb.addrlen = snprintf(tmp, sizeof(tmp), "%" PRIx64, maxaddr);
ret = map_browser__run(&mb);
+ for (unsigned int idx = 0; idx < nr_entries; idx++)
+ symbol__put(entries[idx]);
free(entries);
return ret;
}
diff --git a/tools/perf/ui/stdio/hist.c b/tools/perf/ui/stdio/hist.c
index f80cc73909ed..aedc5eb8b1a4 100644
--- a/tools/perf/ui/stdio/hist.c
+++ b/tools/perf/ui/stdio/hist.c
@@ -908,7 +908,8 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,
free(line);
out:
- zfree(&rem_sq_bracket);
+ symbol__zput(rem_sq_bracket);
+ rem_hits.ms.sym = NULL;
return ret;
}
diff --git a/tools/perf/util/addr2line.c b/tools/perf/util/addr2line.c
index 960dd48701bb..2afa8719ac41 100644
--- a/tools/perf/util/addr2line.c
+++ b/tools/perf/util/addr2line.c
@@ -274,8 +274,14 @@ static int inline_list__append_record(struct dso *dso,
unsigned int line_nr)
{
struct symbol *inline_sym = new_inline_sym(dso, sym, function);
+ char *srcline = srcline_from_fileline(filename, line_nr);
- return inline_list__append(inline_sym, srcline_from_fileline(filename, line_nr), node);
+ if (inline_list__append(inline_sym, srcline, node) != 0) {
+ zfree_srcline(&srcline);
+ symbol__put(inline_sym);
+ return -1;
+ }
+ return 0;
}
int cmd__addr2line(const char *dso_name, u64 addr,
diff --git a/tools/perf/util/addr_location.c b/tools/perf/util/addr_location.c
index 57e8217a00f9..0f21a01a3843 100644
--- a/tools/perf/util/addr_location.c
+++ b/tools/perf/util/addr_location.c
@@ -2,6 +2,7 @@
#include "addr_location.h"
#include "map.h"
#include "maps.h"
+#include "symbol.h"
#include "thread.h"
void addr_location__init(struct addr_location *al)
@@ -29,13 +30,16 @@ void addr_location__exit(struct addr_location *al)
{
map__zput(al->map);
thread__zput(al->thread);
+ symbol__zput(al->sym);
}
void addr_location__copy(struct addr_location *dst, struct addr_location *src)
{
thread__put(dst->thread);
map__put(dst->map);
+ symbol__put(dst->sym);
*dst = *src;
dst->thread = thread__get(src->thread);
dst->map = map__get(src->map);
+ dst->sym = symbol__get(src->sym);
}
diff --git a/tools/perf/util/annotate-arch/annotate-alpha.c b/tools/perf/util/annotate-arch/annotate-alpha.c
index ccb13e2338a2..fc4fcbf27c82 100644
--- a/tools/perf/util/annotate-arch/annotate-alpha.c
+++ b/tools/perf/util/annotate-arch/annotate-alpha.c
@@ -88,8 +88,9 @@ static int alpha_call__parse(const struct arch *arch, struct ins_operands *ops,
if (maps__find_ams(thread__maps(ms->thread), &target) == 0 &&
map__rip_2objdump(target.ms.map,
- map__map_ip(target.ms.map, target.addr)) == ops->target.addr)
- ops->target.sym = target.ms.sym;
+ map__map_ip(target.ms.map, target.addr)) == ops->target.addr &&
+ !RC_CHK_EQUAL(target.ms.sym, ms->sym))
+ ops->target.sym = symbol__get(target.ms.sym);
addr_map_symbol__exit(&target);
return 0;
diff --git a/tools/perf/util/annotate-arch/annotate-loongarch.c b/tools/perf/util/annotate-arch/annotate-loongarch.c
index d275730080b7..775bf44ce495 100644
--- a/tools/perf/util/annotate-arch/annotate-loongarch.c
+++ b/tools/perf/util/annotate-arch/annotate-loongarch.c
@@ -56,8 +56,10 @@ static int loongarch_call__parse(const struct arch *arch, struct ins_operands *o
};
if (maps__find_ams(thread__maps(ms->thread), &target) == 0 &&
- map__rip_2objdump(target.ms.map, map__map_ip(target.ms.map, target.addr)) == ops->target.addr)
- ops->target.sym = target.ms.sym;
+ map__rip_2objdump(target.ms.map,
+ map__map_ip(target.ms.map, target.addr)) == ops->target.addr &&
+ !RC_CHK_EQUAL(target.ms.sym, ms->sym))
+ ops->target.sym = symbol__get(target.ms.sym);
addr_map_symbol__exit(&target);
return 0;
@@ -99,9 +101,10 @@ static int loongarch_jump__parse(const struct arch *arch, struct ins_operands *o
ops->target.outside = target.addr < start || target.addr >= end;
- if (maps__find_ams(thread__maps(ms->thread), &target) == 0 &&
+ if (ops->target.outside &&
+ maps__find_ams(thread__maps(ms->thread), &target) == 0 &&
map__rip_2objdump(target.ms.map, map__map_ip(target.ms.map, target.addr)) == ops->target.addr)
- ops->target.sym = target.ms.sym;
+ ops->target.sym = symbol__get(target.ms.sym);
if (!ops->target.outside) {
ops->target.offset = target.addr - start;
diff --git a/tools/perf/util/annotate-arch/annotate-s390.c b/tools/perf/util/annotate-arch/annotate-s390.c
index af9cabd0a586..ae88e556ee89 100644
--- a/tools/perf/util/annotate-arch/annotate-s390.c
+++ b/tools/perf/util/annotate-arch/annotate-s390.c
@@ -51,8 +51,10 @@ static int s390_call__parse(const struct arch *arch, struct ins_operands *ops,
};
if (maps__find_ams(thread__maps(ms->thread), &target) == 0 &&
- map__rip_2objdump(target.ms.map, map__map_ip(target.ms.map, target.addr)) == ops->target.addr)
- ops->target.sym = target.ms.sym;
+ map__rip_2objdump(target.ms.map,
+ map__map_ip(target.ms.map, target.addr)) == ops->target.addr &&
+ !RC_CHK_EQUAL(target.ms.sym, ms->sym))
+ ops->target.sym = symbol__get(target.ms.sym);
addr_map_symbol__exit(&target);
return 0;
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c
index e4332800fda4..bb9962fec076 100644
--- a/tools/perf/util/annotate.c
+++ b/tools/perf/util/annotate.c
@@ -198,15 +198,22 @@ static bool sym_ptr_equal(long key1, long key2, void *ctx __maybe_unused)
struct annotation *symbol__annotation(struct symbol *sym)
{
struct annotation *notes = NULL;
- long val = 0;
+ long key, val = 0;
if (!sym)
return NULL;
+ /*
+ * Key on the underlying RC_STRUCT(symbol) pointer so all
+ * REFCNT_CHECKING wrapper pointers for the same symbol map to the
+ * same annotation entry.
+ */
+ key = (long)RC_CHK_ACCESS(sym);
+
/* Fast path: look up existing annotation under read lock if flagged. */
if (symbol__annotated(sym)) {
down_read(annotations_lock());
- if (annotations && hashmap__find(annotations, (long)sym, &val))
+ if (annotations && hashmap__find(annotations, key, &val))
notes = (struct annotation *)val;
up_read(annotations_lock());
if (notes)
@@ -221,7 +228,7 @@ struct annotation *symbol__annotation(struct symbol *sym)
up_write(annotations_lock());
return &empty_annotation;
}
- } else if (hashmap__find(annotations, (long)sym, &val)) {
+ } else if (hashmap__find(annotations, key, &val)) {
notes = (struct annotation *)val;
up_write(annotations_lock());
return notes;
@@ -233,7 +240,7 @@ struct annotation *symbol__annotation(struct symbol *sym)
return &empty_annotation;
}
annotation__init(notes);
- if (hashmap__add(annotations, (long)sym, notes) < 0) {
+ if (hashmap__add(annotations, key, notes) < 0) {
free(notes);
if (hashmap__size(annotations) == 0) {
hashmap__free(annotations);
@@ -250,14 +257,15 @@ struct annotation *symbol__annotation(struct symbol *sym)
void symbol__annotation_delete(struct symbol *sym)
{
struct annotation *notes = NULL;
- long val = 0;
+ long key, val = 0;
/* Unannotated symbols avoid taking annotations_lock on teardown. */
if (!sym || !symbol__annotated(sym))
return;
+ key = (long)RC_CHK_ACCESS(sym);
down_write(annotations_lock());
- if (annotations && hashmap__delete(annotations, (long)sym, NULL, &val)) {
+ if (annotations && hashmap__delete(annotations, key, NULL, &val)) {
notes = (struct annotation *)val;
if (hashmap__size(annotations) == 0) {
hashmap__free(annotations);
@@ -571,7 +579,7 @@ int addr_map_symbol__account_cycles(struct addr_map_symbol *ams,
* it starts on the function start.
*/
if (start &&
- (start->ms.sym == ams->ms.sym ||
+ (RC_CHK_EQUAL(start->ms.sym, ams->ms.sym) ||
(ams->ms.sym &&
start->addr == symbol__start(ams->ms.sym) + map__start(ams->ms.map))))
saddr = start->al_addr;
diff --git a/tools/perf/util/block-info.c b/tools/perf/util/block-info.c
index f6ac71a74e6d..bdd7f17ad70c 100644
--- a/tools/perf/util/block-info.c
+++ b/tools/perf/util/block-info.c
@@ -64,8 +64,10 @@ static struct block_info *block_info__new(unsigned int br_cntr_nr)
void block_info__delete(struct block_info *bi)
{
- if (bi)
+ if (bi) {
+ symbol__zput(bi->sym);
free(bi->br_cntr);
+ }
free(bi);
}
@@ -105,7 +107,7 @@ static void init_block_info(struct block_info *bi, struct symbol *sym,
u64 total_cycles, unsigned int br_cntr_nr,
u64 *br_cntr, struct evsel *evsel)
{
- bi->sym = sym;
+ bi->sym = symbol__get(sym);
bi->start = ch->start;
bi->end = offset;
bi->cycles = ch->cycles;
diff --git a/tools/perf/util/bpf_lock_contention.c b/tools/perf/util/bpf_lock_contention.c
index 2b799bf7a9a2..cb4a2546364b 100644
--- a/tools/perf/util/bpf_lock_contention.c
+++ b/tools/perf/util/bpf_lock_contention.c
@@ -144,6 +144,7 @@ static void init_numa_data(struct lock_contention *con)
&kmap);
if (sym) {
skel->rodata->contig_page_data_addr = map__unmap_ip(kmap, symbol__start(sym));
+ symbol__put(sym);
map__put(kmap);
return;
}
@@ -160,6 +161,7 @@ static void init_numa_data(struct lock_contention *con)
return;
skel->rodata->node_data_addr = map__unmap_ip(kmap, symbol__start(sym));
+ symbol__put(sym);
map__put(kmap);
/* get the number of online nodes using the last node number + 1 */
@@ -263,11 +265,15 @@ int lock_contention_prepare(struct lock_contention *con)
(con->filters->nr_addrs + 1) * sizeof(*addrs));
if (addrs == NULL) {
pr_warning("memory allocation failure\n");
+ symbol__put(sym);
+ map__put(kmap);
continue;
}
addrs[con->filters->nr_addrs++] = map__unmap_ip(kmap, symbol__start(sym));
con->filters->addrs = addrs;
+ symbol__put(sym);
+ map__put(kmap);
}
naddrs = con->filters->nr_addrs ?: has_mmap_lock;
skel->rodata->has_addr = 1;
@@ -289,6 +295,8 @@ int lock_contention_prepare(struct lock_contention *con)
}
con->delays[i].addr = map__unmap_ip(kmap, symbol__start(sym));
+ symbol__put(sym);
+ map__put(kmap);
}
skel->rodata->lock_delay = 1;
bpf_map__set_max_entries(skel->maps.lock_delays, con->nr_delays);
@@ -606,9 +614,12 @@ static const char *lock_contention_get_name(struct lock_contention *con,
return "siglock";
/* global locks with symbols */
- sym = machine__find_kernel_symbol(machine, key->lock_addr_or_cgroup, &kmap);
- if (sym)
- return symbol__name(sym);
+ sym = machine__find_kernel_symbol(machine, key->lock_addr_or_cgroup, NULL);
+ if (sym) {
+ snprintf(name_buf, sizeof(name_buf), "%s", symbol__name(sym));
+ symbol__put(sym);
+ return name_buf;
+ }
/* try semi-global locks collected separately */
if (!bpf_map_lookup_elem(lock_fd, &key->lock_addr_or_cgroup, &flags)) {
@@ -655,12 +666,14 @@ static const char *lock_contention_get_name(struct lock_contention *con,
offset = map__map_ip(kmap, addr) - symbol__start(sym);
if (offset == 0)
- return symbol__name(sym);
-
- snprintf(name_buf, sizeof(name_buf), "%s+%#lx", symbol__name(sym), offset);
+ snprintf(name_buf, sizeof(name_buf), "%s", symbol__name(sym));
+ else
+ snprintf(name_buf, sizeof(name_buf), "%s+%#lx", symbol__name(sym), offset);
+ symbol__put(sym);
} else {
snprintf(name_buf, sizeof(name_buf), "%#lx", (unsigned long)addr);
}
+ map__put(kmap);
return name_buf;
}
diff --git a/tools/perf/util/c2c-function.c b/tools/perf/util/c2c-function.c
index b548c76e3016..70f96adc6dc1 100644
--- a/tools/perf/util/c2c-function.c
+++ b/tools/perf/util/c2c-function.c
@@ -976,13 +976,13 @@ c2c_function_hists__level1_entry(struct symbol *sym,
/* mem_info__put() will map_symbol__exit() these, so take refs. */
mem_info__iaddr(mi)->ms.thread = thread__get(detail_he->ms.thread);
mem_info__iaddr(mi)->ms.map = map__get(detail_he->ms.map);
- mem_info__iaddr(mi)->ms.sym = sym;
+ mem_info__iaddr(mi)->ms.sym = symbol__get(sym);
mem_info__daddr(mi)->addr = 0;
addr_location__init(&al);
al.thread = thread__get(synthetic_thread);
al.map = map__get(detail_he->ms.map);
- al.sym = sym;
+ al.sym = symbol__get(sym);
al.addr = sym_start;
al.level = detail_he->level;
al.cpumode = detail_he->cpumode;
@@ -1064,9 +1064,12 @@ c2c_function_hists__level2_entry(struct c2c_hist_entry *level1_c2c,
return NULL;
/* Key this level by the looked-up symbol, not detail_he's. */
- level2_c2c->he.ms.sym = sym;
- if (level2_c2c->he.mem_info)
- mem_info__iaddr(level2_c2c->he.mem_info)->ms.sym = sym;
+ symbol__put(level2_c2c->he.ms.sym);
+ level2_c2c->he.ms.sym = symbol__get(sym);
+ if (level2_c2c->he.mem_info) {
+ symbol__put(mem_info__iaddr(level2_c2c->he.mem_info)->ms.sym);
+ mem_info__iaddr(level2_c2c->he.mem_info)->ms.sym = symbol__get(sym);
+ }
c2c_child_entry__insert(level1_he, &level2_c2c->he, p, parent, leftmost);
diff --git a/tools/perf/util/call-path.c b/tools/perf/util/call-path.c
index 5c60b8be1cf6..5927068fd9b6 100644
--- a/tools/perf/util/call-path.c
+++ b/tools/perf/util/call-path.c
@@ -10,12 +10,13 @@
#include <stdlib.h>
#include "call-path.h"
+#include "symbol.h"
static void call_path__init(struct call_path *cp, struct call_path *parent,
struct symbol *sym, u64 ip, bool in_kernel)
{
cp->parent = parent;
- cp->sym = sym;
+ cp->sym = symbol__get(sym);
cp->ip = sym ? 0 : ip;
cp->db_id = 0;
cp->in_kernel = in_kernel;
@@ -38,8 +39,15 @@ struct call_path_root *call_path_root__new(void)
void call_path_root__free(struct call_path_root *cpr)
{
struct call_path_block *pos, *n;
+ size_t i = 0;
+ if (!cpr)
+ return;
+
+ symbol__zput(cpr->call_path.sym);
list_for_each_entry_safe(pos, n, &cpr->blocks, node) {
+ for (size_t j = 0; j < CALL_PATH_BLOCK_SIZE && i < cpr->next; j++, i++)
+ symbol__zput(pos->cp[j].sym);
list_del_init(&pos->node);
free(pos);
}
@@ -94,10 +102,11 @@ struct call_path *call_path__findnew(struct call_path_root *cpr,
node_parent = *p;
cp = rb_entry(node_parent, struct call_path, rb_node);
- if (cp->sym == sym && cp->ip == ip)
+ if (RC_CHK_EQUAL(cp->sym, sym) && cp->ip == ip)
return cp;
- if (sym < cp->sym || (sym == cp->sym && ip < cp->ip))
+ if ((sym ? RC_CHK_ACCESS(sym) : NULL) < (cp->sym ? RC_CHK_ACCESS(cp->sym) : NULL) ||
+ (RC_CHK_EQUAL(sym, cp->sym) && ip < cp->ip))
p = &(*p)->rb_left;
else
p = &(*p)->rb_right;
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c
index 24c176905c0e..fdb9a7b8639f 100644
--- a/tools/perf/util/callchain.c
+++ b/tools/perf/util/callchain.c
@@ -1094,6 +1094,7 @@ merge_chain_branch(struct callchain_cursor *cursor,
struct map_symbol ms = {
.thread = thread__get(list->ms.thread),
.map = map__get(list->ms.map),
+ .sym = symbol__get(list->ms.sym),
};
callchain_cursor_append(cursor, list->ip, &ms, false, NULL, 0, 0, 0, list->srcline);
list_del_init(&list->list);
@@ -1204,7 +1205,8 @@ int fill_callchain_info(struct addr_location *al, struct callchain_cursor_node *
map__put(al->map);
al->map = map__get(node->ms.map);
- al->sym = node->ms.sym;
+ symbol__put(al->sym);
+ al->sym = symbol__get(node->ms.sym);
al->srcline = node->srcline;
al->addr = node->ip;
diff --git a/tools/perf/util/capstone.c b/tools/perf/util/capstone.c
index 422a48385b78..8178d5a52bcd 100644
--- a/tools/perf/util/capstone.c
+++ b/tools/perf/util/capstone.c
@@ -373,6 +373,7 @@ static void print_capstone_detail(struct cs_insn *insn, char *buf, size_t len,
scnprintf(buf, len, "\t# %"PRIx64" <%s+%#"PRIx64">",
orig_addr, symbol__name(sym), sym_offset);
}
+ symbol__put(sym);
map__put(found_map);
break;
}
@@ -459,6 +460,7 @@ static void format_capstone_insn_arm64(struct cs_insn *insn, char *buf,
scnprintf(last_imm, len, "%"PRIx64" <%s+%#"PRIx64">",
addr, symbol__name(sym), sym_offset);
}
+ symbol__put(sym);
map__put(found_map);
}
}
diff --git a/tools/perf/util/db-export.c b/tools/perf/util/db-export.c
index 97ce6061a77d..f520c3b93a7e 100644
--- a/tools/perf/util/db-export.c
+++ b/tools/perf/util/db-export.c
@@ -13,6 +13,8 @@
#include <linux/err.h>
+#include <internal/rc_check.h>
+
#include "call-path.h"
#include "callchain.h"
#include "comm.h"
@@ -26,14 +28,24 @@
#include "thread-stack.h"
#include "thread.h"
+/*
+ * Hash and compare the underlying RC_STRUCT(symbol) pointer so all
+ * REFCNT_CHECKING wrapper pointers for the same symbol resolve to the same
+ * exported database ID.
+ */
static size_t sym_ptr_hash(long key, void *ctx __maybe_unused)
{
- return hash_bits((size_t)key, 16);
+ const struct symbol *sym = (const struct symbol *)key;
+
+ return hash_bits((size_t)RC_CHK_ACCESS(sym), 16);
}
static bool sym_ptr_equal(long key1, long key2, void *ctx __maybe_unused)
{
- return key1 == key2;
+ const struct symbol *sym1 = (const struct symbol *)key1;
+ const struct symbol *sym2 = (const struct symbol *)key2;
+
+ return RC_CHK_EQUAL(sym1, sym2);
}
int db_export__init(struct db_export *dbe)
@@ -44,8 +56,15 @@ int db_export__init(struct db_export *dbe)
void db_export__exit(struct db_export *dbe)
{
- hashmap__free(dbe->sym_db_ids);
- dbe->sym_db_ids = NULL;
+ if (dbe->sym_db_ids) {
+ struct hashmap_entry *cur;
+ size_t bkt;
+
+ hashmap__for_each_entry(dbe->sym_db_ids, cur, bkt)
+ symbol__put((struct symbol *)cur->pkey);
+ hashmap__free(dbe->sym_db_ids);
+ dbe->sym_db_ids = NULL;
+ }
call_return_processor__free(dbe->crp);
dbe->crp = NULL;
}
@@ -191,6 +210,7 @@ int db_export__symbol(struct db_export *dbe, struct symbol *sym,
struct dso *dso)
{
long db_id;
+ struct symbol *sym_key;
if (db_export__symbol_db_id(dbe, sym))
return 0;
@@ -207,8 +227,11 @@ int db_export__symbol(struct db_export *dbe, struct symbol *sym,
}
db_id = ++dbe->symbol_last_db_id;
- if (hashmap__set(dbe->sym_db_ids, (long)sym, db_id, NULL, NULL))
+ sym_key = symbol__get(sym);
+ if (hashmap__set(dbe->sym_db_ids, (long)sym_key, db_id, NULL, NULL)) {
+ symbol__put(sym_key);
return -ENOMEM;
+ }
if (dbe->export_symbol)
return dbe->export_symbol(dbe, sym, dso);
@@ -231,8 +254,8 @@ static int db_ids_from_al(struct db_export *dbe, struct addr_location *al,
if (!al->sym) {
al->sym = symbol__new(al->addr, 0, 0, 0, "unknown");
- if (al->sym && dso__insert_symbol(dso, al->sym))
- al->sym = NULL;
+ if (al->sym && dso__insert_symbol(dso, symbol__get(al->sym)))
+ symbol__zput(al->sym);
}
if (al->sym) {
@@ -292,7 +315,7 @@ static struct call_path *call_path_from_sample(struct db_export *dbe,
* db_ids_from_al() to perform the export.
*/
addr_location__init(&al);
- al.sym = node->ms.sym;
+ al.sym = symbol__get(node->ms.sym);
al.map = map__get(node->ms.map);
al.addr = node->ip;
al.thread = thread__get(thread);
diff --git a/tools/perf/util/disasm.c b/tools/perf/util/disasm.c
index 9b3e476bab6d..1a96e02e9288 100644
--- a/tools/perf/util/disasm.c
+++ b/tools/perf/util/disasm.c
@@ -228,6 +228,7 @@ static void ins_ops__delete(struct ins_operands *ops)
zfree(&ops->source.name);
zfree(&ops->target.raw);
zfree(&ops->target.name);
+ symbol__zput(ops->target.sym);
}
int ins__raw_scnprintf(const struct ins *ins, char *bf, size_t size,
@@ -289,8 +290,10 @@ static int call__parse(const struct arch *arch, struct ins_operands *ops, struct
};
if (maps__find_ams(thread__maps(ms->thread), &target) == 0 &&
- map__rip_2objdump(target.ms.map, map__map_ip(target.ms.map, target.addr)) == ops->target.addr)
- ops->target.sym = target.ms.sym;
+ map__rip_2objdump(target.ms.map,
+ map__map_ip(target.ms.map, target.addr)) == ops->target.addr &&
+ !RC_CHK_EQUAL(target.ms.sym, ms->sym))
+ ops->target.sym = symbol__get(target.ms.sym);
addr_map_symbol__exit(&target);
return 0;
@@ -424,9 +427,10 @@ static int jump__parse(const struct arch *arch, struct ins_operands *ops, struct
* Actual navigation will come next, with further understanding of how
* the symbol searching and disassembly should be done.
*/
- if (maps__find_ams(thread__maps(ms->thread), &target) == 0 &&
+ if (ops->target.outside &&
+ maps__find_ams(thread__maps(ms->thread), &target) == 0 &&
map__rip_2objdump(target.ms.map, map__map_ip(target.ms.map, target.addr)) == ops->target.addr)
- ops->target.sym = target.ms.sym;
+ ops->target.sym = symbol__get(target.ms.sym);
if (!ops->target.outside) {
ops->target.offset = target.addr - start;
@@ -473,12 +477,14 @@ int jump__scnprintf(const struct ins *ins, char *bf, size_t size,
ops->target.offset);
}
-void jump__delete(struct ins_operands *ops __maybe_unused)
+void jump__delete(struct ins_operands *ops)
{
/*
* The ops->jump.raw_comment and ops->jump.raw_func_start belong to the
* raw string, don't free them.
*/
+ if (ops)
+ symbol__zput(ops->target.sym);
}
const struct ins_ops jump_ops = {
@@ -572,6 +578,7 @@ static void lock__delete(struct ins_operands *ops)
zfree(&ops->locked.ins.name);
zfree(&ops->target.raw);
zfree(&ops->target.name);
+ symbol__zput(ops->target.sym);
}
const struct ins_ops lock_ops = {
@@ -1090,8 +1097,9 @@ static int symbol__parse_objdump_line(struct symbol *sym,
};
if (!maps__find_ams(thread__maps(args->ms->thread), &target) &&
- symbol__start(target.ms.sym) == target.al_addr)
- dl->ops.target.sym = target.ms.sym;
+ symbol__start(target.ms.sym) == target.al_addr &&
+ !RC_CHK_EQUAL(target.ms.sym, sym))
+ dl->ops.target.sym = symbol__get(target.ms.sym);
addr_map_symbol__exit(&target);
}
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c
index 939307796a88..a71267af4f3c 100644
--- a/tools/perf/util/event.c
+++ b/tools/perf/util/event.c
@@ -770,7 +770,7 @@ struct map *thread__find_map_fb(struct thread *thread, u8 cpumode, u64 addr,
struct symbol *thread__find_symbol(struct thread *thread, u8 cpumode,
u64 addr, struct addr_location *al)
{
- al->sym = NULL;
+ symbol__zput(al->sym);
if (thread__find_map(thread, cpumode, addr, al))
al->sym = map__find_symbol(al->map, al->addr);
return al->sym;
@@ -779,7 +779,7 @@ struct symbol *thread__find_symbol(struct thread *thread, u8 cpumode,
struct symbol *thread__find_symbol_fb(struct thread *thread, u8 cpumode,
u64 addr, struct addr_location *al)
{
- al->sym = NULL;
+ symbol__zput(al->sym);
if (thread__find_map_fb(thread, cpumode, addr, al))
al->sym = map__find_symbol(al->map, al->addr);
return al->sym;
@@ -829,7 +829,7 @@ int machine__resolve(struct machine *machine, struct addr_location *al,
thread__put(thread);
thread = NULL;
- al->sym = NULL;
+ symbol__zput(al->sym);
al->cpu = sample->cpu;
al->socket = -1;
al->srcline = NULL;
@@ -937,7 +937,7 @@ void thread__resolve(struct thread *thread, struct addr_location *al,
thread__find_map_fb(thread, sample->cpumode, sample->addr, al);
al->cpu = sample->cpu;
- al->sym = NULL;
+ symbol__zput(al->sym);
if (al->map)
al->sym = map__find_symbol(al->map, al->addr);
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c
index 8a1a5e36d7cd..badae244638c 100644
--- a/tools/perf/util/hist.c
+++ b/tools/perf/util/hist.c
@@ -543,6 +543,7 @@ static int hist_entry__init(struct hist_entry *he,
he->ms.thread = thread__get(he->ms.thread);
he->ms.map = map__get(he->ms.map);
+ he->ms.sym = symbol__get(he->ms.sym);
if (he->branch_info) {
/*
@@ -559,8 +560,10 @@ static int hist_entry__init(struct hist_entry *he,
he->branch_info->from.ms.thread = thread__get(he->branch_info->from.ms.thread);
he->branch_info->from.ms.map = map__get(he->branch_info->from.ms.map);
+ he->branch_info->from.ms.sym = symbol__get(he->branch_info->from.ms.sym);
he->branch_info->to.ms.thread = thread__get(he->branch_info->to.ms.thread);
he->branch_info->to.ms.map = map__get(he->branch_info->to.ms.map);
+ he->branch_info->to.ms.sym = symbol__get(he->branch_info->to.ms.sym);
}
if (he->mem_info) {
@@ -593,6 +596,7 @@ static int hist_entry__init(struct hist_entry *he,
INIT_LIST_HEAD(&he->pairs.node);
he->thread = thread__get(he->thread);
+ he->parent = symbol__get(he->parent);
he->hroot_in = RB_ROOT_CACHED;
he->hroot_out = RB_ROOT_CACHED;
@@ -728,6 +732,8 @@ static struct hist_entry *hists__findnew_entry(struct hists *hists,
if (hists__has(hists, sym) && he->ms.map != entry->ms.map) {
if (he->ms.sym) {
u64 addr = symbol__start(he->ms.sym);
+
+ symbol__put(he->ms.sym);
he->ms.sym = map__find_symbol(entry->ms.map, addr);
}
@@ -1029,7 +1035,8 @@ iter_next_branch_entry(struct hist_entry_iter *iter, struct addr_location *al)
al->thread = thread__get(bi[i].to.ms.thread);
map__put(al->map);
al->map = map__get(bi[i].to.ms.map);
- al->sym = bi[i].to.ms.sym;
+ symbol__put(al->sym);
+ al->sym = symbol__get(bi[i].to.ms.sym);
al->addr = bi[i].to.addr;
return 1;
}
@@ -1343,9 +1350,11 @@ int hist_entry_iter__add(struct hist_entry_iter *iter, struct addr_location *al,
if (al)
alm = map__get(al->map);
+ iter->parent = NULL;
err = sample__resolve_callchain(iter->sample, get_tls_callchain_cursor(), &iter->parent,
al, max_stack_depth);
if (err) {
+ symbol__zput(iter->parent);
map__put(alm);
return err;
}
@@ -1381,6 +1390,7 @@ int hist_entry_iter__add(struct hist_entry_iter *iter, struct addr_location *al,
if (!err)
err = err2;
+ symbol__zput(iter->parent);
map__put(alm);
return err;
@@ -1467,6 +1477,7 @@ void hist_entry__delete(struct hist_entry *he)
}
thread__zput(he->thread);
+ symbol__zput(he->parent);
map_symbol__exit(&he->ms);
if (he->branch_info) {
diff --git a/tools/perf/util/libbfd.c b/tools/perf/util/libbfd.c
index 5e947271e388..a88a89e09545 100644
--- a/tools/perf/util/libbfd.c
+++ b/tools/perf/util/libbfd.c
@@ -246,7 +246,12 @@ static int inline_list__append_dso_a2l(struct dso *dso,
if (a2l->filename)
srcline = srcline_from_fileline(a2l->filename, a2l->line);
- return inline_list__append(inline_sym, srcline, node);
+ if (inline_list__append(inline_sym, srcline, node) != 0) {
+ zfree_srcline(&srcline);
+ symbol__put(inline_sym);
+ return -1;
+ }
+ return 0;
}
int libbfd__addr2line(const char *dso_name, u64 addr,
diff --git a/tools/perf/util/libdw.c b/tools/perf/util/libdw.c
index e660623338d5..3b14a3ad6f86 100644
--- a/tools/perf/util/libdw.c
+++ b/tools/perf/util/libdw.c
@@ -99,7 +99,7 @@ static int libdw_a2l_cb(Dwarf_Die *die, void *_args)
* while DWARF keeps the pre-clone linkage name, which
* left the outer frame spuriously tagged "(inlined)".
*/
- inline_sym = args->sym;
+ inline_sym = symbol__get(args->sym);
} else {
/*
* Prefer DW_AT_linkage_name so C++ inline frames keep
@@ -157,8 +157,7 @@ static int libdw_a2l_cb(Dwarf_Die *die, void *_args)
return 0;
abort_delete_sym:
- if (symbol__inlined(inline_sym))
- symbol__delete(inline_sym);
+ symbol__put(inline_sym);
abort_enomem:
args->err = -ENOMEM;
return DWARF_CB_ABORT;
diff --git a/tools/perf/util/llvm.c b/tools/perf/util/llvm.c
index 7429e8ef6cce..754c9ec18a11 100644
--- a/tools/perf/util/llvm.c
+++ b/tools/perf/util/llvm.c
@@ -63,6 +63,8 @@ int llvm__addr2line(const char *dso_name __maybe_unused, u64 addr __maybe_unused
inline_frames[i].line);
}
if (inline_list__append(inline_sym, srcline, node) != 0) {
+ zfree_srcline(&srcline);
+ symbol__put(inline_sym);
if (file)
zfree(file);
inline_node__clear_frames(node);
diff --git a/tools/perf/util/lock-contention.c b/tools/perf/util/lock-contention.c
index 8ee1b19a8292..367666028eb2 100644
--- a/tools/perf/util/lock-contention.c
+++ b/tools/perf/util/lock-contention.c
@@ -101,7 +101,6 @@ struct lock_stat *lock_stat_findnew(u64 addr, const char *name, int flags)
bool match_callstack_filter(struct machine *machine, u64 *callstack, int max_stack_depth)
{
- struct map *kmap;
struct symbol *sym;
u64 ip;
uint16_t e_machine = perf_env__e_machine(machine->env, /*e_flags=*/NULL);
@@ -130,14 +129,17 @@ bool match_callstack_filter(struct machine *machine, u64 *callstack, int max_sta
break;
ip = callstack[i];
- sym = machine__find_kernel_symbol(machine, ip, &kmap);
+ sym = machine__find_kernel_symbol(machine, ip, NULL);
if (sym == NULL)
continue;
list_for_each_entry(filter, &callstack_filters, list) {
- if (strstr(symbol__name(sym), filter->name))
+ if (strstr(symbol__name(sym), filter->name)) {
+ symbol__put(sym);
return true;
+ }
}
+ symbol__put(sym);
}
return false;
}
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index 14e93e7fb235..cb9e34220036 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -824,8 +824,10 @@ static int machine__process_ksymbol_unregister(struct machine *machine,
struct dso *dso = map__dso(map);
sym = dso__find_symbol(dso, map__map_ip(map, map__start(map)));
- if (sym)
+ if (sym) {
dso__delete_symbol(dso, sym);
+ symbol__put(sym);
+ }
}
map__put(map);
return 0;
@@ -2085,7 +2087,7 @@ static void ip__resolve_ams(struct thread *thread,
ams->al_addr = al.addr;
ams->al_level = al.level;
ams->ms.thread = thread__get(al.thread);
- ams->ms.sym = al.sym;
+ ams->ms.sym = symbol__get(al.sym);
ams->ms.map = map__get(al.map);
ams->phys_addr = 0;
ams->data_page_size = 0;
@@ -2106,7 +2108,7 @@ static void ip__resolve_data(struct thread *thread,
ams->al_addr = al.addr;
ams->al_level = al.level;
ams->ms.thread = thread__get(al.thread);
- ams->ms.sym = al.sym;
+ ams->ms.sym = symbol__get(al.sym);
ams->ms.map = map__get(al.map);
ams->phys_addr = phys_addr;
ams->data_page_size = daddr_page_size;
@@ -2192,7 +2194,8 @@ static int append_inlines(struct callchain_cursor *cursor, struct map_symbol *ms
.map = map__get(map),
};
list_for_each_entry(ilist, &inline_node->val, list) {
- ilist_ms.sym = ilist->symbol;
+ symbol__put(ilist_ms.sym);
+ ilist_ms.sym = symbol__get(ilist->symbol);
if (first) {
ret = callchain_cursor_append(cursor, ip, &ilist_ms,
branch, flags, nr_loop_iter,
@@ -2204,7 +2207,7 @@ static int append_inlines(struct callchain_cursor *cursor, struct map_symbol *ms
first = false;
if (ret != 0)
- return ret;
+ break;
}
map_symbol__exit(&ilist_ms);
@@ -2270,7 +2273,7 @@ static int add_callchain_ip(struct thread *thread,
if (al.sym != NULL) {
if (perf_hpp_list.parent && !*parent &&
symbol__match_regex(al.sym, &parent_regex))
- *parent = al.sym;
+ *parent = symbol__get(al.sym);
else if (have_ignore_callees && root_al &&
symbol__match_regex(al.sym, &ignore_callees_regex)) {
/* Treat this symbol as the root,
@@ -2290,7 +2293,7 @@ static int add_callchain_ip(struct thread *thread,
ms.thread = thread__get(al.thread);
ms.map = map__get(al.map);
- ms.sym = al.sym;
+ ms.sym = symbol__get(al.sym);
if (append_inlines(cursor, &ms, ip, branch, flags, nr_loop_iter,
iter_cycles, branch_from) == 0)
@@ -2451,8 +2454,7 @@ static void save_lbr_cursor_node(struct thread *thread,
map_symbol__exit(&lbr_stitch->prev_lbr_cursor[idx].ms);
memcpy(&lbr_stitch->prev_lbr_cursor[idx], cursor->curr,
sizeof(struct callchain_cursor_node));
- lbr_stitch->prev_lbr_cursor[idx].ms.thread = thread__get(cursor->curr->ms.thread);
- lbr_stitch->prev_lbr_cursor[idx].ms.map = map__get(cursor->curr->ms.map);
+ map_symbol__copy(&lbr_stitch->prev_lbr_cursor[idx].ms, &cursor->curr->ms);
lbr_stitch->prev_lbr_cursor[idx].valid = true;
cursor->pos++;
@@ -2676,9 +2678,7 @@ static bool has_stitched_lbr(struct thread *thread,
memcpy(&stitch_node->cursor, &lbr_stitch->prev_lbr_cursor[i],
sizeof(struct callchain_cursor_node));
- stitch_node->cursor.ms.thread =
- thread__get(lbr_stitch->prev_lbr_cursor[i].ms.thread);
- stitch_node->cursor.ms.map = map__get(lbr_stitch->prev_lbr_cursor[i].ms.map);
+ map_symbol__copy(&stitch_node->cursor.ms, &lbr_stitch->prev_lbr_cursor[i].ms);
if (callee)
list_add(&stitch_node->node, &lbr_stitch->lists);
@@ -3281,16 +3281,28 @@ struct dso *machine__findnew_dso(struct machine *machine, const char *filename)
char *machine__resolve_kernel_addr(void *vmachine, unsigned long long *addrp, char **modp)
{
+ static __thread char resolved_name[512];
+ static __thread char resolved_mod[PATH_MAX];
struct machine *machine = vmachine;
- struct map *map;
+ struct map *map = NULL;
struct symbol *sym = machine__find_kernel_symbol(machine, *addrp, &map);
- if (sym == NULL)
+ if (sym == NULL) {
+ map__put(map);
return NULL;
+ }
- *modp = __map__is_kmodule(map) ? (char *)dso__short_name(map__dso(map)) : NULL;
+ if (__map__is_kmodule(map)) {
+ snprintf(resolved_mod, sizeof(resolved_mod), "%s", dso__short_name(map__dso(map)));
+ *modp = resolved_mod;
+ } else {
+ *modp = NULL;
+ }
*addrp = map__unmap_ip(map, symbol__start(sym));
- return (char *)symbol__name(sym);
+ snprintf(resolved_name, sizeof(resolved_name), "%s", symbol__name(sym));
+ symbol__put(sym);
+ map__put(map);
+ return resolved_name;
}
struct machine__for_each_dso_cb_args {
@@ -3337,26 +3349,38 @@ bool machine__is_lock_function(struct machine *machine, u64 addr)
}
machine->sched.text_start = map__unmap_ip(kmap, symbol__start(sym));
+ symbol__put(sym);
+ map__put(kmap);
/* should not fail from here */
sym = machine__find_kernel_symbol_by_name(machine, "__sched_text_end", &kmap);
machine->sched.text_end = map__unmap_ip(kmap, symbol__start(sym));
+ symbol__put(sym);
+ map__put(kmap);
sym = machine__find_kernel_symbol_by_name(machine, "__lock_text_start", &kmap);
machine->lock.text_start = map__unmap_ip(kmap, symbol__start(sym));
+ symbol__put(sym);
+ map__put(kmap);
sym = machine__find_kernel_symbol_by_name(machine, "__lock_text_end", &kmap);
machine->lock.text_end = map__unmap_ip(kmap, symbol__start(sym));
+ symbol__put(sym);
+ map__put(kmap);
sym = machine__find_kernel_symbol_by_name(machine, "__traceiter_contention_begin", &kmap);
if (sym) {
machine->traceiter.text_start = map__unmap_ip(kmap, symbol__start(sym));
machine->traceiter.text_end = map__unmap_ip(kmap, symbol__end(sym));
+ symbol__put(sym);
+ map__put(kmap);
}
sym = machine__find_kernel_symbol_by_name(machine, "trace_contention_begin", &kmap);
if (sym) {
machine->trace.text_start = map__unmap_ip(kmap, symbol__start(sym));
machine->trace.text_end = map__unmap_ip(kmap, symbol__end(sym));
+ symbol__put(sym);
+ map__put(kmap);
}
}
diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h
index 3d8f72e70d49..745eefa79c7f 100644
--- a/tools/perf/util/map.h
+++ b/tools/perf/util/map.h
@@ -160,13 +160,15 @@ struct thread;
* @sym_name: the symbol name
* @pos: the 'struct symbol *' to use as a loop cursor
* @idx: the cursor index in the symbol names array
+ * Note: caller must call symbol__put(pos) if breaking out of the loop early.
*/
#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(symbol__name(pos), sym_name, \
- SYMBOL_TAG_INCLUDE__DEFAULT_ONLY); \
- pos = dso__next_symbol_by_name(map__dso(map), &idx))
+ pos && \
+ (!symbol__match_symbol_name(symbol__name(pos), sym_name, \
+ SYMBOL_TAG_INCLUDE__DEFAULT_ONLY) || \
+ (symbol__zput(pos), false)); \
+ symbol__put(pos), pos = dso__next_symbol_by_name(map__dso(map), &idx))
#define map__for_each_symbol_by_name(map, sym_name, pos, idx) \
__map__for_each_symbol_by_name(map, sym_name, (pos), idx)
diff --git a/tools/perf/util/map_symbol.c b/tools/perf/util/map_symbol.c
index 11bc0a7f704c..d1ee4e05564e 100644
--- a/tools/perf/util/map_symbol.c
+++ b/tools/perf/util/map_symbol.c
@@ -2,12 +2,14 @@
#include "map_symbol.h"
#include "maps.h"
#include "map.h"
+#include "symbol.h"
#include "thread.h"
void map_symbol__exit(struct map_symbol *ms)
{
thread__zput(ms->thread);
map__zput(ms->map);
+ symbol__zput(ms->sym);
}
void addr_map_symbol__exit(struct addr_map_symbol *ams)
@@ -19,7 +21,7 @@ void map_symbol__copy(struct map_symbol *dst, struct map_symbol *src)
{
dst->thread = thread__get(src->thread);
dst->map = map__get(src->map);
- dst->sym = src->sym;
+ dst->sym = symbol__get(src->sym);
}
void addr_map_symbol__copy(struct addr_map_symbol *dst, struct addr_map_symbol *src)
diff --git a/tools/perf/util/maps.c b/tools/perf/util/maps.c
index f808df2fe77b..a6cbe65cc5ca 100644
--- a/tools/perf/util/maps.c
+++ b/tools/perf/util/maps.c
@@ -788,7 +788,7 @@ struct symbol *maps__find_symbol_by_name(struct maps *maps, const char *name, st
*mapp = map__get(map);
break;
}
- sym = NULL;
+ symbol__zput(sym);
}
for (unsigned int i = 0; i < nr_maps; i++)
@@ -810,6 +810,7 @@ int maps__find_ams(struct maps *maps, struct addr_map_symbol *ams)
}
ams->al_addr = map__map_ip(ams->ms.map, ams->addr);
+ symbol__put(ams->ms.sym);
ams->ms.sym = map__find_symbol(ams->ms.map, ams->al_addr);
return ams->ms.sym ? 0 : -1;
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index bea7903a11e3..a02ec8a7c058 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -152,6 +152,8 @@ static int kernel_get_symbol_address_by_name(const char *name, u64 *addr,
*addr = map__unmap_ip(map, symbol__start(sym)) -
((reloc) ? 0 : map__reloc(map)) -
((reladdr) ? map__start(map) : 0);
+ symbol__put(sym);
+ map__put(map);
}
return 0;
}
@@ -422,6 +424,7 @@ static int find_alternative_probe_point(struct debuginfo *dinfo,
pp->function);
} else
address = map__unmap_ip(map, symbol__start(sym)) - map__reloc(map);
+ symbol__put(sym);
break;
}
if (!address) {
@@ -728,11 +731,14 @@ static int post_process_probe_trace_point(struct probe_trace_point *tp,
else
free(tp->symbol);
tp->symbol = strdup(symbol__name(sym));
- if (!tp->symbol)
+ if (!tp->symbol) {
+ symbol__put(sym);
return -ENOMEM;
+ }
}
tp->offset = addr - symbol__start(sym);
tp->address -= offs;
+ symbol__put(sym);
return 0;
}
@@ -2314,6 +2320,7 @@ static int find_perf_probe_point_from_map(struct probe_trace_point *tp,
ret = pp->function ? 0 : -ENOMEM;
out:
+ symbol__put(sym);
map__put(map);
return ret;
@@ -3078,7 +3085,7 @@ static int find_probe_functions_cb(struct symbol *sym, void *data)
if (strglobmatch(norm, args->name)) {
args->found++;
if (args->syms && args->found < probe_conf.max_probes)
- args->syms[args->found - 1] = sym;
+ args->syms[args->found - 1] = symbol__get(sym);
}
free(buf);
return 0;
@@ -3146,7 +3153,7 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev,
goto out;
}
- syms = malloc(sizeof(struct symbol *) * probe_conf.max_probes);
+ syms = calloc(probe_conf.max_probes, sizeof(struct symbol *));
if (!syms) {
ret = -ENOMEM;
goto out;
@@ -3290,6 +3297,10 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev,
}
out:
+ if (syms) {
+ for (i = 0; i < probe_conf.max_probes; i++)
+ symbol__put(syms[i]);
+ }
map__put(map);
free(syms);
return ret;
diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c
index 71ff233553cc..668bf23c166f 100644
--- a/tools/perf/util/python.c
+++ b/tools/perf/util/python.c
@@ -882,7 +882,7 @@ static PyObject *pyrf_sample_event__srccode(PyObject *self, PyObject *args)
addr_location__init(&al);
al.thread = thread__get(pevent->al.thread);
al.map = map__get(pevent->al.map);
- al.sym = pevent->al.sym;
+ al.sym = symbol__get(pevent->al.sym);
al.addr = pevent->al.addr;
}
@@ -938,13 +938,12 @@ struct pyrf_callchain_node {
u64 ip;
struct map *map;
struct symbol *sym;
- char *sym_name;
};
static void pyrf_callchain_node__delete(struct pyrf_callchain_node *pnode)
{
map__put(pnode->map);
- free(pnode->sym_name);
+ symbol__put(pnode->sym);
Py_TYPE(pnode)->tp_free((PyObject *)pnode);
}
@@ -957,8 +956,6 @@ static PyObject *pyrf_callchain_node__get_ip(struct pyrf_callchain_node *pnode,
static PyObject *pyrf_callchain_node__get_symbol(struct pyrf_callchain_node *pnode,
void *closure __maybe_unused)
{
- if (pnode->sym_name)
- return PyUnicode_FromString(pnode->sym_name);
if (pnode->sym)
return PyUnicode_FromString(symbol__name(pnode->sym));
return PyUnicode_FromString("[unknown]");
@@ -1016,7 +1013,6 @@ struct pyrf_callchain_frame {
u64 ip;
struct map *map;
struct symbol *sym;
- char *sym_name;
};
struct pyrf_callchain {
@@ -1030,7 +1026,7 @@ static void pyrf_callchain__delete(struct pyrf_callchain *pchain)
if (pchain->frames) {
for (u64 i = 0; i < pchain->nr_frames; i++) {
map__put(pchain->frames[i].map);
- free(pchain->frames[i].sym_name);
+ symbol__put(pchain->frames[i].sym);
}
free(pchain->frames);
}
@@ -1060,8 +1056,7 @@ static PyObject *pyrf_callchain__item(PyObject *obj, Py_ssize_t i)
pnode->ip = pchain->frames[i].ip;
pnode->map = map__get(pchain->frames[i].map);
- pnode->sym = pchain->frames[i].sym;
- pnode->sym_name = pchain->frames[i].sym_name ? strdup(pchain->frames[i].sym_name) : NULL;
+ pnode->sym = symbol__get(pchain->frames[i].sym);
return (PyObject *)pnode;
}
@@ -1114,7 +1109,7 @@ static int pyrf_sample_event__resolve_callchain(struct pyrf_event *pevent)
node = callchain_cursor_current(cursor);
pchain->frames[i].ip = node->ip;
pchain->frames[i].map = map__get(node->ms.map);
- pchain->frames[i].sym = node->ms.sym;
+ pchain->frames[i].sym = symbol__get(node->ms.sym);
callchain_cursor_advance(cursor);
}
pevent->callchain = (PyObject *)pchain;
@@ -4504,8 +4499,7 @@ static PyObject *pyrf_call_path__to_callchain(const struct call_path *cp,
struct pyrf_callchain_frame *frame = &pchain->frames[--nr_frames];
frame->ip = pos->ip;
- frame->sym = NULL;
- frame->sym_name = pos->sym ? strdup(symbol__name(pos->sym)) : NULL;
+ frame->sym = symbol__get(pos->sym);
frame->map = NULL;
if (thread) {
struct addr_location al;
@@ -4515,8 +4509,6 @@ static PyObject *pyrf_call_path__to_callchain(const struct call_path *cp,
addr_location__init(&al);
thread__find_map(thread, cpumode, pos->ip, &al);
frame->map = map__get(al.map);
- if (frame->map)
- frame->sym = pos->sym;
addr_location__exit(&al);
}
}
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c
index c4eca7a98642..87f23f1a31ea 100644
--- a/tools/perf/util/sort.c
+++ b/tools/perf/util/sort.c
@@ -466,7 +466,7 @@ int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r)
if (!sym_l || !sym_r)
return cmp_null(sym_l, sym_r);
- if (sym_l == sym_r)
+ if (RC_CHK_EQUAL(sym_l, sym_r))
return 0;
if (symbol__inlined(sym_l) || symbol__inlined(sym_r)) {
diff --git a/tools/perf/util/srcline.c b/tools/perf/util/srcline.c
index 14f2b6dfd086..d465bd8b7549 100644
--- a/tools/perf/util/srcline.c
+++ b/tools/perf/util/srcline.c
@@ -118,7 +118,7 @@ struct symbol *new_inline_sym(struct dso *dso,
if (base_sym && strcmp(funcname, symbol__name(base_sym)) == 0) {
/* reuse the real, existing symbol */
- inline_sym = base_sym;
+ inline_sym = symbol__get(base_sym);
/* ensure that we don't alias an inlined symbol, which could
* lead to double frees in inline_node__delete
*/
@@ -478,9 +478,7 @@ void inline_node__clear_frames(struct inline_node *node)
list_for_each_entry_safe(ilist, tmp, &node->val, list) {
list_del_init(&ilist->list);
zfree_srcline(&ilist->srcline);
- /* only the inlined symbols are owned by the list */
- if (ilist->symbol && symbol__inlined(ilist->symbol))
- symbol__delete(ilist->symbol);
+ symbol__zput(ilist->symbol);
free(ilist);
}
}
diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c
index 77db7ab5011d..4023174ed6db 100644
--- a/tools/perf/util/symbol-elf.c
+++ b/tools/perf/util/symbol-elf.c
@@ -352,10 +352,13 @@ static bool get_ifunc_name(Elf *elf, struct dso *dso, GElf_Ehdr *ehdr,
/* Expecting the address to be an IFUNC or IFUNC alias */
if (!sym || symbol__start(sym) != addr ||
- (symbol__type(sym) != STT_GNU_IFUNC && !symbol__ifunc_alias(sym)))
+ (symbol__type(sym) != STT_GNU_IFUNC && !symbol__ifunc_alias(sym))) {
+ symbol__put(sym);
return false;
+ }
snprintf(buf, buf_sz, "%s@plt", symbol__name(sym));
+ symbol__put(sym);
return true;
}
@@ -633,6 +636,7 @@ int dso__synthesize_plt_symbols(struct dso *dso, struct symsrc *ss)
f = dso__find_symbol_nocache(dso, shdr_plt.sh_offset);
if (f && symbol__start(f) < shdr_plt.sh_offset && symbol__end(f) > shdr_plt.sh_offset)
symbol__set_end(f, shdr_plt.sh_offset);
+ symbol__put(f);
if (!get_plt_sizes(dso, &ehdr, &shdr_plt, &plt_header_size, &plt_entry_size))
return 0;
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 0055bfdc980c..b2c0a606b13a 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
#include <dirent.h>
#include <errno.h>
+#include <stdatomic.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
@@ -228,7 +229,7 @@ void symbols__init(struct symbols *symbols)
static void symbols__delete_locked(struct symbols *symbols)
{
for (unsigned int i = 0; i < symbols->cnt; i++)
- symbol__delete(symbols->symbols[i]);
+ symbol__put(symbols->symbols[i]);
zfree(&symbols->symbols);
zfree(&symbols->symbols_by_name);
@@ -248,7 +249,13 @@ void symbols__delete(struct symbols *symbols)
void symbols__exit(struct symbols *symbols)
{
- symbols__delete(symbols);
+ down_write(&symbols->lock);
+ for (unsigned int i = 0; i < symbols->cnt; i++) {
+ if (symbol__annotated(symbols->symbols[i]))
+ symbol__annotation_delete(symbols->symbols[i]);
+ }
+ symbols__delete_locked(symbols);
+ up_write(&symbols->lock);
exit_rwsem(&symbols->lock);
}
@@ -331,11 +338,11 @@ void symbols__fixup_duplicate(struct symbols *symbols)
if (choose_best_symbol(curr, next) == SYMBOL_A) {
if (symbol__type(next) == STT_GNU_IFUNC)
symbol__set_ifunc_alias(curr, true);
- symbol__delete(next);
+ symbol__put(next);
} else {
if (symbol__type(curr) == STT_GNU_IFUNC)
symbol__set_ifunc_alias(next, true);
- symbol__delete(curr);
+ symbol__put(curr);
symbols->symbols[write_idx] = next;
}
}
@@ -421,81 +428,96 @@ void symbols__fixup_end(struct symbols *symbols, bool is_kallsyms)
struct symbol *symbol__new(u64 start, u64 len, u8 binding, u8 type, const char *name)
{
size_t namelen = strlen(name) + 1;
- struct symbol *sym = calloc(1, sizeof(*sym) + namelen);
-
- if (sym == NULL)
- return NULL;
-
- sym->start = start;
- sym->end = len ? start + len : start;
- atomic_init(&sym->flags, (type << SYMBOL_FLAG_TYPE_SHIFT) |
- (binding << SYMBOL_FLAG_BINDING_SHIFT));
- sym->namelen = namelen - 1;
-
- pr_debug4("%s: %s %#" PRIx64 "-%#" PRIx64 "\n",
- __func__, name, start, sym->end);
- memcpy(sym->name, name, namelen);
+ RC_STRUCT(symbol) *sym;
+ struct symbol *res;
+
+ sym = calloc(1, sizeof(*sym) + namelen);
+
+ if (ADD_RC_CHK(res, sym)) {
+ refcount_set(&sym->refcnt, 1);
+ sym->start = start;
+ sym->end = len ? start + len : start;
+ atomic_init(&sym->flags, (type << SYMBOL_FLAG_TYPE_SHIFT) |
+ (binding << SYMBOL_FLAG_BINDING_SHIFT));
+ sym->namelen = namelen - 1;
+
+ pr_debug4("%s: %s %#" PRIx64 "-%#" PRIx64 "\n",
+ __func__, name, start, sym->end);
+ memcpy(sym->name, name, namelen);
+ } else {
+ free(sym);
+ }
- return sym;
+ return res;
}
void symbol__delete(struct symbol *sym)
{
+ if (!sym)
+ return;
if (symbol__annotated(sym))
symbol__annotation_delete(sym);
- free(sym);
+ RC_CHK_FREE(sym);
+}
+
+void symbol__put(struct symbol *sym)
+{
+ if (sym && refcount_dec_and_test(symbol__refcnt(sym)))
+ symbol__delete(sym);
+ else
+ RC_CHK_PUT(sym);
}
void symbol__set_ignore(struct symbol *sym, bool ignore)
{
if (ignore)
- atomic_fetch_or(&sym->flags, SYMBOL_FLAG_IGNORE);
+ atomic_fetch_or(&RC_CHK_ACCESS(sym)->flags, SYMBOL_FLAG_IGNORE);
else
- atomic_fetch_and(&sym->flags, ~SYMBOL_FLAG_IGNORE);
+ atomic_fetch_and(&RC_CHK_ACCESS(sym)->flags, ~SYMBOL_FLAG_IGNORE);
}
void symbol__set_annotate2(struct symbol *sym, bool annotate2)
{
if (annotate2)
- atomic_fetch_or(&sym->flags, SYMBOL_FLAG_ANNOTATE2);
+ atomic_fetch_or(&RC_CHK_ACCESS(sym)->flags, SYMBOL_FLAG_ANNOTATE2);
else
- atomic_fetch_and(&sym->flags, ~SYMBOL_FLAG_ANNOTATE2);
+ atomic_fetch_and(&RC_CHK_ACCESS(sym)->flags, ~SYMBOL_FLAG_ANNOTATE2);
}
void symbol__set_inlined(struct symbol *sym, bool inlined)
{
if (inlined)
- atomic_fetch_or(&sym->flags, SYMBOL_FLAG_INLINED);
+ atomic_fetch_or(&RC_CHK_ACCESS(sym)->flags, SYMBOL_FLAG_INLINED);
else
- atomic_fetch_and(&sym->flags, ~SYMBOL_FLAG_INLINED);
+ atomic_fetch_and(&RC_CHK_ACCESS(sym)->flags, ~SYMBOL_FLAG_INLINED);
}
void symbol__set_ifunc_alias(struct symbol *sym, bool ifunc_alias)
{
if (ifunc_alias)
- atomic_fetch_or(&sym->flags, SYMBOL_FLAG_IFUNC_ALIAS);
+ atomic_fetch_or(&RC_CHK_ACCESS(sym)->flags, SYMBOL_FLAG_IFUNC_ALIAS);
else
- atomic_fetch_and(&sym->flags, ~SYMBOL_FLAG_IFUNC_ALIAS);
+ atomic_fetch_and(&RC_CHK_ACCESS(sym)->flags, ~SYMBOL_FLAG_IFUNC_ALIAS);
}
void symbol__set_annotated(struct symbol *sym, bool annotated)
{
if (annotated)
- atomic_fetch_or(&sym->flags, SYMBOL_FLAG_ANNOTATED);
+ atomic_fetch_or(&RC_CHK_ACCESS(sym)->flags, SYMBOL_FLAG_ANNOTATED);
else
- atomic_fetch_and(&sym->flags, ~SYMBOL_FLAG_ANNOTATED);
+ atomic_fetch_and(&RC_CHK_ACCESS(sym)->flags, ~SYMBOL_FLAG_ANNOTATED);
}
static void symbol__set_idle(struct symbol *sym, bool idle)
{
- uint16_t old_flags = atomic_load_explicit(&sym->flags, memory_order_relaxed);
+ uint16_t old_flags = atomic_load_explicit(&RC_CHK_ACCESS(sym)->flags, memory_order_relaxed);
uint16_t new_flags;
uint16_t idle_val = idle ? SYMBOL_IDLE__IDLE : SYMBOL_IDLE__NOT_IDLE;
do {
new_flags = old_flags & ~SYMBOL_FLAG_IDLE_MASK;
new_flags |= (idle_val << SYMBOL_FLAG_IDLE_SHIFT);
- } while (!atomic_compare_exchange_weak(&sym->flags, &old_flags, new_flags));
+ } while (!atomic_compare_exchange_weak(&RC_CHK_ACCESS(sym)->flags, &old_flags, new_flags));
}
static int __symbols__insert(struct symbols *symbols, struct symbol *sym)
@@ -506,7 +528,7 @@ static int __symbols__insert(struct symbols *symbols, struct symbol *sym)
sizeof(struct symbol *) * to_allocate);
if (!temp) {
- symbol__delete(sym);
+ symbol__put(sym);
return -ENOMEM;
}
symbols->symbols = temp;
@@ -561,7 +583,7 @@ static struct symbol *symbols__find(struct symbols *symbols, u64 ip)
else if (ip > end || (ip == end && ip != start))
low = mid + 1;
else {
- res = s;
+ res = symbol__get(s);
break;
}
}
@@ -685,7 +707,7 @@ static struct symbol *symbols__find_by_name(struct symbol *symbols[],
}
}
assert(!found_idx || !s || s == symbols[*found_idx]);
- return s;
+ return symbol__get(s);
}
int dso__insert_symbol(struct dso *dso, struct symbol *sym)
@@ -699,14 +721,16 @@ void dso__delete_symbol(struct dso *dso, struct symbol *sym)
down_write(&symbols->lock);
for (unsigned int i = 0; i < symbols->cnt; i++) {
- if (symbols->symbols[i] == sym) {
+ if (RC_CHK_EQUAL(symbols->symbols[i], sym)) {
+ struct symbol *old = symbols->symbols[i];
+
memmove(&symbols->symbols[i], &symbols->symbols[i + 1],
(symbols->cnt - i - 1) * sizeof(struct symbol *));
symbols->cnt--;
zfree(&symbols->symbols_by_name);
symbols->num_by_name = 0;
symbols->sorted_by_name = false;
- symbol__delete(sym);
+ symbol__put(old);
break;
}
}
@@ -731,7 +755,7 @@ struct symbol *dso__next_symbol_by_name(struct dso *dso, size_t *idx)
down_read(&symbols->lock);
if (*idx + 1 < symbols->num_by_name) {
++*idx;
- res = symbols->symbols_by_name[*idx];
+ res = symbol__get(symbols->symbols_by_name[*idx]);
}
up_read(&symbols->lock);
@@ -929,7 +953,8 @@ bool symbol__is_idle(struct symbol *sym, const struct dso *dso, struct perf_env
uint16_t e_machine;
{
- uint16_t flags = atomic_load_explicit(&sym->flags, memory_order_relaxed);
+ uint16_t flags = atomic_load_explicit(&RC_CHK_ACCESS(sym)->flags,
+ memory_order_relaxed);
uint16_t idle_val = (flags & SYMBOL_FLAG_IDLE_MASK) >> SYMBOL_FLAG_IDLE_SHIFT;
if (idle_val != SYMBOL_IDLE__UNKNOWN)
@@ -1077,7 +1102,7 @@ static int maps__split_kallsyms_for_kcore(struct maps *kmaps, struct dso *dso)
curr_map = maps__find(kmaps, symbol__start(pos));
if (!curr_map) {
- symbol__delete(pos);
+ symbol__put(pos);
continue;
}
curr_map_dso = map__dso(curr_map);
@@ -1273,7 +1298,7 @@ static int maps__split_kallsyms(struct maps *kmaps, struct dso *dso, u64 delta,
continue;
discard_symbol:
- symbol__delete(pos);
+ symbol__put(pos);
}
down_write(&root->lock);
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index eed4d388d1fc..b4e5fdf5c4d6 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -13,6 +13,7 @@
#include <linux/string.h>
#include <stdio.h>
#include <errno.h>
+#include <internal/rc_check.h>
#include "addr_location.h"
#include "path.h"
#include "rwsem.h"
@@ -85,10 +86,12 @@ enum symbol_idle_kind {
/**
* A symtab entry.
*/
-struct symbol {
+DECLARE_RC_STRUCT(symbol) {
/** Range of symbol [start, end). */
u64 start;
u64 end;
+ /** Reference count. */
+ refcount_t refcnt;
/** Length of the string name. */
u16 namelen;
_Atomic uint16_t flags;
@@ -135,86 +138,111 @@ void symbols__exit(struct symbols *symbols);
void symbols__delete(struct symbols *symbols);
void symbols__sort_read_lock(struct symbols *symbols) SHARED_LOCK_FUNCTION(symbols->lock);
+static inline refcount_t *symbol__refcnt(struct symbol *sym)
+{
+ return &RC_CHK_ACCESS(sym)->refcnt;
+}
+
+static inline struct symbol *symbol__get(struct symbol *sym)
+{
+ struct symbol *result;
+
+ if (RC_CHK_GET(result, sym))
+ refcount_inc(symbol__refcnt(sym));
+
+ return result;
+}
+
+void symbol__put(struct symbol *sym);
+
+static inline void __symbol__zput(struct symbol **sym)
+{
+ symbol__put(*sym);
+ *sym = NULL;
+}
+
+#define symbol__zput(sym) __symbol__zput(&sym)
+
static inline __attribute__((nonnull)) u64 symbol__start(const struct symbol *sym)
{
- return sym->start;
+ return RC_CHK_ACCESS(sym)->start;
}
static inline __attribute__((nonnull)) void symbol__set_start(struct symbol *sym, u64 start)
{
- sym->start = start;
+ RC_CHK_ACCESS(sym)->start = start;
}
static inline __attribute__((nonnull)) u64 symbol__end(const struct symbol *sym)
{
- return sym->end;
+ return RC_CHK_ACCESS(sym)->end;
}
static inline __attribute__((nonnull)) void symbol__set_end(struct symbol *sym, u64 end)
{
- sym->end = end;
+ RC_CHK_ACCESS(sym)->end = end;
}
static inline __attribute__((nonnull, returns_nonnull))
const char *symbol__name(const struct symbol *sym)
{
- return sym->name;
+ return RC_CHK_ACCESS(sym)->name;
}
static inline __attribute__((nonnull)) u16 symbol__namelen(const struct symbol *sym)
{
- return sym->namelen;
+ return RC_CHK_ACCESS(sym)->namelen;
}
static inline __attribute__((nonnull)) u8 symbol__arch_sym(const struct symbol *sym)
{
- return sym->arch_sym;
+ return RC_CHK_ACCESS(sym)->arch_sym;
}
static inline __attribute__((nonnull)) void symbol__set_arch_sym(struct symbol *sym, u8 arch_sym)
{
- sym->arch_sym = arch_sym;
+ RC_CHK_ACCESS(sym)->arch_sym = arch_sym;
}
static inline u8 symbol__type(const struct symbol *sym)
{
- return (atomic_load_explicit(&sym->flags, memory_order_relaxed) &
+ return (atomic_load_explicit(&RC_CHK_ACCESS(sym)->flags, memory_order_relaxed) &
SYMBOL_FLAG_TYPE_MASK) >> SYMBOL_FLAG_TYPE_SHIFT;
}
static inline u8 symbol__binding(const struct symbol *sym)
{
- return (atomic_load_explicit(&sym->flags, memory_order_relaxed) &
+ return (atomic_load_explicit(&RC_CHK_ACCESS(sym)->flags, memory_order_relaxed) &
SYMBOL_FLAG_BINDING_MASK) >> SYMBOL_FLAG_BINDING_SHIFT;
}
static inline bool symbol__ignore(const struct symbol *sym)
{
- return (atomic_load_explicit(&sym->flags, memory_order_relaxed) &
+ return (atomic_load_explicit(&RC_CHK_ACCESS(sym)->flags, memory_order_relaxed) &
SYMBOL_FLAG_IGNORE) != 0;
}
static inline bool symbol__inlined(const struct symbol *sym)
{
- return (atomic_load_explicit(&sym->flags, memory_order_relaxed) &
+ return (atomic_load_explicit(&RC_CHK_ACCESS(sym)->flags, memory_order_relaxed) &
SYMBOL_FLAG_INLINED) != 0;
}
static inline bool symbol__is_annotate2(const struct symbol *sym)
{
- return (atomic_load_explicit(&sym->flags, memory_order_relaxed) &
+ return (atomic_load_explicit(&RC_CHK_ACCESS(sym)->flags, memory_order_relaxed) &
SYMBOL_FLAG_ANNOTATE2) != 0;
}
static inline bool symbol__ifunc_alias(const struct symbol *sym)
{
- return (atomic_load_explicit(&sym->flags, memory_order_relaxed) &
+ return (atomic_load_explicit(&RC_CHK_ACCESS(sym)->flags, memory_order_relaxed) &
SYMBOL_FLAG_IFUNC_ALIAS) != 0;
}
static inline bool symbol__annotated(const struct symbol *sym)
{
- return (atomic_load_explicit(&sym->flags, memory_order_relaxed) &
+ return (atomic_load_explicit(&RC_CHK_ACCESS(sym)->flags, memory_order_relaxed) &
SYMBOL_FLAG_ANNOTATED) != 0;
}
diff --git a/tools/perf/util/thread-stack.c b/tools/perf/util/thread-stack.c
index 76867ba83365..e2c73e890158 100644
--- a/tools/perf/util/thread-stack.c
+++ b/tools/perf/util/thread-stack.c
@@ -823,7 +823,7 @@ static int thread_stack__pop_cp(struct thread *thread, struct thread_stack *ts,
if (ts->cnt == 1) {
struct thread_stack_entry *tse = &ts->stack[0];
- if (tse->cp->sym == sym)
+ if (RC_CHK_EQUAL(tse->cp->sym, sym))
return thread_stack__call_return(thread, ts, --ts->cnt,
timestamp, ref, false);
}
@@ -941,7 +941,7 @@ static int thread_stack__no_call_return(struct thread *thread,
else
parent = root;
- if (parent->sym == from_al->sym) {
+ if (RC_CHK_EQUAL(parent->sym, from_al->sym)) {
/*
* At the bottom of the stack, assume the missing 'call' was
* before the trace started. So, pop the current symbol and push
@@ -1069,7 +1069,7 @@ static int thread_stack__x86_retpoline(struct thread_stack *ts,
*/
ts->cnt -= 1;
sym = ts->stack[ts->cnt - 2].cp->sym;
- if (sym && sym == tsym && to_al->addr != symbol__start(tsym)) {
+ if (sym && RC_CHK_EQUAL(sym, tsym) && to_al->addr != symbol__start(tsym)) {
/*
* Target is back to the middle of the symbol we came
* from so assume it is an indirect jmp and forget it
@@ -1078,7 +1078,7 @@ static int thread_stack__x86_retpoline(struct thread_stack *ts,
ts->cnt -= 1;
return 0;
}
- } else if (sym && sym == tsym) {
+ } else if (sym && RC_CHK_EQUAL(sym, tsym)) {
/*
* Target is back to the symbol we came from so assume it is an
* indirect jmp and forget it altogether.
@@ -1169,7 +1169,7 @@ int thread_stack__process(struct thread *thread, struct comm *comm,
* may be the start of a x86 retpoline.
*/
if (!err && rstate == X86_RETPOLINE_POSSIBLE && to_al->sym &&
- from_al->sym == to_al->sym &&
+ RC_CHK_EQUAL(from_al->sym, to_al->sym) &&
to_al->addr != symbol__start(to_al->sym))
ts->rstate = X86_RETPOLINE_DETECTED;
@@ -1206,7 +1206,7 @@ int thread_stack__process(struct thread *thread, struct comm *comm,
} else if (sample->flags & PERF_IP_FLAG_TRACE_END) {
err = thread_stack__trace_end(ts, sample, ref);
} else if (sample->flags & PERF_IP_FLAG_BRANCH &&
- from_al->sym != to_al->sym && to_al->sym &&
+ !RC_CHK_EQUAL(from_al->sym, to_al->sym) && to_al->sym &&
to_al->addr == symbol__start(to_al->sym)) {
struct call_path_root *cpr = ts->crp->cpr;
struct call_path *cp;
diff --git a/tools/perf/util/threads.c b/tools/perf/util/threads.c
index 6ca0b178fb6c..b3ec954fe7ab 100644
--- a/tools/perf/util/threads.c
+++ b/tools/perf/util/threads.c
@@ -142,10 +142,13 @@ void threads__remove_all_threads(struct threads *threads)
down_write(&table->lock);
__threads_table_entry__set_last_match(table, NULL);
hashmap__for_each_entry_safe(&table->shard, cur, tmp, bkt) {
- struct thread *old_value;
+ struct thread *old_value = NULL;
- hashmap__delete(&table->shard, cur->key, /*old_key=*/NULL, &old_value);
- thread__put(old_value);
+ if (hashmap__delete(&table->shard, cur->key,
+ /*old_key=*/NULL, &old_value)) {
+ thread__free_stitch_list(old_value);
+ thread__put(old_value);
+ }
}
up_write(&table->lock);
}
@@ -154,14 +157,16 @@ void threads__remove_all_threads(struct threads *threads)
void threads__remove(struct threads *threads, struct thread *thread)
{
struct threads_table_entry *table = threads__table(threads, thread__tid(thread));
- struct thread *old_value;
+ struct thread *old_value = NULL;
down_write(&table->lock);
if (table->last_match && RC_CHK_EQUAL(table->last_match, thread))
__threads_table_entry__set_last_match(table, NULL);
- hashmap__delete(&table->shard, thread__tid(thread), /*old_key=*/NULL, &old_value);
- thread__put(old_value);
+ if (hashmap__delete(&table->shard, thread__tid(thread), /*old_key=*/NULL, &old_value)) {
+ thread__free_stitch_list(old_value);
+ thread__put(old_value);
+ }
up_write(&table->lock);
}
diff --git a/tools/perf/util/unwind-libdw.c b/tools/perf/util/unwind-libdw.c
index 87ade098b5c6..eb4327aa9e7e 100644
--- a/tools/perf/util/unwind-libdw.c
+++ b/tools/perf/util/unwind-libdw.c
@@ -169,7 +169,7 @@ static int entry(u64 ip, struct unwind_info *ui)
e->ip = ip;
e->ms.thread = thread__get(al.thread);
e->ms.map = map__get(al.map);
- e->ms.sym = al.sym;
+ e->ms.sym = symbol__get(al.sym);
pr_debug("unwind: %s:ip = 0x%" PRIx64 " (0x%" PRIx64 ")\n",
al.sym ? symbol__name(al.sym) : "''",
diff --git a/tools/perf/util/unwind-libunwind.c b/tools/perf/util/unwind-libunwind.c
index 6052383e862b..42872c7b6dcd 100644
--- a/tools/perf/util/unwind-libunwind.c
+++ b/tools/perf/util/unwind-libunwind.c
@@ -632,7 +632,7 @@ static int entry(uint64_t ip, struct thread *thread, unwind_entry_cb_t cb, void
e.ms.thread = thread__get(al.thread);
pr_debug("unwind: %s:ip = 0x%" PRIx64 " (0x%" PRIx64 ")\n",
- al.sym ? al.sym->name : "''",
+ al.sym ? symbol__name(al.sym) : "''",
ip,
al.map ? map__map_ip(al.map, ip) : (u64) 0);
--
2.56.0.313.g2479322e68-goog
^ permalink raw reply [flat|nested] 36+ messages in thread
* [PATCH v2 09/18] perf symbol: Optimize symbol sorting with natural-run merge sort and binary insertion
2026-10-27 23:14 ` [PATCH v2 00/18] perf symbol: Reference counting, sorted arrays, and memory shrinking Ian Rogers
` (7 preceding siblings ...)
2026-10-27 23:15 ` [PATCH v2 08/18] perf symbol: Add reference counting and DECLARE_RC_STRUCT(symbol) Ian Rogers
@ 2026-10-27 23:15 ` Ian Rogers
2026-10-27 23:15 ` [PATCH v2 10/18] perf maps: Optimize tail insertion and small-append sorting Ian Rogers
` (8 subsequent siblings)
17 siblings, 0 replies; 36+ messages in thread
From: Ian Rogers @ 2026-10-27 23:15 UTC (permalink / raw)
To: irogers, acme, namhyung
Cc: adrian.hunter, haghdoost, james.clark, jolsa, linux-kernel,
linux-perf-users, mingo, peterz
ELF .symtab and .dynsym sections are typically already mostly ordered by
address in ascending runs, and dynamic symbol resolution
(dso__insert_symbol()) frequently inserts one or a few symbols into an
already-sorted DSO symbol table. Invoking full-array qsort() on every
sort after incremental insertions or across pre-ordered ELF runs spends
avoidable CPU time in symbol comparisons.
Speed up symbol array sorting and out-of-order symbol insertions:
- Track the sorted prefix length (@sorted_cnt) in struct symbols and
replace full-array qsort() with a natural-run merge sort
(symbols__sort_range_with_tmp()) and galloping binary-search merge
(symbols__merge_runs()), plus an O(N) in-place binary insertion fast
path when <= 8 symbols are appended.
- Insert directly into sorted position in dso__insert_symbol() when
symbols->sorted is true so dynamic single-symbol insertions maintain
the sorted invariant without triggering full re-sorts.
Using 'perf bench internals symbols':
Before:
Average DSO load + sort: 6.242 msec (+- 0.057 msec)
Average bulk insert + lookup: 1404.150 usec (+- 18.752 usec)
Average append + lookup: 40429.750 usec (+- 507.578 usec)
After (-7.0% load, -13.9% bulk, -99.6% append):
Average DSO load + sort: 5.808 msec (+- 0.047 msec)
Average bulk insert + lookup: 1209.250 usec (+- 18.446 usec)
Average append + lookup: 152.900 usec (+- 0.736 usec)
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/symbol.c | 434 ++++++++++++++++++++++++++++++++++++---
tools/perf/util/symbol.h | 5 +
2 files changed, 405 insertions(+), 34 deletions(-)
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index b2c0a606b13a..7f1fcd5c64b2 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -220,6 +220,7 @@ void symbols__init(struct symbols *symbols)
symbols->symbols = NULL;
symbols->symbols_by_name = NULL;
symbols->cnt = 0;
+ symbols->sorted_cnt = 0;
symbols->allocated = 0;
symbols->num_by_name = 0;
symbols->sorted = true;
@@ -234,6 +235,7 @@ static void symbols__delete_locked(struct symbols *symbols)
zfree(&symbols->symbols);
zfree(&symbols->symbols_by_name);
symbols->cnt = 0;
+ symbols->sorted_cnt = 0;
symbols->allocated = 0;
symbols->num_by_name = 0;
symbols->sorted = true;
@@ -266,27 +268,312 @@ static inline bool symbol__less_or_equal(const struct symbol *a, const struct sy
return symbol__end(a) <= symbol__end(b);
}
+static unsigned int symbols__upper_bound(struct symbol **arr, unsigned int len,
+ const struct symbol *sym)
+{
+ unsigned int low = 0, high = len;
+
+ while (low < high) {
+ unsigned int mid = low + (high - low) / 2;
+
+ if (symbol__less_or_equal(arr[mid], sym))
+ low = mid + 1;
+ else
+ high = mid;
+ }
+ return low;
+}
+
+static unsigned int symbols__lower_bound(struct symbol **arr, unsigned int len,
+ const struct symbol *sym)
+{
+ unsigned int low = 0, high = len;
+
+ while (low < high) {
+ unsigned int mid = low + (high - low) / 2;
+
+ if (!symbol__less_or_equal(sym, arr[mid]))
+ low = mid + 1;
+ else
+ high = mid;
+ }
+ return low;
+}
+
+static void symbols__insertion_sort_range(struct symbol **arr,
+ unsigned int start,
+ unsigned int end)
+{
+ for (unsigned int i = start + 1; i < end; i++) {
+ struct symbol *sym = arr[i];
+
+ if (!symbol__less_or_equal(arr[i - 1], sym)) {
+ unsigned int pos = start +
+ symbols__upper_bound(&arr[start], i - start, sym);
+
+ memmove(&arr[pos + 1], &arr[pos],
+ (i - pos) * sizeof(struct symbol *));
+ arr[pos] = sym;
+ }
+ }
+}
+
+static void symbols__merge_runs(struct symbol **arr,
+ unsigned int left,
+ unsigned int mid,
+ unsigned int right,
+ struct symbol **tmp)
+{
+ unsigned int first_left, last_left, left_len, right_len;
+ unsigned int i, j, dst;
+
+ if (left >= mid || mid >= right)
+ return;
+ if (symbol__less_or_equal(arr[mid - 1], arr[mid]))
+ return;
+
+ first_left = left + symbols__upper_bound(&arr[left], mid - left, arr[mid]);
+ right_len = right - mid;
+
+ if (mid - first_left <= right_len) {
+ left_len = mid - first_left;
+ memcpy(tmp, &arr[first_left], left_len * sizeof(struct symbol *));
+ i = 0;
+ j = mid;
+ dst = first_left;
+ while (i < left_len && j < right) {
+ if (symbol__less_or_equal(tmp[i], arr[j])) {
+ arr[dst++] = tmp[i++];
+ } else {
+ arr[dst++] = arr[j++];
+ if (right - j >= 16 &&
+ (right - j) > 8 * (left_len - i) &&
+ !symbol__less_or_equal(tmp[i], arr[j])) {
+ unsigned int step =
+ symbols__lower_bound(&arr[j], right - j, tmp[i]);
+
+ if (step > 0) {
+ memmove(&arr[dst], &arr[j],
+ step * sizeof(struct symbol *));
+ dst += step;
+ j += step;
+ }
+ }
+ }
+ }
+ if (i < left_len)
+ memcpy(&arr[dst], &tmp[i], (left_len - i) * sizeof(struct symbol *));
+ return;
+ }
+
+ last_left = first_left +
+ symbols__upper_bound(&arr[first_left], mid - first_left, arr[right - 1]);
+ left_len = last_left - first_left;
+
+ memcpy(tmp, &arr[mid], right_len * sizeof(struct symbol *));
+ if (last_left < mid) {
+ memmove(&arr[last_left + right_len], &arr[last_left],
+ (mid - last_left) * sizeof(struct symbol *));
+ }
+
+ if (first_left == last_left) {
+ memcpy(&arr[first_left], tmp, right_len * sizeof(struct symbol *));
+ return;
+ }
+
+ i = last_left;
+ j = right_len;
+ dst = last_left + right_len;
+ while (i > first_left && j > 0) {
+ if (symbol__less_or_equal(arr[i - 1], tmp[j - 1])) {
+ arr[--dst] = tmp[--j];
+ } else {
+ arr[--dst] = arr[--i];
+ if (i - first_left >= 16 &&
+ (i - first_left) > 8 * j &&
+ !symbol__less_or_equal(arr[i - 1], tmp[j - 1])) {
+ unsigned int pos = first_left +
+ symbols__upper_bound(&arr[first_left],
+ i - first_left,
+ tmp[j - 1]);
+ unsigned int step = i - pos;
+
+ if (step > 0) {
+ dst -= step;
+ i = pos;
+ memmove(&arr[dst], &arr[pos],
+ step * sizeof(struct symbol *));
+ }
+ }
+ }
+ }
+ if (j > 0)
+ memcpy(&arr[first_left], tmp, j * sizeof(struct symbol *));
+}
+
+struct symbol_run {
+ unsigned int start;
+ unsigned int len;
+};
+
+static void symbols__sort_range_with_tmp(struct symbol **arr, unsigned int n,
+ struct symbol **tmp)
+{
+ struct symbol_run runs[64];
+ unsigned int nr_runs = 0;
+ unsigned int i = 0;
+
+ if (n <= 32) {
+ symbols__insertion_sort_range(arr, 0, n);
+ return;
+ }
+
+ while (i < n) {
+ unsigned int start = i++;
+
+ if (i < n) {
+ if (symbol__less_or_equal(arr[i - 1], arr[i])) {
+ while (i < n && symbol__less_or_equal(arr[i - 1], arr[i]))
+ i++;
+ } else {
+ while (i < n && !symbol__less_or_equal(arr[i - 1], arr[i]))
+ i++;
+ for (unsigned int lo = start, hi = i - 1; lo < hi; lo++, hi--) {
+ struct symbol *swap = arr[lo];
+
+ arr[lo] = arr[hi];
+ arr[hi] = swap;
+ }
+ }
+ }
+
+ if (i - start < 32 && i < n) {
+ i = min(start + 32U, n);
+ symbols__insertion_sort_range(arr, start, i);
+ }
+
+ runs[nr_runs++] = (struct symbol_run){
+ .start = start,
+ .len = i - start,
+ };
+
+ while (nr_runs > 1) {
+ unsigned int r = nr_runs - 2;
+
+ if (nr_runs >= 3 &&
+ runs[nr_runs - 3].len <=
+ runs[nr_runs - 2].len + runs[nr_runs - 1].len) {
+ if (runs[nr_runs - 3].len < runs[nr_runs - 1].len)
+ r = nr_runs - 3;
+ } else if (runs[nr_runs - 2].len > runs[nr_runs - 1].len &&
+ nr_runs < ARRAY_SIZE(runs)) {
+ break;
+ }
+
+ symbols__merge_runs(arr, runs[r].start,
+ runs[r + 1].start,
+ runs[r + 1].start + runs[r + 1].len,
+ tmp);
+ runs[r].len += runs[r + 1].len;
+ if (r + 2 < nr_runs)
+ runs[r + 1] = runs[r + 2];
+ nr_runs--;
+ }
+ }
+
+ while (nr_runs > 1) {
+ unsigned int r = nr_runs - 2;
+
+ if (nr_runs >= 3 && runs[nr_runs - 3].len < runs[nr_runs - 1].len)
+ r = nr_runs - 3;
+ symbols__merge_runs(arr, runs[r].start,
+ runs[r + 1].start,
+ runs[r + 1].start + runs[r + 1].len,
+ tmp);
+ runs[r].len += runs[r + 1].len;
+ if (r + 2 < nr_runs)
+ runs[r + 1] = runs[r + 2];
+ nr_runs--;
+ }
+}
+
static int symbols__sort_cmp(const void *vlhs, const void *vrhs)
{
- const struct symbol *lhs = *((const struct symbol **)vlhs);
- const struct symbol *rhs = *((const struct symbol **)vrhs);
+ const struct symbol *l = *((const struct symbol **)vlhs);
+ const struct symbol *r = *((const struct symbol **)vrhs);
- if (symbol__start(lhs) != symbol__start(rhs))
- return symbol__start(lhs) < symbol__start(rhs) ? -1 : 1;
- if (symbol__end(lhs) != symbol__end(rhs))
- return symbol__end(lhs) < symbol__end(rhs) ? -1 : 1;
+ if (symbol__start(l) != symbol__start(r))
+ return symbol__start(l) < symbol__start(r) ? -1 : 1;
+ if (symbol__end(l) != symbol__end(r))
+ return symbol__end(l) < symbol__end(r) ? -1 : 1;
return 0;
}
static void symbols__sort_locked(struct symbols *symbols)
{
- if (symbols->sorted)
+ unsigned int m = symbols->sorted_cnt;
+ unsigned int k;
+
+ if (symbols->sorted) {
+ symbols->sorted_cnt = symbols->cnt;
return;
+ }
+
+ if (m > symbols->cnt)
+ m = 0;
+ k = symbols->cnt - m;
- if (symbols->cnt > 1) {
- qsort(symbols->symbols, symbols->cnt, sizeof(struct symbol *),
- symbols__sort_cmp);
+ if (k == 0) {
+ symbols->sorted = true;
+ return;
}
+
+ /*
+ * When only a few symbols have been appended since the last sort
+ * (including single-symbol insertions), insert them directly at
+ * their binary-searched sorted positions in O(N) without allocating.
+ */
+ if (k <= 8 || symbols->cnt <= 64) {
+ for (unsigned int i = max(m, 1U); i < symbols->cnt; i++) {
+ struct symbol *sym = symbols->symbols[i];
+
+ if (!symbol__less_or_equal(symbols->symbols[i - 1], sym)) {
+ unsigned int pos = symbols__upper_bound(symbols->symbols, i, sym);
+
+ memmove(&symbols->symbols[pos + 1], &symbols->symbols[pos],
+ (i - pos) * sizeof(struct symbol *));
+ symbols->symbols[pos] = sym;
+ }
+ }
+ symbols->sorted_cnt = symbols->cnt;
+ symbols->sorted = true;
+ return;
+ }
+
+ if (m <= 1) {
+ struct symbol **tmp = malloc(((symbols->cnt + 1) / 2) * sizeof(struct symbol *));
+
+ if (tmp) {
+ symbols__sort_range_with_tmp(symbols->symbols, symbols->cnt, tmp);
+ free(tmp);
+ } else {
+ qsort(symbols->symbols, symbols->cnt, sizeof(struct symbol *),
+ symbols__sort_cmp);
+ }
+ } else {
+ struct symbol **tmp = malloc(k * sizeof(struct symbol *));
+
+ if (tmp) {
+ symbols__sort_range_with_tmp(&symbols->symbols[m], k, tmp);
+ symbols__merge_runs(symbols->symbols, 0, m, symbols->cnt, tmp);
+ free(tmp);
+ } else {
+ qsort(symbols->symbols, symbols->cnt, sizeof(struct symbol *),
+ symbols__sort_cmp);
+ }
+ }
+
+ symbols->sorted_cnt = symbols->cnt;
symbols->sorted = true;
}
@@ -347,6 +634,7 @@ void symbols__fixup_duplicate(struct symbols *symbols)
}
}
symbols->cnt = write_idx + 1;
+ symbols->sorted_cnt = symbols->cnt;
zfree(&symbols->symbols_by_name);
symbols->num_by_name = 0;
symbols->sorted_by_name = false;
@@ -388,40 +676,62 @@ void symbols__fixup_end(struct symbols *symbols, bool is_kallsyms)
* like in:
* ffffffffc1937000 T hdmi_driver_init [snd_hda_codec_hdmi]
*/
- if (symbol__end(prev) == symbol__start(prev)) {
+ if (symbol__end(prev) == symbol__start(prev) &&
+ symbol__start(prev) != symbol__start(curr)) {
const char *prev_mod;
const char *curr_mod;
if (!is_kallsyms) {
symbol__set_end(prev, symbol__start(curr));
- continue;
- }
+ } else {
+ prev_mod = strchr(symbol__name(prev), '[');
+ curr_mod = strchr(symbol__name(curr), '[');
+
+ /* Last kernel/module symbol mapped to end of page */
+ if (!prev_mod != !curr_mod) {
+ symbol__set_end(prev,
+ roundup(symbol__end(prev) + 4096, 4096));
+ /* Last symbol in the previous module */
+ } else if (prev_mod && strcmp(prev_mod, curr_mod)) {
+ symbol__set_end(prev,
+ roundup(symbol__end(prev) + 4096, 4096));
+ } else {
+ symbol__set_end(prev, symbol__start(curr));
+ }
- prev_mod = strchr(symbol__name(prev), '[');
- curr_mod = strchr(symbol__name(curr), '[');
+ /* The next module can start within that page */
+ if (symbol__end(prev) > symbol__start(curr))
+ symbol__set_end(prev, symbol__start(curr));
- /* Last kernel/module symbol mapped to end of page */
- if (!prev_mod != !curr_mod)
- symbol__set_end(prev, roundup(symbol__end(prev) + 4096, 4096));
- /* Last symbol in the previous module */
- else if (prev_mod && strcmp(prev_mod, curr_mod))
- symbol__set_end(prev, roundup(symbol__end(prev) + 4096, 4096));
- else
- symbol__set_end(prev, symbol__start(curr));
+ pr_debug4("%s sym:%s end:%#" PRIx64 "\n",
+ __func__, symbol__name(prev), symbol__end(prev));
+ }
- /* The next module can start within that page */
- if (symbol__end(prev) > symbol__start(curr))
- symbol__set_end(prev, symbol__start(curr));
+ for (unsigned int k = i - 1; k > 0; k--) {
+ struct symbol *same = symbols->symbols[k - 1];
- pr_debug4("%s sym:%s end:%#" PRIx64 "\n",
- __func__, symbol__name(prev), symbol__end(prev));
+ if (symbol__start(same) != symbol__start(prev) ||
+ symbol__end(same) != symbol__start(same))
+ break;
+ symbol__set_end(same, symbol__end(prev));
+ }
}
}
/* Last entry */
- if (symbol__end(curr) == symbol__start(curr))
+ if (symbol__end(curr) == symbol__start(curr)) {
symbol__set_end(curr, roundup(symbol__start(curr), 4096) + 4096);
+ for (unsigned int k = symbols->cnt - 1; k > 0; k--) {
+ struct symbol *same = symbols->symbols[k - 1];
+
+ if (symbol__start(same) != symbol__start(curr) ||
+ symbol__end(same) != symbol__start(same))
+ break;
+ symbol__set_end(same, symbol__end(curr));
+ }
+ }
+
up_write(&symbols->lock);
}
@@ -535,9 +845,13 @@ static int __symbols__insert(struct symbols *symbols, struct symbol *sym)
symbols->allocated = to_allocate;
}
- if (symbols->sorted && symbols->cnt > 0 &&
- !symbol__less_or_equal(symbols->symbols[symbols->cnt - 1], sym))
- symbols->sorted = false;
+ if (symbols->sorted) {
+ if (symbols->cnt == 0 ||
+ symbol__less_or_equal(symbols->symbols[symbols->cnt - 1], sym))
+ symbols->sorted_cnt = symbols->cnt + 1;
+ else
+ symbols->sorted = false;
+ }
symbols->symbols[symbols->cnt++] = sym;
zfree(&symbols->symbols_by_name);
@@ -546,6 +860,44 @@ static int __symbols__insert(struct symbols *symbols, struct symbol *sym)
return 0;
}
+static int __symbols__insert_sorted(struct symbols *symbols, struct symbol *sym)
+{
+ unsigned int idx;
+
+ if (!symbols->sorted)
+ return __symbols__insert(symbols, sym);
+
+ if (symbols->cnt == symbols->allocated) {
+ unsigned int to_allocate = symbols->allocated ? symbols->allocated * 2 : 32;
+ struct symbol **temp = realloc(symbols->symbols,
+ sizeof(struct symbol *) * to_allocate);
+
+ if (!temp) {
+ symbol__put(sym);
+ return -ENOMEM;
+ }
+ symbols->symbols = temp;
+ symbols->allocated = to_allocate;
+ }
+
+ if (symbols->cnt == 0 ||
+ symbol__less_or_equal(symbols->symbols[symbols->cnt - 1], sym)) {
+ idx = symbols->cnt;
+ } else {
+ idx = symbols__upper_bound(symbols->symbols, symbols->cnt, sym);
+ memmove(&symbols->symbols[idx + 1], &symbols->symbols[idx],
+ (symbols->cnt - idx) * sizeof(struct symbol *));
+ }
+
+ symbols->symbols[idx] = sym;
+ symbols->cnt++;
+ symbols->sorted_cnt = symbols->cnt;
+ zfree(&symbols->symbols_by_name);
+ symbols->num_by_name = 0;
+ symbols->sorted_by_name = false;
+ return 0;
+}
+
int symbols__insert(struct symbols *symbols, struct symbol *sym)
{
int ret;
@@ -712,7 +1064,13 @@ static struct symbol *symbols__find_by_name(struct symbol *symbols[],
int dso__insert_symbol(struct dso *dso, struct symbol *sym)
{
- return symbols__insert(dso__symbols(dso), sym);
+ struct symbols *symbols = dso__symbols(dso);
+ int ret;
+
+ down_write(&symbols->lock);
+ ret = __symbols__insert_sorted(symbols, sym);
+ up_write(&symbols->lock);
+ return ret;
}
void dso__delete_symbol(struct dso *dso, struct symbol *sym)
@@ -720,13 +1078,16 @@ void dso__delete_symbol(struct dso *dso, struct symbol *sym)
struct symbols *symbols = dso__symbols(dso);
down_write(&symbols->lock);
- for (unsigned int i = 0; i < symbols->cnt; i++) {
+ for (unsigned int i = symbols->cnt; i-- > 0; ) {
if (RC_CHK_EQUAL(symbols->symbols[i], sym)) {
struct symbol *old = symbols->symbols[i];
memmove(&symbols->symbols[i], &symbols->symbols[i + 1],
(symbols->cnt - i - 1) * sizeof(struct symbol *));
symbols->cnt--;
+ if (i < symbols->sorted_cnt)
+ symbols->sorted_cnt--;
+ symbols->sorted = (symbols->sorted_cnt == symbols->cnt);
zfree(&symbols->symbols_by_name);
symbols->num_by_name = 0;
symbols->sorted_by_name = false;
@@ -1082,6 +1443,7 @@ static int maps__split_kallsyms_for_kcore(struct maps *kmaps, struct dso *dso)
old_cnt = root->cnt;
root->symbols = NULL;
root->cnt = 0;
+ root->sorted_cnt = 0;
root->allocated = 0;
root->sorted = true;
zfree(&root->symbols_by_name);
@@ -1159,6 +1521,7 @@ static int maps__split_kallsyms(struct maps *kmaps, struct dso *dso, u64 delta,
old_allocated = root->allocated;
root->symbols = NULL;
root->cnt = 0;
+ root->sorted_cnt = 0;
root->allocated = 0;
root->sorted = true;
zfree(&root->symbols_by_name);
@@ -1305,6 +1668,7 @@ static int maps__split_kallsyms(struct maps *kmaps, struct dso *dso, u64 delta,
if (root->cnt == 0) {
root->symbols = old_symbols;
root->cnt = write_idx;
+ root->sorted_cnt = write_idx;
root->allocated = old_allocated;
root->sorted = true;
old_symbols = NULL;
@@ -1334,12 +1698,14 @@ static int maps__split_kallsyms(struct maps *kmaps, struct dso *dso, u64 delta,
if (root->cnt == 0) {
root->symbols = old_symbols;
root->cnt = write_idx;
+ root->sorted_cnt = (delta == 0) ? write_idx : 0;
root->allocated = old_allocated;
root->sorted = (delta == 0);
old_symbols = NULL;
} else {
for (unsigned int j = 0; j < write_idx; j++)
__symbols__insert(root, old_symbols[j]);
+ root->sorted_cnt = 0;
root->sorted = false;
}
zfree(&root->symbols_by_name);
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index b4e5fdf5c4d6..066697d8da8f 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -119,6 +119,11 @@ struct symbols {
struct symbol **symbols_by_name;
/** @cnt: Number of valid entries in @symbols. */
unsigned int cnt;
+ /**
+ * @sorted_cnt: Number of leading entries in @symbols that are sorted
+ * by address range. Equal to @cnt when @sorted is true.
+ */
+ unsigned int sorted_cnt;
/** @allocated: Capacity (in entries) of the @symbols allocation. */
unsigned int allocated;
/** @num_by_name: Number of valid entries in @symbols_by_name. */
--
2.56.0.313.g2479322e68-goog
^ permalink raw reply [flat|nested] 36+ messages in thread
* [PATCH v2 10/18] perf maps: Optimize tail insertion and small-append sorting
2026-10-27 23:14 ` [PATCH v2 00/18] perf symbol: Reference counting, sorted arrays, and memory shrinking Ian Rogers
` (8 preceding siblings ...)
2026-10-27 23:15 ` [PATCH v2 09/18] perf symbol: Optimize symbol sorting with natural-run merge sort and binary insertion Ian Rogers
@ 2026-10-27 23:15 ` Ian Rogers
2026-10-27 23:15 ` [PATCH v2 11/18] perf symbol-elf: Cache ELF program/section headers and defer symbol name lookup Ian Rogers
` (7 subsequent siblings)
17 siblings, 0 replies; 36+ messages in thread
From: Ian Rogers @ 2026-10-27 23:15 UTC (permalink / raw)
To: irogers, acme, namhyung
Cc: adrian.hunter, haghdoost, james.clark, jolsa, linux-kernel,
linux-perf-users, mingo, peterz
User-space map insertions (from /proc/pid/maps synthesis, runtime
PERF_RECORD_MMAP2, and jitdump) and BPF ksymbol registrations all flow
through maps__fixup_overlap_and_insert() -> first_ending_after().
Currently first_ending_after() has a fast-path check for the first map
(maps_by_address[0]), but no fast-path check for the last map
(maps_by_address[high]). Because /proc/pid/maps entries are synthesized
in ascending address order, every synthesized mapping starts at or after
maps_by_address[high]'s end, yet currently executes the full O(log N)
binary search loop. Add a tail check in first_ending_after() so
non-overlapping ascending insertions complete in O(1).
Additionally, track the sorted prefix lengths (nr_maps_by_address_sorted
and nr_maps_by_name_sorted) in struct maps:
- In __maps__sort_by_address() and maps__sort_by_name(), when the
unsorted tail is small (k <= 8) or the total map count is small
(n <= 64), binary-insert the newly appended maps into the sorted
prefix instead of re-running qsort() over the entire array. This
avoids O(N^2 log N) re-sorting when maps__find_by_name() is called
repeatedly between single-map maps__insert() calls in
dso__process_kernel_symbol() and dso__split_kallsyms().
- In maps__by_address_index() and maps__by_name_index(), binary-search
the known-sorted prefix before scanning only the unsorted tail.
- Add a unit test in tools/perf/tests/maps.c covering the tail
fast-path and incremental binary-insertion sort.
Using 'perf bench internals maps':
Before:
Average ascending insert + lookup: 63.370 usec (+- 0.476 usec)
Average append + lookup: 7477.450 usec (+- 20.829 usec)
After (-6.9% ascending, -98.7% append):
Average ascending insert + lookup: 58.970 usec (+- 0.609 usec)
Average append + lookup: 96.670 usec (+- 1.353 usec)
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/tests/maps.c | 58 +++++++++
tools/perf/util/maps.c | 281 +++++++++++++++++++++++++++++++++-------
2 files changed, 289 insertions(+), 50 deletions(-)
diff --git a/tools/perf/tests/maps.c b/tools/perf/tests/maps.c
index 2c05d62f40dc..1618731b91d0 100644
--- a/tools/perf/tests/maps.c
+++ b/tools/perf/tests/maps.c
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
#include <inttypes.h>
+#include <internal/rc_check.h>
#include <linux/compiler.h>
#include <linux/kernel.h>
#include "tests.h"
@@ -233,9 +234,66 @@ static int test__maps__fixup_overlap_and_insert(struct test_suite *t __maybe_unu
return TEST_OK;
}
+static int test__maps__incremental_sort(struct test_suite *t __maybe_unused,
+ int subtest __maybe_unused)
+{
+ struct map_def ascending_maps[] = {
+ { "map_c", 1000, 2000 },
+ { "map_a", 2000, 3000 },
+ { "map_d", 4000, 5000 },
+ };
+ struct map_def out_of_order = { "map_b", 3000, 4000 };
+ struct map *map, *found;
+ struct maps *maps = maps__new(NULL);
+
+ TEST_ASSERT_VAL("failed to create maps", maps);
+
+ /* Exercise tail fast-path in maps__fixup_overlap_and_insert. */
+ for (unsigned int i = 0; i < ARRAY_SIZE(ascending_maps); i++) {
+ map = dso__new_map(ascending_maps[i].name);
+ TEST_ASSERT_VAL("failed to create map", map);
+ map__set_start(map, ascending_maps[i].start);
+ map__set_end(map, ascending_maps[i].end);
+ TEST_ASSERT_VAL("failed to insert sorted map",
+ maps__fixup_overlap_and_insert(maps, map) == 0);
+ map__put(map);
+ }
+
+ /* Populate maps_by_name and verify lookup. */
+ found = maps__find_by_name(maps, "map_a");
+ TEST_ASSERT_VAL("failed to find map_a by name", found && map__start(found) == 2000);
+ map__zput(found);
+
+ /* Append out-of-order map via maps__insert and verify binary-insertion sort. */
+ map = dso__new_map(out_of_order.name);
+ TEST_ASSERT_VAL("failed to create map_b", map);
+ map__set_start(map, out_of_order.start);
+ map__set_end(map, out_of_order.end);
+ TEST_ASSERT_VAL("failed to insert map_b", maps__insert(maps, map) == 0);
+
+ found = maps__find(maps, 3500);
+ TEST_ASSERT_VAL("failed to find map_b by address", RC_CHK_EQUAL(found, map));
+ map__zput(found);
+
+ found = maps__find_by_name(maps, "map_b");
+ TEST_ASSERT_VAL("failed to find map_b by name", RC_CHK_EQUAL(found, map));
+ map__zput(found);
+
+ maps__remove(maps, map);
+ map__zput(map);
+
+ found = maps__find_by_name(maps, "map_d");
+ TEST_ASSERT_VAL("failed to find map_d after removal", found && map__start(found) == 4000);
+ map__zput(found);
+
+ maps__zput(maps);
+ return TEST_OK;
+}
+
static struct test_case tests__maps[] = {
TEST_CASE("Test merge_in interface", maps__merge_in),
TEST_CASE("Test fix up overlap interface", maps__fixup_overlap_and_insert),
+ TEST_CASE("Test incremental sort and tail insert", maps__incremental_sort),
{ .name = NULL, }
};
diff --git a/tools/perf/util/maps.c b/tools/perf/util/maps.c
index a6cbe65cc5ca..afd2603e77d5 100644
--- a/tools/perf/util/maps.c
+++ b/tools/perf/util/maps.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
#include <errno.h>
#include <stdlib.h>
+#include <linux/kernel.h>
#include <linux/zalloc.h>
#include "debug.h"
#include "dso.h"
@@ -56,6 +57,16 @@ DECLARE_RC_STRUCT(maps) {
* maps_by_name.
*/
unsigned int nr_maps_allocated;
+ /**
+ * @nr_maps_by_address_sorted: number of leading entries in
+ * maps_by_address that are known to be sorted.
+ */
+ unsigned int nr_maps_by_address_sorted;
+ /**
+ * @nr_maps_by_name_sorted: number of leading entries in maps_by_name
+ * that are known to be sorted.
+ */
+ unsigned int nr_maps_by_name_sorted;
/**
* @last_search_by_name_idx: cache of last found by name entry's index
* as frequent searches for the same dso name are common.
@@ -72,8 +83,17 @@ DECLARE_RC_STRUCT(maps) {
static void check_invariants(const struct maps *maps __maybe_unused)
{
#ifndef NDEBUG
- assert(RC_CHK_ACCESS(maps)->nr_maps <= RC_CHK_ACCESS(maps)->nr_maps_allocated);
- for (unsigned int i = 0; i < RC_CHK_ACCESS(maps)->nr_maps; i++) {
+ unsigned int nr_maps = RC_CHK_ACCESS(maps)->nr_maps;
+ unsigned int nr_addr_sorted = RC_CHK_ACCESS(maps)->nr_maps_by_address_sorted;
+ unsigned int nr_name_sorted = RC_CHK_ACCESS(maps)->nr_maps_by_name_sorted;
+
+ assert(nr_maps <= RC_CHK_ACCESS(maps)->nr_maps_allocated);
+ assert(nr_addr_sorted <= nr_maps);
+ assert(!RC_CHK_ACCESS(maps)->maps_by_address_sorted || nr_addr_sorted == nr_maps);
+ assert(nr_name_sorted <= nr_maps);
+ assert(!RC_CHK_ACCESS(maps)->maps_by_name_sorted || nr_name_sorted == nr_maps);
+
+ for (unsigned int i = 0; i < nr_maps; i++) {
struct map *map = RC_CHK_ACCESS(maps)->maps_by_address[i];
/* Check map is well-formed. */
@@ -84,28 +104,25 @@ static void check_invariants(const struct maps *maps __maybe_unused)
if (map__dso(map) && dso__kernel(map__dso(map)))
assert(RC_CHK_EQUAL(map__kmap(map)->kmaps, maps));
- if (i > 0) {
+ if (i > 0 && i < nr_addr_sorted) {
struct map *prev = RC_CHK_ACCESS(maps)->maps_by_address[i - 1];
- /* If addresses are sorted... */
- if (RC_CHK_ACCESS(maps)->maps_by_address_sorted) {
- /* Maps should be in start address order. */
- assert(map__start(prev) <= map__start(map));
- /*
- * If the ends of maps aren't broken (during
- * construction) then they should be ordered
- * too.
- */
- if (!RC_CHK_ACCESS(maps)->ends_broken) {
- assert(map__end(prev) <= map__end(map));
- assert(map__end(prev) <= map__start(map) ||
- map__start(prev) == map__start(map));
- }
+ /* Maps in sorted prefix should be in start address order. */
+ assert(map__start(prev) <= map__start(map));
+ /*
+ * If the ends of maps aren't broken (during
+ * construction) then they should be ordered
+ * too.
+ */
+ if (!RC_CHK_ACCESS(maps)->ends_broken) {
+ assert(map__end(prev) <= map__end(map));
+ assert(map__end(prev) <= map__start(map) ||
+ map__start(prev) == map__start(map));
}
}
}
if (RC_CHK_ACCESS(maps)->maps_by_name) {
- for (unsigned int i = 0; i < RC_CHK_ACCESS(maps)->nr_maps; i++) {
+ for (unsigned int i = 0; i < nr_maps; i++) {
struct map *map = RC_CHK_ACCESS(maps)->maps_by_name[i];
/*
@@ -137,6 +154,14 @@ static void maps__set_nr_maps_allocated(struct maps *maps, unsigned int nr_maps_
static void maps__set_nr_maps(struct maps *maps, unsigned int nr_maps)
{
RC_CHK_ACCESS(maps)->nr_maps = nr_maps;
+ if (RC_CHK_ACCESS(maps)->maps_by_address_sorted ||
+ RC_CHK_ACCESS(maps)->nr_maps_by_address_sorted > nr_maps) {
+ RC_CHK_ACCESS(maps)->nr_maps_by_address_sorted = nr_maps;
+ }
+ if (RC_CHK_ACCESS(maps)->maps_by_name_sorted ||
+ RC_CHK_ACCESS(maps)->nr_maps_by_name_sorted > nr_maps) {
+ RC_CHK_ACCESS(maps)->nr_maps_by_name_sorted = nr_maps;
+ }
}
/* Not in the header, to aid reference counting. */
@@ -160,6 +185,8 @@ static bool maps__maps_by_address_sorted(const struct maps *maps)
static void maps__set_maps_by_address_sorted(struct maps *maps, bool value)
{
RC_CHK_ACCESS(maps)->maps_by_address_sorted = value;
+ if (value)
+ RC_CHK_ACCESS(maps)->nr_maps_by_address_sorted = maps__nr_maps(maps);
}
static bool maps__maps_by_name_sorted(const struct maps *maps)
@@ -170,6 +197,8 @@ static bool maps__maps_by_name_sorted(const struct maps *maps)
static void maps__set_maps_by_name_sorted(struct maps *maps, bool value)
{
RC_CHK_ACCESS(maps)->maps_by_name_sorted = value;
+ if (value)
+ RC_CHK_ACCESS(maps)->nr_maps_by_name_sorted = maps__nr_maps(maps);
}
struct machine *maps__machine(const struct maps *maps)
@@ -241,6 +270,8 @@ static void maps__init(struct maps *maps, struct machine *machine)
refcount_set(maps__refcnt(maps), 1);
RC_CHK_ACCESS(maps)->nr_maps = 0;
RC_CHK_ACCESS(maps)->nr_maps_allocated = 0;
+ RC_CHK_ACCESS(maps)->nr_maps_by_address_sorted = 0;
+ RC_CHK_ACCESS(maps)->nr_maps_by_name_sorted = 0;
RC_CHK_ACCESS(maps)->last_search_by_name_idx = 0;
RC_CHK_ACCESS(maps)->maps_by_address_sorted = true;
RC_CHK_ACCESS(maps)->maps_by_name_sorted = false;
@@ -313,6 +344,7 @@ static void __maps__free_maps_by_name(struct maps *maps)
zfree(&RC_CHK_ACCESS(maps)->maps_by_name);
/* Consistent with maps__init(). When maps_by_name == NULL, maps_by_name_sorted == false */
+ RC_CHK_ACCESS(maps)->nr_maps_by_name_sorted = 0;
maps__set_maps_by_name_sorted(maps, false);
}
@@ -339,15 +371,54 @@ static int map__start_cmp(const void *a, const void *b)
return map_a_start > map_b_start ? 1 : -1;
}
+static unsigned int maps__by_address_upper_bound(struct map **arr, unsigned int len,
+ const struct map *map)
+{
+ unsigned int low = 0, high = len;
+
+ while (low < high) {
+ unsigned int mid = low + (high - low) / 2;
+
+ if (map__start_cmp(&arr[mid], &map) <= 0)
+ low = mid + 1;
+ else
+ high = mid;
+ }
+ return low;
+}
+
static void __maps__sort_by_address(struct maps *maps)
{
+ struct map **maps_by_address;
+ unsigned int n, m, k;
+
if (maps__maps_by_address_sorted(maps))
return;
- qsort(maps__maps_by_address(maps),
- maps__nr_maps(maps),
- sizeof(struct map *),
- map__start_cmp);
+ maps_by_address = maps__maps_by_address(maps);
+ n = maps__nr_maps(maps);
+ m = RC_CHK_ACCESS(maps)->nr_maps_by_address_sorted;
+ if (m > n)
+ m = 0;
+ k = n - m;
+
+ if (k <= 8 || n <= 64) {
+ for (unsigned int i = max(m, 1U); i < n; i++) {
+ struct map *map = maps_by_address[i];
+
+ if (map__start_cmp(&maps_by_address[i - 1], &map) > 0) {
+ unsigned int pos =
+ maps__by_address_upper_bound(maps_by_address, i, map);
+
+ memmove(&maps_by_address[pos + 1],
+ &maps_by_address[pos],
+ (i - pos) * sizeof(*maps_by_address));
+ maps_by_address[pos] = map;
+ }
+ }
+ } else {
+ qsort(maps_by_address, n, sizeof(struct map *), map__start_cmp);
+ }
maps__set_maps_by_address_sorted(maps, true);
}
@@ -373,6 +444,22 @@ static int map__strcmp(const void *a, const void *b)
return ret;
}
+static unsigned int maps__by_name_upper_bound(struct map **arr, unsigned int len,
+ const struct map *map)
+{
+ unsigned int low = 0, high = len;
+
+ while (low < high) {
+ unsigned int mid = low + (high - low) / 2;
+
+ if (map__strcmp(&arr[mid], &map) <= 0)
+ low = mid + 1;
+ else
+ high = mid;
+ }
+ return low;
+}
+
static int maps__sort_by_name(struct maps *maps)
{
int err = 0;
@@ -380,6 +467,7 @@ static int maps__sort_by_name(struct maps *maps)
down_write(maps__lock(maps));
if (!maps__maps_by_name_sorted(maps)) {
struct map **maps_by_name = maps__maps_by_name(maps);
+ unsigned int n = maps__nr_maps(maps);
if (!maps_by_name) {
maps_by_name = malloc(RC_CHK_ACCESS(maps)->nr_maps_allocated *
@@ -388,18 +476,38 @@ static int maps__sort_by_name(struct maps *maps)
err = -ENOMEM;
else {
struct map **maps_by_address = maps__maps_by_address(maps);
- unsigned int n = maps__nr_maps(maps);
maps__set_maps_by_name(maps, maps_by_name);
for (unsigned int i = 0; i < n; i++)
maps_by_name[i] = map__get(maps_by_address[i]);
+ RC_CHK_ACCESS(maps)->nr_maps_by_name_sorted = 0;
}
}
if (!err) {
- qsort(maps_by_name,
- maps__nr_maps(maps),
- sizeof(struct map *),
- map__strcmp);
+ unsigned int m = RC_CHK_ACCESS(maps)->nr_maps_by_name_sorted;
+ unsigned int k;
+
+ if (m > n)
+ m = 0;
+ k = n - m;
+ if (k <= 8 || n <= 64) {
+ for (unsigned int i = max(m, 1U); i < n; i++) {
+ struct map *map = maps_by_name[i];
+
+ if (map__strcmp(&maps_by_name[i - 1], &map) > 0) {
+ unsigned int pos =
+ maps__by_name_upper_bound(maps_by_name,
+ i, map);
+
+ memmove(&maps_by_name[pos + 1],
+ &maps_by_name[pos],
+ (i - pos) * sizeof(*maps_by_name));
+ maps_by_name[pos] = map;
+ }
+ }
+ } else {
+ qsort(maps_by_name, n, sizeof(struct map *), map__strcmp);
+ }
maps__set_maps_by_name_sorted(maps, true);
}
}
@@ -411,19 +519,27 @@ static int maps__sort_by_name(struct maps *maps)
static unsigned int maps__by_address_index(const struct maps *maps, const struct map *map)
{
struct map **maps_by_address = maps__maps_by_address(maps);
+ unsigned int nr_sorted = RC_CHK_ACCESS(maps)->nr_maps_by_address_sorted;
+ unsigned int nr_maps = maps__nr_maps(maps);
+
+ if (nr_sorted > nr_maps)
+ nr_sorted = 0;
- if (maps__maps_by_address_sorted(maps)) {
+ if (nr_sorted > 0) {
struct map **mapp =
- bsearch(&map, maps__maps_by_address(maps), maps__nr_maps(maps),
+ bsearch(&map, maps_by_address, nr_sorted,
sizeof(*mapp), map__start_cmp);
if (mapp)
return mapp - maps_by_address;
- } else {
- for (unsigned int i = 0; i < maps__nr_maps(maps); i++) {
- if (RC_CHK_ACCESS(maps_by_address[i]) == RC_CHK_ACCESS(map))
- return i;
- }
+ }
+ for (unsigned int i = nr_sorted; i < nr_maps; i++) {
+ if (RC_CHK_ACCESS(maps_by_address[i]) == RC_CHK_ACCESS(map))
+ return i;
+ }
+ for (unsigned int i = 0; i < nr_sorted; i++) {
+ if (RC_CHK_ACCESS(maps_by_address[i]) == RC_CHK_ACCESS(map))
+ return i;
}
pr_err("Map missing from maps");
return -1;
@@ -432,19 +548,27 @@ static unsigned int maps__by_address_index(const struct maps *maps, const struct
static unsigned int maps__by_name_index(const struct maps *maps, const struct map *map)
{
struct map **maps_by_name = maps__maps_by_name(maps);
+ unsigned int nr_sorted = RC_CHK_ACCESS(maps)->nr_maps_by_name_sorted;
+ unsigned int nr_maps = maps__nr_maps(maps);
+
+ if (nr_sorted > nr_maps)
+ nr_sorted = 0;
- if (maps__maps_by_name_sorted(maps)) {
+ if (nr_sorted > 0) {
struct map **mapp =
- bsearch(&map, maps_by_name, maps__nr_maps(maps),
+ bsearch(&map, maps_by_name, nr_sorted,
sizeof(*mapp), map__strcmp);
if (mapp)
return mapp - maps_by_name;
- } else {
- for (unsigned int i = 0; i < maps__nr_maps(maps); i++) {
- if (RC_CHK_ACCESS(maps_by_name[i]) == RC_CHK_ACCESS(map))
- return i;
- }
+ }
+ for (unsigned int i = nr_sorted; i < nr_maps; i++) {
+ if (RC_CHK_ACCESS(maps_by_name[i]) == RC_CHK_ACCESS(map))
+ return i;
+ }
+ for (unsigned int i = 0; i < nr_sorted; i++) {
+ if (RC_CHK_ACCESS(maps_by_name[i]) == RC_CHK_ACCESS(map))
+ return i;
}
pr_err("Map missing from maps");
return -1;
@@ -519,8 +643,12 @@ static int __maps__insert(struct maps *maps, struct map *new)
/* Sorted if maps were already sorted and this map starts after the last one. */
maps__set_maps_by_address_sorted(maps,
maps__maps_by_address_sorted(maps) &&
- map__end(maps_by_address[nr_maps - 2]) <= map__start(new));
- maps__set_maps_by_name_sorted(maps, false);
+ map__end(maps_by_address[nr_maps - 2]) <= map__start(new) &&
+ map__start_cmp(&maps_by_address[nr_maps - 2], &new) <= 0);
+ maps__set_maps_by_name_sorted(maps,
+ maps_by_name &&
+ maps__maps_by_name_sorted(maps) &&
+ map__strcmp(&maps_by_name[nr_maps - 2], &new) <= 0);
}
if (map__end(new) < map__start(new))
RC_CHK_ACCESS(maps)->ends_broken = true;
@@ -552,6 +680,8 @@ static void __maps__remove(struct maps *maps, struct map *map)
memmove(&maps_by_address[address_idx],
&maps_by_address[address_idx + 1],
(nr_maps - address_idx - 1) * sizeof(*maps_by_address));
+ if (address_idx < RC_CHK_ACCESS(maps)->nr_maps_by_address_sorted)
+ --RC_CHK_ACCESS(maps)->nr_maps_by_address_sorted;
if (maps_by_name) {
unsigned int name_idx = maps__by_name_index(maps, map);
@@ -560,9 +690,16 @@ static void __maps__remove(struct maps *maps, struct map *map)
memmove(&maps_by_name[name_idx],
&maps_by_name[name_idx + 1],
(nr_maps - name_idx - 1) * sizeof(*maps_by_name));
+ if (name_idx < RC_CHK_ACCESS(maps)->nr_maps_by_name_sorted)
+ --RC_CHK_ACCESS(maps)->nr_maps_by_name_sorted;
}
- --RC_CHK_ACCESS(maps)->nr_maps;
+ --nr_maps;
+ RC_CHK_ACCESS(maps)->nr_maps = nr_maps;
+ if (RC_CHK_ACCESS(maps)->nr_maps_by_address_sorted == nr_maps)
+ RC_CHK_ACCESS(maps)->maps_by_address_sorted = true;
+ if (maps_by_name && RC_CHK_ACCESS(maps)->nr_maps_by_name_sorted == nr_maps)
+ RC_CHK_ACCESS(maps)->maps_by_name_sorted = true;
}
void maps__remove(struct maps *maps, struct map *map)
@@ -603,7 +740,9 @@ int maps__mutate_mapping(struct maps *maps, struct map *map,
err = mutate_cb(map, data);
+ RC_CHK_ACCESS(maps)->nr_maps_by_address_sorted = 0;
RC_CHK_ACCESS(maps)->maps_by_address_sorted = false;
+ RC_CHK_ACCESS(maps)->nr_maps_by_name_sorted = 0;
RC_CHK_ACCESS(maps)->maps_by_name_sorted = false;
up_write(maps__lock(maps));
@@ -858,6 +997,10 @@ static unsigned int first_ending_after(struct maps *maps, const struct map *map)
assert(maps__maps_by_address_sorted(maps));
if (low <= high && map__end(maps_by_address[0]) > map__start(map))
return 0;
+ if (low <= high && !RC_CHK_ACCESS(maps)->ends_broken &&
+ map__end(maps_by_address[high]) <= map__start(map)) {
+ return first;
+ }
while (low <= high) {
int mid = (low + high) / 2;
@@ -925,7 +1068,17 @@ static int __maps__insert_sorted(struct maps *maps, unsigned int first_after_ind
maps_by_name[nr_maps + 1] = map__get(new2);
}
RC_CHK_ACCESS(maps)->nr_maps = nr_maps + to_add;
- maps__set_maps_by_name_sorted(maps, false);
+ RC_CHK_ACCESS(maps)->nr_maps_by_address_sorted = nr_maps + to_add;
+ if (maps_by_name) {
+ if (nr_maps == 0 && to_add == 1) {
+ maps__set_maps_by_name_sorted(maps, true);
+ } else {
+ maps__set_maps_by_name_sorted(maps,
+ maps__maps_by_name_sorted(maps) &&
+ map__strcmp(&maps_by_name[nr_maps - 1], &new1) <= 0 &&
+ (!new2 || map__strcmp(&new1, &new2) <= 0));
+ }
+ }
map__set_kmap_maps(new1, maps);
map__set_kmap_maps(new2, maps);
@@ -1049,9 +1202,16 @@ static int __maps__fixup_overlap_and_insert(struct maps *maps, struct map *new)
map__set_kmap_maps(new, maps);
if (maps_by_name) {
+ unsigned int nr_maps = maps__nr_maps(maps);
+
map__put(maps_by_name[ni]);
- maps_by_name[ni] = map__get(new);
- maps__set_maps_by_name_sorted(maps, false);
+ memmove(&maps_by_name[ni],
+ &maps_by_name[ni + 1],
+ (nr_maps - ni - 1) * sizeof(*maps_by_name));
+ maps_by_name[nr_maps - 1] = map__get(new);
+ if (ni < RC_CHK_ACCESS(maps)->nr_maps_by_name_sorted)
+ --RC_CHK_ACCESS(maps)->nr_maps_by_name_sorted;
+ maps__set_maps_by_name_sorted(maps, nr_maps <= 1);
}
err = __maps__insert_sorted(maps, i + 1, after, NULL);
@@ -1077,8 +1237,13 @@ static int __maps__fixup_overlap_and_insert(struct maps *maps, struct map *new)
if (maps_by_name) {
map__put(maps_by_name[ni]);
- maps_by_name[ni] = map__get(new);
- maps__set_maps_by_name_sorted(maps, false);
+ memmove(&maps_by_name[ni],
+ &maps_by_name[ni + 1],
+ (nr_maps - ni - 1) * sizeof(*maps_by_name));
+ maps_by_name[nr_maps - 1] = map__get(new);
+ if (ni < RC_CHK_ACCESS(maps)->nr_maps_by_name_sorted)
+ --RC_CHK_ACCESS(maps)->nr_maps_by_name_sorted;
+ maps__set_maps_by_name_sorted(maps, nr_maps <= 1);
}
check_invariants(maps);
@@ -1094,14 +1259,22 @@ static int __maps__fixup_overlap_and_insert(struct maps *maps, struct map *new)
memmove(&maps_by_address[i],
&maps_by_address[i + 1],
(nr_maps - i - 1) * sizeof(*maps_by_address));
+ --RC_CHK_ACCESS(maps)->nr_maps_by_address_sorted;
if (maps_by_name) {
map__put(maps_by_name[ni]);
memmove(&maps_by_name[ni],
&maps_by_name[ni + 1],
(nr_maps - ni - 1) * sizeof(*maps_by_name));
+ if (ni < RC_CHK_ACCESS(maps)->nr_maps_by_name_sorted)
+ --RC_CHK_ACCESS(maps)->nr_maps_by_name_sorted;
}
--RC_CHK_ACCESS(maps)->nr_maps;
+ if (maps_by_name &&
+ RC_CHK_ACCESS(maps)->nr_maps_by_name_sorted ==
+ RC_CHK_ACCESS(maps)->nr_maps) {
+ RC_CHK_ACCESS(maps)->maps_by_name_sorted = true;
+ }
check_invariants(maps);
/*
* Maps are ordered but no need to increase `i` as the
@@ -1181,9 +1354,17 @@ int maps__copy_from(struct maps *dest, struct maps *parent)
if (err)
map__put(new);
}
- maps__set_maps_by_address_sorted(dest, maps__maps_by_address_sorted(parent));
+ if (maps__maps_by_address_sorted(parent)) {
+ maps__set_maps_by_address_sorted(dest, true);
+ } else {
+ RC_CHK_ACCESS(dest)->nr_maps_by_address_sorted =
+ min(RC_CHK_ACCESS(parent)->nr_maps_by_address_sorted,
+ RC_CHK_ACCESS(dest)->nr_maps);
+ maps__set_maps_by_address_sorted(dest, false);
+ }
RC_CHK_ACCESS(dest)->last_search_by_name_idx = 0;
/* Values were copied into the name array in address order. */
+ RC_CHK_ACCESS(dest)->nr_maps_by_name_sorted = 0;
maps__set_maps_by_name_sorted(dest, false);
} else {
/* Unexpected copying to a maps containing entries. */
--
2.56.0.313.g2479322e68-goog
^ permalink raw reply [flat|nested] 36+ messages in thread
* [PATCH v2 11/18] perf symbol-elf: Cache ELF program/section headers and defer symbol name lookup
2026-10-27 23:14 ` [PATCH v2 00/18] perf symbol: Reference counting, sorted arrays, and memory shrinking Ian Rogers
` (9 preceding siblings ...)
2026-10-27 23:15 ` [PATCH v2 10/18] perf maps: Optimize tail insertion and small-append sorting Ian Rogers
@ 2026-10-27 23:15 ` Ian Rogers
2026-10-27 23:15 ` [PATCH v2 12/18] perf rwsem: Add down_read_trylock() and down_write_trylock() Ian Rogers
` (6 subsequent siblings)
17 siblings, 0 replies; 36+ messages in thread
From: Ian Rogers @ 2026-10-27 23:15 UTC (permalink / raw)
To: irogers, acme, namhyung
Cc: adrian.hunter, haghdoost, james.clark, jolsa, linux-kernel,
linux-perf-users, mingo, peterz
Profiling dso__load_sym_internal() on large binaries showed significant
time spent in repeated libelf section and program header lookups
(elf_section_by_idx() and elf_read_program_header()) for each symbol,
resolving elf_sym__name() before filtering out non-symbol/non-allocated
entries, and running symbols__fixup_end() and symbols__fixup_duplicate()
twice when both .symtab and .dynsym are loaded.
Speed up ELF symbol table loading in dso__load_sym_internal() and
dso__load_sym():
- Pre-load PT_LOAD program headers into struct elf_phdr_cache and cache
per-section metadata in a 64-entry direct-mapped table in
dso__load_sym_internal() to avoid repeated libelf section and program
header lookups during symbol iteration.
- Defer elf_sym__name() lookup until after non-allocated and non-symbol
entries have been filtered out, and run symbols__fixup_end() and
symbols__fixup_duplicate() once in dso__load_sym() after loading both
.symtab and .dynsym.
Using 'perf bench internals symbols':
Before (perf binary):
Average DSO load + sort: 5.808 msec (+- 0.047 msec)
After (perf binary, -8.3%):
Average DSO load + sort: 5.324 msec (+- 0.045 msec)
Before (perf binary, --no-demangle):
Average DSO load + sort: 3.948 msec (+- 0.064 msec)
After (perf binary, --no-demangle, -13.7%):
Average DSO load + sort: 3.408 msec (+- 0.034 msec)
Before (libLLVM.so.21.1, --no-demangle):
Average DSO load + sort: 22.433 msec (+- 0.260 msec)
After (libLLVM.so.21.1, --no-demangle, -14.8%):
Average DSO load + sort: 19.122 msec (+- 0.226 msec)
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/symbol-elf.c | 275 +++++++++++++++++++++++++++--------
1 file changed, 217 insertions(+), 58 deletions(-)
diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c
index 4023174ed6db..6082136c9662 100644
--- a/tools/perf/util/symbol-elf.c
+++ b/tools/perf/util/symbol-elf.c
@@ -265,6 +265,118 @@ static int elf_read_program_header(Elf *elf, u64 vaddr, GElf_Phdr *phdr)
return -1;
}
+struct elf_phdr_range {
+ u64 vaddr;
+ u64 end;
+ u64 adjust;
+ GElf_Phdr phdr;
+};
+
+struct elf_phdr_cache {
+ struct elf_phdr_range inline_ranges[16];
+ struct elf_phdr_range *ranges;
+ size_t nr_ranges;
+ size_t last_idx;
+};
+
+static void elf_phdr_cache__init(struct elf_phdr_cache *cache, Elf *elf)
+{
+ size_t i, phdrnum = 0, cnt = 0;
+
+ cache->ranges = cache->inline_ranges;
+ cache->nr_ranges = 0;
+ cache->last_idx = 0;
+
+ if (!elf || elf_getphdrnum(elf, &phdrnum) || phdrnum == 0)
+ return;
+
+ if (phdrnum > ARRAY_SIZE(cache->inline_ranges)) {
+ cache->ranges = calloc(phdrnum, sizeof(*cache->ranges));
+ if (!cache->ranges) {
+ cache->ranges = cache->inline_ranges;
+ phdrnum = ARRAY_SIZE(cache->inline_ranges);
+ }
+ }
+
+ for (i = 0; i < phdrnum; i++) {
+ GElf_Phdr phdr;
+ u64 sz;
+
+ if (gelf_getphdr(elf, i, &phdr) == NULL)
+ break;
+ if (phdr.p_type != PT_LOAD)
+ continue;
+ sz = max(phdr.p_memsz, phdr.p_filesz);
+ if (!sz)
+ continue;
+ cache->ranges[cnt].vaddr = phdr.p_vaddr;
+ cache->ranges[cnt].end = phdr.p_vaddr + sz;
+ cache->ranges[cnt].adjust = phdr.p_vaddr - phdr.p_offset;
+ cache->ranges[cnt].phdr = phdr;
+ cnt++;
+ }
+ cache->nr_ranges = cnt;
+}
+
+static void elf_phdr_cache__exit(struct elf_phdr_cache *cache)
+{
+ if (cache->ranges && cache->ranges != cache->inline_ranges)
+ free(cache->ranges);
+}
+
+static const struct elf_phdr_range *
+elf_phdr_cache__find(struct elf_phdr_cache *cache, u64 vaddr)
+{
+ if (cache->nr_ranges == 0)
+ return NULL;
+
+ if (cache->last_idx < cache->nr_ranges) {
+ const struct elf_phdr_range *r = &cache->ranges[cache->last_idx];
+
+ if (vaddr >= r->vaddr && vaddr < r->end)
+ return r;
+ }
+ for (size_t i = 0; i < cache->nr_ranges; i++) {
+ const struct elf_phdr_range *r = &cache->ranges[i];
+
+ if (vaddr >= r->vaddr && vaddr < r->end) {
+ cache->last_idx = i;
+ return r;
+ }
+ }
+ return NULL;
+}
+
+#define ELF_SEC_CACHE_SIZE 64
+
+struct elf_sec_cache_entry {
+ uint32_t shndx;
+ bool valid;
+ bool is_alloc;
+ bool is_label_sec;
+ bool has_phdr;
+ u64 phdr_vaddr;
+ u64 phdr_end;
+ const char *section_name;
+ GElf_Shdr shdr;
+ GElf_Phdr phdr;
+};
+
+static inline const GElf_Phdr *
+elf_sec_cache__phdr(const struct elf_sec_cache_entry *sce,
+ struct elf_phdr_cache *phdr_cache,
+ u64 vaddr)
+{
+ const struct elf_phdr_range *pr;
+
+ if (sce->has_phdr && vaddr >= sce->phdr_vaddr && vaddr < sce->phdr_end)
+ return &sce->phdr;
+ pr = elf_phdr_cache__find(phdr_cache, vaddr);
+ if (!pr)
+ return NULL;
+ return &pr->phdr;
+}
+
struct rel_info {
u32 nr_entries;
u32 *sorted;
@@ -1543,6 +1655,8 @@ dso__load_sym_internal(struct dso *dso, struct map *map, struct symsrc *syms_ss,
int nr = 0;
bool remap_kernel = false, adjust_kernel_syms = false;
u64 max_text_sh_offset = 0;
+ struct elf_phdr_cache phdr_cache = {};
+ struct elf_sec_cache_entry sec_cache[ELF_SEC_CACHE_SIZE];
if (kmap && !kmaps)
return -1;
@@ -1634,14 +1748,18 @@ dso__load_sym_internal(struct dso *dso, struct map *map, struct symsrc *syms_ss,
if (kmodule && adjust_kernel_syms)
max_text_sh_offset = max_text_section(runtime_ss->elf, &runtime_ss->ehdr);
+ elf_phdr_cache__init(&phdr_cache, runtime_ss->elf);
+ memset(sec_cache, 0, sizeof(sec_cache));
+
curr_dso = dso__get(dso);
elf_symtab__for_each_symbol(syms, nr_syms, idx, sym) {
struct symbol *f;
- const char *elf_name = elf_sym__name(&sym, symstrs);
+ const char *elf_name = NULL;
char *demangled = NULL;
int is_label = elf_sym__is_label(&sym);
const char *section_name;
bool used_opd = false;
+ struct elf_sec_cache_entry *sce;
if (!is_label && !elf_sym__filter(&sym))
continue;
@@ -1652,6 +1770,7 @@ dso__load_sym_internal(struct dso *dso, struct map *map, struct symsrc *syms_ss,
* output:
*/
if (ehdr.e_machine == EM_ARM || ehdr.e_machine == EM_AARCH64) {
+ elf_name = elf_sym__name(&sym, symstrs);
if (elf_name[0] == '$' && strchr("adtx", elf_name[1])
&& (elf_name[2] == '\0' || elf_name[2] == '.'))
continue;
@@ -1659,13 +1778,19 @@ dso__load_sym_internal(struct dso *dso, struct map *map, struct symsrc *syms_ss,
/* Reject RISCV ELF "mapping symbols" */
if (ehdr.e_machine == EM_RISCV) {
+ if (!elf_name)
+ elf_name = elf_sym__name(&sym, symstrs);
if (elf_name[0] == '$' && strchr("dx", elf_name[1]))
continue;
}
/* Reject kernel mapping symbols for kernel DSOs only */
- if (dso__kernel(dso) && is_ignored_kernel_symbol(elf_name))
- continue;
+ if (dso__kernel(dso)) {
+ if (!elf_name)
+ elf_name = elf_sym__name(&sym, symstrs);
+ if (is_ignored_kernel_symbol(elf_name))
+ continue;
+ }
if (runtime_ss->opdsec && sym.st_shndx == runtime_ss->opdidx) {
u32 offset = sym.st_value - syms_ss->opdshdr.sh_addr;
@@ -1688,16 +1813,56 @@ dso__load_sym_internal(struct dso *dso, struct map *map, struct symsrc *syms_ss,
if (sym.st_shndx == SHN_ABS)
continue;
- sec = elf_getscn(syms_ss->elf, sym.st_shndx);
- if (!sec) {
- if (dynsym && ehdr.e_shnum &&
- sym.st_shndx < SHN_LORESERVE &&
- sym.st_shndx >= ehdr.e_shnum)
- continue;
- goto out_elf_end;
- }
+ sce = &sec_cache[sym.st_shndx & (ELF_SEC_CACHE_SIZE - 1)];
+ if (!sce->valid || sce->shndx != sym.st_shndx) {
+ const struct elf_phdr_range *pr;
- gelf_getshdr(sec, &shdr);
+ sec = elf_getscn(syms_ss->elf, sym.st_shndx);
+ if (!sec) {
+ if (dynsym && ehdr.e_shnum &&
+ sym.st_shndx < SHN_LORESERVE &&
+ sym.st_shndx >= ehdr.e_shnum)
+ continue;
+ goto out_elf_end;
+ }
+
+ gelf_getshdr(sec, &shdr);
+ secstrs = secstrs_sym;
+
+ /*
+ * We have to fallback to runtime when syms' section
+ * header has NOBITS set. NOBITS results in file offset
+ * (sh_offset) not being incremented. So sh_offset used
+ * below has different values for syms (invalid) and
+ * runtime (valid).
+ */
+ if (shdr.sh_type == SHT_NOBITS && (shdr.sh_flags & SHF_ALLOC)) {
+ sec = elf_getscn(runtime_ss->elf, sym.st_shndx);
+ if (!sec)
+ goto out_elf_end;
+
+ gelf_getshdr(sec, &shdr);
+ secstrs = secstrs_run;
+ }
+
+ sce->shndx = sym.st_shndx;
+ sce->valid = true;
+ sce->is_alloc = (shdr.sh_flags & SHF_ALLOC) != 0;
+ if (sce->is_alloc) {
+ sce->shdr = shdr;
+ sce->section_name = elf_sec__name(&shdr, secstrs);
+ sce->is_label_sec = elf_sec__filter(&shdr, secstrs) != 0;
+ pr = elf_phdr_cache__find(&phdr_cache, shdr.sh_addr);
+ if (pr) {
+ sce->has_phdr = true;
+ sce->phdr_vaddr = pr->vaddr;
+ sce->phdr_end = pr->end;
+ sce->phdr = pr->phdr;
+ } else {
+ sce->has_phdr = false;
+ }
+ }
+ }
/*
* If the attribute bit SHF_ALLOC is not set, the section
@@ -1707,30 +1872,13 @@ dso__load_sym_internal(struct dso *dso, struct map *map, struct symsrc *syms_ss,
* the section aren't loaded to memory during process execution,
* so skip them.
*/
- if (!(shdr.sh_flags & SHF_ALLOC))
+ if (!sce->is_alloc)
continue;
- secstrs = secstrs_sym;
-
- /*
- * We have to fallback to runtime when syms' section header has
- * NOBITS set. NOBITS results in file offset (sh_offset) not
- * being incremented. So sh_offset used below has different
- * values for syms (invalid) and runtime (valid).
- */
- if (shdr.sh_type == SHT_NOBITS) {
- sec = elf_getscn(runtime_ss->elf, sym.st_shndx);
- if (!sec)
- goto out_elf_end;
-
- gelf_getshdr(sec, &shdr);
- secstrs = secstrs_run;
- }
-
- if (is_label && !elf_sec__filter(&shdr, secstrs))
+ if (is_label && !sce->is_label_sec)
continue;
- section_name = elf_sec__name(&shdr, secstrs);
+ section_name = sce->section_name;
/* On ARM, symbols for thumb functions have 1 added to
* the symbol address as a flag - remove it */
@@ -1740,7 +1888,7 @@ dso__load_sym_internal(struct dso *dso, struct map *map, struct symsrc *syms_ss,
--sym.st_value;
if (dso__kernel(dso)) {
- if (dso__process_kernel_symbol(dso, map, &sym, &shdr,
+ if (dso__process_kernel_symbol(dso, map, &sym, &sce->shdr,
kmaps, kmap, &curr_dso,
section_name,
adjust_kernel_syms,
@@ -1750,17 +1898,20 @@ dso__load_sym_internal(struct dso *dso, struct map *map, struct symsrc *syms_ss,
goto out_elf_end;
} else if ((used_opd && runtime_ss->adjust_symbols) ||
(!used_opd && syms_ss->adjust_symbols)) {
- GElf_Phdr phdr;
+ const GElf_Phdr *phdr;
- if (elf_read_program_header(runtime_ss->elf,
- (u64)sym.st_value, &phdr)) {
+ phdr = elf_sec_cache__phdr(sce, &phdr_cache,
+ (u64)sym.st_value);
+ if (!phdr) {
pr_debug4("%s: failed to find program header for "
"symbol: %s st_value: %#" PRIx64 "\n",
- __func__, elf_name, (u64)sym.st_value);
+ __func__, elf_sym__name(&sym, symstrs),
+ (u64)sym.st_value);
pr_debug4("%s: adjusting symbol: st_value: %#" PRIx64 " "
"sh_addr: %#" PRIx64 " sh_offset: %#" PRIx64 "\n",
- __func__, (u64)sym.st_value, (u64)shdr.sh_addr,
- (u64)shdr.sh_offset);
+ __func__, (u64)sym.st_value,
+ (u64)sce->shdr.sh_addr,
+ (u64)sce->shdr.sh_offset);
/*
* Fail to find program header, let's rollback
* to use shdr.sh_addr and shdr.sh_offset to
@@ -1769,16 +1920,18 @@ dso__load_sym_internal(struct dso *dso, struct map *map, struct symsrc *syms_ss,
* still need to handle java JIT symbols in this
* case.
*/
- sym.st_value -= shdr.sh_addr - shdr.sh_offset;
+ sym.st_value -= sce->shdr.sh_addr - sce->shdr.sh_offset;
} else {
pr_debug4("%s: adjusting symbol: st_value: %#" PRIx64 " "
"p_vaddr: %#" PRIx64 " p_offset: %#" PRIx64 "\n",
- __func__, (u64)sym.st_value, (u64)phdr.p_vaddr,
- (u64)phdr.p_offset);
- sym.st_value -= phdr.p_vaddr - phdr.p_offset;
+ __func__, (u64)sym.st_value, (u64)phdr->p_vaddr,
+ (u64)phdr->p_offset);
+ sym.st_value -= phdr->p_vaddr - phdr->p_offset;
}
}
+ if (!elf_name)
+ elf_name = elf_sym__name(&sym, symstrs);
demangled = dso__demangle_sym(dso, kmodule, elf_name);
if (demangled != NULL)
elf_name = demangled;
@@ -1796,25 +1949,13 @@ dso__load_sym_internal(struct dso *dso, struct map *map, struct symsrc *syms_ss,
goto out_elf_end;
nr++;
}
- dso__put(curr_dso);
- /*
- * For misannotated, zeroed, ASM function sizes.
- */
- if (nr > 0) {
- symbols__fixup_end(dso__symbols(dso), false);
- symbols__fixup_duplicate(dso__symbols(dso));
- if (kmap) {
- /*
- * We need to fixup this here too because we create new
- * maps here, for things like vsyscall sections.
- */
- maps__fixup_end(kmaps);
- }
- }
+ dso__put(curr_dso);
+ elf_phdr_cache__exit(&phdr_cache);
return nr;
out_elf_end:
dso__put(curr_dso);
+ elf_phdr_cache__exit(&phdr_cache);
return -1;
}
@@ -1872,6 +2013,24 @@ int dso__load_sym(struct dso *dso, struct map *map, struct symsrc *syms_ss,
nr += err;
}
+ /*
+ * For misannotated, zeroed, ASM function sizes.
+ */
+ if (nr > 0) {
+ struct kmap *kmap = dso__kernel(dso) ? map__kmap(map) : NULL;
+ struct maps *kmaps = kmap ? map__kmaps(map) : NULL;
+
+ symbols__fixup_end(dso__symbols(dso), false);
+ symbols__fixup_duplicate(dso__symbols(dso));
+ if (kmap && kmaps) {
+ /*
+ * We need to fixup this here too because we create new
+ * maps here, for things like vsyscall sections.
+ */
+ maps__fixup_end(kmaps);
+ }
+ }
+
return nr;
}
--
2.56.0.313.g2479322e68-goog
^ permalink raw reply [flat|nested] 36+ messages in thread
* [PATCH v2 12/18] perf rwsem: Add down_read_trylock() and down_write_trylock()
2026-10-27 23:14 ` [PATCH v2 00/18] perf symbol: Reference counting, sorted arrays, and memory shrinking Ian Rogers
` (10 preceding siblings ...)
2026-10-27 23:15 ` [PATCH v2 11/18] perf symbol-elf: Cache ELF program/section headers and defer symbol name lookup Ian Rogers
@ 2026-10-27 23:15 ` Ian Rogers
2026-10-27 23:15 ` [PATCH v2 13/18] perf vdso: Constify dso parameter in dso__is_vdso() Ian Rogers
` (5 subsequent siblings)
17 siblings, 0 replies; 36+ messages in thread
From: Ian Rogers @ 2026-10-27 23:15 UTC (permalink / raw)
To: irogers, acme, namhyung
Cc: adrian.hunter, haghdoost, james.clark, jolsa, linux-kernel,
linux-perf-users, mingo, peterz
On-demand memory shrinking (perf_session__shrink(), dsos__shrink(),
symbols__shrink()) can be triggered from OOM recovery inside allocation
wrappers while the current thread already holds a rw_semaphore (such as
symbols->lock, dsos->lock, or machines->lock). Non-blocking trylock
primitives are needed so shrinking paths can safely skip any locked
container without deadlocking.
Add non-blocking down_read_trylock() and down_write_trylock() wrappers
around pthread_rwlock_tryrdlock() / pthread_rwlock_trywrlock() and
pthread_mutex_trylock() along with SHARED_TRYLOCK_FUNCTION and
EXCLUSIVE_TRYLOCK_FUNCTION thread-safety annotations.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/mutex.h | 3 +++
tools/perf/util/rwsem.c | 20 ++++++++++++++++++++
tools/perf/util/rwsem.h | 2 ++
3 files changed, 25 insertions(+)
diff --git a/tools/perf/util/mutex.h b/tools/perf/util/mutex.h
index 5de1065ea2b9..de7dd6d8326c 100644
--- a/tools/perf/util/mutex.h
+++ b/tools/perf/util/mutex.h
@@ -57,6 +57,8 @@
/* Documents functions that try to acquire a lock, and return success or failure. */
#define EXCLUSIVE_TRYLOCK_FUNCTION(...) \
__attribute__((exclusive_trylock_function(__VA_ARGS__)))
+#define SHARED_TRYLOCK_FUNCTION(...) \
+ __attribute__((shared_trylock_function(__VA_ARGS__)))
/* Documents a function that expects a mutex to be held prior to entry. */
#define EXCLUSIVE_LOCKS_REQUIRED(...) __attribute__((exclusive_locks_required(__VA_ARGS__)))
@@ -78,6 +80,7 @@
#define SHARED_LOCK_FUNCTION(...)
#define UNLOCK_FUNCTION(...)
#define EXCLUSIVE_TRYLOCK_FUNCTION(...)
+#define SHARED_TRYLOCK_FUNCTION(...)
#define EXCLUSIVE_LOCKS_REQUIRED(...)
#define SHARED_LOCKS_REQUIRED(...)
#define NO_THREAD_SAFETY_ANALYSIS
diff --git a/tools/perf/util/rwsem.c b/tools/perf/util/rwsem.c
index 9d26832398db..37fa08bae5e9 100644
--- a/tools/perf/util/rwsem.c
+++ b/tools/perf/util/rwsem.c
@@ -37,6 +37,16 @@ int down_read(struct rw_semaphore *sem)
#endif
}
+bool down_read_trylock(struct rw_semaphore *sem)
+ NO_THREAD_SAFETY_ANALYSIS
+{
+#if RWS_ERRORCHECK
+ return mutex_trylock(&sem->mtx);
+#else
+ return perf_singlethreaded ? true : pthread_rwlock_tryrdlock(&sem->lock) == 0;
+#endif
+}
+
int up_read(struct rw_semaphore *sem)
NO_THREAD_SAFETY_ANALYSIS
{
@@ -59,6 +69,16 @@ int down_write(struct rw_semaphore *sem)
#endif
}
+bool down_write_trylock(struct rw_semaphore *sem)
+ NO_THREAD_SAFETY_ANALYSIS
+{
+#if RWS_ERRORCHECK
+ return mutex_trylock(&sem->mtx);
+#else
+ return perf_singlethreaded ? true : pthread_rwlock_trywrlock(&sem->lock) == 0;
+#endif
+}
+
int up_write(struct rw_semaphore *sem)
NO_THREAD_SAFETY_ANALYSIS
{
diff --git a/tools/perf/util/rwsem.h b/tools/perf/util/rwsem.h
index b102d8143181..782016c3e01f 100644
--- a/tools/perf/util/rwsem.h
+++ b/tools/perf/util/rwsem.h
@@ -22,9 +22,11 @@ int init_rwsem(struct rw_semaphore *sem);
int exit_rwsem(struct rw_semaphore *sem);
int down_read(struct rw_semaphore *sem) SHARED_LOCK_FUNCTION(sem);
+bool down_read_trylock(struct rw_semaphore *sem) SHARED_TRYLOCK_FUNCTION(true, sem);
int up_read(struct rw_semaphore *sem) UNLOCK_FUNCTION(sem);
int down_write(struct rw_semaphore *sem) EXCLUSIVE_LOCK_FUNCTION(sem);
+bool down_write_trylock(struct rw_semaphore *sem) EXCLUSIVE_TRYLOCK_FUNCTION(true, sem);
int up_write(struct rw_semaphore *sem) UNLOCK_FUNCTION(sem);
#endif /* _PERF_RWSEM_H */
--
2.56.0.313.g2479322e68-goog
^ permalink raw reply [flat|nested] 36+ messages in thread
* [PATCH v2 13/18] perf vdso: Constify dso parameter in dso__is_vdso()
2026-10-27 23:14 ` [PATCH v2 00/18] perf symbol: Reference counting, sorted arrays, and memory shrinking Ian Rogers
` (11 preceding siblings ...)
2026-10-27 23:15 ` [PATCH v2 12/18] perf rwsem: Add down_read_trylock() and down_write_trylock() Ian Rogers
@ 2026-10-27 23:15 ` Ian Rogers
2026-10-27 23:15 ` [PATCH v2 14/18] perf machine: Protect machines->guests rbtree with a rw_semaphore Ian Rogers
` (4 subsequent siblings)
17 siblings, 0 replies; 36+ messages in thread
From: Ian Rogers @ 2026-10-27 23:15 UTC (permalink / raw)
To: irogers, acme, namhyung
Cc: adrian.hunter, haghdoost, james.clark, jolsa, linux-kernel,
linux-perf-users, mingo, peterz
dso__can_shrink_symbols(const struct dso *dso) needs to query
dso__is_vdso(dso) on a const struct dso pointer to exclude vDSO images
from symbol shrinking.
Change the parameter of dso__is_vdso() from 'struct dso *' to
'const struct dso *'.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/vdso.c | 2 +-
tools/perf/util/vdso.h | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/tools/perf/util/vdso.c b/tools/perf/util/vdso.c
index c12f5d8c4bf6..7f2dde70f3e4 100644
--- a/tools/perf/util/vdso.c
+++ b/tools/perf/util/vdso.c
@@ -361,7 +361,7 @@ struct dso *machine__findnew_vdso(struct machine *machine,
return __machine__addnew_vdso(machine, DSO__NAME_VDSO, file);
}
-bool dso__is_vdso(struct dso *dso)
+bool dso__is_vdso(const struct dso *dso)
{
return !strcmp(dso__short_name(dso), DSO__NAME_VDSO) ||
!strcmp(dso__short_name(dso), DSO__NAME_VDSO32) ||
diff --git a/tools/perf/util/vdso.h b/tools/perf/util/vdso.h
index bc74ace603ae..b6e05bc2d9f3 100644
--- a/tools/perf/util/vdso.h
+++ b/tools/perf/util/vdso.h
@@ -19,7 +19,7 @@ static inline bool is_vdso_map(const char *filename)
struct dso;
-bool dso__is_vdso(struct dso *dso);
+bool dso__is_vdso(const struct dso *dso);
struct machine;
struct thread;
--
2.56.0.313.g2479322e68-goog
^ permalink raw reply [flat|nested] 36+ messages in thread
* [PATCH v2 14/18] perf machine: Protect machines->guests rbtree with a rw_semaphore
2026-10-27 23:14 ` [PATCH v2 00/18] perf symbol: Reference counting, sorted arrays, and memory shrinking Ian Rogers
` (12 preceding siblings ...)
2026-10-27 23:15 ` [PATCH v2 13/18] perf vdso: Constify dso parameter in dso__is_vdso() Ian Rogers
@ 2026-10-27 23:15 ` Ian Rogers
2026-10-27 23:15 ` [PATCH v2 15/18] perf symbol: Add memory shrinking for symbols, DSOs, and machines Ian Rogers
` (3 subsequent siblings)
17 siblings, 0 replies; 36+ messages in thread
From: Ian Rogers @ 2026-10-27 23:15 UTC (permalink / raw)
To: irogers, acme, namhyung
Cc: adrian.hunter, haghdoost, james.clark, jolsa, linux-kernel,
linux-perf-users, mingo, peterz
Unlike struct dsos, struct maps, and struct threads, struct machines had
no lock protecting its machines->guests rbtree. Synchronizing access to
machines->guests is needed both for concurrent guest machine lookups and
so machines__shrink() can safely iterate guest machines using
down_read_trylock(&machines->lock).
Add a rw_semaphore lock to struct machines to synchronize access to the
machines->guests rbtree across machines__add(), machines__find(),
machines__set_comm_exec(), machines__process_guests(),
machines__set_id_hdr_size(), machines__fprintf_dsos(),
machines__fprintf_dsos_buildid(), machines__destroy_kernel_maps(), and
machines__for_each_thread(), and check for an existing guest machine PID
under write lock in machines__add().
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/machine.c | 42 +++++++++++++++++++++++++++++++++------
tools/perf/util/machine.h | 1 +
2 files changed, 37 insertions(+), 6 deletions(-)
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index cb9e34220036..fb83da8ef09b 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -237,6 +237,7 @@ int machines__init(struct machines *machines, struct perf_session *session)
{
int err = machine__init(&machines->host, "", HOST_KERNEL_ID);
+ init_rwsem(&machines->lock);
machines->session = session;
machines->host.machines = machines;
machines->guests = RB_ROOT_CACHED;
@@ -247,12 +248,13 @@ void machines__exit(struct machines *machines)
{
machine__exit(&machines->host);
/* XXX exit guest */
+ exit_rwsem(&machines->lock);
}
struct machine *machines__add(struct machines *machines, pid_t pid,
const char *root_dir)
{
- struct rb_node **p = &machines->guests.rb_root.rb_node;
+ struct rb_node **p;
struct rb_node *parent = NULL;
struct machine *pos, *machine = malloc(sizeof(*machine));
bool leftmost = true;
@@ -265,9 +267,17 @@ struct machine *machines__add(struct machines *machines, pid_t pid,
return NULL;
}
+ down_write(&machines->lock);
+ p = &machines->guests.rb_root.rb_node;
while (*p != NULL) {
parent = *p;
pos = rb_entry(parent, struct machine, rb_node);
+ if (pid == pos->pid) {
+ up_write(&machines->lock);
+ machine__exit(machine);
+ free(machine);
+ return pos;
+ }
if (pid < pos->pid)
p = &(*p)->rb_left;
else {
@@ -278,8 +288,8 @@ struct machine *machines__add(struct machines *machines, pid_t pid,
rb_link_node(&machine->rb_node, parent, p);
rb_insert_color_cached(&machine->rb_node, &machines->guests, leftmost);
-
machine->machines = machines;
+ up_write(&machines->lock);
return machine;
}
@@ -290,16 +300,18 @@ void machines__set_comm_exec(struct machines *machines, bool comm_exec)
machines->host.comm_exec = comm_exec;
+ down_read(&machines->lock);
for (nd = rb_first_cached(&machines->guests); nd; nd = rb_next(nd)) {
struct machine *machine = rb_entry(nd, struct machine, rb_node);
machine->comm_exec = comm_exec;
}
+ up_read(&machines->lock);
}
struct machine *machines__find(struct machines *machines, pid_t pid)
{
- struct rb_node **p = &machines->guests.rb_root.rb_node;
+ struct rb_node **p;
struct rb_node *parent = NULL;
struct machine *machine;
struct machine *default_machine = NULL;
@@ -307,6 +319,8 @@ struct machine *machines__find(struct machines *machines, pid_t pid)
if (pid == HOST_KERNEL_ID)
return &machines->host;
+ down_read(&machines->lock);
+ p = &machines->guests.rb_root.rb_node;
while (*p != NULL) {
parent = *p;
machine = rb_entry(parent, struct machine, rb_node);
@@ -314,11 +328,14 @@ struct machine *machines__find(struct machines *machines, pid_t pid)
p = &(*p)->rb_left;
else if (pid > machine->pid)
p = &(*p)->rb_right;
- else
+ else {
+ up_read(&machines->lock);
return machine;
+ }
if (!machine->pid)
default_machine = machine;
}
+ up_read(&machines->lock);
return default_machine;
}
@@ -464,10 +481,12 @@ void machines__process_guests(struct machines *machines,
{
struct rb_node *nd;
+ down_read(&machines->lock);
for (nd = rb_first_cached(&machines->guests); nd; nd = rb_next(nd)) {
struct machine *pos = rb_entry(nd, struct machine, rb_node);
process(pos, data);
}
+ up_read(&machines->lock);
}
void machines__set_id_hdr_size(struct machines *machines, u16 id_hdr_size)
@@ -477,11 +496,13 @@ void machines__set_id_hdr_size(struct machines *machines, u16 id_hdr_size)
machines->host.id_hdr_size = id_hdr_size;
+ down_read(&machines->lock);
for (node = rb_first_cached(&machines->guests); node;
node = rb_next(node)) {
machine = rb_entry(node, struct machine, rb_node);
machine->id_hdr_size = id_hdr_size;
}
+ up_read(&machines->lock);
return;
}
@@ -935,10 +956,12 @@ size_t machines__fprintf_dsos(struct machines *machines, FILE *fp)
struct rb_node *nd;
size_t ret = dsos__fprintf(&machines->host.dsos, fp);
+ down_read(&machines->lock);
for (nd = rb_first_cached(&machines->guests); nd; nd = rb_next(nd)) {
struct machine *pos = rb_entry(nd, struct machine, rb_node);
ret += dsos__fprintf(&pos->dsos, fp);
}
+ up_read(&machines->lock);
return ret;
}
@@ -955,10 +978,12 @@ size_t machines__fprintf_dsos_buildid(struct machines *machines, FILE *fp,
struct rb_node *nd;
size_t ret = machine__fprintf_dsos_buildid(&machines->host, fp, skip, parm);
+ down_read(&machines->lock);
for (nd = rb_first_cached(&machines->guests); nd; nd = rb_next(nd)) {
struct machine *pos = rb_entry(nd, struct machine, rb_node);
ret += machine__fprintf_dsos_buildid(pos, fp, skip, parm);
}
+ up_read(&machines->lock);
return ret;
}
@@ -1303,10 +1328,12 @@ int machines__create_guest_kernel_maps(struct machines *machines)
void machines__destroy_kernel_maps(struct machines *machines)
{
- struct rb_node *next = rb_first_cached(&machines->guests);
+ struct rb_node *next;
machine__destroy_kernel_maps(&machines->host);
+ down_write(&machines->lock);
+ next = rb_first_cached(&machines->guests);
while (next) {
struct machine *pos = rb_entry(next, struct machine, rb_node);
@@ -1314,6 +1341,7 @@ void machines__destroy_kernel_maps(struct machines *machines)
rb_erase_cached(&pos->rb_node, &machines->guests);
machine__delete(pos);
}
+ up_write(&machines->lock);
}
int machines__create_kernel_maps(struct machines *machines, pid_t pid)
@@ -3134,13 +3162,15 @@ int machines__for_each_thread(struct machines *machines,
if (rc != 0)
return rc;
+ down_read(&machines->lock);
for (nd = rb_first_cached(&machines->guests); nd; nd = rb_next(nd)) {
struct machine *machine = rb_entry(nd, struct machine, rb_node);
rc = machine__for_each_thread(machine, fn, priv);
if (rc != 0)
- return rc;
+ break;
}
+ up_read(&machines->lock);
return rc;
}
diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h
index 973a59f7c26a..147ce343e370 100644
--- a/tools/perf/util/machine.h
+++ b/tools/perf/util/machine.h
@@ -151,6 +151,7 @@ typedef void (*machine__process_t)(struct machine *machine, void *data);
struct machines {
/** @session: back link to owning session if there is one. */
struct perf_session *session;
+ struct rw_semaphore lock;
struct machine host;
struct rb_root_cached guests;
};
--
2.56.0.313.g2479322e68-goog
^ permalink raw reply [flat|nested] 36+ messages in thread
* [PATCH v2 15/18] perf symbol: Add memory shrinking for symbols, DSOs, and machines
2026-10-27 23:14 ` [PATCH v2 00/18] perf symbol: Reference counting, sorted arrays, and memory shrinking Ian Rogers
` (13 preceding siblings ...)
2026-10-27 23:15 ` [PATCH v2 14/18] perf machine: Protect machines->guests rbtree with a rw_semaphore Ian Rogers
@ 2026-10-27 23:15 ` Ian Rogers
2026-10-27 23:15 ` [PATCH v2 16/18] perf session: Add dso__session() and session-scoped allocation wrappers Ian Rogers
` (2 subsequent siblings)
17 siblings, 0 replies; 36+ messages in thread
From: Ian Rogers @ 2026-10-27 23:15 UTC (permalink / raw)
To: irogers, acme, namhyung
Cc: adrian.hunter, haghdoost, james.clark, jolsa, linux-kernel,
linux-perf-users, mingo, peterz
Long-running 'perf top' and large 'perf report' sessions can load full
symbol tables, cached 4KB data pages, addr2line subprocesses, and libdw
contexts across thousands of DSOs even when only a small fraction of
symbols are actively referenced by live hist_entry or callchain nodes.
Add on-demand memory shrinking functions to reclaim cached symbol, DSO,
and debuginfo state under memory pressure:
- symbols__shrink(): Evict symbols whose only reference is the struct
symbols container (refcnt == 1 and not annotated) while retaining
symbols actively referenced by callers (e.g. hist_entry, map_symbol,
callchain nodes, or annotations). Mark the symbol table as shrunk and
clear dso->loaded when symbols are evicted so subsequent map__load()
calls reload the DSO under dso__lock(dso) and merge retained symbol
instances back in place.
- dso__shrink(): Close open file descriptors, free cached 4KB data
pages (on file-backed user DSOs), addr2line handles, and libdw Dwfl
contexts, and shrink reloadable user DSO symbol tables.
- dsos__shrink(), machine__shrink(), machines__shrink(), and
perf_session__shrink(): Shrink all DSOs across host and guest
machines, evict unreferenced user DSOs with no remaining symbols, and
return free heap pages via malloc_trim(0) on glibc.
All shrinking paths use non-blocking trylocks and perform zero heap
allocations so they can be safely invoked from OOM recovery paths.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/dso.c | 192 ++++++++++++++++++--
tools/perf/util/dso.h | 24 ++-
tools/perf/util/dsos.c | 68 +++++++
tools/perf/util/dsos.h | 2 +
tools/perf/util/machine.c | 32 ++++
tools/perf/util/machine.h | 3 +
tools/perf/util/session.c | 8 +
tools/perf/util/session.h | 2 +
tools/perf/util/symbol-elf.c | 2 +-
tools/perf/util/symbol.c | 332 ++++++++++++++++++++++++++++++-----
tools/perf/util/symbol.h | 6 +
11 files changed, 609 insertions(+), 62 deletions(-)
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c
index 35a5c65030af..be257c77ab0a 100644
--- a/tools/perf/util/dso.c
+++ b/tools/perf/util/dso.c
@@ -1075,12 +1075,12 @@ static int bpf_size(struct dso *dso)
#endif // HAVE_LIBBPF_SUPPORT
static void
-dso_cache__free(struct dso *dso)
+__dso_cache__free(struct dso *dso)
+ EXCLUSIVE_LOCKS_REQUIRED(dso__lock(dso))
{
struct rb_root *root = &dso__data(dso)->cache;
struct rb_node *next = rb_first(root);
- mutex_lock(dso__lock(dso));
while (next) {
struct dso_cache *cache;
@@ -1089,6 +1089,13 @@ dso_cache__free(struct dso *dso)
rb_erase(&cache->rb_node, root);
free(cache);
}
+}
+
+static void
+dso_cache__free(struct dso *dso)
+{
+ mutex_lock(dso__lock(dso));
+ __dso_cache__free(dso);
mutex_unlock(dso__lock(dso));
}
@@ -1243,27 +1250,34 @@ static struct dso_cache *dso_cache__populate(struct dso *dso,
return cache;
}
-static struct dso_cache *dso_cache__find(struct dso *dso,
- struct machine *machine,
- u64 offset,
- ssize_t *ret)
-{
- struct dso_cache *cache = __dso_cache__find(dso, offset);
-
- return cache ? cache : dso_cache__populate(dso, machine, offset, ret);
-}
-
static ssize_t dso_cache_io(struct dso *dso, struct machine *machine,
u64 offset, u8 *data, ssize_t size, bool out)
{
struct dso_cache *cache;
ssize_t ret = 0;
- cache = dso_cache__find(dso, machine, offset, &ret);
- if (!cache)
- return ret;
+ mutex_lock(dso__lock(dso));
+ cache = __dso_cache__find(dso, offset);
+ if (!cache) {
+ mutex_unlock(dso__lock(dso));
+ cache = dso_cache__populate(dso, machine, offset, &ret);
+ if (!cache)
+ return ret;
+ mutex_lock(dso__lock(dso));
+ /*
+ * Re-lookup under dso__lock(dso) in case dso__shrink() freed
+ * the cache while the lock was dropped.
+ */
+ cache = __dso_cache__find(dso, offset);
+ if (!cache) {
+ mutex_unlock(dso__lock(dso));
+ return -EAGAIN;
+ }
+ }
- return dso_cache__memcpy(cache, offset, data, size, out);
+ ret = dso_cache__memcpy(cache, offset, data, size, out);
+ mutex_unlock(dso__lock(dso));
+ return ret;
}
/*
@@ -1803,7 +1817,8 @@ int dso__name_len(const struct dso *dso)
bool dso__loaded(const struct dso *dso)
{
- return RC_CHK_ACCESS(dso)->loaded;
+ return atomic_load_explicit(&RC_CHK_ACCESS(dso)->loaded, memory_order_acquire) &&
+ !dso__symbols_shrunk(dso);
}
bool dso__sorted_by_name(const struct dso *dso)
@@ -1835,7 +1850,7 @@ struct dso *dso__new_id(const char *name, const struct dso_id *id)
dso->binary_type = DSO_BINARY_TYPE__NOT_FOUND;
dso->dbginfo_type = DSO_BINARY_TYPE__NOT_FOUND;
dso->is_64_bit = (sizeof(void *) == 8);
- dso->loaded = 0;
+ atomic_init(&dso->loaded, false);
dso->rel = 0;
dso->has_srcline = 1;
dso->a2l_fails = 1;
@@ -1896,6 +1911,144 @@ void dso__delete(struct dso *dso)
RC_CHK_FREE(dso);
}
+bool dso__can_shrink_symbols(const struct dso *dso)
+{
+ const char *name;
+
+ if (!dso || (!dso__loaded(dso) && !dso__symbols_shrunk(dso)))
+ return false;
+
+ if (dso__kernel(dso) || dso__is_kmod(dso))
+ return false;
+
+ if (dso__is_vdso(dso))
+ return false;
+
+ switch (dso__binary_type(dso)) {
+ case DSO_BINARY_TYPE__KALLSYMS:
+ case DSO_BINARY_TYPE__GUEST_KALLSYMS:
+ case DSO_BINARY_TYPE__VMLINUX:
+ case DSO_BINARY_TYPE__GUEST_VMLINUX:
+ case DSO_BINARY_TYPE__KCORE:
+ case DSO_BINARY_TYPE__GUEST_KCORE:
+ case DSO_BINARY_TYPE__BPF_PROG_INFO:
+ case DSO_BINARY_TYPE__BPF_IMAGE:
+ case DSO_BINARY_TYPE__OOL:
+ case DSO_BINARY_TYPE__JAVA_JIT:
+ case DSO_BINARY_TYPE__GUEST_KMODULE:
+ case DSO_BINARY_TYPE__GUEST_KMODULE_COMP:
+ case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE:
+ case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP:
+ return false;
+ case DSO_BINARY_TYPE__DEBUGLINK:
+ case DSO_BINARY_TYPE__BUILD_ID_CACHE:
+ case DSO_BINARY_TYPE__BUILD_ID_CACHE_DEBUGINFO:
+ case DSO_BINARY_TYPE__FEDORA_DEBUGINFO:
+ case DSO_BINARY_TYPE__UBUNTU_DEBUGINFO:
+ case DSO_BINARY_TYPE__MIXEDUP_UBUNTU_DEBUGINFO:
+ case DSO_BINARY_TYPE__BUILDID_DEBUGINFO:
+ case DSO_BINARY_TYPE__GNU_DEBUGDATA:
+ case DSO_BINARY_TYPE__SYSTEM_PATH_DSO:
+ case DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO:
+ case DSO_BINARY_TYPE__NOT_FOUND:
+ default:
+ break;
+ }
+
+ switch (dso__symtab_type(dso)) {
+ case DSO_BINARY_TYPE__KALLSYMS:
+ case DSO_BINARY_TYPE__GUEST_KALLSYMS:
+ case DSO_BINARY_TYPE__VMLINUX:
+ case DSO_BINARY_TYPE__GUEST_VMLINUX:
+ case DSO_BINARY_TYPE__KCORE:
+ case DSO_BINARY_TYPE__GUEST_KCORE:
+ case DSO_BINARY_TYPE__BPF_PROG_INFO:
+ case DSO_BINARY_TYPE__BPF_IMAGE:
+ case DSO_BINARY_TYPE__OOL:
+ case DSO_BINARY_TYPE__JAVA_JIT:
+ case DSO_BINARY_TYPE__GUEST_KMODULE:
+ case DSO_BINARY_TYPE__GUEST_KMODULE_COMP:
+ case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE:
+ case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP:
+ case DSO_BINARY_TYPE__NOT_FOUND:
+ return false;
+ case DSO_BINARY_TYPE__DEBUGLINK:
+ case DSO_BINARY_TYPE__BUILD_ID_CACHE:
+ case DSO_BINARY_TYPE__BUILD_ID_CACHE_DEBUGINFO:
+ case DSO_BINARY_TYPE__FEDORA_DEBUGINFO:
+ case DSO_BINARY_TYPE__UBUNTU_DEBUGINFO:
+ case DSO_BINARY_TYPE__MIXEDUP_UBUNTU_DEBUGINFO:
+ case DSO_BINARY_TYPE__BUILDID_DEBUGINFO:
+ case DSO_BINARY_TYPE__GNU_DEBUGDATA:
+ case DSO_BINARY_TYPE__SYSTEM_PATH_DSO:
+ case DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO:
+ default:
+ break;
+ }
+
+ name = dso__long_name(dso);
+ if (!name || name[0] == '[' || is_perf_pid_map_name(name) ||
+ strstr(name, " (deleted)") != NULL)
+ return false;
+
+ return true;
+}
+
+size_t dso__shrink(struct dso *dso)
+ NO_THREAD_SAFETY_ANALYSIS
+{
+ size_t freed = 0;
+
+ if (!dso)
+ return 0;
+
+ if (mutex_trylock(dso__data_open_lock())) {
+ if (dso__data(dso)->fd >= 0) {
+ close_dso(dso);
+ freed++;
+ }
+ mutex_unlock(dso__data_open_lock());
+ }
+
+ if (!mutex_trylock(dso__lock(dso)))
+ return freed;
+
+ if (!dso__kernel(dso) && dso__binary_type(dso) != DSO_BINARY_TYPE__OOL &&
+ !RB_EMPTY_ROOT(&dso__data(dso)->cache)) {
+ __dso_cache__free(dso);
+ freed++;
+ }
+ if (dso__a2l(dso)) {
+ dso__free_a2l(dso);
+ freed++;
+ }
+#ifdef HAVE_LIBBFD_SUPPORT
+ if (dso__a2l_libbfd(dso)) {
+ dso__free_a2l_libbfd(dso);
+ freed++;
+ }
+#endif
+#ifdef HAVE_LIBDW_SUPPORT
+ if (dso__libdw(dso)) {
+ dso__free_libdw(dso);
+ freed++;
+ }
+#endif
+
+ if (dso__can_shrink_symbols(dso)) {
+ size_t sym_freed = symbols__shrink(dso__symbols(dso));
+
+ if (sym_freed > 0) {
+ atomic_store_explicit(&RC_CHK_ACCESS(dso)->loaded, false,
+ memory_order_release);
+ freed += sym_freed;
+ }
+ }
+
+ mutex_unlock(dso__lock(dso));
+ return freed;
+}
+
struct dso *dso__get(struct dso *dso)
{
struct dso *result;
@@ -1909,7 +2062,8 @@ struct dso *dso__get(struct dso *dso)
void dso__put(struct dso *dso)
{
#ifdef REFCNT_CHECKING
- if (dso && dso__data(dso) && refcount_read(&RC_CHK_ACCESS(dso)->refcnt) == 2)
+ if (dso && dso__data(dso) && dso__data(dso)->fd >= 0 &&
+ refcount_read(&RC_CHK_ACCESS(dso)->refcnt) == 2)
dso__data_close(dso);
#endif
if (dso && refcount_dec_and_test(&RC_CHK_ACCESS(dso)->refcnt))
diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h
index f23cf9957aec..64a118ea2524 100644
--- a/tools/perf/util/dso.h
+++ b/tools/perf/util/dso.h
@@ -346,7 +346,7 @@ DECLARE_RC_STRUCT(dso) {
u8 is_64_bit:1;
u8 bintype_searched:1;
u8 debuginfo_searched:1;
- bool loaded;
+ _Atomic bool loaded;
u8 rel;
char name[];
};
@@ -602,7 +602,7 @@ static inline enum dso_load_errno *dso__load_errno(struct dso *dso)
static inline void dso__set_loaded(struct dso *dso)
{
- RC_CHK_ACCESS(dso)->loaded = true;
+ atomic_store_explicit(&RC_CHK_ACCESS(dso)->loaded, true, memory_order_release);
}
static inline struct mutex *dso__lock(struct dso *dso)
@@ -783,6 +783,26 @@ static inline bool dso__has_symbols(const struct dso *dso)
return RC_CHK_ACCESS(dso)->symbols.cnt > 0;
}
+static inline bool dso__symbols_shrunk(const struct dso *dso)
+{
+ return atomic_load_explicit(&RC_CHK_ACCESS(dso)->symbols.shrunk,
+ memory_order_acquire);
+}
+
+static inline void dso__set_symbols_shrunk(struct dso *dso, bool val)
+{
+ atomic_store_explicit(&RC_CHK_ACCESS(dso)->symbols.shrunk, val,
+ memory_order_release);
+}
+
+static inline refcount_t *dso__refcnt(struct dso *dso)
+{
+ return &RC_CHK_ACCESS(dso)->refcnt;
+}
+
+bool dso__can_shrink_symbols(const struct dso *dso);
+size_t dso__shrink(struct dso *dso);
+
char *dso__filename_with_chroot(const struct dso *dso, const char *filename);
bool dso__sorted_by_name(const struct dso *dso);
diff --git a/tools/perf/util/dsos.c b/tools/perf/util/dsos.c
index e927e707abac..30ecc9a19244 100644
--- a/tools/perf/util/dsos.c
+++ b/tools/perf/util/dsos.c
@@ -6,6 +6,9 @@
#include "vdso.h"
#include "namespaces.h"
#include <errno.h>
+#ifdef __GLIBC__
+#include <malloc.h>
+#endif
#include <stdlib.h>
#include <string.h>
#include <symbol.h> // filename__read_build_id
@@ -493,3 +496,68 @@ int dsos__for_each_dso(struct dsos *dsos, int (*cb)(struct dso *dso, void *data)
up_read(&dsos->lock);
return err;
}
+
+size_t dsos__shrink(struct dsos *dsos)
+ NO_THREAD_SAFETY_ANALYSIS
+{
+ unsigned int write_idx = 0;
+ size_t freed = 0;
+ bool any_removed = false;
+
+ if (!down_write_trylock(&dsos->lock))
+ return 0;
+
+ for (unsigned int read_idx = 0; read_idx < dsos->cnt; read_idx++) {
+ struct dso *dso = dsos->dsos[read_idx];
+
+ freed += dso__shrink(dso);
+
+ if (!dso__has_symbols(dso) &&
+ !dso__header_build_id(dso) &&
+ !dso__hit(dso) &&
+ !dso__kernel(dso) &&
+ refcount_read(dso__refcnt(dso)) == 1 &&
+ mutex_trylock(&_dso__data_open_lock)) {
+ bool can_evict = dso__data(dso)->fd < 0 &&
+ list_empty(&dso__data(dso)->open_entry) &&
+ mutex_trylock(dso__lock(dso));
+
+ if (can_evict)
+ mutex_unlock(dso__lock(dso));
+ mutex_unlock(&_dso__data_open_lock);
+ if (can_evict) {
+ dso__set_dsos(dso, NULL);
+ dso__put(dso);
+ any_removed = true;
+ freed++;
+ continue;
+ }
+ }
+ dsos->dsos[write_idx++] = dso;
+ }
+
+ dsos->cnt = write_idx;
+ if (any_removed) {
+ if (write_idx == 0) {
+ zfree(&dsos->dsos);
+ dsos->allocated = 0;
+ dsos->sorted = true;
+ } else if (write_idx < dsos->allocated / 2) {
+ struct dso **shrunk = realloc(dsos->dsos,
+ write_idx * sizeof(struct dso *));
+ if (shrunk) {
+ dsos->dsos = shrunk;
+ dsos->allocated = write_idx;
+ }
+ }
+ }
+
+ up_write(&dsos->lock);
+
+#ifdef __GLIBC__
+ if (freed > 0)
+ malloc_trim(0);
+#endif
+
+ return freed;
+}
diff --git a/tools/perf/util/dsos.h b/tools/perf/util/dsos.h
index a26774950866..151efdba2aa4 100644
--- a/tools/perf/util/dsos.h
+++ b/tools/perf/util/dsos.h
@@ -49,4 +49,6 @@ struct dso *dsos__find_kernel_dso(struct dsos *dsos);
int dsos__for_each_dso(struct dsos *dsos, int (*cb)(struct dso *dso, void *data), void *data);
+size_t dsos__shrink(struct dsos *dsos);
+
#endif /* __PERF_DSOS */
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index fb83da8ef09b..8d0c545ee544 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -3446,3 +3446,35 @@ int machine__hit_all_dsos(struct machine *machine)
{
return dsos__hit_all(&machine->dsos);
}
+
+size_t machine__shrink(struct machine *machine)
+{
+ if (!machine)
+ return 0;
+
+ return dsos__shrink(&machine->dsos);
+}
+
+size_t machines__shrink(struct machines *machines)
+ NO_THREAD_SAFETY_ANALYSIS
+{
+ struct rb_node *nd;
+ size_t freed;
+
+ if (!machines)
+ return 0;
+
+ freed = machine__shrink(&machines->host);
+
+ if (!down_read_trylock(&machines->lock))
+ return freed;
+
+ for (nd = rb_first_cached(&machines->guests); nd; nd = rb_next(nd)) {
+ struct machine *pos = rb_entry(nd, struct machine, rb_node);
+
+ freed += machine__shrink(pos);
+ }
+ up_read(&machines->lock);
+
+ return freed;
+}
diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h
index 147ce343e370..1d1f46a01a38 100644
--- a/tools/perf/util/machine.h
+++ b/tools/perf/util/machine.h
@@ -337,4 +337,7 @@ int machine__resolve(struct machine *machine, struct addr_location *al,
int machine__hit_all_dsos(struct machine *machine);
+size_t machine__shrink(struct machine *machine);
+size_t machines__shrink(struct machines *machines);
+
#endif /* __PERF_MACHINE_H */
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
index 56a5e57ec512..3f35c7affc29 100644
--- a/tools/perf/util/session.c
+++ b/tools/perf/util/session.c
@@ -4266,3 +4266,11 @@ uint16_t perf_session__e_machine(struct perf_session *session, uint32_t *e_flags
*/
return perf_env__e_machine_nocache(env, e_flags);
}
+
+size_t perf_session__shrink(struct perf_session *session)
+{
+ if (!session)
+ return 0;
+
+ return machines__shrink(&session->machines);
+}
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h
index ac5803d5fb4e..fdec65fcfee6 100644
--- a/tools/perf/util/session.h
+++ b/tools/perf/util/session.h
@@ -217,4 +217,6 @@ int perf_event__process_finished_round(const struct perf_tool *tool,
struct perf_env *perf_session__env(struct perf_session *session);
uint16_t perf_session__e_machine(struct perf_session *session, uint32_t *e_flags);
+size_t perf_session__shrink(struct perf_session *session);
+
#endif /* __PERF_SESSION_H */
diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c
index 6082136c9662..946a2426933c 100644
--- a/tools/perf/util/symbol-elf.c
+++ b/tools/perf/util/symbol-elf.c
@@ -1973,7 +1973,7 @@ int dso__load_sym(struct dso *dso, struct map *map, struct symsrc *syms_ss,
* Modules may already have symbols from kallsyms, but those symbols
* have the wrong values for the dso maps, so remove them.
*/
- if (kmodule && syms_ss->symtab)
+ if (kmodule && syms_ss->symtab && !dso__symbols_shrunk(dso))
symbols__delete(dso__symbols(dso));
if (!syms_ss->symtab) {
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 7f1fcd5c64b2..e5338c8e03dc 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -164,6 +164,18 @@ static int choose_best_symbol(struct symbol *syma, struct symbol *symb)
s64 b;
size_t na, nb;
+ /*
+ * Prefer a symbol that already has external references (e.g. retained
+ * across a symbols__shrink() and subsequent reload) so existing
+ * callers continue pointing to the canonical symbol in the DSO.
+ */
+ a = refcount_read(symbol__refcnt(syma)) > 1 || symbol__annotated(syma);
+ b = refcount_read(symbol__refcnt(symb)) > 1 || symbol__annotated(symb);
+ if (a && !b)
+ return SYMBOL_A;
+ if (b && !a)
+ return SYMBOL_B;
+
/* Prefer a symbol with non zero length */
a = symbol__end(syma) - symbol__start(syma);
b = symbol__end(symb) - symbol__start(symb);
@@ -225,6 +237,7 @@ void symbols__init(struct symbols *symbols)
symbols->num_by_name = 0;
symbols->sorted = true;
symbols->sorted_by_name = false;
+ atomic_init(&symbols->shrunk, false);
}
static void symbols__delete_locked(struct symbols *symbols)
@@ -240,6 +253,7 @@ static void symbols__delete_locked(struct symbols *symbols)
symbols->num_by_name = 0;
symbols->sorted = true;
symbols->sorted_by_name = false;
+ atomic_store_explicit(&symbols->shrunk, false, memory_order_release);
}
void symbols__delete(struct symbols *symbols)
@@ -261,6 +275,70 @@ void symbols__exit(struct symbols *symbols)
exit_rwsem(&symbols->lock);
}
+/*
+ * Evict symbols whose only reference is the @symbols array itself
+ * (refcnt == 1), retaining symbols referenced by active callers (e.g.
+ * hist_entry, map_symbol, or callchain nodes with refcnt > 1).
+ *
+ * Uses down_write_trylock() so this is safe to call from OOM recovery
+ * paths where the current thread might already hold a lock on @symbols.
+ *
+ * Returns the number of symbols evicted.
+ */
+size_t symbols__shrink(struct symbols *symbols)
+{
+ unsigned int write_idx = 0;
+ unsigned int old_sorted_cnt;
+ unsigned int new_sorted_cnt = 0;
+ size_t freed = 0;
+
+ if (!down_write_trylock(&symbols->lock))
+ return 0;
+
+ old_sorted_cnt = symbols->sorted ? symbols->cnt : symbols->sorted_cnt;
+
+ for (unsigned int read_idx = 0; read_idx < symbols->cnt; read_idx++) {
+ struct symbol *sym = symbols->symbols[read_idx];
+
+ if (refcount_read(symbol__refcnt(sym)) > 1 || symbol__annotated(sym)) {
+ if (read_idx < old_sorted_cnt)
+ new_sorted_cnt++;
+ symbols->symbols[write_idx++] = sym;
+ } else {
+ symbol__put(sym);
+ freed++;
+ }
+ }
+
+ symbols->cnt = write_idx;
+ symbols->sorted_cnt = new_sorted_cnt;
+ if (new_sorted_cnt == write_idx)
+ symbols->sorted = true;
+
+ if (freed > 0) {
+ zfree(&symbols->symbols_by_name);
+ symbols->num_by_name = 0;
+ symbols->sorted_by_name = false;
+ atomic_store_explicit(&symbols->shrunk, true, memory_order_release);
+
+ if (write_idx == 0) {
+ zfree(&symbols->symbols);
+ symbols->allocated = 0;
+ symbols->sorted = true;
+ } else if (write_idx < symbols->allocated / 2) {
+ struct symbol **shrunk = realloc(symbols->symbols,
+ write_idx * sizeof(struct symbol *));
+ if (shrunk) {
+ symbols->symbols = shrunk;
+ symbols->allocated = write_idx;
+ }
+ }
+ }
+
+ up_write(&symbols->lock);
+ return freed;
+}
+
static inline bool symbol__less_or_equal(const struct symbol *a, const struct symbol *b)
{
if (symbol__start(a) != symbol__start(b))
@@ -602,9 +680,7 @@ void symbols__sort_read_lock(struct symbols *symbols)
void symbols__fixup_duplicate(struct symbols *symbols)
{
unsigned int write_idx = 0;
-
- if (symbol_conf.allow_aliases)
- return;
+ unsigned int group_start = 0;
down_write(&symbols->lock);
symbols__sort_locked(symbols);
@@ -614,14 +690,37 @@ void symbols__fixup_duplicate(struct symbols *symbols)
}
for (unsigned int read_idx = 1; read_idx < symbols->cnt; read_idx++) {
- struct symbol *curr = symbols->symbols[write_idx];
struct symbol *next = symbols->symbols[read_idx];
+ struct symbol *curr;
+ unsigned int match_idx = write_idx;
+ bool matched = false;
- if (symbol__start(curr) != symbol__start(next)) {
+ if (symbol__start(symbols->symbols[group_start]) != symbol__start(next)) {
symbols->symbols[++write_idx] = next;
+ group_start = write_idx;
continue;
}
+ if (!symbol_conf.allow_aliases) {
+ matched = true;
+ } else {
+ for (unsigned int k = group_start; k <= write_idx; k++) {
+ if (strcmp(symbol__name(symbols->symbols[k]),
+ symbol__name(next)) == 0) {
+ match_idx = k;
+ matched = true;
+ break;
+ }
+ }
+ }
+
+ if (!matched) {
+ symbols->symbols[++write_idx] = next;
+ continue;
+ }
+
+ curr = symbols->symbols[match_idx];
+
if (choose_best_symbol(curr, next) == SYMBOL_A) {
if (symbol__type(next) == STT_GNU_IFUNC)
symbol__set_ifunc_alias(curr, true);
@@ -630,7 +729,13 @@ void symbols__fixup_duplicate(struct symbols *symbols)
if (symbol__type(curr) == STT_GNU_IFUNC)
symbol__set_ifunc_alias(next, true);
symbol__put(curr);
- symbols->symbols[write_idx] = next;
+ symbols->symbols[match_idx] = next;
+ while (match_idx < write_idx &&
+ !symbol__less_or_equal(next, symbols->symbols[match_idx + 1])) {
+ symbols->symbols[match_idx] = symbols->symbols[match_idx + 1];
+ symbols->symbols[match_idx + 1] = next;
+ match_idx++;
+ }
}
}
symbols->cnt = write_idx + 1;
@@ -676,44 +781,47 @@ void symbols__fixup_end(struct symbols *symbols, bool is_kallsyms)
* like in:
* ffffffffc1937000 T hdmi_driver_init [snd_hda_codec_hdmi]
*/
- if (symbol__end(prev) == symbol__start(prev) &&
- symbol__start(prev) != symbol__start(curr)) {
- const char *prev_mod;
- const char *curr_mod;
+ if (symbol__start(prev) != symbol__start(curr)) {
+ if (symbol__end(prev) == symbol__start(prev)) {
+ const char *prev_mod;
+ const char *curr_mod;
- if (!is_kallsyms) {
- symbol__set_end(prev, symbol__start(curr));
- } else {
- prev_mod = strchr(symbol__name(prev), '[');
- curr_mod = strchr(symbol__name(curr), '[');
-
- /* Last kernel/module symbol mapped to end of page */
- if (!prev_mod != !curr_mod) {
- symbol__set_end(prev,
- roundup(symbol__end(prev) + 4096, 4096));
- /* Last symbol in the previous module */
- } else if (prev_mod && strcmp(prev_mod, curr_mod)) {
- symbol__set_end(prev,
- roundup(symbol__end(prev) + 4096, 4096));
- } else {
+ if (!is_kallsyms) {
symbol__set_end(prev, symbol__start(curr));
- }
+ } else {
+ prev_mod = strchr(symbol__name(prev), '[');
+ curr_mod = strchr(symbol__name(curr), '[');
+
+ /* Last kernel/module symbol mapped to end of page */
+ if (!prev_mod != !curr_mod) {
+ symbol__set_end(prev,
+ roundup(symbol__end(prev) + 4096,
+ 4096));
+ /* Last symbol in the previous module */
+ } else if (prev_mod && strcmp(prev_mod, curr_mod)) {
+ symbol__set_end(prev,
+ roundup(symbol__end(prev) + 4096,
+ 4096));
+ } else {
+ symbol__set_end(prev, symbol__start(curr));
+ }
- /* The next module can start within that page */
- if (symbol__end(prev) > symbol__start(curr))
- symbol__set_end(prev, symbol__start(curr));
+ /* The next module can start within that page */
+ if (symbol__end(prev) > symbol__start(curr))
+ symbol__set_end(prev, symbol__start(curr));
- pr_debug4("%s sym:%s end:%#" PRIx64 "\n",
- __func__, symbol__name(prev), symbol__end(prev));
- }
+ pr_debug4("%s sym:%s end:%#" PRIx64 "\n",
+ __func__, symbol__name(prev), symbol__end(prev));
+ }
- for (unsigned int k = i - 1; k > 0; k--) {
- struct symbol *same = symbols->symbols[k - 1];
+ for (unsigned int k = i - 1; k > 0; k--) {
+ struct symbol *same = symbols->symbols[k - 1];
- if (symbol__start(same) != symbol__start(prev) ||
- symbol__end(same) != symbol__start(same))
- break;
- symbol__set_end(same, symbol__end(prev));
+ if (symbol__start(same) != symbol__start(prev) ||
+ symbol__end(same) != symbol__start(same))
+ break;
+ symbol__set_end(same, symbol__end(prev));
+ }
}
}
}
@@ -860,6 +968,118 @@ static int __symbols__insert(struct symbols *symbols, struct symbol *sym)
return 0;
}
+static void symbols__detach_retained(struct symbols *symbols,
+ struct symbol ***retained,
+ unsigned int *retained_cnt,
+ unsigned int *retained_allocated)
+{
+ down_write(&symbols->lock);
+ symbols__sort_locked(symbols);
+ *retained = symbols->symbols;
+ *retained_cnt = symbols->cnt;
+ *retained_allocated = symbols->allocated;
+ symbols->symbols = NULL;
+ symbols->cnt = 0;
+ symbols->sorted_cnt = 0;
+ symbols->allocated = 0;
+ zfree(&symbols->symbols_by_name);
+ symbols->num_by_name = 0;
+ symbols->sorted = true;
+ symbols->sorted_by_name = false;
+ up_write(&symbols->lock);
+}
+
+static void symbols__restore_retained(struct symbols *symbols,
+ struct symbol **retained,
+ unsigned int retained_cnt,
+ unsigned int retained_allocated)
+{
+ down_write(&symbols->lock);
+ symbols__delete_locked(symbols);
+ symbols->symbols = retained;
+ symbols->cnt = retained_cnt;
+ symbols->sorted_cnt = retained_cnt;
+ symbols->allocated = retained_allocated;
+ symbols->sorted = true;
+ symbols__sort_locked(symbols);
+ atomic_store_explicit(&symbols->shrunk, false, memory_order_release);
+ up_write(&symbols->lock);
+}
+
+static void symbols__merge_retained(struct symbols *symbols,
+ struct symbol **retained,
+ unsigned int retained_cnt)
+{
+ unsigned int fresh_cnt;
+ bool need_sort = false;
+
+ if (!retained)
+ return;
+
+ down_write(&symbols->lock);
+ symbols__sort_locked(symbols);
+ fresh_cnt = symbols->cnt;
+
+ for (unsigned int i = 0; i < retained_cnt; i++) {
+ struct symbol *ret_sym = retained[i];
+ u64 start = symbol__start(ret_sym);
+ unsigned int low = 0, high = fresh_cnt;
+ unsigned int match_idx = fresh_cnt;
+
+ while (low < high) {
+ unsigned int mid = low + (high - low) / 2;
+
+ if (symbol__start(symbols->symbols[mid]) < start)
+ low = mid + 1;
+ else
+ high = mid;
+ }
+
+ for (unsigned int k = low;
+ k < fresh_cnt && symbol__start(symbols->symbols[k]) == start;
+ k++) {
+ bool already_retained = false;
+
+ if (strcmp(symbol__name(symbols->symbols[k]),
+ symbol__name(ret_sym)) != 0)
+ continue;
+
+ for (unsigned int j = 0; j < i; j++) {
+ if (RC_CHK_EQUAL(symbols->symbols[k], retained[j])) {
+ already_retained = true;
+ break;
+ }
+ }
+ if (!already_retained) {
+ match_idx = k;
+ break;
+ }
+ }
+
+ if (match_idx < fresh_cnt) {
+ struct symbol *fresh_sym = symbols->symbols[match_idx];
+
+ symbol__set_end(ret_sym, symbol__end(fresh_sym));
+ if (symbol__ifunc_alias(fresh_sym))
+ symbol__set_ifunc_alias(ret_sym, true);
+ symbols->symbols[match_idx] = ret_sym;
+ symbol__put(fresh_sym);
+ } else if (__symbols__insert(symbols, ret_sym) == 0) {
+ need_sort = true;
+ }
+ }
+
+ if (need_sort)
+ symbols__sort_locked(symbols);
+
+ zfree(&symbols->symbols_by_name);
+ symbols->num_by_name = 0;
+ symbols->sorted_by_name = false;
+ atomic_store_explicit(&symbols->shrunk, false, memory_order_release);
+ up_write(&symbols->lock);
+ free(retained);
+}
+
static int __symbols__insert_sorted(struct symbols *symbols, struct symbol *sym)
{
unsigned int idx;
@@ -1449,6 +1669,7 @@ static int maps__split_kallsyms_for_kcore(struct maps *kmaps, struct dso *dso)
zfree(&root->symbols_by_name);
root->num_by_name = 0;
root->sorted_by_name = false;
+ atomic_store_explicit(&root->shrunk, false, memory_order_release);
up_write(&root->lock);
for (unsigned int i = 0; i < old_cnt; i++) {
@@ -1679,6 +1900,7 @@ static int maps__split_kallsyms(struct maps *kmaps, struct dso *dso, u64 delta,
zfree(&root->symbols_by_name);
root->num_by_name = 0;
root->sorted_by_name = false;
+ atomic_store_explicit(&root->shrunk, false, memory_order_release);
up_write(&root->lock);
free(old_symbols);
@@ -1711,6 +1933,7 @@ static int maps__split_kallsyms(struct maps *kmaps, struct dso *dso, u64 delta,
zfree(&root->symbols_by_name);
root->num_by_name = 0;
root->sorted_by_name = false;
+ atomic_store_explicit(&root->shrunk, false, memory_order_release);
up_write(&root->lock);
free(old_symbols);
return -1;
@@ -2388,6 +2611,11 @@ int dso__load(struct dso *dso, struct map *map)
struct symsrc *syms_ss = NULL, *runtime_ss = NULL;
bool kmod;
bool perfmap;
+ bool was_shrunk;
+ bool saved_adjust_symbols;
+ struct symbol **retained = NULL;
+ unsigned int retained_cnt = 0;
+ unsigned int retained_allocated = 0;
struct nscookie nsc;
char newmapname[PATH_MAX];
const char *map_path = dso__long_name(dso);
@@ -2408,8 +2636,15 @@ int dso__load(struct dso *dso, struct map *map)
/* check again under the dso->lock */
if (dso__loaded(dso)) {
ret = 1;
- goto out;
+ goto out_unlock;
}
+ was_shrunk = dso__symbols_shrunk(dso);
+ saved_adjust_symbols = dso__adjust_symbols(dso);
+ if (was_shrunk) {
+ symbols__detach_retained(dso__symbols(dso), &retained,
+ &retained_cnt, &retained_allocated);
+ }
+ atomic_store_explicit(&RC_CHK_ACCESS(dso)->loaded, false, memory_order_release);
kmod = dso__is_kmod(dso);
@@ -2556,6 +2791,8 @@ int dso__load(struct dso *dso, struct map *map)
nr_plt = dso__synthesize_plt_symbols(dso, runtime_ss);
if (nr_plt > 0)
ret += nr_plt;
+ if (nr_plt > 0 || !dso__symbols(dso)->sorted)
+ symbols__fixup_duplicate(dso__symbols(dso));
}
out_destroy:
@@ -2566,7 +2803,22 @@ int dso__load(struct dso *dso, struct map *map)
if (ret < 0 && strstr(dso__name(dso), " (deleted)") != NULL)
ret = 0;
out:
- dso__set_loaded(dso);
+ if (was_shrunk) {
+ if (ret > 0) {
+ symbols__merge_retained(dso__symbols(dso), retained, retained_cnt);
+ } else {
+ dso__set_adjust_symbols(dso, saved_adjust_symbols);
+ symbols__restore_retained(dso__symbols(dso), retained,
+ retained_cnt, retained_allocated);
+ if (retained_cnt > 0)
+ ret = retained_cnt;
+ }
+ dso__set_symbols_shrunk(dso, false);
+ dso__set_loaded(dso);
+ } else {
+ dso__set_loaded(dso);
+ }
+out_unlock:
mutex_unlock(dso__lock(dso));
nsinfo__mountns_exit(&nsc);
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index 066697d8da8f..7d6469d6a976 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -135,12 +135,18 @@ struct symbols {
* by name.
*/
bool sorted_by_name;
+ /**
+ * @shrunk: True when symbols have been evicted by symbols__shrink()
+ * and may need to be reloaded on demand.
+ */
+ _Atomic bool shrunk;
};
void symbol__delete(struct symbol *sym);
void symbols__init(struct symbols *symbols);
void symbols__exit(struct symbols *symbols);
void symbols__delete(struct symbols *symbols);
+size_t symbols__shrink(struct symbols *symbols);
void symbols__sort_read_lock(struct symbols *symbols) SHARED_LOCK_FUNCTION(symbols->lock);
static inline refcount_t *symbol__refcnt(struct symbol *sym)
--
2.56.0.313.g2479322e68-goog
^ permalink raw reply [flat|nested] 36+ messages in thread
* [PATCH v2 16/18] perf session: Add dso__session() and session-scoped allocation wrappers
2026-10-27 23:14 ` [PATCH v2 00/18] perf symbol: Reference counting, sorted arrays, and memory shrinking Ian Rogers
` (14 preceding siblings ...)
2026-10-27 23:15 ` [PATCH v2 15/18] perf symbol: Add memory shrinking for symbols, DSOs, and machines Ian Rogers
@ 2026-10-27 23:15 ` Ian Rogers
2026-10-27 23:15 ` [PATCH v2 17/18] perf symbol: Use session allocation wrappers in symbol, DSO, thread, and hist paths Ian Rogers
2026-10-27 23:15 ` [PATCH v2 18/18] perf test symbols: Add tests for symbol shrinking and session alloc wrappers Ian Rogers
17 siblings, 0 replies; 36+ messages in thread
From: Ian Rogers @ 2026-10-27 23:15 UTC (permalink / raw)
To: irogers, acme, namhyung
Cc: adrian.hunter, haghdoost, james.clark, jolsa, linux-kernel,
linux-perf-users, mingo, peterz
Add explicit session-scoped memory allocation wrappers:
- perf_session__malloc(session, size)
- perf_session__zalloc(session, size)
- perf_session__calloc(session, nmemb, size)
- perf_session__realloc(session, ptr, size)
- perf_session__strdup(session, s)
When an allocation fails and returns NULL, if a non-NULL struct
perf_session is provided, the wrapper invokes perf_session__shrink() to
evict unreferenced symbols, DSOs, and cached debuginfo across the
session's machines and then retries the allocation once. This avoids
the thrashing overhead of periodic heuristic shrinking while allowing
perf to recover from memory exhaustion.
Also plumb a back-pointer from struct dsos to its owning struct machine
so machine__session(machine) and dso__session(dso) can resolve the
active perf_session.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/dso.c | 7 ++++
tools/perf/util/dso.h | 2 ++
tools/perf/util/dsos.c | 1 +
tools/perf/util/dsos.h | 1 +
tools/perf/util/machine.c | 1 +
tools/perf/util/machine.h | 5 +++
tools/perf/util/session.c | 70 +++++++++++++++++++++++++++++++++++++++
tools/perf/util/session.h | 6 ++++
8 files changed, 93 insertions(+)
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c
index be257c77ab0a..c19de9ab4b24 100644
--- a/tools/perf/util/dso.c
+++ b/tools/perf/util/dso.c
@@ -1911,6 +1911,13 @@ void dso__delete(struct dso *dso)
RC_CHK_FREE(dso);
}
+struct perf_session *dso__session(const struct dso *dso)
+{
+ struct dsos *dsos = dso ? RC_CHK_ACCESS(dso)->dsos : NULL;
+
+ return dsos ? machine__session(dsos->machine) : NULL;
+}
+
bool dso__can_shrink_symbols(const struct dso *dso)
{
const char *name;
diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h
index 64a118ea2524..5fa059ba6cf1 100644
--- a/tools/perf/util/dso.h
+++ b/tools/perf/util/dso.h
@@ -19,6 +19,7 @@
struct machine;
struct map;
struct perf_env;
+struct perf_session;
#define DSO__NAME_KALLSYMS "[kernel.kallsyms]"
#define DSO__NAME_KCORE "[kernel.kcore]"
@@ -802,6 +803,7 @@ static inline refcount_t *dso__refcnt(struct dso *dso)
bool dso__can_shrink_symbols(const struct dso *dso);
size_t dso__shrink(struct dso *dso);
+struct perf_session *dso__session(const struct dso *dso);
char *dso__filename_with_chroot(const struct dso *dso, const char *filename);
diff --git a/tools/perf/util/dsos.c b/tools/perf/util/dsos.c
index 30ecc9a19244..8f3e3f3c6ce3 100644
--- a/tools/perf/util/dsos.c
+++ b/tools/perf/util/dsos.c
@@ -18,6 +18,7 @@ void dsos__init(struct dsos *dsos)
{
init_rwsem(&dsos->lock);
+ dsos->machine = NULL;
dsos->cnt = 0;
dsos->allocated = 0;
dsos->dsos = NULL;
diff --git a/tools/perf/util/dsos.h b/tools/perf/util/dsos.h
index 151efdba2aa4..d636e4c5cbaf 100644
--- a/tools/perf/util/dsos.h
+++ b/tools/perf/util/dsos.h
@@ -19,6 +19,7 @@ struct machine;
*/
struct dsos {
struct rw_semaphore lock;
+ struct machine *machine;
struct dso **dsos;
unsigned int cnt;
unsigned int allocated;
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index 8d0c545ee544..3e0d68fd5efe 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -81,6 +81,7 @@ int machine__init(struct machine *machine, const char *root_dir, pid_t pid)
memset(machine, 0, sizeof(*machine));
RB_CLEAR_NODE(&machine->rb_node);
dsos__init(&machine->dsos);
+ machine->dsos.machine = machine;
threads__init(&machine->threads);
machine->kmaps = maps__new(machine);
diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h
index 1d1f46a01a38..ab67cef67c80 100644
--- a/tools/perf/util/machine.h
+++ b/tools/perf/util/machine.h
@@ -164,6 +164,11 @@ static inline struct perf_session *machines__session(struct machines *machines)
return machines->session;
}
+static inline struct perf_session *machine__session(const struct machine *machine)
+{
+ return machine && machine->machines ? machine->machines->session : NULL;
+}
+
void machines__process_guests(struct machines *machines,
machine__process_t process, void *data);
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
index 3f35c7affc29..3d4bc9c0063a 100644
--- a/tools/perf/util/session.c
+++ b/tools/perf/util/session.c
@@ -4274,3 +4274,73 @@ size_t perf_session__shrink(struct perf_session *session)
return machines__shrink(&session->machines);
}
+
+static __thread bool perf_session__in_shrink;
+
+static bool perf_session__try_shrink(struct perf_session *session)
+{
+ size_t freed;
+
+ if (!session || perf_session__in_shrink)
+ return false;
+
+ perf_session__in_shrink = true;
+ freed = perf_session__shrink(session);
+ perf_session__in_shrink = false;
+
+ return freed > 0;
+}
+
+void *perf_session__malloc(struct perf_session *session, size_t size)
+{
+ void *ptr = malloc(size);
+
+ if (unlikely(!ptr && size != 0) && perf_session__try_shrink(session))
+ ptr = malloc(size);
+
+ return ptr;
+}
+
+void *perf_session__zalloc(struct perf_session *session, size_t size)
+{
+ void *ptr = zalloc(size);
+
+ if (unlikely(!ptr && size != 0) && perf_session__try_shrink(session))
+ ptr = zalloc(size);
+
+ return ptr;
+}
+
+void *perf_session__calloc(struct perf_session *session, size_t nmemb, size_t size)
+{
+ void *ptr = calloc(nmemb, size);
+
+ if (unlikely(!ptr && nmemb != 0 && size != 0) && perf_session__try_shrink(session))
+ ptr = calloc(nmemb, size);
+
+ return ptr;
+}
+
+void *perf_session__realloc(struct perf_session *session, void *ptr, size_t size)
+{
+ void *new_ptr = realloc(ptr, size);
+
+ if (unlikely(!new_ptr && size != 0) && perf_session__try_shrink(session))
+ new_ptr = realloc(ptr, size);
+
+ return new_ptr;
+}
+
+char *perf_session__strdup(struct perf_session *session, const char *s)
+{
+ char *dup;
+
+ if (!s)
+ return NULL;
+
+ dup = strdup(s);
+ if (unlikely(!dup) && perf_session__try_shrink(session))
+ dup = strdup(s);
+
+ return dup;
+}
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h
index fdec65fcfee6..90ae16a631b0 100644
--- a/tools/perf/util/session.h
+++ b/tools/perf/util/session.h
@@ -219,4 +219,10 @@ uint16_t perf_session__e_machine(struct perf_session *session, uint32_t *e_flags
size_t perf_session__shrink(struct perf_session *session);
+void *perf_session__malloc(struct perf_session *session, size_t size);
+void *perf_session__zalloc(struct perf_session *session, size_t size);
+void *perf_session__calloc(struct perf_session *session, size_t nmemb, size_t size);
+void *perf_session__realloc(struct perf_session *session, void *ptr, size_t size);
+char *perf_session__strdup(struct perf_session *session, const char *s);
+
#endif /* __PERF_SESSION_H */
--
2.56.0.313.g2479322e68-goog
^ permalink raw reply [flat|nested] 36+ messages in thread
* [PATCH v2 17/18] perf symbol: Use session allocation wrappers in symbol, DSO, thread, and hist paths
2026-10-27 23:14 ` [PATCH v2 00/18] perf symbol: Reference counting, sorted arrays, and memory shrinking Ian Rogers
` (15 preceding siblings ...)
2026-10-27 23:15 ` [PATCH v2 16/18] perf session: Add dso__session() and session-scoped allocation wrappers Ian Rogers
@ 2026-10-27 23:15 ` Ian Rogers
2026-10-27 23:15 ` [PATCH v2 18/18] perf test symbols: Add tests for symbol shrinking and session alloc wrappers Ian Rogers
17 siblings, 0 replies; 36+ messages in thread
From: Ian Rogers @ 2026-10-27 23:15 UTC (permalink / raw)
To: irogers, acme, namhyung
Cc: adrian.hunter, haghdoost, james.clark, jolsa, linux-kernel,
linux-perf-users, mingo, peterz
Connect the session-scoped allocation wrappers (perf_session__malloc,
perf_session__zalloc, perf_session__calloc, perf_session__realloc,
perf_session__strdup) to the primary heap allocation paths during sample
and symbol processing: symbol__new(), symbols->symbols array growth,
dso__new_id(), thread__new(), and hist_entry allocation.
In normal operation when memory is available, behavior is unchanged.
When an allocation fails with NULL, these paths now invoke
perf_session__shrink() on the owning perf_session to reclaim
unreferenced symbols and DSOs and retry the allocation once before
failing.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/dso.c | 4 ++-
tools/perf/util/dsos.c | 7 +++++
tools/perf/util/hist.c | 41 +++++++++++++++++++++---
tools/perf/util/machine.c | 8 ++++-
tools/perf/util/symbol-elf.c | 14 ++++++---
tools/perf/util/symbol.c | 60 +++++++++++++++++++++++-------------
tools/perf/util/symbol.h | 3 ++
7 files changed, 104 insertions(+), 33 deletions(-)
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c
index c19de9ab4b24..de694e119a6e 100644
--- a/tools/perf/util/dso.c
+++ b/tools/perf/util/dso.c
@@ -27,6 +27,7 @@
#include "map.h"
#include "namespaces.h"
#include "path.h"
+#include "session.h"
#include "srcline.h"
#include "string2.h"
#include "symbol.h"
@@ -1214,10 +1215,11 @@ static struct dso_cache *dso_cache__populate(struct dso *dso,
u64 offset, ssize_t *ret)
{
u64 cache_offset = offset & DSO__DATA_CACHE_MASK;
+ struct perf_session *session = machine ? machine__session(machine) : dso__session(dso);
struct dso_cache *cache;
struct dso_cache *old;
- cache = zalloc(sizeof(*cache) + DSO__DATA_CACHE_SIZE);
+ cache = perf_session__zalloc(session, sizeof(*cache) + DSO__DATA_CACHE_SIZE);
if (!cache) {
*ret = -ENOMEM;
return NULL;
diff --git a/tools/perf/util/dsos.c b/tools/perf/util/dsos.c
index 8f3e3f3c6ce3..374e1c5d2c32 100644
--- a/tools/perf/util/dsos.c
+++ b/tools/perf/util/dsos.c
@@ -2,6 +2,8 @@
#include "debug.h"
#include "dsos.h"
#include "dso.h"
+#include "machine.h"
+#include "session.h"
#include "util.h"
#include "vdso.h"
#include "namespaces.h"
@@ -350,6 +352,11 @@ struct dso *dsos__findnew_id(struct dsos *dsos, const char *name, const struct d
down_write(&dsos->lock);
dso = __dsos__findnew_id(dsos, name, id);
up_write(&dsos->lock);
+ if (!dso && dsos->machine && perf_session__shrink(machine__session(dsos->machine)) > 0) {
+ down_write(&dsos->lock);
+ dso = __dsos__findnew_id(dsos, name, id);
+ up_write(&dsos->lock);
+ }
return dso;
}
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c
index badae244638c..4242d3f6f955 100644
--- a/tools/perf/util/hist.c
+++ b/tools/perf/util/hist.c
@@ -524,16 +524,45 @@ struct hist_entry *hists__get_entry(struct hists *hists, int idx)
* histogram, sorted on item, collects periods
*/
+static struct perf_session *hist_entry__session(const struct hist_entry *he)
+{
+ struct maps *maps;
+
+ if (!he)
+ return NULL;
+
+ if (he->ms.map) {
+ struct dso *dso = map__dso(he->ms.map);
+ struct perf_session *session = dso__session(dso);
+
+ if (session)
+ return session;
+ }
+
+ maps = he->thread ? thread__maps(he->thread) : NULL;
+ if (maps) {
+ struct machine *machine = maps__machine(maps);
+ struct perf_session *session = machine__session(machine);
+
+ if (session)
+ return session;
+ }
+
+ return NULL;
+}
+
static int hist_entry__init(struct hist_entry *he,
struct hist_entry *template,
bool sample_self,
size_t callchain_size)
{
+ struct perf_session *session = hist_entry__session(template);
+
*he = *template;
he->callchain_size = callchain_size;
if (symbol_conf.cumulate_callchain) {
- he->stat_acc = malloc(sizeof(he->stat));
+ he->stat_acc = perf_session__malloc(session, sizeof(he->stat));
if (he->stat_acc == NULL)
return -ENOMEM;
memcpy(he->stat_acc, &he->stat, sizeof(he->stat));
@@ -551,7 +580,7 @@ static int hist_entry__init(struct hist_entry *he,
* sample__resolve_bstack() and will be freed after
* adding new entries. So we need to save a copy.
*/
- he->branch_info = malloc(sizeof(*he->branch_info));
+ he->branch_info = perf_session__malloc(session, sizeof(*he->branch_info));
if (he->branch_info == NULL)
goto err;
@@ -582,14 +611,14 @@ static int hist_entry__init(struct hist_entry *he,
}
if (he->srcline && he->srcline != SRCLINE_UNKNOWN) {
- he->srcline = strdup(he->srcline);
+ he->srcline = perf_session__strdup(session, he->srcline);
if (he->srcline == NULL)
goto err_rawdata;
}
if (symbol_conf.res_sample) {
- he->res_samples = calloc(symbol_conf.res_sample,
- sizeof(struct res_sample));
+ he->res_samples = perf_session__calloc(session, symbol_conf.res_sample,
+ sizeof(struct res_sample));
if (!he->res_samples)
goto err_srcline;
}
@@ -655,6 +684,8 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template,
callchain_size = sizeof(struct callchain_root);
he = ops->new(callchain_size);
+ if (!he && perf_session__shrink(hist_entry__session(template)) > 0)
+ he = ops->new(callchain_size);
if (he) {
err = hist_entry__init(he, template, sample_self, callchain_size);
if (err) {
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index 3e0d68fd5efe..f45befc35a7f 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -20,6 +20,7 @@
#include "mem-events.h"
#include "mem-info.h"
#include "path.h"
+#include "session.h"
#include "srcline.h"
#include "symbol.h"
#include "synthetic-events.h"
@@ -598,7 +599,12 @@ static struct thread *__machine__findnew_thread(struct machine *machine,
struct thread *machine__findnew_thread(struct machine *machine, pid_t pid, pid_t tid)
{
- return __machine__findnew_thread(machine, pid, tid, /*create=*/true);
+ struct thread *th = __machine__findnew_thread(machine, pid, tid, /*create=*/true);
+
+ if (!th && perf_session__shrink(machine__session(machine)) > 0)
+ th = __machine__findnew_thread(machine, pid, tid, /*create=*/true);
+
+ return th;
}
struct thread *machine__find_thread(struct machine *machine, pid_t pid,
diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c
index 946a2426933c..75139fab0748 100644
--- a/tools/perf/util/symbol-elf.c
+++ b/tools/perf/util/symbol-elf.c
@@ -15,6 +15,7 @@
#include "symbol.h"
#include "symsrc.h"
#include "machine.h"
+#include "session.h"
#include "vdso.h"
#include "debug.h"
#include "util/copyfile.h"
@@ -701,7 +702,7 @@ static int dso__synthesize_plt_got_symbols(struct dso *dso, Elf *elf,
sym = symbol__new(shdr.sh_offset + i, shdr.sh_entsize, STB_GLOBAL, STT_FUNC, buf);
if (!sym)
goto out;
- if (symbols__insert(dso__symbols(dso), sym))
+ if (symbols__insert_session(dso__symbols(dso), sym, dso__session(dso)))
goto out;
}
err = 0;
@@ -757,7 +758,7 @@ int dso__synthesize_plt_symbols(struct dso *dso, struct symsrc *ss)
plt_sym = symbol__new(shdr_plt.sh_offset, plt_header_size, STB_GLOBAL, STT_FUNC, ".plt");
if (!plt_sym)
goto out_elf_end;
- if (symbols__insert(dso__symbols(dso), plt_sym))
+ if (symbols__insert_session(dso__symbols(dso), plt_sym, dso__session(dso)))
goto out_elf_end;
/* Only x86 has .plt.got */
@@ -880,7 +881,7 @@ int dso__synthesize_plt_symbols(struct dso *dso, struct symsrc *ss)
goto out_elf_end;
plt_offset += plt_entry_size;
- if (symbols__insert(dso__symbols(dso), f))
+ if (symbols__insert_session(dso__symbols(dso), f, dso__session(dso)))
goto out_elf_end;
++nr;
}
@@ -1939,13 +1940,18 @@ dso__load_sym_internal(struct dso *dso, struct map *map, struct symsrc *syms_ss,
f = symbol__new(sym.st_value, sym.st_size,
GELF_ST_BIND(sym.st_info),
GELF_ST_TYPE(sym.st_info), elf_name);
+ if (!f && perf_session__shrink(dso__session(curr_dso)) > 0) {
+ f = symbol__new(sym.st_value, sym.st_size,
+ GELF_ST_BIND(sym.st_info),
+ GELF_ST_TYPE(sym.st_info), elf_name);
+ }
free(demangled);
if (!f)
goto out_elf_end;
arch__sym_update(f, &sym);
- if (symbols__insert(dso__symbols(curr_dso), f))
+ if (symbols__insert_session(dso__symbols(curr_dso), f, dso__session(curr_dso)))
goto out_elf_end;
nr++;
}
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index e5338c8e03dc..6bba489ff535 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -41,6 +41,7 @@
#include "namespaces.h"
#include "header.h"
#include "path.h"
+#include "session.h"
#include <linux/ctype.h>
#include <linux/log2.h>
#include <linux/zalloc.h>
@@ -938,12 +939,13 @@ static void symbol__set_idle(struct symbol *sym, bool idle)
} while (!atomic_compare_exchange_weak(&RC_CHK_ACCESS(sym)->flags, &old_flags, new_flags));
}
-static int __symbols__insert(struct symbols *symbols, struct symbol *sym)
+static int __symbols__insert(struct symbols *symbols, struct symbol *sym,
+ struct perf_session *session)
{
if (symbols->cnt == symbols->allocated) {
unsigned int to_allocate = symbols->allocated ? symbols->allocated * 2 : 32;
- struct symbol **temp = realloc(symbols->symbols,
- sizeof(struct symbol *) * to_allocate);
+ struct symbol **temp = perf_session__realloc(session, symbols->symbols,
+ sizeof(struct symbol *) * to_allocate);
if (!temp) {
symbol__put(sym);
@@ -1008,7 +1010,8 @@ static void symbols__restore_retained(struct symbols *symbols,
static void symbols__merge_retained(struct symbols *symbols,
struct symbol **retained,
- unsigned int retained_cnt)
+ unsigned int retained_cnt,
+ struct perf_session *session)
{
unsigned int fresh_cnt;
bool need_sort = false;
@@ -1064,7 +1067,7 @@ static void symbols__merge_retained(struct symbols *symbols,
symbol__set_ifunc_alias(ret_sym, true);
symbols->symbols[match_idx] = ret_sym;
symbol__put(fresh_sym);
- } else if (__symbols__insert(symbols, ret_sym) == 0) {
+ } else if (__symbols__insert(symbols, ret_sym, session) == 0) {
need_sort = true;
}
}
@@ -1080,17 +1083,18 @@ static void symbols__merge_retained(struct symbols *symbols,
free(retained);
}
-static int __symbols__insert_sorted(struct symbols *symbols, struct symbol *sym)
+static int __symbols__insert_sorted(struct symbols *symbols, struct symbol *sym,
+ struct perf_session *session)
{
unsigned int idx;
if (!symbols->sorted)
- return __symbols__insert(symbols, sym);
+ return __symbols__insert(symbols, sym, session);
if (symbols->cnt == symbols->allocated) {
unsigned int to_allocate = symbols->allocated ? symbols->allocated * 2 : 32;
- struct symbol **temp = realloc(symbols->symbols,
- sizeof(struct symbol *) * to_allocate);
+ struct symbol **temp = perf_session__realloc(session, symbols->symbols,
+ sizeof(struct symbol *) * to_allocate);
if (!temp) {
symbol__put(sym);
@@ -1118,16 +1122,22 @@ static int __symbols__insert_sorted(struct symbols *symbols, struct symbol *sym)
return 0;
}
-int symbols__insert(struct symbols *symbols, struct symbol *sym)
+int symbols__insert_session(struct symbols *symbols, struct symbol *sym,
+ struct perf_session *session)
{
int ret;
down_write(&symbols->lock);
- ret = __symbols__insert(symbols, sym);
+ ret = __symbols__insert(symbols, sym, session);
up_write(&symbols->lock);
return ret;
}
+int symbols__insert(struct symbols *symbols, struct symbol *sym)
+{
+ return symbols__insert_session(symbols, sym, NULL);
+}
+
/*
* Binary search @symbols->symbols for a symbol covering @ip. Lazily sorts
* @symbols under @symbols->lock if unsorted.
@@ -1288,7 +1298,7 @@ int dso__insert_symbol(struct dso *dso, struct symbol *sym)
int ret;
down_write(&symbols->lock);
- ret = __symbols__insert_sorted(symbols, sym);
+ ret = __symbols__insert_sorted(symbols, sym, dso__session(dso));
up_write(&symbols->lock);
return ret;
}
@@ -1633,7 +1643,7 @@ static int map__process_kallsym_symbol(void *arg, const char *name,
* We will pass the symbols to the filter later, in
* map__split_kallsyms, when we have split the maps per module
*/
- return symbols__insert(root, sym);
+ return symbols__insert_session(root, sym, dso__session(dso));
}
/*
@@ -1697,7 +1707,8 @@ static int maps__split_kallsyms_for_kcore(struct maps *kmaps, struct dso *dso)
symbol__set_end(pos, symbol__end(pos) -
(map__start(curr_map) - map__pgoff(curr_map)));
}
- if (symbols__insert(dso__symbols(curr_map_dso), pos) == 0)
+ if (symbols__insert_session(dso__symbols(curr_map_dso), pos,
+ dso__session(curr_map_dso)) == 0)
++count;
map__put(curr_map);
}
@@ -1873,7 +1884,8 @@ static int maps__split_kallsyms(struct maps *kmaps, struct dso *dso, u64 delta,
if (!RC_CHK_EQUAL(curr_map, initial_map)) {
struct dso *curr_map_dso = map__dso(curr_map);
- if (symbols__insert(dso__symbols(curr_map_dso), pos) == 0)
+ if (symbols__insert_session(dso__symbols(curr_map_dso), pos,
+ dso__session(curr_map_dso)) == 0)
++moved;
} else {
old_symbols[write_idx++] = pos;
@@ -1895,7 +1907,7 @@ static int maps__split_kallsyms(struct maps *kmaps, struct dso *dso, u64 delta,
old_symbols = NULL;
} else {
for (unsigned int j = 0; j < write_idx; j++)
- __symbols__insert(root, old_symbols[j]);
+ __symbols__insert(root, old_symbols[j], dso__session(dso));
}
zfree(&root->symbols_by_name);
root->num_by_name = 0;
@@ -1926,7 +1938,7 @@ static int maps__split_kallsyms(struct maps *kmaps, struct dso *dso, u64 delta,
old_symbols = NULL;
} else {
for (unsigned int j = 0; j < write_idx; j++)
- __symbols__insert(root, old_symbols[j]);
+ __symbols__insert(root, old_symbols[j], dso__session(dso));
root->sorted_cnt = 0;
root->sorted = false;
}
@@ -2497,7 +2509,7 @@ static int dso__load_perf_map(const char *map_path, struct dso *dso)
if (sym == NULL)
goto out_delete_line;
- if (symbols__insert(dso__symbols(dso), sym))
+ if (symbols__insert_session(dso__symbols(dso), sym, dso__session(dso)))
goto out_delete_line;
nr_syms++;
}
@@ -2674,7 +2686,7 @@ int dso__load(struct dso *dso, struct map *map)
if (machine)
root_dir = machine->root_dir;
- name = malloc(PATH_MAX);
+ name = perf_session__malloc(dso__session(dso), PATH_MAX);
if (!name)
goto out;
@@ -2750,8 +2762,11 @@ int dso__load(struct dso *dso, struct map *map)
if (!syms_ss && symsrc__has_symtab(ss)) {
syms_ss = ss;
next_slot = true;
- if (!dso__symsrc_filename(dso))
- dso__set_symsrc_filename(dso, strdup(name));
+ if (!dso__symsrc_filename(dso)) {
+ dso__set_symsrc_filename(dso,
+ perf_session__strdup(dso__session(dso),
+ name));
+ }
}
if (!runtime_ss && symsrc__possibly_runtime(ss)) {
@@ -2805,7 +2820,8 @@ int dso__load(struct dso *dso, struct map *map)
out:
if (was_shrunk) {
if (ret > 0) {
- symbols__merge_retained(dso__symbols(dso), retained, retained_cnt);
+ symbols__merge_retained(dso__symbols(dso), retained, retained_cnt,
+ dso__session(dso));
} else {
dso__set_adjust_symbols(dso, saved_adjust_symbols);
symbols__restore_retained(dso__symbols(dso), retained,
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index 7d6469d6a976..2db5eb84b268 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -33,6 +33,7 @@ struct maps;
struct option;
struct build_id;
struct perf_env;
+struct perf_session;
/*
* Ignore kernel mapping symbols, matching kernel is_mapping_symbol() logic.
@@ -376,6 +377,8 @@ int dso__synthesize_plt_symbols(struct dso *dso, struct symsrc *ss);
char *dso__demangle_sym(struct dso *dso, int kmodule, const char *elf_name);
int symbols__insert(struct symbols *symbols, struct symbol *sym);
+int symbols__insert_session(struct symbols *symbols, struct symbol *sym,
+ struct perf_session *session);
void symbols__fixup_duplicate(struct symbols *symbols);
void symbols__fixup_end(struct symbols *symbols, bool is_kallsyms);
--
2.56.0.313.g2479322e68-goog
^ permalink raw reply [flat|nested] 36+ messages in thread
* [PATCH v2 18/18] perf test symbols: Add tests for symbol shrinking and session alloc wrappers
2026-10-27 23:14 ` [PATCH v2 00/18] perf symbol: Reference counting, sorted arrays, and memory shrinking Ian Rogers
` (16 preceding siblings ...)
2026-10-27 23:15 ` [PATCH v2 17/18] perf symbol: Use session allocation wrappers in symbol, DSO, thread, and hist paths Ian Rogers
@ 2026-10-27 23:15 ` Ian Rogers
17 siblings, 0 replies; 36+ messages in thread
From: Ian Rogers @ 2026-10-27 23:15 UTC (permalink / raw)
To: irogers, acme, namhyung
Cc: adrian.hunter, haghdoost, james.clark, jolsa, linux-kernel,
linux-perf-users, mingo, peterz
Ensure the new symbol sorting, reference-counted symbol and DSO
shrinking, transparent DSO reload with retained-symbol pointer
preservation, and session-scoped allocation wrappers are verified by
'perf test symbols' (including under REFCNT_CHECKING=1).
Add unit tests to 'perf test symbols' covering:
- test_symbols_sorted_insert(): Verify ascending symbols__insert(),
out-of-order dso__insert_symbol() maintaining sorted order, and
unsorted symbols__insert() followed by sort-on-lookup in
dso__find_symbol().
- test_symbols_shrink(): Verify symbols__shrink() evicts unreferenced
symbols (refcnt == 1), retains symbols with active external references
(refcnt > 1), and sets symbols.shrunk.
- test_dsos_shrink(): Verify dsos__shrink() evicts unreferenced user
DSOs with no remaining symbols while retaining DSOs with active
external references.
- test_session_alloc_shrink(): Verify machine__session(),
dso__session(), perf_session__malloc/zalloc/calloc/realloc/strdup(),
and perf_session__shrink().
- test_dso_shrink_and_reload(): Verify dso__shrink() on a loaded ELF
binary followed by map__find_symbol() transparently reloads evicted
symbols while preserving pointer identity for retained symbols.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/tests/symbols.c | 372 +++++++++++++++++++++++++++++++++++++
1 file changed, 372 insertions(+)
diff --git a/tools/perf/tests/symbols.c b/tools/perf/tests/symbols.c
index d70adbad3978..1a773c535732 100644
--- a/tools/perf/tests/symbols.c
+++ b/tools/perf/tests/symbols.c
@@ -1,13 +1,21 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/compiler.h>
#include <linux/string.h>
+#include <linux/zalloc.h>
#include <sys/mman.h>
+#include <errno.h>
#include <inttypes.h>
#include <limits.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
#include "debug.h"
#include "dso.h"
#include "env.h"
#include "machine.h"
+#include "session.h"
#include "thread.h"
#include "symbol.h"
#include "map.h"
@@ -179,6 +187,264 @@ static int process_subdivided_dso(struct machine *machine, struct dso *dso)
return ret < 0 ? TEST_FAIL : TEST_OK;
}
+static int test_symbols_shrink(void)
+{
+ struct symbols symbols;
+ struct symbol *sym1, *sym2, *sym3, *held;
+ int ret = TEST_FAIL;
+
+ symbols__init(&symbols);
+
+ sym1 = symbol__new(0x100, 0x50, STB_GLOBAL, STT_FUNC, "sym1");
+ sym2 = symbol__new(0x200, 0x50, STB_GLOBAL, STT_FUNC, "sym2");
+ sym3 = symbol__new(0x300, 0x50, STB_GLOBAL, STT_FUNC, "sym3");
+ if (!sym1 || !sym2 || !sym3) {
+ symbol__put(sym1);
+ symbol__put(sym2);
+ symbol__put(sym3);
+ goto out;
+ }
+
+ /* Hold an active reference on sym1; sym2 and sym3 only have the table ref. */
+ held = symbol__get(sym1);
+
+ symbols__insert(&symbols, sym1);
+ symbols__insert(&symbols, sym2);
+ symbols__insert(&symbols, sym3);
+
+ /*
+ * First shrink: sym2 and sym3 (refcnt == 1) are evicted; sym1 has
+ * refcnt > 1 and is retained.
+ */
+ if (symbols__shrink(&symbols) != 2 || symbols.cnt != 1 || !symbols.shrunk) {
+ pr_debug("First symbols__shrink failed (cnt=%u)!\n", symbols.cnt);
+ symbol__put(held);
+ goto out;
+ }
+
+ /*
+ * Drop caller reference on sym1 and shrink again: sym1 is now evicted.
+ */
+ symbol__put(held);
+ if (symbols__shrink(&symbols) != 1 || symbols.cnt != 0) {
+ pr_debug("Second symbols__shrink failed (cnt=%u)!\n", symbols.cnt);
+ goto out;
+ }
+
+ ret = TEST_OK;
+out:
+ symbols__exit(&symbols);
+ return ret;
+}
+
+static int test_dsos_shrink(void)
+{
+ struct dsos dsos;
+ struct dso *dso1, *dso2;
+ int ret = TEST_FAIL;
+
+ dsos__init(&dsos);
+
+ dso1 = dsos__findnew_id(&dsos, "/tmp/perf_test_dso1", NULL);
+ dso2 = dsos__findnew_id(&dsos, "/tmp/perf_test_dso2", NULL);
+ if (!dso1 || !dso2) {
+ dso__put(dso1);
+ dso__put(dso2);
+ goto out;
+ }
+
+ /* Keep the caller's active reference on dso1; drop caller reference on dso2. */
+ dso__put(dso2);
+
+ /*
+ * First shrink: dso2 has refcnt == 1 and no symbols, so it is evicted;
+ * dso1 has refcnt == 2, so it is retained.
+ */
+ if (dsos__shrink(&dsos) != 1 || dsos.cnt != 1) {
+ pr_debug("First dsos__shrink failed (cnt=%u)!\n", dsos.cnt);
+ dso__put(dso1);
+ goto out;
+ }
+
+ dso__put(dso1);
+ if (dsos__shrink(&dsos) != 1 || dsos.cnt != 0) {
+ pr_debug("Second dsos__shrink failed (cnt=%u)!\n", dsos.cnt);
+ goto out;
+ }
+
+ ret = TEST_OK;
+out:
+ dsos__exit(&dsos);
+ return ret;
+}
+
+static int test_session_alloc_shrink(void)
+{
+ struct perf_session *session = zalloc(sizeof(*session));
+ struct dso *dso;
+ char *p1 = NULL, *p2 = NULL, *p3 = NULL, *s = NULL;
+ int ret = TEST_FAIL;
+
+ if (!session)
+ return TEST_FAIL;
+
+ if (machines__init(&session->machines, session))
+ goto out;
+ if (machine__session(&session->machines.host) != session) {
+ pr_debug("machine__session did not return owning session!\n");
+ goto out;
+ }
+
+ dso = machine__findnew_dso(&session->machines.host, "/tmp/perf_test_session_dso");
+ if (!dso)
+ goto out;
+ if (dso__session(dso) != session) {
+ pr_debug("dso__session did not return owning session!\n");
+ dso__put(dso);
+ goto out;
+ }
+ dso__put(dso);
+
+ p1 = perf_session__malloc(session, 64);
+ p2 = perf_session__zalloc(session, 64);
+ p3 = perf_session__calloc(session, 4, 16);
+ s = perf_session__strdup(session, "perf_session_alloc_test");
+ if (!p1 || !p2 || !p3 || !s || strcmp(s, "perf_session_alloc_test") != 0) {
+ pr_debug("perf_session allocation wrappers failed!\n");
+ goto out;
+ }
+
+ p1 = perf_session__realloc(session, p1, 128);
+ if (!p1) {
+ pr_debug("perf_session__realloc failed!\n");
+ goto out;
+ }
+
+ if (perf_session__shrink(session) == 0 || session->machines.host.dsos.cnt != 0) {
+ pr_debug("perf_session__shrink failed to evict unreferenced DSO!\n");
+ goto out;
+ }
+
+ ret = TEST_OK;
+out:
+ free(p1);
+ free(p2);
+ free(p3);
+ free(s);
+ machines__exit(&session->machines);
+ free(session);
+ return ret;
+}
+
+struct shrink_reload_find_args {
+ struct symbol *kept;
+ u64 first_start;
+ u64 second_start;
+ char *second_name;
+ u64 max_end;
+};
+
+static int shrink_reload_find_cb(struct symbol *sym, void *data)
+{
+ struct shrink_reload_find_args *args = data;
+
+ if (!args->kept) {
+ args->kept = symbol__get(sym);
+ args->first_start = symbol__start(sym);
+ args->max_end = symbol__end(sym);
+ return 0;
+ }
+ if (!args->second_name && symbol__start(sym) >= args->max_end) {
+ args->second_start = symbol__start(sym);
+ args->second_name = strdup(symbol__name(sym));
+ if (!args->second_name)
+ return -ENOMEM;
+ }
+ if (symbol__end(sym) > args->max_end)
+ args->max_end = symbol__end(sym);
+ return 0;
+}
+
+static int test_dso_shrink_and_reload(struct dso *dso, struct map *map)
+{
+ struct shrink_reload_find_args args = {
+ .kept = NULL,
+ .first_start = 0,
+ .second_start = 0,
+ .second_name = NULL,
+ .max_end = 0,
+ };
+ struct symbol *sym, *reloaded;
+ size_t removed;
+
+ if (!dso__can_shrink_symbols(dso) || dso__kernel(dso))
+ return TEST_OK;
+ if (dso__for_each_symbol(dso, shrink_reload_find_cb, &args) < 0) {
+ symbol__put(args.kept);
+ return TEST_FAIL;
+ }
+ if (!args.second_name) {
+ symbol__put(args.kept);
+ return TEST_OK;
+ }
+
+ /* Unreferenced symbols have refcnt == 1 and are evicted immediately. */
+ removed = dso__shrink(dso);
+ if (removed == 0 || !dso__symbols_shrunk(dso)) {
+ pr_debug("dso__shrink did not evict any symbols!\n");
+ free(args.second_name);
+ symbol__put(args.kept);
+ return TEST_FAIL;
+ }
+
+ /* Kept symbol is still found without reloading. */
+ sym = dso__find_symbol(dso, args.first_start);
+ if (!RC_CHK_EQUAL(sym, args.kept)) {
+ pr_debug("Kept symbol not found after dso__shrink!\n");
+ symbol__put(sym);
+ free(args.second_name);
+ symbol__put(args.kept);
+ return TEST_FAIL;
+ }
+ symbol__put(sym);
+
+ /* Evicted symbol is no longer in dso->symbols before reload. */
+ sym = dso__find_symbol(dso, args.second_start);
+ if (sym != NULL) {
+ pr_debug("Evicted symbol still found after dso__shrink!\n");
+ symbol__put(sym);
+ free(args.second_name);
+ symbol__put(args.kept);
+ return TEST_FAIL;
+ }
+
+ /* Lookup via map__find_symbol transparently reloads the DSO. */
+ reloaded = map__find_symbol(map, args.second_start);
+ if (!reloaded || strcmp(symbol__name(reloaded), args.second_name) != 0) {
+ pr_debug("Failed to reload evicted symbol %s!\n", args.second_name);
+ symbol__put(reloaded);
+ free(args.second_name);
+ symbol__put(args.kept);
+ return TEST_FAIL;
+ }
+ symbol__put(reloaded);
+
+ /* Kept symbol pointer was preserved across reload deduplication. */
+ sym = dso__find_symbol(dso, args.first_start);
+ if (!RC_CHK_EQUAL(sym, args.kept)) {
+ pr_debug("Kept symbol pointer was not preserved across reload!\n");
+ symbol__put(sym);
+ free(args.second_name);
+ symbol__put(args.kept);
+ return TEST_FAIL;
+ }
+ symbol__put(sym);
+ symbol__zput(args.kept);
+
+ free(args.second_name);
+ return TEST_OK;
+}
+
static int test_file(struct test_info *ti, char *filename)
{
struct map *map = NULL;
@@ -210,18 +476,124 @@ static int test_file(struct test_info *ti, char *filename)
/* Module dso is split into many dsos by section */
if (ret == TEST_OK && dso__kernel(dso) != DSO_SPACE__USER)
ret = process_subdivided_dso(ti->machine, dso);
+
+ if (ret == TEST_OK)
+ ret = test_dso_shrink_and_reload(dso, map);
out_put:
map__put(map);
return ret;
}
+static int test_symbols_sorted_insert(void)
+{
+ struct dso *dso = dso__new("/tmp/perf_test_sorted_insert");
+ struct symbols *symbols;
+ int ret = TEST_FAIL;
+
+ if (!dso)
+ return TEST_FAIL;
+
+ symbols = dso__symbols(dso);
+
+ /*
+ * Insert 100 odd-indexed symbols in ascending order via
+ * symbols__insert(), then insert 5 even-indexed symbols out of order via
+ * dso__insert_symbol() and verify symbols->sorted remains true.
+ */
+ for (unsigned int i = 0; i < 100; i++) {
+ char name[32];
+ u64 addr = (u64)(i * 2 + 1) * 0x100;
+ struct symbol *sym;
+
+ snprintf(name, sizeof(name), "sym_%u", i * 2 + 1);
+ sym = symbol__new(addr, 0x40, STB_GLOBAL, STT_FUNC, name);
+ if (!sym || symbols__insert(symbols, sym))
+ goto out;
+ }
+ if (!symbols->sorted || symbols->cnt != 100) {
+ pr_debug("Expected sorted=true, cnt=100 after ordered inserts\n");
+ goto out;
+ }
+
+ for (unsigned int i = 0; i < 5; i++) {
+ char name[32];
+ u64 addr = (u64)(i * 20) * 0x100;
+ struct symbol *sym;
+
+ snprintf(name, sizeof(name), "ins_%u", i * 20);
+ sym = symbol__new(addr, 0x40, STB_GLOBAL, STT_FUNC, name);
+ if (!sym || dso__insert_symbol(dso, sym))
+ goto out;
+ if (!symbols->sorted) {
+ pr_debug("dso__insert_symbol did not preserve sorted invariant!\n");
+ goto out;
+ }
+ }
+
+ /*
+ * Now append 95 remaining even-indexed symbols in descending order via
+ * symbols__insert() to exercise the unsorted append + sort-on-lookup path.
+ */
+ for (unsigned int i = 99; i > 0; i--) {
+ char name[32];
+ u64 addr;
+ struct symbol *sym;
+
+ if ((i * 2) % 20 == 0 && (i * 2) < 100)
+ continue;
+ addr = (u64)(i * 2) * 0x100;
+ snprintf(name, sizeof(name), "ins_%u", i * 2);
+ sym = symbol__new(addr, 0x40, STB_GLOBAL, STT_FUNC, name);
+ if (!sym || symbols__insert(symbols, sym))
+ goto out;
+ }
+ if (symbols->sorted || symbols->cnt != 200) {
+ pr_debug("Unexpected sorted=%d cnt=%u before lookup\n",
+ symbols->sorted, symbols->cnt);
+ goto out;
+ }
+
+ for (unsigned int i = 0; i < 200; i++) {
+ u64 addr = (u64)i * 0x100 + 0x10;
+ struct symbol *sym = dso__find_symbol(dso, addr);
+
+ if (!sym || symbol__start(sym) != (u64)i * 0x100) {
+ pr_debug("Lookup failed at idx %u (addr=%#" PRIx64 ")\n", i, addr);
+ symbol__put(sym);
+ goto out;
+ }
+ symbol__put(sym);
+ }
+
+ ret = TEST_OK;
+out:
+ dso__put(dso);
+ return ret;
+}
+
static int test__symbols(struct test_suite *test __maybe_unused, int subtest __maybe_unused)
{
char filename[PATH_MAX];
struct test_info ti;
int ret;
+ ret = test_symbols_sorted_insert();
+ if (ret != TEST_OK)
+ return ret;
+
+ ret = test_symbols_shrink();
+ if (ret != TEST_OK)
+ return ret;
+
+ ret = test_dsos_shrink();
+ if (ret != TEST_OK)
+ return ret;
+
+ ret = test_session_alloc_shrink();
+ if (ret != TEST_OK)
+ return ret;
+
ret = init_test_info(&ti);
if (ret != TEST_OK)
return ret;
--
2.56.0.313.g2479322e68-goog
^ permalink raw reply [flat|nested] 36+ messages in thread
end of thread, other threads:[~2026-10-27 23:16 UTC | newest]
Thread overview: 36+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-28 7:52 [PATCH v1 0/7] perf symbol: Reference counting, flat array storage, and LRU shrinking Ian Rogers
2026-09-28 7:52 ` [PATCH v1 1/7] perf symbol: Add accessor functions for struct symbol fields Ian Rogers
2026-09-28 7:52 ` [PATCH v1 2/7] perf symbol: Remove symbol_conf.priv_size and negative-offset allocations Ian Rogers
2026-09-28 7:52 ` [PATCH v1 3/7] perf symbol: Switch backing storage from rbtree to struct symbols array Ian Rogers
2026-09-28 7:52 ` [PATCH v1 4/7] perf symbol: Add reference counting and DECLARE_RC_STRUCT(symbol) Ian Rogers
2026-09-28 7:52 ` [PATCH v1 5/7] perf symbol: Add LRU memory shrinking for symbols, DSOs, and machines Ian Rogers
2026-09-28 7:52 ` [PATCH v1 6/7] perf session: Periodically shrink symbols and DSOs during event processing Ian Rogers
2026-09-28 7:52 ` [PATCH v1 7/7] perf test symbols: Add tests for symbol and DSO LRU shrinking Ian Rogers
2026-09-28 15:37 ` [PATCH v1 0/7] perf symbol: Reference counting, flat array storage, and " Ian Rogers
2026-09-28 19:45 ` Alireza Haghdoost
2026-09-28 20:55 ` Ian Rogers
2026-09-28 21:51 ` Namhyung Kim
2026-09-28 22:04 ` Ian Rogers
2026-09-29 0:12 ` Alireza Haghdoost
2026-09-29 18:09 ` Namhyung Kim
2026-09-29 21:24 ` Alireza Haghdoost
2026-09-29 18:06 ` Namhyung Kim
2026-10-27 23:14 ` [PATCH v2 00/18] perf symbol: Reference counting, sorted arrays, and memory shrinking Ian Rogers
2026-10-27 23:14 ` [PATCH v2 01/18] perf bench internals: Add symbols and maps benchmarks Ian Rogers
2026-10-27 23:14 ` [PATCH v2 02/18] perf demangle: Add fast-path prefix checks for Rust and Java demanglers Ian Rogers
2026-10-27 23:14 ` [PATCH v2 03/18] perf symbol: Add accessor functions for struct symbol fields Ian Rogers
2026-10-27 23:14 ` [PATCH v2 04/18] perf map_browser: Compute symbol browser index on demand Ian Rogers
2026-10-27 23:14 ` [PATCH v2 05/18] perf db-export: Store symbol database IDs in a hashmap Ian Rogers
2026-10-27 23:15 ` [PATCH v2 06/18] perf annotate: Move struct annotation to a hashmap and remove symbol_conf.priv_size Ian Rogers
2026-10-27 23:15 ` [PATCH v2 07/18] perf symbol: Switch backing storage from rbtree to struct symbols array Ian Rogers
2026-10-27 23:15 ` [PATCH v2 08/18] perf symbol: Add reference counting and DECLARE_RC_STRUCT(symbol) Ian Rogers
2026-10-27 23:15 ` [PATCH v2 09/18] perf symbol: Optimize symbol sorting with natural-run merge sort and binary insertion Ian Rogers
2026-10-27 23:15 ` [PATCH v2 10/18] perf maps: Optimize tail insertion and small-append sorting Ian Rogers
2026-10-27 23:15 ` [PATCH v2 11/18] perf symbol-elf: Cache ELF program/section headers and defer symbol name lookup Ian Rogers
2026-10-27 23:15 ` [PATCH v2 12/18] perf rwsem: Add down_read_trylock() and down_write_trylock() Ian Rogers
2026-10-27 23:15 ` [PATCH v2 13/18] perf vdso: Constify dso parameter in dso__is_vdso() Ian Rogers
2026-10-27 23:15 ` [PATCH v2 14/18] perf machine: Protect machines->guests rbtree with a rw_semaphore Ian Rogers
2026-10-27 23:15 ` [PATCH v2 15/18] perf symbol: Add memory shrinking for symbols, DSOs, and machines Ian Rogers
2026-10-27 23:15 ` [PATCH v2 16/18] perf session: Add dso__session() and session-scoped allocation wrappers Ian Rogers
2026-10-27 23:15 ` [PATCH v2 17/18] perf symbol: Use session allocation wrappers in symbol, DSO, thread, and hist paths Ian Rogers
2026-10-27 23:15 ` [PATCH v2 18/18] perf test symbols: Add tests for symbol shrinking and session alloc wrappers Ian Rogers
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®