* [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
` (8 more replies)
0 siblings, 9 replies; 13+ 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] 13+ 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
` (7 subsequent siblings)
8 siblings, 0 replies; 13+ 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] 13+ 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
` (6 subsequent siblings)
8 siblings, 0 replies; 13+ 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] 13+ 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
` (5 subsequent siblings)
8 siblings, 0 replies; 13+ 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] 13+ 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
` (4 subsequent siblings)
8 siblings, 0 replies; 13+ 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] 13+ 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
` (3 subsequent siblings)
8 siblings, 0 replies; 13+ 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] 13+ 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
` (2 subsequent siblings)
8 siblings, 0 replies; 13+ 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] 13+ 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
2026-09-28 19:45 ` Alireza Haghdoost
8 siblings, 0 replies; 13+ 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] 13+ 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
8 siblings, 0 replies; 13+ 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] 13+ 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
8 siblings, 1 reply; 13+ 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] 13+ 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; 13+ 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] 13+ 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; 13+ 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] 13+ 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
0 siblings, 0 replies; 13+ 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] 13+ messages in thread
end of thread, other threads:[~2026-09-28 22:05 UTC | newest]
Thread overview: 13+ 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
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®