mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [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
                   ` (7 more replies)
  0 siblings, 8 replies; 9+ 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] 9+ 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
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 9+ 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, &notes->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, &notes->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] 9+ 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
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 9+ 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(&notes->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] 9+ 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
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 9+ 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] 9+ 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
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 9+ 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] 9+ 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
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 9+ 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] 9+ 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
  2026-09-28 15:37 ` [PATCH v1 0/7] perf symbol: Reference counting, flat array storage, and " Ian Rogers
  7 siblings, 0 replies; 9+ 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] 9+ 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
  7 siblings, 0 replies; 9+ 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] 9+ 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
  7 siblings, 0 replies; 9+ 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] 9+ messages in thread

end of thread, other threads:[~2026-09-28 15:38 UTC | newest]

Thread overview: 9+ 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

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®