* [PATCH v2 01/18] perf bench internals: Add symbols and maps benchmarks
2026-10-27 23:14 ` [PATCH v2 00/18] perf symbol: Reference counting, sorted arrays, and memory shrinking Ian Rogers
@ 2026-10-27 23:14 ` Ian Rogers
2026-10-27 23:14 ` [PATCH v2 02/18] perf demangle: Add fast-path prefix checks for Rust and Java demanglers Ian Rogers
` (17 subsequent siblings)
18 siblings, 0 replies; 37+ messages in thread
From: Ian Rogers @ 2026-10-27 23:14 UTC (permalink / raw)
To: irogers, acme, namhyung
Cc: adrian.hunter, haghdoost, james.clark, jolsa, linux-kernel,
linux-perf-users, mingo, peterz
Add two new 'perf bench internals' subcommands to measure symbol and map
operations directly:
- 'perf bench internals symbols':
1. Loads symbols from an ELF binary (defaulting to the running perf
executable, or specified via -b/--binary) and sorts them by name,
reporting both average runtime and heap memory consumption.
2. Inserts synthetic symbols in two ascending runs (simulating .symtab
and .dynsym), fixes up symbol ends and duplicates, and queries each
symbol by address.
3. Incrementally appends symbols at interior addresses and immediately
queries each appended symbol by address.
- 'perf bench internals maps':
1. Inserts non-overlapping maps in ascending address order (simulating
/proc/<pid>/maps synthesis) and queries each map by address.
2. Incrementally appends maps at interior addresses (simulating runtime
PERF_RECORD_MMAP2 / JIT events) and immediately queries each map by
address and by name.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/Documentation/perf-bench.txt | 6 +
tools/perf/bench/Build | 2 +
tools/perf/bench/bench.h | 2 +
tools/perf/bench/maps.c | 186 +++++++++++++++++++
tools/perf/bench/symbols.c | 236 ++++++++++++++++++++++++
tools/perf/builtin-bench.c | 2 +
6 files changed, 434 insertions(+)
create mode 100644 tools/perf/bench/maps.c
create mode 100644 tools/perf/bench/symbols.c
diff --git a/tools/perf/Documentation/perf-bench.txt b/tools/perf/Documentation/perf-bench.txt
index 7f820f69740f..c65dd8f4131f 100644
--- a/tools/perf/Documentation/perf-bench.txt
+++ b/tools/perf/Documentation/perf-bench.txt
@@ -351,6 +351,12 @@ SUITES FOR 'internals'
*synthesize*::
Suite for evaluating perf's event synthesis performance.
+*symbols*::
+Suite for evaluating ELF symbol loading, sorting, and lookup performance.
+
+*maps*::
+Suite for evaluating maps insertion, sorting, and lookup performance.
+
SEE ALSO
--------
linkperf:perf[1]
diff --git a/tools/perf/bench/Build b/tools/perf/bench/Build
index c64c52468d3d..d51e52d3742a 100644
--- a/tools/perf/bench/Build
+++ b/tools/perf/bench/Build
@@ -20,6 +20,8 @@ perf-bench-y += evlist-open-close.o
perf-bench-y += breakpoint.o
perf-bench-y += pmu-scan.o
perf-bench-y += uprobe.o
+perf-bench-y += symbols.o
+perf-bench-y += maps.o
perf-bench-$(CONFIG_X86_64) += mem-memcpy-x86-64-asm.o
perf-bench-$(CONFIG_X86_64) += mem-memset-x86-64-asm.o
diff --git a/tools/perf/bench/bench.h b/tools/perf/bench/bench.h
index 85e3ac471875..c988d236e0f0 100644
--- a/tools/perf/bench/bench.h
+++ b/tools/perf/bench/bench.h
@@ -52,6 +52,8 @@ int bench_uprobe_trace_printk(int argc, const char **argv);
int bench_uprobe_empty_ret(int argc, const char **argv);
int bench_uprobe_trace_printk_ret(int argc, const char **argv);
int bench_pmu_scan(int argc, const char **argv);
+int bench_symbols(int argc, const char **argv);
+int bench_maps(int argc, const char **argv);
#define BENCH_FORMAT_DEFAULT_STR "default"
#define BENCH_FORMAT_DEFAULT 0
diff --git a/tools/perf/bench/maps.c b/tools/perf/bench/maps.c
new file mode 100644
index 000000000000..ed5a5be3cbf0
--- /dev/null
+++ b/tools/perf/bench/maps.c
@@ -0,0 +1,186 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Benchmark maps insertion, sorting, and address/name lookup.
+ */
+#include <errno.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <linux/compiler.h>
+#include <linux/kernel.h>
+#include <linux/time64.h>
+#include <subcmd/parse-options.h>
+#include "bench.h"
+#include "../util/debug.h"
+#include "../util/dso.h"
+#include "../util/map.h"
+#include "../util/maps.h"
+#include "../util/stat.h"
+
+static unsigned int iterations = 100;
+static unsigned int nr_maps = 1000;
+static unsigned int nr_appends = 200;
+
+static const struct option options[] = {
+ OPT_UINTEGER('i', "iterations", &iterations,
+ "Number of iterations used to compute average (default: 100)"),
+ OPT_UINTEGER('m', "nr-maps", &nr_maps,
+ "Number of ascending maps per iteration (default: 1000)"),
+ OPT_UINTEGER('a', "nr-appends", &nr_appends,
+ "Number of incremental map appends per iteration (default: 200)"),
+ OPT_END()
+};
+
+static const char *const bench_usage[] = {
+ "perf bench internals maps <options>",
+ NULL
+};
+
+static int do_maps_bench(void)
+{
+ struct stats asc_stats, append_stats;
+ unsigned int total_dsos = nr_maps + nr_appends;
+ struct dso **dsos;
+ struct map **all_maps;
+ unsigned int i, j;
+ int err = 0;
+
+ if (total_dsos == 0)
+ return 0;
+
+ dsos = calloc(total_dsos, sizeof(*dsos));
+ all_maps = calloc(total_dsos, sizeof(*all_maps));
+ if (!dsos || !all_maps) {
+ free(dsos);
+ free(all_maps);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < total_dsos; i++) {
+ char name[32];
+ u64 addr;
+
+ scnprintf(name, sizeof(name), "/tmp/bench_dso_%06u.so", i);
+ dsos[i] = dso__new(name);
+ if (!dsos[i]) {
+ err = -ENOMEM;
+ goto out_free;
+ }
+ if (i < nr_maps) {
+ addr = 0x100000ULL + (u64)i * 0x2000ULL;
+ all_maps[i] = map__new2(addr, dsos[i]);
+ if (!all_maps[i]) {
+ err = -ENOMEM;
+ goto out_free;
+ }
+ map__set_end(all_maps[i], addr + 0x1000ULL);
+ } else {
+ u64 k = i - nr_maps;
+ u64 slot = nr_maps ? (k * 197ULL) % nr_maps : k;
+
+ addr = 0x101000ULL + slot * 0x2000ULL +
+ (nr_maps ? (k / nr_maps) * 0x100ULL : 0);
+ all_maps[i] = map__new2(addr, dsos[i]);
+ if (!all_maps[i]) {
+ err = -ENOMEM;
+ goto out_free;
+ }
+ map__set_end(all_maps[i], addr + 0x80ULL);
+ }
+ }
+
+ init_stats(&asc_stats);
+ init_stats(&append_stats);
+
+ for (i = 0; i < iterations; i++) {
+ struct timeval start, end, diff;
+ struct maps *maps = maps__new(NULL);
+ u64 runtime_us;
+
+ if (!maps) {
+ err = -ENOMEM;
+ goto out_free;
+ }
+
+ /*
+ * Phase 1: Insert maps in ascending address order (simulating
+ * /proc/<pid>/maps synthesis) and look up each map by address.
+ */
+ gettimeofday(&start, NULL);
+ for (j = 0; j < nr_maps; j++) {
+ err = maps__fixup_overlap_and_insert(maps, all_maps[j]);
+ if (err) {
+ maps__zput(maps);
+ goto out_free;
+ }
+ }
+ for (j = 0; j < nr_maps; j++) {
+ u64 addr = 0x100000ULL + (u64)j * 0x2000ULL + 0x100ULL;
+ struct map *found = maps__find(maps, addr);
+
+ map__put(found);
+ }
+ gettimeofday(&end, NULL);
+ timersub(&end, &start, &diff);
+ runtime_us = diff.tv_sec * USEC_PER_SEC + diff.tv_usec;
+ update_stats(&asc_stats, runtime_us);
+
+ /*
+ * Phase 2: Incrementally insert maps at interior addresses
+ * (simulating runtime MMAP2 / JIT code events) and immediately
+ * query by address and by name.
+ */
+ gettimeofday(&start, NULL);
+ for (j = 0; j < nr_appends; j++) {
+ struct map *map = all_maps[nr_maps + j];
+ struct dso *dso = dsos[nr_maps + j];
+ u64 addr = map__start(map);
+ struct map *found;
+
+ err = maps__fixup_overlap_and_insert(maps, map);
+ if (err) {
+ maps__zput(maps);
+ goto out_free;
+ }
+
+ found = maps__find(maps, addr + 0x10ULL);
+ map__put(found);
+
+ found = maps__find_by_name(maps, dso__short_name(dso));
+ map__put(found);
+ }
+ gettimeofday(&end, NULL);
+ timersub(&end, &start, &diff);
+ runtime_us = diff.tv_sec * USEC_PER_SEC + diff.tv_usec;
+ update_stats(&append_stats, runtime_us);
+
+ maps__zput(maps);
+ }
+
+ printf(" Average ascending insert + lookup: %.3f usec (+- %.3f usec)\n",
+ avg_stats(&asc_stats), stddev_stats(&asc_stats));
+ printf(" Average append + lookup: %.3f usec (+- %.3f usec)\n",
+ avg_stats(&append_stats), stddev_stats(&append_stats));
+
+out_free:
+ for (i = 0; i < total_dsos; i++) {
+ map__put(all_maps[i]);
+ dso__put(dsos[i]);
+ }
+ free(all_maps);
+ free(dsos);
+ return err;
+}
+
+int bench_maps(int argc, const char **argv)
+{
+ argc = parse_options(argc, argv, options, bench_usage, 0);
+ if (argc) {
+ usage_with_options(bench_usage, options);
+ exit(EXIT_FAILURE);
+ }
+
+ return do_maps_bench();
+}
diff --git a/tools/perf/bench/symbols.c b/tools/perf/bench/symbols.c
new file mode 100644
index 000000000000..5ab71d440e67
--- /dev/null
+++ b/tools/perf/bench/symbols.c
@@ -0,0 +1,236 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Benchmark symbol loading, sorting, and address/name lookup.
+ */
+#include <elf.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <malloc.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <linux/compiler.h>
+#include <linux/kernel.h>
+#include <linux/time64.h>
+#include <subcmd/parse-options.h>
+#include "bench.h"
+#include "../util/debug.h"
+#include "../util/dso.h"
+#include "../util/map.h"
+#include "../util/stat.h"
+#include "../util/symbol.h"
+#include "../util/util.h"
+
+static unsigned int iterations = 20;
+static unsigned int nr_symbols = 10000;
+static unsigned int nr_appends = 200;
+static const char *binary_path;
+static bool no_demangle;
+
+static const struct option options[] = {
+ OPT_UINTEGER('i', "iterations", &iterations,
+ "Number of iterations used to compute average (default: 20)"),
+ OPT_STRING('b', "binary", &binary_path, "path",
+ "Binary path for ELF symbol load benchmark (default: perf binary)"),
+ OPT_UINTEGER('n', "nr-symbols", &nr_symbols,
+ "Number of synthetic symbols for bulk benchmark (default: 10000)"),
+ OPT_UINTEGER('a', "nr-appends", &nr_appends,
+ "Number of incremental symbol appends per iteration (default: 200)"),
+ OPT_BOOLEAN(0, "no-demangle", &no_demangle,
+ "Disable symbol demangling during ELF load"),
+ OPT_END()
+};
+
+static const char *const bench_usage[] = {
+ "perf bench internals symbols <options>",
+ NULL
+};
+
+static int do_dso_load_bench(const char *path)
+{
+ struct stats time_stats;
+#if defined(__GLIBC__) && __GLIBC_PREREQ(2, 33)
+ struct stats mem_stats;
+#endif
+ double time_average, time_stddev;
+ unsigned int i;
+
+ init_stats(&time_stats);
+#if defined(__GLIBC__) && __GLIBC_PREREQ(2, 33)
+ init_stats(&mem_stats);
+#endif
+
+ for (i = 0; i < iterations; i++) {
+ struct timeval start, end, diff;
+ struct map *map;
+ struct dso *dso;
+ u64 runtime_us;
+#if defined(__GLIBC__) && __GLIBC_PREREQ(2, 33)
+ size_t mem_before = mallinfo2().uordblks;
+ size_t mem_after;
+#endif
+
+ gettimeofday(&start, NULL);
+ map = dso__new_map(path);
+ if (!map) {
+ pr_err("Failed to create map for %s\n", path);
+ return -ENOMEM;
+ }
+ if (map__load(map) < 0) {
+ pr_err("Failed to load symbols from %s\n", path);
+ map__put(map);
+ return -EINVAL;
+ }
+ dso = map__dso(map);
+ dso__sort_by_name(dso);
+ gettimeofday(&end, NULL);
+
+#if defined(__GLIBC__) && __GLIBC_PREREQ(2, 33)
+ mem_after = mallinfo2().uordblks;
+ if (mem_after > mem_before)
+ update_stats(&mem_stats, (mem_after - mem_before) / 1024);
+#endif
+ map__put(map);
+
+ timersub(&end, &start, &diff);
+ runtime_us = diff.tv_sec * USEC_PER_SEC + diff.tv_usec;
+ update_stats(&time_stats, runtime_us);
+ }
+
+ time_average = avg_stats(&time_stats) / USEC_PER_MSEC;
+ time_stddev = stddev_stats(&time_stats) / USEC_PER_MSEC;
+ printf(" Average DSO load + sort: %.3f msec (+- %.3f msec)\n",
+ time_average, time_stddev);
+
+#if defined(__GLIBC__) && __GLIBC_PREREQ(2, 33)
+ printf(" Average DSO memory usage: %.0f KB (+- %.0f KB)\n",
+ avg_stats(&mem_stats), stddev_stats(&mem_stats));
+#endif
+ return 0;
+}
+
+static int do_synthetic_symbols_bench(void)
+{
+ struct stats bulk_stats, append_stats;
+ unsigned int i, j, half = nr_symbols / 2;
+
+ if (half == 0)
+ return 0;
+
+ init_stats(&bulk_stats);
+ init_stats(&append_stats);
+
+ for (i = 0; i < iterations; i++) {
+ struct timeval start, end, diff;
+ struct dso *dso = dso__new("[bench_symbols]");
+ u64 runtime_us;
+ char name[32];
+
+ if (!dso)
+ return -ENOMEM;
+
+ /*
+ * Phase 1: Insert symbols in two ascending runs (simulating
+ * .symtab + .dynsym), fixup ends and duplicates, and look up
+ * all symbols by address.
+ */
+ gettimeofday(&start, NULL);
+ for (j = 0; j < nr_symbols; j++) {
+ u64 addr = (j < half) ?
+ (0x100000ULL + (u64)j * 0x40ULL) :
+ (0x100020ULL + (u64)(j - half) * 0x40ULL);
+ struct symbol *sym;
+
+ scnprintf(name, sizeof(name), "bench_sym_%06u", j);
+ sym = symbol__new(addr, 0x18, STB_GLOBAL, STT_FUNC, name);
+ if (!sym) {
+ dso__put(dso);
+ return -ENOMEM;
+ }
+ symbols__insert(dso__symbols(dso), sym);
+ }
+ symbols__fixup_end(dso__symbols(dso), false);
+ symbols__fixup_duplicate(dso__symbols(dso));
+
+ for (j = 0; j < nr_symbols; j++) {
+ u64 addr = (j < half) ?
+ (0x100000ULL + (u64)j * 0x40ULL + 4) :
+ (0x100020ULL + (u64)(j - half) * 0x40ULL + 4);
+
+ (void)dso__find_symbol_nocache(dso, addr);
+ }
+ gettimeofday(&end, NULL);
+ timersub(&end, &start, &diff);
+ runtime_us = diff.tv_sec * USEC_PER_SEC + diff.tv_usec;
+ update_stats(&bulk_stats, runtime_us);
+
+ /*
+ * Phase 2: Incrementally append one symbol at a time at an
+ * interior address and immediately query by address.
+ */
+ gettimeofday(&start, NULL);
+ for (j = 0; j < nr_appends; j++) {
+ u64 addr = 0x100010ULL + ((u64)(j * 197 % half)) * 0x40ULL;
+ struct symbol *sym;
+
+ scnprintf(name, sizeof(name), "jit_sym_%06u", j);
+ sym = symbol__new(addr, 0x08, STB_GLOBAL, STT_FUNC, name);
+ if (!sym) {
+ dso__put(dso);
+ return -ENOMEM;
+ }
+ dso__insert_symbol(dso, sym);
+ (void)dso__find_symbol_nocache(dso, addr + 2);
+ }
+ gettimeofday(&end, NULL);
+ timersub(&end, &start, &diff);
+ runtime_us = diff.tv_sec * USEC_PER_SEC + diff.tv_usec;
+ update_stats(&append_stats, runtime_us);
+
+ dso__put(dso);
+ }
+
+ printf(" Average bulk insert + lookup: %.3f usec (+- %.3f usec)\n",
+ avg_stats(&bulk_stats), stddev_stats(&bulk_stats));
+ printf(" Average append + lookup: %.3f usec (+- %.3f usec)\n",
+ avg_stats(&append_stats), stddev_stats(&append_stats));
+ return 0;
+}
+
+int bench_symbols(int argc, const char **argv)
+{
+ char exe_buf[PATH_MAX];
+ const char *path;
+ bool orig_demangle;
+ int err;
+
+ argc = parse_options(argc, argv, options, bench_usage, 0);
+ if (argc) {
+ usage_with_options(bench_usage, options);
+ exit(EXIT_FAILURE);
+ }
+
+ if (symbol__init(NULL) < 0)
+ return -1;
+
+ orig_demangle = symbol_conf.demangle;
+ if (no_demangle)
+ symbol_conf.demangle = false;
+
+ if (binary_path) {
+ path = binary_path;
+ } else {
+ perf_exe(exe_buf, sizeof(exe_buf));
+ path = exe_buf;
+ }
+
+ err = do_dso_load_bench(path);
+ if (!err)
+ err = do_synthetic_symbols_bench();
+
+ symbol_conf.demangle = orig_demangle;
+ return err;
+}
diff --git a/tools/perf/builtin-bench.c b/tools/perf/builtin-bench.c
index 62bebdedd3b8..57a4199a058c 100644
--- a/tools/perf/builtin-bench.c
+++ b/tools/perf/builtin-bench.c
@@ -97,6 +97,8 @@ static const struct bench internals_benchmarks[] = {
{ "inject-build-id", "Benchmark build-id injection", bench_inject_build_id },
{ "evlist-open-close", "Benchmark evlist open and close", bench_evlist_open_close },
{ "pmu-scan", "Benchmark sysfs PMU info scanning", bench_pmu_scan },
+ { "symbols", "Benchmark symbol loading, sorting and lookup", bench_symbols },
+ { "maps", "Benchmark maps insertion, sorting and lookup", bench_maps },
{ NULL, NULL, NULL }
};
--
2.56.0.313.g2479322e68-goog
^ permalink raw reply [flat|nested] 37+ messages in thread* [PATCH v2 02/18] perf demangle: Add fast-path prefix checks for Rust and Java demanglers
2026-10-27 23:14 ` [PATCH v2 00/18] perf symbol: Reference counting, sorted arrays, and memory shrinking Ian Rogers
2026-10-27 23:14 ` [PATCH v2 01/18] perf bench internals: Add symbols and maps benchmarks Ian Rogers
@ 2026-10-27 23:14 ` Ian Rogers
2026-10-27 23:14 ` [PATCH v2 03/18] perf symbol: Add accessor functions for struct symbol fields Ian Rogers
` (16 subsequent siblings)
18 siblings, 0 replies; 37+ messages in thread
From: Ian Rogers @ 2026-10-27 23:14 UTC (permalink / raw)
To: irogers, acme, namhyung
Cc: adrian.hunter, haghdoost, james.clark, jolsa, linux-kernel,
linux-perf-users, mingo, peterz
During DSO symbol loading in dso__load_sym_internal(), dso__demangle_sym()
is called for every ELF symbol. Both rust_demangle_demangle() and
java_demangle_sym() previously performed string searches or buffer
allocations before checking whether the symbol name could possibly be a
mangled Rust or Java symbol, adding unnecessary overhead on plain C
symbols.
Add fast-path prefix checks in dso__demangle_sym() before calling
rust_demangle_demangle() (using strstarts() for '_ZN', 'ZN', '__ZN',
'_R', 'R', or '__R', keeping demangle-rust-v0.c unmodified relative to
upstream rustc-demangle) and in java_demangle_sym() (requiring 'L', ';',
'(', and ')') to reject non-Rust and non-Java symbol names immediately.
Using 'perf bench internals symbols':
Before (perf binary):
Average DSO load + sort: 7.348 msec (+- 0.069 msec)
After (perf binary, -4.2%):
Average DSO load + sort: 7.041 msec (+- 0.051 msec)
Before (libLLVM.so.21.1):
Average DSO load + sort: 81.196 msec (+- 0.780 msec)
After (libLLVM.so.21.1, -3.2%):
Average DSO load + sort: 78.599 msec (+- 0.857 msec)
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/demangle-java.c | 23 +++++++++++++++++++++--
tools/perf/util/symbol.c | 15 ++++++++++++++-
2 files changed, 35 insertions(+), 3 deletions(-)
diff --git a/tools/perf/util/demangle-java.c b/tools/perf/util/demangle-java.c
index c3cb327ed562..5625c4de39eb 100644
--- a/tools/perf/util/demangle-java.c
+++ b/tools/perf/util/demangle-java.c
@@ -158,14 +158,33 @@ char *
java_demangle_sym(const char *str, int flags)
{
char *buf, *ptr;
- const char *p;
+ const char *l, *semi, *open, *p;
size_t len, l1 = 0;
if (!str)
return NULL;
+ /*
+ * OpenJDK JIT symbols have the form:
+ * [prefix]L<class>;<method>(<args>)<return_type>
+ * where the return type (such as 'I' for int or 'V' for void) is at the
+ * end after ')'. In __demangle_java_sym(), the parser starts in
+ * MODE_PREFIX and only reaches MODE_TYPE (required when ')' is reached)
+ * via 'L' (MODE_CLASS) -> ';' (MODE_FUNC) -> '(' (MODE_TYPE). Verify
+ * those delimiters appear in order before allocating the output buffer.
+ */
+ l = strchr(str, 'L');
+ if (!l)
+ return NULL;
+ semi = strchr(l + 1, ';');
+ if (!semi)
+ return NULL;
+ open = strchr(semi + 1, '(');
+ if (!open)
+ return NULL;
+
/* find start of return type */
- p = strrchr(str, ')');
+ p = strrchr(open + 1, ')');
if (!p)
return NULL;
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index b61a574ce9a3..dc70bbe8514f 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -2738,7 +2738,20 @@ char *dso__demangle_sym(struct dso *dso, int kmodule, const char *elf_name)
if (!want_demangle((dso && dso__kernel(dso)) || kmodule))
return demangled;
- rust_demangle_demangle(elf_name, &rust_demangle);
+ /*
+ * Rust mangled symbols start with one of the prefixes checked by
+ * rust_demangle_legacy_demangle() ("_ZN", Windows dbghelp "ZN", or
+ * macOS "__ZN") or rust_demangle_v0_demangle() ("_R", Windows dbghelp
+ * "R", or macOS "__R") in demangle-rust-v0.c. Check the prefix here so
+ * we avoid calling rust_demangle_demangle() (which scans the entire
+ * symbol name with strstr() and strlen() for ThinLTO ".llvm." suffixes)
+ * on non-Rust symbols, while keeping demangle-rust-v0.c unmodified
+ * relative to upstream rustc-demangle.
+ */
+ if (strstarts(elf_name, "_ZN") || strstarts(elf_name, "ZN") ||
+ strstarts(elf_name, "__ZN") || strstarts(elf_name, "_R") ||
+ strstarts(elf_name, "R") || strstarts(elf_name, "__R"))
+ rust_demangle_demangle(elf_name, &rust_demangle);
if (rust_demangle_is_known(&rust_demangle)) {
/* A rust mangled name. */
if (rust_demangle.mangled_len == 0)
--
2.56.0.313.g2479322e68-goog
^ permalink raw reply [flat|nested] 37+ messages in thread* [PATCH v2 03/18] perf symbol: Add accessor functions for struct symbol fields
2026-10-27 23:14 ` [PATCH v2 00/18] perf symbol: Reference counting, sorted arrays, and memory shrinking Ian Rogers
2026-10-27 23:14 ` [PATCH v2 01/18] perf bench internals: Add symbols and maps benchmarks Ian Rogers
2026-10-27 23:14 ` [PATCH v2 02/18] perf demangle: Add fast-path prefix checks for Rust and Java demanglers Ian Rogers
@ 2026-10-27 23:14 ` Ian Rogers
2026-10-27 23:14 ` [PATCH v2 04/18] perf map_browser: Compute symbol browser index on demand Ian Rogers
` (15 subsequent siblings)
18 siblings, 0 replies; 37+ messages in thread
From: Ian Rogers @ 2026-10-27 23:14 UTC (permalink / raw)
To: irogers, acme, namhyung
Cc: adrian.hunter, haghdoost, james.clark, jolsa, linux-kernel,
linux-perf-users, mingo, peterz
To track symbol lifetimes across DSO reloads, histogram decay, and
on-demand memory shrinking, struct symbol needs to be reference-counted
and wrapped with DECLARE_RC_STRUCT(symbol) so REFCNT_CHECKING builds can
detect missing or leaked reference counts.
To prepare for wrapping struct symbol in DECLARE_RC_STRUCT(symbol), add
inline accessor functions for struct symbol fields (symbol__start,
symbol__set_start, symbol__end, symbol__set_end, symbol__name,
symbol__namelen, symbol__arch_sym, symbol__set_arch_sym) and convert
direct member accesses across tools/perf.
Signed-off-by: Ian Rogers <irogers@google.com>
---
.../arch/powerpc/util/skip-callchain-idx.c | 2 +-
tools/perf/arch/powerpc/util/sym-handling.c | 8 +-
tools/perf/builtin-annotate.c | 4 +-
tools/perf/builtin-diff.c | 4 +-
tools/perf/builtin-kallsyms.c | 7 +-
tools/perf/builtin-kmem.c | 29 ++---
tools/perf/builtin-kwork.c | 4 +-
tools/perf/builtin-lock.c | 10 +-
tools/perf/builtin-mem.c | 2 +-
tools/perf/builtin-sched.c | 6 +-
tools/perf/builtin-script.c | 14 +--
tools/perf/builtin-timechart.c | 2 +-
tools/perf/builtin-top.c | 22 ++--
tools/perf/builtin-trace.c | 22 ++--
tools/perf/tests/dlfilter-test.c | 4 +-
tools/perf/tests/dwarf-unwind.c | 2 +-
tools/perf/tests/hists_common.c | 4 +-
tools/perf/tests/hists_cumulate.c | 4 +-
tools/perf/tests/hists_output.c | 2 +-
tools/perf/tests/symbols.c | 12 +-
tools/perf/tests/vmlinux-kallsyms.c | 34 +++---
tools/perf/ui/browsers/annotate.c | 10 +-
tools/perf/ui/browsers/hists.c | 10 +-
tools/perf/ui/browsers/map.c | 8 +-
tools/perf/ui/gtk/annotate.c | 6 +-
tools/perf/ui/stdio/hist.c | 3 +-
.../perf/util/annotate-arch/annotate-arm64.c | 12 +-
.../util/annotate-arch/annotate-loongarch.c | 4 +-
tools/perf/util/annotate-arch/annotate-x86.c | 14 +--
tools/perf/util/annotate-data.c | 12 +-
tools/perf/util/annotate.c | 69 ++++++------
tools/perf/util/auxtrace.c | 14 +--
tools/perf/util/block-info.c | 6 +-
tools/perf/util/bpf_lock_contention.c | 16 +--
tools/perf/util/c2c-function.c | 19 ++--
tools/perf/util/callchain.c | 19 ++--
tools/perf/util/capstone.c | 20 ++--
tools/perf/util/data-convert-json.c | 4 +-
tools/perf/util/db-export.c | 2 +-
tools/perf/util/debug.c | 3 +-
tools/perf/util/disasm.c | 49 +++++----
tools/perf/util/dlfilter.c | 12 +-
tools/perf/util/dso.c | 4 +-
tools/perf/util/event.c | 4 +-
tools/perf/util/evsel_fprintf.c | 2 +-
tools/perf/util/hist.c | 16 +--
tools/perf/util/intel-pt.c | 8 +-
tools/perf/util/libbfd.c | 4 +-
tools/perf/util/llvm.c | 6 +-
tools/perf/util/lock-contention.c | 2 +-
tools/perf/util/machine.c | 26 ++---
tools/perf/util/map.c | 6 +-
tools/perf/util/map.h | 2 +-
tools/perf/util/probe-event.c | 41 +++----
tools/perf/util/python.c | 18 +--
tools/perf/util/sort.c | 40 +++----
tools/perf/util/srcline.c | 16 +--
tools/perf/util/symbol-elf.c | 10 +-
tools/perf/util/symbol.c | 103 +++++++++---------
tools/perf/util/symbol.h | 43 +++++++-
tools/perf/util/symbol_fprintf.c | 14 +--
tools/perf/util/thread-stack.c | 8 +-
tools/perf/util/unwind-libdw.c | 2 +-
63 files changed, 478 insertions(+), 407 deletions(-)
diff --git a/tools/perf/arch/powerpc/util/skip-callchain-idx.c b/tools/perf/arch/powerpc/util/skip-callchain-idx.c
index 472714cfad38..94854f9fa440 100644
--- a/tools/perf/arch/powerpc/util/skip-callchain-idx.c
+++ b/tools/perf/arch/powerpc/util/skip-callchain-idx.c
@@ -239,7 +239,7 @@ int arch_skip_callchain_idx(struct thread *thread, struct ip_callchain *chain)
rc = check_return_addr(dso, map__map_ip(al.map, ip));
pr_debug("[DSO %s, sym %s, ip 0x%" PRIx64 "] rc %d\n",
- dso__long_name(dso), al.sym->name, ip, rc);
+ dso__long_name(dso), al.sym ? symbol__name(al.sym) : "unknown", ip, rc);
if (rc == 0) {
/*
diff --git a/tools/perf/arch/powerpc/util/sym-handling.c b/tools/perf/arch/powerpc/util/sym-handling.c
index 947bfad7aa59..d73bfdcdbec1 100644
--- a/tools/perf/arch/powerpc/util/sym-handling.c
+++ b/tools/perf/arch/powerpc/util/sym-handling.c
@@ -13,7 +13,7 @@
int arch__choose_best_symbol(struct symbol *syma,
struct symbol *symb __maybe_unused)
{
- char *sym = syma->name;
+ const char *sym = symbol__name(syma);
#if !defined(_CALL_ELF) || _CALL_ELF != 2
/* Skip over any initial dot */
@@ -69,7 +69,7 @@ const char *arch__normalize_symbol_name(const char *name)
#ifdef HAVE_LIBELF_SUPPORT
void arch__sym_update(struct symbol *s, GElf_Sym *sym)
{
- s->arch_sym = sym->st_other;
+ symbol__set_arch_sym(s, sym->st_other);
}
#endif
@@ -102,7 +102,7 @@ void arch__fix_tev_from_maps(struct perf_probe_event *pev,
return;
}
- lep_offset = PPC64_LOCAL_ENTRY_OFFSET(sym->arch_sym);
+ lep_offset = PPC64_LOCAL_ENTRY_OFFSET(symbol__arch_sym(sym));
if (map__dso(map)->symtab_type == DSO_BINARY_TYPE__KALLSYMS)
tev->point.offset += PPC64LE_LEP_OFFSET;
@@ -131,7 +131,7 @@ void arch__post_process_probe_trace_events(struct perf_probe_event *pev,
for (i = 0; i < ntevs; i++) {
tev = &pev->tevs[i];
map__for_each_symbol(map, sym, tmp) {
- if (map__unmap_ip(map, sym->start) == tev->point.address) {
+ if (map__unmap_ip(map, symbol__start(sym)) == tev->point.address) {
arch__fix_tev_from_maps(pev, tev, map, sym);
break;
}
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c
index 8863f3a570f6..c93b67986334 100644
--- a/tools/perf/builtin-annotate.c
+++ b/tools/perf/builtin-annotate.c
@@ -252,7 +252,7 @@ static int add_sample(struct perf_sample *sample,
if ((!ann->has_br_stack || !has_annotation(ann)) &&
ann->sym_hist_filter != NULL &&
(al->sym == NULL ||
- strcmp(ann->sym_hist_filter, al->sym->name) != 0)) {
+ strcmp(ann->sym_hist_filter, symbol__name(al->sym)) != 0)) {
/* We're only interested in a symbol named sym_hist_filter */
/*
* FIXME: why isn't this done in the symbol_filter when loading
@@ -423,7 +423,7 @@ static void hists__find_annotations(struct hists *hists,
goto find_next;
if (ann->sym_hist_filter &&
- (strcmp(he->ms.sym->name, ann->sym_hist_filter) != 0))
+ (strcmp(symbol__name(he->ms.sym), ann->sym_hist_filter) != 0))
goto find_next;
if (ann->min_percent) {
diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c
index 9fa8e900637b..c263b848153b 100644
--- a/tools/perf/builtin-diff.c
+++ b/tools/perf/builtin-diff.c
@@ -1358,10 +1358,10 @@ static int cycles_printf(struct hist_entry *he, struct hist_entry *pair,
bi = block_he->block_info;
- start_line = map__srcline(he->ms.map, bi->sym->start + bi->start,
+ start_line = map__srcline(he->ms.map, symbol__start(bi->sym) + bi->start,
he->ms.sym);
- end_line = map__srcline(he->ms.map, bi->sym->start + bi->end,
+ end_line = map__srcline(he->ms.map, symbol__start(bi->sym) + bi->end,
he->ms.sym);
if (start_line != SRCLINE_UNKNOWN &&
diff --git a/tools/perf/builtin-kallsyms.c b/tools/perf/builtin-kallsyms.c
index 3c4339982b16..e99f61c3653e 100644
--- a/tools/perf/builtin-kallsyms.c
+++ b/tools/perf/builtin-kallsyms.c
@@ -48,9 +48,10 @@ static int __cmd_kallsyms(int argc, const char **argv)
dso = map__dso(map);
printf("%s: %s %s %#" PRIx64 "-%#" PRIx64 " (%#" PRIx64 "-%#" PRIx64")\n",
- symbol->name, dso__short_name(dso), dso__long_name(dso),
- map__unmap_ip(map, symbol->start), map__unmap_ip(map, symbol->end),
- symbol->start, symbol->end);
+ symbol__name(symbol), dso__short_name(dso), dso__long_name(dso),
+ map__unmap_ip(map, symbol__start(symbol)),
+ map__unmap_ip(map, symbol__end(symbol)),
+ symbol__start(symbol), symbol__end(symbol));
}
out:
machine__delete(machine);
diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c
index 8693c6b135ca..7af3fd9868a1 100644
--- a/tools/perf/builtin-kmem.c
+++ b/tools/perf/builtin-kmem.c
@@ -310,7 +310,7 @@ static struct rb_root page_caller_sorted;
struct alloc_func {
u64 start;
u64 end;
- char *name;
+ const char *name;
};
static int nr_alloc_funcs;
@@ -368,7 +368,7 @@ static int build_alloc_func_list(void)
}
map__for_each_symbol(kernel_map, sym, node) {
- if (regexec(&alloc_func_regex, sym->name, 0, NULL, 0))
+ if (regexec(&alloc_func_regex, symbol__name(sym), 0, NULL, 0))
continue;
func = realloc(alloc_func_list,
@@ -376,10 +376,10 @@ static int build_alloc_func_list(void)
if (func == NULL)
return -ENOMEM;
- pr_debug("alloc func: %s\n", sym->name);
- func[nr_alloc_funcs].start = sym->start;
- func[nr_alloc_funcs].end = sym->end;
- func[nr_alloc_funcs].name = sym->name;
+ pr_debug("alloc func: %s\n", symbol__name(sym));
+ func[nr_alloc_funcs].start = symbol__start(sym);
+ func[nr_alloc_funcs].end = symbol__end(sym);
+ func[nr_alloc_funcs].name = symbol__name(sym);
alloc_func_list = func;
nr_alloc_funcs++;
@@ -1048,11 +1048,12 @@ static void __print_slab_result(struct rb_root *root,
} else
addr = data->ptr;
- if (sym != NULL)
- snprintf(buf, sizeof(buf), "%s+%" PRIx64 "", sym->name,
- addr - map__unmap_ip(map, sym->start));
- else
+ if (sym != NULL) {
+ snprintf(buf, sizeof(buf), "%s+%" PRIx64 "", symbol__name(sym),
+ addr - map__unmap_ip(map, symbol__start(sym)));
+ } else {
snprintf(buf, sizeof(buf), "%#" PRIx64 "", addr);
+ }
printf(" %-34s |", buf);
printf(" %9llu/%-5lu | %9llu/%-5lu | %8lu | %9lu | %6.3f%%\n",
@@ -1105,12 +1106,12 @@ static void __print_page_alloc_result(struct perf_session *session, int n_lines)
struct symbol *sym;
struct map *map;
char buf[32];
- char *caller = buf;
+ const char *caller = buf;
data = rb_entry(next, struct page_stat, node);
sym = machine__find_kernel_symbol(machine, data->callsite, &map);
if (sym)
- caller = sym->name;
+ caller = symbol__name(sym);
else
scnprintf(buf, sizeof(buf), "%"PRIx64, data->callsite);
@@ -1147,12 +1148,12 @@ static void __print_page_caller_result(struct perf_session *session, int n_lines
struct symbol *sym;
struct map *map;
char buf[32];
- char *caller = buf;
+ const char *caller = buf;
data = rb_entry(next, struct page_stat, node);
sym = machine__find_kernel_symbol(machine, data->callsite, &map);
if (sym)
- caller = sym->name;
+ caller = symbol__name(sym);
else
scnprintf(buf, sizeof(buf), "%"PRIx64, data->callsite);
diff --git a/tools/perf/builtin-kwork.c b/tools/perf/builtin-kwork.c
index fce588441e49..98714f1ed949 100644
--- a/tools/perf/builtin-kwork.c
+++ b/tools/perf/builtin-kwork.c
@@ -768,8 +768,8 @@ static void timehist_save_callchain(struct perf_kwork *kwork,
sym = node->ms.sym;
if (sym) {
- if (!strcmp(sym->name, "__softirqentry_text_start") ||
- !strcmp(sym->name, "__do_softirq"))
+ if (!strcmp(symbol__name(sym), "__softirqentry_text_start") ||
+ !strcmp(symbol__name(sym), "__do_softirq"))
symbol__set_ignore(sym, true);
}
diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c
index 778923c92f42..e52e3785b657 100644
--- a/tools/perf/builtin-lock.c
+++ b/tools/perf/builtin-lock.c
@@ -826,12 +826,12 @@ static int get_symbol_name_offset(struct map *map, struct symbol *sym, u64 ip,
return 0;
}
- offset = map__map_ip(map, ip) - sym->start;
+ offset = map__map_ip(map, ip) - symbol__start(sym);
if (offset)
- return scnprintf(buf, size, "%s+%#lx", sym->name, offset);
+ return scnprintf(buf, size, "%s+%#lx", symbol__name(sym), offset);
else
- return strlcpy(buf, sym->name, size);
+ return strlcpy(buf, symbol__name(sym), size);
}
static int lock_contention_caller(struct perf_sample *sample,
char *buf, int size)
@@ -1006,7 +1006,7 @@ static int report_lock_contention_begin_event(struct perf_sample *sample)
return -ENOMEM;
}
- addrs[filters.nr_addrs++] = map__unmap_ip(kmap, sym->start);
+ addrs[filters.nr_addrs++] = map__unmap_ip(kmap, symbol__start(sym));
filters.addrs = addrs;
}
}
@@ -1059,7 +1059,7 @@ static int report_lock_contention_begin_event(struct perf_sample *sample)
case LOCK_AGGR_ADDR:
sym = machine__find_kernel_symbol(machine, key, &kmap);
if (sym)
- name = sym->name;
+ name = symbol__name(sym);
break;
case LOCK_AGGR_CALLER:
name = buf;
diff --git a/tools/perf/builtin-mem.c b/tools/perf/builtin-mem.c
index ade53688ebcd..5a19be73348d 100644
--- a/tools/perf/builtin-mem.c
+++ b/tools/perf/builtin-mem.c
@@ -249,7 +249,7 @@ dump_raw_samples(const struct perf_tool *tool,
sample->data_src,
symbol_conf.field_sep,
dso ? dso__long_name(dso) : "???",
- al.sym ? al.sym->name : "???");
+ al.sym ? symbol__name(al.sym) : "???");
out_put:
addr_location__exit(&al);
return 0;
diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c
index 39571f03eda2..fba0b9194eee 100644
--- a/tools/perf/builtin-sched.c
+++ b/tools/perf/builtin-sched.c
@@ -2629,9 +2629,9 @@ static void save_task_callchain(struct perf_sched *sched,
sym = node->ms.sym;
if (sym) {
- if (!strcmp(sym->name, "schedule") ||
- !strcmp(sym->name, "__schedule") ||
- !strcmp(sym->name, "preempt_schedule"))
+ if (!strcmp(symbol__name(sym), "schedule") ||
+ !strcmp(symbol__name(sym), "__schedule") ||
+ !strcmp(symbol__name(sym), "preempt_schedule"))
symbol__set_ignore(sym, true);
}
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c
index b6691ebb1b4e..7559d4d230f2 100644
--- a/tools/perf/builtin-script.c
+++ b/tools/perf/builtin-script.c
@@ -1408,7 +1408,7 @@ static int ip__fprintf_sym(uint64_t addr, struct thread *thread,
addr_location__init(&al);
thread__find_map(thread, cpumode, addr, &al);
- if ((*lastsym) && al.addr >= (*lastsym)->start && al.addr < (*lastsym)->end)
+ if ((*lastsym) && al.addr >= symbol__start((*lastsym)) && al.addr < symbol__end((*lastsym)))
goto out;
al.cpu = cpu;
@@ -1419,11 +1419,11 @@ static int ip__fprintf_sym(uint64_t addr, struct thread *thread,
if (!al.sym)
goto out;
- if (al.addr < al.sym->end)
- off = al.addr - al.sym->start;
+ if (al.addr < symbol__end(al.sym))
+ off = al.addr - symbol__start(al.sym);
else
- off = al.addr - map__start(al.map) - al.sym->start;
- printed += fprintf(fp, "\t%s", al.sym->name);
+ off = al.addr - map__start(al.map) - symbol__start(al.sym);
+ printed += fprintf(fp, "\t%s", symbol__name(al.sym));
if (off)
printed += fprintf(fp, "%+d", off);
printed += fprintf(fp, ":");
@@ -1642,7 +1642,7 @@ static const char *resolve_branch_sym(struct perf_sample *sample,
if (!addr_al->thread)
thread__resolve(thread, addr_al, sample);
if (addr_al->sym)
- name = addr_al->sym->name;
+ name = symbol__name(addr_al->sym);
else
*ip = sample->addr;
} else {
@@ -1650,7 +1650,7 @@ static const char *resolve_branch_sym(struct perf_sample *sample,
}
} else if (sample->flags & (PERF_IP_FLAG_RETURN | PERF_IP_FLAG_TRACE_END)) {
if (al->sym)
- name = al->sym->name;
+ name = symbol__name(al->sym);
else
*ip = sample->ip;
}
diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c
index 9eb335d9db16..52db3522aa77 100644
--- a/tools/perf/builtin-timechart.c
+++ b/tools/perf/builtin-timechart.c
@@ -563,7 +563,7 @@ static char *cat_backtrace(struct perf_sample *sample,
addr_location__init(&tal);
tal.filtered = 0;
if (thread__find_symbol(al.thread, cpumode, ip, &tal))
- fprintf(f, "..... %016" PRIx64 " %s\n", ip, tal.sym->name);
+ fprintf(f, "..... %016" PRIx64 " %s\n", ip, symbol__name(tal.sym));
else
fprintf(f, "..... %016" PRIx64 "\n", ip);
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
index a3442d2d6edf..d85a49d546eb 100644
--- a/tools/perf/builtin-top.c
+++ b/tools/perf/builtin-top.c
@@ -133,8 +133,8 @@ static int perf_top__parse_source(struct perf_top *top, struct hist_entry *he)
* We can't annotate with just /proc/kallsyms
*/
if (dso__symtab_type(dso) == DSO_BINARY_TYPE__KALLSYMS && !dso__is_kcore(dso)) {
- pr_err("Can't annotate %s: No vmlinux file was found in the "
- "path\n", sym->name);
+ pr_err("Can't annotate %s: No vmlinux file was found in the path\n",
+ symbol__name(sym));
sleep(1);
return -1;
}
@@ -145,7 +145,7 @@ static int perf_top__parse_source(struct perf_top *top, struct hist_entry *he)
if (!symbol__hists(sym, evlist__nr_entries(top->evlist))) {
annotation__unlock(notes);
pr_err("Not enough memory for annotating '%s' symbol!\n",
- sym->name);
+ symbol__name(sym));
sleep(1);
return err;
}
@@ -156,7 +156,7 @@ static int perf_top__parse_source(struct perf_top *top, struct hist_entry *he)
} else {
char msg[BUFSIZ];
symbol__strerror_disassemble(&he->ms, err, msg, sizeof(msg));
- pr_err("Couldn't annotate %s: %s\n", sym->name, msg);
+ pr_err("Couldn't annotate %s: %s\n", symbol__name(sym), msg);
}
annotation__unlock(notes);
@@ -186,9 +186,9 @@ static void ui__warn_map_erange(struct map *map, struct symbol *sym, u64 ip)
"Not all samples will be on the annotation output.\n\n"
"Please report to linux-kernel@vger.kernel.org\n",
ip, dso__long_name(dso), dso__symtab_origin(dso),
- map__start(map), map__end(map), sym->start, sym->end,
+ map__start(map), map__end(map), symbol__start(sym), symbol__end(sym),
symbol__binding(sym) == STB_GLOBAL ? 'g' :
- symbol__binding(sym) == STB_LOCAL ? 'l' : 'w', sym->name,
+ symbol__binding(sym) == STB_LOCAL ? 'l' : 'w', symbol__name(sym),
err ? "[unknown]" : uts.machine,
err ? "[unknown]" : uts.release, perf_version_string);
if (use_browser <= 0)
@@ -232,7 +232,7 @@ static void perf_top__record_precise_ip(struct perf_top *top,
ui__warn_map_erange(he->ms.map, sym, ip);
else if (err == -ENOMEM) {
pr_err("Not enough memory for annotating '%s' symbol!\n",
- sym->name);
+ symbol__name(sym));
sleep(1);
}
@@ -263,7 +263,7 @@ static void perf_top__show_details(struct perf_top *top)
if (notes->src == NULL)
goto out_unlock;
- printf("Showing %s for %s\n", evsel__name(top->sym_evsel), symbol->name);
+ printf("Showing %s for %s\n", evsel__name(top->sym_evsel), symbol__name(symbol));
printf(" Events Pcnt (>=%d%%)\n", annotate_opts.min_pcnt);
more = hist_entry__annotate_printf(he, top->sym_evsel);
@@ -418,7 +418,7 @@ static void perf_top__prompt_symbol(struct perf_top *top, const char *msg)
next = rb_first_cached(&hists->entries);
while (next) {
n = rb_entry(next, struct hist_entry, rb_node);
- if (n->ms.sym && !strcmp(buf, n->ms.sym->name)) {
+ if (n->ms.sym && !strcmp(buf, symbol__name(n->ms.sym))) {
found = n;
break;
}
@@ -437,11 +437,11 @@ static void perf_top__prompt_symbol(struct perf_top *top, const char *msg)
static void perf_top__print_mapped_keys(struct perf_top *top)
{
- char *name = NULL;
+ const char *name = NULL;
if (top->sym_filter_entry) {
struct symbol *sym = top->sym_filter_entry->ms.sym;
- name = sym->name;
+ name = symbol__name(sym);
}
fprintf(stdout, "\nMapped keys:\n");
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c
index 4094141461f9..7683b362783a 100644
--- a/tools/perf/builtin-trace.c
+++ b/tools/perf/builtin-trace.c
@@ -789,15 +789,16 @@ size_t syscall_arg__scnprintf_ksym(char *bf, size_t size, struct syscall_arg *ar
arg->val, &map);
if (sym) {
- u64 start = map__unmap_ip(map, sym->start);
+ u64 start = map__unmap_ip(map, symbol__start(sym));
u64 offset = arg->val - start;
size_t printed;
- if (offset == 0)
- printed = scnprintf(bf, size, "%s", sym->name);
- else
+ if (offset == 0) {
+ printed = scnprintf(bf, size, "%s", symbol__name(sym));
+ } else {
printed = scnprintf(bf, size, "%s+0x%" PRIx64,
- sym->name, offset);
+ symbol__name(sym), offset);
+ }
map__put(map);
return printed;
}
@@ -3669,13 +3670,14 @@ static void print_location(FILE *f, struct perf_sample *sample,
if ((verbose > 0 || print_dso) && al->map)
fprintf(f, "%s@", dso__long_name(map__dso(al->map)));
- if ((verbose > 0 || print_sym) && al->sym)
- fprintf(f, "%s+0x%" PRIx64, al->sym->name,
- al->addr - al->sym->start);
- else if (al->map)
+ if ((verbose > 0 || print_sym) && al->sym) {
+ fprintf(f, "%s+0x%" PRIx64, symbol__name(al->sym),
+ al->addr - symbol__start(al->sym));
+ } else if (al->map) {
fprintf(f, "0x%" PRIx64, al->addr);
- else
+ } else {
fprintf(f, "0x%" PRIx64, sample->addr);
+ }
}
static int trace__pgfault(struct trace *trace,
diff --git a/tools/perf/tests/dlfilter-test.c b/tools/perf/tests/dlfilter-test.c
index 204663571943..b4649c79c309 100644
--- a/tools/perf/tests/dlfilter-test.c
+++ b/tools/perf/tests/dlfilter-test.c
@@ -274,11 +274,11 @@ static int get_ip_addr(struct test_data *td)
sym = map__find_symbol_by_name(map, "foo");
if (sym)
- td->foo = sym->start;
+ td->foo = symbol__start(sym);
sym = map__find_symbol_by_name(map, "bar");
if (sym)
- td->bar = sym->start;
+ td->bar = symbol__start(sym);
map__put(map);
diff --git a/tools/perf/tests/dwarf-unwind.c b/tools/perf/tests/dwarf-unwind.c
index 9ed78d00fb87..b8b0c430a6b3 100644
--- a/tools/perf/tests/dwarf-unwind.c
+++ b/tools/perf/tests/dwarf-unwind.c
@@ -56,7 +56,7 @@ int test__dwarf_unwind(struct test_suite *test, int subtest);
static int unwind_entry(struct unwind_entry *entry, void *arg)
{
unsigned long *cnt = (unsigned long *) arg;
- char *symbol = entry->ms.sym ? entry->ms.sym->name : NULL;
+ const char *symbol = entry->ms.sym ? symbol__name(entry->ms.sym) : NULL;
static const char *funcs[MAX_STACK] = {
"test__arch_unwind_sample",
"test_dwarf_unwind__thread",
diff --git a/tools/perf/tests/hists_common.c b/tools/perf/tests/hists_common.c
index 187f12f5bc21..0db3ea9cf62e 100644
--- a/tools/perf/tests/hists_common.c
+++ b/tools/perf/tests/hists_common.c
@@ -184,7 +184,7 @@ void print_hists_in(struct hists *hists)
pr_info("%2d: entry: %-8s [%-8s] %20s: period = %"PRIu64"\n",
i, thread__comm_str(he->thread),
dso__short_name(dso),
- he->ms.sym->name, he->stat.period);
+ symbol__name(he->ms.sym), he->stat.period);
}
i++;
@@ -213,7 +213,7 @@ void print_hists_out(struct hists *hists)
pr_info("%2d: entry: %8s:%5d [%-8s] %20s: period = %"PRIu64"/%"PRIu64"\n",
i, thread__comm_str(he->thread), thread__tid(he->thread),
dso__short_name(dso),
- he->ms.sym->name, he->stat.period,
+ symbol__name(he->ms.sym), he->stat.period,
he->stat_acc ? he->stat_acc->period : 0);
}
diff --git a/tools/perf/tests/hists_cumulate.c b/tools/perf/tests/hists_cumulate.c
index c4a11a3255bc..95b9a629e59e 100644
--- a/tools/perf/tests/hists_cumulate.c
+++ b/tools/perf/tests/hists_cumulate.c
@@ -164,11 +164,11 @@ typedef int (*test_fn_t)(struct evsel *, struct machine *);
#define COMM(he) (thread__comm_str(he->thread))
#define DSO(he) (dso__short_name(map__dso(he->ms.map)))
-#define SYM(he) (he->ms.sym->name)
+#define SYM(he) (symbol__name(he->ms.sym))
#define CPU(he) (he->cpu)
#define DEPTH(he) (he->callchain->max_depth)
#define CDSO(cl) (dso__short_name(map__dso(cl->ms.map)))
-#define CSYM(cl) (cl->ms.sym->name)
+#define CSYM(cl) (symbol__name(cl->ms.sym))
struct result {
u64 children;
diff --git a/tools/perf/tests/hists_output.c b/tools/perf/tests/hists_output.c
index 35d230a83e43..77fb3a187139 100644
--- a/tools/perf/tests/hists_output.c
+++ b/tools/perf/tests/hists_output.c
@@ -129,7 +129,7 @@ typedef int (*test_fn_t)(struct evsel *, struct machine *);
#define COMM(he) (thread__comm_str(he->thread))
#define DSO(he) (dso__short_name(map__dso(he->ms.map)))
-#define SYM(he) (he->ms.sym->name)
+#define SYM(he) (symbol__name(he->ms.sym))
#define CPU(he) (he->cpu)
#define PID(he) (thread__tid(he->thread))
diff --git a/tools/perf/tests/symbols.c b/tools/perf/tests/symbols.c
index b644d95c6be9..456ef7a70e28 100644
--- a/tools/perf/tests/symbols.c
+++ b/tools/perf/tests/symbols.c
@@ -130,14 +130,14 @@ static int test_dso(struct dso *dso)
continue;
/* Check for overlapping function symbols */
- if (last_sym && sym->start < last_sym->end) {
+ if (last_sym && symbol__start(sym) < symbol__end(last_sym)) {
pr_debug("Overlapping symbols:\n");
symbol__fprintf(last_sym, stderr);
symbol__fprintf(sym, stderr);
ret = TEST_FAIL;
}
/* Check for zero-length function symbol */
- if (sym->start == sym->end) {
+ if (symbol__start(sym) == symbol__end(sym)) {
pr_debug("Zero-length symbol:\n");
symbol__fprintf(sym, stderr);
ret = TEST_FAIL;
@@ -250,14 +250,14 @@ static int check_symbol(struct dso *dso, u64 addr, const char *name, u64 end)
{
struct symbol *sym = dso__find_symbol_nocache(dso, addr);
- if (!sym || strcmp(sym->name, name)) {
+ if (!sym || strcmp(symbol__name(sym), name)) {
pr_debug("%#" PRIx64 ": expected %s, got %s\n", addr, name,
- sym ? sym->name : "no symbol");
+ sym ? symbol__name(sym) : "no symbol");
return TEST_FAIL;
}
- if (sym->end != end) {
+ if (symbol__end(sym) != end) {
pr_debug("%s: expected end %#" PRIx64 ", got %#" PRIx64 "\n",
- name, end, sym->end);
+ name, end, symbol__end(sym));
return TEST_FAIL;
}
return TEST_OK;
diff --git a/tools/perf/tests/vmlinux-kallsyms.c b/tools/perf/tests/vmlinux-kallsyms.c
index 9396c8a77c86..9b6f2d836d8f 100644
--- a/tools/perf/tests/vmlinux-kallsyms.c
+++ b/tools/perf/tests/vmlinux-kallsyms.c
@@ -292,18 +292,19 @@ static int test__vmlinux_matches_kallsyms(struct test_suite *test __maybe_unused
sym = rb_entry(nd, struct symbol, rb_node);
- if (sym->start == sym->end)
+ if (symbol__start(sym) == symbol__end(sym))
continue;
- mem_start = map__unmap_ip(args.vmlinux_map, sym->start);
- mem_end = map__unmap_ip(args.vmlinux_map, sym->end);
+ mem_start = map__unmap_ip(args.vmlinux_map, symbol__start(sym));
+ mem_end = map__unmap_ip(args.vmlinux_map, symbol__end(sym));
first_pair = machine__find_kernel_symbol(&args.kallsyms, mem_start, NULL);
pair = first_pair;
- if (pair && UM(pair->start) == mem_start) {
+ if (pair && UM(symbol__start(pair)) == mem_start) {
next_pair:
- if (arch__compare_symbol_names(sym->name, pair->name) == 0) {
+ if (arch__compare_symbol_names(symbol__name(sym),
+ symbol__name(pair)) == 0) {
/*
* kallsyms don't have the symbol end, so we
* set that by using the next symbol start - 1,
@@ -313,11 +314,13 @@ static int test__vmlinux_matches_kallsyms(struct test_suite *test __maybe_unused
* off the real size. More than that and we
* _really_ have a problem.
*/
- s64 skew = mem_end - UM(pair->end);
- if (llabs(skew) >= page_size)
+ s64 skew = mem_end - UM(symbol__end(pair));
+
+ if (llabs(skew) >= page_size) {
pr_debug("WARN: %#" PRIx64 ": diff end addr for %s v: %#" PRIx64 " k: %#" PRIx64 "\n",
- mem_start, sym->name, mem_end,
- UM(pair->end));
+ mem_start, symbol__name(sym), mem_end,
+ UM(symbol__end(pair)));
+ }
/*
* Do not count this as a failure, because we
@@ -328,16 +331,17 @@ static int test__vmlinux_matches_kallsyms(struct test_suite *test __maybe_unused
continue;
} else {
pair = machine__find_kernel_symbol_by_name(&args.kallsyms,
- sym->name, NULL);
+ symbol__name(sym), NULL);
if (pair) {
- if (UM(pair->start) == mem_start)
+ if (UM(symbol__start(pair)) == mem_start)
goto next_pair;
pr_debug("WARN: %#" PRIx64 ": diff name v: %s k: %s\n",
- mem_start, sym->name, pair->name);
+ mem_start, symbol__name(sym), symbol__name(pair));
} else {
pr_debug("WARN: %#" PRIx64 ": diff name v: %s k: %s\n",
- mem_start, sym->name, first_pair->name);
+ mem_start, symbol__name(sym),
+ symbol__name(first_pair));
}
continue;
@@ -348,14 +352,14 @@ static int test__vmlinux_matches_kallsyms(struct test_suite *test __maybe_unused
* such as __indirect_thunk_end.
*/
continue;
- } else if (is_ignored_symbol(sym->name, symbol__type(sym))) {
+ } else if (is_ignored_symbol(symbol__name(sym), symbol__type(sym))) {
/*
* Ignore hidden symbols, see scripts/kallsyms.c for the details
*/
continue;
} else {
pr_debug("ERR : %#" PRIx64 ": %s not on kallsyms\n",
- mem_start, sym->name);
+ mem_start, symbol__name(sym));
}
err = -1;
diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c
index cf36b5347f72..66b7b42ea197 100644
--- a/tools/perf/ui/browsers/annotate.c
+++ b/tools/perf/ui/browsers/annotate.c
@@ -196,7 +196,7 @@ static void annotate_browser__draw_current_jump(struct ui_browser *browser)
int diff = 0;
/* PLT symbols contain external offsets */
- if (strstr(sym->name, "@plt"))
+ if (strstr(symbol__name(sym), "@plt"))
return;
if (!disasm_line__is_valid_local_jump(cursor, sym))
@@ -554,7 +554,7 @@ static void ui_browser__init_asm_mode(struct ui_browser *browser)
static int sym_title(struct symbol *sym, struct map *map, char *title,
size_t sz, int percent_type)
{
- return snprintf(title, sz, "%s %s [Percent: %s] %s", sym->name,
+ return snprintf(title, sz, "%s %s [Percent: %s] %s", symbol__name(sym),
dso__long_name(map__dso(map)),
percent_type_str(percent_type),
annotate_opts.code_with_type ? "[Type]" : "");
@@ -602,7 +602,7 @@ static bool annotate_browser__callq(struct annotate_browser *browser,
if (!symbol__hists(dl->ops.target.sym, evlist__nr_entries(evsel->evlist))) {
annotation__unlock(notes);
ui__warning("Not enough memory for annotating '%s' symbol!\n",
- dl->ops.target.sym->name);
+ symbol__name(dl->ops.target.sym));
return true;
}
@@ -876,7 +876,7 @@ static void annotate_browser__symbol_annotate_error(struct annotate_browser *bro
dso__set_annotate_warned(dso);
symbol__strerror_disassemble(ms, err, msg, sizeof(msg));
- ui__error("Couldn't annotate %s:\n%s", sym->name, msg);
+ ui__error("Couldn't annotate %s:\n%s", symbol__name(sym), msg);
}
static int annotate_browser__run(struct annotate_browser *browser,
@@ -1255,7 +1255,7 @@ int __hist_entry__tui_annotate(struct hist_entry *he, struct map_symbol *ms,
*/
if (al_addr != NO_ADDR) {
struct annotation_line *al = annotated_source__get_line(notes->src,
- al_addr - sym->start);
+ al_addr - symbol__start(sym));
browser.selection = al;
}
diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c
index 593e1fd5759d..206657039223 100644
--- a/tools/perf/ui/browsers/hists.c
+++ b/tools/perf/ui/browsers/hists.c
@@ -2548,7 +2548,7 @@ add_annotate_opt(struct popup_action *act, char **optstr,
if (ms->sym == NULL || symbol__annotation(ms->sym)->src == NULL)
return 0;
- if (asprintf(optstr, "Annotate %s", ms->sym->name) < 0)
+ if (asprintf(optstr, "Annotate %s", symbol__name(ms->sym)) < 0)
return 0;
map_symbol__copy(&act->ms, ms);
@@ -2748,7 +2748,7 @@ do_run_script(struct hist_browser *browser,
if (act->ms.thread)
len += strlen(thread__comm_str(act->ms.thread));
else if (act->ms.sym)
- len += strlen(act->ms.sym->name);
+ len += strlen(symbol__name(act->ms.sym));
script_opt = malloc(len);
if (!script_opt)
return -1;
@@ -2759,7 +2759,7 @@ do_run_script(struct hist_browser *browser,
thread__comm_str(act->ms.thread));
} else if (act->ms.sym) {
n = scnprintf(script_opt, len, " -S %s ",
- act->ms.sym->name);
+ symbol__name(act->ms.sym));
}
if (act->time) {
@@ -2804,7 +2804,7 @@ add_script_opt_2(struct popup_action *act, char **optstr,
return 0;
} else if (sym) {
if (asprintf(optstr, "Run scripts for samples of symbol [%s]%s",
- sym->name, tstr) < 0)
+ symbol__name(sym), tstr) < 0)
return 0;
} else {
if (asprintf(optstr, "Run scripts for all samples%s", tstr) < 0)
@@ -3208,7 +3208,7 @@ static int evsel__hists_browse(struct evsel *evsel, int nr_events, const char *h
ui_browser__warning(&browser->b, delay_secs * 2,
"No samples for the \"%s\" symbol.\n\n"
"Probably appeared just in a callchain",
- browser->selection->sym->name);
+ symbol__name(browser->selection->sym));
continue;
}
diff --git a/tools/perf/ui/browsers/map.c b/tools/perf/ui/browsers/map.c
index 075a575cdc5d..47f2743cae26 100644
--- a/tools/perf/ui/browsers/map.c
+++ b/tools/perf/ui/browsers/map.c
@@ -31,12 +31,12 @@ static void map_browser__write(struct ui_browser *browser, void *nd, int row)
ui_browser__set_percent_color(browser, 0, current_entry);
ui_browser__printf(browser, "%*" PRIx64 " %*" PRIx64 " %c ",
- mb->addrlen, sym->start, mb->addrlen, sym->end,
+ mb->addrlen, symbol__start(sym), mb->addrlen, symbol__end(sym),
symbol__binding(sym) == STB_GLOBAL ? 'g' :
symbol__binding(sym) == STB_LOCAL ? 'l' : 'w');
width = browser->width - ((mb->addrlen * 2) + 4);
if (width > 0)
- ui_browser__write_nstring(browser, sym->name, width);
+ ui_browser__write_nstring(browser, symbol__name(sym), width);
}
/* FIXME uber-kludgy, see comment on cmd_report... */
@@ -122,8 +122,8 @@ int map__browse(struct map *map)
for (nd = rb_first(mb.b.entries); nd; nd = rb_next(nd)) {
struct symbol *pos = rb_entry(nd, struct symbol, rb_node);
- if (maxaddr < pos->end)
- maxaddr = pos->end;
+ if (maxaddr < symbol__end(pos))
+ maxaddr = symbol__end(pos);
if (verbose > 0) {
u32 *idx = symbol__browser_index(pos);
*idx = mb.b.nr_entries;
diff --git a/tools/perf/ui/gtk/annotate.c b/tools/perf/ui/gtk/annotate.c
index 094dfa045b77..2307a7f609f8 100644
--- a/tools/perf/ui/gtk/annotate.c
+++ b/tools/perf/ui/gtk/annotate.c
@@ -67,7 +67,7 @@ static int perf_gtk__get_percent(char *buf, size_t size, struct symbol *sym,
static int perf_gtk__get_offset(char *buf, size_t size, struct map_symbol *ms,
struct disasm_line *dl)
{
- u64 start = map__rip_2objdump(ms->map, ms->sym->start);
+ u64 start = map__rip_2objdump(ms->map, symbol__start(ms->sym));
strcpy(buf, "");
@@ -192,7 +192,7 @@ static int symbol__gtk_annotate(struct map_symbol *ms, struct evsel *evsel,
dso__set_annotate_warned(dso);
symbol__strerror_disassemble(ms, err, msg, sizeof(msg));
- ui__error("Couldn't annotate %s: %s\n", sym->name, msg);
+ ui__error("Couldn't annotate %s: %s\n", symbol__name(sym), msg);
return -1;
}
@@ -234,7 +234,7 @@ static int symbol__gtk_annotate(struct map_symbol *ms, struct evsel *evsel,
}
scrolled_window = gtk_scrolled_window_new();
- tab_label = gtk_label_new(sym->name);
+ tab_label = gtk_label_new(symbol__name(sym));
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
GTK_POLICY_AUTOMATIC,
diff --git a/tools/perf/ui/stdio/hist.c b/tools/perf/ui/stdio/hist.c
index 8c4c8925df2c..f80cc73909ed 100644
--- a/tools/perf/ui/stdio/hist.c
+++ b/tools/perf/ui/stdio/hist.c
@@ -100,13 +100,12 @@ static struct callchain_list rem_hits;
static void init_rem_hits(void)
{
- rem_sq_bracket = malloc(sizeof(*rem_sq_bracket) + 6);
+ rem_sq_bracket = symbol__new(0, 0, 0, 0, "[...]");
if (!rem_sq_bracket) {
fprintf(stderr, "Not enough memory to display remaining hits\n");
return;
}
- strcpy(rem_sq_bracket->name, "[...]");
rem_hits.ms.sym = rem_sq_bracket;
}
diff --git a/tools/perf/util/annotate-arch/annotate-arm64.c b/tools/perf/util/annotate-arch/annotate-arm64.c
index 81369ae47290..5ad03c519a5d 100644
--- a/tools/perf/util/annotate-arch/annotate-arm64.c
+++ b/tools/perf/util/annotate-arch/annotate-arm64.c
@@ -477,12 +477,12 @@ static void update_call_insn_state(struct type_state *state,
Dwarf_Die type_die;
u32 insn_offset = dl->al.offset;
- call_name = func ? func->name : dl->ops.target.name;
+ call_name = func ? symbol__name(func) : dl->ops.target.name;
pr_debug_dtp("call [%x] %s\n", insn_offset, call_name ?: "<unknown>");
/* Invalidate caller-saved registers after call */
call_addr = map__rip_2objdump(dloc->ms->map,
- dloc->ms->sym->start + dl->al.offset);
+ symbol__start(dloc->ms->sym) + dl->al.offset);
for (unsigned int i = 0; i < ARRAY_SIZE(state->regs); i++) {
struct type_state_reg *reg = &state->regs[i];
@@ -640,7 +640,7 @@ static int propagate_load_reg_state(struct type_state *state,
tsr->lifetime_end = 0;
if (dloc->fb_cfa) {
- u64 ip = dloc->ms->sym->start + dl->al.offset;
+ u64 ip = symbol__start(dloc->ms->sym) + dl->al.offset;
u64 pc = map__rip_2objdump(dloc->ms->map, ip);
if (die_get_cfa(dloc->di->dbg, pc, &fbreg, &fboff) < 0)
@@ -727,7 +727,7 @@ static int propagate_load_reg_state(struct type_state *state,
/* Or check if it's a global variable */
if (src_tsr->kind == TSR_KIND_GLOBAL_ADDR) {
- u64 ip = dloc->ms->sym->start + dl->al.offset;
+ u64 ip = symbol__start(dloc->ms->sym) + dl->al.offset;
u64 addr = src_tsr->imm_value + reg_offset;
int offset;
u8 kind;
@@ -875,7 +875,7 @@ static void propagate_store_reg_state(struct type_state *state,
reg_offset += mem_spacing;
if (dloc->fb_cfa) {
- u64 ip = dloc->ms->sym->start + dl->al.offset;
+ u64 ip = symbol__start(dloc->ms->sym) + dl->al.offset;
u64 pc = map__rip_2objdump(dloc->ms->map, ip);
if (die_get_cfa(dloc->di->dbg, pc, &fbreg, &fboff) < 0)
@@ -1144,7 +1144,7 @@ static void update_mrs_insn_state(struct type_state *state,
return;
}
- ip = dloc->ms->sym->start + dl->al.offset;
+ ip = symbol__start(dloc->ms->sym) + dl->al.offset;
pc = map__rip_2objdump(dloc->ms->map, ip);
/*
diff --git a/tools/perf/util/annotate-arch/annotate-loongarch.c b/tools/perf/util/annotate-arch/annotate-loongarch.c
index a949b4392dd4..d275730080b7 100644
--- a/tools/perf/util/annotate-arch/annotate-loongarch.c
+++ b/tools/perf/util/annotate-arch/annotate-loongarch.c
@@ -94,8 +94,8 @@ static int loongarch_jump__parse(const struct arch *arch, struct ins_operands *o
ops->target.addr = strtoull(ops->raw, NULL, 16);
target.addr = map__objdump_2mem(map, ops->target.addr);
- start = map__unmap_ip(map, sym->start);
- end = map__unmap_ip(map, sym->end);
+ start = map__unmap_ip(map, symbol__start(sym));
+ end = map__unmap_ip(map, symbol__end(sym));
ops->target.outside = target.addr < start || target.addr >= end;
diff --git a/tools/perf/util/annotate-arch/annotate-x86.c b/tools/perf/util/annotate-arch/annotate-x86.c
index 3a6ea7e978e0..194fa9aff885 100644
--- a/tools/perf/util/annotate-arch/annotate-x86.c
+++ b/tools/perf/util/annotate-arch/annotate-x86.c
@@ -230,7 +230,7 @@ static void update_insn_state_x86(struct type_state *state,
/* Try to resolve the call target name */
if (func)
- call_name = func->name;
+ call_name = symbol__name(func);
else
call_name = dl->ops.target.name;
@@ -245,7 +245,7 @@ static void update_insn_state_x86(struct type_state *state,
/* Invalidate caller-saved registers after call */
call_addr = map__rip_2objdump(dloc->ms->map,
- dloc->ms->sym->start + dl->al.offset);
+ symbol__start(dloc->ms->sym) + dl->al.offset);
for (unsigned i = 0; i < ARRAY_SIZE(state->regs); i++) {
struct type_state_reg *reg = &state->regs[i];
@@ -277,7 +277,7 @@ static void update_insn_state_x86(struct type_state *state,
int offset;
const char *var_name = NULL;
struct map_symbol *ms = dloc->ms;
- u64 ip = ms->sym->start + dl->al.offset;
+ u64 ip = symbol__start(ms->sym) + dl->al.offset;
if (!has_reg_type(state, dst->reg1))
return;
@@ -478,7 +478,7 @@ static void update_insn_state_x86(struct type_state *state,
return;
if (dloc->fb_cfa) {
- u64 ip = dloc->ms->sym->start + dl->al.offset;
+ u64 ip = symbol__start(dloc->ms->sym) + dl->al.offset;
u64 pc = map__rip_2objdump(dloc->ms->map, ip);
if (die_get_cfa(dloc->di->dbg, pc, &fbreg, &fboff) < 0)
@@ -495,7 +495,7 @@ static void update_insn_state_x86(struct type_state *state,
if (dso__kernel(map__dso(dloc->ms->map)) &&
src->segment == INSN_SEG_X86_GS && src->imm) {
- u64 ip = dloc->ms->sym->start + dl->al.offset;
+ u64 ip = symbol__start(dloc->ms->sym) + dl->al.offset;
u64 var_addr;
int offset;
@@ -646,7 +646,7 @@ static void update_insn_state_x86(struct type_state *state,
/* Or check if it's a global variable */
else if (sreg == DWARF_REG_PC) {
struct map_symbol *ms = dloc->ms;
- u64 ip = ms->sym->start + dl->al.offset;
+ u64 ip = symbol__start(ms->sym) + dl->al.offset;
u64 addr;
int offset;
@@ -671,7 +671,7 @@ static void update_insn_state_x86(struct type_state *state,
/* And check percpu access with base register */
else if (has_reg_type(state, sreg) &&
state->regs[sreg].kind == TSR_KIND_PERCPU_BASE) {
- u64 ip = dloc->ms->sym->start + dl->al.offset;
+ u64 ip = symbol__start(dloc->ms->sym) + dl->al.offset;
u64 var_addr = src->offset;
int offset;
diff --git a/tools/perf/util/annotate-data.c b/tools/perf/util/annotate-data.c
index b3de1ca688a2..e430bb0ea043 100644
--- a/tools/perf/util/annotate-data.c
+++ b/tools/perf/util/annotate-data.c
@@ -887,9 +887,9 @@ bool get_global_var_info(struct data_loc_info *dloc, u64 addr,
sym = thread__find_symbol_fb(dloc->thread, dloc->cpumode,
mem_addr, &al);
if (sym) {
- *var_name = sym->name;
+ *var_name = symbol__name(sym);
/* Calculate type offset from the start of variable */
- *var_offset = mem_addr - map__unmap_ip(al.map, sym->start);
+ *var_offset = mem_addr - map__unmap_ip(al.map, symbol__start(sym));
} else {
*var_name = NULL;
}
@@ -1657,7 +1657,7 @@ static enum type_match_result find_data_type_insn(struct data_loc_info *dloc,
bb->begin->al.offset, bb->end->al.offset);
list_for_each_entry_from(dl, ¬es->src->source, al.node) {
- u64 this_ip = sym->start + dl->al.offset;
+ u64 this_ip = symbol__start(sym) + dl->al.offset;
u64 addr = map__rip_2objdump(dloc->ms->map, this_ip);
/* Skip comment or debug info lines */
@@ -1737,8 +1737,8 @@ static enum type_match_result find_data_type_block(struct data_loc_info *dloc,
}
pr_debug_dtp("cannot find a basic block from %"PRIx64" to %"PRIx64"\n",
- src_ip - dloc->ms->sym->start,
- dst_ip - dloc->ms->sym->start);
+ src_ip - symbol__start(dloc->ms->sym),
+ dst_ip - symbol__start(dloc->ms->sym));
continue;
}
prepend_basic_blocks(&this_blocks, &basic_blocks);
@@ -1817,7 +1817,7 @@ static int find_data_type_die(struct data_loc_info *dloc, Dwarf_Die *type_die)
pr_debug_dtp("-----------------------------------------------------------\n");
pr_debug_dtp("find data type for %s%#x(%s) at %s+%#"PRIx64"\n",
offset_sign, offset, buf,
- dloc->ms->sym->name, dloc->ip - dloc->ms->sym->start);
+ symbol__name(dloc->ms->sym), dloc->ip - symbol__start(dloc->ms->sym));
/*
* IP is a relative instruction address from the start of the map, as
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c
index 86a4beec6528..4163689b6b2c 100644
--- a/tools/perf/util/annotate.c
+++ b/tools/perf/util/annotate.c
@@ -237,18 +237,20 @@ static int __symbol__inc_addr_samples(struct map_symbol *ms,
pr_debug3("%s: addr=%#" PRIx64 "\n", __func__, map__unmap_ip(ms->map, addr));
- if ((addr < sym->start || addr >= sym->end) &&
- (addr != sym->end || sym->start != sym->end)) {
+ if ((addr < symbol__start(sym) || addr >= symbol__end(sym)) &&
+ (addr != symbol__end(sym) || symbol__start(sym) != symbol__end(sym))) {
pr_debug("%s(%d): ERANGE! sym->name=%s, start=%#" PRIx64 ", addr=%#" PRIx64 ", end=%#" PRIx64 "\n",
- __func__, __LINE__, sym->name, sym->start, addr, sym->end);
+ __func__, __LINE__, symbol__name(sym),
+ symbol__start(sym), addr, symbol__end(sym));
return -ERANGE;
}
- offset = addr - sym->start;
+ offset = addr - symbol__start(sym);
h = annotated_source__histogram(src, evsel);
if (h == NULL) {
pr_debug("%s(%d): ENOMEM! sym->name=%s, start=%#" PRIx64 ", addr=%#" PRIx64 ", end=%#" PRIx64 ", func: %d\n",
- __func__, __LINE__, sym->name, sym->start, addr, sym->end,
+ __func__, __LINE__, symbol__name(sym),
+ symbol__start(sym), addr, symbol__end(sym),
symbol__type(sym) == STT_FUNC);
return -ENOMEM;
}
@@ -288,7 +290,8 @@ static int __symbol__inc_addr_samples(struct map_symbol *ms,
pr_debug3("%#" PRIx64 " %s: period++ [addr: %#" PRIx64 ", %#" PRIx64
", evidx=%d] => nr_samples: %" PRIu64 ", period: %" PRIu64
" weight %" PRIu64 "\n",
- sym->start, sym->name, addr, addr - sym->start, evsel->core.idx,
+ symbol__start(sym), symbol__name(sym), addr,
+ addr - symbol__start(sym), evsel->core.idx,
entry->nr_samples, entry->period, weight);
return 0;
}
@@ -402,18 +405,18 @@ static int symbol__account_cycles(u64 addr, u64 start, struct symbol *sym,
branch = symbol__find_branch_hist(sym, evlist__nr_br_cntr(evsel->evlist));
if (!branch)
return -ENOMEM;
- if (addr < sym->start || addr >= sym->end)
+ if (addr < symbol__start(sym) || addr >= symbol__end(sym))
return -ERANGE;
if (start) {
- if (start < sym->start || start >= sym->end)
+ if (start < symbol__start(sym) || start >= symbol__end(sym))
return -ERANGE;
if (start >= addr)
start = 0;
}
- offset = addr - sym->start;
+ offset = addr - symbol__start(sym);
ret = __symbol__account_cycles(branch->cycles_hist,
- start ? start - sym->start : 0,
+ start ? start - symbol__start(sym) : 0,
offset, cycles,
!!start);
@@ -445,13 +448,13 @@ int addr_map_symbol__account_cycles(struct addr_map_symbol *ams,
if (start &&
(start->ms.sym == ams->ms.sym ||
(ams->ms.sym &&
- start->addr == ams->ms.sym->start + map__start(ams->ms.map))))
+ start->addr == symbol__start(ams->ms.sym) + map__start(ams->ms.map))))
saddr = start->al_addr;
if (saddr == 0)
pr_debug2("BB with bad start: addr %"PRIx64" start %"PRIx64" sym %"PRIx64" saddr %"PRIx64"\n",
ams->addr,
start ? start->addr : 0,
- ams->ms.sym ? ams->ms.sym->start + map__start(ams->ms.map) : 0,
+ ams->ms.sym ? symbol__start(ams->ms.sym) + map__start(ams->ms.map) : 0,
saddr);
err = symbol__account_cycles(ams->al_addr, saddr, ams->ms.sym, cycles, evsel, br_cntr);
if (err)
@@ -1165,9 +1168,9 @@ int symbol__annotate(struct map_symbol *ms, struct evsel *evsel,
notes->src->nr_events = nr ? nr : 1;
if (annotate_opts.full_addr)
- notes->src->start = map__objdump_2mem(ms->map, ms->sym->start);
+ notes->src->start = map__objdump_2mem(ms->map, symbol__start(ms->sym));
else
- notes->src->start = map__rip_2objdump(ms->map, ms->sym->start);
+ notes->src->start = map__rip_2objdump(ms->map, symbol__start(ms->sym));
return symbol__disassemble(sym, &args);
}
@@ -1307,9 +1310,10 @@ static void symbol__annotate_hits(struct symbol *sym, struct evsel *evsel)
struct sym_hist_entry *entry;
entry = annotated_source__hist_entry(notes->src, evsel, offset);
- if (entry && entry->nr_samples != 0)
+ if (entry && entry->nr_samples != 0) {
printf("%*" PRIx64 ": %" PRIu64 "\n", BITS_PER_LONG / 2,
- sym->start + offset, entry->nr_samples);
+ symbol__start(sym) + offset, entry->nr_samples);
+ }
}
printf("%*s: %" PRIu64 "\n", BITS_PER_LONG / 2, "h->nr_samples", h->nr_samples);
}
@@ -1521,7 +1525,7 @@ int map_symbol__annotation_dump(struct map_symbol *ms, struct evsel *evsel,
.evsel = evsel,
};
- if (asprintf(&filename, "%s.annotation", ms->sym->name) < 0)
+ if (asprintf(&filename, "%s.annotation", symbol__name(ms->sym)) < 0)
return -1;
fp = fopen(filename, "w");
@@ -1534,7 +1538,7 @@ int map_symbol__annotation_dump(struct map_symbol *ms, struct evsel *evsel,
}
fprintf(fp, "%s() %s\nEvent: %s\n\n",
- ms->sym->name, dso__long_name(map__dso(ms->map)), ev_name);
+ symbol__name(ms->sym), dso__long_name(map__dso(ms->map)), ev_name);
symbol__annotate_fprintf2(ms->sym, fp, &apd);
fclose(fp);
@@ -1629,7 +1633,7 @@ annotation__mark_jump_targets(struct annotation *notes, struct symbol *sym)
struct annotation_line *al;
/* PLT symbols contain external offsets */
- if (strstr(sym->name, "@plt"))
+ if (strstr(symbol__name(sym), "@plt"))
return;
list_for_each_entry(al, ¬es->src->source, node) {
@@ -1708,7 +1712,7 @@ annotation__init_column_widths(struct annotation *notes, struct symbol *sym)
{
notes->src->widths.addr = notes->src->widths.target =
notes->src->widths.min_addr = hex_width(symbol__size(sym));
- notes->src->widths.max_addr = hex_width(sym->end);
+ notes->src->widths.max_addr = hex_width(symbol__end(sym));
notes->src->widths.jumps = width_jumps(notes->src->max_jump_sources);
notes->src->widths.max_ins_name = annotation__max_ins_name(notes);
}
@@ -1733,9 +1737,9 @@ void annotation__toggle_full_addr(struct annotation *notes, struct map_symbol *m
annotate_opts.full_addr = !annotate_opts.full_addr;
if (annotate_opts.full_addr)
- notes->src->start = map__objdump_2mem(ms->map, ms->sym->start);
+ notes->src->start = map__objdump_2mem(ms->map, symbol__start(ms->sym));
else
- notes->src->start = map__rip_2objdump(ms->map, ms->sym->start);
+ notes->src->start = map__rip_2objdump(ms->map, symbol__start(ms->sym));
annotation__update_column_widths(notes);
}
@@ -1764,9 +1768,9 @@ static void annotation__calc_lines(struct annotation *notes, struct map_symbol *
if (percent_max <= 0.5)
continue;
- addr = map__rip_2objdump(ms->map, ms->sym->start);
+ addr = map__rip_2objdump(ms->map, symbol__start(ms->sym));
al->path = get_srcline(map__dso(ms->map), addr + al->offset, NULL,
- false, true, ms->sym->start + al->offset);
+ false, true, symbol__start(ms->sym) + al->offset);
insert_source_line(&tmp_root, al);
}
@@ -1800,7 +1804,7 @@ int hist_entry__tty_annotate2(struct hist_entry *he, struct evsel *evsel)
dso__set_annotate_warned(dso);
symbol__strerror_disassemble(ms, err, msg, sizeof(msg));
- ui__error("Couldn't annotate %s:\n%s", sym->name, msg);
+ ui__error("Couldn't annotate %s:\n%s", symbol__name(sym), msg);
return -1;
}
@@ -1812,7 +1816,8 @@ int hist_entry__tty_annotate2(struct hist_entry *he, struct evsel *evsel)
hists__scnprintf_title(hists, buf, sizeof(buf));
fprintf(stdout, "%s, [percent: %s]\n%s() %s\n",
- buf, percent_type_str(annotate_opts.percent_type), sym->name, dso__long_name(dso));
+ buf, percent_type_str(annotate_opts.percent_type),
+ symbol__name(sym), dso__long_name(dso));
symbol__annotate_fprintf2(sym, stdout, &apd);
annotated_source__purge(symbol__annotation(sym)->src);
@@ -1834,7 +1839,7 @@ int hist_entry__tty_annotate(struct hist_entry *he, struct evsel *evsel)
dso__set_annotate_warned(dso);
symbol__strerror_disassemble(ms, err, msg, sizeof(msg));
- ui__error("Couldn't annotate %s:\n%s", sym->name, msg);
+ ui__error("Couldn't annotate %s:\n%s", symbol__name(sym), msg);
return -1;
}
@@ -2682,7 +2687,7 @@ static struct disasm_line *find_disasm_line(struct symbol *sym, u64 ip,
if (dl->al.offset == -1)
continue;
- if (sym->start + dl->al.offset == ip) {
+ if (symbol__start(sym) + dl->al.offset == ip) {
/*
* llvm-objdump places "lock" in a separate line and
* in that case, we want to get the next line.
@@ -2818,7 +2823,7 @@ u64 annotate_calc_pcrel(struct map_symbol *ms, u64 ip, int offset,
*/
next = annotation__next_asm_line(notes, dl);
if (next == NULL)
- addr = ms->sym->end + offset;
+ addr = symbol__end(ms->sym) + offset;
else
addr = ip + (next->al.offset - dl->al.offset) + offset;
@@ -2880,7 +2885,7 @@ __hist_entry__get_data_type(struct hist_entry *he, const struct arch *arch,
.arch = arch,
.thread = he->thread,
.ms = ms,
- .ip = ms->sym->start + dl->al.offset,
+ .ip = symbol__start(ms->sym) + dl->al.offset,
.cpumode = he->cpumode,
.op = op_loc,
.di = dbg,
@@ -3134,7 +3139,7 @@ static bool process_basic_block(struct basic_block_data *bb_data,
if (dl->al.offset == -1)
continue;
/* Found the target instruction */
- if (sym->start + dl->al.offset == target) {
+ if (symbol__start(sym) + dl->al.offset == target) {
found = true;
break;
}
@@ -3151,7 +3156,7 @@ static bool process_basic_block(struct basic_block_data *bb_data,
if (dl->ops.target.outside)
break;
/* jump instruction creates new basic block(s) */
- next_dl = find_disasm_line(sym, sym->start + dl->ops.target.offset,
+ next_dl = find_disasm_line(sym, symbol__start(sym) + dl->ops.target.offset,
/*allow_update=*/false);
if (next_dl)
add_basic_block(bb_data, link, next_dl);
diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c
index 8bb491d389a2..b3cc7b17aca4 100644
--- a/tools/perf/util/auxtrace.c
+++ b/tools/perf/util/auxtrace.c
@@ -2707,7 +2707,7 @@ static bool dso_sym_match(struct symbol *sym, const char *name, int *cnt,
int idx)
{
/* Same name, and global or the n'th found or any */
- return !arch__compare_symbol_names(name, sym->name) &&
+ return !arch__compare_symbol_names(name, symbol__name(sym)) &&
((!idx && symbol__binding(sym) == STB_GLOBAL) ||
(idx > 0 && ++*cnt == idx) ||
idx < 0);
@@ -2725,14 +2725,14 @@ static void print_duplicate_syms(struct dso *dso, const char *sym_name)
while (sym) {
if (dso_sym_match(sym, sym_name, &cnt, -1)) {
pr_err("#%d\t0x%"PRIx64"\t%c\t%s\n",
- ++cnt, sym->start,
+ ++cnt, symbol__start(sym),
symbol__binding(sym) == STB_GLOBAL ? 'g' :
symbol__binding(sym) == STB_LOCAL ? 'l' : 'w',
- sym->name);
+ symbol__name(sym));
near = true;
} else if (near) {
near = false;
- pr_err("\t\twhich is near\t\t%s\n", sym->name);
+ pr_err("\t\twhich is near\t\t%s\n", symbol__name(sym));
}
sym = dso__next_symbol(sym);
}
@@ -2755,7 +2755,7 @@ static int find_dso_sym(struct dso *dso, const char *sym_name, u64 *start,
while (sym) {
if (*start) {
if (!*size)
- *size = sym->start - *start;
+ *size = symbol__start(sym) - *start;
if (idx > 0) {
if (*size)
return 0;
@@ -2764,8 +2764,8 @@ static int find_dso_sym(struct dso *dso, const char *sym_name, u64 *start,
return -EINVAL;
}
} else if (dso_sym_match(sym, sym_name, &cnt, idx)) {
- *start = sym->start;
- *size = sym->end - sym->start;
+ *start = symbol__start(sym);
+ *size = symbol__end(sym) - symbol__start(sym);
}
sym = dso__next_symbol(sym);
}
diff --git a/tools/perf/util/block-info.c b/tools/perf/util/block-info.c
index 1135e54f4c7f..f6ac71a74e6d 100644
--- a/tools/perf/util/block-info.c
+++ b/tools/perf/util/block-info.c
@@ -84,7 +84,7 @@ int64_t __block_info__cmp(struct hist_entry *left, struct hist_entry *right)
return 1;
}
- cmp = strcmp(bi_l->sym->name, bi_r->sym->name);
+ cmp = strcmp(symbol__name(bi_l->sym), symbol__name(bi_r->sym));
if (cmp)
return cmp;
@@ -305,10 +305,10 @@ static int block_range_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
symbol_conf.addr2line_disable_warn = true;
- start_line = map__srcline(he->ms.map, bi->sym->start + bi->start,
+ start_line = map__srcline(he->ms.map, symbol__start(bi->sym) + bi->start,
he->ms.sym);
- end_line = map__srcline(he->ms.map, bi->sym->start + bi->end,
+ end_line = map__srcline(he->ms.map, symbol__start(bi->sym) + bi->end,
he->ms.sym);
if (start_line != SRCLINE_UNKNOWN &&
diff --git a/tools/perf/util/bpf_lock_contention.c b/tools/perf/util/bpf_lock_contention.c
index c20bd075664e..2b799bf7a9a2 100644
--- a/tools/perf/util/bpf_lock_contention.c
+++ b/tools/perf/util/bpf_lock_contention.c
@@ -143,7 +143,7 @@ static void init_numa_data(struct lock_contention *con)
"contig_page_data",
&kmap);
if (sym) {
- skel->rodata->contig_page_data_addr = map__unmap_ip(kmap, sym->start);
+ skel->rodata->contig_page_data_addr = map__unmap_ip(kmap, symbol__start(sym));
map__put(kmap);
return;
}
@@ -159,7 +159,7 @@ static void init_numa_data(struct lock_contention *con)
if (sym == NULL)
return;
- skel->rodata->node_data_addr = map__unmap_ip(kmap, sym->start);
+ skel->rodata->node_data_addr = map__unmap_ip(kmap, symbol__start(sym));
map__put(kmap);
/* get the number of online nodes using the last node number + 1 */
@@ -266,7 +266,7 @@ int lock_contention_prepare(struct lock_contention *con)
continue;
}
- addrs[con->filters->nr_addrs++] = map__unmap_ip(kmap, sym->start);
+ addrs[con->filters->nr_addrs++] = map__unmap_ip(kmap, symbol__start(sym));
con->filters->addrs = addrs;
}
naddrs = con->filters->nr_addrs ?: has_mmap_lock;
@@ -288,7 +288,7 @@ int lock_contention_prepare(struct lock_contention *con)
continue;
}
- con->delays[i].addr = map__unmap_ip(kmap, sym->start);
+ con->delays[i].addr = map__unmap_ip(kmap, symbol__start(sym));
}
skel->rodata->lock_delay = 1;
bpf_map__set_max_entries(skel->maps.lock_delays, con->nr_delays);
@@ -608,7 +608,7 @@ static const char *lock_contention_get_name(struct lock_contention *con,
/* global locks with symbols */
sym = machine__find_kernel_symbol(machine, key->lock_addr_or_cgroup, &kmap);
if (sym)
- return sym->name;
+ return symbol__name(sym);
/* try semi-global locks collected separately */
if (!bpf_map_lookup_elem(lock_fd, &key->lock_addr_or_cgroup, &flags)) {
@@ -652,12 +652,12 @@ static const char *lock_contention_get_name(struct lock_contention *con,
if (sym) {
unsigned long offset;
- offset = map__map_ip(kmap, addr) - sym->start;
+ offset = map__map_ip(kmap, addr) - symbol__start(sym);
if (offset == 0)
- return sym->name;
+ return symbol__name(sym);
- snprintf(name_buf, sizeof(name_buf), "%s+%#lx", sym->name, offset);
+ snprintf(name_buf, sizeof(name_buf), "%s+%#lx", symbol__name(sym), offset);
} else {
snprintf(name_buf, sizeof(name_buf), "%#lx", (unsigned long)addr);
}
diff --git a/tools/perf/util/c2c-function.c b/tools/perf/util/c2c-function.c
index d410196d0429..b548c76e3016 100644
--- a/tools/perf/util/c2c-function.c
+++ b/tools/perf/util/c2c-function.c
@@ -966,7 +966,7 @@ c2c_function_hists__level1_entry(struct symbol *sym,
* true "function view" rather than a per-code-address view.
*/
u64 sym_start = (sym && detail_he->ms.map) ?
- map__unmap_ip(detail_he->ms.map, sym->start) : detail_he->ip;
+ map__unmap_ip(detail_he->ms.map, symbol__start(sym)) : detail_he->ip;
mi = mem_info__new();
if (!mi)
@@ -1058,8 +1058,8 @@ c2c_function_hists__level2_entry(struct c2c_hist_entry *level1_c2c,
/* Key by the function symbol start so all code addresses collapse. */
level2_c2c = c2c_child_entry__alloc(level1_he, detail_he, 1,
(sym && detail_he->ms.map) ?
- map__unmap_ip(detail_he->ms.map, sym->start) :
- hist_entry__iaddr(detail_he));
+ map__unmap_ip(detail_he->ms.map, symbol__start(sym)) :
+ hist_entry__iaddr(detail_he));
if (!level2_c2c)
return NULL;
@@ -1178,13 +1178,14 @@ static void c2c_he__resort_by_stores(struct hist_entry *parent_he)
/* Stable tie-break: instruction address, name, then cacheline. */
u64 a = hist_entry__iaddr(he), b = hist_entry__iaddr(iter);
- if (a != b)
+ if (a != b) {
cmp = a < b ? -1 : 1;
- else if (he->ms.sym && iter->ms.sym)
- cmp = strcmp(he->ms.sym->name,
- iter->ms.sym->name);
- else
+ } else if (he->ms.sym && iter->ms.sym) {
+ cmp = strcmp(symbol__name(he->ms.sym),
+ symbol__name(iter->ms.sym));
+ } else {
cmp = (iter->ms.sym ? 1 : 0) - (he->ms.sym ? 1 : 0);
+ }
if (!cmp) {
struct c2c_hist_entry *he_c2c;
@@ -1407,7 +1408,7 @@ static int c2c_function__ident_len(struct hist_entry *he)
* Match symbol_view_entry(): sort_sym adds the cpumode prefix and, in
* verbose mode, the address and DSO origin before the symbol name.
*/
- size = strlen(he->ms.sym->name) + 64;
+ size = strlen(symbol__name(he->ms.sym)) + 64;
symbuf = malloc(size);
if (!symbuf)
return size - 1;
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c
index 31c675cbab63..24c176905c0e 100644
--- a/tools/perf/util/callchain.c
+++ b/tools/perf/util/callchain.c
@@ -802,13 +802,15 @@ static enum match_result match_chain(struct callchain_cursor_node *node,
* on the symbol start address.
*/
if (symbol__inlined(cnode->ms.sym) || symbol__inlined(node->ms.sym)) {
- match = match_chain_strings(cnode->ms.sym->name,
- node->ms.sym->name);
+ match = match_chain_strings(symbol__name(cnode->ms.sym),
+ symbol__name(node->ms.sym));
if (match != MATCH_ERROR)
break;
} else {
- match = match_chain_dso_addresses(cnode->ms.map, cnode->ms.sym->start,
- node->ms.map, node->ms.sym->start);
+ match = match_chain_dso_addresses(cnode->ms.map,
+ symbol__start(cnode->ms.sym),
+ node->ms.map,
+ symbol__start(node->ms.sym));
break;
}
}
@@ -1247,13 +1249,14 @@ char *callchain_list__sym_name(struct callchain_list *cl,
if (cl->ms.sym) {
const char *inlined = symbol__inlined(cl->ms.sym) ? " (inlined)" : "";
- if (show_srcline && cl->srcline)
+ if (show_srcline && cl->srcline) {
printed = scnprintf(bf, bfsize, "%s %s%s",
- cl->ms.sym->name, cl->srcline,
+ symbol__name(cl->ms.sym), cl->srcline,
inlined);
- else
+ } else {
printed = scnprintf(bf, bfsize, "%s%s",
- cl->ms.sym->name, inlined);
+ symbol__name(cl->ms.sym), inlined);
+ }
} else
printed = scnprintf(bf, bfsize, "%#" PRIx64, cl->ip);
diff --git a/tools/perf/util/capstone.c b/tools/perf/util/capstone.c
index 95881f15324a..422a48385b78 100644
--- a/tools/perf/util/capstone.c
+++ b/tools/perf/util/capstone.c
@@ -333,7 +333,7 @@ static struct symbol *find_symbol_from_addr(struct map *map, u64 addr,
return NULL;
}
- *sym_offset = al_addr - sym->start;
+ *sym_offset = al_addr - symbol__start(sym);
return sym;
}
@@ -368,10 +368,10 @@ static void print_capstone_detail(struct cs_insn *insn, char *buf, size_t len,
if (sym_offset == 0) {
scnprintf(buf, len, "\t# %"PRIx64" <%s>",
- orig_addr, sym->name);
+ orig_addr, symbol__name(sym));
} else {
scnprintf(buf, len, "\t# %"PRIx64" <%s+%#"PRIx64">",
- orig_addr, sym->name, sym_offset);
+ orig_addr, symbol__name(sym), sym_offset);
}
map__put(found_map);
break;
@@ -454,10 +454,10 @@ static void format_capstone_insn_arm64(struct cs_insn *insn, char *buf,
len = len - (last_imm - buf);
if (sym_offset == 0) {
scnprintf(last_imm, len, "%"PRIx64" <%s>",
- addr, sym->name);
+ addr, symbol__name(sym));
} else {
scnprintf(last_imm, len, "%"PRIx64" <%s+%#"PRIx64">",
- addr, sym->name, sym_offset);
+ addr, symbol__name(sym), sym_offset);
}
map__put(found_map);
}
@@ -498,7 +498,7 @@ int symbol__disassemble_capstone(const char *filename, struct symbol *sym,
struct annotation *notes = symbol__annotation(sym);
struct map *map = args->ms->map;
struct dso *dso = map__dso(map);
- u64 start = map__rip_2objdump(map, sym->start);
+ u64 start = map__rip_2objdump(map, symbol__start(sym));
u64 offset;
int i, count, free_count;
bool is_64bit = false;
@@ -526,7 +526,7 @@ int symbol__disassemble_capstone(const char *filename, struct symbol *sym,
/* add the function address and name */
scnprintf(disasm_buf, sizeof(disasm_buf), "%#"PRIx64" <%s>:",
- start, sym->name);
+ start, symbol__name(sym));
args->offset = -1;
args->line = disasm_buf;
@@ -631,8 +631,8 @@ int symbol__disassemble_capstone_powerpc(const char *filename __maybe_unused,
struct map *map = args->ms->map;
struct dso *dso = map__dso(map);
struct nscookie nsc;
- u64 start = map__rip_2objdump(map, sym->start);
- u64 end = map__rip_2objdump(map, sym->end);
+ u64 start = map__rip_2objdump(map, symbol__start(sym));
+ u64 end = map__rip_2objdump(map, symbol__end(sym));
u64 len = end - start;
u64 offset;
int i, fd, count;
@@ -703,7 +703,7 @@ int symbol__disassemble_capstone_powerpc(const char *filename __maybe_unused,
/* add the function address and name */
scnprintf(disasm_buf, sizeof(disasm_buf), "%#"PRIx64" <%s>:",
- start, sym->name);
+ start, symbol__name(sym));
args->offset = -1;
args->line = disasm_buf;
diff --git a/tools/perf/util/data-convert-json.c b/tools/perf/util/data-convert-json.c
index 40888b7c4467..c09bd77f5665 100644
--- a/tools/perf/util/data-convert-json.c
+++ b/tools/perf/util/data-convert-json.c
@@ -138,11 +138,11 @@ static void output_sample_callchain_entry(const struct perf_tool *tool,
output_json_format(out, false, 4, "{");
output_json_key_format(out, false, 5, "ip", "\"0x%" PRIx64 "\"", ip);
- if (al && al->sym && al->sym->namelen) {
+ if (al && al->sym && symbol__namelen(al->sym)) {
struct dso *dso = al->map ? map__dso(al->map) : NULL;
fputc(',', out);
- output_json_key_string(out, false, 5, "symbol", al->sym->name);
+ output_json_key_string(out, false, 5, "symbol", symbol__name(al->sym));
if (dso) {
const char *dso_name = dso__short_name(dso);
diff --git a/tools/perf/util/db-export.c b/tools/perf/util/db-export.c
index ba54b1119ee6..732369fe0046 100644
--- a/tools/perf/util/db-export.c
+++ b/tools/perf/util/db-export.c
@@ -198,7 +198,7 @@ static int db_ids_from_al(struct db_export *dbe, struct addr_location *al,
if (err)
return err;
*sym_db_id = *db_id;
- *offset = al->addr - al->sym->start;
+ *offset = al->addr - symbol__start(al->sym);
}
}
diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c
index b7095519419e..eec3fc14df98 100644
--- a/tools/perf/util/debug.c
+++ b/tools/perf/util/debug.c
@@ -342,7 +342,8 @@ void __dump_stack(FILE *file, void **stackdump, size_t stackdump_size)
if (thread && thread__find_map(thread, PERF_RECORD_MISC_USER, addr, &al)) {
al.sym = map__find_symbol(al.map, al.addr);
if (al.sym) {
- fprintf(file, " #%zd %p in %s ", i, stackdump[i], al.sym->name);
+ fprintf(file, " #%zd %p in %s ", i, stackdump[i],
+ symbol__name(al.sym));
printed = true;
}
}
diff --git a/tools/perf/util/disasm.c b/tools/perf/util/disasm.c
index 7c289b67b5bd..9b3e476bab6d 100644
--- a/tools/perf/util/disasm.c
+++ b/tools/perf/util/disasm.c
@@ -311,8 +311,10 @@ static int call__parse(const struct arch *arch, struct ins_operands *ops, struct
int call__scnprintf(const struct ins *ins, char *bf, size_t size,
struct ins_operands *ops, int max_ins_name)
{
- if (ops->target.sym)
- return scnprintf(bf, size, "%-*s %s", max_ins_name, ins->name, ops->target.sym->name);
+ if (ops->target.sym) {
+ return scnprintf(bf, size, "%-*s %s", max_ins_name, ins->name,
+ symbol__name(ops->target.sym));
+ }
if (ops->target.addr == 0)
return ins__raw_scnprintf(ins, bf, size, ops, max_ins_name);
@@ -399,8 +401,8 @@ static int jump__parse(const struct arch *arch, struct ins_operands *ops, struct
}
target.addr = map__objdump_2mem(map, ops->target.addr);
- start = map__unmap_ip(map, sym->start);
- end = map__unmap_ip(map, sym->end);
+ start = map__unmap_ip(map, symbol__start(sym));
+ end = map__unmap_ip(map, symbol__end(sym));
ops->target.outside = target.addr < start || target.addr >= end;
@@ -444,8 +446,10 @@ int jump__scnprintf(const struct ins *ins, char *bf, size_t size,
if (!ops->target.addr || ops->target.offset < 0)
return ins__raw_scnprintf(ins, bf, size, ops, max_ins_name);
- if (ops->target.outside && ops->target.sym != NULL)
- return scnprintf(bf, size, "%-*s %s", max_ins_name, ins->name, ops->target.sym->name);
+ if (ops->target.outside && ops->target.sym != NULL) {
+ return scnprintf(bf, size, "%-*s %s", max_ins_name, ins->name,
+ symbol__name(ops->target.sym));
+ }
c = strchr(ops->raw, ',');
c = validate_comma(c, ops);
@@ -1050,8 +1054,8 @@ static int symbol__parse_objdump_line(struct symbol *sym,
/* Process hex address followed by ':'. */
line_ip = strtoull(parsed_line, &tmp, 16);
if (parsed_line != tmp && tmp[0] == ':' && tmp[1] != '\0') {
- u64 start = map__rip_2objdump(map, sym->start),
- end = map__rip_2objdump(map, sym->end);
+ u64 start = map__rip_2objdump(map, symbol__start(sym)),
+ end = map__rip_2objdump(map, symbol__end(sym));
offset = line_ip - start;
if ((u64)line_ip < start || (u64)line_ip >= end)
@@ -1074,7 +1078,7 @@ static int symbol__parse_objdump_line(struct symbol *sym,
if (!disasm_line__has_local_offset(dl)) {
dl->ops.target.offset = dl->ops.target.addr -
- map__rip_2objdump(map, sym->start);
+ map__rip_2objdump(map, symbol__start(sym));
dl->ops.target.offset_avail = true;
}
@@ -1086,7 +1090,7 @@ static int symbol__parse_objdump_line(struct symbol *sym,
};
if (!maps__find_ams(thread__maps(args->ms->thread), &target) &&
- target.ms.sym->start == target.al_addr)
+ symbol__start(target.ms.sym) == target.al_addr)
dl->ops.target.sym = target.ms.sym;
addr_map_symbol__exit(&target);
@@ -1182,8 +1186,8 @@ static int symbol__disassemble_raw(char *filename, struct symbol *sym,
struct annotation *notes = symbol__annotation(sym);
struct map *map = args->ms->map;
struct dso *dso = map__dso(map);
- u64 start = map__rip_2objdump(map, sym->start);
- u64 end = map__rip_2objdump(map, sym->end);
+ u64 start = map__rip_2objdump(map, symbol__start(sym));
+ u64 end = map__rip_2objdump(map, symbol__end(sym));
u64 len = end - start;
u64 offset;
int i, count;
@@ -1202,7 +1206,7 @@ static int symbol__disassemble_raw(char *filename, struct symbol *sym,
if (buf == NULL)
goto err;
- count = dso__data_read_offset(dso, NULL, sym->start, buf, len);
+ count = dso__data_read_offset(dso, NULL, symbol__start(sym), buf, len);
line = (u32 *)buf;
@@ -1211,7 +1215,7 @@ static int symbol__disassemble_raw(char *filename, struct symbol *sym,
/* add the function address and name */
scnprintf(disasm_buf, sizeof(disasm_buf), "%#"PRIx64" <%s>:",
- start, sym->name);
+ start, symbol__name(sym));
args->offset = -1;
args->line = disasm_buf;
@@ -1376,8 +1380,8 @@ static int symbol__disassemble_objdump(const char *filename, struct symbol *sym,
opts->objdump_path ?: "objdump",
opts->disassembler_style ? "-M " : "",
opts->disassembler_style ?: "",
- map__rip_2objdump(map, sym->start),
- map__rip_2objdump(map, sym->end),
+ map__rip_2objdump(map, symbol__start(sym)),
+ map__rip_2objdump(map, symbol__end(sym)),
opts->show_linenr ? "-l" : "",
opts->show_asm_raw ? "" : "--no-show-raw-insn",
opts->annotate_src ? "-S" : "",
@@ -1513,16 +1517,17 @@ int symbol__disassemble(struct symbol *sym, struct annotate_args *args)
}
pr_debug("%s: filename=%s, sym=%s, start=%#" PRIx64 ", end=%#" PRIx64 "\n", __func__,
- symfs_filename, sym->name, map__unmap_ip(map, sym->start),
- map__unmap_ip(map, sym->end));
+ symfs_filename, symbol__name(sym), map__unmap_ip(map, symbol__start(sym)),
+ map__unmap_ip(map, symbol__end(sym)));
- pr_debug("annotating [%p] %30s : [%p] %30s\n", dso, dso__long_name(dso), sym, sym->name);
+ pr_debug("annotating [%p] %30s : [%p] %30s\n", dso, dso__long_name(dso), sym,
+ symbol__name(sym));
if (dso__is_kcore(dso)) {
- kce.addr = map__rip_2objdump(map, sym->start);
+ kce.addr = map__rip_2objdump(map, symbol__start(sym));
kce.kcore_filename = symfs_filename;
- kce.len = sym->end - sym->start;
- kce.offs = sym->start;
+ kce.len = symbol__end(sym) - symbol__start(sym);
+ kce.offs = symbol__start(sym);
if (!kcore_extract__create(&kce)) {
delete_extract = true;
diff --git a/tools/perf/util/dlfilter.c b/tools/perf/util/dlfilter.c
index e11e144af62b..3b72cb7b412e 100644
--- a/tools/perf/util/dlfilter.c
+++ b/tools/perf/util/dlfilter.c
@@ -47,13 +47,13 @@ static void al_to_d_al(struct addr_location *al, struct perf_dlfilter_al *d_al)
d_al->buildid = NULL;
}
if (sym) {
- d_al->sym = sym->name;
- d_al->sym_start = sym->start;
- d_al->sym_end = sym->end;
- if (al->addr < sym->end)
- d_al->symoff = al->addr - sym->start;
+ d_al->sym = symbol__name(sym);
+ d_al->sym_start = symbol__start(sym);
+ d_al->sym_end = symbol__end(sym);
+ if (al->addr < symbol__end(sym))
+ d_al->symoff = al->addr - symbol__start(sym);
else if (al->map)
- d_al->symoff = al->addr - map__start(al->map) - sym->start;
+ d_al->symoff = al->addr - map__start(al->map) - symbol__start(sym);
else
d_al->symoff = 0;
d_al->sym_binding = symbol__binding(sym);
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c
index ce98613868e7..39a1eb9cb73f 100644
--- a/tools/perf/util/dso.c
+++ b/tools/perf/util/dso.c
@@ -2147,8 +2147,8 @@ const u8 *dso__read_symbol(struct dso *dso, const char *symfs_filename,
const struct map *map, const struct symbol *sym,
u8 **out_buf, u64 *out_buf_len, bool *is_64bit)
{
- u64 start = map__rip_2objdump(map, sym->start);
- u64 end = map__rip_2objdump(map, sym->end);
+ u64 start = map__rip_2objdump(map, symbol__start(sym));
+ u64 end = map__rip_2objdump(map, symbol__end(sym));
size_t len = end - start;
*out_buf = NULL;
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c
index ea75816d126a..939307796a88 100644
--- a/tools/perf/util/event.c
+++ b/tools/perf/util/event.c
@@ -884,11 +884,11 @@ int machine__resolve(struct machine *machine, struct addr_location *al,
if (al->sym) {
ret = strlist__has_entry(symbol_conf.sym_list,
- al->sym->name);
+ symbol__name(al->sym));
}
if (!ret && al->sym) {
snprintf(al_addr_str, sz, "0x%"PRIx64,
- map__unmap_ip(al->map, al->sym->start));
+ map__unmap_ip(al->map, symbol__start(al->sym)));
ret = strlist__has_entry(symbol_conf.sym_list,
al_addr_str);
}
diff --git a/tools/perf/util/evsel_fprintf.c b/tools/perf/util/evsel_fprintf.c
index 0f7a25500a44..76a0c69e6f45 100644
--- a/tools/perf/util/evsel_fprintf.c
+++ b/tools/perf/util/evsel_fprintf.c
@@ -200,7 +200,7 @@ int sample__fprintf_callchain(struct perf_sample *sample, int left_alignment,
/* Add srccode here too? */
if (bt_stop_list && sym &&
- strlist__has_entry(bt_stop_list, sym->name)) {
+ strlist__has_entry(bt_stop_list, symbol__name(sym))) {
break;
}
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c
index e75a4fcce975..8a1a5e36d7cd 100644
--- a/tools/perf/util/hist.c
+++ b/tools/perf/util/hist.c
@@ -97,7 +97,7 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
* +3 accounts for ' y ' symtab origin info
*/
if (h->ms.sym) {
- symlen = h->ms.sym->namelen + 4;
+ symlen = symbol__namelen(h->ms.sym) + 4;
if (verbose > 0)
symlen += BITS_PER_LONG / 4 + 2 + 3;
hists__new_col_len(hists, HISTC_SYMBOL, symlen);
@@ -120,11 +120,11 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
}
if (h->parent)
- hists__new_col_len(hists, HISTC_PARENT, h->parent->namelen);
+ hists__new_col_len(hists, HISTC_PARENT, symbol__namelen(h->parent));
if (h->branch_info) {
if (h->branch_info->from.ms.sym) {
- symlen = (int)h->branch_info->from.ms.sym->namelen + 4;
+ symlen = (int) symbol__namelen(h->branch_info->from.ms.sym) + 4;
if (verbose > 0)
symlen += BITS_PER_LONG / 4 + 2 + 3;
hists__new_col_len(hists, HISTC_SYMBOL_FROM, symlen);
@@ -139,7 +139,7 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
}
if (h->branch_info->to.ms.sym) {
- symlen = (int)h->branch_info->to.ms.sym->namelen + 4;
+ symlen = (int) symbol__namelen(h->branch_info->to.ms.sym) + 4;
if (verbose > 0)
symlen += BITS_PER_LONG / 4 + 2 + 3;
hists__new_col_len(hists, HISTC_SYMBOL_TO, symlen);
@@ -163,7 +163,7 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
if (h->mem_info) {
if (mem_info__daddr(h->mem_info)->ms.sym) {
- symlen = (int)mem_info__daddr(h->mem_info)->ms.sym->namelen + 4
+ symlen = (int) symbol__namelen(mem_info__daddr(h->mem_info)->ms.sym) + 4
+ unresolved_col_width + 2;
hists__new_col_len(hists, HISTC_MEM_DADDR_SYMBOL,
symlen);
@@ -178,7 +178,7 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
}
if (mem_info__iaddr(h->mem_info)->ms.sym) {
- symlen = (int)mem_info__iaddr(h->mem_info)->ms.sym->namelen + 4
+ symlen = (int) symbol__namelen(mem_info__iaddr(h->mem_info)->ms.sym) + 4
+ unresolved_col_width + 2;
hists__new_col_len(hists, HISTC_MEM_IADDR_SYMBOL,
symlen);
@@ -727,7 +727,7 @@ static struct hist_entry *hists__findnew_entry(struct hists *hists,
*/
if (hists__has(hists, sym) && he->ms.map != entry->ms.map) {
if (he->ms.sym) {
- u64 addr = he->ms.sym->start;
+ u64 addr = symbol__start(he->ms.sym);
he->ms.sym = map__find_symbol(entry->ms.map, addr);
}
@@ -2300,7 +2300,7 @@ static bool hists__filter_entry_by_symbol(struct hists *hists,
struct hist_entry *he)
{
if (hists->symbol_filter_str != NULL &&
- (!he->ms.sym || strstr(he->ms.sym->name,
+ (!he->ms.sym || strstr(symbol__name(he->ms.sym),
hists->symbol_filter_str) == NULL)) {
he->filtered |= (1 << HIST_FILTER__SYMBOL);
return true;
diff --git a/tools/perf/util/intel-pt.c b/tools/perf/util/intel-pt.c
index 8c21c9f52d57..a9f43ac8d152 100644
--- a/tools/perf/util/intel-pt.c
+++ b/tools/perf/util/intel-pt.c
@@ -3003,8 +3003,8 @@ static u64 intel_pt_switch_ip(struct intel_pt *pt, u64 *ptss_ip)
for (sym = start; sym; sym = dso__next_symbol(sym)) {
if (symbol__binding(sym) == STB_GLOBAL &&
- !strcmp(sym->name, "__switch_to")) {
- ip = map__unmap_ip(map, sym->start);
+ !strcmp(symbol__name(sym), "__switch_to")) {
+ ip = map__unmap_ip(map, symbol__start(sym));
if (ip >= map__start(map) && ip < map__end(map)) {
switch_ip = ip;
break;
@@ -3021,8 +3021,8 @@ static u64 intel_pt_switch_ip(struct intel_pt *pt, u64 *ptss_ip)
ptss = "__perf_event_task_sched_out";
for (sym = start; sym; sym = dso__next_symbol(sym)) {
- if (!strcmp(sym->name, ptss)) {
- ip = map__unmap_ip(map, sym->start);
+ if (!strcmp(symbol__name(sym), ptss)) {
+ ip = map__unmap_ip(map, symbol__start(sym));
if (ip >= map__start(map) && ip < map__end(map)) {
*ptss_ip = ip;
break;
diff --git a/tools/perf/util/libbfd.c b/tools/perf/util/libbfd.c
index 992e2c2a7198..1a5d38b8e5f4 100644
--- a/tools/perf/util/libbfd.c
+++ b/tools/perf/util/libbfd.c
@@ -552,7 +552,7 @@ int symbol__disassemble_bpf_libbfd(struct symbol *sym __maybe_unused,
struct annotation *notes = symbol__annotation(sym);
struct bpf_prog_linfo *prog_linfo = NULL;
struct bpf_prog_info_node *info_node;
- int len = sym->end - sym->start;
+ int len = symbol__end(sym) - symbol__start(sym);
disassembler_ftype disassemble;
struct map *map = args->ms->map;
struct perf_bpil *info_linear;
@@ -572,7 +572,7 @@ int symbol__disassemble_bpf_libbfd(struct symbol *sym __maybe_unused,
return SYMBOL_ANNOTATE_ERRNO__BPF_INVALID_FILE;
pr_debug("%s: handling sym %s addr %" PRIx64 " len %" PRIx64 "\n", __func__,
- sym->name, sym->start, sym->end - sym->start);
+ symbol__name(sym), symbol__start(sym), symbol__end(sym) - symbol__start(sym));
memset(tpath, 0, sizeof(tpath));
perf_exe(tpath, sizeof(tpath));
diff --git a/tools/perf/util/llvm.c b/tools/perf/util/llvm.c
index 0ecb1a53359e..7429e8ef6cce 100644
--- a/tools/perf/util/llvm.c
+++ b/tools/perf/util/llvm.c
@@ -138,7 +138,7 @@ int symbol__disassemble_llvm(const char *filename, struct symbol *sym,
struct annotation *notes = symbol__annotation(sym);
struct map *map = args->ms->map;
struct dso *dso = map__dso(map);
- u64 start = map__rip_2objdump(map, sym->start);
+ u64 start = map__rip_2objdump(map, symbol__start(sym));
/* Malloc-ed buffer containing instructions read from disk. */
u8 *code_buf = NULL;
/* Pointer to code to be disassembled. */
@@ -202,7 +202,7 @@ int symbol__disassemble_llvm(const char *filename, struct symbol *sym,
/* add the function address and name */
scnprintf(disasm_buf, sizeof(disasm_buf), "%#"PRIx64" <%s>:",
- start, sym->name);
+ start, symbol__name(sym));
args->offset = -1;
args->line = disasm_buf;
@@ -323,7 +323,7 @@ int symbol__disassemble_llvm(const char *filename, struct symbol *sym,
return ret;
#else // HAVE_LIBLLVM_SUPPORT
pr_debug("The LLVM disassembler isn't linked in for %s in %s\n",
- sym->name, filename);
+ symbol__name(sym), filename);
return -1;
#endif
}
diff --git a/tools/perf/util/lock-contention.c b/tools/perf/util/lock-contention.c
index 119a7206f3cd..8ee1b19a8292 100644
--- a/tools/perf/util/lock-contention.c
+++ b/tools/perf/util/lock-contention.c
@@ -135,7 +135,7 @@ bool match_callstack_filter(struct machine *machine, u64 *callstack, int max_sta
continue;
list_for_each_entry(filter, &callstack_filters, list) {
- if (strstr(sym->name, filter->name))
+ if (strstr(symbol__name(sym), filter->name))
return true;
}
}
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index 1d9d3bf57720..6a10c3036383 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -1110,8 +1110,8 @@ static u64 find_entry_trampoline(struct dso *dso)
if (symbol__binding(sym) != STB_GLOBAL)
continue;
for (i = 0; i < ARRAY_SIZE(syms); i++) {
- if (!strcmp(sym->name, syms[i]))
- return sym->start;
+ if (!strcmp(symbol__name(sym), syms[i]))
+ return symbol__start(sym);
}
}
@@ -2056,7 +2056,7 @@ int machine__process_event(struct machine *machine, union perf_event *event,
static bool symbol__match_regex(struct symbol *sym, regex_t *regex)
{
- return regexec(regex, sym->name, 0, NULL, 0) == 0;
+ return regexec(regex, symbol__name(sym), 0, NULL, 0) == 0;
}
static void ip__resolve_ams(struct thread *thread,
@@ -3283,8 +3283,8 @@ char *machine__resolve_kernel_addr(void *vmachine, unsigned long long *addrp, ch
return NULL;
*modp = __map__is_kmodule(map) ? (char *)dso__short_name(map__dso(map)) : NULL;
- *addrp = map__unmap_ip(map, sym->start);
- return sym->name;
+ *addrp = map__unmap_ip(map, symbol__start(sym));
+ return (char *)symbol__name(sym);
}
struct machine__for_each_dso_cb_args {
@@ -3330,27 +3330,27 @@ bool machine__is_lock_function(struct machine *machine, u64 addr)
return false;
}
- machine->sched.text_start = map__unmap_ip(kmap, sym->start);
+ machine->sched.text_start = map__unmap_ip(kmap, symbol__start(sym));
/* should not fail from here */
sym = machine__find_kernel_symbol_by_name(machine, "__sched_text_end", &kmap);
- machine->sched.text_end = map__unmap_ip(kmap, sym->start);
+ machine->sched.text_end = map__unmap_ip(kmap, symbol__start(sym));
sym = machine__find_kernel_symbol_by_name(machine, "__lock_text_start", &kmap);
- machine->lock.text_start = map__unmap_ip(kmap, sym->start);
+ machine->lock.text_start = map__unmap_ip(kmap, symbol__start(sym));
sym = machine__find_kernel_symbol_by_name(machine, "__lock_text_end", &kmap);
- machine->lock.text_end = map__unmap_ip(kmap, sym->start);
+ machine->lock.text_end = map__unmap_ip(kmap, symbol__start(sym));
sym = machine__find_kernel_symbol_by_name(machine, "__traceiter_contention_begin", &kmap);
if (sym) {
- machine->traceiter.text_start = map__unmap_ip(kmap, sym->start);
- machine->traceiter.text_end = map__unmap_ip(kmap, sym->end);
+ machine->traceiter.text_start = map__unmap_ip(kmap, symbol__start(sym));
+ machine->traceiter.text_end = map__unmap_ip(kmap, symbol__end(sym));
}
sym = machine__find_kernel_symbol_by_name(machine, "trace_contention_begin", &kmap);
if (sym) {
- machine->trace.text_start = map__unmap_ip(kmap, sym->start);
- machine->trace.text_end = map__unmap_ip(kmap, sym->end);
+ machine->trace.text_start = map__unmap_ip(kmap, symbol__start(sym));
+ machine->trace.text_end = map__unmap_ip(kmap, symbol__end(sym));
}
}
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c
index 41cdddc987ee..85216fa71f1a 100644
--- a/tools/perf/util/map.c
+++ b/tools/perf/util/map.c
@@ -321,7 +321,7 @@ void map__fixup_start(struct map *map)
if (nd != NULL) {
struct symbol *sym = rb_entry(nd, struct symbol, rb_node);
- map__set_start(map, sym->start);
+ map__set_start(map, symbol__start(sym));
}
}
@@ -333,7 +333,7 @@ void map__fixup_end(struct map *map)
if (nd != NULL) {
struct symbol *sym = rb_entry(nd, struct symbol, rb_node);
- map__set_end(map, sym->end);
+ map__set_end(map, symbol__end(sym));
}
}
@@ -606,7 +606,7 @@ u64 map__objdump_2rip(const struct map *map, u64 ip)
bool map__contains_symbol(const struct map *map, const struct symbol *sym)
{
- u64 ip = map__unmap_ip(map, sym->start);
+ u64 ip = map__unmap_ip(map, symbol__start(sym));
return ip >= map__start(map) && ip < map__end(map);
}
diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h
index fb0279810ae9..f49b4aaacf2f 100644
--- a/tools/perf/util/map.h
+++ b/tools/perf/util/map.h
@@ -164,7 +164,7 @@ struct thread;
#define __map__for_each_symbol_by_name(map, sym_name, pos, idx) \
for (pos = map__find_symbol_by_name_idx(map, sym_name, &idx); \
pos && \
- !symbol__match_symbol_name(pos->name, sym_name, \
+ !symbol__match_symbol_name(symbol__name(pos), sym_name, \
SYMBOL_TAG_INCLUDE__DEFAULT_ONLY); \
pos = dso__next_symbol_by_name(map__dso(map), &idx))
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 1534280c0c1d..2d554fed0c3f 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -149,7 +149,7 @@ static int kernel_get_symbol_address_by_name(const char *name, u64 *addr,
sym = machine__find_kernel_symbol_by_name(host_machine, name, &map);
if (!sym)
return -ENOENT;
- *addr = map__unmap_ip(map, sym->start) -
+ *addr = map__unmap_ip(map, symbol__start(sym)) -
((reloc) ? 0 : map__reloc(map)) -
((reladdr) ? map__start(map) : 0);
}
@@ -415,13 +415,13 @@ static int find_alternative_probe_point(struct debuginfo *dinfo,
/* Find the address of given function */
map__for_each_symbol_by_name(map, pp->function, sym, idx) {
if (uprobes) {
- address = sym->start;
+ address = symbol__start(sym);
if (symbol__type(sym) == STT_GNU_IFUNC)
pr_warning("Warning: The probe function (%s) is a GNU indirect function.\n"
"Consider identifying the final function used at run time and set the probe directly on that.\n",
pp->function);
} else
- address = map__unmap_ip(map, sym->start) - map__reloc(map);
+ address = map__unmap_ip(map, symbol__start(sym)) - map__reloc(map);
break;
}
if (!address) {
@@ -721,17 +721,17 @@ static int post_process_probe_trace_point(struct probe_trace_point *tp,
return (symbol_conf.ignore_vmlinux_buildid) ? 0 : -ENOENT;
}
- if (strcmp(sym->name, tp->symbol)) {
+ if (strcmp(symbol__name(sym), tp->symbol)) {
/* If we have no realname, use symbol for it */
if (!tp->realname)
tp->realname = tp->symbol;
else
free(tp->symbol);
- tp->symbol = strdup(sym->name);
+ tp->symbol = strdup(symbol__name(sym));
if (!tp->symbol)
return -ENOMEM;
}
- tp->offset = addr - sym->start;
+ tp->offset = addr - symbol__start(sym);
tp->address -= offs;
return 0;
@@ -2309,8 +2309,8 @@ static int find_perf_probe_point_from_map(struct probe_trace_point *tp,
goto out;
pp->retprobe = tp->retprobe;
- pp->offset = addr - map__unmap_ip(map, sym->start);
- pp->function = strdup(sym->name);
+ pp->offset = addr - map__unmap_ip(map, symbol__start(sym));
+ pp->function = strdup(symbol__name(sym));
ret = pp->function ? 0 : -ENOMEM;
out:
@@ -3065,7 +3065,7 @@ static int find_probe_functions(struct map *map, char *name,
cut_version = false;
map__for_each_symbol(map, sym, tmp) {
- norm = arch__normalize_symbol_name(sym->name);
+ norm = arch__normalize_symbol_name(symbol__name(sym));
if (!norm)
continue;
@@ -3193,12 +3193,13 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev,
continue;
/* There can be duplicated symbols in the map */
- for (i = 0; i < j; i++)
- if (sym->start == syms[i]->start) {
+ for (i = 0; i < j; i++) {
+ if (symbol__start(sym) == symbol__start(syms[i])) {
pr_debug("Found duplicated symbol %s @ %" PRIx64 "\n",
- sym->name, sym->start);
+ symbol__name(sym), symbol__start(sym));
break;
}
+ }
if (i != j)
continue;
@@ -3210,28 +3211,28 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev,
}
ret++;
- if (pp->offset > sym->end - sym->start) {
+ if (pp->offset > symbol__end(sym) - symbol__start(sym)) {
pr_warning("Offset %ld is bigger than the size of %s\n",
- pp->offset, sym->name);
+ pp->offset, symbol__name(sym));
ret = -ENOENT;
goto err_out;
}
/* Add one probe point */
- tp->address = map__unmap_ip(map, sym->start) + pp->offset;
+ tp->address = map__unmap_ip(map, symbol__start(sym)) + pp->offset;
/* Check the kprobe (not in module) is within .text */
if (!pev->uprobes && !pev->target &&
- kprobe_warn_out_range(sym->name, tp->address)) {
+ kprobe_warn_out_range(symbol__name(sym), tp->address)) {
tp->symbol = NULL; /* Skip it */
skipped++;
} else if (reloc_sym) {
tp->symbol = strdup_or_goto(reloc_sym->name, nomem_out);
tp->offset = tp->address - reloc_sym->addr;
} else {
- tp->symbol = strdup_or_goto(sym->name, nomem_out);
+ tp->symbol = strdup_or_goto(symbol__name(sym), nomem_out);
tp->offset = pp->offset;
}
- tp->realname = strdup_or_goto(sym->name, nomem_out);
+ tp->realname = strdup_or_goto(symbol__name(sym), nomem_out);
tp->retprobe = pp->retprobe;
if (pev->target) {
@@ -3813,8 +3814,8 @@ int show_available_funcs(const char *target, struct nsinfo *nsi,
for (size_t i = 0; i < dso__symbol_names_len(dso); i++) {
struct symbol *pos = dso__symbol_names(dso)[i];
- if (strfilter__compare(_filter, pos->name))
- printf("%s\n", pos->name);
+ if (strfilter__compare(_filter, symbol__name(pos)))
+ printf("%s\n", symbol__name(pos));
}
end:
map__put(map);
diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c
index 60dbe2dad5eb..71ff233553cc 100644
--- a/tools/perf/util/python.c
+++ b/tools/perf/util/python.c
@@ -812,7 +812,7 @@ static PyObject *pyrf_sample_event__get_symbol(struct pyrf_event *pevent,
if (pyrf_sample_event__resolve_al(pevent) < 0 || !pevent->al.sym)
Py_RETURN_NONE;
- return PyUnicode_FromString(pevent->al.sym->name);
+ return PyUnicode_FromString(symbol__name(pevent->al.sym));
}
static PyObject *pyrf_sample_event__get_sym_start(struct pyrf_event *pevent,
@@ -821,7 +821,7 @@ static PyObject *pyrf_sample_event__get_sym_start(struct pyrf_event *pevent,
if (pyrf_sample_event__resolve_al(pevent) < 0 || !pevent->al.sym)
Py_RETURN_NONE;
- return PyLong_FromUnsignedLongLong(pevent->al.sym->start);
+ return PyLong_FromUnsignedLongLong(symbol__start(pevent->al.sym));
}
static PyObject *pyrf_sample_event__get_sym_offset(struct pyrf_event *pevent,
@@ -830,7 +830,7 @@ static PyObject *pyrf_sample_event__get_sym_offset(struct pyrf_event *pevent,
if (pyrf_sample_event__resolve_al(pevent) < 0 || !pevent->al.sym)
Py_RETURN_NONE;
- return PyLong_FromUnsignedLongLong(pevent->al.addr - pevent->al.sym->start);
+ return PyLong_FromUnsignedLongLong(pevent->al.addr - symbol__start(pevent->al.sym));
}
static PyObject *pyrf_sample_event__get_sym_end(struct pyrf_event *pevent,
@@ -839,7 +839,7 @@ static PyObject *pyrf_sample_event__get_sym_end(struct pyrf_event *pevent,
if (pyrf_sample_event__resolve_al(pevent) < 0 || !pevent->al.sym)
Py_RETURN_NONE;
- return PyLong_FromUnsignedLongLong(pevent->al.sym->end);
+ return PyLong_FromUnsignedLongLong(symbol__end(pevent->al.sym));
}
static PyObject *pyrf_sample_event__get_raw_buf(struct pyrf_event *pevent,
@@ -960,7 +960,7 @@ static PyObject *pyrf_callchain_node__get_symbol(struct pyrf_callchain_node *pno
if (pnode->sym_name)
return PyUnicode_FromString(pnode->sym_name);
if (pnode->sym)
- return PyUnicode_FromString(pnode->sym->name);
+ return PyUnicode_FromString(symbol__name(pnode->sym));
return PyUnicode_FromString("[unknown]");
}
@@ -991,7 +991,7 @@ static PyObject *pyrf_callchain_node__get_sym_offset(struct pyrf_callchain_node
Py_RETURN_NONE;
addr = pnode->map ? map__map_ip(pnode->map, pnode->ip) : pnode->ip;
- return PyLong_FromUnsignedLongLong(addr - pnode->sym->start);
+ return PyLong_FromUnsignedLongLong(addr - symbol__start(pnode->sym));
}
static PyGetSetDef pyrf_callchain_node__getset[] = {
@@ -1330,7 +1330,7 @@ static PyObject *pyrf_sample_event__get_addr_symbol(struct pyrf_event *pevent,
PyObject *ret = Py_None;
if (pyrf_sample_event__resolve_addr_al(pevent, &addr_al) == 0 && addr_al.sym)
- ret = PyUnicode_FromString(addr_al.sym->name);
+ ret = PyUnicode_FromString(symbol__name(addr_al.sym));
else
Py_INCREF(Py_None);
@@ -1345,7 +1345,7 @@ static PyObject *pyrf_sample_event__get_addr_sym_offset(struct pyrf_event *peven
PyObject *ret = Py_None;
if (pyrf_sample_event__resolve_addr_al(pevent, &addr_al) == 0 && addr_al.sym)
- ret = PyLong_FromUnsignedLongLong(addr_al.addr - addr_al.sym->start);
+ ret = PyLong_FromUnsignedLongLong(addr_al.addr - symbol__start(addr_al.sym));
else
Py_INCREF(Py_None);
@@ -4505,7 +4505,7 @@ static PyObject *pyrf_call_path__to_callchain(const struct call_path *cp,
frame->ip = pos->ip;
frame->sym = NULL;
- frame->sym_name = pos->sym ? strdup(pos->sym->name) : NULL;
+ frame->sym_name = pos->sym ? strdup(symbol__name(pos->sym)) : NULL;
frame->map = NULL;
if (thread) {
struct addr_location al;
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c
index 58638ec9ae0e..62cf3d40e4b6 100644
--- a/tools/perf/util/sort.c
+++ b/tools/perf/util/sort.c
@@ -470,18 +470,19 @@ int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r)
return 0;
if (symbol__inlined(sym_l) || symbol__inlined(sym_r)) {
- int ret = strcmp(sym_l->name, sym_r->name);
+ int ret = strcmp(symbol__name(sym_l), symbol__name(sym_r));
if (ret)
return ret;
- if ((sym_l->start <= sym_r->end) && (sym_l->end >= sym_r->start))
+ if ((symbol__start(sym_l) <= symbol__end(sym_r)) &&
+ (symbol__end(sym_l) >= symbol__start(sym_r)))
return 0;
}
- if (sym_l->start != sym_r->start)
- return (int64_t)(sym_r->start - sym_l->start);
+ if (symbol__start(sym_l) != symbol__start(sym_r))
+ return (int64_t)(symbol__start(sym_r) - symbol__start(sym_l));
- return (int64_t)(sym_r->end - sym_l->end);
+ return (int64_t)(symbol__end(sym_r) - symbol__end(sym_l));
}
static int64_t
@@ -511,7 +512,7 @@ sort__sym_sort(struct hist_entry *left, struct hist_entry *right)
if (!left->ms.sym || !right->ms.sym)
return cmp_null(left->ms.sym, right->ms.sym);
- return strcmp(right->ms.sym->name, left->ms.sym->name);
+ return strcmp(symbol__name(right->ms.sym), symbol__name(left->ms.sym));
}
static int _hist_entry__sym_snprintf(struct map_symbol *ms,
@@ -537,13 +538,13 @@ static int _hist_entry__sym_snprintf(struct map_symbol *ms,
ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", level);
if (sym && map) {
if (symbol__type(sym) == STT_OBJECT) {
- ret += repsep_snprintf(bf + ret, size - ret, "%s", sym->name);
+ ret += repsep_snprintf(bf + ret, size - ret, "%s", symbol__name(sym));
ret += repsep_snprintf(bf + ret, size - ret, "+0x%llx",
- ip - map__unmap_ip(map, sym->start));
+ ip - map__unmap_ip(map, symbol__start(sym)));
} else {
ret += repsep_snprintf(bf + ret, size - ret, "%.*s",
width - ret,
- sym->name);
+ symbol__name(sym));
if (symbol__inlined(sym))
ret += repsep_snprintf(bf + ret, size - ret,
" (inlined)");
@@ -570,7 +571,7 @@ static int hist_entry__sym_filter(struct hist_entry *he, int type, const void *a
if (type != HIST_FILTER__SYMBOL)
return -1;
- return sym && (!he->ms.sym || !strstr(he->ms.sym->name, sym));
+ return sym && (!he->ms.sym || !strstr(symbol__name(he->ms.sym), sym));
}
struct sort_entry sort_sym = {
@@ -616,7 +617,8 @@ hist_entry__symoff_snprintf(struct hist_entry *he, char *bf, size_t size, unsign
if (sym == NULL)
return repsep_snprintf(bf, size, "[%c] %-#.*llx", he->level, width - 4, he->ip);
- return repsep_snprintf(bf, size, "[%c] %s+0x%llx", he->level, sym->name, he->ip - sym->start);
+ return repsep_snprintf(bf, size, "[%c] %s+0x%llx", he->level,
+ symbol__name(sym), he->ip - symbol__start(sym));
}
static struct sort_entry sort_sym_offset = {
@@ -1016,14 +1018,14 @@ sort__parent_cmp(struct hist_entry *left, struct hist_entry *right)
if (!sym_l || !sym_r)
return cmp_null(sym_l, sym_r);
- return strcmp(sym_r->name, sym_l->name);
+ return strcmp(symbol__name(sym_r), symbol__name(sym_l));
}
static int hist_entry__parent_snprintf(struct hist_entry *he, char *bf,
size_t size, unsigned int width)
{
return repsep_snprintf(bf, size, "%-*.*s", width, width,
- he->parent ? he->parent->name : "[other]");
+ he->parent ? symbol__name(he->parent) : "[other]");
}
struct sort_entry sort_parent = {
@@ -1426,7 +1428,7 @@ static int hist_entry__sym_from_filter(struct hist_entry *he, int type,
return -1;
return sym && !(he->branch_info && he->branch_info->from.ms.sym &&
- strstr(he->branch_info->from.ms.sym->name, sym));
+ strstr(symbol__name(he->branch_info->from.ms.sym), sym));
}
static int hist_entry__sym_to_filter(struct hist_entry *he, int type,
@@ -1438,7 +1440,7 @@ static int hist_entry__sym_to_filter(struct hist_entry *he, int type,
return -1;
return sym && !(he->branch_info && he->branch_info->to.ms.sym &&
- strstr(he->branch_info->to.ms.sym->name, sym));
+ strstr(symbol__name(he->branch_info->to.ms.sym), sym));
}
struct sort_entry sort_dso_from = {
@@ -1484,14 +1486,14 @@ static int _hist_entry__addr_snprintf(struct map_symbol *ms,
ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", level);
if (sym && map) {
if (symbol__type(sym) == STT_OBJECT) {
- ret += repsep_snprintf(bf + ret, size - ret, "%s", sym->name);
+ ret += repsep_snprintf(bf + ret, size - ret, "%s", symbol__name(sym));
ret += repsep_snprintf(bf + ret, size - ret, "+0x%llx",
- ip - map__unmap_ip(map, sym->start));
+ ip - map__unmap_ip(map, symbol__start(sym)));
} else {
ret += repsep_snprintf(bf + ret, size - ret, "%.*s",
width - ret,
- sym->name);
- offs = ip - sym->start;
+ symbol__name(sym));
+ offs = ip - symbol__start(sym);
if (offs)
ret += repsep_snprintf(bf + ret, size - ret, "+0x%llx", offs);
}
diff --git a/tools/perf/util/srcline.c b/tools/perf/util/srcline.c
index 23951c35fb66..14f2b6dfd086 100644
--- a/tools/perf/util/srcline.c
+++ b/tools/perf/util/srcline.c
@@ -116,7 +116,7 @@ struct symbol *new_inline_sym(struct dso *dso,
funcname = demangled;
}
- if (base_sym && strcmp(funcname, base_sym->name) == 0) {
+ if (base_sym && strcmp(funcname, symbol__name(base_sym)) == 0) {
/* reuse the real, existing symbol */
inline_sym = base_sym;
/* ensure that we don't alias an inlined symbol, which could
@@ -125,8 +125,9 @@ struct symbol *new_inline_sym(struct dso *dso,
assert(!symbol__inlined(base_sym));
} else {
/* create a fake symbol for the inline frame */
- inline_sym = symbol__new(base_sym ? base_sym->start : 0,
- base_sym ? (base_sym->end - base_sym->start) : 0,
+ inline_sym = symbol__new(base_sym ? symbol__start(base_sym) : 0,
+ base_sym ?
+ (symbol__end(base_sym) - symbol__start(base_sym)) : 0,
base_sym ? symbol__binding(base_sym) : 0,
base_sym ? symbol__type(base_sym) : 0,
funcname);
@@ -310,13 +311,14 @@ char *__get_srcline(struct dso *dso, u64 addr, struct symbol *sym,
}
mutex_unlock(dso__lock(dso));
out:
- if (!show_addr)
+ if (!show_addr) {
return (show_sym && sym) ?
- strndup(sym->name, sym->namelen) : SRCLINE_UNKNOWN;
+ strndup(symbol__name(sym), symbol__namelen(sym)) : SRCLINE_UNKNOWN;
+ }
if (sym) {
- if (asprintf(&srcline, "%s+%" PRIu64, show_sym ? sym->name : "",
- ip - sym->start) < 0)
+ if (asprintf(&srcline, "%s+%" PRIu64, show_sym ? symbol__name(sym) : "",
+ ip - symbol__start(sym)) < 0)
return SRCLINE_UNKNOWN;
} else if (asprintf(&srcline, "%s[%" PRIx64 "]", dso__short_name(dso), addr) < 0)
return SRCLINE_UNKNOWN;
diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c
index 1e2716c6af45..817486a4b8f1 100644
--- a/tools/perf/util/symbol-elf.c
+++ b/tools/perf/util/symbol-elf.c
@@ -351,11 +351,11 @@ static bool get_ifunc_name(Elf *elf, struct dso *dso, GElf_Ehdr *ehdr,
sym = dso__find_symbol_nocache(dso, addr);
/* Expecting the address to be an IFUNC or IFUNC alias */
- if (!sym || sym->start != addr ||
+ if (!sym || symbol__start(sym) != addr ||
(symbol__type(sym) != STT_GNU_IFUNC && !symbol__ifunc_alias(sym)))
return false;
- snprintf(buf, buf_sz, "%s@plt", sym->name);
+ snprintf(buf, buf_sz, "%s@plt", symbol__name(sym));
return true;
}
@@ -630,8 +630,8 @@ int dso__synthesize_plt_symbols(struct dso *dso, struct symsrc *ss)
* a symbol for .plt header.
*/
f = dso__find_symbol_nocache(dso, shdr_plt.sh_offset);
- if (f && f->start < shdr_plt.sh_offset && f->end > shdr_plt.sh_offset)
- f->end = shdr_plt.sh_offset;
+ if (f && symbol__start(f) < shdr_plt.sh_offset && symbol__end(f) > shdr_plt.sh_offset)
+ symbol__set_end(f, shdr_plt.sh_offset);
if (!get_plt_sizes(dso, &ehdr, &shdr_plt, &plt_header_size, &plt_entry_size))
return 0;
@@ -653,7 +653,7 @@ int dso__synthesize_plt_symbols(struct dso *dso, struct symsrc *ss)
if (!get_plt_sizes(dso, &ehdr, &plt_sec_shdr, &plt_header_size, &plt_entry_size))
return 0;
/* Extend .plt symbol to entire .plt */
- plt_sym->end = plt_sym->start + shdr_plt.sh_size;
+ symbol__set_end(plt_sym, symbol__start(plt_sym) + shdr_plt.sh_size);
/* Use .plt.sec offset */
plt_offset = plt_sec_shdr.sh_offset;
lazy_plt = false;
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index dc70bbe8514f..991167b2dfa4 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -149,9 +149,9 @@ int __weak arch__choose_best_symbol(struct symbol *syma,
struct symbol *symb __maybe_unused)
{
/* Avoid "SyS" kernel syscall aliases */
- if (strlen(syma->name) >= 3 && !strncmp(syma->name, "SyS", 3))
+ if (strlen(symbol__name(syma)) >= 3 && !strncmp(symbol__name(syma), "SyS", 3))
return SYMBOL_B;
- if (strlen(syma->name) >= 10 && !strncmp(syma->name, "compat_SyS", 10))
+ if (strlen(symbol__name(syma)) >= 10 && !strncmp(symbol__name(syma), "compat_SyS", 10))
return SYMBOL_B;
return SYMBOL_A;
@@ -164,8 +164,8 @@ static int choose_best_symbol(struct symbol *syma, struct symbol *symb)
size_t na, nb;
/* Prefer a symbol with non zero length */
- a = syma->end - syma->start;
- b = symb->end - symb->start;
+ a = symbol__end(syma) - symbol__start(syma);
+ b = symbol__end(symb) - symbol__start(symb);
if ((b == 0) && (a > 0))
return SYMBOL_A;
else if ((a == 0) && (b > 0))
@@ -195,16 +195,16 @@ static int choose_best_symbol(struct symbol *syma, struct symbol *symb)
return SYMBOL_B;
/* Prefer a symbol with less underscores */
- a = prefix_underscores_count(syma->name);
- b = prefix_underscores_count(symb->name);
+ a = prefix_underscores_count(symbol__name(syma));
+ b = prefix_underscores_count(symbol__name(symb));
if (b > a)
return SYMBOL_A;
else if (a > b)
return SYMBOL_B;
/* Choose the symbol with the longest name */
- na = strlen(syma->name);
- nb = strlen(symb->name);
+ na = strlen(symbol__name(syma));
+ nb = strlen(symbol__name(symb));
if (na > nb)
return SYMBOL_A;
else if (na < nb)
@@ -231,7 +231,7 @@ void symbols__fixup_duplicate(struct rb_root_cached *symbols)
break;
next = rb_entry(nd, struct symbol, rb_node);
- if (curr->start != next->start)
+ if (symbol__start(curr) != symbol__start(next))
continue;
if (choose_best_symbol(curr, next) == SYMBOL_A) {
@@ -281,39 +281,39 @@ void symbols__fixup_end(struct rb_root_cached *symbols, bool is_kallsyms)
* like in:
* ffffffffc1937000 T hdmi_driver_init [snd_hda_codec_hdmi]
*/
- if (prev->end == prev->start) {
+ if (symbol__end(prev) == symbol__start(prev)) {
const char *prev_mod;
const char *curr_mod;
if (!is_kallsyms) {
- prev->end = curr->start;
+ symbol__set_end(prev, symbol__start(curr));
continue;
}
- prev_mod = strchr(prev->name, '[');
- curr_mod = strchr(curr->name, '[');
+ prev_mod = strchr(symbol__name(prev), '[');
+ curr_mod = strchr(symbol__name(curr), '[');
/* Last kernel/module symbol mapped to end of page */
if (!prev_mod != !curr_mod)
- prev->end = roundup(prev->end + 4096, 4096);
+ symbol__set_end(prev, roundup(symbol__end(prev) + 4096, 4096));
/* Last symbol in the previous module */
else if (prev_mod && strcmp(prev_mod, curr_mod))
- prev->end = roundup(prev->end + 4096, 4096);
+ symbol__set_end(prev, roundup(symbol__end(prev) + 4096, 4096));
else
- prev->end = curr->start;
+ symbol__set_end(prev, symbol__start(curr));
/* The next module can start within that page */
- if (prev->end > curr->start)
- prev->end = curr->start;
+ if (symbol__end(prev) > symbol__start(curr))
+ symbol__set_end(prev, symbol__start(curr));
pr_debug4("%s sym:%s end:%#" PRIx64 "\n",
- __func__, prev->name, prev->end);
+ __func__, symbol__name(prev), symbol__end(prev));
}
}
/* Last entry */
- if (curr->end == curr->start)
- curr->end = roundup(curr->start, 4096) + 4096;
+ if (symbol__end(curr) == symbol__start(curr))
+ symbol__set_end(curr, roundup(symbol__start(curr), 4096) + 4096);
}
struct symbol *symbol__new(u64 start, u64 len, u8 binding, u8 type, const char *name)
@@ -417,14 +417,14 @@ void __symbols__insert(struct rb_root_cached *symbols, struct symbol *sym)
{
struct rb_node **p = &symbols->rb_root.rb_node;
struct rb_node *parent = NULL;
- const u64 ip = sym->start;
+ const u64 ip = symbol__start(sym);
struct symbol *s;
bool leftmost = true;
while (*p != NULL) {
parent = *p;
s = rb_entry(parent, struct symbol, rb_node);
- if (ip < s->start)
+ if (ip < symbol__start(s))
p = &(*p)->rb_left;
else {
p = &(*p)->rb_right;
@@ -452,9 +452,9 @@ static struct symbol *symbols__find(struct rb_root_cached *symbols, u64 ip)
while (n) {
struct symbol *s = rb_entry(n, struct symbol, rb_node);
- if (ip < s->start)
+ if (ip < symbol__start(s))
n = n->rb_left;
- else if (ip > s->end || (ip == s->end && ip != s->start))
+ else if (ip > symbol__end(s) || (ip == symbol__end(s) && ip != symbol__start(s)))
n = n->rb_right;
else
return s;
@@ -498,7 +498,7 @@ static int symbols__sort_name_cmp(const void *vlhs, const void *vrhs)
const struct symbol *lhs = *((const struct symbol **)vlhs);
const struct symbol *rhs = *((const struct symbol **)vrhs);
- return strcmp(lhs->name, rhs->name);
+ return strcmp(symbol__name(lhs), symbol__name(rhs));
}
static struct symbol **symbols__sort_by_name(struct rb_root_cached *source, size_t *len)
@@ -560,7 +560,7 @@ static struct symbol *symbols__find_by_name(struct symbol *symbols[],
int cmp;
i = (lower + upper) / 2;
- cmp = symbol__match_symbol_name(symbols[i]->name, name, includes);
+ cmp = symbol__match_symbol_name(symbol__name(symbols[i]), name, includes);
if (cmp > 0)
upper = i;
@@ -578,7 +578,7 @@ static struct symbol *symbols__find_by_name(struct symbol *symbols[],
for (; i > 0; i--) {
struct symbol *tmp = symbols[i - 1];
- if (!arch__compare_symbol_names(tmp->name, s->name)) {
+ if (!arch__compare_symbol_names(symbol__name(tmp), symbol__name(s))) {
if (found_idx)
*found_idx = i - 1;
s = tmp;
@@ -601,9 +601,9 @@ void dso__insert_symbol(struct dso *dso, struct symbol *sym)
__symbols__insert(dso__symbols(dso), sym);
/* update the symbol cache if necessary */
- if (dso__last_find_result_addr(dso) >= sym->start &&
- (dso__last_find_result_addr(dso) < sym->end ||
- sym->start == sym->end)) {
+ if (dso__last_find_result_addr(dso) >= symbol__start(sym) &&
+ (dso__last_find_result_addr(dso) < symbol__end(sym) ||
+ symbol__start(sym) == symbol__end(sym))) {
dso__set_last_find_result_symbol(dso, sym);
}
}
@@ -800,7 +800,7 @@ bool symbol__is_idle(struct symbol *sym, const struct dso *dso, struct perf_env
"poll_idle",
"pseries_dedicated_idle_sleep",
};
- const char *name = sym->name;
+ const char *name = symbol__name(sym);
uint16_t e_machine;
{
@@ -940,22 +940,25 @@ static int maps__split_kallsyms_for_kcore(struct maps *kmaps, struct dso *dso)
rb_erase_cached(&pos->rb_node, &old_root);
RB_CLEAR_NODE(&pos->rb_node);
- module = strchr(pos->name, '\t');
+ module = strchr((char *)symbol__name(pos), '\t');
if (module)
*module = '\0';
- curr_map = maps__find(kmaps, pos->start);
+ curr_map = maps__find(kmaps, symbol__start(pos));
if (!curr_map) {
symbol__delete(pos);
continue;
}
curr_map_dso = map__dso(curr_map);
- pos->start -= map__start(curr_map) - map__pgoff(curr_map);
- if (pos->end > map__end(curr_map))
- pos->end = map__end(curr_map);
- if (pos->end)
- pos->end -= map__start(curr_map) - map__pgoff(curr_map);
+ symbol__set_start(pos, symbol__start(pos) -
+ (map__start(curr_map) - map__pgoff(curr_map)));
+ if (symbol__end(pos) > map__end(curr_map))
+ symbol__set_end(pos, map__end(curr_map));
+ if (symbol__end(pos)) {
+ symbol__set_end(pos, symbol__end(pos) -
+ (map__start(curr_map) - map__pgoff(curr_map)));
+ }
symbols__insert(dso__symbols(curr_map_dso), pos);
++count;
map__put(curr_map);
@@ -996,7 +999,7 @@ static int maps__split_kallsyms(struct maps *kmaps, struct dso *dso, u64 delta,
pos = rb_entry(next, struct symbol, rb_node);
next = rb_next(&pos->rb_node);
- module = strchr(pos->name, '\t');
+ module = strchr((char *)symbol__name(pos), '\t');
if (module) {
struct dso *curr_map_dso;
@@ -1038,9 +1041,9 @@ static int maps__split_kallsyms(struct maps *kmaps, struct dso *dso, u64 delta,
* So that we look just like we get from .ko files,
* i.e. not prelinked, relative to initial_map->start.
*/
- pos->start = map__map_ip(curr_map, pos->start);
- pos->end = map__map_ip(curr_map, pos->end);
- } else if (e_machine == EM_X86_64 && is_entry_trampoline(pos->name)) {
+ symbol__set_start(pos, map__map_ip(curr_map, symbol__start(pos)));
+ symbol__set_end(pos, map__map_ip(curr_map, symbol__end(pos)));
+ } else if (e_machine == EM_X86_64 && is_entry_trampoline(symbol__name(pos))) {
/*
* These symbols are not needed anymore since the
* trampoline maps refer to the text section and it's
@@ -1056,12 +1059,12 @@ static int maps__split_kallsyms(struct maps *kmaps, struct dso *dso, u64 delta,
if (delta) {
/* Kernel was relocated at boot time */
- pos->start -= delta;
- pos->end -= delta;
+ symbol__set_start(pos, symbol__start(pos) - delta);
+ symbol__set_end(pos, symbol__end(pos) - delta);
}
- if (map__start(initial_map) <= (pos->start + delta) &&
- (pos->start + delta) < map__end(initial_map)) {
+ if (map__start(initial_map) <= (symbol__start(pos) + delta) &&
+ (symbol__start(pos) + delta) < map__end(initial_map)) {
map__zput(curr_map);
curr_map = map__get(initial_map);
goto add_symbol;
@@ -1091,7 +1094,7 @@ static int maps__split_kallsyms(struct maps *kmaps, struct dso *dso, u64 delta,
}
dso__set_loaded(ndso);
- curr_map = map__new2(pos->start, ndso);
+ curr_map = map__new2(symbol__start(pos), ndso);
if (curr_map == NULL) {
dso__put(ndso);
return -1;
@@ -1107,8 +1110,8 @@ static int maps__split_kallsyms(struct maps *kmaps, struct dso *dso, u64 delta,
++kernel_range;
} else if (delta) {
/* Kernel was relocated at boot time */
- pos->start -= delta;
- pos->end -= delta;
+ symbol__set_start(pos, symbol__start(pos) - delta);
+ symbol__set_end(pos, symbol__end(pos) - delta);
}
add_symbol:
if (!RC_CHK_EQUAL(curr_map, initial_map)) {
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index 46b1649c64fc..780ea3030a6f 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -101,6 +101,47 @@ struct symbol {
void symbol__delete(struct symbol *sym);
void symbols__delete(struct rb_root_cached *symbols);
+static inline __attribute__((nonnull)) u64 symbol__start(const struct symbol *sym)
+{
+ return sym->start;
+}
+
+static inline __attribute__((nonnull)) void symbol__set_start(struct symbol *sym, u64 start)
+{
+ sym->start = start;
+}
+
+static inline __attribute__((nonnull)) u64 symbol__end(const struct symbol *sym)
+{
+ return sym->end;
+}
+
+static inline __attribute__((nonnull)) void symbol__set_end(struct symbol *sym, u64 end)
+{
+ sym->end = end;
+}
+
+static inline __attribute__((nonnull, returns_nonnull))
+const char *symbol__name(const struct symbol *sym)
+{
+ return sym->name;
+}
+
+static inline __attribute__((nonnull)) u16 symbol__namelen(const struct symbol *sym)
+{
+ return sym->namelen;
+}
+
+static inline __attribute__((nonnull)) u8 symbol__arch_sym(const struct symbol *sym)
+{
+ return sym->arch_sym;
+}
+
+static inline __attribute__((nonnull)) void symbol__set_arch_sym(struct symbol *sym, u8 arch_sym)
+{
+ sym->arch_sym = arch_sym;
+}
+
static inline u8 symbol__type(const struct symbol *sym)
{
return (atomic_load_explicit(&sym->flags, memory_order_relaxed) &
@@ -157,7 +198,7 @@ void symbol__set_ifunc_alias(struct symbol *sym, bool ifunc_alias);
static inline size_t symbol__size(const struct symbol *sym)
{
- return sym->end - sym->start;
+ return symbol__end(sym) - symbol__start(sym);
}
struct strlist;
diff --git a/tools/perf/util/symbol_fprintf.c b/tools/perf/util/symbol_fprintf.c
index 4dc8d5761f52..8c131325e989 100644
--- a/tools/perf/util/symbol_fprintf.c
+++ b/tools/perf/util/symbol_fprintf.c
@@ -10,10 +10,10 @@
size_t symbol__fprintf(struct symbol *sym, FILE *fp)
{
return fprintf(fp, " %" PRIx64 "-%" PRIx64 " %c %s\n",
- sym->start, sym->end,
+ symbol__start(sym), symbol__end(sym),
symbol__binding(sym) == STB_GLOBAL ? 'g' :
symbol__binding(sym) == STB_LOCAL ? 'l' : 'w',
- sym->name);
+ symbol__name(sym));
}
size_t __symbol__fprintf_symname_offs(const struct symbol *sym,
@@ -25,12 +25,12 @@ size_t __symbol__fprintf_symname_offs(const struct symbol *sym,
size_t length;
if (sym) {
- length = fprintf(fp, "%s", sym->name);
+ length = fprintf(fp, "%s", symbol__name(sym));
if (al && print_offsets) {
- if (al->addr < sym->end)
- offset = al->addr - sym->start;
+ if (al->addr < symbol__end(sym))
+ offset = al->addr - symbol__start(sym);
else
- offset = al->addr - map__start(al->map) - sym->start;
+ offset = al->addr - map__start(al->map) - symbol__start(sym);
length += fprintf(fp, "+0x%lx", offset);
}
return length;
@@ -67,7 +67,7 @@ size_t dso__fprintf_symbols_by_name(struct dso *dso,
for (size_t i = 0; i < dso__symbol_names_len(dso); i++) {
struct symbol *pos = dso__symbol_names(dso)[i];
- ret += fprintf(fp, "%s\n", pos->name);
+ ret += fprintf(fp, "%s\n", symbol__name(pos));
}
return ret;
}
diff --git a/tools/perf/util/thread-stack.c b/tools/perf/util/thread-stack.c
index 1360f44421ef..76867ba83365 100644
--- a/tools/perf/util/thread-stack.c
+++ b/tools/perf/util/thread-stack.c
@@ -1056,7 +1056,7 @@ static int thread_stack__x86_retpoline(struct thread_stack *ts,
struct symbol *tsym = to_al->sym;
struct call_path *cp;
- if (sym && is_x86_retpoline(sym->name)) {
+ if (sym && is_x86_retpoline(symbol__name(sym))) {
/*
* This is a x86 retpoline fn. It pollutes the call graph by
* showing up everywhere there is an indirect branch, but does
@@ -1069,7 +1069,7 @@ static int thread_stack__x86_retpoline(struct thread_stack *ts,
*/
ts->cnt -= 1;
sym = ts->stack[ts->cnt - 2].cp->sym;
- if (sym && sym == tsym && to_al->addr != tsym->start) {
+ if (sym && sym == tsym && to_al->addr != symbol__start(tsym)) {
/*
* Target is back to the middle of the symbol we came
* from so assume it is an indirect jmp and forget it
@@ -1170,7 +1170,7 @@ int thread_stack__process(struct thread *thread, struct comm *comm,
*/
if (!err && rstate == X86_RETPOLINE_POSSIBLE && to_al->sym &&
from_al->sym == to_al->sym &&
- to_al->addr != to_al->sym->start)
+ to_al->addr != symbol__start(to_al->sym))
ts->rstate = X86_RETPOLINE_DETECTED;
} else if (sample->flags & PERF_IP_FLAG_RETURN) {
@@ -1207,7 +1207,7 @@ int thread_stack__process(struct thread *thread, struct comm *comm,
err = thread_stack__trace_end(ts, sample, ref);
} else if (sample->flags & PERF_IP_FLAG_BRANCH &&
from_al->sym != to_al->sym && to_al->sym &&
- to_al->addr == to_al->sym->start) {
+ to_al->addr == symbol__start(to_al->sym)) {
struct call_path_root *cpr = ts->crp->cpr;
struct call_path *cp;
diff --git a/tools/perf/util/unwind-libdw.c b/tools/perf/util/unwind-libdw.c
index d49901b1571e..87ade098b5c6 100644
--- a/tools/perf/util/unwind-libdw.c
+++ b/tools/perf/util/unwind-libdw.c
@@ -172,7 +172,7 @@ static int entry(u64 ip, struct unwind_info *ui)
e->ms.sym = al.sym;
pr_debug("unwind: %s:ip = 0x%" PRIx64 " (0x%" PRIx64 ")\n",
- al.sym ? al.sym->name : "''",
+ al.sym ? symbol__name(al.sym) : "''",
ip,
al.map ? map__map_ip(al.map, ip) : (u64) 0);
addr_location__exit(&al);
--
2.56.0.313.g2479322e68-goog
^ permalink raw reply [flat|nested] 37+ messages in thread* [PATCH v2 04/18] perf map_browser: Compute symbol browser index on demand
2026-10-27 23:14 ` [PATCH v2 00/18] perf symbol: Reference counting, sorted arrays, and memory shrinking Ian Rogers
` (2 preceding siblings ...)
2026-10-27 23:14 ` [PATCH v2 03/18] perf symbol: Add accessor functions for struct symbol fields Ian Rogers
@ 2026-10-27 23:14 ` Ian Rogers
2026-10-27 23:14 ` [PATCH v2 05/18] perf db-export: Store symbol database IDs in a hashmap Ian Rogers
` (14 subsequent siblings)
18 siblings, 0 replies; 37+ messages in thread
From: Ian Rogers @ 2026-10-27 23:14 UTC (permalink / raw)
To: irogers, acme, namhyung
Cc: adrian.hunter, haghdoost, james.clark, jolsa, linux-kernel,
linux-perf-users, mingo, peterz
To wrap struct symbol in DECLARE_RC_STRUCT(symbol) and reduce per-symbol
memory overhead, negative-offset allocations ahead of struct symbol via
symbol_conf.priv_size and symbol__priv() need to be eliminated.
Remove symbol__browser_index() in ui/browsers/map.c and compute the TUI
map browser index on demand in map_browser__search() by walking the map
browser's symbol entries when the user searches with '/'.
While this changes finding a symbol's display row index in
map_browser__search() from O(1) to O(N) in the number of symbols in that
single map, map_browser__search() is only invoked interactively when a
user opens the TUI "Browse map details" view for a map, presses '/',
types a query, and hits ENTER. Even for a large DSO such as vmlinux
(~100k symbols), scanning the map's symbols once per interactive search
takes well under a millisecond (and becomes a flat pointer array scan
once symbols are stored in struct symbols).
Previously, '/' search in the TUI map browser was gated behind
'verbose > 0' (-v) in builtin-c2c.c, builtin-report.c, and
ui/browsers/map.c solely to avoid adding sizeof(u32) to
symbol_conf.priv_size for every struct symbol across all DSOs in normal
runs. With on-demand index computation there is no per-symbol memory
overhead, so remove the verbose > 0 restriction and allow '/' search in
the map browser unconditionally.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/builtin-c2c.c | 14 --------------
tools/perf/builtin-report.c | 14 --------------
tools/perf/ui/browsers/map.c | 37 ++++++++++++++++++------------------
3 files changed, 18 insertions(+), 47 deletions(-)
diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c
index f51fa23f3bf1..719f47dac2dc 100644
--- a/tools/perf/builtin-c2c.c
+++ b/tools/perf/builtin-c2c.c
@@ -3412,20 +3412,6 @@ static int perf_c2c__report(int argc, const char **argv)
if (ret < 0)
goto out_mem2node;
- /*
- * For searching by name on the "Browse map details".
- * providing it only in verbose mode not to bloat too
- * much struct symbol.
- */
- if (verbose > 0) {
- /*
- * XXX: Need to provide a less kludgy way to ask for
- * more space per symbol, the u32 is for the index on
- * the ui browser.
- * See symbol__browser_index.
- */
- symbol_conf.priv_size += sizeof(u32);
- }
annotation_config__init();
}
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index 29085fdc91e5..6672a4d16529 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -1885,20 +1885,6 @@ int cmd_report(int argc, const char **argv)
ret = symbol__annotation_init();
if (ret < 0)
goto error;
- /*
- * For searching by name on the "Browse map details".
- * providing it only in verbose mode not to bloat too
- * much struct symbol.
- */
- if (verbose > 0) {
- /*
- * XXX: Need to provide a less kludgy way to ask for
- * more space per symbol, the u32 is for the index on
- * the ui browser.
- * See symbol__browser_index.
- */
- symbol_conf.priv_size += sizeof(u32);
- }
annotation_config__init();
}
diff --git a/tools/perf/ui/browsers/map.c b/tools/perf/ui/browsers/map.c
index 47f2743cae26..2f070a53b5cb 100644
--- a/tools/perf/ui/browsers/map.c
+++ b/tools/perf/ui/browsers/map.c
@@ -39,12 +39,6 @@ static void map_browser__write(struct ui_browser *browser, void *nd, int row)
ui_browser__write_nstring(browser, symbol__name(sym), width);
}
-/* FIXME uber-kludgy, see comment on cmd_report... */
-static u32 *symbol__browser_index(struct symbol *browser)
-{
- return ((void *)browser) - sizeof(struct rb_node) - sizeof(u32);
-}
-
static int map_browser__search(struct map_browser *browser)
{
char target[512];
@@ -62,10 +56,21 @@ static int map_browser__search(struct map_browser *browser)
sym = map__find_symbol_by_name(browser->map, target);
if (sym != NULL) {
- u32 *idx = symbol__browser_index(sym);
-
- browser->b.top = &sym->rb_node;
- browser->b.index = browser->b.top_idx = *idx;
+ struct rb_node *nd;
+ u32 idx = 0;
+
+ /*
+ * Walk the map browser's symbol entries to find the matching
+ * symbol node and its display row index, then position the
+ * browser cursor at that entry.
+ */
+ for (nd = rb_first(browser->b.entries); nd; nd = rb_next(nd), ++idx) {
+ if (&sym->rb_node == nd) {
+ browser->b.top = nd;
+ browser->b.index = browser->b.top_idx = idx;
+ break;
+ }
+ }
} else
ui_helpline__fpush("%s not found!", target);
@@ -77,8 +82,7 @@ static int map_browser__run(struct map_browser *browser)
int key;
if (ui_browser__show(&browser->b, dso__long_name(map__dso(browser->map)),
- "Press ESC to exit, %s / to search",
- verbose > 0 ? "" : "restart with -v to use") < 0)
+ "Press ESC to exit, / to search") < 0)
return -1;
while (1) {
@@ -86,9 +90,8 @@ static int map_browser__run(struct map_browser *browser)
switch (key) {
case '/':
- if (verbose > 0)
- map_browser__search(browser);
- /* fall thru */
+ map_browser__search(browser);
+ continue;
default:
ui_browser__warn_unhandled_hotkey(&browser->b, key, 0, NULL);
continue;
@@ -124,10 +127,6 @@ int map__browse(struct map *map)
if (maxaddr < symbol__end(pos))
maxaddr = symbol__end(pos);
- if (verbose > 0) {
- u32 *idx = symbol__browser_index(pos);
- *idx = mb.b.nr_entries;
- }
++mb.b.nr_entries;
}
--
2.56.0.313.g2479322e68-goog
^ permalink raw reply [flat|nested] 37+ messages in thread* [PATCH v2 05/18] perf db-export: Store symbol database IDs in a hashmap
2026-10-27 23:14 ` [PATCH v2 00/18] perf symbol: Reference counting, sorted arrays, and memory shrinking Ian Rogers
` (3 preceding siblings ...)
2026-10-27 23:14 ` [PATCH v2 04/18] perf map_browser: Compute symbol browser index on demand Ian Rogers
@ 2026-10-27 23:14 ` Ian Rogers
2026-10-27 23:15 ` [PATCH v2 06/18] perf annotate: Move struct annotation to a hashmap and remove symbol_conf.priv_size Ian Rogers
` (13 subsequent siblings)
18 siblings, 0 replies; 37+ messages in thread
From: Ian Rogers @ 2026-10-27 23:14 UTC (permalink / raw)
To: irogers, acme, namhyung
Cc: adrian.hunter, haghdoost, james.clark, jolsa, linux-kernel,
linux-perf-users, mingo, peterz
Currently, db-export reserves sizeof(u64) bytes ahead of every allocated
struct symbol via symbol_conf.priv_size and accesses the database ID at
a negative offset via symbol__priv(). This adds 8 bytes to every loaded
symbol across all DSOs even when only a subset of symbols are exported,
and negative-offset pointer arithmetic ahead of struct symbol is
incompatible with wrapping struct symbol in DECLARE_RC_STRUCT(symbol).
Store exported symbol database IDs in an on-demand sym_db_ids hashmap in
struct db_export instead of using symbol__priv().
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/db-export.c | 68 ++++++++++++++++++++++++++++++-------
tools/perf/util/db-export.h | 8 +++++
2 files changed, 63 insertions(+), 13 deletions(-)
diff --git a/tools/perf/util/db-export.c b/tools/perf/util/db-export.c
index 732369fe0046..6ebc060bdb8f 100644
--- a/tools/perf/util/db-export.c
+++ b/tools/perf/util/db-export.c
@@ -4,21 +4,37 @@
* Copyright (c) 2014, Intel Corporation.
*/
+#include "db-export.h"
+
#include <errno.h>
+#include <stdbool.h>
+#include <stddef.h>
#include <stdlib.h>
+#include <linux/err.h>
+
+#include "call-path.h"
+#include "callchain.h"
+#include "comm.h"
#include "dso.h"
+#include "event.h"
#include "evsel.h"
+#include "hashmap.h"
#include "machine.h"
-#include "thread.h"
-#include "comm.h"
-#include "symbol.h"
#include "map.h"
-#include "event.h"
+#include "symbol.h"
#include "thread-stack.h"
-#include "callchain.h"
-#include "call-path.h"
-#include "db-export.h"
+#include "thread.h"
+
+static size_t sym_ptr_hash(long key, void *ctx __maybe_unused)
+{
+ return hash_bits((size_t)key, 16);
+}
+
+static bool sym_ptr_equal(long key1, long key2, void *ctx __maybe_unused)
+{
+ return key1 == key2;
+}
int db_export__init(struct db_export *dbe)
{
@@ -28,6 +44,8 @@ int db_export__init(struct db_export *dbe)
void db_export__exit(struct db_export *dbe)
{
+ hashmap__free(dbe->sym_db_ids);
+ dbe->sym_db_ids = NULL;
call_return_processor__free(dbe->crp);
dbe->crp = NULL;
}
@@ -156,15 +174,41 @@ int db_export__dso(struct db_export *dbe, struct dso *dso,
return 0;
}
+/*
+ * Return the database export ID assigned to @sym in @dbe->sym_db_ids, or 0
+ * if @sym has not been exported yet.
+ */
+u64 db_export__symbol_db_id(struct db_export *dbe, struct symbol *sym)
+{
+ long db_id = 0;
+
+ if (sym && dbe->sym_db_ids)
+ hashmap__find(dbe->sym_db_ids, (long)sym, &db_id);
+ return (u64)db_id;
+}
+
int db_export__symbol(struct db_export *dbe, struct symbol *sym,
struct dso *dso)
{
- u64 *sym_db_id = symbol__priv(sym);
+ long db_id;
- if (*sym_db_id)
+ if (db_export__symbol_db_id(dbe, sym))
return 0;
- *sym_db_id = ++dbe->symbol_last_db_id;
+ if (!dbe->sym_db_ids) {
+ dbe->sym_db_ids =
+ hashmap__new(sym_ptr_hash, sym_ptr_equal, NULL);
+ if (IS_ERR(dbe->sym_db_ids)) {
+ int err = PTR_ERR(dbe->sym_db_ids);
+
+ dbe->sym_db_ids = NULL;
+ return err;
+ }
+ }
+
+ db_id = ++dbe->symbol_last_db_id;
+ if (hashmap__set(dbe->sym_db_ids, (long)sym, db_id, NULL, NULL))
+ return -ENOMEM;
if (dbe->export_symbol)
return dbe->export_symbol(dbe, sym, dso);
@@ -192,12 +236,10 @@ static int db_ids_from_al(struct db_export *dbe, struct addr_location *al,
}
if (al->sym) {
- u64 *db_id = symbol__priv(al->sym);
-
err = db_export__symbol(dbe, al->sym, dso);
if (err)
return err;
- *sym_db_id = *db_id;
+ *sym_db_id = db_export__symbol_db_id(dbe, al->sym);
*offset = al->addr - symbol__start(al->sym);
}
}
diff --git a/tools/perf/util/db-export.h b/tools/perf/util/db-export.h
index 1abbfd398e3a..2e0cf2dfa281 100644
--- a/tools/perf/util/db-export.h
+++ b/tools/perf/util/db-export.h
@@ -21,6 +21,8 @@ struct call_return_processor;
struct call_path_root;
struct call_path;
struct call_return;
+struct hashmap;
+struct symbol;
struct export_sample {
union perf_event *event;
@@ -63,6 +65,11 @@ struct db_export {
u64 th_in_id, u64 comm_in_id, int flags);
struct call_return_processor *crp;
struct call_path_root *cpr;
+ /**
+ * @sym_db_ids: Hashmap mapping struct symbol pointers to their
+ * exported 64-bit database IDs.
+ */
+ struct hashmap *sym_db_ids;
u64 evsel_last_db_id;
u64 machine_last_db_id;
u64 thread_last_db_id;
@@ -90,6 +97,7 @@ int db_export__comm_thread(struct db_export *dbe, struct comm *comm,
struct thread *thread);
int db_export__dso(struct db_export *dbe, struct dso *dso,
struct machine *machine);
+u64 db_export__symbol_db_id(struct db_export *dbe, struct symbol *sym);
int db_export__symbol(struct db_export *dbe, struct symbol *sym,
struct dso *dso);
int db_export__branch_type(struct db_export *dbe, u32 branch_type,
--
2.56.0.313.g2479322e68-goog
^ permalink raw reply [flat|nested] 37+ messages in thread* [PATCH v2 06/18] perf annotate: Move struct annotation to a hashmap and remove symbol_conf.priv_size
2026-10-27 23:14 ` [PATCH v2 00/18] perf symbol: Reference counting, sorted arrays, and memory shrinking Ian Rogers
` (4 preceding siblings ...)
2026-10-27 23:14 ` [PATCH v2 05/18] perf db-export: Store symbol database IDs in a hashmap Ian Rogers
@ 2026-10-27 23:15 ` Ian Rogers
2026-10-27 23:15 ` [PATCH v2 07/18] perf symbol: Switch backing storage from rbtree to struct symbols array Ian Rogers
` (12 subsequent siblings)
18 siblings, 0 replies; 37+ messages in thread
From: Ian Rogers @ 2026-10-27 23:15 UTC (permalink / raw)
To: irogers, acme, namhyung
Cc: adrian.hunter, haghdoost, james.clark, jolsa, linux-kernel,
linux-perf-users, mingo, peterz
Previously, enabling annotation added sizeof(struct annotation) to
symbol_conf.priv_size, bloating every loaded struct symbol in every DSO
even though only a small fraction of symbols are ever annotated. In
addition, allocating private data at a negative offset ahead of struct
symbol via symbol__priv() prevents wrapping struct symbol with
DECLARE_RC_STRUCT(symbol) for reference-count checking.
Move struct annotation storage to an on-demand hashmap in
util/annotate.c keyed by struct symbol pointer, guarded by
annotations_lock and tracked via a new SYMBOL_FLAG_ANNOTATED flag in
sym->flags so unannotated symbols pay zero lookup or teardown overhead.
If allocation fails in symbol__annotation(), return a static
empty_annotation fallback so callers that do not check for NULL remain
safe while mutating callers check for &empty_annotation.
With annotation, TUI map browser indices, and db-export symbol IDs no
longer using negative-offset space ahead of struct symbol, remove
symbol_conf.priv_size and symbol__priv().
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/tests/builtin-test.c | 1 -
tools/perf/ui/browsers/hists.c | 13 +++-
tools/perf/util/annotate.c | 132 +++++++++++++++++++++++++++++++-
tools/perf/util/annotate.h | 6 +-
tools/perf/util/sort.c | 5 +-
tools/perf/util/symbol.c | 34 ++++----
tools/perf/util/symbol.h | 16 ++--
tools/perf/util/symbol_conf.h | 1 -
8 files changed, 167 insertions(+), 41 deletions(-)
diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c
index 0b8897fd42ca..9e52a194f919 100644
--- a/tools/perf/tests/builtin-test.c
+++ b/tools/perf/tests/builtin-test.c
@@ -2050,7 +2050,6 @@ int cmd_test(int argc, const char **argv)
if (dont_fork)
sequential = true;
- symbol_conf.priv_size = sizeof(int);
symbol_conf.try_vmlinux_path = true;
diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c
index 206657039223..1f1a877f667e 100644
--- a/tools/perf/ui/browsers/hists.c
+++ b/tools/perf/ui/browsers/hists.c
@@ -796,7 +796,9 @@ static void hist_browser__show_callchain_entry(struct hist_browser *browser,
{
int color, width;
char folded_sign = callchain_list__folded(chain);
- bool show_annotated = browser->show_dso && chain->ms.sym && symbol__annotation(chain->ms.sym)->src;
+ bool show_annotated = browser->show_dso && chain->ms.sym &&
+ symbol__annotated(chain->ms.sym) &&
+ symbol__annotation(chain->ms.sym)->src;
color = HE_COLORSET_NORMAL;
width = browser->b.width - (offset + 2);
@@ -2486,6 +2488,9 @@ do_annotate(struct hist_browser *browser, struct popup_action *act)
perf_env__lookup_objdump(browser->env, &annotate_opts.objdump_path))
return 0;
+ if (!symbol__annotated(act->ms.sym))
+ return 0;
+
notes = symbol__annotation(act->ms.sym);
if (!notes->src)
return 0;
@@ -2545,7 +2550,8 @@ add_annotate_opt(struct popup_action *act, char **optstr,
if (!ms->sym)
ms->sym = symbol__new_unresolved(addr, ms->map);
- if (ms->sym == NULL || symbol__annotation(ms->sym)->src == NULL)
+ if (ms->sym == NULL || !symbol__annotated(ms->sym) ||
+ symbol__annotation(ms->sym)->src == NULL)
return 0;
if (asprintf(optstr, "Annotate %s", symbol__name(ms->sym)) < 0)
@@ -3204,7 +3210,8 @@ static int evsel__hists_browse(struct evsel *evsel, int nr_events, const char *h
do_annotate(browser, &hotkey_act);
map_symbol__exit(&hotkey_act.ms);
} else {
- if (symbol__annotation(browser->selection->sym)->src == NULL) {
+ if (!symbol__annotated(browser->selection->sym) ||
+ symbol__annotation(browser->selection->sym)->src == NULL) {
ui_browser__warning(&browser->b, delay_secs * 2,
"No samples for the \"%s\" symbol.\n\n"
"Probably appeared just in a callchain",
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c
index 4163689b6b2c..e4332800fda4 100644
--- a/tools/perf/util/annotate.c
+++ b/tools/perf/util/annotate.c
@@ -8,6 +8,9 @@
#include <errno.h>
#include <inttypes.h>
+#include <pthread.h>
+#include <stdbool.h>
+#include <stddef.h>
#include <stdlib.h>
#include "util.h" // hex_width()
#include "ui/ui.h"
@@ -35,6 +38,7 @@
#include "string2.h"
#include "dwarf-regs.h"
#include "util/event.h"
+#include "util/rwsem.h"
#include "util/sharded_mutex.h"
#include "arch/common.h"
#include "namespaces.h"
@@ -118,6 +122,7 @@ static __maybe_unused void annotated_source__delete(struct annotated_source *src
if (src == NULL)
return;
+ annotated_source__purge(src);
if (src->samples) {
hashmap__for_each_entry(src->samples, cur, bkt)
zfree(&cur->pvalue);
@@ -156,9 +161,126 @@ annotated_source__hist_entry(struct annotated_source *src, const struct evsel *e
return entry;
}
+/*
+ * Map from struct symbol pointer to struct annotation. Allocated on demand so
+ * unannotated symbols do not pay the memory cost of struct annotation.
+ * empty_annotation is returned as a fallback on allocation failure so callers
+ * that do not check for NULL do not dereference NULL, while mutating callers
+ * check for &empty_annotation and return an error.
+ */
+static struct rw_semaphore _annotations_lock;
+static struct hashmap *annotations GUARDED_BY(_annotations_lock);
+static struct annotation empty_annotation;
+
+static void annotations_lock_init(void)
+{
+ init_rwsem(&_annotations_lock);
+}
+
+static struct rw_semaphore *annotations_lock(void) LOCK_RETURNED(_annotations_lock)
+{
+ static pthread_once_t annotations_lock_once = PTHREAD_ONCE_INIT;
+
+ pthread_once(&annotations_lock_once, annotations_lock_init);
+ return &_annotations_lock;
+}
+
+static size_t sym_ptr_hash(long key, void *ctx __maybe_unused)
+{
+ return hash_bits((size_t)key, 16);
+}
+
+static bool sym_ptr_equal(long key1, long key2, void *ctx __maybe_unused)
+{
+ return key1 == key2;
+}
+
+struct annotation *symbol__annotation(struct symbol *sym)
+{
+ struct annotation *notes = NULL;
+ long val = 0;
+
+ if (!sym)
+ return NULL;
+
+ /* Fast path: look up existing annotation under read lock if flagged. */
+ if (symbol__annotated(sym)) {
+ down_read(annotations_lock());
+ if (annotations && hashmap__find(annotations, (long)sym, &val))
+ notes = (struct annotation *)val;
+ up_read(annotations_lock());
+ if (notes)
+ return notes;
+ }
+
+ down_write(annotations_lock());
+ if (!annotations) {
+ annotations = hashmap__new(sym_ptr_hash, sym_ptr_equal, NULL);
+ if (IS_ERR(annotations)) {
+ annotations = NULL;
+ up_write(annotations_lock());
+ return &empty_annotation;
+ }
+ } else if (hashmap__find(annotations, (long)sym, &val)) {
+ notes = (struct annotation *)val;
+ up_write(annotations_lock());
+ return notes;
+ }
+
+ notes = zalloc(sizeof(*notes));
+ if (!notes) {
+ up_write(annotations_lock());
+ return &empty_annotation;
+ }
+ annotation__init(notes);
+ if (hashmap__add(annotations, (long)sym, notes) < 0) {
+ free(notes);
+ if (hashmap__size(annotations) == 0) {
+ hashmap__free(annotations);
+ annotations = NULL;
+ }
+ up_write(annotations_lock());
+ return &empty_annotation;
+ }
+ symbol__set_annotated(sym, true);
+ up_write(annotations_lock());
+ return notes;
+}
+
+void symbol__annotation_delete(struct symbol *sym)
+{
+ struct annotation *notes = NULL;
+ long val = 0;
+
+ /* Unannotated symbols avoid taking annotations_lock on teardown. */
+ if (!sym || !symbol__annotated(sym))
+ return;
+
+ down_write(annotations_lock());
+ if (annotations && hashmap__delete(annotations, (long)sym, NULL, &val)) {
+ notes = (struct annotation *)val;
+ if (hashmap__size(annotations) == 0) {
+ hashmap__free(annotations);
+ annotations = NULL;
+ }
+ }
+ symbol__set_annotated(sym, false);
+ up_write(annotations_lock());
+
+ if (notes) {
+ annotation__exit(notes);
+ free(notes);
+ }
+}
+
void symbol__annotate_zero_histograms(struct symbol *sym)
{
- struct annotation *notes = symbol__annotation(sym);
+ struct annotation *notes;
+
+ if (!symbol__annotated(sym))
+ return;
+
+ notes = symbol__annotation(sym);
annotation__lock(notes);
if (notes->src != NULL) {
@@ -298,7 +420,7 @@ static int __symbol__inc_addr_samples(struct map_symbol *ms,
struct annotated_branch *annotation__get_branch(struct annotation *notes)
{
- if (notes == NULL)
+ if (notes == NULL || notes == &empty_annotation)
return NULL;
if (notes->branch == NULL)
@@ -337,6 +459,9 @@ struct annotated_source *symbol__hists(struct symbol *sym, int nr_hists)
{
struct annotation *notes = symbol__annotation(sym);
+ if (!notes || notes == &empty_annotation)
+ return NULL;
+
if (notes->src == NULL) {
notes->src = annotated_source__new();
if (notes->src == NULL)
@@ -1142,6 +1267,9 @@ int symbol__annotate(struct map_symbol *ms, struct evsel *evsel,
if (parch)
*parch = arch;
+ if (!notes || notes == &empty_annotation)
+ return -ENOMEM;
+
if (notes->src && !list_empty(¬es->src->source))
return 0;
diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h
index 1b47c1156b9a..d87de0f11a60 100644
--- a/tools/perf/util/annotate.h
+++ b/tools/perf/util/annotate.h
@@ -449,10 +449,8 @@ static inline struct sym_hist *annotation__histogram(struct annotation *notes,
struct sym_hist_entry *
annotated_source__hist_entry(struct annotated_source *src, const struct evsel *evsel, u64 offset);
-static inline struct annotation *symbol__annotation(struct symbol *sym)
-{
- return (void *)sym - symbol_conf.priv_size;
-}
+struct annotation *symbol__annotation(struct symbol *sym);
+void symbol__annotation_delete(struct symbol *sym);
int addr_map_symbol__inc_samples(struct addr_map_symbol *ams, struct perf_sample *sample);
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c
index 62cf3d40e4b6..c4eca7a98642 100644
--- a/tools/perf/util/sort.c
+++ b/tools/perf/util/sort.c
@@ -795,14 +795,15 @@ static int hist_entry__sym_ipc_snprintf(struct hist_entry *he, char *bf,
{
struct symbol *sym = he->ms.sym;
- struct annotated_branch *branch;
+ struct annotated_branch *branch = NULL;
double ipc = 0.0, coverage = 0.0;
char tmp[64];
if (!sym)
return repsep_snprintf(bf, size, "%-*s", width, "-");
- branch = symbol__annotation(sym)->branch;
+ if (symbol__annotated(sym))
+ branch = symbol__annotation(sym)->branch;
if (branch && branch->hit_cycles)
ipc = branch->hit_insn / ((double)branch->hit_cycles);
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 991167b2dfa4..5b50cca7989d 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -319,19 +319,11 @@ void symbols__fixup_end(struct rb_root_cached *symbols, bool is_kallsyms)
struct symbol *symbol__new(u64 start, u64 len, u8 binding, u8 type, const char *name)
{
size_t namelen = strlen(name) + 1;
- struct symbol *sym = calloc(1, (symbol_conf.priv_size +
- sizeof(*sym) + namelen));
+ struct symbol *sym = calloc(1, sizeof(*sym) + namelen);
+
if (sym == NULL)
return NULL;
- if (symbol_conf.priv_size) {
- if (symbol_conf.init_annotation) {
- struct annotation *notes = (void *)sym;
- annotation__init(notes);
- }
- sym = ((void *)sym) + symbol_conf.priv_size;
- }
-
sym->start = start;
sym->end = len ? start + len : start;
atomic_init(&sym->flags, (type << SYMBOL_FLAG_TYPE_SHIFT) |
@@ -347,14 +339,9 @@ struct symbol *symbol__new(u64 start, u64 len, u8 binding, u8 type, const char *
void symbol__delete(struct symbol *sym)
{
- if (symbol_conf.priv_size) {
- if (symbol_conf.init_annotation) {
- struct annotation *notes = symbol__annotation(sym);
-
- annotation__exit(notes);
- }
- }
- free(((void *)sym) - symbol_conf.priv_size);
+ if (symbol__annotated(sym))
+ symbol__annotation_delete(sym);
+ free(sym);
}
void symbol__set_ignore(struct symbol *sym, bool ignore)
@@ -389,6 +376,14 @@ void symbol__set_ifunc_alias(struct symbol *sym, bool ifunc_alias)
atomic_fetch_and(&sym->flags, ~SYMBOL_FLAG_IFUNC_ALIAS);
}
+void symbol__set_annotated(struct symbol *sym, bool annotated)
+{
+ if (annotated)
+ atomic_fetch_or(&sym->flags, SYMBOL_FLAG_ANNOTATED);
+ else
+ atomic_fetch_and(&sym->flags, ~SYMBOL_FLAG_ANNOTATED);
+}
+
static void symbol__set_idle(struct symbol *sym, bool idle)
{
uint16_t old_flags = atomic_load_explicit(&sym->flags, memory_order_relaxed);
@@ -2482,7 +2477,6 @@ int symbol__annotation_init(void)
return -1;
}
- symbol_conf.priv_size += sizeof(struct annotation);
symbol_conf.init_annotation = true;
return 0;
}
@@ -2525,8 +2519,6 @@ int symbol__init(struct perf_env *env)
if (symbol_conf.initialized)
return 0;
- symbol_conf.priv_size = PERF_ALIGN(symbol_conf.priv_size, sizeof(u64));
-
symbol__elf_init();
if (symbol_conf.try_vmlinux_path && vmlinux_path__init(env) < 0)
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index 780ea3030a6f..37e6ab1478b5 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -80,9 +80,9 @@ enum symbol_idle_kind {
#define SYMBOL_FLAG_INLINED (1 << 11)
#define SYMBOL_FLAG_ANNOTATE2 (1 << 12)
#define SYMBOL_FLAG_IFUNC_ALIAS (1 << 13)
+#define SYMBOL_FLAG_ANNOTATED (1 << 14)
/**
- * A symtab entry. When allocated this may be preceded by an annotation (see
- * symbol__annotation) and/or a browser_index (see symbol__browser_index).
+ * A symtab entry.
*/
struct symbol {
struct rb_node rb_node;
@@ -178,12 +178,19 @@ static inline bool symbol__ifunc_alias(const struct symbol *sym)
SYMBOL_FLAG_IFUNC_ALIAS) != 0;
}
+static inline bool symbol__annotated(const struct symbol *sym)
+{
+ return (atomic_load_explicit(&sym->flags, memory_order_relaxed) &
+ SYMBOL_FLAG_ANNOTATED) != 0;
+}
+
bool symbol__is_idle(struct symbol *sym, const struct dso *dso, struct perf_env *env);
void symbol__set_ignore(struct symbol *sym, bool ignore);
void symbol__set_annotate2(struct symbol *sym, bool annotate2);
void symbol__set_inlined(struct symbol *sym, bool inlined);
void symbol__set_ifunc_alias(struct symbol *sym, bool ifunc_alias);
+void symbol__set_annotated(struct symbol *sym, bool annotated);
/* symbols__for_each_entry - iterate over symbols (rb_root)
*
@@ -217,11 +224,6 @@ static inline int __symbol__join_symfs(char *bf, size_t size, const char *path)
extern int vmlinux_path__nr_entries;
extern char **vmlinux_path;
-static inline void *symbol__priv(struct symbol *sym)
-{
- return ((void *)sym) - symbol_conf.priv_size;
-}
-
struct ref_reloc_sym {
const char *name;
u64 addr;
diff --git a/tools/perf/util/symbol_conf.h b/tools/perf/util/symbol_conf.h
index 37d35f42dcc1..aaa7148c3ac1 100644
--- a/tools/perf/util/symbol_conf.h
+++ b/tools/perf/util/symbol_conf.h
@@ -42,7 +42,6 @@ struct symbol_conf {
/* Was hybrid_merge given on the command line rather than in a config file? */
bool hybrid_merge_set;
bool nanosecs;
- unsigned short priv_size;
bool try_vmlinux_path,
init_annotation,
force,
--
2.56.0.313.g2479322e68-goog
^ permalink raw reply [flat|nested] 37+ messages in thread* [PATCH v2 07/18] perf symbol: Switch backing storage from rbtree to struct symbols array
2026-10-27 23:14 ` [PATCH v2 00/18] perf symbol: Reference counting, sorted arrays, and memory shrinking Ian Rogers
` (5 preceding siblings ...)
2026-10-27 23:15 ` [PATCH v2 06/18] perf annotate: Move struct annotation to a hashmap and remove symbol_conf.priv_size Ian Rogers
@ 2026-10-27 23:15 ` Ian Rogers
2026-10-27 23:15 ` [PATCH v2 08/18] perf symbol: Add reference counting and DECLARE_RC_STRUCT(symbol) Ian Rogers
` (11 subsequent siblings)
18 siblings, 0 replies; 37+ messages in thread
From: Ian Rogers @ 2026-10-27 23:15 UTC (permalink / raw)
To: irogers, acme, namhyung
Cc: adrian.hunter, haghdoost, james.clark, jolsa, linux-kernel,
linux-perf-users, mingo, peterz
Previously, DSO symbols were stored in an unprotected rb_root_cached
with a 24-byte struct rb_node embedded inside every struct symbol. That
design had three drawbacks:
1. Embedding struct rb_node (3 pointers, 24 bytes on 64-bit) accounted
for nearly 40% of struct symbol's fixed header size and scattered
symbol lookups across non-contiguous heap nodes.
2. Unlike struct dsos and struct maps, the symbol rbtree had no lock
protecting concurrent lookups, sorting, and insertions.
3. Reclaiming unreferenced symbols under memory pressure requires
compacting the symbol container in place under a lock.
Replace struct rb_node in struct symbol with a locked, lazily-sorted
pointer array in struct symbols, matching the design of struct dsos and
struct maps:
- Introduce struct symbols containing a rw_semaphore lock, primary
address-sorted symbols array, secondary name-sorted symbols_by_name
array, counts, capacity, and sorted state flags.
- Lazily sort symbols by [start, end) using qsort (symbols__sort_locked)
and use binary search in symbols__find().
- Update symbols__fixup_duplicate(), symbols__fixup_end(),
maps__split_kallsyms_for_kcore(), and maps__split_kallsyms() to
operate on the sorted array and compact entries in place.
- Convert symbols__for_each_entry(), dso__for_each_symbol(), and
map__for_each_symbol() to callback-based iteration under
symbols->lock, matching dsos__for_each_dso() and maps__for_each_map().
- Remove the unsynchronized single-entry dso->last_find_result cache and
dso__reset_find_symbol_cache() since binary search over a contiguous
array is cache-friendly.
- Remove the direct rb_erase_cached() workaround in builtin-annotate.c's
add_sample().
- Update the TUI map browser (ui/browsers/map.c) to snapshot the sorted
symbol array into an entries array and use ui_browser__argv_refresh
and ui_browser__argv_seek instead of ui_browser__rb_tree_refresh.
Using 'perf bench internals symbols':
Before (perf binary):
Average DSO load + sort: 7.041 msec (+- 0.051 msec)
Average DSO memory usage: 1401 KB (+- 4 KB)
Average bulk insert + lookup: 1453.950 usec (+- 10.895 usec)
After (perf binary, -13.5% load, -14.6% memory, -7.7% bulk):
Average DSO load + sort: 6.089 msec (+- 0.039 msec)
Average DSO memory usage: 1197 KB (+- 3 KB)
Average bulk insert + lookup: 1341.500 usec (+- 21.564 usec)
Before (libLLVM.so.21.1):
Average DSO load + sort: 78.599 msec (+- 0.857 msec)
Average DSO memory usage: 9530 KB (+- 5 KB)
After (libLLVM.so.21.1, -22.3% load, -8.1% memory):
Average DSO load + sort: 61.074 msec (+- 0.236 msec)
Average DSO memory usage: 8755 KB (+- 1 KB)
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/arch/powerpc/util/sym-handling.c | 34 +-
tools/perf/builtin-annotate.c | 11 -
tools/perf/builtin-kmem.c | 51 +-
tools/perf/tests/hists_common.c | 5 +-
tools/perf/tests/symbols.c | 62 ++-
tools/perf/tests/vmlinux-kallsyms.c | 185 +++----
tools/perf/ui/browser.c | 3 +
tools/perf/ui/browsers/hists.c | 3 +-
tools/perf/ui/browsers/map.c | 62 ++-
tools/perf/util/auxtrace.c | 109 ++--
tools/perf/util/db-export.c | 4 +-
tools/perf/util/dso.c | 50 +-
tools/perf/util/dso.h | 68 +--
tools/perf/util/intel-pt.c | 74 ++-
tools/perf/util/libbfd.c | 3 +-
tools/perf/util/machine.c | 30 +-
tools/perf/util/map.c | 25 +-
tools/perf/util/map.h | 8 +-
tools/perf/util/probe-event.c | 98 ++--
tools/perf/util/symbol-elf.c | 12 +-
tools/perf/util/symbol.c | 571 +++++++++++++-------
tools/perf/util/symbol.h | 72 ++-
tools/perf/util/symbol_fprintf.c | 25 +-
23 files changed, 941 insertions(+), 624 deletions(-)
diff --git a/tools/perf/arch/powerpc/util/sym-handling.c b/tools/perf/arch/powerpc/util/sym-handling.c
index d73bfdcdbec1..0ec327930b0a 100644
--- a/tools/perf/arch/powerpc/util/sym-handling.c
+++ b/tools/perf/arch/powerpc/util/sym-handling.c
@@ -115,13 +115,27 @@ void arch__fix_tev_from_maps(struct perf_probe_event *pev,
}
#ifdef HAVE_LIBELF_SUPPORT
+struct post_process_probe_trace_events_args {
+ struct perf_probe_event *pev;
+ struct probe_trace_event *tev;
+ struct map *map;
+};
+
+static int post_process_probe_trace_events_cb(struct symbol *sym, void *data)
+{
+ struct post_process_probe_trace_events_args *args = data;
+
+ if (map__unmap_ip(args->map, symbol__start(sym)) == args->tev->point.address) {
+ arch__fix_tev_from_maps(args->pev, args->tev, args->map, sym);
+ return 1;
+ }
+ return 0;
+}
+
void arch__post_process_probe_trace_events(struct perf_probe_event *pev,
int ntevs)
{
- struct probe_trace_event *tev;
struct map *map;
- struct symbol *sym = NULL;
- struct rb_node *tmp;
int i = 0;
map = get_target_map(pev->target, pev->nsi, pev->uprobes);
@@ -129,13 +143,13 @@ void arch__post_process_probe_trace_events(struct perf_probe_event *pev,
return;
for (i = 0; i < ntevs; i++) {
- tev = &pev->tevs[i];
- map__for_each_symbol(map, sym, tmp) {
- if (map__unmap_ip(map, symbol__start(sym)) == tev->point.address) {
- arch__fix_tev_from_maps(pev, tev, map, sym);
- break;
- }
- }
+ struct post_process_probe_trace_events_args args = {
+ .pev = pev,
+ .tev = &pev->tevs[i],
+ .map = map,
+ };
+
+ map__for_each_symbol(map, post_process_probe_trace_events_cb, &args);
}
}
#endif /* HAVE_LIBELF_SUPPORT */
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c
index c93b67986334..5ee02a25fb9c 100644
--- a/tools/perf/builtin-annotate.c
+++ b/tools/perf/builtin-annotate.c
@@ -254,17 +254,6 @@ static int add_sample(struct perf_sample *sample,
(al->sym == NULL ||
strcmp(ann->sym_hist_filter, symbol__name(al->sym)) != 0)) {
/* We're only interested in a symbol named sym_hist_filter */
- /*
- * FIXME: why isn't this done in the symbol_filter when loading
- * the DSO?
- */
- if (al->sym != NULL) {
- struct dso *dso = map__dso(al->map);
-
- rb_erase_cached(&al->sym->rb_node, dso__symbols(dso));
- symbol__delete(al->sym);
- dso__reset_find_symbol_cache(dso);
- }
return 0;
}
diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c
index 7af3fd9868a1..7070611e3619 100644
--- a/tools/perf/builtin-kmem.c
+++ b/tools/perf/builtin-kmem.c
@@ -341,13 +341,33 @@ static int callcmp(const void *a, const void *b)
return -1;
}
+static int build_alloc_func_list_cb(struct symbol *sym, void *data)
+{
+ regex_t *alloc_func_regex = data;
+ struct alloc_func *func;
+
+ if (regexec(alloc_func_regex, symbol__name(sym), 0, NULL, 0))
+ return 0;
+
+ func = realloc(alloc_func_list,
+ (nr_alloc_funcs + 1) * sizeof(*func));
+ if (func == NULL)
+ return -ENOMEM;
+
+ pr_debug("alloc func: %s\n", symbol__name(sym));
+ func[nr_alloc_funcs].start = symbol__start(sym);
+ func[nr_alloc_funcs].end = symbol__end(sym);
+ func[nr_alloc_funcs].name = symbol__name(sym);
+
+ alloc_func_list = func;
+ nr_alloc_funcs++;
+ return 0;
+}
+
static int build_alloc_func_list(void)
{
int ret;
struct map *kernel_map;
- struct symbol *sym;
- struct rb_node *node;
- struct alloc_func *func;
struct machine *machine = &kmem_session->machines.host;
regex_t alloc_func_regex;
static const char pattern[] = "^_?_?(alloc|get_free|get_zeroed)_pages?";
@@ -364,30 +384,17 @@ static int build_alloc_func_list(void)
kernel_map = machine__kernel_map(machine);
if (map__load(kernel_map) < 0) {
pr_err("cannot load kernel map\n");
+ regfree(&alloc_func_regex);
return -ENOENT;
}
- map__for_each_symbol(kernel_map, sym, node) {
- if (regexec(&alloc_func_regex, symbol__name(sym), 0, NULL, 0))
- continue;
-
- func = realloc(alloc_func_list,
- (nr_alloc_funcs + 1) * sizeof(*func));
- if (func == NULL)
- return -ENOMEM;
-
- pr_debug("alloc func: %s\n", symbol__name(sym));
- func[nr_alloc_funcs].start = symbol__start(sym);
- func[nr_alloc_funcs].end = symbol__end(sym);
- func[nr_alloc_funcs].name = symbol__name(sym);
-
- alloc_func_list = func;
- nr_alloc_funcs++;
- }
+ ret = map__for_each_symbol(kernel_map, build_alloc_func_list_cb, &alloc_func_regex);
+ regfree(&alloc_func_regex);
+ if (ret)
+ return ret;
- qsort(alloc_func_list, nr_alloc_funcs, sizeof(*func), funcmp);
+ qsort(alloc_func_list, nr_alloc_funcs, sizeof(*alloc_func_list), funcmp);
- regfree(&alloc_func_regex);
return 0;
}
diff --git a/tools/perf/tests/hists_common.c b/tools/perf/tests/hists_common.c
index 0db3ea9cf62e..beb147e2a654 100644
--- a/tools/perf/tests/hists_common.c
+++ b/tools/perf/tests/hists_common.c
@@ -146,7 +146,10 @@ struct machine *setup_fake_machine(struct machines *machines)
goto out;
}
- symbols__insert(dso__symbols(dso), sym);
+ if (symbols__insert(dso__symbols(dso), sym)) {
+ dso__put(dso);
+ goto out;
+ }
}
dso__put(dso);
diff --git a/tools/perf/tests/symbols.c b/tools/perf/tests/symbols.c
index 456ef7a70e28..8ccf977aa9f7 100644
--- a/tools/perf/tests/symbols.c
+++ b/tools/perf/tests/symbols.c
@@ -113,39 +113,50 @@ static int create_map(struct test_info *ti, char *filename, struct map **map_p)
return TEST_OK;
}
+struct test_dso_args {
+ struct symbol *last_sym;
+ int ret;
+};
+
+static int test_dso_cb(struct symbol *sym, void *data)
+{
+ struct test_dso_args *args = data;
+ struct symbol *last_sym = args->last_sym;
+
+ if (symbol__type(sym) != STT_FUNC && symbol__type(sym) != STT_GNU_IFUNC)
+ return 0;
+
+ /* Check for overlapping function symbols */
+ if (last_sym && symbol__start(sym) < symbol__end(last_sym)) {
+ pr_debug("Overlapping symbols:\n");
+ symbol__fprintf(last_sym, stderr);
+ symbol__fprintf(sym, stderr);
+ args->ret = TEST_FAIL;
+ }
+ /* Check for zero-length function symbol */
+ if (symbol__start(sym) == symbol__end(sym)) {
+ pr_debug("Zero-length symbol:\n");
+ symbol__fprintf(sym, stderr);
+ args->ret = TEST_FAIL;
+ }
+ args->last_sym = sym;
+ return 0;
+}
+
static int test_dso(struct dso *dso)
{
- struct symbol *last_sym = NULL;
- struct rb_node *nd;
- int ret = TEST_OK;
+ struct test_dso_args args = {
+ .last_sym = NULL,
+ .ret = TEST_OK,
+ };
/* dso__fprintf() prints all the symbols */
if (verbose > 1)
dso__fprintf(dso, stderr);
- for (nd = rb_first_cached(dso__symbols(dso)); nd; nd = rb_next(nd)) {
- struct symbol *sym = rb_entry(nd, struct symbol, rb_node);
-
- if (symbol__type(sym) != STT_FUNC && symbol__type(sym) != STT_GNU_IFUNC)
- continue;
+ dso__for_each_symbol(dso, test_dso_cb, &args);
- /* Check for overlapping function symbols */
- if (last_sym && symbol__start(sym) < symbol__end(last_sym)) {
- pr_debug("Overlapping symbols:\n");
- symbol__fprintf(last_sym, stderr);
- symbol__fprintf(sym, stderr);
- ret = TEST_FAIL;
- }
- /* Check for zero-length function symbol */
- if (symbol__start(sym) == symbol__end(sym)) {
- pr_debug("Zero-length symbol:\n");
- symbol__fprintf(sym, stderr);
- ret = TEST_FAIL;
- }
- last_sym = sym;
- }
-
- return ret;
+ return args.ret;
}
static int subdivided_dso_cb(struct dso *dso, struct machine *machine __maybe_unused, void *d)
@@ -318,3 +329,4 @@ struct test_suite suite__symbols = {
.desc = "Symbols",
.test_cases = tests__symbols,
};
+
diff --git a/tools/perf/tests/vmlinux-kallsyms.c b/tools/perf/tests/vmlinux-kallsyms.c
index 9b6f2d836d8f..bef40c194d70 100644
--- a/tools/perf/tests/vmlinux-kallsyms.c
+++ b/tools/perf/tests/vmlinux-kallsyms.c
@@ -113,6 +113,7 @@ static bool is_ignored_symbol(const char *name, char type)
struct test__vmlinux_matches_kallsyms_cb_args {
struct machine kallsyms;
struct map *vmlinux_map;
+ int err;
bool header_printed;
};
@@ -185,16 +186,101 @@ static int test__vmlinux_matches_kallsyms_cb3(struct map *map, void *data)
return 0;
}
+static int test__vmlinux_matches_kallsyms_sym_cb(struct symbol *sym, void *data)
+{
+ struct test__vmlinux_matches_kallsyms_cb_args *args = data;
+ /*
+ * Step 4:
+ *
+ * kallsyms will be internally on demand sorted by name so that we can
+ * find the reference relocation * symbol, i.e. the symbol we will use
+ * to see if the running kernel was relocated by checking if it has the
+ * same value in the vmlinux file we load.
+ */
+ struct map *kallsyms_map = machine__kernel_map(&args->kallsyms);
+ struct symbol *pair, *first_pair;
+ u64 mem_start, mem_end;
+
+ if (symbol__start(sym) == symbol__end(sym))
+ return 0;
+
+ mem_start = map__unmap_ip(args->vmlinux_map, symbol__start(sym));
+ mem_end = map__unmap_ip(args->vmlinux_map, symbol__end(sym));
+
+ first_pair = machine__find_kernel_symbol(&args->kallsyms, mem_start, NULL);
+ pair = first_pair;
+
+ if (pair && UM(symbol__start(pair)) == mem_start) {
+next_pair:
+ if (arch__compare_symbol_names(symbol__name(sym), symbol__name(pair)) == 0) {
+ /*
+ * kallsyms don't have the symbol end, so we
+ * set that by using the next symbol start - 1,
+ * in some cases we get this up to a page
+ * wrong, trace_kmalloc when I was developing
+ * this code was one such example, 2106 bytes
+ * off the real size. More than that and we
+ * _really_ have a problem.
+ */
+ s64 skew = mem_end - UM(symbol__end(pair));
+
+ if (llabs(skew) >= page_size) {
+ pr_debug("WARN: %#" PRIx64 ": diff end addr for %s v: %#"
+ PRIx64 " k: %#" PRIx64 "\n",
+ mem_start, symbol__name(sym), mem_end,
+ UM(symbol__end(pair)));
+ }
+
+ /*
+ * Do not count this as a failure, because we
+ * could really find a case where it's not
+ * possible to get proper function end from
+ * kallsyms.
+ */
+ return 0;
+ }
+ pair = machine__find_kernel_symbol_by_name(&args->kallsyms,
+ symbol__name(sym), NULL);
+ if (pair) {
+ if (UM(symbol__start(pair)) == mem_start)
+ goto next_pair;
+
+ pr_debug("WARN: %#" PRIx64 ": diff name v: %s k: %s\n",
+ mem_start, symbol__name(sym), symbol__name(pair));
+ } else {
+ pr_debug("WARN: %#" PRIx64 ": diff name v: %s k: %s\n",
+ mem_start, symbol__name(sym), symbol__name(first_pair));
+ }
+
+ return 0;
+ }
+
+ if (mem_start == map__end(args->kallsyms.vmlinux_map)) {
+ /*
+ * Ignore aliases to _etext, i.e. to the end of the kernel text area,
+ * such as __indirect_thunk_end.
+ */
+ return 0;
+ }
+ if (is_ignored_symbol(symbol__name(sym), symbol__type(sym))) {
+ /*
+ * Ignore hidden symbols, see scripts/kallsyms.c for the details
+ */
+ return 0;
+ }
+ pr_debug("ERR : %#" PRIx64 ": %s not on kallsyms\n",
+ mem_start, symbol__name(sym));
+
+ args->err = -1;
+ return 0;
+}
+
static int test__vmlinux_matches_kallsyms(struct test_suite *test __maybe_unused,
int subtest __maybe_unused)
{
int err = TEST_FAIL;
- struct rb_node *nd;
- struct symbol *sym;
- struct map *kallsyms_map;
struct machine vmlinux = { 0 };
struct maps *maps;
- u64 mem_start, mem_end;
struct test__vmlinux_matches_kallsyms_cb_args args;
/*
@@ -240,16 +326,6 @@ static int test__vmlinux_matches_kallsyms(struct test_suite *test __maybe_unused
goto out;
}
- /*
- * Step 4:
- *
- * kallsyms will be internally on demand sorted by name so that we can
- * find the reference relocation * symbol, i.e. the symbol we will use
- * to see if the running kernel was relocated by checking if it has the
- * same value in the vmlinux file we load.
- */
- kallsyms_map = machine__kernel_map(&args.kallsyms);
-
/*
* Step 5:
*
@@ -279,7 +355,7 @@ static int test__vmlinux_matches_kallsyms(struct test_suite *test __maybe_unused
goto out;
}
- err = 0;
+ args.err = 0;
/*
* Step 7:
*
@@ -287,83 +363,8 @@ static int test__vmlinux_matches_kallsyms(struct test_suite *test __maybe_unused
* in the kallsyms dso. For the ones that are in both, check its names and
* end addresses too.
*/
- map__for_each_symbol(args.vmlinux_map, sym, nd) {
- struct symbol *pair, *first_pair;
-
- sym = rb_entry(nd, struct symbol, rb_node);
-
- if (symbol__start(sym) == symbol__end(sym))
- continue;
-
- mem_start = map__unmap_ip(args.vmlinux_map, symbol__start(sym));
- mem_end = map__unmap_ip(args.vmlinux_map, symbol__end(sym));
-
- first_pair = machine__find_kernel_symbol(&args.kallsyms, mem_start, NULL);
- pair = first_pair;
-
- if (pair && UM(symbol__start(pair)) == mem_start) {
-next_pair:
- if (arch__compare_symbol_names(symbol__name(sym),
- symbol__name(pair)) == 0) {
- /*
- * kallsyms don't have the symbol end, so we
- * set that by using the next symbol start - 1,
- * in some cases we get this up to a page
- * wrong, trace_kmalloc when I was developing
- * this code was one such example, 2106 bytes
- * off the real size. More than that and we
- * _really_ have a problem.
- */
- s64 skew = mem_end - UM(symbol__end(pair));
-
- if (llabs(skew) >= page_size) {
- pr_debug("WARN: %#" PRIx64 ": diff end addr for %s v: %#" PRIx64 " k: %#" PRIx64 "\n",
- mem_start, symbol__name(sym), mem_end,
- UM(symbol__end(pair)));
- }
-
- /*
- * Do not count this as a failure, because we
- * could really find a case where it's not
- * possible to get proper function end from
- * kallsyms.
- */
- continue;
- } else {
- pair = machine__find_kernel_symbol_by_name(&args.kallsyms,
- symbol__name(sym), NULL);
- if (pair) {
- if (UM(symbol__start(pair)) == mem_start)
- goto next_pair;
-
- pr_debug("WARN: %#" PRIx64 ": diff name v: %s k: %s\n",
- mem_start, symbol__name(sym), symbol__name(pair));
- } else {
- pr_debug("WARN: %#" PRIx64 ": diff name v: %s k: %s\n",
- mem_start, symbol__name(sym),
- symbol__name(first_pair));
- }
-
- continue;
- }
- } else if (mem_start == map__end(args.kallsyms.vmlinux_map)) {
- /*
- * Ignore aliases to _etext, i.e. to the end of the kernel text area,
- * such as __indirect_thunk_end.
- */
- continue;
- } else if (is_ignored_symbol(symbol__name(sym), symbol__type(sym))) {
- /*
- * Ignore hidden symbols, see scripts/kallsyms.c for the details
- */
- continue;
- } else {
- pr_debug("ERR : %#" PRIx64 ": %s not on kallsyms\n",
- mem_start, symbol__name(sym));
- }
-
- err = -1;
- }
+ map__for_each_symbol(args.vmlinux_map, test__vmlinux_matches_kallsyms_sym_cb, &args);
+ err = args.err;
if (verbose <= 0)
goto out;
diff --git a/tools/perf/ui/browser.c b/tools/perf/ui/browser.c
index 321187b204d3..c7451f640d1d 100644
--- a/tools/perf/ui/browser.c
+++ b/tools/perf/ui/browser.c
@@ -622,6 +622,9 @@ static int ui_browser__color_config(const char *var, const char *value,
void ui_browser__argv_seek(struct ui_browser *browser, off_t offset, int whence)
{
+ if (browser->nr_entries == 0)
+ return;
+
switch (whence) {
case SEEK_SET:
browser->top = browser->entries;
diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c
index 1f1a877f667e..5147b7dca256 100644
--- a/tools/perf/ui/browsers/hists.c
+++ b/tools/perf/ui/browsers/hists.c
@@ -2531,7 +2531,8 @@ static struct symbol *symbol__new_unresolved(u64 addr, struct map *map)
return NULL;
}
- dso__insert_symbol(map__dso(map), sym);
+ if (dso__insert_symbol(map__dso(map), sym))
+ return NULL;
}
return sym;
diff --git a/tools/perf/ui/browsers/map.c b/tools/perf/ui/browsers/map.c
index 2f070a53b5cb..71f6161ed27a 100644
--- a/tools/perf/ui/browsers/map.c
+++ b/tools/perf/ui/browsers/map.c
@@ -24,7 +24,7 @@ struct map_browser {
static void map_browser__write(struct ui_browser *browser, void *nd, int row)
{
- struct symbol *sym = rb_entry(nd, struct symbol, rb_node);
+ struct symbol *sym = *(struct symbol **)nd;
struct map_browser *mb = container_of(browser, struct map_browser, b);
bool current_entry = ui_browser__is_current_entry(browser, row);
int width;
@@ -56,17 +56,16 @@ static int map_browser__search(struct map_browser *browser)
sym = map__find_symbol_by_name(browser->map, target);
if (sym != NULL) {
- struct rb_node *nd;
- u32 idx = 0;
+ struct symbol **entries = browser->b.entries;
/*
- * Walk the map browser's symbol entries to find the matching
- * symbol node and its display row index, then position the
- * browser cursor at that entry.
+ * Scan the map browser's symbol entries to find the matching
+ * symbol and its display row index, then position the browser
+ * cursor at that entry.
*/
- for (nd = rb_first(browser->b.entries); nd; nd = rb_next(nd), ++idx) {
- if (&sym->rb_node == nd) {
- browser->b.top = nd;
+ for (u32 idx = 0; idx < browser->b.nr_entries; ++idx) {
+ if (entries[idx] == sym) {
+ browser->b.top = &entries[idx];
browser->b.index = browser->b.top_idx = idx;
break;
}
@@ -109,27 +108,48 @@ static int map_browser__run(struct map_browser *browser)
int map__browse(struct map *map)
{
+ struct symbols *symbols = dso__symbols(map__dso(map));
struct map_browser mb = {
.b = {
- .entries = dso__symbols(map__dso(map)),
- .refresh = ui_browser__rb_tree_refresh,
- .seek = ui_browser__rb_tree_seek,
+ .refresh = ui_browser__argv_refresh,
+ .seek = ui_browser__argv_seek,
.write = map_browser__write,
},
.map = map,
};
- struct rb_node *nd;
+ struct symbol **entries = NULL;
+ unsigned int nr_entries = 0;
char tmp[BITS_PER_LONG / 4];
u64 maxaddr = 0;
-
- for (nd = rb_first(mb.b.entries); nd; nd = rb_next(nd)) {
- struct symbol *pos = rb_entry(nd, struct symbol, rb_node);
-
- if (maxaddr < symbol__end(pos))
- maxaddr = symbol__end(pos);
- ++mb.b.nr_entries;
+ int ret;
+
+ /*
+ * Snapshot the DSO's sorted symbol array under symbols->lock for use
+ * with ui_browser__argv_refresh and ui_browser__argv_seek.
+ */
+ symbols__sort_read_lock(symbols);
+ nr_entries = symbols->cnt;
+ if (nr_entries > 0) {
+ entries = malloc(nr_entries * sizeof(*entries));
+ if (entries) {
+ for (unsigned int idx = 0; idx < nr_entries; idx++) {
+ struct symbol *pos = symbols->symbols[idx];
+
+ entries[idx] = pos;
+ if (maxaddr < symbol__end(pos))
+ maxaddr = symbol__end(pos);
+ }
+ } else {
+ nr_entries = 0;
+ }
}
+ up_read(&symbols->lock);
+
+ mb.b.entries = entries;
+ mb.b.nr_entries = nr_entries;
mb.addrlen = snprintf(tmp, sizeof(tmp), "%" PRIx64, maxaddr);
- return map_browser__run(&mb);
+ ret = map_browser__run(&mb);
+ free(entries);
+ return ret;
}
diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c
index b3cc7b17aca4..a51c871d730b 100644
--- a/tools/perf/util/auxtrace.c
+++ b/tools/perf/util/auxtrace.c
@@ -2713,61 +2713,96 @@ static bool dso_sym_match(struct symbol *sym, const char *name, int *cnt,
idx < 0);
}
+struct print_duplicate_syms_args {
+ const char *sym_name;
+ bool near;
+ int cnt;
+};
+
+static int print_duplicate_syms_cb(struct symbol *sym, void *data)
+{
+ struct print_duplicate_syms_args *args = data;
+
+ if (dso_sym_match(sym, args->sym_name, &args->cnt, -1)) {
+ pr_err("#%d\t0x%"PRIx64"\t%c\t%s\n",
+ ++args->cnt, symbol__start(sym),
+ symbol__binding(sym) == STB_GLOBAL ? 'g' :
+ symbol__binding(sym) == STB_LOCAL ? 'l' : 'w',
+ symbol__name(sym));
+ args->near = true;
+ } else if (args->near) {
+ args->near = false;
+ pr_err("\t\twhich is near\t\t%s\n", symbol__name(sym));
+ }
+ return 0;
+}
+
static void print_duplicate_syms(struct dso *dso, const char *sym_name)
{
- struct symbol *sym;
- bool near = false;
- int cnt = 0;
+ struct print_duplicate_syms_args args = {
+ .sym_name = sym_name,
+ .near = false,
+ .cnt = 0,
+ };
pr_err("Multiple symbols with name '%s'\n", sym_name);
- sym = dso__first_symbol(dso);
- while (sym) {
- if (dso_sym_match(sym, sym_name, &cnt, -1)) {
- pr_err("#%d\t0x%"PRIx64"\t%c\t%s\n",
- ++cnt, symbol__start(sym),
- symbol__binding(sym) == STB_GLOBAL ? 'g' :
- symbol__binding(sym) == STB_LOCAL ? 'l' : 'w',
- symbol__name(sym));
- near = true;
- } else if (near) {
- near = false;
- pr_err("\t\twhich is near\t\t%s\n", symbol__name(sym));
- }
- sym = dso__next_symbol(sym);
- }
+ dso__for_each_symbol(dso, print_duplicate_syms_cb, &args);
pr_err("Disambiguate symbol name by inserting #n after the name e.g. %s #2\n",
sym_name);
pr_err("Or select a global symbol by inserting #0 or #g or #G\n");
}
+struct find_dso_sym_args {
+ const char *sym_name;
+ u64 *start;
+ u64 *size;
+ int idx;
+ int cnt;
+ bool duplicate;
+};
+
+static int find_dso_sym_cb(struct symbol *sym, void *data)
+{
+ struct find_dso_sym_args *args = data;
+
+ if (*args->start) {
+ if (!*args->size)
+ *args->size = symbol__start(sym) - *args->start;
+ if (args->idx > 0) {
+ if (*args->size)
+ return 1;
+ } else if (dso_sym_match(sym, args->sym_name, &args->cnt, args->idx)) {
+ args->duplicate = true;
+ return 1;
+ }
+ } else if (dso_sym_match(sym, args->sym_name, &args->cnt, args->idx)) {
+ *args->start = symbol__start(sym);
+ *args->size = symbol__end(sym) - symbol__start(sym);
+ }
+ return 0;
+}
+
static int find_dso_sym(struct dso *dso, const char *sym_name, u64 *start,
u64 *size, int idx)
{
- struct symbol *sym;
- int cnt = 0;
+ struct find_dso_sym_args args = {
+ .sym_name = sym_name,
+ .start = start,
+ .size = size,
+ .idx = idx,
+ .cnt = 0,
+ .duplicate = false,
+ };
*start = 0;
*size = 0;
- sym = dso__first_symbol(dso);
- while (sym) {
- if (*start) {
- if (!*size)
- *size = symbol__start(sym) - *start;
- if (idx > 0) {
- if (*size)
- return 0;
- } else if (dso_sym_match(sym, sym_name, &cnt, idx)) {
- print_duplicate_syms(dso, sym_name);
- return -EINVAL;
- }
- } else if (dso_sym_match(sym, sym_name, &cnt, idx)) {
- *start = symbol__start(sym);
- *size = symbol__end(sym) - symbol__start(sym);
- }
- sym = dso__next_symbol(sym);
+ dso__for_each_symbol(dso, find_dso_sym_cb, &args);
+ if (args.duplicate) {
+ print_duplicate_syms(dso, sym_name);
+ return -EINVAL;
}
if (!*start)
diff --git a/tools/perf/util/db-export.c b/tools/perf/util/db-export.c
index 6ebc060bdb8f..97ce6061a77d 100644
--- a/tools/perf/util/db-export.c
+++ b/tools/perf/util/db-export.c
@@ -231,8 +231,8 @@ static int db_ids_from_al(struct db_export *dbe, struct addr_location *al,
if (!al->sym) {
al->sym = symbol__new(al->addr, 0, 0, 0, "unknown");
- if (al->sym)
- dso__insert_symbol(dso, al->sym);
+ if (al->sym && dso__insert_symbol(dso, al->sym))
+ al->sym = NULL;
}
if (al->sym) {
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c
index 39a1eb9cb73f..35a5c65030af 100644
--- a/tools/perf/util/dso.c
+++ b/tools/perf/util/dso.c
@@ -1808,12 +1808,7 @@ bool dso__loaded(const struct dso *dso)
bool dso__sorted_by_name(const struct dso *dso)
{
- return RC_CHK_ACCESS(dso)->sorted_by_name;
-}
-
-void dso__set_sorted_by_name(struct dso *dso)
-{
- RC_CHK_ACCESS(dso)->sorted_by_name = true;
+ return RC_CHK_ACCESS(dso)->symbols.sorted_by_name;
}
struct dso *dso__new_id(const char *name, const struct dso_id *id)
@@ -1831,9 +1826,7 @@ struct dso *dso__new_id(const char *name, const struct dso_id *id)
dso->id = *id;
dso__set_long_name_id(res, dso->name, false);
dso__set_short_name(res, dso->name, false);
- dso->symbols = RB_ROOT_CACHED;
- dso->symbol_names = NULL;
- dso->symbol_names_len = 0;
+ symbols__init(&dso->symbols);
dso->inlined_nodes = RB_ROOT_CACHED;
dso->srclines = RB_ROOT_CACHED;
dso->data_types = RB_ROOT;
@@ -1844,7 +1837,6 @@ struct dso *dso__new_id(const char *name, const struct dso_id *id)
dso->is_64_bit = (sizeof(void *) == 8);
dso->loaded = 0;
dso->rel = 0;
- dso->sorted_by_name = 0;
dso->has_srcline = 1;
dso->a2l_fails = 1;
dso->kernel = DSO_SPACE__USER;
@@ -1878,9 +1870,7 @@ void dso__delete(struct dso *dso)
/* free inlines first, as they reference symbols */
inlines__tree_delete(&RC_CHK_ACCESS(dso)->inlined_nodes);
srcline__tree_delete(&RC_CHK_ACCESS(dso)->srclines);
- symbols__delete(&RC_CHK_ACCESS(dso)->symbols);
- RC_CHK_ACCESS(dso)->symbol_names_len = 0;
- zfree(&RC_CHK_ACCESS(dso)->symbol_names);
+ symbols__exit(&RC_CHK_ACCESS(dso)->symbols);
annotated_data_type__tree_delete(dso__data_types(dso));
global_var_type__tree_delete(dso__global_vars(dso));
@@ -2003,22 +1993,34 @@ static size_t dso__fprintf_buildid(struct dso *dso, FILE *fp)
return fprintf(fp, "%s", sbuild_id);
}
+struct dso__fprintf_cb_args {
+ FILE *fp;
+ size_t ret;
+};
+
+static int dso__fprintf_cb(struct symbol *pos, void *data)
+{
+ struct dso__fprintf_cb_args *args = data;
+
+ args->ret += symbol__fprintf(pos, args->fp);
+ return 0;
+}
+
size_t dso__fprintf(struct dso *dso, FILE *fp)
{
- struct rb_node *nd;
- size_t ret = fprintf(fp, "dso: %s (", dso__short_name(dso));
+ struct dso__fprintf_cb_args args = {
+ .fp = fp,
+ .ret = fprintf(fp, "dso: %s (", dso__short_name(dso)),
+ };
if (dso__short_name(dso) != dso__long_name(dso))
- ret += fprintf(fp, "%s, ", dso__long_name(dso));
- ret += fprintf(fp, "%sloaded, ", dso__loaded(dso) ? "" : "NOT ");
- ret += dso__fprintf_buildid(dso, fp);
- ret += fprintf(fp, ")\n");
- for (nd = rb_first_cached(dso__symbols(dso)); nd; nd = rb_next(nd)) {
- struct symbol *pos = rb_entry(nd, struct symbol, rb_node);
- ret += symbol__fprintf(pos, fp);
- }
+ args.ret += fprintf(fp, "%s, ", dso__long_name(dso));
+ args.ret += fprintf(fp, "%sloaded, ", dso__loaded(dso) ? "" : "NOT ");
+ args.ret += dso__fprintf_buildid(dso, fp);
+ args.ret += fprintf(fp, ")\n");
+ dso__for_each_symbol(dso, dso__fprintf_cb, &args);
- return ret;
+ return args.ret;
}
enum dso_type dso__type(struct dso *dso, struct machine *machine)
diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h
index d5f7382d4e13..f23cf9957aec 100644
--- a/tools/perf/util/dso.h
+++ b/tools/perf/util/dso.h
@@ -13,6 +13,7 @@
#include "build-id.h"
#include "debuginfo.h"
#include "mutex.h"
+#include "symbol.h"
#include <internal/rc_check.h>
struct machine;
@@ -287,18 +288,12 @@ struct auxtrace_cache;
DECLARE_RC_STRUCT(dso) {
struct mutex lock;
struct dsos *dsos;
- struct rb_root_cached symbols;
- struct symbol **symbol_names;
- size_t symbol_names_len;
+ struct symbols symbols;
struct rb_root_cached inlined_nodes;
struct rb_root_cached srclines;
struct rb_root data_types;
struct rb_root global_vars;
- struct {
- u64 addr;
- struct symbol *symbol;
- } last_find_result;
u64 text_offset;
u64 text_end;
const char *short_name;
@@ -351,7 +346,6 @@ DECLARE_RC_STRUCT(dso) {
u8 is_64_bit:1;
u8 bintype_searched:1;
u8 debuginfo_searched:1;
- bool sorted_by_name;
bool loaded;
u8 rel;
char name[];
@@ -365,11 +359,14 @@ int dso_id__cmp(const struct dso_id *a, const struct dso_id *b);
/* dso__for_each_symbol - iterate over the symbols of given type
*
* @dso: the 'struct dso *' in which symbols are iterated
- * @pos: the 'struct symbol *' to use as a loop cursor
- * @n: the 'struct rb_node *' to use as a temporary storage
+ * @cb: callback function invoked for each symbol
+ * @data: opaque data pointer passed to @cb
*/
-#define dso__for_each_symbol(dso, pos, n) \
- symbols__for_each_entry(dso__symbols(dso), pos, n)
+#define dso__for_each_symbol(dso, cb, data) \
+ symbols__for_each_entry(dso__symbols(dso), cb, data)
+
+#define dso__for_each_symbol_by_name(dso, cb, data) \
+ symbols__visit_by_name(dso__symbols(dso), cb, data)
static inline void *dso__a2l(const struct dso *dso)
{
@@ -598,26 +595,6 @@ static inline void dso__set_kernel(struct dso *dso, enum dso_space_type kernel)
RC_CHK_ACCESS(dso)->kernel = kernel;
}
-static inline u64 dso__last_find_result_addr(const struct dso *dso)
-{
- return RC_CHK_ACCESS(dso)->last_find_result.addr;
-}
-
-static inline void dso__set_last_find_result_addr(struct dso *dso, u64 addr)
-{
- RC_CHK_ACCESS(dso)->last_find_result.addr = addr;
-}
-
-static inline struct symbol *dso__last_find_result_symbol(const struct dso *dso)
-{
- return RC_CHK_ACCESS(dso)->last_find_result.symbol;
-}
-
-static inline void dso__set_last_find_result_symbol(struct dso *dso, struct symbol *symbol)
-{
- RC_CHK_ACCESS(dso)->last_find_result.symbol = symbol;
-}
-
static inline enum dso_load_errno *dso__load_errno(struct dso *dso)
{
return &RC_CHK_ACCESS(dso)->load_errno;
@@ -730,31 +707,11 @@ static inline struct rb_root *dso__global_vars(struct dso *dso)
return &RC_CHK_ACCESS(dso)->global_vars;
}
-static inline struct rb_root_cached *dso__symbols(struct dso *dso)
+static inline struct symbols *dso__symbols(struct dso *dso)
{
return &RC_CHK_ACCESS(dso)->symbols;
}
-static inline struct symbol **dso__symbol_names(struct dso *dso)
-{
- return RC_CHK_ACCESS(dso)->symbol_names;
-}
-
-static inline void dso__set_symbol_names(struct dso *dso, struct symbol **names)
-{
- RC_CHK_ACCESS(dso)->symbol_names = names;
-}
-
-static inline size_t dso__symbol_names_len(struct dso *dso)
-{
- return RC_CHK_ACCESS(dso)->symbol_names_len;
-}
-
-static inline void dso__set_symbol_names_len(struct dso *dso, size_t len)
-{
- RC_CHK_ACCESS(dso)->symbol_names_len = len;
-}
-
static inline const char *dso__symsrc_filename(const struct dso *dso)
{
return RC_CHK_ACCESS(dso)->symsrc_filename;
@@ -823,13 +780,12 @@ bool dso__loaded(const struct dso *dso);
static inline bool dso__has_symbols(const struct dso *dso)
{
- return !RB_EMPTY_ROOT(&RC_CHK_ACCESS(dso)->symbols.rb_root);
+ return RC_CHK_ACCESS(dso)->symbols.cnt > 0;
}
char *dso__filename_with_chroot(const struct dso *dso, const char *filename);
bool dso__sorted_by_name(const struct dso *dso);
-void dso__set_sorted_by_name(struct dso *dso);
void dso__sort_by_name(struct dso *dso);
int dso__swap_init(struct dso *dso, unsigned char eidata);
@@ -953,8 +909,6 @@ struct map *dso__new_map(const char *name);
struct dso *machine__findnew_kernel(struct machine *machine, const char *name,
const char *short_name, int dso_type);
-void dso__reset_find_symbol_cache(struct dso *dso);
-
size_t dso__fprintf_symbols_by_name(struct dso *dso, FILE *fp);
size_t dso__fprintf(struct dso *dso, FILE *fp);
diff --git a/tools/perf/util/intel-pt.c b/tools/perf/util/intel-pt.c
index a9f43ac8d152..2e64b3faabc9 100644
--- a/tools/perf/util/intel-pt.c
+++ b/tools/perf/util/intel-pt.c
@@ -2981,13 +2981,37 @@ static int intel_pt_sample(struct intel_pt_queue *ptq)
return 0;
}
+struct intel_pt_switch_ip_args {
+ struct map *map;
+ const char *name;
+ bool check_global;
+ u64 ip;
+};
+
+static int intel_pt_switch_ip_cb(struct symbol *sym, void *data)
+{
+ struct intel_pt_switch_ip_args *args = data;
+ u64 ip;
+
+ if (args->check_global && symbol__binding(sym) != STB_GLOBAL)
+ return 0;
+
+ if (!strcmp(symbol__name(sym), args->name)) {
+ ip = map__unmap_ip(args->map, symbol__start(sym));
+ if (ip >= map__start(args->map) && ip < map__end(args->map)) {
+ args->ip = ip;
+ return 1;
+ }
+ }
+ return 0;
+}
+
static u64 intel_pt_switch_ip(struct intel_pt *pt, u64 *ptss_ip)
{
struct machine *machine = pt->machine;
struct map *map;
- struct symbol *sym, *start;
- u64 ip, switch_ip = 0;
- const char *ptss;
+ struct intel_pt_switch_ip_args args;
+ u64 switch_ip = 0;
if (ptss_ip)
*ptss_ip = 0;
@@ -2999,36 +3023,28 @@ static u64 intel_pt_switch_ip(struct intel_pt *pt, u64 *ptss_ip)
if (map__load(map))
return 0;
- start = dso__first_symbol(map__dso(map));
-
- for (sym = start; sym; sym = dso__next_symbol(sym)) {
- if (symbol__binding(sym) == STB_GLOBAL &&
- !strcmp(symbol__name(sym), "__switch_to")) {
- ip = map__unmap_ip(map, symbol__start(sym));
- if (ip >= map__start(map) && ip < map__end(map)) {
- switch_ip = ip;
- break;
- }
- }
- }
+ args = (struct intel_pt_switch_ip_args) {
+ .map = map,
+ .name = "__switch_to",
+ .check_global = true,
+ .ip = 0,
+ };
+ map__for_each_symbol(map, intel_pt_switch_ip_cb, &args);
+ switch_ip = args.ip;
if (!switch_ip || !ptss_ip)
return 0;
- if (pt->have_sched_switch == 1)
- ptss = "perf_trace_sched_switch";
- else
- ptss = "__perf_event_task_sched_out";
-
- for (sym = start; sym; sym = dso__next_symbol(sym)) {
- if (!strcmp(symbol__name(sym), ptss)) {
- ip = map__unmap_ip(map, symbol__start(sym));
- if (ip >= map__start(map) && ip < map__end(map)) {
- *ptss_ip = ip;
- break;
- }
- }
- }
+ args = (struct intel_pt_switch_ip_args) {
+ .map = map,
+ .name = pt->have_sched_switch == 1 ?
+ "perf_trace_sched_switch" :
+ "__perf_event_task_sched_out",
+ .check_global = false,
+ .ip = 0,
+ };
+ map__for_each_symbol(map, intel_pt_switch_ip_cb, &args);
+ *ptss_ip = args.ip;
return switch_ip;
}
diff --git a/tools/perf/util/libbfd.c b/tools/perf/util/libbfd.c
index 1a5d38b8e5f4..5e947271e388 100644
--- a/tools/perf/util/libbfd.c
+++ b/tools/perf/util/libbfd.c
@@ -453,7 +453,8 @@ int dso__load_bfd_symbols(struct dso *dso, const char *debugfile)
if (!symbol)
goto out_free;
- symbols__insert(dso__symbols(dso), symbol);
+ if (symbols__insert(dso__symbols(dso), symbol))
+ goto out_free;
}
#ifdef bfd_get_section
#undef bfd_asymbol_section
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index 6a10c3036383..14e93e7fb235 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -796,7 +796,7 @@ static int machine__process_ksymbol_register(struct machine *machine,
err = -ENOMEM;
goto out;
}
- dso__insert_symbol(dso, sym);
+ err = dso__insert_symbol(dso, sym);
out:
map__put(map);
dso__put(dso);
@@ -1095,29 +1095,35 @@ int machine__create_extra_kernel_map(struct machine *machine,
return err;
}
-static u64 find_entry_trampoline(struct dso *dso)
+static int find_entry_trampoline_cb(struct symbol *sym, void *data)
{
/* Duplicates are removed so lookup all aliases */
- const char *syms[] = {
+ static const char * const syms[] = {
"_entry_trampoline",
"__entry_trampoline_start",
"entry_SYSCALL_64_trampoline",
};
- struct symbol *sym = dso__first_symbol(dso);
- unsigned int i;
+ u64 *addr = data;
- for (; sym; sym = dso__next_symbol(sym)) {
- if (symbol__binding(sym) != STB_GLOBAL)
- continue;
- for (i = 0; i < ARRAY_SIZE(syms); i++) {
- if (!strcmp(symbol__name(sym), syms[i]))
- return symbol__start(sym);
+ if (symbol__binding(sym) != STB_GLOBAL)
+ return 0;
+ for (unsigned int i = 0; i < ARRAY_SIZE(syms); i++) {
+ if (!strcmp(symbol__name(sym), syms[i])) {
+ *addr = symbol__start(sym);
+ return 1;
}
}
-
return 0;
}
+static u64 find_entry_trampoline(struct dso *dso)
+{
+ u64 addr = 0;
+
+ dso__for_each_symbol(dso, find_entry_trampoline_cb, &addr);
+ return addr;
+}
+
/*
* These values can be used for kernels that do not have symbols for the entry
* trampolines in kallsyms.
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c
index 85216fa71f1a..19afe676adf2 100644
--- a/tools/perf/util/map.c
+++ b/tools/perf/util/map.c
@@ -314,27 +314,22 @@ void map__put(struct map *map)
void map__fixup_start(struct map *map)
{
- struct dso *dso = map__dso(map);
- struct rb_root_cached *symbols = dso__symbols(dso);
- struct rb_node *nd = rb_first_cached(symbols);
-
- if (nd != NULL) {
- struct symbol *sym = rb_entry(nd, struct symbol, rb_node);
+ struct symbols *symbols = dso__symbols(map__dso(map));
- map__set_start(map, symbol__start(sym));
- }
+ symbols__sort_read_lock(symbols);
+ if (symbols->cnt > 0)
+ map__set_start(map, symbol__start(symbols->symbols[0]));
+ up_read(&symbols->lock);
}
void map__fixup_end(struct map *map)
{
- struct dso *dso = map__dso(map);
- struct rb_root_cached *symbols = dso__symbols(dso);
- struct rb_node *nd = rb_last(&symbols->rb_root);
+ struct symbols *symbols = dso__symbols(map__dso(map));
- if (nd != NULL) {
- struct symbol *sym = rb_entry(nd, struct symbol, rb_node);
- map__set_end(map, symbol__end(sym));
- }
+ symbols__sort_read_lock(symbols);
+ if (symbols->cnt > 0)
+ map__set_end(map, symbol__end(symbols->symbols[symbols->cnt - 1]));
+ up_read(&symbols->lock);
}
#define DSO__DELETED "(deleted)"
diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h
index f49b4aaacf2f..3d8f72e70d49 100644
--- a/tools/perf/util/map.h
+++ b/tools/perf/util/map.h
@@ -146,12 +146,12 @@ struct thread;
/* map__for_each_symbol - iterate over the symbols in the given map
*
* @map: the 'struct map *' in which symbols are iterated
- * @pos: the 'struct symbol *' to use as a loop cursor
- * @n: the 'struct rb_node *' to use as a temporary storage
+ * @cb: callback function invoked for each symbol
+ * @data: opaque data pointer passed to @cb
* Note: caller must ensure map->dso is not NULL (map is loaded).
*/
-#define map__for_each_symbol(map, pos, n) \
- dso__for_each_symbol(map__dso(map), pos, n)
+#define map__for_each_symbol(map, cb, data) \
+ dso__for_each_symbol(map__dso(map), cb, data)
/* map__for_each_symbol_with_name - iterate over the symbols in the given map
* that have the given name
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 2d554fed0c3f..bea7903a11e3 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -3047,49 +3047,63 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
return ret;
}
-static int find_probe_functions(struct map *map, char *name,
- struct symbol **syms)
+struct find_probe_functions_args {
+ const char *name;
+ struct symbol **syms;
+ int found;
+ bool cut_version;
+};
+
+static int find_probe_functions_cb(struct symbol *sym, void *data)
{
- int found = 0;
- struct symbol *sym;
- struct rb_node *tmp;
+ struct find_probe_functions_args *args = data;
const char *norm, *ver;
char *buf = NULL;
- bool cut_version = true;
- if (map__load(map) < 0)
- return -EACCES; /* Possible permission error to load symbols */
-
- /* If user gives a version, don't cut off the version from symbols */
- if (strchr(name, '@'))
- cut_version = false;
-
- map__for_each_symbol(map, sym, tmp) {
- norm = arch__normalize_symbol_name(symbol__name(sym));
- if (!norm)
- continue;
+ norm = arch__normalize_symbol_name(symbol__name(sym));
+ if (!norm)
+ return 0;
- if (cut_version) {
- /* We don't care about default symbol or not */
- ver = strchr(norm, '@');
- if (ver) {
- buf = strndup(norm, ver - norm);
- if (!buf)
- return -ENOMEM;
- norm = buf;
- }
+ if (args->cut_version) {
+ /* We don't care about default symbol or not */
+ ver = strchr(norm, '@');
+ if (ver) {
+ buf = strndup(norm, ver - norm);
+ if (!buf)
+ return -ENOMEM;
+ norm = buf;
}
+ }
- if (strglobmatch(norm, name)) {
- found++;
- if (syms && found < probe_conf.max_probes)
- syms[found - 1] = sym;
- }
- if (buf)
- zfree(&buf);
+ if (strglobmatch(norm, args->name)) {
+ args->found++;
+ if (args->syms && args->found < probe_conf.max_probes)
+ args->syms[args->found - 1] = sym;
}
+ free(buf);
+ return 0;
+}
- return found;
+static int find_probe_functions(struct map *map, char *name,
+ struct symbol **syms)
+{
+ struct find_probe_functions_args args = {
+ .name = name,
+ .syms = syms,
+ /* If user gives a version, don't cut off the version from symbols */
+ .cut_version = strchr(name, '@') == NULL,
+ .found = 0,
+ };
+ int ret;
+
+ if (map__load(map) < 0)
+ return -EACCES; /* Possible permission error to load symbols */
+
+ ret = map__for_each_symbol(map, find_probe_functions_cb, &args);
+ if (ret < 0)
+ return ret;
+
+ return args.found;
}
void __weak arch__fix_tev_from_maps(struct perf_probe_event *pev __maybe_unused,
@@ -3775,6 +3789,15 @@ void cleanup_perf_probe_events(struct perf_probe_event *pevs, int npevs)
}
}
+static int show_available_funcs_cb(struct symbol *pos, void *data)
+{
+ struct strfilter *_filter = data;
+
+ if (strfilter__compare(_filter, symbol__name(pos)))
+ printf("%s\n", symbol__name(pos));
+ return 0;
+}
+
int show_available_funcs(const char *target, struct nsinfo *nsi,
struct strfilter *_filter, bool user)
{
@@ -3811,12 +3834,7 @@ int show_available_funcs(const char *target, struct nsinfo *nsi,
/* Show all (filtered) symbols */
setup_pager();
- for (size_t i = 0; i < dso__symbol_names_len(dso); i++) {
- struct symbol *pos = dso__symbol_names(dso)[i];
-
- if (strfilter__compare(_filter, symbol__name(pos)))
- printf("%s\n", symbol__name(pos));
- }
+ dso__for_each_symbol_by_name(dso, show_available_funcs_cb, _filter);
end:
map__put(map);
exit_probe_symbol_maps();
diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c
index 817486a4b8f1..77db7ab5011d 100644
--- a/tools/perf/util/symbol-elf.c
+++ b/tools/perf/util/symbol-elf.c
@@ -586,7 +586,8 @@ static int dso__synthesize_plt_got_symbols(struct dso *dso, Elf *elf,
sym = symbol__new(shdr.sh_offset + i, shdr.sh_entsize, STB_GLOBAL, STT_FUNC, buf);
if (!sym)
goto out;
- symbols__insert(dso__symbols(dso), sym);
+ if (symbols__insert(dso__symbols(dso), sym))
+ goto out;
}
err = 0;
out:
@@ -640,7 +641,8 @@ int dso__synthesize_plt_symbols(struct dso *dso, struct symsrc *ss)
plt_sym = symbol__new(shdr_plt.sh_offset, plt_header_size, STB_GLOBAL, STT_FUNC, ".plt");
if (!plt_sym)
goto out_elf_end;
- symbols__insert(dso__symbols(dso), plt_sym);
+ if (symbols__insert(dso__symbols(dso), plt_sym))
+ goto out_elf_end;
/* Only x86 has .plt.got */
if (machine_is_x86(ehdr.e_machine) &&
@@ -762,7 +764,8 @@ int dso__synthesize_plt_symbols(struct dso *dso, struct symsrc *ss)
goto out_elf_end;
plt_offset += plt_entry_size;
- symbols__insert(dso__symbols(dso), f);
+ if (symbols__insert(dso__symbols(dso), f))
+ goto out_elf_end;
++nr;
}
@@ -1785,7 +1788,8 @@ dso__load_sym_internal(struct dso *dso, struct map *map, struct symsrc *syms_ss,
arch__sym_update(f, &sym);
- __symbols__insert(dso__symbols(curr_dso), f);
+ if (symbols__insert(dso__symbols(curr_dso), f))
+ goto out_elf_end;
nr++;
}
dso__put(curr_dso);
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 5b50cca7989d..0055bfdc980c 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -213,57 +213,157 @@ static int choose_best_symbol(struct symbol *syma, struct symbol *symb)
return arch__choose_best_symbol(syma, symb);
}
-void symbols__fixup_duplicate(struct rb_root_cached *symbols)
+void symbols__init(struct symbols *symbols)
{
- struct rb_node *nd;
- struct symbol *curr, *next;
+ init_rwsem(&symbols->lock);
+ symbols->symbols = NULL;
+ symbols->symbols_by_name = NULL;
+ symbols->cnt = 0;
+ symbols->allocated = 0;
+ symbols->num_by_name = 0;
+ symbols->sorted = true;
+ symbols->sorted_by_name = false;
+}
+
+static void symbols__delete_locked(struct symbols *symbols)
+{
+ for (unsigned int i = 0; i < symbols->cnt; i++)
+ symbol__delete(symbols->symbols[i]);
+
+ zfree(&symbols->symbols);
+ zfree(&symbols->symbols_by_name);
+ symbols->cnt = 0;
+ symbols->allocated = 0;
+ symbols->num_by_name = 0;
+ symbols->sorted = true;
+ symbols->sorted_by_name = false;
+}
+
+void symbols__delete(struct symbols *symbols)
+{
+ down_write(&symbols->lock);
+ symbols__delete_locked(symbols);
+ up_write(&symbols->lock);
+}
+
+void symbols__exit(struct symbols *symbols)
+{
+ symbols__delete(symbols);
+ exit_rwsem(&symbols->lock);
+}
+
+static inline bool symbol__less_or_equal(const struct symbol *a, const struct symbol *b)
+{
+ if (symbol__start(a) != symbol__start(b))
+ return symbol__start(a) < symbol__start(b);
+ return symbol__end(a) <= symbol__end(b);
+}
+
+static int symbols__sort_cmp(const void *vlhs, const void *vrhs)
+{
+ const struct symbol *lhs = *((const struct symbol **)vlhs);
+ const struct symbol *rhs = *((const struct symbol **)vrhs);
+
+ if (symbol__start(lhs) != symbol__start(rhs))
+ return symbol__start(lhs) < symbol__start(rhs) ? -1 : 1;
+ if (symbol__end(lhs) != symbol__end(rhs))
+ return symbol__end(lhs) < symbol__end(rhs) ? -1 : 1;
+ return 0;
+}
+
+static void symbols__sort_locked(struct symbols *symbols)
+{
+ if (symbols->sorted)
+ return;
+
+ if (symbols->cnt > 1) {
+ qsort(symbols->symbols, symbols->cnt, sizeof(struct symbol *),
+ symbols__sort_cmp);
+ }
+ symbols->sorted = true;
+}
+
+/*
+ * Acquire @symbols->lock for reading after ensuring @symbols->symbols is
+ * sorted.
+ */
+void symbols__sort_read_lock(struct symbols *symbols)
+ NO_THREAD_SAFETY_ANALYSIS
+{
+ down_read(&symbols->lock);
+ while (!symbols->sorted) {
+ up_read(&symbols->lock);
+ down_write(&symbols->lock);
+ symbols__sort_locked(symbols);
+ up_write(&symbols->lock);
+ down_read(&symbols->lock);
+ }
+}
+
+/*
+ * Deduplicate symbols sharing the same start address by sorting @symbols and
+ * compacting the array in place, keeping the best symbol at each address as
+ * chosen by choose_best_symbol().
+ */
+void symbols__fixup_duplicate(struct symbols *symbols)
+{
+ unsigned int write_idx = 0;
if (symbol_conf.allow_aliases)
return;
- nd = rb_first_cached(symbols);
+ down_write(&symbols->lock);
+ symbols__sort_locked(symbols);
+ if (symbols->cnt <= 1) {
+ up_write(&symbols->lock);
+ return;
+ }
- while (nd) {
- curr = rb_entry(nd, struct symbol, rb_node);
-again:
- nd = rb_next(&curr->rb_node);
- if (!nd)
- break;
+ for (unsigned int read_idx = 1; read_idx < symbols->cnt; read_idx++) {
+ struct symbol *curr = symbols->symbols[write_idx];
+ struct symbol *next = symbols->symbols[read_idx];
- next = rb_entry(nd, struct symbol, rb_node);
- if (symbol__start(curr) != symbol__start(next))
+ if (symbol__start(curr) != symbol__start(next)) {
+ symbols->symbols[++write_idx] = next;
continue;
+ }
if (choose_best_symbol(curr, next) == SYMBOL_A) {
if (symbol__type(next) == STT_GNU_IFUNC)
symbol__set_ifunc_alias(curr, true);
- rb_erase_cached(&next->rb_node, symbols);
symbol__delete(next);
- goto again;
} else {
if (symbol__type(curr) == STT_GNU_IFUNC)
symbol__set_ifunc_alias(next, true);
- nd = rb_next(&curr->rb_node);
- rb_erase_cached(&curr->rb_node, symbols);
symbol__delete(curr);
+ symbols->symbols[write_idx] = next;
}
}
+ symbols->cnt = write_idx + 1;
+ zfree(&symbols->symbols_by_name);
+ symbols->num_by_name = 0;
+ symbols->sorted_by_name = false;
+ up_write(&symbols->lock);
}
/* Update zero-sized symbols using the address of the next symbol */
-void symbols__fixup_end(struct rb_root_cached *symbols, bool is_kallsyms)
+void symbols__fixup_end(struct symbols *symbols, bool is_kallsyms)
{
- struct rb_node *nd, *prevnd = rb_first_cached(symbols);
struct symbol *curr, *prev;
- if (prevnd == NULL)
+ down_write(&symbols->lock);
+ symbols__sort_locked(symbols);
+
+ if (symbols->cnt == 0) {
+ up_write(&symbols->lock);
return;
+ }
- curr = rb_entry(prevnd, struct symbol, rb_node);
+ curr = symbols->symbols[0];
- for (nd = rb_next(prevnd); nd; nd = rb_next(nd)) {
+ for (unsigned int i = 1; i < symbols->cnt; i++) {
prev = curr;
- curr = rb_entry(nd, struct symbol, rb_node);
+ curr = symbols->symbols[i];
/*
* On some architecture kernel text segment start is located at
@@ -314,6 +414,8 @@ void symbols__fixup_end(struct rb_root_cached *symbols, bool is_kallsyms)
/* Last entry */
if (symbol__end(curr) == symbol__start(curr))
symbol__set_end(curr, roundup(symbol__start(curr), 4096) + 4096);
+
+ up_write(&symbols->lock);
}
struct symbol *symbol__new(u64 start, u64 len, u8 binding, u8 type, const char *name)
@@ -395,97 +497,94 @@ static void symbol__set_idle(struct symbol *sym, bool idle)
new_flags |= (idle_val << SYMBOL_FLAG_IDLE_SHIFT);
} while (!atomic_compare_exchange_weak(&sym->flags, &old_flags, new_flags));
}
-void symbols__delete(struct rb_root_cached *symbols)
-{
- struct symbol *pos;
- struct rb_node *next = rb_first_cached(symbols);
- while (next) {
- pos = rb_entry(next, struct symbol, rb_node);
- next = rb_next(&pos->rb_node);
- rb_erase_cached(&pos->rb_node, symbols);
- symbol__delete(pos);
- }
-}
-
-void __symbols__insert(struct rb_root_cached *symbols, struct symbol *sym)
+static int __symbols__insert(struct symbols *symbols, struct symbol *sym)
{
- struct rb_node **p = &symbols->rb_root.rb_node;
- struct rb_node *parent = NULL;
- const u64 ip = symbol__start(sym);
- struct symbol *s;
- bool leftmost = true;
+ if (symbols->cnt == symbols->allocated) {
+ unsigned int to_allocate = symbols->allocated ? symbols->allocated * 2 : 32;
+ struct symbol **temp = realloc(symbols->symbols,
+ sizeof(struct symbol *) * to_allocate);
- while (*p != NULL) {
- parent = *p;
- s = rb_entry(parent, struct symbol, rb_node);
- if (ip < symbol__start(s))
- p = &(*p)->rb_left;
- else {
- p = &(*p)->rb_right;
- leftmost = false;
+ if (!temp) {
+ symbol__delete(sym);
+ return -ENOMEM;
}
+ symbols->symbols = temp;
+ symbols->allocated = to_allocate;
}
- rb_link_node(&sym->rb_node, parent, p);
- rb_insert_color_cached(&sym->rb_node, symbols, leftmost);
+
+ if (symbols->sorted && symbols->cnt > 0 &&
+ !symbol__less_or_equal(symbols->symbols[symbols->cnt - 1], sym))
+ symbols->sorted = false;
+
+ symbols->symbols[symbols->cnt++] = sym;
+ zfree(&symbols->symbols_by_name);
+ symbols->num_by_name = 0;
+ symbols->sorted_by_name = false;
+ return 0;
}
-void symbols__insert(struct rb_root_cached *symbols, struct symbol *sym)
+int symbols__insert(struct symbols *symbols, struct symbol *sym)
{
- __symbols__insert(symbols, sym);
+ int ret;
+
+ down_write(&symbols->lock);
+ ret = __symbols__insert(symbols, sym);
+ up_write(&symbols->lock);
+ return ret;
}
-static struct symbol *symbols__find(struct rb_root_cached *symbols, u64 ip)
+/*
+ * Binary search @symbols->symbols for a symbol covering @ip. Lazily sorts
+ * @symbols under @symbols->lock if unsorted.
+ */
+static struct symbol *symbols__find(struct symbols *symbols, u64 ip)
{
- struct rb_node *n;
+ int low, high;
+ struct symbol *res = NULL;
if (symbols == NULL)
return NULL;
- n = symbols->rb_root.rb_node;
+ symbols__sort_read_lock(symbols);
- while (n) {
- struct symbol *s = rb_entry(n, struct symbol, rb_node);
+ low = 0;
+ high = (int)symbols->cnt - 1;
+ while (low <= high) {
+ int mid = low + (high - low) / 2;
+ struct symbol *s = symbols->symbols[mid];
+ u64 start = symbol__start(s);
+ u64 end = symbol__end(s);
- if (ip < symbol__start(s))
- n = n->rb_left;
- else if (ip > symbol__end(s) || (ip == symbol__end(s) && ip != symbol__start(s)))
- n = n->rb_right;
- else
- return s;
+ if (ip < start)
+ high = mid - 1;
+ else if (ip > end || (ip == end && ip != start))
+ low = mid + 1;
+ else {
+ res = s;
+ break;
+ }
}
+ up_read(&symbols->lock);
- return NULL;
-}
-
-static struct symbol *symbols__first(struct rb_root_cached *symbols)
-{
- struct rb_node *n = rb_first_cached(symbols);
-
- if (n)
- return rb_entry(n, struct symbol, rb_node);
-
- return NULL;
+ return res;
}
-static struct symbol *symbols__last(struct rb_root_cached *symbols)
+int symbols__for_each_entry(struct symbols *symbols,
+ int (*cb)(struct symbol *sym, void *data),
+ void *data)
{
- struct rb_node *n = rb_last(&symbols->rb_root);
-
- if (n)
- return rb_entry(n, struct symbol, rb_node);
-
- return NULL;
-}
-
-static struct symbol *symbols__next(struct symbol *sym)
-{
- struct rb_node *n = rb_next(&sym->rb_node);
+ int err = 0;
- if (n)
- return rb_entry(n, struct symbol, rb_node);
+ symbols__sort_read_lock(symbols);
+ for (unsigned int i = 0; i < symbols->cnt; i++) {
+ err = cb(symbols->symbols[i], data);
+ if (err)
+ break;
+ }
+ up_read(&symbols->lock);
- return NULL;
+ return err;
}
static int symbols__sort_name_cmp(const void *vlhs, const void *vrhs)
@@ -496,27 +595,31 @@ static int symbols__sort_name_cmp(const void *vlhs, const void *vrhs)
return strcmp(symbol__name(lhs), symbol__name(rhs));
}
-static struct symbol **symbols__sort_by_name(struct rb_root_cached *source, size_t *len)
+static void symbols__sort_by_name_locked(struct symbols *symbols)
{
- struct rb_node *nd;
struct symbol **result;
- size_t i = 0, size = 0;
+ unsigned int size = symbols->cnt;
+
+ if (symbols->sorted_by_name)
+ return;
- for (nd = rb_first_cached(source); nd; nd = rb_next(nd))
- size++;
+ zfree(&symbols->symbols_by_name);
+ symbols->num_by_name = 0;
+
+ if (size == 0) {
+ symbols->sorted_by_name = true;
+ return;
+ }
result = malloc(sizeof(*result) * size);
if (!result)
- return NULL;
-
- for (nd = rb_first_cached(source); nd; nd = rb_next(nd)) {
- struct symbol *pos = rb_entry(nd, struct symbol, rb_node);
+ return;
- result[i++] = pos;
- }
+ memcpy(result, symbols->symbols, sizeof(*result) * size);
qsort(result, size, sizeof(*result), symbols__sort_name_cmp);
- *len = size;
- return result;
+ symbols->symbols_by_name = result;
+ symbols->num_by_name = size;
+ symbols->sorted_by_name = true;
}
int symbol__match_symbol_name(const char *name, const char *str,
@@ -585,39 +688,34 @@ static struct symbol *symbols__find_by_name(struct symbol *symbols[],
return s;
}
-void dso__reset_find_symbol_cache(struct dso *dso)
-{
- dso__set_last_find_result_addr(dso, 0);
- dso__set_last_find_result_symbol(dso, NULL);
-}
-
-void dso__insert_symbol(struct dso *dso, struct symbol *sym)
+int dso__insert_symbol(struct dso *dso, struct symbol *sym)
{
- __symbols__insert(dso__symbols(dso), sym);
-
- /* update the symbol cache if necessary */
- if (dso__last_find_result_addr(dso) >= symbol__start(sym) &&
- (dso__last_find_result_addr(dso) < symbol__end(sym) ||
- symbol__start(sym) == symbol__end(sym))) {
- dso__set_last_find_result_symbol(dso, sym);
- }
+ return symbols__insert(dso__symbols(dso), sym);
}
void dso__delete_symbol(struct dso *dso, struct symbol *sym)
{
- rb_erase_cached(&sym->rb_node, dso__symbols(dso));
- symbol__delete(sym);
- dso__reset_find_symbol_cache(dso);
+ struct symbols *symbols = dso__symbols(dso);
+
+ down_write(&symbols->lock);
+ for (unsigned int i = 0; i < symbols->cnt; i++) {
+ if (symbols->symbols[i] == sym) {
+ memmove(&symbols->symbols[i], &symbols->symbols[i + 1],
+ (symbols->cnt - i - 1) * sizeof(struct symbol *));
+ symbols->cnt--;
+ zfree(&symbols->symbols_by_name);
+ symbols->num_by_name = 0;
+ symbols->sorted_by_name = false;
+ symbol__delete(sym);
+ break;
+ }
+ }
+ up_write(&symbols->lock);
}
struct symbol *dso__find_symbol(struct dso *dso, u64 addr)
{
- if (dso__last_find_result_addr(dso) != addr || dso__last_find_result_symbol(dso) == NULL) {
- dso__set_last_find_result_addr(dso, addr);
- dso__set_last_find_result_symbol(dso, symbols__find(dso__symbols(dso), addr));
- }
-
- return dso__last_find_result_symbol(dso);
+ return symbols__find(dso__symbols(dso), addr);
}
struct symbol *dso__find_symbol_nocache(struct dso *dso, u64 addr)
@@ -625,28 +723,19 @@ struct symbol *dso__find_symbol_nocache(struct dso *dso, u64 addr)
return symbols__find(dso__symbols(dso), addr);
}
-struct symbol *dso__first_symbol(struct dso *dso)
-{
- return symbols__first(dso__symbols(dso));
-}
-
-struct symbol *dso__last_symbol(struct dso *dso)
-{
- return symbols__last(dso__symbols(dso));
-}
-
-struct symbol *dso__next_symbol(struct symbol *sym)
-{
- return symbols__next(sym);
-}
-
struct symbol *dso__next_symbol_by_name(struct dso *dso, size_t *idx)
{
- if (*idx + 1 >= dso__symbol_names_len(dso))
- return NULL;
+ struct symbols *symbols = dso__symbols(dso);
+ struct symbol *res = NULL;
- ++*idx;
- return dso__symbol_names(dso)[*idx];
+ down_read(&symbols->lock);
+ if (*idx + 1 < symbols->num_by_name) {
+ ++*idx;
+ res = symbols->symbols_by_name[*idx];
+ }
+ up_read(&symbols->lock);
+
+ return res;
}
/*
@@ -654,29 +743,70 @@ struct symbol *dso__next_symbol_by_name(struct dso *dso, size_t *idx)
*/
struct symbol *dso__find_symbol_by_name(struct dso *dso, const char *name, size_t *idx)
{
- struct symbol *s = symbols__find_by_name(dso__symbol_names(dso),
- dso__symbol_names_len(dso),
- name, SYMBOL_TAG_INCLUDE__NONE, idx);
+ struct symbols *symbols;
+ struct symbol *s;
+
+ if (!dso)
+ return NULL;
+ symbols = dso__symbols(dso);
+ down_read(&symbols->lock);
+ while (!symbols->sorted_by_name) {
+ up_read(&symbols->lock);
+ dso__sort_by_name(dso);
+ down_read(&symbols->lock);
+ if (!symbols->sorted_by_name && !symbols->symbols_by_name)
+ break;
+ }
+ s = symbols__find_by_name(symbols->symbols_by_name,
+ symbols->num_by_name,
+ name, SYMBOL_TAG_INCLUDE__NONE, idx);
if (!s) {
- s = symbols__find_by_name(dso__symbol_names(dso), dso__symbol_names_len(dso),
+ s = symbols__find_by_name(symbols->symbols_by_name, symbols->num_by_name,
name, SYMBOL_TAG_INCLUDE__DEFAULT_ONLY, idx);
}
+ up_read(&symbols->lock);
return s;
}
void dso__sort_by_name(struct dso *dso)
{
- mutex_lock(dso__lock(dso));
- if (!dso__sorted_by_name(dso)) {
- size_t len = 0;
+ struct symbols *symbols = dso__symbols(dso);
- dso__set_symbol_names(dso, symbols__sort_by_name(dso__symbols(dso), &len));
- if (dso__symbol_names(dso)) {
- dso__set_symbol_names_len(dso, len);
- dso__set_sorted_by_name(dso);
- }
+ down_read(&symbols->lock);
+ if (!symbols->sorted_by_name) {
+ up_read(&symbols->lock);
+ down_write(&symbols->lock);
+ symbols__sort_by_name_locked(symbols);
+ up_write(&symbols->lock);
+ return;
}
- mutex_unlock(dso__lock(dso));
+ up_read(&symbols->lock);
+}
+
+int symbols__visit_by_name(struct symbols *symbols,
+ int (*cb)(struct symbol *sym, void *data),
+ void *data)
+{
+ int err = 0;
+
+ down_read(&symbols->lock);
+ while (!symbols->sorted_by_name) {
+ up_read(&symbols->lock);
+ down_write(&symbols->lock);
+ symbols__sort_by_name_locked(symbols);
+ up_write(&symbols->lock);
+ down_read(&symbols->lock);
+ if (!symbols->sorted_by_name && !symbols->symbols_by_name)
+ break;
+ }
+ for (size_t i = 0; i < symbols->num_by_name; i++) {
+ err = cb(symbols->symbols_by_name[i], data);
+ if (err)
+ break;
+ }
+ up_read(&symbols->lock);
+
+ return err;
}
/*
@@ -876,7 +1006,7 @@ static int map__process_kallsym_symbol(void *arg, const char *name,
{
struct symbol *sym;
struct dso *dso = arg;
- struct rb_root_cached *root = dso__symbols(dso);
+ struct symbols *root = dso__symbols(dso);
if (!symbol_type__filter(type))
return 0;
@@ -897,9 +1027,7 @@ static int map__process_kallsym_symbol(void *arg, const char *name,
* We will pass the symbols to the filter later, in
* map__split_kallsyms, when we have split the maps per module
*/
- __symbols__insert(root, sym);
-
- return 0;
+ return symbols__insert(root, sym);
}
/*
@@ -916,25 +1044,32 @@ static int maps__split_kallsyms_for_kcore(struct maps *kmaps, struct dso *dso)
{
struct symbol *pos;
int count = 0;
- struct rb_root_cached *root = dso__symbols(dso);
- struct rb_root_cached old_root = *root;
- struct rb_node *next = rb_first_cached(root);
+ struct symbols *root = dso__symbols(dso);
+ struct symbol **old_symbols;
+ unsigned int old_cnt;
if (!kmaps)
return -1;
- *root = RB_ROOT_CACHED;
-
- while (next) {
+ down_write(&root->lock);
+ symbols__sort_locked(root);
+ old_symbols = root->symbols;
+ old_cnt = root->cnt;
+ root->symbols = NULL;
+ root->cnt = 0;
+ root->allocated = 0;
+ root->sorted = true;
+ zfree(&root->symbols_by_name);
+ root->num_by_name = 0;
+ root->sorted_by_name = false;
+ up_write(&root->lock);
+
+ for (unsigned int i = 0; i < old_cnt; i++) {
struct map *curr_map;
struct dso *curr_map_dso;
char *module;
- pos = rb_entry(next, struct symbol, rb_node);
- next = rb_next(&pos->rb_node);
-
- rb_erase_cached(&pos->rb_node, &old_root);
- RB_CLEAR_NODE(&pos->rb_node);
+ pos = old_symbols[i];
module = strchr((char *)symbol__name(pos), '\t');
if (module)
*module = '\0';
@@ -954,11 +1089,13 @@ static int maps__split_kallsyms_for_kcore(struct maps *kmaps, struct dso *dso)
symbol__set_end(pos, symbol__end(pos) -
(map__start(curr_map) - map__pgoff(curr_map)));
}
- symbols__insert(dso__symbols(curr_map_dso), pos);
- ++count;
+ if (symbols__insert(dso__symbols(curr_map_dso), pos) == 0)
+ ++count;
map__put(curr_map);
}
+ free(old_symbols);
+
/* Symbols have been adjusted */
dso__set_adjust_symbols(dso, true);
@@ -977,8 +1114,10 @@ static int maps__split_kallsyms(struct maps *kmaps, struct dso *dso, u64 delta,
struct map *curr_map = map__get(initial_map);
struct symbol *pos;
int count = 0, moved = 0;
- struct rb_root_cached *root = dso__symbols(dso);
- struct rb_node *next = rb_first_cached(root);
+ struct symbols *root = dso__symbols(dso);
+ struct symbol **old_symbols;
+ unsigned int old_cnt, old_allocated;
+ unsigned int i, write_idx = 0;
int kernel_range = 0;
uint16_t e_machine = EM_NONE;
@@ -988,11 +1127,24 @@ static int maps__split_kallsyms(struct maps *kmaps, struct dso *dso, u64 delta,
machine = maps__machine(kmaps);
e_machine = perf_env__e_machine(machine->env, /*e_flags=*/NULL);
- while (next) {
+ down_write(&root->lock);
+ symbols__sort_locked(root);
+ old_symbols = root->symbols;
+ old_cnt = root->cnt;
+ old_allocated = root->allocated;
+ root->symbols = NULL;
+ root->cnt = 0;
+ root->allocated = 0;
+ root->sorted = true;
+ zfree(&root->symbols_by_name);
+ root->num_by_name = 0;
+ root->sorted_by_name = false;
+ up_write(&root->lock);
+
+ for (i = 0; i < old_cnt; i++) {
char *module;
- pos = rb_entry(next, struct symbol, rb_node);
- next = rb_next(&pos->rb_node);
+ pos = old_symbols[i];
module = strchr((char *)symbol__name(pos), '\t');
if (module) {
@@ -1077,7 +1229,7 @@ static int maps__split_kallsyms(struct maps *kmaps, struct dso *dso, u64 delta,
ndso = dso__new(dso_name);
map__zput(curr_map);
if (ndso == NULL)
- return -1;
+ goto out_err;
dso__set_kernel(ndso, dso__kernel(dso));
if (dso__kernel(dso) == DSO_SPACE__KERNEL_GUEST) {
@@ -1092,14 +1244,14 @@ static int maps__split_kallsyms(struct maps *kmaps, struct dso *dso, u64 delta,
curr_map = map__new2(symbol__start(pos), ndso);
if (curr_map == NULL) {
dso__put(ndso);
- return -1;
+ goto out_err;
}
map__set_mapping_type(curr_map, MAPPING_TYPE__IDENTITY);
if (maps__insert(kmaps, curr_map)) {
map__zput(curr_map);
dso__put(ndso);
- return -1;
+ goto out_err;
}
dso__put(ndso);
++kernel_range;
@@ -1112,18 +1264,35 @@ static int maps__split_kallsyms(struct maps *kmaps, struct dso *dso, u64 delta,
if (!RC_CHK_EQUAL(curr_map, initial_map)) {
struct dso *curr_map_dso = map__dso(curr_map);
- rb_erase_cached(&pos->rb_node, root);
- symbols__insert(dso__symbols(curr_map_dso), pos);
- ++moved;
- } else
+ if (symbols__insert(dso__symbols(curr_map_dso), pos) == 0)
+ ++moved;
+ } else {
+ old_symbols[write_idx++] = pos;
++count;
+ }
continue;
discard_symbol:
- rb_erase_cached(&pos->rb_node, root);
symbol__delete(pos);
}
+ down_write(&root->lock);
+ if (root->cnt == 0) {
+ root->symbols = old_symbols;
+ root->cnt = write_idx;
+ root->allocated = old_allocated;
+ root->sorted = true;
+ old_symbols = NULL;
+ } else {
+ for (unsigned int j = 0; j < write_idx; j++)
+ __symbols__insert(root, old_symbols[j]);
+ }
+ zfree(&root->symbols_by_name);
+ root->num_by_name = 0;
+ root->sorted_by_name = false;
+ up_write(&root->lock);
+ free(old_symbols);
+
if (!RC_CHK_EQUAL(curr_map, initial_map) &&
dso__kernel(dso) == DSO_SPACE__KERNEL_GUEST &&
machine__is_default_guest(maps__machine(kmaps))) {
@@ -1131,6 +1300,29 @@ static int maps__split_kallsyms(struct maps *kmaps, struct dso *dso, u64 delta,
}
map__put(curr_map);
return count + moved;
+
+out_err:
+ memmove(&old_symbols[write_idx], &old_symbols[i],
+ (old_cnt - i) * sizeof(struct symbol *));
+ write_idx += old_cnt - i;
+ down_write(&root->lock);
+ if (root->cnt == 0) {
+ root->symbols = old_symbols;
+ root->cnt = write_idx;
+ root->allocated = old_allocated;
+ root->sorted = (delta == 0);
+ old_symbols = NULL;
+ } else {
+ for (unsigned int j = 0; j < write_idx; j++)
+ __symbols__insert(root, old_symbols[j]);
+ root->sorted = false;
+ }
+ zfree(&root->symbols_by_name);
+ root->num_by_name = 0;
+ root->sorted_by_name = false;
+ up_write(&root->lock);
+ free(old_symbols);
+ return -1;
}
bool symbol__restricted_filename(const char *filename,
@@ -1691,7 +1883,8 @@ static int dso__load_perf_map(const char *map_path, struct dso *dso)
if (sym == NULL)
goto out_delete_line;
- symbols__insert(dso__symbols(dso), sym);
+ if (symbols__insert(dso__symbols(dso), sym))
+ goto out_delete_line;
nr_syms++;
}
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index 37e6ab1478b5..eed4d388d1fc 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -15,6 +15,7 @@
#include <errno.h>
#include "addr_location.h"
#include "path.h"
+#include "rwsem.h"
#include "symbol_conf.h"
#include "spark.h"
#include "util.h"
@@ -85,7 +86,6 @@ enum symbol_idle_kind {
* A symtab entry.
*/
struct symbol {
- struct rb_node rb_node;
/** Range of symbol [start, end). */
u64 start;
u64 end;
@@ -98,8 +98,42 @@ struct symbol {
char name[];
};
+/**
+ * struct symbols - A locked collection of symbols stored in a sorted array.
+ */
+struct symbols {
+ /** @lock: Read/write semaphore synchronizing access to the arrays. */
+ struct rw_semaphore lock;
+ /**
+ * @symbols: Dynamically allocated array of symbol pointers, sorted by
+ * address range [start, end) when @sorted is true.
+ */
+ struct symbol **symbols;
+ /**
+ * @symbols_by_name: Optional dynamically allocated array of symbol
+ * pointers sorted lexicographically by symbol__name().
+ */
+ struct symbol **symbols_by_name;
+ /** @cnt: Number of valid entries in @symbols. */
+ unsigned int cnt;
+ /** @allocated: Capacity (in entries) of the @symbols allocation. */
+ unsigned int allocated;
+ /** @num_by_name: Number of valid entries in @symbols_by_name. */
+ unsigned int num_by_name;
+ /** @sorted: True when @symbols is sorted by address range. */
+ bool sorted;
+ /**
+ * @sorted_by_name: True when @symbols_by_name is populated and sorted
+ * by name.
+ */
+ bool sorted_by_name;
+};
+
void symbol__delete(struct symbol *sym);
-void symbols__delete(struct rb_root_cached *symbols);
+void symbols__init(struct symbols *symbols);
+void symbols__exit(struct symbols *symbols);
+void symbols__delete(struct symbols *symbols);
+void symbols__sort_read_lock(struct symbols *symbols) SHARED_LOCK_FUNCTION(symbols->lock);
static inline __attribute__((nonnull)) u64 symbol__start(const struct symbol *sym)
{
@@ -192,16 +226,19 @@ void symbol__set_inlined(struct symbol *sym, bool inlined);
void symbol__set_ifunc_alias(struct symbol *sym, bool ifunc_alias);
void symbol__set_annotated(struct symbol *sym, bool annotated);
-/* symbols__for_each_entry - iterate over symbols (rb_root)
+/*
+ * symbols__for_each_entry - iterate over symbols calling @cb for each entry
*
- * @symbols: the rb_root of symbols
- * @pos: the 'struct symbol *' to use as a loop cursor
- * @nd: the 'struct rb_node *' to use as a temporary storage
+ * @symbols: the 'struct symbols *' of symbols
+ * @cb: callback function invoked under @symbols->lock
+ * @data: opaque data pointer passed to @cb
*/
-#define symbols__for_each_entry(symbols, pos, nd) \
- for (nd = rb_first_cached(symbols); \
- nd && (pos = rb_entry(nd, struct symbol, rb_node)); \
- nd = rb_next(nd))
+int symbols__for_each_entry(struct symbols *symbols,
+ int (*cb)(struct symbol *sym, void *data),
+ void *data);
+int symbols__visit_by_name(struct symbols *symbols,
+ int (*cb)(struct symbol *sym, void *data),
+ void *data);
static inline size_t symbol__size(const struct symbol *sym)
{
@@ -238,8 +275,8 @@ int __dso__load_kallsyms(struct dso *dso, const char *filename, struct map *map,
bool no_kcore);
int dso__load_kallsyms(struct dso *dso, const char *filename, struct map *map);
-void dso__insert_symbol(struct dso *dso,
- struct symbol *sym);
+int dso__insert_symbol(struct dso *dso,
+ struct symbol *sym);
void dso__delete_symbol(struct dso *dso,
struct symbol *sym);
@@ -249,10 +286,6 @@ struct symbol *dso__find_symbol_nocache(struct dso *dso, u64 addr);
struct symbol *dso__next_symbol_by_name(struct dso *dso, size_t *idx);
struct symbol *dso__find_symbol_by_name(struct dso *dso, const char *name, size_t *idx);
-struct symbol *dso__first_symbol(struct dso *dso);
-struct symbol *dso__last_symbol(struct dso *dso);
-struct symbol *dso__next_symbol(struct symbol *sym);
-
enum dso_type dso__type_fd(int fd);
int filename__read_build_id(const char *filename, struct build_id *id);
@@ -303,10 +336,9 @@ int dso__synthesize_plt_symbols(struct dso *dso, struct symsrc *ss);
char *dso__demangle_sym(struct dso *dso, int kmodule, const char *elf_name);
-void __symbols__insert(struct rb_root_cached *symbols, struct symbol *sym);
-void symbols__insert(struct rb_root_cached *symbols, struct symbol *sym);
-void symbols__fixup_duplicate(struct rb_root_cached *symbols);
-void symbols__fixup_end(struct rb_root_cached *symbols, bool is_kallsyms);
+int symbols__insert(struct symbols *symbols, struct symbol *sym);
+void symbols__fixup_duplicate(struct symbols *symbols);
+void symbols__fixup_end(struct symbols *symbols, bool is_kallsyms);
typedef int (*mapfn_t)(u64 start, u64 len, u64 pgoff, void *data);
int file__read_maps(int fd, bool exe, mapfn_t mapfn, void *data,
diff --git a/tools/perf/util/symbol_fprintf.c b/tools/perf/util/symbol_fprintf.c
index 8c131325e989..8453a3554cd4 100644
--- a/tools/perf/util/symbol_fprintf.c
+++ b/tools/perf/util/symbol_fprintf.c
@@ -59,15 +59,26 @@ size_t symbol__fprintf_symname(const struct symbol *sym, FILE *fp)
return __symbol__fprintf_symname_offs(sym, NULL, false, false, fp);
}
+struct fprintf_symbols_by_name_args {
+ FILE *fp;
+ size_t ret;
+};
+
+static int fprintf_symbols_by_name_cb(struct symbol *pos, void *data)
+{
+ struct fprintf_symbols_by_name_args *args = data;
+
+ args->ret += fprintf(args->fp, "%s\n", symbol__name(pos));
+ return 0;
+}
+
size_t dso__fprintf_symbols_by_name(struct dso *dso,
FILE *fp)
{
- size_t ret = 0;
-
- for (size_t i = 0; i < dso__symbol_names_len(dso); i++) {
- struct symbol *pos = dso__symbol_names(dso)[i];
+ struct fprintf_symbols_by_name_args args = {
+ .fp = fp,
+ };
- ret += fprintf(fp, "%s\n", symbol__name(pos));
- }
- return ret;
+ dso__for_each_symbol_by_name(dso, fprintf_symbols_by_name_cb, &args);
+ return args.ret;
}
--
2.56.0.313.g2479322e68-goog
^ permalink raw reply [flat|nested] 37+ messages in thread* [PATCH v2 08/18] perf symbol: Add reference counting and DECLARE_RC_STRUCT(symbol)
2026-10-27 23:14 ` [PATCH v2 00/18] perf symbol: Reference counting, sorted arrays, and memory shrinking Ian Rogers
` (6 preceding siblings ...)
2026-10-27 23:15 ` [PATCH v2 07/18] perf symbol: Switch backing storage from rbtree to struct symbols array Ian Rogers
@ 2026-10-27 23:15 ` Ian Rogers
2026-10-27 23:15 ` [PATCH v2 09/18] perf symbol: Optimize symbol sorting with natural-run merge sort and binary insertion Ian Rogers
` (10 subsequent siblings)
18 siblings, 0 replies; 37+ messages in thread
From: Ian Rogers @ 2026-10-27 23:15 UTC (permalink / raw)
To: irogers, acme, namhyung
Cc: adrian.hunter, haghdoost, james.clark, jolsa, linux-kernel,
linux-perf-users, mingo, peterz
Previously, struct symbol pointers returned by dso__find_symbol() and
map__find_symbol() were uncounted borrowed pointers into the DSO's
symbol table. Callers stored these raw pointers in struct map_symbol,
struct addr_location, struct hist_entry, struct callchain_list, and
annotation structures without lifetime tracking, making it unsafe to
evict unreferenced symbols from a DSO when reclaiming memory.
Wrap struct symbol with DECLARE_RC_STRUCT(symbol) and add a refcount_t
refcnt field along with symbol__get(), symbol__put(), and symbol__zput()
helpers:
- Return a counted symbol reference acquired under symbols->lock from
symbol lookup functions (symbols__find(), dso__find_symbol(),
dso__find_symbol_nocache(), map__find_symbol(),
dso__find_symbol_by_name(), dso__next_symbol_by_name(),
map__find_symbol_by_name()).
- Update util/annotate.c and util/db-export.c to hash and compare the
underlying RC_STRUCT(symbol) pointer via RC_CHK_ACCESS(sym) and
RC_CHK_EQUAL() so REFCNT_CHECKING wrapper pointers for the same
symbol resolve to the same hashmap entry, and hold a symbol reference
in struct db_export's sym_db_ids hashmap.
- Update struct addr_location, struct map_symbol, struct call_path,
struct block_info, struct disasm_line, struct annotated_op_loc, struct
c2c_sym_Key, inline_list, srcline, thread_stack, and hist/sort/unwind
consumers to acquire and release symbol references and use
RC_CHK_EQUAL() for pointer identity comparisons.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/bench/symbols.c | 7 +-
tools/perf/builtin-annotate.c | 7 +-
tools/perf/builtin-kallsyms.c | 2 +
tools/perf/builtin-kmem.c | 12 +-
tools/perf/builtin-lock.c | 17 ++-
tools/perf/builtin-script.c | 4 +-
tools/perf/builtin-top.c | 2 +-
tools/perf/builtin-trace.c | 1 +
tools/perf/tests/dlfilter-test.c | 8 +-
tools/perf/tests/hists_cumulate.c | 4 +-
tools/perf/tests/hists_filter.c | 10 +-
tools/perf/tests/hists_link.c | 16 ++-
tools/perf/tests/hists_output.c | 7 +-
tools/perf/tests/kallsyms-split.c | 5 +-
tools/perf/tests/pe-file-parsing.c | 1 +
tools/perf/tests/symbols.c | 14 ++-
tools/perf/tests/vmlinux-kallsyms.c | 10 +-
tools/perf/ui/browsers/hists.c | 11 +-
tools/perf/ui/browsers/map.c | 7 +-
tools/perf/ui/stdio/hist.c | 3 +-
tools/perf/util/addr2line.c | 8 +-
tools/perf/util/addr_location.c | 4 +
.../perf/util/annotate-arch/annotate-alpha.c | 5 +-
.../util/annotate-arch/annotate-loongarch.c | 11 +-
tools/perf/util/annotate-arch/annotate-s390.c | 6 +-
tools/perf/util/annotate.c | 22 ++--
tools/perf/util/block-info.c | 6 +-
tools/perf/util/bpf_lock_contention.c | 25 +++-
tools/perf/util/c2c-function.c | 13 ++-
tools/perf/util/call-path.c | 15 ++-
tools/perf/util/callchain.c | 4 +-
tools/perf/util/capstone.c | 2 +
tools/perf/util/db-export.c | 39 +++++--
tools/perf/util/disasm.c | 22 ++--
tools/perf/util/event.c | 8 +-
tools/perf/util/hist.c | 13 ++-
tools/perf/util/libbfd.c | 7 +-
tools/perf/util/libdw.c | 5 +-
tools/perf/util/llvm.c | 2 +
tools/perf/util/lock-contention.c | 8 +-
tools/perf/util/machine.c | 56 ++++++---
tools/perf/util/map.h | 10 +-
tools/perf/util/map_symbol.c | 4 +-
tools/perf/util/maps.c | 3 +-
tools/perf/util/probe-event.c | 17 ++-
tools/perf/util/python.c | 20 +---
tools/perf/util/sort.c | 2 +-
tools/perf/util/srcline.c | 6 +-
tools/perf/util/symbol-elf.c | 6 +-
tools/perf/util/symbol.c | 107 +++++++++++-------
tools/perf/util/symbol.h | 60 +++++++---
tools/perf/util/thread-stack.c | 12 +-
tools/perf/util/threads.c | 17 ++-
tools/perf/util/unwind-libdw.c | 2 +-
tools/perf/util/unwind-libunwind.c | 2 +-
55 files changed, 478 insertions(+), 219 deletions(-)
diff --git a/tools/perf/bench/symbols.c b/tools/perf/bench/symbols.c
index 5ab71d440e67..8e0621deec83 100644
--- a/tools/perf/bench/symbols.c
+++ b/tools/perf/bench/symbols.c
@@ -159,8 +159,9 @@ static int do_synthetic_symbols_bench(void)
u64 addr = (j < half) ?
(0x100000ULL + (u64)j * 0x40ULL + 4) :
(0x100020ULL + (u64)(j - half) * 0x40ULL + 4);
+ struct symbol *sym = dso__find_symbol_nocache(dso, addr);
- (void)dso__find_symbol_nocache(dso, addr);
+ symbol__put(sym);
}
gettimeofday(&end, NULL);
timersub(&end, &start, &diff);
@@ -183,7 +184,9 @@ static int do_synthetic_symbols_bench(void)
return -ENOMEM;
}
dso__insert_symbol(dso, sym);
- (void)dso__find_symbol_nocache(dso, addr + 2);
+
+ sym = dso__find_symbol_nocache(dso, addr + 2);
+ symbol__put(sym);
}
gettimeofday(&end, NULL);
timersub(&end, &start, &diff);
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c
index 5ee02a25fb9c..6bf88aada13d 100644
--- a/tools/perf/builtin-annotate.c
+++ b/tools/perf/builtin-annotate.c
@@ -134,7 +134,8 @@ static void process_basic_block(struct addr_map_symbol *start,
entry = block_range_iter(&iter);
entry->coverage++;
- entry->sym = sym;
+ symbol__put(entry->sym);
+ entry->sym = symbol__get(sym);
if (branch)
branch->max_coverage = max(branch->max_coverage, entry->coverage);
@@ -174,6 +175,10 @@ static void process_branch_stack(struct branch_stack *bs, struct addr_location *
prev = &bi[i].to;
}
+ for (i = 0; i < (int)bs->nr; i++) {
+ map_symbol__exit(&bi[i].to.ms);
+ map_symbol__exit(&bi[i].from.ms);
+ }
free(bi);
}
diff --git a/tools/perf/builtin-kallsyms.c b/tools/perf/builtin-kallsyms.c
index e99f61c3653e..8f0eaca1099b 100644
--- a/tools/perf/builtin-kallsyms.c
+++ b/tools/perf/builtin-kallsyms.c
@@ -52,6 +52,8 @@ static int __cmd_kallsyms(int argc, const char **argv)
map__unmap_ip(map, symbol__start(symbol)),
map__unmap_ip(map, symbol__end(symbol)),
symbol__start(symbol), symbol__end(symbol));
+ symbol__put(symbol);
+ map__put(map);
}
out:
machine__delete(machine);
diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c
index 7070611e3619..e178be48f269 100644
--- a/tools/perf/builtin-kmem.c
+++ b/tools/perf/builtin-kmem.c
@@ -1044,7 +1044,7 @@ static void __print_slab_result(struct rb_root *root,
struct alloc_stat *data = rb_entry(next, struct alloc_stat,
node);
struct symbol *sym = NULL;
- struct map *map;
+ struct map *map = NULL;
char buf[BUFSIZ];
u64 addr;
@@ -1061,6 +1061,8 @@ static void __print_slab_result(struct rb_root *root,
} else {
snprintf(buf, sizeof(buf), "%#" PRIx64 "", addr);
}
+ symbol__put(sym);
+ map__put(map);
printf(" %-34s |", buf);
printf(" %9llu/%-5lu | %9llu/%-5lu | %8lu | %9lu | %6.3f%%\n",
@@ -1111,12 +1113,11 @@ static void __print_page_alloc_result(struct perf_session *session, int n_lines)
while (next && n_lines--) {
struct page_stat *data;
struct symbol *sym;
- struct map *map;
char buf[32];
const char *caller = buf;
data = rb_entry(next, struct page_stat, node);
- sym = machine__find_kernel_symbol(machine, data->callsite, &map);
+ sym = machine__find_kernel_symbol(machine, data->callsite, NULL);
if (sym)
caller = symbol__name(sym);
else
@@ -1127,6 +1128,7 @@ static void __print_page_alloc_result(struct perf_session *session, int n_lines)
data->nr_alloc, data->order,
migrate_type_str[data->migrate_type],
gfp_len, compact_gfp_string(data->gfp_flags), caller);
+ symbol__put(sym);
next = rb_next(next);
}
@@ -1153,12 +1155,11 @@ static void __print_page_caller_result(struct perf_session *session, int n_lines
while (next && n_lines--) {
struct page_stat *data;
struct symbol *sym;
- struct map *map;
char buf[32];
const char *caller = buf;
data = rb_entry(next, struct page_stat, node);
- sym = machine__find_kernel_symbol(machine, data->callsite, &map);
+ sym = machine__find_kernel_symbol(machine, data->callsite, NULL);
if (sym)
caller = symbol__name(sym);
else
@@ -1169,6 +1170,7 @@ static void __print_page_caller_result(struct perf_session *session, int n_lines
data->nr_alloc, data->order,
migrate_type_str[data->migrate_type],
gfp_len, compact_gfp_string(data->gfp_flags), caller);
+ symbol__put(sym);
next = rb_next(next);
}
diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c
index e52e3785b657..147bbc09ab76 100644
--- a/tools/perf/builtin-lock.c
+++ b/tools/perf/builtin-lock.c
@@ -1003,11 +1003,15 @@ static int report_lock_contention_begin_event(struct perf_sample *sample)
(filters.nr_addrs + 1) * sizeof(*addrs));
if (addrs == NULL) {
pr_warning("memory allocation failure\n");
+ symbol__put(sym);
+ map__put(kmap);
return -ENOMEM;
}
addrs[filters.nr_addrs++] = map__unmap_ip(kmap, symbol__start(sym));
filters.addrs = addrs;
+ symbol__put(sym);
+ map__put(kmap);
}
}
@@ -1057,9 +1061,12 @@ static int report_lock_contention_begin_event(struct perf_sample *sample)
switch (aggr_mode) {
case LOCK_AGGR_ADDR:
- sym = machine__find_kernel_symbol(machine, key, &kmap);
- if (sym)
- name = symbol__name(sym);
+ sym = machine__find_kernel_symbol(machine, key, NULL);
+ if (sym) {
+ scnprintf(buf, sizeof(buf), "%s", symbol__name(sym));
+ name = buf;
+ symbol__put(sym);
+ }
break;
case LOCK_AGGR_CALLER:
name = buf;
@@ -1671,6 +1678,8 @@ static void print_lock_stat_stdio(struct lock_contention *con, struct lock_stat
ip = st->callstack[i];
sym = machine__find_kernel_symbol(con->machine, ip, &kmap);
get_symbol_name_offset(kmap, sym, ip, buf, sizeof(buf));
+ symbol__put(sym);
+ map__put(kmap);
fprintf(lock_output, "\t\t\t%#lx %s\n", (unsigned long)ip, buf);
}
}
@@ -1724,6 +1733,8 @@ static void print_lock_stat_csv(struct lock_contention *con, struct lock_stat *s
ip = st->callstack[i];
sym = machine__find_kernel_symbol(con->machine, ip, &kmap);
get_symbol_name_offset(kmap, sym, ip, buf, sizeof(buf));
+ symbol__put(sym);
+ map__put(kmap);
fprintf(lock_output, "%s %#lx %s", i ? ":" : sep, (unsigned long) ip, buf);
}
fprintf(lock_output, "\n");
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c
index 7559d4d230f2..1237b755cfdf 100644
--- a/tools/perf/builtin-script.c
+++ b/tools/perf/builtin-script.c
@@ -1430,7 +1430,8 @@ static int ip__fprintf_sym(uint64_t addr, struct thread *thread,
if (PRINT_FIELD(SRCLINE))
printed += map__fprintf_srcline(al.map, al.addr, "\t", fp);
printed += fprintf(fp, "\n");
- *lastsym = al.sym;
+ symbol__put(*lastsym);
+ *lastsym = symbol__get(al.sym);
ret = printed;
out:
@@ -1597,6 +1598,7 @@ static int perf_sample__fprintf_brstackinsn(struct perf_sample *sample,
print_srccode(thread, x.cpumode, start + off);
}
out:
+ symbol__put(lastsym);
return printed;
}
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
index d85a49d546eb..11c46c38b7fb 100644
--- a/tools/perf/builtin-top.c
+++ b/tools/perf/builtin-top.c
@@ -209,7 +209,7 @@ static void perf_top__record_precise_ip(struct perf_top *top,
if (sym == NULL || (use_browser == 0 &&
(top->sym_filter_entry == NULL ||
- top->sym_filter_entry->ms.sym != sym)))
+ !RC_CHK_EQUAL(top->sym_filter_entry->ms.sym, sym))))
return;
notes = symbol__annotation(sym);
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c
index 7683b362783a..0dcf042f7ac0 100644
--- a/tools/perf/builtin-trace.c
+++ b/tools/perf/builtin-trace.c
@@ -799,6 +799,7 @@ size_t syscall_arg__scnprintf_ksym(char *bf, size_t size, struct syscall_arg *ar
printed = scnprintf(bf, size, "%s+0x%" PRIx64,
symbol__name(sym), offset);
}
+ symbol__put(sym);
map__put(map);
return printed;
}
diff --git a/tools/perf/tests/dlfilter-test.c b/tools/perf/tests/dlfilter-test.c
index b4649c79c309..1b7f400761bf 100644
--- a/tools/perf/tests/dlfilter-test.c
+++ b/tools/perf/tests/dlfilter-test.c
@@ -273,12 +273,16 @@ static int get_ip_addr(struct test_data *td)
return -1;
sym = map__find_symbol_by_name(map, "foo");
- if (sym)
+ if (sym) {
td->foo = symbol__start(sym);
+ symbol__put(sym);
+ }
sym = map__find_symbol_by_name(map, "bar");
- if (sym)
+ if (sym) {
td->bar = symbol__start(sym);
+ symbol__put(sym);
+ }
map__put(map);
diff --git a/tools/perf/tests/hists_cumulate.c b/tools/perf/tests/hists_cumulate.c
index 95b9a629e59e..16637f1e1e23 100644
--- a/tools/perf/tests/hists_cumulate.c
+++ b/tools/perf/tests/hists_cumulate.c
@@ -114,7 +114,8 @@ static int add_hist_entries(struct hists *hists, struct machine *machine)
fake_samples[i].thread = thread__get(al.thread);
map__put(fake_samples[i].map);
fake_samples[i].map = map__get(al.map);
- fake_samples[i].sym = al.sym;
+ symbol__put(fake_samples[i].sym);
+ fake_samples[i].sym = symbol__get(al.sym);
}
addr_location__exit(&al);
@@ -155,6 +156,7 @@ static void put_fake_samples(void)
size_t i;
for (i = 0; i < ARRAY_SIZE(fake_samples); i++) {
+ symbol__zput(fake_samples[i].sym);
map__zput(fake_samples[i].map);
thread__zput(fake_samples[i].thread);
}
diff --git a/tools/perf/tests/hists_filter.c b/tools/perf/tests/hists_filter.c
index 6c7e4cdc71a7..3bd0b0b270f5 100644
--- a/tools/perf/tests/hists_filter.c
+++ b/tools/perf/tests/hists_filter.c
@@ -93,7 +93,8 @@ static int add_hist_entries(struct evlist *evlist,
fake_samples[i].thread = thread__get(al.thread);
map__put(fake_samples[i].map);
fake_samples[i].map = map__get(al.map);
- fake_samples[i].sym = al.sym;
+ symbol__put(fake_samples[i].sym);
+ fake_samples[i].sym = symbol__get(al.sym);
}
}
addr_location__exit(&al);
@@ -109,8 +110,11 @@ static void put_fake_samples(void)
{
size_t i;
- for (i = 0; i < ARRAY_SIZE(fake_samples); i++)
- map__put(fake_samples[i].map);
+ for (i = 0; i < ARRAY_SIZE(fake_samples); i++) {
+ symbol__zput(fake_samples[i].sym);
+ map__zput(fake_samples[i].map);
+ thread__zput(fake_samples[i].thread);
+ }
}
static int test__hists_filter(struct test_suite *test __maybe_unused, int subtest __maybe_unused)
diff --git a/tools/perf/tests/hists_link.c b/tools/perf/tests/hists_link.c
index cd32c8277627..b91a75a8fa23 100644
--- a/tools/perf/tests/hists_link.c
+++ b/tools/perf/tests/hists_link.c
@@ -99,7 +99,8 @@ static int add_hist_entries(struct evlist *evlist, struct machine *machine)
fake_common_samples[k].thread = thread__get(al.thread);
map__put(fake_common_samples[k].map);
fake_common_samples[k].map = map__get(al.map);
- fake_common_samples[k].sym = al.sym;
+ symbol__put(fake_common_samples[k].sym);
+ fake_common_samples[k].sym = symbol__get(al.sym);
}
for (k = 0; k < ARRAY_SIZE(fake_samples[i]); k++) {
@@ -119,7 +120,8 @@ static int add_hist_entries(struct evlist *evlist, struct machine *machine)
fake_samples[i][k].thread = thread__get(al.thread);
map__put(fake_samples[i][k].map);
fake_samples[i][k].map = map__get(al.map);
- fake_samples[i][k].sym = al.sym;
+ symbol__put(fake_samples[i][k].sym);
+ fake_samples[i][k].sym = symbol__get(al.sym);
}
i++;
}
@@ -136,11 +138,15 @@ static void put_fake_samples(void)
{
size_t i, j;
- for (i = 0; i < ARRAY_SIZE(fake_common_samples); i++)
+ for (i = 0; i < ARRAY_SIZE(fake_common_samples); i++) {
map__put(fake_common_samples[i].map);
+ symbol__put(fake_common_samples[i].sym);
+ }
for (i = 0; i < ARRAY_SIZE(fake_samples); i++) {
- for (j = 0; j < ARRAY_SIZE(fake_samples[0]); j++)
+ for (j = 0; j < ARRAY_SIZE(fake_samples[0]); j++) {
map__put(fake_samples[i][j].map);
+ symbol__put(fake_samples[i][j].sym);
+ }
}
}
@@ -150,7 +156,7 @@ static int find_sample(struct sample *samples, size_t nr_samples,
while (nr_samples--) {
if (RC_CHK_EQUAL(samples->thread, t) &&
RC_CHK_EQUAL(samples->map, m) &&
- samples->sym == s)
+ RC_CHK_EQUAL(samples->sym, s))
return 1;
samples++;
}
diff --git a/tools/perf/tests/hists_output.c b/tools/perf/tests/hists_output.c
index 77fb3a187139..63b9e48ada63 100644
--- a/tools/perf/tests/hists_output.c
+++ b/tools/perf/tests/hists_output.c
@@ -79,7 +79,8 @@ static int add_hist_entries(struct hists *hists, struct machine *machine)
fake_samples[i].thread = al.thread;
map__put(fake_samples[i].map);
fake_samples[i].map = map__get(al.map);
- fake_samples[i].sym = al.sym;
+ symbol__put(fake_samples[i].sym);
+ fake_samples[i].sym = symbol__get(al.sym);
}
addr_location__exit(&al);
@@ -120,8 +121,8 @@ static void put_fake_samples(void)
size_t i;
for (i = 0; i < ARRAY_SIZE(fake_samples); i++) {
- map__put(fake_samples[i].map);
- fake_samples[i].map = NULL;
+ symbol__zput(fake_samples[i].sym);
+ map__zput(fake_samples[i].map);
}
}
diff --git a/tools/perf/tests/kallsyms-split.c b/tools/perf/tests/kallsyms-split.c
index 244daa01bd5d..0e5c029a583e 100644
--- a/tools/perf/tests/kallsyms-split.c
+++ b/tools/perf/tests/kallsyms-split.c
@@ -99,6 +99,7 @@ static int test__kallsyms_split(struct test_suite *test __maybe_unused,
{
struct machine m = { 0 };
struct map *map = NULL;
+ struct symbol *sym;
int ret = TEST_FAIL;
pr_debug("try to create fake root directory\n");
@@ -139,10 +140,12 @@ static int test__kallsyms_split(struct test_suite *test __maybe_unused,
goto out;
}
- if (machine__find_kernel_symbol_by_name(&m, "main_symbol3", &map) == NULL) {
+ sym = machine__find_kernel_symbol_by_name(&m, "main_symbol3", &map);
+ if (sym == NULL) {
pr_debug("FAIL: failed to find a symbol\n");
goto out;
}
+ symbol__put(sym);
if (!RC_CHK_EQUAL(map, machine__kernel_map(&m))) {
pr_debug("FAIL: the symbol is not in the kernel map\n");
diff --git a/tools/perf/tests/pe-file-parsing.c b/tools/perf/tests/pe-file-parsing.c
index 190520ffc3ad..fdb8d2a7e4c6 100644
--- a/tools/perf/tests/pe-file-parsing.c
+++ b/tools/perf/tests/pe-file-parsing.c
@@ -64,6 +64,7 @@ static int run_dir(const char *d)
dso__sort_by_name(dso);
sym = dso__find_symbol_by_name(dso, "main", &idx);
TEST_ASSERT_VAL("Failed to find main", sym);
+ symbol__put(sym);
dso__delete(dso);
return TEST_OK;
diff --git a/tools/perf/tests/symbols.c b/tools/perf/tests/symbols.c
index 8ccf977aa9f7..d70adbad3978 100644
--- a/tools/perf/tests/symbols.c
+++ b/tools/perf/tests/symbols.c
@@ -260,18 +260,19 @@ static const struct kallsyms_sym kallsyms_syms[] = {
static int check_symbol(struct dso *dso, u64 addr, const char *name, u64 end)
{
struct symbol *sym = dso__find_symbol_nocache(dso, addr);
+ int ret = TEST_OK;
if (!sym || strcmp(symbol__name(sym), name)) {
pr_debug("%#" PRIx64 ": expected %s, got %s\n", addr, name,
sym ? symbol__name(sym) : "no symbol");
- return TEST_FAIL;
- }
- if (symbol__end(sym) != end) {
+ ret = TEST_FAIL;
+ } else if (symbol__end(sym) != end) {
pr_debug("%s: expected end %#" PRIx64 ", got %#" PRIx64 "\n",
name, end, symbol__end(sym));
- return TEST_FAIL;
+ ret = TEST_FAIL;
}
- return TEST_OK;
+ symbol__put(sym);
+ return ret;
}
static int test__kallsyms_fixup_end(struct test_suite *test __maybe_unused,
@@ -289,7 +290,8 @@ static int test__kallsyms_fixup_end(struct test_suite *test __maybe_unused,
if (!sym)
goto out;
- symbols__insert(dso__symbols(dso), sym);
+ if (symbols__insert(dso__symbols(dso), sym))
+ goto out;
}
symbols__fixup_end(dso__symbols(dso), true);
diff --git a/tools/perf/tests/vmlinux-kallsyms.c b/tools/perf/tests/vmlinux-kallsyms.c
index bef40c194d70..7454f13dba06 100644
--- a/tools/perf/tests/vmlinux-kallsyms.c
+++ b/tools/perf/tests/vmlinux-kallsyms.c
@@ -208,7 +208,7 @@ static int test__vmlinux_matches_kallsyms_sym_cb(struct symbol *sym, void *data)
mem_end = map__unmap_ip(args->vmlinux_map, symbol__end(sym));
first_pair = machine__find_kernel_symbol(&args->kallsyms, mem_start, NULL);
- pair = first_pair;
+ pair = symbol__get(first_pair);
if (pair && UM(symbol__start(pair)) == mem_start) {
next_pair:
@@ -237,8 +237,11 @@ static int test__vmlinux_matches_kallsyms_sym_cb(struct symbol *sym, void *data)
* possible to get proper function end from
* kallsyms.
*/
+ symbol__put(pair);
+ symbol__put(first_pair);
return 0;
}
+ symbol__put(pair);
pair = machine__find_kernel_symbol_by_name(&args->kallsyms,
symbol__name(sym), NULL);
if (pair) {
@@ -247,14 +250,17 @@ static int test__vmlinux_matches_kallsyms_sym_cb(struct symbol *sym, void *data)
pr_debug("WARN: %#" PRIx64 ": diff name v: %s k: %s\n",
mem_start, symbol__name(sym), symbol__name(pair));
+ symbol__put(pair);
} else {
pr_debug("WARN: %#" PRIx64 ": diff name v: %s k: %s\n",
mem_start, symbol__name(sym), symbol__name(first_pair));
}
+ symbol__put(first_pair);
return 0;
}
-
+ symbol__put(pair);
+ symbol__put(first_pair);
if (mem_start == map__end(args->kallsyms.vmlinux_map)) {
/*
* Ignore aliases to _etext, i.e. to the end of the kernel text area,
diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c
index 5147b7dca256..0267a91b06e7 100644
--- a/tools/perf/ui/browsers/hists.c
+++ b/tools/perf/ui/browsers/hists.c
@@ -2527,12 +2527,14 @@ static struct symbol *symbol__new_unresolved(u64 addr, struct map *map)
if (sym) {
src = symbol__hists(sym, 1);
if (!src) {
- symbol__delete(sym);
+ symbol__put(sym);
return NULL;
}
- if (dso__insert_symbol(map__dso(map), sym))
+ if (dso__insert_symbol(map__dso(map), symbol__get(sym))) {
+ symbol__put(sym);
return NULL;
+ }
}
return sym;
@@ -2819,7 +2821,7 @@ add_script_opt_2(struct popup_action *act, char **optstr,
}
act->ms.thread = thread__get(thread);
- act->ms.sym = sym;
+ act->ms.sym = symbol__get(sym);
act->fn = do_run_script;
return 1;
}
@@ -3208,6 +3210,7 @@ static int evsel__hists_browse(struct evsel *evsel, int nr_events, const char *h
memset(&hotkey_act, 0, sizeof(hotkey_act));
map_symbol__copy(&hotkey_act.ms, &source_ms);
+ symbol__zput(source_ms.sym);
do_annotate(browser, &hotkey_act);
map_symbol__exit(&hotkey_act.ms);
} else {
@@ -3410,7 +3413,7 @@ static int evsel__hists_browse(struct evsel *evsel, int nr_events, const char *h
&options[nr_options],
&bi->from.ms,
bi->from.al_addr);
- if (bi->to.ms.sym != bi->from.ms.sym)
+ if (!RC_CHK_EQUAL(bi->to.ms.sym, bi->from.ms.sym))
nr_options += add_annotate_opt(&actions[nr_options],
&options[nr_options],
&bi->to.ms,
diff --git a/tools/perf/ui/browsers/map.c b/tools/perf/ui/browsers/map.c
index 71f6161ed27a..3ef62c512355 100644
--- a/tools/perf/ui/browsers/map.c
+++ b/tools/perf/ui/browsers/map.c
@@ -64,12 +64,13 @@ static int map_browser__search(struct map_browser *browser)
* cursor at that entry.
*/
for (u32 idx = 0; idx < browser->b.nr_entries; ++idx) {
- if (entries[idx] == sym) {
+ if (RC_CHK_EQUAL(entries[idx], sym)) {
browser->b.top = &entries[idx];
browser->b.index = browser->b.top_idx = idx;
break;
}
}
+ symbol__put(sym);
} else
ui_helpline__fpush("%s not found!", target);
@@ -135,7 +136,7 @@ int map__browse(struct map *map)
for (unsigned int idx = 0; idx < nr_entries; idx++) {
struct symbol *pos = symbols->symbols[idx];
- entries[idx] = pos;
+ entries[idx] = symbol__get(pos);
if (maxaddr < symbol__end(pos))
maxaddr = symbol__end(pos);
}
@@ -150,6 +151,8 @@ int map__browse(struct map *map)
mb.addrlen = snprintf(tmp, sizeof(tmp), "%" PRIx64, maxaddr);
ret = map_browser__run(&mb);
+ for (unsigned int idx = 0; idx < nr_entries; idx++)
+ symbol__put(entries[idx]);
free(entries);
return ret;
}
diff --git a/tools/perf/ui/stdio/hist.c b/tools/perf/ui/stdio/hist.c
index f80cc73909ed..aedc5eb8b1a4 100644
--- a/tools/perf/ui/stdio/hist.c
+++ b/tools/perf/ui/stdio/hist.c
@@ -908,7 +908,8 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,
free(line);
out:
- zfree(&rem_sq_bracket);
+ symbol__zput(rem_sq_bracket);
+ rem_hits.ms.sym = NULL;
return ret;
}
diff --git a/tools/perf/util/addr2line.c b/tools/perf/util/addr2line.c
index 960dd48701bb..2afa8719ac41 100644
--- a/tools/perf/util/addr2line.c
+++ b/tools/perf/util/addr2line.c
@@ -274,8 +274,14 @@ static int inline_list__append_record(struct dso *dso,
unsigned int line_nr)
{
struct symbol *inline_sym = new_inline_sym(dso, sym, function);
+ char *srcline = srcline_from_fileline(filename, line_nr);
- return inline_list__append(inline_sym, srcline_from_fileline(filename, line_nr), node);
+ if (inline_list__append(inline_sym, srcline, node) != 0) {
+ zfree_srcline(&srcline);
+ symbol__put(inline_sym);
+ return -1;
+ }
+ return 0;
}
int cmd__addr2line(const char *dso_name, u64 addr,
diff --git a/tools/perf/util/addr_location.c b/tools/perf/util/addr_location.c
index 57e8217a00f9..0f21a01a3843 100644
--- a/tools/perf/util/addr_location.c
+++ b/tools/perf/util/addr_location.c
@@ -2,6 +2,7 @@
#include "addr_location.h"
#include "map.h"
#include "maps.h"
+#include "symbol.h"
#include "thread.h"
void addr_location__init(struct addr_location *al)
@@ -29,13 +30,16 @@ void addr_location__exit(struct addr_location *al)
{
map__zput(al->map);
thread__zput(al->thread);
+ symbol__zput(al->sym);
}
void addr_location__copy(struct addr_location *dst, struct addr_location *src)
{
thread__put(dst->thread);
map__put(dst->map);
+ symbol__put(dst->sym);
*dst = *src;
dst->thread = thread__get(src->thread);
dst->map = map__get(src->map);
+ dst->sym = symbol__get(src->sym);
}
diff --git a/tools/perf/util/annotate-arch/annotate-alpha.c b/tools/perf/util/annotate-arch/annotate-alpha.c
index ccb13e2338a2..fc4fcbf27c82 100644
--- a/tools/perf/util/annotate-arch/annotate-alpha.c
+++ b/tools/perf/util/annotate-arch/annotate-alpha.c
@@ -88,8 +88,9 @@ static int alpha_call__parse(const struct arch *arch, struct ins_operands *ops,
if (maps__find_ams(thread__maps(ms->thread), &target) == 0 &&
map__rip_2objdump(target.ms.map,
- map__map_ip(target.ms.map, target.addr)) == ops->target.addr)
- ops->target.sym = target.ms.sym;
+ map__map_ip(target.ms.map, target.addr)) == ops->target.addr &&
+ !RC_CHK_EQUAL(target.ms.sym, ms->sym))
+ ops->target.sym = symbol__get(target.ms.sym);
addr_map_symbol__exit(&target);
return 0;
diff --git a/tools/perf/util/annotate-arch/annotate-loongarch.c b/tools/perf/util/annotate-arch/annotate-loongarch.c
index d275730080b7..775bf44ce495 100644
--- a/tools/perf/util/annotate-arch/annotate-loongarch.c
+++ b/tools/perf/util/annotate-arch/annotate-loongarch.c
@@ -56,8 +56,10 @@ static int loongarch_call__parse(const struct arch *arch, struct ins_operands *o
};
if (maps__find_ams(thread__maps(ms->thread), &target) == 0 &&
- map__rip_2objdump(target.ms.map, map__map_ip(target.ms.map, target.addr)) == ops->target.addr)
- ops->target.sym = target.ms.sym;
+ map__rip_2objdump(target.ms.map,
+ map__map_ip(target.ms.map, target.addr)) == ops->target.addr &&
+ !RC_CHK_EQUAL(target.ms.sym, ms->sym))
+ ops->target.sym = symbol__get(target.ms.sym);
addr_map_symbol__exit(&target);
return 0;
@@ -99,9 +101,10 @@ static int loongarch_jump__parse(const struct arch *arch, struct ins_operands *o
ops->target.outside = target.addr < start || target.addr >= end;
- if (maps__find_ams(thread__maps(ms->thread), &target) == 0 &&
+ if (ops->target.outside &&
+ maps__find_ams(thread__maps(ms->thread), &target) == 0 &&
map__rip_2objdump(target.ms.map, map__map_ip(target.ms.map, target.addr)) == ops->target.addr)
- ops->target.sym = target.ms.sym;
+ ops->target.sym = symbol__get(target.ms.sym);
if (!ops->target.outside) {
ops->target.offset = target.addr - start;
diff --git a/tools/perf/util/annotate-arch/annotate-s390.c b/tools/perf/util/annotate-arch/annotate-s390.c
index af9cabd0a586..ae88e556ee89 100644
--- a/tools/perf/util/annotate-arch/annotate-s390.c
+++ b/tools/perf/util/annotate-arch/annotate-s390.c
@@ -51,8 +51,10 @@ static int s390_call__parse(const struct arch *arch, struct ins_operands *ops,
};
if (maps__find_ams(thread__maps(ms->thread), &target) == 0 &&
- map__rip_2objdump(target.ms.map, map__map_ip(target.ms.map, target.addr)) == ops->target.addr)
- ops->target.sym = target.ms.sym;
+ map__rip_2objdump(target.ms.map,
+ map__map_ip(target.ms.map, target.addr)) == ops->target.addr &&
+ !RC_CHK_EQUAL(target.ms.sym, ms->sym))
+ ops->target.sym = symbol__get(target.ms.sym);
addr_map_symbol__exit(&target);
return 0;
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c
index e4332800fda4..bb9962fec076 100644
--- a/tools/perf/util/annotate.c
+++ b/tools/perf/util/annotate.c
@@ -198,15 +198,22 @@ static bool sym_ptr_equal(long key1, long key2, void *ctx __maybe_unused)
struct annotation *symbol__annotation(struct symbol *sym)
{
struct annotation *notes = NULL;
- long val = 0;
+ long key, val = 0;
if (!sym)
return NULL;
+ /*
+ * Key on the underlying RC_STRUCT(symbol) pointer so all
+ * REFCNT_CHECKING wrapper pointers for the same symbol map to the
+ * same annotation entry.
+ */
+ key = (long)RC_CHK_ACCESS(sym);
+
/* Fast path: look up existing annotation under read lock if flagged. */
if (symbol__annotated(sym)) {
down_read(annotations_lock());
- if (annotations && hashmap__find(annotations, (long)sym, &val))
+ if (annotations && hashmap__find(annotations, key, &val))
notes = (struct annotation *)val;
up_read(annotations_lock());
if (notes)
@@ -221,7 +228,7 @@ struct annotation *symbol__annotation(struct symbol *sym)
up_write(annotations_lock());
return &empty_annotation;
}
- } else if (hashmap__find(annotations, (long)sym, &val)) {
+ } else if (hashmap__find(annotations, key, &val)) {
notes = (struct annotation *)val;
up_write(annotations_lock());
return notes;
@@ -233,7 +240,7 @@ struct annotation *symbol__annotation(struct symbol *sym)
return &empty_annotation;
}
annotation__init(notes);
- if (hashmap__add(annotations, (long)sym, notes) < 0) {
+ if (hashmap__add(annotations, key, notes) < 0) {
free(notes);
if (hashmap__size(annotations) == 0) {
hashmap__free(annotations);
@@ -250,14 +257,15 @@ struct annotation *symbol__annotation(struct symbol *sym)
void symbol__annotation_delete(struct symbol *sym)
{
struct annotation *notes = NULL;
- long val = 0;
+ long key, val = 0;
/* Unannotated symbols avoid taking annotations_lock on teardown. */
if (!sym || !symbol__annotated(sym))
return;
+ key = (long)RC_CHK_ACCESS(sym);
down_write(annotations_lock());
- if (annotations && hashmap__delete(annotations, (long)sym, NULL, &val)) {
+ if (annotations && hashmap__delete(annotations, key, NULL, &val)) {
notes = (struct annotation *)val;
if (hashmap__size(annotations) == 0) {
hashmap__free(annotations);
@@ -571,7 +579,7 @@ int addr_map_symbol__account_cycles(struct addr_map_symbol *ams,
* it starts on the function start.
*/
if (start &&
- (start->ms.sym == ams->ms.sym ||
+ (RC_CHK_EQUAL(start->ms.sym, ams->ms.sym) ||
(ams->ms.sym &&
start->addr == symbol__start(ams->ms.sym) + map__start(ams->ms.map))))
saddr = start->al_addr;
diff --git a/tools/perf/util/block-info.c b/tools/perf/util/block-info.c
index f6ac71a74e6d..bdd7f17ad70c 100644
--- a/tools/perf/util/block-info.c
+++ b/tools/perf/util/block-info.c
@@ -64,8 +64,10 @@ static struct block_info *block_info__new(unsigned int br_cntr_nr)
void block_info__delete(struct block_info *bi)
{
- if (bi)
+ if (bi) {
+ symbol__zput(bi->sym);
free(bi->br_cntr);
+ }
free(bi);
}
@@ -105,7 +107,7 @@ static void init_block_info(struct block_info *bi, struct symbol *sym,
u64 total_cycles, unsigned int br_cntr_nr,
u64 *br_cntr, struct evsel *evsel)
{
- bi->sym = sym;
+ bi->sym = symbol__get(sym);
bi->start = ch->start;
bi->end = offset;
bi->cycles = ch->cycles;
diff --git a/tools/perf/util/bpf_lock_contention.c b/tools/perf/util/bpf_lock_contention.c
index 2b799bf7a9a2..cb4a2546364b 100644
--- a/tools/perf/util/bpf_lock_contention.c
+++ b/tools/perf/util/bpf_lock_contention.c
@@ -144,6 +144,7 @@ static void init_numa_data(struct lock_contention *con)
&kmap);
if (sym) {
skel->rodata->contig_page_data_addr = map__unmap_ip(kmap, symbol__start(sym));
+ symbol__put(sym);
map__put(kmap);
return;
}
@@ -160,6 +161,7 @@ static void init_numa_data(struct lock_contention *con)
return;
skel->rodata->node_data_addr = map__unmap_ip(kmap, symbol__start(sym));
+ symbol__put(sym);
map__put(kmap);
/* get the number of online nodes using the last node number + 1 */
@@ -263,11 +265,15 @@ int lock_contention_prepare(struct lock_contention *con)
(con->filters->nr_addrs + 1) * sizeof(*addrs));
if (addrs == NULL) {
pr_warning("memory allocation failure\n");
+ symbol__put(sym);
+ map__put(kmap);
continue;
}
addrs[con->filters->nr_addrs++] = map__unmap_ip(kmap, symbol__start(sym));
con->filters->addrs = addrs;
+ symbol__put(sym);
+ map__put(kmap);
}
naddrs = con->filters->nr_addrs ?: has_mmap_lock;
skel->rodata->has_addr = 1;
@@ -289,6 +295,8 @@ int lock_contention_prepare(struct lock_contention *con)
}
con->delays[i].addr = map__unmap_ip(kmap, symbol__start(sym));
+ symbol__put(sym);
+ map__put(kmap);
}
skel->rodata->lock_delay = 1;
bpf_map__set_max_entries(skel->maps.lock_delays, con->nr_delays);
@@ -606,9 +614,12 @@ static const char *lock_contention_get_name(struct lock_contention *con,
return "siglock";
/* global locks with symbols */
- sym = machine__find_kernel_symbol(machine, key->lock_addr_or_cgroup, &kmap);
- if (sym)
- return symbol__name(sym);
+ sym = machine__find_kernel_symbol(machine, key->lock_addr_or_cgroup, NULL);
+ if (sym) {
+ snprintf(name_buf, sizeof(name_buf), "%s", symbol__name(sym));
+ symbol__put(sym);
+ return name_buf;
+ }
/* try semi-global locks collected separately */
if (!bpf_map_lookup_elem(lock_fd, &key->lock_addr_or_cgroup, &flags)) {
@@ -655,12 +666,14 @@ static const char *lock_contention_get_name(struct lock_contention *con,
offset = map__map_ip(kmap, addr) - symbol__start(sym);
if (offset == 0)
- return symbol__name(sym);
-
- snprintf(name_buf, sizeof(name_buf), "%s+%#lx", symbol__name(sym), offset);
+ snprintf(name_buf, sizeof(name_buf), "%s", symbol__name(sym));
+ else
+ snprintf(name_buf, sizeof(name_buf), "%s+%#lx", symbol__name(sym), offset);
+ symbol__put(sym);
} else {
snprintf(name_buf, sizeof(name_buf), "%#lx", (unsigned long)addr);
}
+ map__put(kmap);
return name_buf;
}
diff --git a/tools/perf/util/c2c-function.c b/tools/perf/util/c2c-function.c
index b548c76e3016..70f96adc6dc1 100644
--- a/tools/perf/util/c2c-function.c
+++ b/tools/perf/util/c2c-function.c
@@ -976,13 +976,13 @@ c2c_function_hists__level1_entry(struct symbol *sym,
/* mem_info__put() will map_symbol__exit() these, so take refs. */
mem_info__iaddr(mi)->ms.thread = thread__get(detail_he->ms.thread);
mem_info__iaddr(mi)->ms.map = map__get(detail_he->ms.map);
- mem_info__iaddr(mi)->ms.sym = sym;
+ mem_info__iaddr(mi)->ms.sym = symbol__get(sym);
mem_info__daddr(mi)->addr = 0;
addr_location__init(&al);
al.thread = thread__get(synthetic_thread);
al.map = map__get(detail_he->ms.map);
- al.sym = sym;
+ al.sym = symbol__get(sym);
al.addr = sym_start;
al.level = detail_he->level;
al.cpumode = detail_he->cpumode;
@@ -1064,9 +1064,12 @@ c2c_function_hists__level2_entry(struct c2c_hist_entry *level1_c2c,
return NULL;
/* Key this level by the looked-up symbol, not detail_he's. */
- level2_c2c->he.ms.sym = sym;
- if (level2_c2c->he.mem_info)
- mem_info__iaddr(level2_c2c->he.mem_info)->ms.sym = sym;
+ symbol__put(level2_c2c->he.ms.sym);
+ level2_c2c->he.ms.sym = symbol__get(sym);
+ if (level2_c2c->he.mem_info) {
+ symbol__put(mem_info__iaddr(level2_c2c->he.mem_info)->ms.sym);
+ mem_info__iaddr(level2_c2c->he.mem_info)->ms.sym = symbol__get(sym);
+ }
c2c_child_entry__insert(level1_he, &level2_c2c->he, p, parent, leftmost);
diff --git a/tools/perf/util/call-path.c b/tools/perf/util/call-path.c
index 5c60b8be1cf6..5927068fd9b6 100644
--- a/tools/perf/util/call-path.c
+++ b/tools/perf/util/call-path.c
@@ -10,12 +10,13 @@
#include <stdlib.h>
#include "call-path.h"
+#include "symbol.h"
static void call_path__init(struct call_path *cp, struct call_path *parent,
struct symbol *sym, u64 ip, bool in_kernel)
{
cp->parent = parent;
- cp->sym = sym;
+ cp->sym = symbol__get(sym);
cp->ip = sym ? 0 : ip;
cp->db_id = 0;
cp->in_kernel = in_kernel;
@@ -38,8 +39,15 @@ struct call_path_root *call_path_root__new(void)
void call_path_root__free(struct call_path_root *cpr)
{
struct call_path_block *pos, *n;
+ size_t i = 0;
+ if (!cpr)
+ return;
+
+ symbol__zput(cpr->call_path.sym);
list_for_each_entry_safe(pos, n, &cpr->blocks, node) {
+ for (size_t j = 0; j < CALL_PATH_BLOCK_SIZE && i < cpr->next; j++, i++)
+ symbol__zput(pos->cp[j].sym);
list_del_init(&pos->node);
free(pos);
}
@@ -94,10 +102,11 @@ struct call_path *call_path__findnew(struct call_path_root *cpr,
node_parent = *p;
cp = rb_entry(node_parent, struct call_path, rb_node);
- if (cp->sym == sym && cp->ip == ip)
+ if (RC_CHK_EQUAL(cp->sym, sym) && cp->ip == ip)
return cp;
- if (sym < cp->sym || (sym == cp->sym && ip < cp->ip))
+ if ((sym ? RC_CHK_ACCESS(sym) : NULL) < (cp->sym ? RC_CHK_ACCESS(cp->sym) : NULL) ||
+ (RC_CHK_EQUAL(sym, cp->sym) && ip < cp->ip))
p = &(*p)->rb_left;
else
p = &(*p)->rb_right;
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c
index 24c176905c0e..fdb9a7b8639f 100644
--- a/tools/perf/util/callchain.c
+++ b/tools/perf/util/callchain.c
@@ -1094,6 +1094,7 @@ merge_chain_branch(struct callchain_cursor *cursor,
struct map_symbol ms = {
.thread = thread__get(list->ms.thread),
.map = map__get(list->ms.map),
+ .sym = symbol__get(list->ms.sym),
};
callchain_cursor_append(cursor, list->ip, &ms, false, NULL, 0, 0, 0, list->srcline);
list_del_init(&list->list);
@@ -1204,7 +1205,8 @@ int fill_callchain_info(struct addr_location *al, struct callchain_cursor_node *
map__put(al->map);
al->map = map__get(node->ms.map);
- al->sym = node->ms.sym;
+ symbol__put(al->sym);
+ al->sym = symbol__get(node->ms.sym);
al->srcline = node->srcline;
al->addr = node->ip;
diff --git a/tools/perf/util/capstone.c b/tools/perf/util/capstone.c
index 422a48385b78..8178d5a52bcd 100644
--- a/tools/perf/util/capstone.c
+++ b/tools/perf/util/capstone.c
@@ -373,6 +373,7 @@ static void print_capstone_detail(struct cs_insn *insn, char *buf, size_t len,
scnprintf(buf, len, "\t# %"PRIx64" <%s+%#"PRIx64">",
orig_addr, symbol__name(sym), sym_offset);
}
+ symbol__put(sym);
map__put(found_map);
break;
}
@@ -459,6 +460,7 @@ static void format_capstone_insn_arm64(struct cs_insn *insn, char *buf,
scnprintf(last_imm, len, "%"PRIx64" <%s+%#"PRIx64">",
addr, symbol__name(sym), sym_offset);
}
+ symbol__put(sym);
map__put(found_map);
}
}
diff --git a/tools/perf/util/db-export.c b/tools/perf/util/db-export.c
index 97ce6061a77d..f520c3b93a7e 100644
--- a/tools/perf/util/db-export.c
+++ b/tools/perf/util/db-export.c
@@ -13,6 +13,8 @@
#include <linux/err.h>
+#include <internal/rc_check.h>
+
#include "call-path.h"
#include "callchain.h"
#include "comm.h"
@@ -26,14 +28,24 @@
#include "thread-stack.h"
#include "thread.h"
+/*
+ * Hash and compare the underlying RC_STRUCT(symbol) pointer so all
+ * REFCNT_CHECKING wrapper pointers for the same symbol resolve to the same
+ * exported database ID.
+ */
static size_t sym_ptr_hash(long key, void *ctx __maybe_unused)
{
- return hash_bits((size_t)key, 16);
+ const struct symbol *sym = (const struct symbol *)key;
+
+ return hash_bits((size_t)RC_CHK_ACCESS(sym), 16);
}
static bool sym_ptr_equal(long key1, long key2, void *ctx __maybe_unused)
{
- return key1 == key2;
+ const struct symbol *sym1 = (const struct symbol *)key1;
+ const struct symbol *sym2 = (const struct symbol *)key2;
+
+ return RC_CHK_EQUAL(sym1, sym2);
}
int db_export__init(struct db_export *dbe)
@@ -44,8 +56,15 @@ int db_export__init(struct db_export *dbe)
void db_export__exit(struct db_export *dbe)
{
- hashmap__free(dbe->sym_db_ids);
- dbe->sym_db_ids = NULL;
+ if (dbe->sym_db_ids) {
+ struct hashmap_entry *cur;
+ size_t bkt;
+
+ hashmap__for_each_entry(dbe->sym_db_ids, cur, bkt)
+ symbol__put((struct symbol *)cur->pkey);
+ hashmap__free(dbe->sym_db_ids);
+ dbe->sym_db_ids = NULL;
+ }
call_return_processor__free(dbe->crp);
dbe->crp = NULL;
}
@@ -191,6 +210,7 @@ int db_export__symbol(struct db_export *dbe, struct symbol *sym,
struct dso *dso)
{
long db_id;
+ struct symbol *sym_key;
if (db_export__symbol_db_id(dbe, sym))
return 0;
@@ -207,8 +227,11 @@ int db_export__symbol(struct db_export *dbe, struct symbol *sym,
}
db_id = ++dbe->symbol_last_db_id;
- if (hashmap__set(dbe->sym_db_ids, (long)sym, db_id, NULL, NULL))
+ sym_key = symbol__get(sym);
+ if (hashmap__set(dbe->sym_db_ids, (long)sym_key, db_id, NULL, NULL)) {
+ symbol__put(sym_key);
return -ENOMEM;
+ }
if (dbe->export_symbol)
return dbe->export_symbol(dbe, sym, dso);
@@ -231,8 +254,8 @@ static int db_ids_from_al(struct db_export *dbe, struct addr_location *al,
if (!al->sym) {
al->sym = symbol__new(al->addr, 0, 0, 0, "unknown");
- if (al->sym && dso__insert_symbol(dso, al->sym))
- al->sym = NULL;
+ if (al->sym && dso__insert_symbol(dso, symbol__get(al->sym)))
+ symbol__zput(al->sym);
}
if (al->sym) {
@@ -292,7 +315,7 @@ static struct call_path *call_path_from_sample(struct db_export *dbe,
* db_ids_from_al() to perform the export.
*/
addr_location__init(&al);
- al.sym = node->ms.sym;
+ al.sym = symbol__get(node->ms.sym);
al.map = map__get(node->ms.map);
al.addr = node->ip;
al.thread = thread__get(thread);
diff --git a/tools/perf/util/disasm.c b/tools/perf/util/disasm.c
index 9b3e476bab6d..1a96e02e9288 100644
--- a/tools/perf/util/disasm.c
+++ b/tools/perf/util/disasm.c
@@ -228,6 +228,7 @@ static void ins_ops__delete(struct ins_operands *ops)
zfree(&ops->source.name);
zfree(&ops->target.raw);
zfree(&ops->target.name);
+ symbol__zput(ops->target.sym);
}
int ins__raw_scnprintf(const struct ins *ins, char *bf, size_t size,
@@ -289,8 +290,10 @@ static int call__parse(const struct arch *arch, struct ins_operands *ops, struct
};
if (maps__find_ams(thread__maps(ms->thread), &target) == 0 &&
- map__rip_2objdump(target.ms.map, map__map_ip(target.ms.map, target.addr)) == ops->target.addr)
- ops->target.sym = target.ms.sym;
+ map__rip_2objdump(target.ms.map,
+ map__map_ip(target.ms.map, target.addr)) == ops->target.addr &&
+ !RC_CHK_EQUAL(target.ms.sym, ms->sym))
+ ops->target.sym = symbol__get(target.ms.sym);
addr_map_symbol__exit(&target);
return 0;
@@ -424,9 +427,10 @@ static int jump__parse(const struct arch *arch, struct ins_operands *ops, struct
* Actual navigation will come next, with further understanding of how
* the symbol searching and disassembly should be done.
*/
- if (maps__find_ams(thread__maps(ms->thread), &target) == 0 &&
+ if (ops->target.outside &&
+ maps__find_ams(thread__maps(ms->thread), &target) == 0 &&
map__rip_2objdump(target.ms.map, map__map_ip(target.ms.map, target.addr)) == ops->target.addr)
- ops->target.sym = target.ms.sym;
+ ops->target.sym = symbol__get(target.ms.sym);
if (!ops->target.outside) {
ops->target.offset = target.addr - start;
@@ -473,12 +477,14 @@ int jump__scnprintf(const struct ins *ins, char *bf, size_t size,
ops->target.offset);
}
-void jump__delete(struct ins_operands *ops __maybe_unused)
+void jump__delete(struct ins_operands *ops)
{
/*
* The ops->jump.raw_comment and ops->jump.raw_func_start belong to the
* raw string, don't free them.
*/
+ if (ops)
+ symbol__zput(ops->target.sym);
}
const struct ins_ops jump_ops = {
@@ -572,6 +578,7 @@ static void lock__delete(struct ins_operands *ops)
zfree(&ops->locked.ins.name);
zfree(&ops->target.raw);
zfree(&ops->target.name);
+ symbol__zput(ops->target.sym);
}
const struct ins_ops lock_ops = {
@@ -1090,8 +1097,9 @@ static int symbol__parse_objdump_line(struct symbol *sym,
};
if (!maps__find_ams(thread__maps(args->ms->thread), &target) &&
- symbol__start(target.ms.sym) == target.al_addr)
- dl->ops.target.sym = target.ms.sym;
+ symbol__start(target.ms.sym) == target.al_addr &&
+ !RC_CHK_EQUAL(target.ms.sym, sym))
+ dl->ops.target.sym = symbol__get(target.ms.sym);
addr_map_symbol__exit(&target);
}
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c
index 939307796a88..a71267af4f3c 100644
--- a/tools/perf/util/event.c
+++ b/tools/perf/util/event.c
@@ -770,7 +770,7 @@ struct map *thread__find_map_fb(struct thread *thread, u8 cpumode, u64 addr,
struct symbol *thread__find_symbol(struct thread *thread, u8 cpumode,
u64 addr, struct addr_location *al)
{
- al->sym = NULL;
+ symbol__zput(al->sym);
if (thread__find_map(thread, cpumode, addr, al))
al->sym = map__find_symbol(al->map, al->addr);
return al->sym;
@@ -779,7 +779,7 @@ struct symbol *thread__find_symbol(struct thread *thread, u8 cpumode,
struct symbol *thread__find_symbol_fb(struct thread *thread, u8 cpumode,
u64 addr, struct addr_location *al)
{
- al->sym = NULL;
+ symbol__zput(al->sym);
if (thread__find_map_fb(thread, cpumode, addr, al))
al->sym = map__find_symbol(al->map, al->addr);
return al->sym;
@@ -829,7 +829,7 @@ int machine__resolve(struct machine *machine, struct addr_location *al,
thread__put(thread);
thread = NULL;
- al->sym = NULL;
+ symbol__zput(al->sym);
al->cpu = sample->cpu;
al->socket = -1;
al->srcline = NULL;
@@ -937,7 +937,7 @@ void thread__resolve(struct thread *thread, struct addr_location *al,
thread__find_map_fb(thread, sample->cpumode, sample->addr, al);
al->cpu = sample->cpu;
- al->sym = NULL;
+ symbol__zput(al->sym);
if (al->map)
al->sym = map__find_symbol(al->map, al->addr);
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c
index 8a1a5e36d7cd..badae244638c 100644
--- a/tools/perf/util/hist.c
+++ b/tools/perf/util/hist.c
@@ -543,6 +543,7 @@ static int hist_entry__init(struct hist_entry *he,
he->ms.thread = thread__get(he->ms.thread);
he->ms.map = map__get(he->ms.map);
+ he->ms.sym = symbol__get(he->ms.sym);
if (he->branch_info) {
/*
@@ -559,8 +560,10 @@ static int hist_entry__init(struct hist_entry *he,
he->branch_info->from.ms.thread = thread__get(he->branch_info->from.ms.thread);
he->branch_info->from.ms.map = map__get(he->branch_info->from.ms.map);
+ he->branch_info->from.ms.sym = symbol__get(he->branch_info->from.ms.sym);
he->branch_info->to.ms.thread = thread__get(he->branch_info->to.ms.thread);
he->branch_info->to.ms.map = map__get(he->branch_info->to.ms.map);
+ he->branch_info->to.ms.sym = symbol__get(he->branch_info->to.ms.sym);
}
if (he->mem_info) {
@@ -593,6 +596,7 @@ static int hist_entry__init(struct hist_entry *he,
INIT_LIST_HEAD(&he->pairs.node);
he->thread = thread__get(he->thread);
+ he->parent = symbol__get(he->parent);
he->hroot_in = RB_ROOT_CACHED;
he->hroot_out = RB_ROOT_CACHED;
@@ -728,6 +732,8 @@ static struct hist_entry *hists__findnew_entry(struct hists *hists,
if (hists__has(hists, sym) && he->ms.map != entry->ms.map) {
if (he->ms.sym) {
u64 addr = symbol__start(he->ms.sym);
+
+ symbol__put(he->ms.sym);
he->ms.sym = map__find_symbol(entry->ms.map, addr);
}
@@ -1029,7 +1035,8 @@ iter_next_branch_entry(struct hist_entry_iter *iter, struct addr_location *al)
al->thread = thread__get(bi[i].to.ms.thread);
map__put(al->map);
al->map = map__get(bi[i].to.ms.map);
- al->sym = bi[i].to.ms.sym;
+ symbol__put(al->sym);
+ al->sym = symbol__get(bi[i].to.ms.sym);
al->addr = bi[i].to.addr;
return 1;
}
@@ -1343,9 +1350,11 @@ int hist_entry_iter__add(struct hist_entry_iter *iter, struct addr_location *al,
if (al)
alm = map__get(al->map);
+ iter->parent = NULL;
err = sample__resolve_callchain(iter->sample, get_tls_callchain_cursor(), &iter->parent,
al, max_stack_depth);
if (err) {
+ symbol__zput(iter->parent);
map__put(alm);
return err;
}
@@ -1381,6 +1390,7 @@ int hist_entry_iter__add(struct hist_entry_iter *iter, struct addr_location *al,
if (!err)
err = err2;
+ symbol__zput(iter->parent);
map__put(alm);
return err;
@@ -1467,6 +1477,7 @@ void hist_entry__delete(struct hist_entry *he)
}
thread__zput(he->thread);
+ symbol__zput(he->parent);
map_symbol__exit(&he->ms);
if (he->branch_info) {
diff --git a/tools/perf/util/libbfd.c b/tools/perf/util/libbfd.c
index 5e947271e388..a88a89e09545 100644
--- a/tools/perf/util/libbfd.c
+++ b/tools/perf/util/libbfd.c
@@ -246,7 +246,12 @@ static int inline_list__append_dso_a2l(struct dso *dso,
if (a2l->filename)
srcline = srcline_from_fileline(a2l->filename, a2l->line);
- return inline_list__append(inline_sym, srcline, node);
+ if (inline_list__append(inline_sym, srcline, node) != 0) {
+ zfree_srcline(&srcline);
+ symbol__put(inline_sym);
+ return -1;
+ }
+ return 0;
}
int libbfd__addr2line(const char *dso_name, u64 addr,
diff --git a/tools/perf/util/libdw.c b/tools/perf/util/libdw.c
index e660623338d5..3b14a3ad6f86 100644
--- a/tools/perf/util/libdw.c
+++ b/tools/perf/util/libdw.c
@@ -99,7 +99,7 @@ static int libdw_a2l_cb(Dwarf_Die *die, void *_args)
* while DWARF keeps the pre-clone linkage name, which
* left the outer frame spuriously tagged "(inlined)".
*/
- inline_sym = args->sym;
+ inline_sym = symbol__get(args->sym);
} else {
/*
* Prefer DW_AT_linkage_name so C++ inline frames keep
@@ -157,8 +157,7 @@ static int libdw_a2l_cb(Dwarf_Die *die, void *_args)
return 0;
abort_delete_sym:
- if (symbol__inlined(inline_sym))
- symbol__delete(inline_sym);
+ symbol__put(inline_sym);
abort_enomem:
args->err = -ENOMEM;
return DWARF_CB_ABORT;
diff --git a/tools/perf/util/llvm.c b/tools/perf/util/llvm.c
index 7429e8ef6cce..754c9ec18a11 100644
--- a/tools/perf/util/llvm.c
+++ b/tools/perf/util/llvm.c
@@ -63,6 +63,8 @@ int llvm__addr2line(const char *dso_name __maybe_unused, u64 addr __maybe_unused
inline_frames[i].line);
}
if (inline_list__append(inline_sym, srcline, node) != 0) {
+ zfree_srcline(&srcline);
+ symbol__put(inline_sym);
if (file)
zfree(file);
inline_node__clear_frames(node);
diff --git a/tools/perf/util/lock-contention.c b/tools/perf/util/lock-contention.c
index 8ee1b19a8292..367666028eb2 100644
--- a/tools/perf/util/lock-contention.c
+++ b/tools/perf/util/lock-contention.c
@@ -101,7 +101,6 @@ struct lock_stat *lock_stat_findnew(u64 addr, const char *name, int flags)
bool match_callstack_filter(struct machine *machine, u64 *callstack, int max_stack_depth)
{
- struct map *kmap;
struct symbol *sym;
u64 ip;
uint16_t e_machine = perf_env__e_machine(machine->env, /*e_flags=*/NULL);
@@ -130,14 +129,17 @@ bool match_callstack_filter(struct machine *machine, u64 *callstack, int max_sta
break;
ip = callstack[i];
- sym = machine__find_kernel_symbol(machine, ip, &kmap);
+ sym = machine__find_kernel_symbol(machine, ip, NULL);
if (sym == NULL)
continue;
list_for_each_entry(filter, &callstack_filters, list) {
- if (strstr(symbol__name(sym), filter->name))
+ if (strstr(symbol__name(sym), filter->name)) {
+ symbol__put(sym);
return true;
+ }
}
+ symbol__put(sym);
}
return false;
}
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index 14e93e7fb235..cb9e34220036 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -824,8 +824,10 @@ static int machine__process_ksymbol_unregister(struct machine *machine,
struct dso *dso = map__dso(map);
sym = dso__find_symbol(dso, map__map_ip(map, map__start(map)));
- if (sym)
+ if (sym) {
dso__delete_symbol(dso, sym);
+ symbol__put(sym);
+ }
}
map__put(map);
return 0;
@@ -2085,7 +2087,7 @@ static void ip__resolve_ams(struct thread *thread,
ams->al_addr = al.addr;
ams->al_level = al.level;
ams->ms.thread = thread__get(al.thread);
- ams->ms.sym = al.sym;
+ ams->ms.sym = symbol__get(al.sym);
ams->ms.map = map__get(al.map);
ams->phys_addr = 0;
ams->data_page_size = 0;
@@ -2106,7 +2108,7 @@ static void ip__resolve_data(struct thread *thread,
ams->al_addr = al.addr;
ams->al_level = al.level;
ams->ms.thread = thread__get(al.thread);
- ams->ms.sym = al.sym;
+ ams->ms.sym = symbol__get(al.sym);
ams->ms.map = map__get(al.map);
ams->phys_addr = phys_addr;
ams->data_page_size = daddr_page_size;
@@ -2192,7 +2194,8 @@ static int append_inlines(struct callchain_cursor *cursor, struct map_symbol *ms
.map = map__get(map),
};
list_for_each_entry(ilist, &inline_node->val, list) {
- ilist_ms.sym = ilist->symbol;
+ symbol__put(ilist_ms.sym);
+ ilist_ms.sym = symbol__get(ilist->symbol);
if (first) {
ret = callchain_cursor_append(cursor, ip, &ilist_ms,
branch, flags, nr_loop_iter,
@@ -2204,7 +2207,7 @@ static int append_inlines(struct callchain_cursor *cursor, struct map_symbol *ms
first = false;
if (ret != 0)
- return ret;
+ break;
}
map_symbol__exit(&ilist_ms);
@@ -2270,7 +2273,7 @@ static int add_callchain_ip(struct thread *thread,
if (al.sym != NULL) {
if (perf_hpp_list.parent && !*parent &&
symbol__match_regex(al.sym, &parent_regex))
- *parent = al.sym;
+ *parent = symbol__get(al.sym);
else if (have_ignore_callees && root_al &&
symbol__match_regex(al.sym, &ignore_callees_regex)) {
/* Treat this symbol as the root,
@@ -2290,7 +2293,7 @@ static int add_callchain_ip(struct thread *thread,
ms.thread = thread__get(al.thread);
ms.map = map__get(al.map);
- ms.sym = al.sym;
+ ms.sym = symbol__get(al.sym);
if (append_inlines(cursor, &ms, ip, branch, flags, nr_loop_iter,
iter_cycles, branch_from) == 0)
@@ -2451,8 +2454,7 @@ static void save_lbr_cursor_node(struct thread *thread,
map_symbol__exit(&lbr_stitch->prev_lbr_cursor[idx].ms);
memcpy(&lbr_stitch->prev_lbr_cursor[idx], cursor->curr,
sizeof(struct callchain_cursor_node));
- lbr_stitch->prev_lbr_cursor[idx].ms.thread = thread__get(cursor->curr->ms.thread);
- lbr_stitch->prev_lbr_cursor[idx].ms.map = map__get(cursor->curr->ms.map);
+ map_symbol__copy(&lbr_stitch->prev_lbr_cursor[idx].ms, &cursor->curr->ms);
lbr_stitch->prev_lbr_cursor[idx].valid = true;
cursor->pos++;
@@ -2676,9 +2678,7 @@ static bool has_stitched_lbr(struct thread *thread,
memcpy(&stitch_node->cursor, &lbr_stitch->prev_lbr_cursor[i],
sizeof(struct callchain_cursor_node));
- stitch_node->cursor.ms.thread =
- thread__get(lbr_stitch->prev_lbr_cursor[i].ms.thread);
- stitch_node->cursor.ms.map = map__get(lbr_stitch->prev_lbr_cursor[i].ms.map);
+ map_symbol__copy(&stitch_node->cursor.ms, &lbr_stitch->prev_lbr_cursor[i].ms);
if (callee)
list_add(&stitch_node->node, &lbr_stitch->lists);
@@ -3281,16 +3281,28 @@ struct dso *machine__findnew_dso(struct machine *machine, const char *filename)
char *machine__resolve_kernel_addr(void *vmachine, unsigned long long *addrp, char **modp)
{
+ static __thread char resolved_name[512];
+ static __thread char resolved_mod[PATH_MAX];
struct machine *machine = vmachine;
- struct map *map;
+ struct map *map = NULL;
struct symbol *sym = machine__find_kernel_symbol(machine, *addrp, &map);
- if (sym == NULL)
+ if (sym == NULL) {
+ map__put(map);
return NULL;
+ }
- *modp = __map__is_kmodule(map) ? (char *)dso__short_name(map__dso(map)) : NULL;
+ if (__map__is_kmodule(map)) {
+ snprintf(resolved_mod, sizeof(resolved_mod), "%s", dso__short_name(map__dso(map)));
+ *modp = resolved_mod;
+ } else {
+ *modp = NULL;
+ }
*addrp = map__unmap_ip(map, symbol__start(sym));
- return (char *)symbol__name(sym);
+ snprintf(resolved_name, sizeof(resolved_name), "%s", symbol__name(sym));
+ symbol__put(sym);
+ map__put(map);
+ return resolved_name;
}
struct machine__for_each_dso_cb_args {
@@ -3337,26 +3349,38 @@ bool machine__is_lock_function(struct machine *machine, u64 addr)
}
machine->sched.text_start = map__unmap_ip(kmap, symbol__start(sym));
+ symbol__put(sym);
+ map__put(kmap);
/* should not fail from here */
sym = machine__find_kernel_symbol_by_name(machine, "__sched_text_end", &kmap);
machine->sched.text_end = map__unmap_ip(kmap, symbol__start(sym));
+ symbol__put(sym);
+ map__put(kmap);
sym = machine__find_kernel_symbol_by_name(machine, "__lock_text_start", &kmap);
machine->lock.text_start = map__unmap_ip(kmap, symbol__start(sym));
+ symbol__put(sym);
+ map__put(kmap);
sym = machine__find_kernel_symbol_by_name(machine, "__lock_text_end", &kmap);
machine->lock.text_end = map__unmap_ip(kmap, symbol__start(sym));
+ symbol__put(sym);
+ map__put(kmap);
sym = machine__find_kernel_symbol_by_name(machine, "__traceiter_contention_begin", &kmap);
if (sym) {
machine->traceiter.text_start = map__unmap_ip(kmap, symbol__start(sym));
machine->traceiter.text_end = map__unmap_ip(kmap, symbol__end(sym));
+ symbol__put(sym);
+ map__put(kmap);
}
sym = machine__find_kernel_symbol_by_name(machine, "trace_contention_begin", &kmap);
if (sym) {
machine->trace.text_start = map__unmap_ip(kmap, symbol__start(sym));
machine->trace.text_end = map__unmap_ip(kmap, symbol__end(sym));
+ symbol__put(sym);
+ map__put(kmap);
}
}
diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h
index 3d8f72e70d49..745eefa79c7f 100644
--- a/tools/perf/util/map.h
+++ b/tools/perf/util/map.h
@@ -160,13 +160,15 @@ struct thread;
* @sym_name: the symbol name
* @pos: the 'struct symbol *' to use as a loop cursor
* @idx: the cursor index in the symbol names array
+ * Note: caller must call symbol__put(pos) if breaking out of the loop early.
*/
#define __map__for_each_symbol_by_name(map, sym_name, pos, idx) \
for (pos = map__find_symbol_by_name_idx(map, sym_name, &idx); \
- pos && \
- !symbol__match_symbol_name(symbol__name(pos), sym_name, \
- SYMBOL_TAG_INCLUDE__DEFAULT_ONLY); \
- pos = dso__next_symbol_by_name(map__dso(map), &idx))
+ pos && \
+ (!symbol__match_symbol_name(symbol__name(pos), sym_name, \
+ SYMBOL_TAG_INCLUDE__DEFAULT_ONLY) || \
+ (symbol__zput(pos), false)); \
+ symbol__put(pos), pos = dso__next_symbol_by_name(map__dso(map), &idx))
#define map__for_each_symbol_by_name(map, sym_name, pos, idx) \
__map__for_each_symbol_by_name(map, sym_name, (pos), idx)
diff --git a/tools/perf/util/map_symbol.c b/tools/perf/util/map_symbol.c
index 11bc0a7f704c..d1ee4e05564e 100644
--- a/tools/perf/util/map_symbol.c
+++ b/tools/perf/util/map_symbol.c
@@ -2,12 +2,14 @@
#include "map_symbol.h"
#include "maps.h"
#include "map.h"
+#include "symbol.h"
#include "thread.h"
void map_symbol__exit(struct map_symbol *ms)
{
thread__zput(ms->thread);
map__zput(ms->map);
+ symbol__zput(ms->sym);
}
void addr_map_symbol__exit(struct addr_map_symbol *ams)
@@ -19,7 +21,7 @@ void map_symbol__copy(struct map_symbol *dst, struct map_symbol *src)
{
dst->thread = thread__get(src->thread);
dst->map = map__get(src->map);
- dst->sym = src->sym;
+ dst->sym = symbol__get(src->sym);
}
void addr_map_symbol__copy(struct addr_map_symbol *dst, struct addr_map_symbol *src)
diff --git a/tools/perf/util/maps.c b/tools/perf/util/maps.c
index f808df2fe77b..a6cbe65cc5ca 100644
--- a/tools/perf/util/maps.c
+++ b/tools/perf/util/maps.c
@@ -788,7 +788,7 @@ struct symbol *maps__find_symbol_by_name(struct maps *maps, const char *name, st
*mapp = map__get(map);
break;
}
- sym = NULL;
+ symbol__zput(sym);
}
for (unsigned int i = 0; i < nr_maps; i++)
@@ -810,6 +810,7 @@ int maps__find_ams(struct maps *maps, struct addr_map_symbol *ams)
}
ams->al_addr = map__map_ip(ams->ms.map, ams->addr);
+ symbol__put(ams->ms.sym);
ams->ms.sym = map__find_symbol(ams->ms.map, ams->al_addr);
return ams->ms.sym ? 0 : -1;
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index bea7903a11e3..a02ec8a7c058 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -152,6 +152,8 @@ static int kernel_get_symbol_address_by_name(const char *name, u64 *addr,
*addr = map__unmap_ip(map, symbol__start(sym)) -
((reloc) ? 0 : map__reloc(map)) -
((reladdr) ? map__start(map) : 0);
+ symbol__put(sym);
+ map__put(map);
}
return 0;
}
@@ -422,6 +424,7 @@ static int find_alternative_probe_point(struct debuginfo *dinfo,
pp->function);
} else
address = map__unmap_ip(map, symbol__start(sym)) - map__reloc(map);
+ symbol__put(sym);
break;
}
if (!address) {
@@ -728,11 +731,14 @@ static int post_process_probe_trace_point(struct probe_trace_point *tp,
else
free(tp->symbol);
tp->symbol = strdup(symbol__name(sym));
- if (!tp->symbol)
+ if (!tp->symbol) {
+ symbol__put(sym);
return -ENOMEM;
+ }
}
tp->offset = addr - symbol__start(sym);
tp->address -= offs;
+ symbol__put(sym);
return 0;
}
@@ -2314,6 +2320,7 @@ static int find_perf_probe_point_from_map(struct probe_trace_point *tp,
ret = pp->function ? 0 : -ENOMEM;
out:
+ symbol__put(sym);
map__put(map);
return ret;
@@ -3078,7 +3085,7 @@ static int find_probe_functions_cb(struct symbol *sym, void *data)
if (strglobmatch(norm, args->name)) {
args->found++;
if (args->syms && args->found < probe_conf.max_probes)
- args->syms[args->found - 1] = sym;
+ args->syms[args->found - 1] = symbol__get(sym);
}
free(buf);
return 0;
@@ -3146,7 +3153,7 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev,
goto out;
}
- syms = malloc(sizeof(struct symbol *) * probe_conf.max_probes);
+ syms = calloc(probe_conf.max_probes, sizeof(struct symbol *));
if (!syms) {
ret = -ENOMEM;
goto out;
@@ -3290,6 +3297,10 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev,
}
out:
+ if (syms) {
+ for (i = 0; i < probe_conf.max_probes; i++)
+ symbol__put(syms[i]);
+ }
map__put(map);
free(syms);
return ret;
diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c
index 71ff233553cc..668bf23c166f 100644
--- a/tools/perf/util/python.c
+++ b/tools/perf/util/python.c
@@ -882,7 +882,7 @@ static PyObject *pyrf_sample_event__srccode(PyObject *self, PyObject *args)
addr_location__init(&al);
al.thread = thread__get(pevent->al.thread);
al.map = map__get(pevent->al.map);
- al.sym = pevent->al.sym;
+ al.sym = symbol__get(pevent->al.sym);
al.addr = pevent->al.addr;
}
@@ -938,13 +938,12 @@ struct pyrf_callchain_node {
u64 ip;
struct map *map;
struct symbol *sym;
- char *sym_name;
};
static void pyrf_callchain_node__delete(struct pyrf_callchain_node *pnode)
{
map__put(pnode->map);
- free(pnode->sym_name);
+ symbol__put(pnode->sym);
Py_TYPE(pnode)->tp_free((PyObject *)pnode);
}
@@ -957,8 +956,6 @@ static PyObject *pyrf_callchain_node__get_ip(struct pyrf_callchain_node *pnode,
static PyObject *pyrf_callchain_node__get_symbol(struct pyrf_callchain_node *pnode,
void *closure __maybe_unused)
{
- if (pnode->sym_name)
- return PyUnicode_FromString(pnode->sym_name);
if (pnode->sym)
return PyUnicode_FromString(symbol__name(pnode->sym));
return PyUnicode_FromString("[unknown]");
@@ -1016,7 +1013,6 @@ struct pyrf_callchain_frame {
u64 ip;
struct map *map;
struct symbol *sym;
- char *sym_name;
};
struct pyrf_callchain {
@@ -1030,7 +1026,7 @@ static void pyrf_callchain__delete(struct pyrf_callchain *pchain)
if (pchain->frames) {
for (u64 i = 0; i < pchain->nr_frames; i++) {
map__put(pchain->frames[i].map);
- free(pchain->frames[i].sym_name);
+ symbol__put(pchain->frames[i].sym);
}
free(pchain->frames);
}
@@ -1060,8 +1056,7 @@ static PyObject *pyrf_callchain__item(PyObject *obj, Py_ssize_t i)
pnode->ip = pchain->frames[i].ip;
pnode->map = map__get(pchain->frames[i].map);
- pnode->sym = pchain->frames[i].sym;
- pnode->sym_name = pchain->frames[i].sym_name ? strdup(pchain->frames[i].sym_name) : NULL;
+ pnode->sym = symbol__get(pchain->frames[i].sym);
return (PyObject *)pnode;
}
@@ -1114,7 +1109,7 @@ static int pyrf_sample_event__resolve_callchain(struct pyrf_event *pevent)
node = callchain_cursor_current(cursor);
pchain->frames[i].ip = node->ip;
pchain->frames[i].map = map__get(node->ms.map);
- pchain->frames[i].sym = node->ms.sym;
+ pchain->frames[i].sym = symbol__get(node->ms.sym);
callchain_cursor_advance(cursor);
}
pevent->callchain = (PyObject *)pchain;
@@ -4504,8 +4499,7 @@ static PyObject *pyrf_call_path__to_callchain(const struct call_path *cp,
struct pyrf_callchain_frame *frame = &pchain->frames[--nr_frames];
frame->ip = pos->ip;
- frame->sym = NULL;
- frame->sym_name = pos->sym ? strdup(symbol__name(pos->sym)) : NULL;
+ frame->sym = symbol__get(pos->sym);
frame->map = NULL;
if (thread) {
struct addr_location al;
@@ -4515,8 +4509,6 @@ static PyObject *pyrf_call_path__to_callchain(const struct call_path *cp,
addr_location__init(&al);
thread__find_map(thread, cpumode, pos->ip, &al);
frame->map = map__get(al.map);
- if (frame->map)
- frame->sym = pos->sym;
addr_location__exit(&al);
}
}
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c
index c4eca7a98642..87f23f1a31ea 100644
--- a/tools/perf/util/sort.c
+++ b/tools/perf/util/sort.c
@@ -466,7 +466,7 @@ int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r)
if (!sym_l || !sym_r)
return cmp_null(sym_l, sym_r);
- if (sym_l == sym_r)
+ if (RC_CHK_EQUAL(sym_l, sym_r))
return 0;
if (symbol__inlined(sym_l) || symbol__inlined(sym_r)) {
diff --git a/tools/perf/util/srcline.c b/tools/perf/util/srcline.c
index 14f2b6dfd086..d465bd8b7549 100644
--- a/tools/perf/util/srcline.c
+++ b/tools/perf/util/srcline.c
@@ -118,7 +118,7 @@ struct symbol *new_inline_sym(struct dso *dso,
if (base_sym && strcmp(funcname, symbol__name(base_sym)) == 0) {
/* reuse the real, existing symbol */
- inline_sym = base_sym;
+ inline_sym = symbol__get(base_sym);
/* ensure that we don't alias an inlined symbol, which could
* lead to double frees in inline_node__delete
*/
@@ -478,9 +478,7 @@ void inline_node__clear_frames(struct inline_node *node)
list_for_each_entry_safe(ilist, tmp, &node->val, list) {
list_del_init(&ilist->list);
zfree_srcline(&ilist->srcline);
- /* only the inlined symbols are owned by the list */
- if (ilist->symbol && symbol__inlined(ilist->symbol))
- symbol__delete(ilist->symbol);
+ symbol__zput(ilist->symbol);
free(ilist);
}
}
diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c
index 77db7ab5011d..4023174ed6db 100644
--- a/tools/perf/util/symbol-elf.c
+++ b/tools/perf/util/symbol-elf.c
@@ -352,10 +352,13 @@ static bool get_ifunc_name(Elf *elf, struct dso *dso, GElf_Ehdr *ehdr,
/* Expecting the address to be an IFUNC or IFUNC alias */
if (!sym || symbol__start(sym) != addr ||
- (symbol__type(sym) != STT_GNU_IFUNC && !symbol__ifunc_alias(sym)))
+ (symbol__type(sym) != STT_GNU_IFUNC && !symbol__ifunc_alias(sym))) {
+ symbol__put(sym);
return false;
+ }
snprintf(buf, buf_sz, "%s@plt", symbol__name(sym));
+ symbol__put(sym);
return true;
}
@@ -633,6 +636,7 @@ int dso__synthesize_plt_symbols(struct dso *dso, struct symsrc *ss)
f = dso__find_symbol_nocache(dso, shdr_plt.sh_offset);
if (f && symbol__start(f) < shdr_plt.sh_offset && symbol__end(f) > shdr_plt.sh_offset)
symbol__set_end(f, shdr_plt.sh_offset);
+ symbol__put(f);
if (!get_plt_sizes(dso, &ehdr, &shdr_plt, &plt_header_size, &plt_entry_size))
return 0;
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 0055bfdc980c..b2c0a606b13a 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
#include <dirent.h>
#include <errno.h>
+#include <stdatomic.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
@@ -228,7 +229,7 @@ void symbols__init(struct symbols *symbols)
static void symbols__delete_locked(struct symbols *symbols)
{
for (unsigned int i = 0; i < symbols->cnt; i++)
- symbol__delete(symbols->symbols[i]);
+ symbol__put(symbols->symbols[i]);
zfree(&symbols->symbols);
zfree(&symbols->symbols_by_name);
@@ -248,7 +249,13 @@ void symbols__delete(struct symbols *symbols)
void symbols__exit(struct symbols *symbols)
{
- symbols__delete(symbols);
+ down_write(&symbols->lock);
+ for (unsigned int i = 0; i < symbols->cnt; i++) {
+ if (symbol__annotated(symbols->symbols[i]))
+ symbol__annotation_delete(symbols->symbols[i]);
+ }
+ symbols__delete_locked(symbols);
+ up_write(&symbols->lock);
exit_rwsem(&symbols->lock);
}
@@ -331,11 +338,11 @@ void symbols__fixup_duplicate(struct symbols *symbols)
if (choose_best_symbol(curr, next) == SYMBOL_A) {
if (symbol__type(next) == STT_GNU_IFUNC)
symbol__set_ifunc_alias(curr, true);
- symbol__delete(next);
+ symbol__put(next);
} else {
if (symbol__type(curr) == STT_GNU_IFUNC)
symbol__set_ifunc_alias(next, true);
- symbol__delete(curr);
+ symbol__put(curr);
symbols->symbols[write_idx] = next;
}
}
@@ -421,81 +428,96 @@ void symbols__fixup_end(struct symbols *symbols, bool is_kallsyms)
struct symbol *symbol__new(u64 start, u64 len, u8 binding, u8 type, const char *name)
{
size_t namelen = strlen(name) + 1;
- struct symbol *sym = calloc(1, sizeof(*sym) + namelen);
-
- if (sym == NULL)
- return NULL;
-
- sym->start = start;
- sym->end = len ? start + len : start;
- atomic_init(&sym->flags, (type << SYMBOL_FLAG_TYPE_SHIFT) |
- (binding << SYMBOL_FLAG_BINDING_SHIFT));
- sym->namelen = namelen - 1;
-
- pr_debug4("%s: %s %#" PRIx64 "-%#" PRIx64 "\n",
- __func__, name, start, sym->end);
- memcpy(sym->name, name, namelen);
+ RC_STRUCT(symbol) *sym;
+ struct symbol *res;
+
+ sym = calloc(1, sizeof(*sym) + namelen);
+
+ if (ADD_RC_CHK(res, sym)) {
+ refcount_set(&sym->refcnt, 1);
+ sym->start = start;
+ sym->end = len ? start + len : start;
+ atomic_init(&sym->flags, (type << SYMBOL_FLAG_TYPE_SHIFT) |
+ (binding << SYMBOL_FLAG_BINDING_SHIFT));
+ sym->namelen = namelen - 1;
+
+ pr_debug4("%s: %s %#" PRIx64 "-%#" PRIx64 "\n",
+ __func__, name, start, sym->end);
+ memcpy(sym->name, name, namelen);
+ } else {
+ free(sym);
+ }
- return sym;
+ return res;
}
void symbol__delete(struct symbol *sym)
{
+ if (!sym)
+ return;
if (symbol__annotated(sym))
symbol__annotation_delete(sym);
- free(sym);
+ RC_CHK_FREE(sym);
+}
+
+void symbol__put(struct symbol *sym)
+{
+ if (sym && refcount_dec_and_test(symbol__refcnt(sym)))
+ symbol__delete(sym);
+ else
+ RC_CHK_PUT(sym);
}
void symbol__set_ignore(struct symbol *sym, bool ignore)
{
if (ignore)
- atomic_fetch_or(&sym->flags, SYMBOL_FLAG_IGNORE);
+ atomic_fetch_or(&RC_CHK_ACCESS(sym)->flags, SYMBOL_FLAG_IGNORE);
else
- atomic_fetch_and(&sym->flags, ~SYMBOL_FLAG_IGNORE);
+ atomic_fetch_and(&RC_CHK_ACCESS(sym)->flags, ~SYMBOL_FLAG_IGNORE);
}
void symbol__set_annotate2(struct symbol *sym, bool annotate2)
{
if (annotate2)
- atomic_fetch_or(&sym->flags, SYMBOL_FLAG_ANNOTATE2);
+ atomic_fetch_or(&RC_CHK_ACCESS(sym)->flags, SYMBOL_FLAG_ANNOTATE2);
else
- atomic_fetch_and(&sym->flags, ~SYMBOL_FLAG_ANNOTATE2);
+ atomic_fetch_and(&RC_CHK_ACCESS(sym)->flags, ~SYMBOL_FLAG_ANNOTATE2);
}
void symbol__set_inlined(struct symbol *sym, bool inlined)
{
if (inlined)
- atomic_fetch_or(&sym->flags, SYMBOL_FLAG_INLINED);
+ atomic_fetch_or(&RC_CHK_ACCESS(sym)->flags, SYMBOL_FLAG_INLINED);
else
- atomic_fetch_and(&sym->flags, ~SYMBOL_FLAG_INLINED);
+ atomic_fetch_and(&RC_CHK_ACCESS(sym)->flags, ~SYMBOL_FLAG_INLINED);
}
void symbol__set_ifunc_alias(struct symbol *sym, bool ifunc_alias)
{
if (ifunc_alias)
- atomic_fetch_or(&sym->flags, SYMBOL_FLAG_IFUNC_ALIAS);
+ atomic_fetch_or(&RC_CHK_ACCESS(sym)->flags, SYMBOL_FLAG_IFUNC_ALIAS);
else
- atomic_fetch_and(&sym->flags, ~SYMBOL_FLAG_IFUNC_ALIAS);
+ atomic_fetch_and(&RC_CHK_ACCESS(sym)->flags, ~SYMBOL_FLAG_IFUNC_ALIAS);
}
void symbol__set_annotated(struct symbol *sym, bool annotated)
{
if (annotated)
- atomic_fetch_or(&sym->flags, SYMBOL_FLAG_ANNOTATED);
+ atomic_fetch_or(&RC_CHK_ACCESS(sym)->flags, SYMBOL_FLAG_ANNOTATED);
else
- atomic_fetch_and(&sym->flags, ~SYMBOL_FLAG_ANNOTATED);
+ atomic_fetch_and(&RC_CHK_ACCESS(sym)->flags, ~SYMBOL_FLAG_ANNOTATED);
}
static void symbol__set_idle(struct symbol *sym, bool idle)
{
- uint16_t old_flags = atomic_load_explicit(&sym->flags, memory_order_relaxed);
+ uint16_t old_flags = atomic_load_explicit(&RC_CHK_ACCESS(sym)->flags, memory_order_relaxed);
uint16_t new_flags;
uint16_t idle_val = idle ? SYMBOL_IDLE__IDLE : SYMBOL_IDLE__NOT_IDLE;
do {
new_flags = old_flags & ~SYMBOL_FLAG_IDLE_MASK;
new_flags |= (idle_val << SYMBOL_FLAG_IDLE_SHIFT);
- } while (!atomic_compare_exchange_weak(&sym->flags, &old_flags, new_flags));
+ } while (!atomic_compare_exchange_weak(&RC_CHK_ACCESS(sym)->flags, &old_flags, new_flags));
}
static int __symbols__insert(struct symbols *symbols, struct symbol *sym)
@@ -506,7 +528,7 @@ static int __symbols__insert(struct symbols *symbols, struct symbol *sym)
sizeof(struct symbol *) * to_allocate);
if (!temp) {
- symbol__delete(sym);
+ symbol__put(sym);
return -ENOMEM;
}
symbols->symbols = temp;
@@ -561,7 +583,7 @@ static struct symbol *symbols__find(struct symbols *symbols, u64 ip)
else if (ip > end || (ip == end && ip != start))
low = mid + 1;
else {
- res = s;
+ res = symbol__get(s);
break;
}
}
@@ -685,7 +707,7 @@ static struct symbol *symbols__find_by_name(struct symbol *symbols[],
}
}
assert(!found_idx || !s || s == symbols[*found_idx]);
- return s;
+ return symbol__get(s);
}
int dso__insert_symbol(struct dso *dso, struct symbol *sym)
@@ -699,14 +721,16 @@ void dso__delete_symbol(struct dso *dso, struct symbol *sym)
down_write(&symbols->lock);
for (unsigned int i = 0; i < symbols->cnt; i++) {
- if (symbols->symbols[i] == sym) {
+ if (RC_CHK_EQUAL(symbols->symbols[i], sym)) {
+ struct symbol *old = symbols->symbols[i];
+
memmove(&symbols->symbols[i], &symbols->symbols[i + 1],
(symbols->cnt - i - 1) * sizeof(struct symbol *));
symbols->cnt--;
zfree(&symbols->symbols_by_name);
symbols->num_by_name = 0;
symbols->sorted_by_name = false;
- symbol__delete(sym);
+ symbol__put(old);
break;
}
}
@@ -731,7 +755,7 @@ struct symbol *dso__next_symbol_by_name(struct dso *dso, size_t *idx)
down_read(&symbols->lock);
if (*idx + 1 < symbols->num_by_name) {
++*idx;
- res = symbols->symbols_by_name[*idx];
+ res = symbol__get(symbols->symbols_by_name[*idx]);
}
up_read(&symbols->lock);
@@ -929,7 +953,8 @@ bool symbol__is_idle(struct symbol *sym, const struct dso *dso, struct perf_env
uint16_t e_machine;
{
- uint16_t flags = atomic_load_explicit(&sym->flags, memory_order_relaxed);
+ uint16_t flags = atomic_load_explicit(&RC_CHK_ACCESS(sym)->flags,
+ memory_order_relaxed);
uint16_t idle_val = (flags & SYMBOL_FLAG_IDLE_MASK) >> SYMBOL_FLAG_IDLE_SHIFT;
if (idle_val != SYMBOL_IDLE__UNKNOWN)
@@ -1077,7 +1102,7 @@ static int maps__split_kallsyms_for_kcore(struct maps *kmaps, struct dso *dso)
curr_map = maps__find(kmaps, symbol__start(pos));
if (!curr_map) {
- symbol__delete(pos);
+ symbol__put(pos);
continue;
}
curr_map_dso = map__dso(curr_map);
@@ -1273,7 +1298,7 @@ static int maps__split_kallsyms(struct maps *kmaps, struct dso *dso, u64 delta,
continue;
discard_symbol:
- symbol__delete(pos);
+ symbol__put(pos);
}
down_write(&root->lock);
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index eed4d388d1fc..b4e5fdf5c4d6 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -13,6 +13,7 @@
#include <linux/string.h>
#include <stdio.h>
#include <errno.h>
+#include <internal/rc_check.h>
#include "addr_location.h"
#include "path.h"
#include "rwsem.h"
@@ -85,10 +86,12 @@ enum symbol_idle_kind {
/**
* A symtab entry.
*/
-struct symbol {
+DECLARE_RC_STRUCT(symbol) {
/** Range of symbol [start, end). */
u64 start;
u64 end;
+ /** Reference count. */
+ refcount_t refcnt;
/** Length of the string name. */
u16 namelen;
_Atomic uint16_t flags;
@@ -135,86 +138,111 @@ void symbols__exit(struct symbols *symbols);
void symbols__delete(struct symbols *symbols);
void symbols__sort_read_lock(struct symbols *symbols) SHARED_LOCK_FUNCTION(symbols->lock);
+static inline refcount_t *symbol__refcnt(struct symbol *sym)
+{
+ return &RC_CHK_ACCESS(sym)->refcnt;
+}
+
+static inline struct symbol *symbol__get(struct symbol *sym)
+{
+ struct symbol *result;
+
+ if (RC_CHK_GET(result, sym))
+ refcount_inc(symbol__refcnt(sym));
+
+ return result;
+}
+
+void symbol__put(struct symbol *sym);
+
+static inline void __symbol__zput(struct symbol **sym)
+{
+ symbol__put(*sym);
+ *sym = NULL;
+}
+
+#define symbol__zput(sym) __symbol__zput(&sym)
+
static inline __attribute__((nonnull)) u64 symbol__start(const struct symbol *sym)
{
- return sym->start;
+ return RC_CHK_ACCESS(sym)->start;
}
static inline __attribute__((nonnull)) void symbol__set_start(struct symbol *sym, u64 start)
{
- sym->start = start;
+ RC_CHK_ACCESS(sym)->start = start;
}
static inline __attribute__((nonnull)) u64 symbol__end(const struct symbol *sym)
{
- return sym->end;
+ return RC_CHK_ACCESS(sym)->end;
}
static inline __attribute__((nonnull)) void symbol__set_end(struct symbol *sym, u64 end)
{
- sym->end = end;
+ RC_CHK_ACCESS(sym)->end = end;
}
static inline __attribute__((nonnull, returns_nonnull))
const char *symbol__name(const struct symbol *sym)
{
- return sym->name;
+ return RC_CHK_ACCESS(sym)->name;
}
static inline __attribute__((nonnull)) u16 symbol__namelen(const struct symbol *sym)
{
- return sym->namelen;
+ return RC_CHK_ACCESS(sym)->namelen;
}
static inline __attribute__((nonnull)) u8 symbol__arch_sym(const struct symbol *sym)
{
- return sym->arch_sym;
+ return RC_CHK_ACCESS(sym)->arch_sym;
}
static inline __attribute__((nonnull)) void symbol__set_arch_sym(struct symbol *sym, u8 arch_sym)
{
- sym->arch_sym = arch_sym;
+ RC_CHK_ACCESS(sym)->arch_sym = arch_sym;
}
static inline u8 symbol__type(const struct symbol *sym)
{
- return (atomic_load_explicit(&sym->flags, memory_order_relaxed) &
+ return (atomic_load_explicit(&RC_CHK_ACCESS(sym)->flags, memory_order_relaxed) &
SYMBOL_FLAG_TYPE_MASK) >> SYMBOL_FLAG_TYPE_SHIFT;
}
static inline u8 symbol__binding(const struct symbol *sym)
{
- return (atomic_load_explicit(&sym->flags, memory_order_relaxed) &
+ return (atomic_load_explicit(&RC_CHK_ACCESS(sym)->flags, memory_order_relaxed) &
SYMBOL_FLAG_BINDING_MASK) >> SYMBOL_FLAG_BINDING_SHIFT;
}
static inline bool symbol__ignore(const struct symbol *sym)
{
- return (atomic_load_explicit(&sym->flags, memory_order_relaxed) &
+ return (atomic_load_explicit(&RC_CHK_ACCESS(sym)->flags, memory_order_relaxed) &
SYMBOL_FLAG_IGNORE) != 0;
}
static inline bool symbol__inlined(const struct symbol *sym)
{
- return (atomic_load_explicit(&sym->flags, memory_order_relaxed) &
+ return (atomic_load_explicit(&RC_CHK_ACCESS(sym)->flags, memory_order_relaxed) &
SYMBOL_FLAG_INLINED) != 0;
}
static inline bool symbol__is_annotate2(const struct symbol *sym)
{
- return (atomic_load_explicit(&sym->flags, memory_order_relaxed) &
+ return (atomic_load_explicit(&RC_CHK_ACCESS(sym)->flags, memory_order_relaxed) &
SYMBOL_FLAG_ANNOTATE2) != 0;
}
static inline bool symbol__ifunc_alias(const struct symbol *sym)
{
- return (atomic_load_explicit(&sym->flags, memory_order_relaxed) &
+ return (atomic_load_explicit(&RC_CHK_ACCESS(sym)->flags, memory_order_relaxed) &
SYMBOL_FLAG_IFUNC_ALIAS) != 0;
}
static inline bool symbol__annotated(const struct symbol *sym)
{
- return (atomic_load_explicit(&sym->flags, memory_order_relaxed) &
+ return (atomic_load_explicit(&RC_CHK_ACCESS(sym)->flags, memory_order_relaxed) &
SYMBOL_FLAG_ANNOTATED) != 0;
}
diff --git a/tools/perf/util/thread-stack.c b/tools/perf/util/thread-stack.c
index 76867ba83365..e2c73e890158 100644
--- a/tools/perf/util/thread-stack.c
+++ b/tools/perf/util/thread-stack.c
@@ -823,7 +823,7 @@ static int thread_stack__pop_cp(struct thread *thread, struct thread_stack *ts,
if (ts->cnt == 1) {
struct thread_stack_entry *tse = &ts->stack[0];
- if (tse->cp->sym == sym)
+ if (RC_CHK_EQUAL(tse->cp->sym, sym))
return thread_stack__call_return(thread, ts, --ts->cnt,
timestamp, ref, false);
}
@@ -941,7 +941,7 @@ static int thread_stack__no_call_return(struct thread *thread,
else
parent = root;
- if (parent->sym == from_al->sym) {
+ if (RC_CHK_EQUAL(parent->sym, from_al->sym)) {
/*
* At the bottom of the stack, assume the missing 'call' was
* before the trace started. So, pop the current symbol and push
@@ -1069,7 +1069,7 @@ static int thread_stack__x86_retpoline(struct thread_stack *ts,
*/
ts->cnt -= 1;
sym = ts->stack[ts->cnt - 2].cp->sym;
- if (sym && sym == tsym && to_al->addr != symbol__start(tsym)) {
+ if (sym && RC_CHK_EQUAL(sym, tsym) && to_al->addr != symbol__start(tsym)) {
/*
* Target is back to the middle of the symbol we came
* from so assume it is an indirect jmp and forget it
@@ -1078,7 +1078,7 @@ static int thread_stack__x86_retpoline(struct thread_stack *ts,
ts->cnt -= 1;
return 0;
}
- } else if (sym && sym == tsym) {
+ } else if (sym && RC_CHK_EQUAL(sym, tsym)) {
/*
* Target is back to the symbol we came from so assume it is an
* indirect jmp and forget it altogether.
@@ -1169,7 +1169,7 @@ int thread_stack__process(struct thread *thread, struct comm *comm,
* may be the start of a x86 retpoline.
*/
if (!err && rstate == X86_RETPOLINE_POSSIBLE && to_al->sym &&
- from_al->sym == to_al->sym &&
+ RC_CHK_EQUAL(from_al->sym, to_al->sym) &&
to_al->addr != symbol__start(to_al->sym))
ts->rstate = X86_RETPOLINE_DETECTED;
@@ -1206,7 +1206,7 @@ int thread_stack__process(struct thread *thread, struct comm *comm,
} else if (sample->flags & PERF_IP_FLAG_TRACE_END) {
err = thread_stack__trace_end(ts, sample, ref);
} else if (sample->flags & PERF_IP_FLAG_BRANCH &&
- from_al->sym != to_al->sym && to_al->sym &&
+ !RC_CHK_EQUAL(from_al->sym, to_al->sym) && to_al->sym &&
to_al->addr == symbol__start(to_al->sym)) {
struct call_path_root *cpr = ts->crp->cpr;
struct call_path *cp;
diff --git a/tools/perf/util/threads.c b/tools/perf/util/threads.c
index 6ca0b178fb6c..b3ec954fe7ab 100644
--- a/tools/perf/util/threads.c
+++ b/tools/perf/util/threads.c
@@ -142,10 +142,13 @@ void threads__remove_all_threads(struct threads *threads)
down_write(&table->lock);
__threads_table_entry__set_last_match(table, NULL);
hashmap__for_each_entry_safe(&table->shard, cur, tmp, bkt) {
- struct thread *old_value;
+ struct thread *old_value = NULL;
- hashmap__delete(&table->shard, cur->key, /*old_key=*/NULL, &old_value);
- thread__put(old_value);
+ if (hashmap__delete(&table->shard, cur->key,
+ /*old_key=*/NULL, &old_value)) {
+ thread__free_stitch_list(old_value);
+ thread__put(old_value);
+ }
}
up_write(&table->lock);
}
@@ -154,14 +157,16 @@ void threads__remove_all_threads(struct threads *threads)
void threads__remove(struct threads *threads, struct thread *thread)
{
struct threads_table_entry *table = threads__table(threads, thread__tid(thread));
- struct thread *old_value;
+ struct thread *old_value = NULL;
down_write(&table->lock);
if (table->last_match && RC_CHK_EQUAL(table->last_match, thread))
__threads_table_entry__set_last_match(table, NULL);
- hashmap__delete(&table->shard, thread__tid(thread), /*old_key=*/NULL, &old_value);
- thread__put(old_value);
+ if (hashmap__delete(&table->shard, thread__tid(thread), /*old_key=*/NULL, &old_value)) {
+ thread__free_stitch_list(old_value);
+ thread__put(old_value);
+ }
up_write(&table->lock);
}
diff --git a/tools/perf/util/unwind-libdw.c b/tools/perf/util/unwind-libdw.c
index 87ade098b5c6..eb4327aa9e7e 100644
--- a/tools/perf/util/unwind-libdw.c
+++ b/tools/perf/util/unwind-libdw.c
@@ -169,7 +169,7 @@ static int entry(u64 ip, struct unwind_info *ui)
e->ip = ip;
e->ms.thread = thread__get(al.thread);
e->ms.map = map__get(al.map);
- e->ms.sym = al.sym;
+ e->ms.sym = symbol__get(al.sym);
pr_debug("unwind: %s:ip = 0x%" PRIx64 " (0x%" PRIx64 ")\n",
al.sym ? symbol__name(al.sym) : "''",
diff --git a/tools/perf/util/unwind-libunwind.c b/tools/perf/util/unwind-libunwind.c
index 6052383e862b..42872c7b6dcd 100644
--- a/tools/perf/util/unwind-libunwind.c
+++ b/tools/perf/util/unwind-libunwind.c
@@ -632,7 +632,7 @@ static int entry(uint64_t ip, struct thread *thread, unwind_entry_cb_t cb, void
e.ms.thread = thread__get(al.thread);
pr_debug("unwind: %s:ip = 0x%" PRIx64 " (0x%" PRIx64 ")\n",
- al.sym ? al.sym->name : "''",
+ al.sym ? symbol__name(al.sym) : "''",
ip,
al.map ? map__map_ip(al.map, ip) : (u64) 0);
--
2.56.0.313.g2479322e68-goog
^ permalink raw reply [flat|nested] 37+ messages in thread* [PATCH v2 09/18] perf symbol: Optimize symbol sorting with natural-run merge sort and binary insertion
2026-10-27 23:14 ` [PATCH v2 00/18] perf symbol: Reference counting, sorted arrays, and memory shrinking Ian Rogers
` (7 preceding siblings ...)
2026-10-27 23:15 ` [PATCH v2 08/18] perf symbol: Add reference counting and DECLARE_RC_STRUCT(symbol) Ian Rogers
@ 2026-10-27 23:15 ` Ian Rogers
2026-10-27 23:15 ` [PATCH v2 10/18] perf maps: Optimize tail insertion and small-append sorting Ian Rogers
` (9 subsequent siblings)
18 siblings, 0 replies; 37+ messages in thread
From: Ian Rogers @ 2026-10-27 23:15 UTC (permalink / raw)
To: irogers, acme, namhyung
Cc: adrian.hunter, haghdoost, james.clark, jolsa, linux-kernel,
linux-perf-users, mingo, peterz
ELF .symtab and .dynsym sections are typically already mostly ordered by
address in ascending runs, and dynamic symbol resolution
(dso__insert_symbol()) frequently inserts one or a few symbols into an
already-sorted DSO symbol table. Invoking full-array qsort() on every
sort after incremental insertions or across pre-ordered ELF runs spends
avoidable CPU time in symbol comparisons.
Speed up symbol array sorting and out-of-order symbol insertions:
- Track the sorted prefix length (@sorted_cnt) in struct symbols and
replace full-array qsort() with a natural-run merge sort
(symbols__sort_range_with_tmp()) and galloping binary-search merge
(symbols__merge_runs()), plus an O(N) in-place binary insertion fast
path when <= 8 symbols are appended.
- Insert directly into sorted position in dso__insert_symbol() when
symbols->sorted is true so dynamic single-symbol insertions maintain
the sorted invariant without triggering full re-sorts.
Using 'perf bench internals symbols':
Before:
Average DSO load + sort: 6.242 msec (+- 0.057 msec)
Average bulk insert + lookup: 1404.150 usec (+- 18.752 usec)
Average append + lookup: 40429.750 usec (+- 507.578 usec)
After (-7.0% load, -13.9% bulk, -99.6% append):
Average DSO load + sort: 5.808 msec (+- 0.047 msec)
Average bulk insert + lookup: 1209.250 usec (+- 18.446 usec)
Average append + lookup: 152.900 usec (+- 0.736 usec)
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/symbol.c | 434 ++++++++++++++++++++++++++++++++++++---
tools/perf/util/symbol.h | 5 +
2 files changed, 405 insertions(+), 34 deletions(-)
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index b2c0a606b13a..7f1fcd5c64b2 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -220,6 +220,7 @@ void symbols__init(struct symbols *symbols)
symbols->symbols = NULL;
symbols->symbols_by_name = NULL;
symbols->cnt = 0;
+ symbols->sorted_cnt = 0;
symbols->allocated = 0;
symbols->num_by_name = 0;
symbols->sorted = true;
@@ -234,6 +235,7 @@ static void symbols__delete_locked(struct symbols *symbols)
zfree(&symbols->symbols);
zfree(&symbols->symbols_by_name);
symbols->cnt = 0;
+ symbols->sorted_cnt = 0;
symbols->allocated = 0;
symbols->num_by_name = 0;
symbols->sorted = true;
@@ -266,27 +268,312 @@ static inline bool symbol__less_or_equal(const struct symbol *a, const struct sy
return symbol__end(a) <= symbol__end(b);
}
+static unsigned int symbols__upper_bound(struct symbol **arr, unsigned int len,
+ const struct symbol *sym)
+{
+ unsigned int low = 0, high = len;
+
+ while (low < high) {
+ unsigned int mid = low + (high - low) / 2;
+
+ if (symbol__less_or_equal(arr[mid], sym))
+ low = mid + 1;
+ else
+ high = mid;
+ }
+ return low;
+}
+
+static unsigned int symbols__lower_bound(struct symbol **arr, unsigned int len,
+ const struct symbol *sym)
+{
+ unsigned int low = 0, high = len;
+
+ while (low < high) {
+ unsigned int mid = low + (high - low) / 2;
+
+ if (!symbol__less_or_equal(sym, arr[mid]))
+ low = mid + 1;
+ else
+ high = mid;
+ }
+ return low;
+}
+
+static void symbols__insertion_sort_range(struct symbol **arr,
+ unsigned int start,
+ unsigned int end)
+{
+ for (unsigned int i = start + 1; i < end; i++) {
+ struct symbol *sym = arr[i];
+
+ if (!symbol__less_or_equal(arr[i - 1], sym)) {
+ unsigned int pos = start +
+ symbols__upper_bound(&arr[start], i - start, sym);
+
+ memmove(&arr[pos + 1], &arr[pos],
+ (i - pos) * sizeof(struct symbol *));
+ arr[pos] = sym;
+ }
+ }
+}
+
+static void symbols__merge_runs(struct symbol **arr,
+ unsigned int left,
+ unsigned int mid,
+ unsigned int right,
+ struct symbol **tmp)
+{
+ unsigned int first_left, last_left, left_len, right_len;
+ unsigned int i, j, dst;
+
+ if (left >= mid || mid >= right)
+ return;
+ if (symbol__less_or_equal(arr[mid - 1], arr[mid]))
+ return;
+
+ first_left = left + symbols__upper_bound(&arr[left], mid - left, arr[mid]);
+ right_len = right - mid;
+
+ if (mid - first_left <= right_len) {
+ left_len = mid - first_left;
+ memcpy(tmp, &arr[first_left], left_len * sizeof(struct symbol *));
+ i = 0;
+ j = mid;
+ dst = first_left;
+ while (i < left_len && j < right) {
+ if (symbol__less_or_equal(tmp[i], arr[j])) {
+ arr[dst++] = tmp[i++];
+ } else {
+ arr[dst++] = arr[j++];
+ if (right - j >= 16 &&
+ (right - j) > 8 * (left_len - i) &&
+ !symbol__less_or_equal(tmp[i], arr[j])) {
+ unsigned int step =
+ symbols__lower_bound(&arr[j], right - j, tmp[i]);
+
+ if (step > 0) {
+ memmove(&arr[dst], &arr[j],
+ step * sizeof(struct symbol *));
+ dst += step;
+ j += step;
+ }
+ }
+ }
+ }
+ if (i < left_len)
+ memcpy(&arr[dst], &tmp[i], (left_len - i) * sizeof(struct symbol *));
+ return;
+ }
+
+ last_left = first_left +
+ symbols__upper_bound(&arr[first_left], mid - first_left, arr[right - 1]);
+ left_len = last_left - first_left;
+
+ memcpy(tmp, &arr[mid], right_len * sizeof(struct symbol *));
+ if (last_left < mid) {
+ memmove(&arr[last_left + right_len], &arr[last_left],
+ (mid - last_left) * sizeof(struct symbol *));
+ }
+
+ if (first_left == last_left) {
+ memcpy(&arr[first_left], tmp, right_len * sizeof(struct symbol *));
+ return;
+ }
+
+ i = last_left;
+ j = right_len;
+ dst = last_left + right_len;
+ while (i > first_left && j > 0) {
+ if (symbol__less_or_equal(arr[i - 1], tmp[j - 1])) {
+ arr[--dst] = tmp[--j];
+ } else {
+ arr[--dst] = arr[--i];
+ if (i - first_left >= 16 &&
+ (i - first_left) > 8 * j &&
+ !symbol__less_or_equal(arr[i - 1], tmp[j - 1])) {
+ unsigned int pos = first_left +
+ symbols__upper_bound(&arr[first_left],
+ i - first_left,
+ tmp[j - 1]);
+ unsigned int step = i - pos;
+
+ if (step > 0) {
+ dst -= step;
+ i = pos;
+ memmove(&arr[dst], &arr[pos],
+ step * sizeof(struct symbol *));
+ }
+ }
+ }
+ }
+ if (j > 0)
+ memcpy(&arr[first_left], tmp, j * sizeof(struct symbol *));
+}
+
+struct symbol_run {
+ unsigned int start;
+ unsigned int len;
+};
+
+static void symbols__sort_range_with_tmp(struct symbol **arr, unsigned int n,
+ struct symbol **tmp)
+{
+ struct symbol_run runs[64];
+ unsigned int nr_runs = 0;
+ unsigned int i = 0;
+
+ if (n <= 32) {
+ symbols__insertion_sort_range(arr, 0, n);
+ return;
+ }
+
+ while (i < n) {
+ unsigned int start = i++;
+
+ if (i < n) {
+ if (symbol__less_or_equal(arr[i - 1], arr[i])) {
+ while (i < n && symbol__less_or_equal(arr[i - 1], arr[i]))
+ i++;
+ } else {
+ while (i < n && !symbol__less_or_equal(arr[i - 1], arr[i]))
+ i++;
+ for (unsigned int lo = start, hi = i - 1; lo < hi; lo++, hi--) {
+ struct symbol *swap = arr[lo];
+
+ arr[lo] = arr[hi];
+ arr[hi] = swap;
+ }
+ }
+ }
+
+ if (i - start < 32 && i < n) {
+ i = min(start + 32U, n);
+ symbols__insertion_sort_range(arr, start, i);
+ }
+
+ runs[nr_runs++] = (struct symbol_run){
+ .start = start,
+ .len = i - start,
+ };
+
+ while (nr_runs > 1) {
+ unsigned int r = nr_runs - 2;
+
+ if (nr_runs >= 3 &&
+ runs[nr_runs - 3].len <=
+ runs[nr_runs - 2].len + runs[nr_runs - 1].len) {
+ if (runs[nr_runs - 3].len < runs[nr_runs - 1].len)
+ r = nr_runs - 3;
+ } else if (runs[nr_runs - 2].len > runs[nr_runs - 1].len &&
+ nr_runs < ARRAY_SIZE(runs)) {
+ break;
+ }
+
+ symbols__merge_runs(arr, runs[r].start,
+ runs[r + 1].start,
+ runs[r + 1].start + runs[r + 1].len,
+ tmp);
+ runs[r].len += runs[r + 1].len;
+ if (r + 2 < nr_runs)
+ runs[r + 1] = runs[r + 2];
+ nr_runs--;
+ }
+ }
+
+ while (nr_runs > 1) {
+ unsigned int r = nr_runs - 2;
+
+ if (nr_runs >= 3 && runs[nr_runs - 3].len < runs[nr_runs - 1].len)
+ r = nr_runs - 3;
+ symbols__merge_runs(arr, runs[r].start,
+ runs[r + 1].start,
+ runs[r + 1].start + runs[r + 1].len,
+ tmp);
+ runs[r].len += runs[r + 1].len;
+ if (r + 2 < nr_runs)
+ runs[r + 1] = runs[r + 2];
+ nr_runs--;
+ }
+}
+
static int symbols__sort_cmp(const void *vlhs, const void *vrhs)
{
- const struct symbol *lhs = *((const struct symbol **)vlhs);
- const struct symbol *rhs = *((const struct symbol **)vrhs);
+ const struct symbol *l = *((const struct symbol **)vlhs);
+ const struct symbol *r = *((const struct symbol **)vrhs);
- if (symbol__start(lhs) != symbol__start(rhs))
- return symbol__start(lhs) < symbol__start(rhs) ? -1 : 1;
- if (symbol__end(lhs) != symbol__end(rhs))
- return symbol__end(lhs) < symbol__end(rhs) ? -1 : 1;
+ if (symbol__start(l) != symbol__start(r))
+ return symbol__start(l) < symbol__start(r) ? -1 : 1;
+ if (symbol__end(l) != symbol__end(r))
+ return symbol__end(l) < symbol__end(r) ? -1 : 1;
return 0;
}
static void symbols__sort_locked(struct symbols *symbols)
{
- if (symbols->sorted)
+ unsigned int m = symbols->sorted_cnt;
+ unsigned int k;
+
+ if (symbols->sorted) {
+ symbols->sorted_cnt = symbols->cnt;
return;
+ }
+
+ if (m > symbols->cnt)
+ m = 0;
+ k = symbols->cnt - m;
- if (symbols->cnt > 1) {
- qsort(symbols->symbols, symbols->cnt, sizeof(struct symbol *),
- symbols__sort_cmp);
+ if (k == 0) {
+ symbols->sorted = true;
+ return;
}
+
+ /*
+ * When only a few symbols have been appended since the last sort
+ * (including single-symbol insertions), insert them directly at
+ * their binary-searched sorted positions in O(N) without allocating.
+ */
+ if (k <= 8 || symbols->cnt <= 64) {
+ for (unsigned int i = max(m, 1U); i < symbols->cnt; i++) {
+ struct symbol *sym = symbols->symbols[i];
+
+ if (!symbol__less_or_equal(symbols->symbols[i - 1], sym)) {
+ unsigned int pos = symbols__upper_bound(symbols->symbols, i, sym);
+
+ memmove(&symbols->symbols[pos + 1], &symbols->symbols[pos],
+ (i - pos) * sizeof(struct symbol *));
+ symbols->symbols[pos] = sym;
+ }
+ }
+ symbols->sorted_cnt = symbols->cnt;
+ symbols->sorted = true;
+ return;
+ }
+
+ if (m <= 1) {
+ struct symbol **tmp = malloc(((symbols->cnt + 1) / 2) * sizeof(struct symbol *));
+
+ if (tmp) {
+ symbols__sort_range_with_tmp(symbols->symbols, symbols->cnt, tmp);
+ free(tmp);
+ } else {
+ qsort(symbols->symbols, symbols->cnt, sizeof(struct symbol *),
+ symbols__sort_cmp);
+ }
+ } else {
+ struct symbol **tmp = malloc(k * sizeof(struct symbol *));
+
+ if (tmp) {
+ symbols__sort_range_with_tmp(&symbols->symbols[m], k, tmp);
+ symbols__merge_runs(symbols->symbols, 0, m, symbols->cnt, tmp);
+ free(tmp);
+ } else {
+ qsort(symbols->symbols, symbols->cnt, sizeof(struct symbol *),
+ symbols__sort_cmp);
+ }
+ }
+
+ symbols->sorted_cnt = symbols->cnt;
symbols->sorted = true;
}
@@ -347,6 +634,7 @@ void symbols__fixup_duplicate(struct symbols *symbols)
}
}
symbols->cnt = write_idx + 1;
+ symbols->sorted_cnt = symbols->cnt;
zfree(&symbols->symbols_by_name);
symbols->num_by_name = 0;
symbols->sorted_by_name = false;
@@ -388,40 +676,62 @@ void symbols__fixup_end(struct symbols *symbols, bool is_kallsyms)
* like in:
* ffffffffc1937000 T hdmi_driver_init [snd_hda_codec_hdmi]
*/
- if (symbol__end(prev) == symbol__start(prev)) {
+ if (symbol__end(prev) == symbol__start(prev) &&
+ symbol__start(prev) != symbol__start(curr)) {
const char *prev_mod;
const char *curr_mod;
if (!is_kallsyms) {
symbol__set_end(prev, symbol__start(curr));
- continue;
- }
+ } else {
+ prev_mod = strchr(symbol__name(prev), '[');
+ curr_mod = strchr(symbol__name(curr), '[');
+
+ /* Last kernel/module symbol mapped to end of page */
+ if (!prev_mod != !curr_mod) {
+ symbol__set_end(prev,
+ roundup(symbol__end(prev) + 4096, 4096));
+ /* Last symbol in the previous module */
+ } else if (prev_mod && strcmp(prev_mod, curr_mod)) {
+ symbol__set_end(prev,
+ roundup(symbol__end(prev) + 4096, 4096));
+ } else {
+ symbol__set_end(prev, symbol__start(curr));
+ }
- prev_mod = strchr(symbol__name(prev), '[');
- curr_mod = strchr(symbol__name(curr), '[');
+ /* The next module can start within that page */
+ if (symbol__end(prev) > symbol__start(curr))
+ symbol__set_end(prev, symbol__start(curr));
- /* Last kernel/module symbol mapped to end of page */
- if (!prev_mod != !curr_mod)
- symbol__set_end(prev, roundup(symbol__end(prev) + 4096, 4096));
- /* Last symbol in the previous module */
- else if (prev_mod && strcmp(prev_mod, curr_mod))
- symbol__set_end(prev, roundup(symbol__end(prev) + 4096, 4096));
- else
- symbol__set_end(prev, symbol__start(curr));
+ pr_debug4("%s sym:%s end:%#" PRIx64 "\n",
+ __func__, symbol__name(prev), symbol__end(prev));
+ }
- /* The next module can start within that page */
- if (symbol__end(prev) > symbol__start(curr))
- symbol__set_end(prev, symbol__start(curr));
+ for (unsigned int k = i - 1; k > 0; k--) {
+ struct symbol *same = symbols->symbols[k - 1];
- pr_debug4("%s sym:%s end:%#" PRIx64 "\n",
- __func__, symbol__name(prev), symbol__end(prev));
+ if (symbol__start(same) != symbol__start(prev) ||
+ symbol__end(same) != symbol__start(same))
+ break;
+ symbol__set_end(same, symbol__end(prev));
+ }
}
}
/* Last entry */
- if (symbol__end(curr) == symbol__start(curr))
+ if (symbol__end(curr) == symbol__start(curr)) {
symbol__set_end(curr, roundup(symbol__start(curr), 4096) + 4096);
+ for (unsigned int k = symbols->cnt - 1; k > 0; k--) {
+ struct symbol *same = symbols->symbols[k - 1];
+
+ if (symbol__start(same) != symbol__start(curr) ||
+ symbol__end(same) != symbol__start(same))
+ break;
+ symbol__set_end(same, symbol__end(curr));
+ }
+ }
+
up_write(&symbols->lock);
}
@@ -535,9 +845,13 @@ static int __symbols__insert(struct symbols *symbols, struct symbol *sym)
symbols->allocated = to_allocate;
}
- if (symbols->sorted && symbols->cnt > 0 &&
- !symbol__less_or_equal(symbols->symbols[symbols->cnt - 1], sym))
- symbols->sorted = false;
+ if (symbols->sorted) {
+ if (symbols->cnt == 0 ||
+ symbol__less_or_equal(symbols->symbols[symbols->cnt - 1], sym))
+ symbols->sorted_cnt = symbols->cnt + 1;
+ else
+ symbols->sorted = false;
+ }
symbols->symbols[symbols->cnt++] = sym;
zfree(&symbols->symbols_by_name);
@@ -546,6 +860,44 @@ static int __symbols__insert(struct symbols *symbols, struct symbol *sym)
return 0;
}
+static int __symbols__insert_sorted(struct symbols *symbols, struct symbol *sym)
+{
+ unsigned int idx;
+
+ if (!symbols->sorted)
+ return __symbols__insert(symbols, sym);
+
+ if (symbols->cnt == symbols->allocated) {
+ unsigned int to_allocate = symbols->allocated ? symbols->allocated * 2 : 32;
+ struct symbol **temp = realloc(symbols->symbols,
+ sizeof(struct symbol *) * to_allocate);
+
+ if (!temp) {
+ symbol__put(sym);
+ return -ENOMEM;
+ }
+ symbols->symbols = temp;
+ symbols->allocated = to_allocate;
+ }
+
+ if (symbols->cnt == 0 ||
+ symbol__less_or_equal(symbols->symbols[symbols->cnt - 1], sym)) {
+ idx = symbols->cnt;
+ } else {
+ idx = symbols__upper_bound(symbols->symbols, symbols->cnt, sym);
+ memmove(&symbols->symbols[idx + 1], &symbols->symbols[idx],
+ (symbols->cnt - idx) * sizeof(struct symbol *));
+ }
+
+ symbols->symbols[idx] = sym;
+ symbols->cnt++;
+ symbols->sorted_cnt = symbols->cnt;
+ zfree(&symbols->symbols_by_name);
+ symbols->num_by_name = 0;
+ symbols->sorted_by_name = false;
+ return 0;
+}
+
int symbols__insert(struct symbols *symbols, struct symbol *sym)
{
int ret;
@@ -712,7 +1064,13 @@ static struct symbol *symbols__find_by_name(struct symbol *symbols[],
int dso__insert_symbol(struct dso *dso, struct symbol *sym)
{
- return symbols__insert(dso__symbols(dso), sym);
+ struct symbols *symbols = dso__symbols(dso);
+ int ret;
+
+ down_write(&symbols->lock);
+ ret = __symbols__insert_sorted(symbols, sym);
+ up_write(&symbols->lock);
+ return ret;
}
void dso__delete_symbol(struct dso *dso, struct symbol *sym)
@@ -720,13 +1078,16 @@ void dso__delete_symbol(struct dso *dso, struct symbol *sym)
struct symbols *symbols = dso__symbols(dso);
down_write(&symbols->lock);
- for (unsigned int i = 0; i < symbols->cnt; i++) {
+ for (unsigned int i = symbols->cnt; i-- > 0; ) {
if (RC_CHK_EQUAL(symbols->symbols[i], sym)) {
struct symbol *old = symbols->symbols[i];
memmove(&symbols->symbols[i], &symbols->symbols[i + 1],
(symbols->cnt - i - 1) * sizeof(struct symbol *));
symbols->cnt--;
+ if (i < symbols->sorted_cnt)
+ symbols->sorted_cnt--;
+ symbols->sorted = (symbols->sorted_cnt == symbols->cnt);
zfree(&symbols->symbols_by_name);
symbols->num_by_name = 0;
symbols->sorted_by_name = false;
@@ -1082,6 +1443,7 @@ static int maps__split_kallsyms_for_kcore(struct maps *kmaps, struct dso *dso)
old_cnt = root->cnt;
root->symbols = NULL;
root->cnt = 0;
+ root->sorted_cnt = 0;
root->allocated = 0;
root->sorted = true;
zfree(&root->symbols_by_name);
@@ -1159,6 +1521,7 @@ static int maps__split_kallsyms(struct maps *kmaps, struct dso *dso, u64 delta,
old_allocated = root->allocated;
root->symbols = NULL;
root->cnt = 0;
+ root->sorted_cnt = 0;
root->allocated = 0;
root->sorted = true;
zfree(&root->symbols_by_name);
@@ -1305,6 +1668,7 @@ static int maps__split_kallsyms(struct maps *kmaps, struct dso *dso, u64 delta,
if (root->cnt == 0) {
root->symbols = old_symbols;
root->cnt = write_idx;
+ root->sorted_cnt = write_idx;
root->allocated = old_allocated;
root->sorted = true;
old_symbols = NULL;
@@ -1334,12 +1698,14 @@ static int maps__split_kallsyms(struct maps *kmaps, struct dso *dso, u64 delta,
if (root->cnt == 0) {
root->symbols = old_symbols;
root->cnt = write_idx;
+ root->sorted_cnt = (delta == 0) ? write_idx : 0;
root->allocated = old_allocated;
root->sorted = (delta == 0);
old_symbols = NULL;
} else {
for (unsigned int j = 0; j < write_idx; j++)
__symbols__insert(root, old_symbols[j]);
+ root->sorted_cnt = 0;
root->sorted = false;
}
zfree(&root->symbols_by_name);
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index b4e5fdf5c4d6..066697d8da8f 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -119,6 +119,11 @@ struct symbols {
struct symbol **symbols_by_name;
/** @cnt: Number of valid entries in @symbols. */
unsigned int cnt;
+ /**
+ * @sorted_cnt: Number of leading entries in @symbols that are sorted
+ * by address range. Equal to @cnt when @sorted is true.
+ */
+ unsigned int sorted_cnt;
/** @allocated: Capacity (in entries) of the @symbols allocation. */
unsigned int allocated;
/** @num_by_name: Number of valid entries in @symbols_by_name. */
--
2.56.0.313.g2479322e68-goog
^ permalink raw reply [flat|nested] 37+ messages in thread* [PATCH v2 10/18] perf maps: Optimize tail insertion and small-append sorting
2026-10-27 23:14 ` [PATCH v2 00/18] perf symbol: Reference counting, sorted arrays, and memory shrinking Ian Rogers
` (8 preceding siblings ...)
2026-10-27 23:15 ` [PATCH v2 09/18] perf symbol: Optimize symbol sorting with natural-run merge sort and binary insertion Ian Rogers
@ 2026-10-27 23:15 ` Ian Rogers
2026-10-27 23:15 ` [PATCH v2 11/18] perf symbol-elf: Cache ELF program/section headers and defer symbol name lookup Ian Rogers
` (8 subsequent siblings)
18 siblings, 0 replies; 37+ messages in thread
From: Ian Rogers @ 2026-10-27 23:15 UTC (permalink / raw)
To: irogers, acme, namhyung
Cc: adrian.hunter, haghdoost, james.clark, jolsa, linux-kernel,
linux-perf-users, mingo, peterz
User-space map insertions (from /proc/pid/maps synthesis, runtime
PERF_RECORD_MMAP2, and jitdump) and BPF ksymbol registrations all flow
through maps__fixup_overlap_and_insert() -> first_ending_after().
Currently first_ending_after() has a fast-path check for the first map
(maps_by_address[0]), but no fast-path check for the last map
(maps_by_address[high]). Because /proc/pid/maps entries are synthesized
in ascending address order, every synthesized mapping starts at or after
maps_by_address[high]'s end, yet currently executes the full O(log N)
binary search loop. Add a tail check in first_ending_after() so
non-overlapping ascending insertions complete in O(1).
Additionally, track the sorted prefix lengths (nr_maps_by_address_sorted
and nr_maps_by_name_sorted) in struct maps:
- In __maps__sort_by_address() and maps__sort_by_name(), when the
unsorted tail is small (k <= 8) or the total map count is small
(n <= 64), binary-insert the newly appended maps into the sorted
prefix instead of re-running qsort() over the entire array. This
avoids O(N^2 log N) re-sorting when maps__find_by_name() is called
repeatedly between single-map maps__insert() calls in
dso__process_kernel_symbol() and dso__split_kallsyms().
- In maps__by_address_index() and maps__by_name_index(), binary-search
the known-sorted prefix before scanning only the unsorted tail.
- Add a unit test in tools/perf/tests/maps.c covering the tail
fast-path and incremental binary-insertion sort.
Using 'perf bench internals maps':
Before:
Average ascending insert + lookup: 63.370 usec (+- 0.476 usec)
Average append + lookup: 7477.450 usec (+- 20.829 usec)
After (-6.9% ascending, -98.7% append):
Average ascending insert + lookup: 58.970 usec (+- 0.609 usec)
Average append + lookup: 96.670 usec (+- 1.353 usec)
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/tests/maps.c | 58 +++++++++
tools/perf/util/maps.c | 281 +++++++++++++++++++++++++++++++++-------
2 files changed, 289 insertions(+), 50 deletions(-)
diff --git a/tools/perf/tests/maps.c b/tools/perf/tests/maps.c
index 2c05d62f40dc..1618731b91d0 100644
--- a/tools/perf/tests/maps.c
+++ b/tools/perf/tests/maps.c
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
#include <inttypes.h>
+#include <internal/rc_check.h>
#include <linux/compiler.h>
#include <linux/kernel.h>
#include "tests.h"
@@ -233,9 +234,66 @@ static int test__maps__fixup_overlap_and_insert(struct test_suite *t __maybe_unu
return TEST_OK;
}
+static int test__maps__incremental_sort(struct test_suite *t __maybe_unused,
+ int subtest __maybe_unused)
+{
+ struct map_def ascending_maps[] = {
+ { "map_c", 1000, 2000 },
+ { "map_a", 2000, 3000 },
+ { "map_d", 4000, 5000 },
+ };
+ struct map_def out_of_order = { "map_b", 3000, 4000 };
+ struct map *map, *found;
+ struct maps *maps = maps__new(NULL);
+
+ TEST_ASSERT_VAL("failed to create maps", maps);
+
+ /* Exercise tail fast-path in maps__fixup_overlap_and_insert. */
+ for (unsigned int i = 0; i < ARRAY_SIZE(ascending_maps); i++) {
+ map = dso__new_map(ascending_maps[i].name);
+ TEST_ASSERT_VAL("failed to create map", map);
+ map__set_start(map, ascending_maps[i].start);
+ map__set_end(map, ascending_maps[i].end);
+ TEST_ASSERT_VAL("failed to insert sorted map",
+ maps__fixup_overlap_and_insert(maps, map) == 0);
+ map__put(map);
+ }
+
+ /* Populate maps_by_name and verify lookup. */
+ found = maps__find_by_name(maps, "map_a");
+ TEST_ASSERT_VAL("failed to find map_a by name", found && map__start(found) == 2000);
+ map__zput(found);
+
+ /* Append out-of-order map via maps__insert and verify binary-insertion sort. */
+ map = dso__new_map(out_of_order.name);
+ TEST_ASSERT_VAL("failed to create map_b", map);
+ map__set_start(map, out_of_order.start);
+ map__set_end(map, out_of_order.end);
+ TEST_ASSERT_VAL("failed to insert map_b", maps__insert(maps, map) == 0);
+
+ found = maps__find(maps, 3500);
+ TEST_ASSERT_VAL("failed to find map_b by address", RC_CHK_EQUAL(found, map));
+ map__zput(found);
+
+ found = maps__find_by_name(maps, "map_b");
+ TEST_ASSERT_VAL("failed to find map_b by name", RC_CHK_EQUAL(found, map));
+ map__zput(found);
+
+ maps__remove(maps, map);
+ map__zput(map);
+
+ found = maps__find_by_name(maps, "map_d");
+ TEST_ASSERT_VAL("failed to find map_d after removal", found && map__start(found) == 4000);
+ map__zput(found);
+
+ maps__zput(maps);
+ return TEST_OK;
+}
+
static struct test_case tests__maps[] = {
TEST_CASE("Test merge_in interface", maps__merge_in),
TEST_CASE("Test fix up overlap interface", maps__fixup_overlap_and_insert),
+ TEST_CASE("Test incremental sort and tail insert", maps__incremental_sort),
{ .name = NULL, }
};
diff --git a/tools/perf/util/maps.c b/tools/perf/util/maps.c
index a6cbe65cc5ca..afd2603e77d5 100644
--- a/tools/perf/util/maps.c
+++ b/tools/perf/util/maps.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
#include <errno.h>
#include <stdlib.h>
+#include <linux/kernel.h>
#include <linux/zalloc.h>
#include "debug.h"
#include "dso.h"
@@ -56,6 +57,16 @@ DECLARE_RC_STRUCT(maps) {
* maps_by_name.
*/
unsigned int nr_maps_allocated;
+ /**
+ * @nr_maps_by_address_sorted: number of leading entries in
+ * maps_by_address that are known to be sorted.
+ */
+ unsigned int nr_maps_by_address_sorted;
+ /**
+ * @nr_maps_by_name_sorted: number of leading entries in maps_by_name
+ * that are known to be sorted.
+ */
+ unsigned int nr_maps_by_name_sorted;
/**
* @last_search_by_name_idx: cache of last found by name entry's index
* as frequent searches for the same dso name are common.
@@ -72,8 +83,17 @@ DECLARE_RC_STRUCT(maps) {
static void check_invariants(const struct maps *maps __maybe_unused)
{
#ifndef NDEBUG
- assert(RC_CHK_ACCESS(maps)->nr_maps <= RC_CHK_ACCESS(maps)->nr_maps_allocated);
- for (unsigned int i = 0; i < RC_CHK_ACCESS(maps)->nr_maps; i++) {
+ unsigned int nr_maps = RC_CHK_ACCESS(maps)->nr_maps;
+ unsigned int nr_addr_sorted = RC_CHK_ACCESS(maps)->nr_maps_by_address_sorted;
+ unsigned int nr_name_sorted = RC_CHK_ACCESS(maps)->nr_maps_by_name_sorted;
+
+ assert(nr_maps <= RC_CHK_ACCESS(maps)->nr_maps_allocated);
+ assert(nr_addr_sorted <= nr_maps);
+ assert(!RC_CHK_ACCESS(maps)->maps_by_address_sorted || nr_addr_sorted == nr_maps);
+ assert(nr_name_sorted <= nr_maps);
+ assert(!RC_CHK_ACCESS(maps)->maps_by_name_sorted || nr_name_sorted == nr_maps);
+
+ for (unsigned int i = 0; i < nr_maps; i++) {
struct map *map = RC_CHK_ACCESS(maps)->maps_by_address[i];
/* Check map is well-formed. */
@@ -84,28 +104,25 @@ static void check_invariants(const struct maps *maps __maybe_unused)
if (map__dso(map) && dso__kernel(map__dso(map)))
assert(RC_CHK_EQUAL(map__kmap(map)->kmaps, maps));
- if (i > 0) {
+ if (i > 0 && i < nr_addr_sorted) {
struct map *prev = RC_CHK_ACCESS(maps)->maps_by_address[i - 1];
- /* If addresses are sorted... */
- if (RC_CHK_ACCESS(maps)->maps_by_address_sorted) {
- /* Maps should be in start address order. */
- assert(map__start(prev) <= map__start(map));
- /*
- * If the ends of maps aren't broken (during
- * construction) then they should be ordered
- * too.
- */
- if (!RC_CHK_ACCESS(maps)->ends_broken) {
- assert(map__end(prev) <= map__end(map));
- assert(map__end(prev) <= map__start(map) ||
- map__start(prev) == map__start(map));
- }
+ /* Maps in sorted prefix should be in start address order. */
+ assert(map__start(prev) <= map__start(map));
+ /*
+ * If the ends of maps aren't broken (during
+ * construction) then they should be ordered
+ * too.
+ */
+ if (!RC_CHK_ACCESS(maps)->ends_broken) {
+ assert(map__end(prev) <= map__end(map));
+ assert(map__end(prev) <= map__start(map) ||
+ map__start(prev) == map__start(map));
}
}
}
if (RC_CHK_ACCESS(maps)->maps_by_name) {
- for (unsigned int i = 0; i < RC_CHK_ACCESS(maps)->nr_maps; i++) {
+ for (unsigned int i = 0; i < nr_maps; i++) {
struct map *map = RC_CHK_ACCESS(maps)->maps_by_name[i];
/*
@@ -137,6 +154,14 @@ static void maps__set_nr_maps_allocated(struct maps *maps, unsigned int nr_maps_
static void maps__set_nr_maps(struct maps *maps, unsigned int nr_maps)
{
RC_CHK_ACCESS(maps)->nr_maps = nr_maps;
+ if (RC_CHK_ACCESS(maps)->maps_by_address_sorted ||
+ RC_CHK_ACCESS(maps)->nr_maps_by_address_sorted > nr_maps) {
+ RC_CHK_ACCESS(maps)->nr_maps_by_address_sorted = nr_maps;
+ }
+ if (RC_CHK_ACCESS(maps)->maps_by_name_sorted ||
+ RC_CHK_ACCESS(maps)->nr_maps_by_name_sorted > nr_maps) {
+ RC_CHK_ACCESS(maps)->nr_maps_by_name_sorted = nr_maps;
+ }
}
/* Not in the header, to aid reference counting. */
@@ -160,6 +185,8 @@ static bool maps__maps_by_address_sorted(const struct maps *maps)
static void maps__set_maps_by_address_sorted(struct maps *maps, bool value)
{
RC_CHK_ACCESS(maps)->maps_by_address_sorted = value;
+ if (value)
+ RC_CHK_ACCESS(maps)->nr_maps_by_address_sorted = maps__nr_maps(maps);
}
static bool maps__maps_by_name_sorted(const struct maps *maps)
@@ -170,6 +197,8 @@ static bool maps__maps_by_name_sorted(const struct maps *maps)
static void maps__set_maps_by_name_sorted(struct maps *maps, bool value)
{
RC_CHK_ACCESS(maps)->maps_by_name_sorted = value;
+ if (value)
+ RC_CHK_ACCESS(maps)->nr_maps_by_name_sorted = maps__nr_maps(maps);
}
struct machine *maps__machine(const struct maps *maps)
@@ -241,6 +270,8 @@ static void maps__init(struct maps *maps, struct machine *machine)
refcount_set(maps__refcnt(maps), 1);
RC_CHK_ACCESS(maps)->nr_maps = 0;
RC_CHK_ACCESS(maps)->nr_maps_allocated = 0;
+ RC_CHK_ACCESS(maps)->nr_maps_by_address_sorted = 0;
+ RC_CHK_ACCESS(maps)->nr_maps_by_name_sorted = 0;
RC_CHK_ACCESS(maps)->last_search_by_name_idx = 0;
RC_CHK_ACCESS(maps)->maps_by_address_sorted = true;
RC_CHK_ACCESS(maps)->maps_by_name_sorted = false;
@@ -313,6 +344,7 @@ static void __maps__free_maps_by_name(struct maps *maps)
zfree(&RC_CHK_ACCESS(maps)->maps_by_name);
/* Consistent with maps__init(). When maps_by_name == NULL, maps_by_name_sorted == false */
+ RC_CHK_ACCESS(maps)->nr_maps_by_name_sorted = 0;
maps__set_maps_by_name_sorted(maps, false);
}
@@ -339,15 +371,54 @@ static int map__start_cmp(const void *a, const void *b)
return map_a_start > map_b_start ? 1 : -1;
}
+static unsigned int maps__by_address_upper_bound(struct map **arr, unsigned int len,
+ const struct map *map)
+{
+ unsigned int low = 0, high = len;
+
+ while (low < high) {
+ unsigned int mid = low + (high - low) / 2;
+
+ if (map__start_cmp(&arr[mid], &map) <= 0)
+ low = mid + 1;
+ else
+ high = mid;
+ }
+ return low;
+}
+
static void __maps__sort_by_address(struct maps *maps)
{
+ struct map **maps_by_address;
+ unsigned int n, m, k;
+
if (maps__maps_by_address_sorted(maps))
return;
- qsort(maps__maps_by_address(maps),
- maps__nr_maps(maps),
- sizeof(struct map *),
- map__start_cmp);
+ maps_by_address = maps__maps_by_address(maps);
+ n = maps__nr_maps(maps);
+ m = RC_CHK_ACCESS(maps)->nr_maps_by_address_sorted;
+ if (m > n)
+ m = 0;
+ k = n - m;
+
+ if (k <= 8 || n <= 64) {
+ for (unsigned int i = max(m, 1U); i < n; i++) {
+ struct map *map = maps_by_address[i];
+
+ if (map__start_cmp(&maps_by_address[i - 1], &map) > 0) {
+ unsigned int pos =
+ maps__by_address_upper_bound(maps_by_address, i, map);
+
+ memmove(&maps_by_address[pos + 1],
+ &maps_by_address[pos],
+ (i - pos) * sizeof(*maps_by_address));
+ maps_by_address[pos] = map;
+ }
+ }
+ } else {
+ qsort(maps_by_address, n, sizeof(struct map *), map__start_cmp);
+ }
maps__set_maps_by_address_sorted(maps, true);
}
@@ -373,6 +444,22 @@ static int map__strcmp(const void *a, const void *b)
return ret;
}
+static unsigned int maps__by_name_upper_bound(struct map **arr, unsigned int len,
+ const struct map *map)
+{
+ unsigned int low = 0, high = len;
+
+ while (low < high) {
+ unsigned int mid = low + (high - low) / 2;
+
+ if (map__strcmp(&arr[mid], &map) <= 0)
+ low = mid + 1;
+ else
+ high = mid;
+ }
+ return low;
+}
+
static int maps__sort_by_name(struct maps *maps)
{
int err = 0;
@@ -380,6 +467,7 @@ static int maps__sort_by_name(struct maps *maps)
down_write(maps__lock(maps));
if (!maps__maps_by_name_sorted(maps)) {
struct map **maps_by_name = maps__maps_by_name(maps);
+ unsigned int n = maps__nr_maps(maps);
if (!maps_by_name) {
maps_by_name = malloc(RC_CHK_ACCESS(maps)->nr_maps_allocated *
@@ -388,18 +476,38 @@ static int maps__sort_by_name(struct maps *maps)
err = -ENOMEM;
else {
struct map **maps_by_address = maps__maps_by_address(maps);
- unsigned int n = maps__nr_maps(maps);
maps__set_maps_by_name(maps, maps_by_name);
for (unsigned int i = 0; i < n; i++)
maps_by_name[i] = map__get(maps_by_address[i]);
+ RC_CHK_ACCESS(maps)->nr_maps_by_name_sorted = 0;
}
}
if (!err) {
- qsort(maps_by_name,
- maps__nr_maps(maps),
- sizeof(struct map *),
- map__strcmp);
+ unsigned int m = RC_CHK_ACCESS(maps)->nr_maps_by_name_sorted;
+ unsigned int k;
+
+ if (m > n)
+ m = 0;
+ k = n - m;
+ if (k <= 8 || n <= 64) {
+ for (unsigned int i = max(m, 1U); i < n; i++) {
+ struct map *map = maps_by_name[i];
+
+ if (map__strcmp(&maps_by_name[i - 1], &map) > 0) {
+ unsigned int pos =
+ maps__by_name_upper_bound(maps_by_name,
+ i, map);
+
+ memmove(&maps_by_name[pos + 1],
+ &maps_by_name[pos],
+ (i - pos) * sizeof(*maps_by_name));
+ maps_by_name[pos] = map;
+ }
+ }
+ } else {
+ qsort(maps_by_name, n, sizeof(struct map *), map__strcmp);
+ }
maps__set_maps_by_name_sorted(maps, true);
}
}
@@ -411,19 +519,27 @@ static int maps__sort_by_name(struct maps *maps)
static unsigned int maps__by_address_index(const struct maps *maps, const struct map *map)
{
struct map **maps_by_address = maps__maps_by_address(maps);
+ unsigned int nr_sorted = RC_CHK_ACCESS(maps)->nr_maps_by_address_sorted;
+ unsigned int nr_maps = maps__nr_maps(maps);
+
+ if (nr_sorted > nr_maps)
+ nr_sorted = 0;
- if (maps__maps_by_address_sorted(maps)) {
+ if (nr_sorted > 0) {
struct map **mapp =
- bsearch(&map, maps__maps_by_address(maps), maps__nr_maps(maps),
+ bsearch(&map, maps_by_address, nr_sorted,
sizeof(*mapp), map__start_cmp);
if (mapp)
return mapp - maps_by_address;
- } else {
- for (unsigned int i = 0; i < maps__nr_maps(maps); i++) {
- if (RC_CHK_ACCESS(maps_by_address[i]) == RC_CHK_ACCESS(map))
- return i;
- }
+ }
+ for (unsigned int i = nr_sorted; i < nr_maps; i++) {
+ if (RC_CHK_ACCESS(maps_by_address[i]) == RC_CHK_ACCESS(map))
+ return i;
+ }
+ for (unsigned int i = 0; i < nr_sorted; i++) {
+ if (RC_CHK_ACCESS(maps_by_address[i]) == RC_CHK_ACCESS(map))
+ return i;
}
pr_err("Map missing from maps");
return -1;
@@ -432,19 +548,27 @@ static unsigned int maps__by_address_index(const struct maps *maps, const struct
static unsigned int maps__by_name_index(const struct maps *maps, const struct map *map)
{
struct map **maps_by_name = maps__maps_by_name(maps);
+ unsigned int nr_sorted = RC_CHK_ACCESS(maps)->nr_maps_by_name_sorted;
+ unsigned int nr_maps = maps__nr_maps(maps);
+
+ if (nr_sorted > nr_maps)
+ nr_sorted = 0;
- if (maps__maps_by_name_sorted(maps)) {
+ if (nr_sorted > 0) {
struct map **mapp =
- bsearch(&map, maps_by_name, maps__nr_maps(maps),
+ bsearch(&map, maps_by_name, nr_sorted,
sizeof(*mapp), map__strcmp);
if (mapp)
return mapp - maps_by_name;
- } else {
- for (unsigned int i = 0; i < maps__nr_maps(maps); i++) {
- if (RC_CHK_ACCESS(maps_by_name[i]) == RC_CHK_ACCESS(map))
- return i;
- }
+ }
+ for (unsigned int i = nr_sorted; i < nr_maps; i++) {
+ if (RC_CHK_ACCESS(maps_by_name[i]) == RC_CHK_ACCESS(map))
+ return i;
+ }
+ for (unsigned int i = 0; i < nr_sorted; i++) {
+ if (RC_CHK_ACCESS(maps_by_name[i]) == RC_CHK_ACCESS(map))
+ return i;
}
pr_err("Map missing from maps");
return -1;
@@ -519,8 +643,12 @@ static int __maps__insert(struct maps *maps, struct map *new)
/* Sorted if maps were already sorted and this map starts after the last one. */
maps__set_maps_by_address_sorted(maps,
maps__maps_by_address_sorted(maps) &&
- map__end(maps_by_address[nr_maps - 2]) <= map__start(new));
- maps__set_maps_by_name_sorted(maps, false);
+ map__end(maps_by_address[nr_maps - 2]) <= map__start(new) &&
+ map__start_cmp(&maps_by_address[nr_maps - 2], &new) <= 0);
+ maps__set_maps_by_name_sorted(maps,
+ maps_by_name &&
+ maps__maps_by_name_sorted(maps) &&
+ map__strcmp(&maps_by_name[nr_maps - 2], &new) <= 0);
}
if (map__end(new) < map__start(new))
RC_CHK_ACCESS(maps)->ends_broken = true;
@@ -552,6 +680,8 @@ static void __maps__remove(struct maps *maps, struct map *map)
memmove(&maps_by_address[address_idx],
&maps_by_address[address_idx + 1],
(nr_maps - address_idx - 1) * sizeof(*maps_by_address));
+ if (address_idx < RC_CHK_ACCESS(maps)->nr_maps_by_address_sorted)
+ --RC_CHK_ACCESS(maps)->nr_maps_by_address_sorted;
if (maps_by_name) {
unsigned int name_idx = maps__by_name_index(maps, map);
@@ -560,9 +690,16 @@ static void __maps__remove(struct maps *maps, struct map *map)
memmove(&maps_by_name[name_idx],
&maps_by_name[name_idx + 1],
(nr_maps - name_idx - 1) * sizeof(*maps_by_name));
+ if (name_idx < RC_CHK_ACCESS(maps)->nr_maps_by_name_sorted)
+ --RC_CHK_ACCESS(maps)->nr_maps_by_name_sorted;
}
- --RC_CHK_ACCESS(maps)->nr_maps;
+ --nr_maps;
+ RC_CHK_ACCESS(maps)->nr_maps = nr_maps;
+ if (RC_CHK_ACCESS(maps)->nr_maps_by_address_sorted == nr_maps)
+ RC_CHK_ACCESS(maps)->maps_by_address_sorted = true;
+ if (maps_by_name && RC_CHK_ACCESS(maps)->nr_maps_by_name_sorted == nr_maps)
+ RC_CHK_ACCESS(maps)->maps_by_name_sorted = true;
}
void maps__remove(struct maps *maps, struct map *map)
@@ -603,7 +740,9 @@ int maps__mutate_mapping(struct maps *maps, struct map *map,
err = mutate_cb(map, data);
+ RC_CHK_ACCESS(maps)->nr_maps_by_address_sorted = 0;
RC_CHK_ACCESS(maps)->maps_by_address_sorted = false;
+ RC_CHK_ACCESS(maps)->nr_maps_by_name_sorted = 0;
RC_CHK_ACCESS(maps)->maps_by_name_sorted = false;
up_write(maps__lock(maps));
@@ -858,6 +997,10 @@ static unsigned int first_ending_after(struct maps *maps, const struct map *map)
assert(maps__maps_by_address_sorted(maps));
if (low <= high && map__end(maps_by_address[0]) > map__start(map))
return 0;
+ if (low <= high && !RC_CHK_ACCESS(maps)->ends_broken &&
+ map__end(maps_by_address[high]) <= map__start(map)) {
+ return first;
+ }
while (low <= high) {
int mid = (low + high) / 2;
@@ -925,7 +1068,17 @@ static int __maps__insert_sorted(struct maps *maps, unsigned int first_after_ind
maps_by_name[nr_maps + 1] = map__get(new2);
}
RC_CHK_ACCESS(maps)->nr_maps = nr_maps + to_add;
- maps__set_maps_by_name_sorted(maps, false);
+ RC_CHK_ACCESS(maps)->nr_maps_by_address_sorted = nr_maps + to_add;
+ if (maps_by_name) {
+ if (nr_maps == 0 && to_add == 1) {
+ maps__set_maps_by_name_sorted(maps, true);
+ } else {
+ maps__set_maps_by_name_sorted(maps,
+ maps__maps_by_name_sorted(maps) &&
+ map__strcmp(&maps_by_name[nr_maps - 1], &new1) <= 0 &&
+ (!new2 || map__strcmp(&new1, &new2) <= 0));
+ }
+ }
map__set_kmap_maps(new1, maps);
map__set_kmap_maps(new2, maps);
@@ -1049,9 +1202,16 @@ static int __maps__fixup_overlap_and_insert(struct maps *maps, struct map *new)
map__set_kmap_maps(new, maps);
if (maps_by_name) {
+ unsigned int nr_maps = maps__nr_maps(maps);
+
map__put(maps_by_name[ni]);
- maps_by_name[ni] = map__get(new);
- maps__set_maps_by_name_sorted(maps, false);
+ memmove(&maps_by_name[ni],
+ &maps_by_name[ni + 1],
+ (nr_maps - ni - 1) * sizeof(*maps_by_name));
+ maps_by_name[nr_maps - 1] = map__get(new);
+ if (ni < RC_CHK_ACCESS(maps)->nr_maps_by_name_sorted)
+ --RC_CHK_ACCESS(maps)->nr_maps_by_name_sorted;
+ maps__set_maps_by_name_sorted(maps, nr_maps <= 1);
}
err = __maps__insert_sorted(maps, i + 1, after, NULL);
@@ -1077,8 +1237,13 @@ static int __maps__fixup_overlap_and_insert(struct maps *maps, struct map *new)
if (maps_by_name) {
map__put(maps_by_name[ni]);
- maps_by_name[ni] = map__get(new);
- maps__set_maps_by_name_sorted(maps, false);
+ memmove(&maps_by_name[ni],
+ &maps_by_name[ni + 1],
+ (nr_maps - ni - 1) * sizeof(*maps_by_name));
+ maps_by_name[nr_maps - 1] = map__get(new);
+ if (ni < RC_CHK_ACCESS(maps)->nr_maps_by_name_sorted)
+ --RC_CHK_ACCESS(maps)->nr_maps_by_name_sorted;
+ maps__set_maps_by_name_sorted(maps, nr_maps <= 1);
}
check_invariants(maps);
@@ -1094,14 +1259,22 @@ static int __maps__fixup_overlap_and_insert(struct maps *maps, struct map *new)
memmove(&maps_by_address[i],
&maps_by_address[i + 1],
(nr_maps - i - 1) * sizeof(*maps_by_address));
+ --RC_CHK_ACCESS(maps)->nr_maps_by_address_sorted;
if (maps_by_name) {
map__put(maps_by_name[ni]);
memmove(&maps_by_name[ni],
&maps_by_name[ni + 1],
(nr_maps - ni - 1) * sizeof(*maps_by_name));
+ if (ni < RC_CHK_ACCESS(maps)->nr_maps_by_name_sorted)
+ --RC_CHK_ACCESS(maps)->nr_maps_by_name_sorted;
}
--RC_CHK_ACCESS(maps)->nr_maps;
+ if (maps_by_name &&
+ RC_CHK_ACCESS(maps)->nr_maps_by_name_sorted ==
+ RC_CHK_ACCESS(maps)->nr_maps) {
+ RC_CHK_ACCESS(maps)->maps_by_name_sorted = true;
+ }
check_invariants(maps);
/*
* Maps are ordered but no need to increase `i` as the
@@ -1181,9 +1354,17 @@ int maps__copy_from(struct maps *dest, struct maps *parent)
if (err)
map__put(new);
}
- maps__set_maps_by_address_sorted(dest, maps__maps_by_address_sorted(parent));
+ if (maps__maps_by_address_sorted(parent)) {
+ maps__set_maps_by_address_sorted(dest, true);
+ } else {
+ RC_CHK_ACCESS(dest)->nr_maps_by_address_sorted =
+ min(RC_CHK_ACCESS(parent)->nr_maps_by_address_sorted,
+ RC_CHK_ACCESS(dest)->nr_maps);
+ maps__set_maps_by_address_sorted(dest, false);
+ }
RC_CHK_ACCESS(dest)->last_search_by_name_idx = 0;
/* Values were copied into the name array in address order. */
+ RC_CHK_ACCESS(dest)->nr_maps_by_name_sorted = 0;
maps__set_maps_by_name_sorted(dest, false);
} else {
/* Unexpected copying to a maps containing entries. */
--
2.56.0.313.g2479322e68-goog
^ permalink raw reply [flat|nested] 37+ messages in thread* [PATCH v2 11/18] perf symbol-elf: Cache ELF program/section headers and defer symbol name lookup
2026-10-27 23:14 ` [PATCH v2 00/18] perf symbol: Reference counting, sorted arrays, and memory shrinking Ian Rogers
` (9 preceding siblings ...)
2026-10-27 23:15 ` [PATCH v2 10/18] perf maps: Optimize tail insertion and small-append sorting Ian Rogers
@ 2026-10-27 23:15 ` Ian Rogers
2026-10-27 23:15 ` [PATCH v2 12/18] perf rwsem: Add down_read_trylock() and down_write_trylock() Ian Rogers
` (7 subsequent siblings)
18 siblings, 0 replies; 37+ messages in thread
From: Ian Rogers @ 2026-10-27 23:15 UTC (permalink / raw)
To: irogers, acme, namhyung
Cc: adrian.hunter, haghdoost, james.clark, jolsa, linux-kernel,
linux-perf-users, mingo, peterz
Profiling dso__load_sym_internal() on large binaries showed significant
time spent in repeated libelf section and program header lookups
(elf_section_by_idx() and elf_read_program_header()) for each symbol,
resolving elf_sym__name() before filtering out non-symbol/non-allocated
entries, and running symbols__fixup_end() and symbols__fixup_duplicate()
twice when both .symtab and .dynsym are loaded.
Speed up ELF symbol table loading in dso__load_sym_internal() and
dso__load_sym():
- Pre-load PT_LOAD program headers into struct elf_phdr_cache and cache
per-section metadata in a 64-entry direct-mapped table in
dso__load_sym_internal() to avoid repeated libelf section and program
header lookups during symbol iteration.
- Defer elf_sym__name() lookup until after non-allocated and non-symbol
entries have been filtered out, and run symbols__fixup_end() and
symbols__fixup_duplicate() once in dso__load_sym() after loading both
.symtab and .dynsym.
Using 'perf bench internals symbols':
Before (perf binary):
Average DSO load + sort: 5.808 msec (+- 0.047 msec)
After (perf binary, -8.3%):
Average DSO load + sort: 5.324 msec (+- 0.045 msec)
Before (perf binary, --no-demangle):
Average DSO load + sort: 3.948 msec (+- 0.064 msec)
After (perf binary, --no-demangle, -13.7%):
Average DSO load + sort: 3.408 msec (+- 0.034 msec)
Before (libLLVM.so.21.1, --no-demangle):
Average DSO load + sort: 22.433 msec (+- 0.260 msec)
After (libLLVM.so.21.1, --no-demangle, -14.8%):
Average DSO load + sort: 19.122 msec (+- 0.226 msec)
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/symbol-elf.c | 275 +++++++++++++++++++++++++++--------
1 file changed, 217 insertions(+), 58 deletions(-)
diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c
index 4023174ed6db..6082136c9662 100644
--- a/tools/perf/util/symbol-elf.c
+++ b/tools/perf/util/symbol-elf.c
@@ -265,6 +265,118 @@ static int elf_read_program_header(Elf *elf, u64 vaddr, GElf_Phdr *phdr)
return -1;
}
+struct elf_phdr_range {
+ u64 vaddr;
+ u64 end;
+ u64 adjust;
+ GElf_Phdr phdr;
+};
+
+struct elf_phdr_cache {
+ struct elf_phdr_range inline_ranges[16];
+ struct elf_phdr_range *ranges;
+ size_t nr_ranges;
+ size_t last_idx;
+};
+
+static void elf_phdr_cache__init(struct elf_phdr_cache *cache, Elf *elf)
+{
+ size_t i, phdrnum = 0, cnt = 0;
+
+ cache->ranges = cache->inline_ranges;
+ cache->nr_ranges = 0;
+ cache->last_idx = 0;
+
+ if (!elf || elf_getphdrnum(elf, &phdrnum) || phdrnum == 0)
+ return;
+
+ if (phdrnum > ARRAY_SIZE(cache->inline_ranges)) {
+ cache->ranges = calloc(phdrnum, sizeof(*cache->ranges));
+ if (!cache->ranges) {
+ cache->ranges = cache->inline_ranges;
+ phdrnum = ARRAY_SIZE(cache->inline_ranges);
+ }
+ }
+
+ for (i = 0; i < phdrnum; i++) {
+ GElf_Phdr phdr;
+ u64 sz;
+
+ if (gelf_getphdr(elf, i, &phdr) == NULL)
+ break;
+ if (phdr.p_type != PT_LOAD)
+ continue;
+ sz = max(phdr.p_memsz, phdr.p_filesz);
+ if (!sz)
+ continue;
+ cache->ranges[cnt].vaddr = phdr.p_vaddr;
+ cache->ranges[cnt].end = phdr.p_vaddr + sz;
+ cache->ranges[cnt].adjust = phdr.p_vaddr - phdr.p_offset;
+ cache->ranges[cnt].phdr = phdr;
+ cnt++;
+ }
+ cache->nr_ranges = cnt;
+}
+
+static void elf_phdr_cache__exit(struct elf_phdr_cache *cache)
+{
+ if (cache->ranges && cache->ranges != cache->inline_ranges)
+ free(cache->ranges);
+}
+
+static const struct elf_phdr_range *
+elf_phdr_cache__find(struct elf_phdr_cache *cache, u64 vaddr)
+{
+ if (cache->nr_ranges == 0)
+ return NULL;
+
+ if (cache->last_idx < cache->nr_ranges) {
+ const struct elf_phdr_range *r = &cache->ranges[cache->last_idx];
+
+ if (vaddr >= r->vaddr && vaddr < r->end)
+ return r;
+ }
+ for (size_t i = 0; i < cache->nr_ranges; i++) {
+ const struct elf_phdr_range *r = &cache->ranges[i];
+
+ if (vaddr >= r->vaddr && vaddr < r->end) {
+ cache->last_idx = i;
+ return r;
+ }
+ }
+ return NULL;
+}
+
+#define ELF_SEC_CACHE_SIZE 64
+
+struct elf_sec_cache_entry {
+ uint32_t shndx;
+ bool valid;
+ bool is_alloc;
+ bool is_label_sec;
+ bool has_phdr;
+ u64 phdr_vaddr;
+ u64 phdr_end;
+ const char *section_name;
+ GElf_Shdr shdr;
+ GElf_Phdr phdr;
+};
+
+static inline const GElf_Phdr *
+elf_sec_cache__phdr(const struct elf_sec_cache_entry *sce,
+ struct elf_phdr_cache *phdr_cache,
+ u64 vaddr)
+{
+ const struct elf_phdr_range *pr;
+
+ if (sce->has_phdr && vaddr >= sce->phdr_vaddr && vaddr < sce->phdr_end)
+ return &sce->phdr;
+ pr = elf_phdr_cache__find(phdr_cache, vaddr);
+ if (!pr)
+ return NULL;
+ return &pr->phdr;
+}
+
struct rel_info {
u32 nr_entries;
u32 *sorted;
@@ -1543,6 +1655,8 @@ dso__load_sym_internal(struct dso *dso, struct map *map, struct symsrc *syms_ss,
int nr = 0;
bool remap_kernel = false, adjust_kernel_syms = false;
u64 max_text_sh_offset = 0;
+ struct elf_phdr_cache phdr_cache = {};
+ struct elf_sec_cache_entry sec_cache[ELF_SEC_CACHE_SIZE];
if (kmap && !kmaps)
return -1;
@@ -1634,14 +1748,18 @@ dso__load_sym_internal(struct dso *dso, struct map *map, struct symsrc *syms_ss,
if (kmodule && adjust_kernel_syms)
max_text_sh_offset = max_text_section(runtime_ss->elf, &runtime_ss->ehdr);
+ elf_phdr_cache__init(&phdr_cache, runtime_ss->elf);
+ memset(sec_cache, 0, sizeof(sec_cache));
+
curr_dso = dso__get(dso);
elf_symtab__for_each_symbol(syms, nr_syms, idx, sym) {
struct symbol *f;
- const char *elf_name = elf_sym__name(&sym, symstrs);
+ const char *elf_name = NULL;
char *demangled = NULL;
int is_label = elf_sym__is_label(&sym);
const char *section_name;
bool used_opd = false;
+ struct elf_sec_cache_entry *sce;
if (!is_label && !elf_sym__filter(&sym))
continue;
@@ -1652,6 +1770,7 @@ dso__load_sym_internal(struct dso *dso, struct map *map, struct symsrc *syms_ss,
* output:
*/
if (ehdr.e_machine == EM_ARM || ehdr.e_machine == EM_AARCH64) {
+ elf_name = elf_sym__name(&sym, symstrs);
if (elf_name[0] == '$' && strchr("adtx", elf_name[1])
&& (elf_name[2] == '\0' || elf_name[2] == '.'))
continue;
@@ -1659,13 +1778,19 @@ dso__load_sym_internal(struct dso *dso, struct map *map, struct symsrc *syms_ss,
/* Reject RISCV ELF "mapping symbols" */
if (ehdr.e_machine == EM_RISCV) {
+ if (!elf_name)
+ elf_name = elf_sym__name(&sym, symstrs);
if (elf_name[0] == '$' && strchr("dx", elf_name[1]))
continue;
}
/* Reject kernel mapping symbols for kernel DSOs only */
- if (dso__kernel(dso) && is_ignored_kernel_symbol(elf_name))
- continue;
+ if (dso__kernel(dso)) {
+ if (!elf_name)
+ elf_name = elf_sym__name(&sym, symstrs);
+ if (is_ignored_kernel_symbol(elf_name))
+ continue;
+ }
if (runtime_ss->opdsec && sym.st_shndx == runtime_ss->opdidx) {
u32 offset = sym.st_value - syms_ss->opdshdr.sh_addr;
@@ -1688,16 +1813,56 @@ dso__load_sym_internal(struct dso *dso, struct map *map, struct symsrc *syms_ss,
if (sym.st_shndx == SHN_ABS)
continue;
- sec = elf_getscn(syms_ss->elf, sym.st_shndx);
- if (!sec) {
- if (dynsym && ehdr.e_shnum &&
- sym.st_shndx < SHN_LORESERVE &&
- sym.st_shndx >= ehdr.e_shnum)
- continue;
- goto out_elf_end;
- }
+ sce = &sec_cache[sym.st_shndx & (ELF_SEC_CACHE_SIZE - 1)];
+ if (!sce->valid || sce->shndx != sym.st_shndx) {
+ const struct elf_phdr_range *pr;
- gelf_getshdr(sec, &shdr);
+ sec = elf_getscn(syms_ss->elf, sym.st_shndx);
+ if (!sec) {
+ if (dynsym && ehdr.e_shnum &&
+ sym.st_shndx < SHN_LORESERVE &&
+ sym.st_shndx >= ehdr.e_shnum)
+ continue;
+ goto out_elf_end;
+ }
+
+ gelf_getshdr(sec, &shdr);
+ secstrs = secstrs_sym;
+
+ /*
+ * We have to fallback to runtime when syms' section
+ * header has NOBITS set. NOBITS results in file offset
+ * (sh_offset) not being incremented. So sh_offset used
+ * below has different values for syms (invalid) and
+ * runtime (valid).
+ */
+ if (shdr.sh_type == SHT_NOBITS && (shdr.sh_flags & SHF_ALLOC)) {
+ sec = elf_getscn(runtime_ss->elf, sym.st_shndx);
+ if (!sec)
+ goto out_elf_end;
+
+ gelf_getshdr(sec, &shdr);
+ secstrs = secstrs_run;
+ }
+
+ sce->shndx = sym.st_shndx;
+ sce->valid = true;
+ sce->is_alloc = (shdr.sh_flags & SHF_ALLOC) != 0;
+ if (sce->is_alloc) {
+ sce->shdr = shdr;
+ sce->section_name = elf_sec__name(&shdr, secstrs);
+ sce->is_label_sec = elf_sec__filter(&shdr, secstrs) != 0;
+ pr = elf_phdr_cache__find(&phdr_cache, shdr.sh_addr);
+ if (pr) {
+ sce->has_phdr = true;
+ sce->phdr_vaddr = pr->vaddr;
+ sce->phdr_end = pr->end;
+ sce->phdr = pr->phdr;
+ } else {
+ sce->has_phdr = false;
+ }
+ }
+ }
/*
* If the attribute bit SHF_ALLOC is not set, the section
@@ -1707,30 +1872,13 @@ dso__load_sym_internal(struct dso *dso, struct map *map, struct symsrc *syms_ss,
* the section aren't loaded to memory during process execution,
* so skip them.
*/
- if (!(shdr.sh_flags & SHF_ALLOC))
+ if (!sce->is_alloc)
continue;
- secstrs = secstrs_sym;
-
- /*
- * We have to fallback to runtime when syms' section header has
- * NOBITS set. NOBITS results in file offset (sh_offset) not
- * being incremented. So sh_offset used below has different
- * values for syms (invalid) and runtime (valid).
- */
- if (shdr.sh_type == SHT_NOBITS) {
- sec = elf_getscn(runtime_ss->elf, sym.st_shndx);
- if (!sec)
- goto out_elf_end;
-
- gelf_getshdr(sec, &shdr);
- secstrs = secstrs_run;
- }
-
- if (is_label && !elf_sec__filter(&shdr, secstrs))
+ if (is_label && !sce->is_label_sec)
continue;
- section_name = elf_sec__name(&shdr, secstrs);
+ section_name = sce->section_name;
/* On ARM, symbols for thumb functions have 1 added to
* the symbol address as a flag - remove it */
@@ -1740,7 +1888,7 @@ dso__load_sym_internal(struct dso *dso, struct map *map, struct symsrc *syms_ss,
--sym.st_value;
if (dso__kernel(dso)) {
- if (dso__process_kernel_symbol(dso, map, &sym, &shdr,
+ if (dso__process_kernel_symbol(dso, map, &sym, &sce->shdr,
kmaps, kmap, &curr_dso,
section_name,
adjust_kernel_syms,
@@ -1750,17 +1898,20 @@ dso__load_sym_internal(struct dso *dso, struct map *map, struct symsrc *syms_ss,
goto out_elf_end;
} else if ((used_opd && runtime_ss->adjust_symbols) ||
(!used_opd && syms_ss->adjust_symbols)) {
- GElf_Phdr phdr;
+ const GElf_Phdr *phdr;
- if (elf_read_program_header(runtime_ss->elf,
- (u64)sym.st_value, &phdr)) {
+ phdr = elf_sec_cache__phdr(sce, &phdr_cache,
+ (u64)sym.st_value);
+ if (!phdr) {
pr_debug4("%s: failed to find program header for "
"symbol: %s st_value: %#" PRIx64 "\n",
- __func__, elf_name, (u64)sym.st_value);
+ __func__, elf_sym__name(&sym, symstrs),
+ (u64)sym.st_value);
pr_debug4("%s: adjusting symbol: st_value: %#" PRIx64 " "
"sh_addr: %#" PRIx64 " sh_offset: %#" PRIx64 "\n",
- __func__, (u64)sym.st_value, (u64)shdr.sh_addr,
- (u64)shdr.sh_offset);
+ __func__, (u64)sym.st_value,
+ (u64)sce->shdr.sh_addr,
+ (u64)sce->shdr.sh_offset);
/*
* Fail to find program header, let's rollback
* to use shdr.sh_addr and shdr.sh_offset to
@@ -1769,16 +1920,18 @@ dso__load_sym_internal(struct dso *dso, struct map *map, struct symsrc *syms_ss,
* still need to handle java JIT symbols in this
* case.
*/
- sym.st_value -= shdr.sh_addr - shdr.sh_offset;
+ sym.st_value -= sce->shdr.sh_addr - sce->shdr.sh_offset;
} else {
pr_debug4("%s: adjusting symbol: st_value: %#" PRIx64 " "
"p_vaddr: %#" PRIx64 " p_offset: %#" PRIx64 "\n",
- __func__, (u64)sym.st_value, (u64)phdr.p_vaddr,
- (u64)phdr.p_offset);
- sym.st_value -= phdr.p_vaddr - phdr.p_offset;
+ __func__, (u64)sym.st_value, (u64)phdr->p_vaddr,
+ (u64)phdr->p_offset);
+ sym.st_value -= phdr->p_vaddr - phdr->p_offset;
}
}
+ if (!elf_name)
+ elf_name = elf_sym__name(&sym, symstrs);
demangled = dso__demangle_sym(dso, kmodule, elf_name);
if (demangled != NULL)
elf_name = demangled;
@@ -1796,25 +1949,13 @@ dso__load_sym_internal(struct dso *dso, struct map *map, struct symsrc *syms_ss,
goto out_elf_end;
nr++;
}
- dso__put(curr_dso);
- /*
- * For misannotated, zeroed, ASM function sizes.
- */
- if (nr > 0) {
- symbols__fixup_end(dso__symbols(dso), false);
- symbols__fixup_duplicate(dso__symbols(dso));
- if (kmap) {
- /*
- * We need to fixup this here too because we create new
- * maps here, for things like vsyscall sections.
- */
- maps__fixup_end(kmaps);
- }
- }
+ dso__put(curr_dso);
+ elf_phdr_cache__exit(&phdr_cache);
return nr;
out_elf_end:
dso__put(curr_dso);
+ elf_phdr_cache__exit(&phdr_cache);
return -1;
}
@@ -1872,6 +2013,24 @@ int dso__load_sym(struct dso *dso, struct map *map, struct symsrc *syms_ss,
nr += err;
}
+ /*
+ * For misannotated, zeroed, ASM function sizes.
+ */
+ if (nr > 0) {
+ struct kmap *kmap = dso__kernel(dso) ? map__kmap(map) : NULL;
+ struct maps *kmaps = kmap ? map__kmaps(map) : NULL;
+
+ symbols__fixup_end(dso__symbols(dso), false);
+ symbols__fixup_duplicate(dso__symbols(dso));
+ if (kmap && kmaps) {
+ /*
+ * We need to fixup this here too because we create new
+ * maps here, for things like vsyscall sections.
+ */
+ maps__fixup_end(kmaps);
+ }
+ }
+
return nr;
}
--
2.56.0.313.g2479322e68-goog
^ permalink raw reply [flat|nested] 37+ messages in thread* [PATCH v2 12/18] perf rwsem: Add down_read_trylock() and down_write_trylock()
2026-10-27 23:14 ` [PATCH v2 00/18] perf symbol: Reference counting, sorted arrays, and memory shrinking Ian Rogers
` (10 preceding siblings ...)
2026-10-27 23:15 ` [PATCH v2 11/18] perf symbol-elf: Cache ELF program/section headers and defer symbol name lookup Ian Rogers
@ 2026-10-27 23:15 ` Ian Rogers
2026-10-27 23:15 ` [PATCH v2 13/18] perf vdso: Constify dso parameter in dso__is_vdso() Ian Rogers
` (6 subsequent siblings)
18 siblings, 0 replies; 37+ messages in thread
From: Ian Rogers @ 2026-10-27 23:15 UTC (permalink / raw)
To: irogers, acme, namhyung
Cc: adrian.hunter, haghdoost, james.clark, jolsa, linux-kernel,
linux-perf-users, mingo, peterz
On-demand memory shrinking (perf_session__shrink(), dsos__shrink(),
symbols__shrink()) can be triggered from OOM recovery inside allocation
wrappers while the current thread already holds a rw_semaphore (such as
symbols->lock, dsos->lock, or machines->lock). Non-blocking trylock
primitives are needed so shrinking paths can safely skip any locked
container without deadlocking.
Add non-blocking down_read_trylock() and down_write_trylock() wrappers
around pthread_rwlock_tryrdlock() / pthread_rwlock_trywrlock() and
pthread_mutex_trylock() along with SHARED_TRYLOCK_FUNCTION and
EXCLUSIVE_TRYLOCK_FUNCTION thread-safety annotations.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/mutex.h | 3 +++
tools/perf/util/rwsem.c | 20 ++++++++++++++++++++
tools/perf/util/rwsem.h | 2 ++
3 files changed, 25 insertions(+)
diff --git a/tools/perf/util/mutex.h b/tools/perf/util/mutex.h
index 5de1065ea2b9..de7dd6d8326c 100644
--- a/tools/perf/util/mutex.h
+++ b/tools/perf/util/mutex.h
@@ -57,6 +57,8 @@
/* Documents functions that try to acquire a lock, and return success or failure. */
#define EXCLUSIVE_TRYLOCK_FUNCTION(...) \
__attribute__((exclusive_trylock_function(__VA_ARGS__)))
+#define SHARED_TRYLOCK_FUNCTION(...) \
+ __attribute__((shared_trylock_function(__VA_ARGS__)))
/* Documents a function that expects a mutex to be held prior to entry. */
#define EXCLUSIVE_LOCKS_REQUIRED(...) __attribute__((exclusive_locks_required(__VA_ARGS__)))
@@ -78,6 +80,7 @@
#define SHARED_LOCK_FUNCTION(...)
#define UNLOCK_FUNCTION(...)
#define EXCLUSIVE_TRYLOCK_FUNCTION(...)
+#define SHARED_TRYLOCK_FUNCTION(...)
#define EXCLUSIVE_LOCKS_REQUIRED(...)
#define SHARED_LOCKS_REQUIRED(...)
#define NO_THREAD_SAFETY_ANALYSIS
diff --git a/tools/perf/util/rwsem.c b/tools/perf/util/rwsem.c
index 9d26832398db..37fa08bae5e9 100644
--- a/tools/perf/util/rwsem.c
+++ b/tools/perf/util/rwsem.c
@@ -37,6 +37,16 @@ int down_read(struct rw_semaphore *sem)
#endif
}
+bool down_read_trylock(struct rw_semaphore *sem)
+ NO_THREAD_SAFETY_ANALYSIS
+{
+#if RWS_ERRORCHECK
+ return mutex_trylock(&sem->mtx);
+#else
+ return perf_singlethreaded ? true : pthread_rwlock_tryrdlock(&sem->lock) == 0;
+#endif
+}
+
int up_read(struct rw_semaphore *sem)
NO_THREAD_SAFETY_ANALYSIS
{
@@ -59,6 +69,16 @@ int down_write(struct rw_semaphore *sem)
#endif
}
+bool down_write_trylock(struct rw_semaphore *sem)
+ NO_THREAD_SAFETY_ANALYSIS
+{
+#if RWS_ERRORCHECK
+ return mutex_trylock(&sem->mtx);
+#else
+ return perf_singlethreaded ? true : pthread_rwlock_trywrlock(&sem->lock) == 0;
+#endif
+}
+
int up_write(struct rw_semaphore *sem)
NO_THREAD_SAFETY_ANALYSIS
{
diff --git a/tools/perf/util/rwsem.h b/tools/perf/util/rwsem.h
index b102d8143181..782016c3e01f 100644
--- a/tools/perf/util/rwsem.h
+++ b/tools/perf/util/rwsem.h
@@ -22,9 +22,11 @@ int init_rwsem(struct rw_semaphore *sem);
int exit_rwsem(struct rw_semaphore *sem);
int down_read(struct rw_semaphore *sem) SHARED_LOCK_FUNCTION(sem);
+bool down_read_trylock(struct rw_semaphore *sem) SHARED_TRYLOCK_FUNCTION(true, sem);
int up_read(struct rw_semaphore *sem) UNLOCK_FUNCTION(sem);
int down_write(struct rw_semaphore *sem) EXCLUSIVE_LOCK_FUNCTION(sem);
+bool down_write_trylock(struct rw_semaphore *sem) EXCLUSIVE_TRYLOCK_FUNCTION(true, sem);
int up_write(struct rw_semaphore *sem) UNLOCK_FUNCTION(sem);
#endif /* _PERF_RWSEM_H */
--
2.56.0.313.g2479322e68-goog
^ permalink raw reply [flat|nested] 37+ messages in thread* [PATCH v2 13/18] perf vdso: Constify dso parameter in dso__is_vdso()
2026-10-27 23:14 ` [PATCH v2 00/18] perf symbol: Reference counting, sorted arrays, and memory shrinking Ian Rogers
` (11 preceding siblings ...)
2026-10-27 23:15 ` [PATCH v2 12/18] perf rwsem: Add down_read_trylock() and down_write_trylock() Ian Rogers
@ 2026-10-27 23:15 ` Ian Rogers
2026-10-27 23:15 ` [PATCH v2 14/18] perf machine: Protect machines->guests rbtree with a rw_semaphore Ian Rogers
` (5 subsequent siblings)
18 siblings, 0 replies; 37+ messages in thread
From: Ian Rogers @ 2026-10-27 23:15 UTC (permalink / raw)
To: irogers, acme, namhyung
Cc: adrian.hunter, haghdoost, james.clark, jolsa, linux-kernel,
linux-perf-users, mingo, peterz
dso__can_shrink_symbols(const struct dso *dso) needs to query
dso__is_vdso(dso) on a const struct dso pointer to exclude vDSO images
from symbol shrinking.
Change the parameter of dso__is_vdso() from 'struct dso *' to
'const struct dso *'.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/vdso.c | 2 +-
tools/perf/util/vdso.h | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/tools/perf/util/vdso.c b/tools/perf/util/vdso.c
index c12f5d8c4bf6..7f2dde70f3e4 100644
--- a/tools/perf/util/vdso.c
+++ b/tools/perf/util/vdso.c
@@ -361,7 +361,7 @@ struct dso *machine__findnew_vdso(struct machine *machine,
return __machine__addnew_vdso(machine, DSO__NAME_VDSO, file);
}
-bool dso__is_vdso(struct dso *dso)
+bool dso__is_vdso(const struct dso *dso)
{
return !strcmp(dso__short_name(dso), DSO__NAME_VDSO) ||
!strcmp(dso__short_name(dso), DSO__NAME_VDSO32) ||
diff --git a/tools/perf/util/vdso.h b/tools/perf/util/vdso.h
index bc74ace603ae..b6e05bc2d9f3 100644
--- a/tools/perf/util/vdso.h
+++ b/tools/perf/util/vdso.h
@@ -19,7 +19,7 @@ static inline bool is_vdso_map(const char *filename)
struct dso;
-bool dso__is_vdso(struct dso *dso);
+bool dso__is_vdso(const struct dso *dso);
struct machine;
struct thread;
--
2.56.0.313.g2479322e68-goog
^ permalink raw reply [flat|nested] 37+ messages in thread* [PATCH v2 14/18] perf machine: Protect machines->guests rbtree with a rw_semaphore
2026-10-27 23:14 ` [PATCH v2 00/18] perf symbol: Reference counting, sorted arrays, and memory shrinking Ian Rogers
` (12 preceding siblings ...)
2026-10-27 23:15 ` [PATCH v2 13/18] perf vdso: Constify dso parameter in dso__is_vdso() Ian Rogers
@ 2026-10-27 23:15 ` Ian Rogers
2026-10-27 23:15 ` [PATCH v2 15/18] perf symbol: Add memory shrinking for symbols, DSOs, and machines Ian Rogers
` (4 subsequent siblings)
18 siblings, 0 replies; 37+ messages in thread
From: Ian Rogers @ 2026-10-27 23:15 UTC (permalink / raw)
To: irogers, acme, namhyung
Cc: adrian.hunter, haghdoost, james.clark, jolsa, linux-kernel,
linux-perf-users, mingo, peterz
Unlike struct dsos, struct maps, and struct threads, struct machines had
no lock protecting its machines->guests rbtree. Synchronizing access to
machines->guests is needed both for concurrent guest machine lookups and
so machines__shrink() can safely iterate guest machines using
down_read_trylock(&machines->lock).
Add a rw_semaphore lock to struct machines to synchronize access to the
machines->guests rbtree across machines__add(), machines__find(),
machines__set_comm_exec(), machines__process_guests(),
machines__set_id_hdr_size(), machines__fprintf_dsos(),
machines__fprintf_dsos_buildid(), machines__destroy_kernel_maps(), and
machines__for_each_thread(), and check for an existing guest machine PID
under write lock in machines__add().
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/machine.c | 42 +++++++++++++++++++++++++++++++++------
tools/perf/util/machine.h | 1 +
2 files changed, 37 insertions(+), 6 deletions(-)
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index cb9e34220036..fb83da8ef09b 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -237,6 +237,7 @@ int machines__init(struct machines *machines, struct perf_session *session)
{
int err = machine__init(&machines->host, "", HOST_KERNEL_ID);
+ init_rwsem(&machines->lock);
machines->session = session;
machines->host.machines = machines;
machines->guests = RB_ROOT_CACHED;
@@ -247,12 +248,13 @@ void machines__exit(struct machines *machines)
{
machine__exit(&machines->host);
/* XXX exit guest */
+ exit_rwsem(&machines->lock);
}
struct machine *machines__add(struct machines *machines, pid_t pid,
const char *root_dir)
{
- struct rb_node **p = &machines->guests.rb_root.rb_node;
+ struct rb_node **p;
struct rb_node *parent = NULL;
struct machine *pos, *machine = malloc(sizeof(*machine));
bool leftmost = true;
@@ -265,9 +267,17 @@ struct machine *machines__add(struct machines *machines, pid_t pid,
return NULL;
}
+ down_write(&machines->lock);
+ p = &machines->guests.rb_root.rb_node;
while (*p != NULL) {
parent = *p;
pos = rb_entry(parent, struct machine, rb_node);
+ if (pid == pos->pid) {
+ up_write(&machines->lock);
+ machine__exit(machine);
+ free(machine);
+ return pos;
+ }
if (pid < pos->pid)
p = &(*p)->rb_left;
else {
@@ -278,8 +288,8 @@ struct machine *machines__add(struct machines *machines, pid_t pid,
rb_link_node(&machine->rb_node, parent, p);
rb_insert_color_cached(&machine->rb_node, &machines->guests, leftmost);
-
machine->machines = machines;
+ up_write(&machines->lock);
return machine;
}
@@ -290,16 +300,18 @@ void machines__set_comm_exec(struct machines *machines, bool comm_exec)
machines->host.comm_exec = comm_exec;
+ down_read(&machines->lock);
for (nd = rb_first_cached(&machines->guests); nd; nd = rb_next(nd)) {
struct machine *machine = rb_entry(nd, struct machine, rb_node);
machine->comm_exec = comm_exec;
}
+ up_read(&machines->lock);
}
struct machine *machines__find(struct machines *machines, pid_t pid)
{
- struct rb_node **p = &machines->guests.rb_root.rb_node;
+ struct rb_node **p;
struct rb_node *parent = NULL;
struct machine *machine;
struct machine *default_machine = NULL;
@@ -307,6 +319,8 @@ struct machine *machines__find(struct machines *machines, pid_t pid)
if (pid == HOST_KERNEL_ID)
return &machines->host;
+ down_read(&machines->lock);
+ p = &machines->guests.rb_root.rb_node;
while (*p != NULL) {
parent = *p;
machine = rb_entry(parent, struct machine, rb_node);
@@ -314,11 +328,14 @@ struct machine *machines__find(struct machines *machines, pid_t pid)
p = &(*p)->rb_left;
else if (pid > machine->pid)
p = &(*p)->rb_right;
- else
+ else {
+ up_read(&machines->lock);
return machine;
+ }
if (!machine->pid)
default_machine = machine;
}
+ up_read(&machines->lock);
return default_machine;
}
@@ -464,10 +481,12 @@ void machines__process_guests(struct machines *machines,
{
struct rb_node *nd;
+ down_read(&machines->lock);
for (nd = rb_first_cached(&machines->guests); nd; nd = rb_next(nd)) {
struct machine *pos = rb_entry(nd, struct machine, rb_node);
process(pos, data);
}
+ up_read(&machines->lock);
}
void machines__set_id_hdr_size(struct machines *machines, u16 id_hdr_size)
@@ -477,11 +496,13 @@ void machines__set_id_hdr_size(struct machines *machines, u16 id_hdr_size)
machines->host.id_hdr_size = id_hdr_size;
+ down_read(&machines->lock);
for (node = rb_first_cached(&machines->guests); node;
node = rb_next(node)) {
machine = rb_entry(node, struct machine, rb_node);
machine->id_hdr_size = id_hdr_size;
}
+ up_read(&machines->lock);
return;
}
@@ -935,10 +956,12 @@ size_t machines__fprintf_dsos(struct machines *machines, FILE *fp)
struct rb_node *nd;
size_t ret = dsos__fprintf(&machines->host.dsos, fp);
+ down_read(&machines->lock);
for (nd = rb_first_cached(&machines->guests); nd; nd = rb_next(nd)) {
struct machine *pos = rb_entry(nd, struct machine, rb_node);
ret += dsos__fprintf(&pos->dsos, fp);
}
+ up_read(&machines->lock);
return ret;
}
@@ -955,10 +978,12 @@ size_t machines__fprintf_dsos_buildid(struct machines *machines, FILE *fp,
struct rb_node *nd;
size_t ret = machine__fprintf_dsos_buildid(&machines->host, fp, skip, parm);
+ down_read(&machines->lock);
for (nd = rb_first_cached(&machines->guests); nd; nd = rb_next(nd)) {
struct machine *pos = rb_entry(nd, struct machine, rb_node);
ret += machine__fprintf_dsos_buildid(pos, fp, skip, parm);
}
+ up_read(&machines->lock);
return ret;
}
@@ -1303,10 +1328,12 @@ int machines__create_guest_kernel_maps(struct machines *machines)
void machines__destroy_kernel_maps(struct machines *machines)
{
- struct rb_node *next = rb_first_cached(&machines->guests);
+ struct rb_node *next;
machine__destroy_kernel_maps(&machines->host);
+ down_write(&machines->lock);
+ next = rb_first_cached(&machines->guests);
while (next) {
struct machine *pos = rb_entry(next, struct machine, rb_node);
@@ -1314,6 +1341,7 @@ void machines__destroy_kernel_maps(struct machines *machines)
rb_erase_cached(&pos->rb_node, &machines->guests);
machine__delete(pos);
}
+ up_write(&machines->lock);
}
int machines__create_kernel_maps(struct machines *machines, pid_t pid)
@@ -3134,13 +3162,15 @@ int machines__for_each_thread(struct machines *machines,
if (rc != 0)
return rc;
+ down_read(&machines->lock);
for (nd = rb_first_cached(&machines->guests); nd; nd = rb_next(nd)) {
struct machine *machine = rb_entry(nd, struct machine, rb_node);
rc = machine__for_each_thread(machine, fn, priv);
if (rc != 0)
- return rc;
+ break;
}
+ up_read(&machines->lock);
return rc;
}
diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h
index 973a59f7c26a..147ce343e370 100644
--- a/tools/perf/util/machine.h
+++ b/tools/perf/util/machine.h
@@ -151,6 +151,7 @@ typedef void (*machine__process_t)(struct machine *machine, void *data);
struct machines {
/** @session: back link to owning session if there is one. */
struct perf_session *session;
+ struct rw_semaphore lock;
struct machine host;
struct rb_root_cached guests;
};
--
2.56.0.313.g2479322e68-goog
^ permalink raw reply [flat|nested] 37+ messages in thread* [PATCH v2 15/18] perf symbol: Add memory shrinking for symbols, DSOs, and machines
2026-10-27 23:14 ` [PATCH v2 00/18] perf symbol: Reference counting, sorted arrays, and memory shrinking Ian Rogers
` (13 preceding siblings ...)
2026-10-27 23:15 ` [PATCH v2 14/18] perf machine: Protect machines->guests rbtree with a rw_semaphore Ian Rogers
@ 2026-10-27 23:15 ` Ian Rogers
2026-10-27 23:15 ` [PATCH v2 16/18] perf session: Add dso__session() and session-scoped allocation wrappers Ian Rogers
` (3 subsequent siblings)
18 siblings, 0 replies; 37+ messages in thread
From: Ian Rogers @ 2026-10-27 23:15 UTC (permalink / raw)
To: irogers, acme, namhyung
Cc: adrian.hunter, haghdoost, james.clark, jolsa, linux-kernel,
linux-perf-users, mingo, peterz
Long-running 'perf top' and large 'perf report' sessions can load full
symbol tables, cached 4KB data pages, addr2line subprocesses, and libdw
contexts across thousands of DSOs even when only a small fraction of
symbols are actively referenced by live hist_entry or callchain nodes.
Add on-demand memory shrinking functions to reclaim cached symbol, DSO,
and debuginfo state under memory pressure:
- symbols__shrink(): Evict symbols whose only reference is the struct
symbols container (refcnt == 1 and not annotated) while retaining
symbols actively referenced by callers (e.g. hist_entry, map_symbol,
callchain nodes, or annotations). Mark the symbol table as shrunk and
clear dso->loaded when symbols are evicted so subsequent map__load()
calls reload the DSO under dso__lock(dso) and merge retained symbol
instances back in place.
- dso__shrink(): Close open file descriptors, free cached 4KB data
pages (on file-backed user DSOs), addr2line handles, and libdw Dwfl
contexts, and shrink reloadable user DSO symbol tables.
- dsos__shrink(), machine__shrink(), machines__shrink(), and
perf_session__shrink(): Shrink all DSOs across host and guest
machines, evict unreferenced user DSOs with no remaining symbols, and
return free heap pages via malloc_trim(0) on glibc.
All shrinking paths use non-blocking trylocks and perform zero heap
allocations so they can be safely invoked from OOM recovery paths.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/dso.c | 192 ++++++++++++++++++--
tools/perf/util/dso.h | 24 ++-
tools/perf/util/dsos.c | 68 +++++++
tools/perf/util/dsos.h | 2 +
tools/perf/util/machine.c | 32 ++++
tools/perf/util/machine.h | 3 +
tools/perf/util/session.c | 8 +
tools/perf/util/session.h | 2 +
tools/perf/util/symbol-elf.c | 2 +-
tools/perf/util/symbol.c | 332 ++++++++++++++++++++++++++++++-----
tools/perf/util/symbol.h | 6 +
11 files changed, 609 insertions(+), 62 deletions(-)
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c
index 35a5c65030af..be257c77ab0a 100644
--- a/tools/perf/util/dso.c
+++ b/tools/perf/util/dso.c
@@ -1075,12 +1075,12 @@ static int bpf_size(struct dso *dso)
#endif // HAVE_LIBBPF_SUPPORT
static void
-dso_cache__free(struct dso *dso)
+__dso_cache__free(struct dso *dso)
+ EXCLUSIVE_LOCKS_REQUIRED(dso__lock(dso))
{
struct rb_root *root = &dso__data(dso)->cache;
struct rb_node *next = rb_first(root);
- mutex_lock(dso__lock(dso));
while (next) {
struct dso_cache *cache;
@@ -1089,6 +1089,13 @@ dso_cache__free(struct dso *dso)
rb_erase(&cache->rb_node, root);
free(cache);
}
+}
+
+static void
+dso_cache__free(struct dso *dso)
+{
+ mutex_lock(dso__lock(dso));
+ __dso_cache__free(dso);
mutex_unlock(dso__lock(dso));
}
@@ -1243,27 +1250,34 @@ static struct dso_cache *dso_cache__populate(struct dso *dso,
return cache;
}
-static struct dso_cache *dso_cache__find(struct dso *dso,
- struct machine *machine,
- u64 offset,
- ssize_t *ret)
-{
- struct dso_cache *cache = __dso_cache__find(dso, offset);
-
- return cache ? cache : dso_cache__populate(dso, machine, offset, ret);
-}
-
static ssize_t dso_cache_io(struct dso *dso, struct machine *machine,
u64 offset, u8 *data, ssize_t size, bool out)
{
struct dso_cache *cache;
ssize_t ret = 0;
- cache = dso_cache__find(dso, machine, offset, &ret);
- if (!cache)
- return ret;
+ mutex_lock(dso__lock(dso));
+ cache = __dso_cache__find(dso, offset);
+ if (!cache) {
+ mutex_unlock(dso__lock(dso));
+ cache = dso_cache__populate(dso, machine, offset, &ret);
+ if (!cache)
+ return ret;
+ mutex_lock(dso__lock(dso));
+ /*
+ * Re-lookup under dso__lock(dso) in case dso__shrink() freed
+ * the cache while the lock was dropped.
+ */
+ cache = __dso_cache__find(dso, offset);
+ if (!cache) {
+ mutex_unlock(dso__lock(dso));
+ return -EAGAIN;
+ }
+ }
- return dso_cache__memcpy(cache, offset, data, size, out);
+ ret = dso_cache__memcpy(cache, offset, data, size, out);
+ mutex_unlock(dso__lock(dso));
+ return ret;
}
/*
@@ -1803,7 +1817,8 @@ int dso__name_len(const struct dso *dso)
bool dso__loaded(const struct dso *dso)
{
- return RC_CHK_ACCESS(dso)->loaded;
+ return atomic_load_explicit(&RC_CHK_ACCESS(dso)->loaded, memory_order_acquire) &&
+ !dso__symbols_shrunk(dso);
}
bool dso__sorted_by_name(const struct dso *dso)
@@ -1835,7 +1850,7 @@ struct dso *dso__new_id(const char *name, const struct dso_id *id)
dso->binary_type = DSO_BINARY_TYPE__NOT_FOUND;
dso->dbginfo_type = DSO_BINARY_TYPE__NOT_FOUND;
dso->is_64_bit = (sizeof(void *) == 8);
- dso->loaded = 0;
+ atomic_init(&dso->loaded, false);
dso->rel = 0;
dso->has_srcline = 1;
dso->a2l_fails = 1;
@@ -1896,6 +1911,144 @@ void dso__delete(struct dso *dso)
RC_CHK_FREE(dso);
}
+bool dso__can_shrink_symbols(const struct dso *dso)
+{
+ const char *name;
+
+ if (!dso || (!dso__loaded(dso) && !dso__symbols_shrunk(dso)))
+ return false;
+
+ if (dso__kernel(dso) || dso__is_kmod(dso))
+ return false;
+
+ if (dso__is_vdso(dso))
+ return false;
+
+ switch (dso__binary_type(dso)) {
+ case DSO_BINARY_TYPE__KALLSYMS:
+ case DSO_BINARY_TYPE__GUEST_KALLSYMS:
+ case DSO_BINARY_TYPE__VMLINUX:
+ case DSO_BINARY_TYPE__GUEST_VMLINUX:
+ case DSO_BINARY_TYPE__KCORE:
+ case DSO_BINARY_TYPE__GUEST_KCORE:
+ case DSO_BINARY_TYPE__BPF_PROG_INFO:
+ case DSO_BINARY_TYPE__BPF_IMAGE:
+ case DSO_BINARY_TYPE__OOL:
+ case DSO_BINARY_TYPE__JAVA_JIT:
+ case DSO_BINARY_TYPE__GUEST_KMODULE:
+ case DSO_BINARY_TYPE__GUEST_KMODULE_COMP:
+ case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE:
+ case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP:
+ return false;
+ case DSO_BINARY_TYPE__DEBUGLINK:
+ case DSO_BINARY_TYPE__BUILD_ID_CACHE:
+ case DSO_BINARY_TYPE__BUILD_ID_CACHE_DEBUGINFO:
+ case DSO_BINARY_TYPE__FEDORA_DEBUGINFO:
+ case DSO_BINARY_TYPE__UBUNTU_DEBUGINFO:
+ case DSO_BINARY_TYPE__MIXEDUP_UBUNTU_DEBUGINFO:
+ case DSO_BINARY_TYPE__BUILDID_DEBUGINFO:
+ case DSO_BINARY_TYPE__GNU_DEBUGDATA:
+ case DSO_BINARY_TYPE__SYSTEM_PATH_DSO:
+ case DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO:
+ case DSO_BINARY_TYPE__NOT_FOUND:
+ default:
+ break;
+ }
+
+ switch (dso__symtab_type(dso)) {
+ case DSO_BINARY_TYPE__KALLSYMS:
+ case DSO_BINARY_TYPE__GUEST_KALLSYMS:
+ case DSO_BINARY_TYPE__VMLINUX:
+ case DSO_BINARY_TYPE__GUEST_VMLINUX:
+ case DSO_BINARY_TYPE__KCORE:
+ case DSO_BINARY_TYPE__GUEST_KCORE:
+ case DSO_BINARY_TYPE__BPF_PROG_INFO:
+ case DSO_BINARY_TYPE__BPF_IMAGE:
+ case DSO_BINARY_TYPE__OOL:
+ case DSO_BINARY_TYPE__JAVA_JIT:
+ case DSO_BINARY_TYPE__GUEST_KMODULE:
+ case DSO_BINARY_TYPE__GUEST_KMODULE_COMP:
+ case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE:
+ case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP:
+ case DSO_BINARY_TYPE__NOT_FOUND:
+ return false;
+ case DSO_BINARY_TYPE__DEBUGLINK:
+ case DSO_BINARY_TYPE__BUILD_ID_CACHE:
+ case DSO_BINARY_TYPE__BUILD_ID_CACHE_DEBUGINFO:
+ case DSO_BINARY_TYPE__FEDORA_DEBUGINFO:
+ case DSO_BINARY_TYPE__UBUNTU_DEBUGINFO:
+ case DSO_BINARY_TYPE__MIXEDUP_UBUNTU_DEBUGINFO:
+ case DSO_BINARY_TYPE__BUILDID_DEBUGINFO:
+ case DSO_BINARY_TYPE__GNU_DEBUGDATA:
+ case DSO_BINARY_TYPE__SYSTEM_PATH_DSO:
+ case DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO:
+ default:
+ break;
+ }
+
+ name = dso__long_name(dso);
+ if (!name || name[0] == '[' || is_perf_pid_map_name(name) ||
+ strstr(name, " (deleted)") != NULL)
+ return false;
+
+ return true;
+}
+
+size_t dso__shrink(struct dso *dso)
+ NO_THREAD_SAFETY_ANALYSIS
+{
+ size_t freed = 0;
+
+ if (!dso)
+ return 0;
+
+ if (mutex_trylock(dso__data_open_lock())) {
+ if (dso__data(dso)->fd >= 0) {
+ close_dso(dso);
+ freed++;
+ }
+ mutex_unlock(dso__data_open_lock());
+ }
+
+ if (!mutex_trylock(dso__lock(dso)))
+ return freed;
+
+ if (!dso__kernel(dso) && dso__binary_type(dso) != DSO_BINARY_TYPE__OOL &&
+ !RB_EMPTY_ROOT(&dso__data(dso)->cache)) {
+ __dso_cache__free(dso);
+ freed++;
+ }
+ if (dso__a2l(dso)) {
+ dso__free_a2l(dso);
+ freed++;
+ }
+#ifdef HAVE_LIBBFD_SUPPORT
+ if (dso__a2l_libbfd(dso)) {
+ dso__free_a2l_libbfd(dso);
+ freed++;
+ }
+#endif
+#ifdef HAVE_LIBDW_SUPPORT
+ if (dso__libdw(dso)) {
+ dso__free_libdw(dso);
+ freed++;
+ }
+#endif
+
+ if (dso__can_shrink_symbols(dso)) {
+ size_t sym_freed = symbols__shrink(dso__symbols(dso));
+
+ if (sym_freed > 0) {
+ atomic_store_explicit(&RC_CHK_ACCESS(dso)->loaded, false,
+ memory_order_release);
+ freed += sym_freed;
+ }
+ }
+
+ mutex_unlock(dso__lock(dso));
+ return freed;
+}
+
struct dso *dso__get(struct dso *dso)
{
struct dso *result;
@@ -1909,7 +2062,8 @@ struct dso *dso__get(struct dso *dso)
void dso__put(struct dso *dso)
{
#ifdef REFCNT_CHECKING
- if (dso && dso__data(dso) && refcount_read(&RC_CHK_ACCESS(dso)->refcnt) == 2)
+ if (dso && dso__data(dso) && dso__data(dso)->fd >= 0 &&
+ refcount_read(&RC_CHK_ACCESS(dso)->refcnt) == 2)
dso__data_close(dso);
#endif
if (dso && refcount_dec_and_test(&RC_CHK_ACCESS(dso)->refcnt))
diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h
index f23cf9957aec..64a118ea2524 100644
--- a/tools/perf/util/dso.h
+++ b/tools/perf/util/dso.h
@@ -346,7 +346,7 @@ DECLARE_RC_STRUCT(dso) {
u8 is_64_bit:1;
u8 bintype_searched:1;
u8 debuginfo_searched:1;
- bool loaded;
+ _Atomic bool loaded;
u8 rel;
char name[];
};
@@ -602,7 +602,7 @@ static inline enum dso_load_errno *dso__load_errno(struct dso *dso)
static inline void dso__set_loaded(struct dso *dso)
{
- RC_CHK_ACCESS(dso)->loaded = true;
+ atomic_store_explicit(&RC_CHK_ACCESS(dso)->loaded, true, memory_order_release);
}
static inline struct mutex *dso__lock(struct dso *dso)
@@ -783,6 +783,26 @@ static inline bool dso__has_symbols(const struct dso *dso)
return RC_CHK_ACCESS(dso)->symbols.cnt > 0;
}
+static inline bool dso__symbols_shrunk(const struct dso *dso)
+{
+ return atomic_load_explicit(&RC_CHK_ACCESS(dso)->symbols.shrunk,
+ memory_order_acquire);
+}
+
+static inline void dso__set_symbols_shrunk(struct dso *dso, bool val)
+{
+ atomic_store_explicit(&RC_CHK_ACCESS(dso)->symbols.shrunk, val,
+ memory_order_release);
+}
+
+static inline refcount_t *dso__refcnt(struct dso *dso)
+{
+ return &RC_CHK_ACCESS(dso)->refcnt;
+}
+
+bool dso__can_shrink_symbols(const struct dso *dso);
+size_t dso__shrink(struct dso *dso);
+
char *dso__filename_with_chroot(const struct dso *dso, const char *filename);
bool dso__sorted_by_name(const struct dso *dso);
diff --git a/tools/perf/util/dsos.c b/tools/perf/util/dsos.c
index e927e707abac..30ecc9a19244 100644
--- a/tools/perf/util/dsos.c
+++ b/tools/perf/util/dsos.c
@@ -6,6 +6,9 @@
#include "vdso.h"
#include "namespaces.h"
#include <errno.h>
+#ifdef __GLIBC__
+#include <malloc.h>
+#endif
#include <stdlib.h>
#include <string.h>
#include <symbol.h> // filename__read_build_id
@@ -493,3 +496,68 @@ int dsos__for_each_dso(struct dsos *dsos, int (*cb)(struct dso *dso, void *data)
up_read(&dsos->lock);
return err;
}
+
+size_t dsos__shrink(struct dsos *dsos)
+ NO_THREAD_SAFETY_ANALYSIS
+{
+ unsigned int write_idx = 0;
+ size_t freed = 0;
+ bool any_removed = false;
+
+ if (!down_write_trylock(&dsos->lock))
+ return 0;
+
+ for (unsigned int read_idx = 0; read_idx < dsos->cnt; read_idx++) {
+ struct dso *dso = dsos->dsos[read_idx];
+
+ freed += dso__shrink(dso);
+
+ if (!dso__has_symbols(dso) &&
+ !dso__header_build_id(dso) &&
+ !dso__hit(dso) &&
+ !dso__kernel(dso) &&
+ refcount_read(dso__refcnt(dso)) == 1 &&
+ mutex_trylock(&_dso__data_open_lock)) {
+ bool can_evict = dso__data(dso)->fd < 0 &&
+ list_empty(&dso__data(dso)->open_entry) &&
+ mutex_trylock(dso__lock(dso));
+
+ if (can_evict)
+ mutex_unlock(dso__lock(dso));
+ mutex_unlock(&_dso__data_open_lock);
+ if (can_evict) {
+ dso__set_dsos(dso, NULL);
+ dso__put(dso);
+ any_removed = true;
+ freed++;
+ continue;
+ }
+ }
+ dsos->dsos[write_idx++] = dso;
+ }
+
+ dsos->cnt = write_idx;
+ if (any_removed) {
+ if (write_idx == 0) {
+ zfree(&dsos->dsos);
+ dsos->allocated = 0;
+ dsos->sorted = true;
+ } else if (write_idx < dsos->allocated / 2) {
+ struct dso **shrunk = realloc(dsos->dsos,
+ write_idx * sizeof(struct dso *));
+ if (shrunk) {
+ dsos->dsos = shrunk;
+ dsos->allocated = write_idx;
+ }
+ }
+ }
+
+ up_write(&dsos->lock);
+
+#ifdef __GLIBC__
+ if (freed > 0)
+ malloc_trim(0);
+#endif
+
+ return freed;
+}
diff --git a/tools/perf/util/dsos.h b/tools/perf/util/dsos.h
index a26774950866..151efdba2aa4 100644
--- a/tools/perf/util/dsos.h
+++ b/tools/perf/util/dsos.h
@@ -49,4 +49,6 @@ struct dso *dsos__find_kernel_dso(struct dsos *dsos);
int dsos__for_each_dso(struct dsos *dsos, int (*cb)(struct dso *dso, void *data), void *data);
+size_t dsos__shrink(struct dsos *dsos);
+
#endif /* __PERF_DSOS */
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index fb83da8ef09b..8d0c545ee544 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -3446,3 +3446,35 @@ int machine__hit_all_dsos(struct machine *machine)
{
return dsos__hit_all(&machine->dsos);
}
+
+size_t machine__shrink(struct machine *machine)
+{
+ if (!machine)
+ return 0;
+
+ return dsos__shrink(&machine->dsos);
+}
+
+size_t machines__shrink(struct machines *machines)
+ NO_THREAD_SAFETY_ANALYSIS
+{
+ struct rb_node *nd;
+ size_t freed;
+
+ if (!machines)
+ return 0;
+
+ freed = machine__shrink(&machines->host);
+
+ if (!down_read_trylock(&machines->lock))
+ return freed;
+
+ for (nd = rb_first_cached(&machines->guests); nd; nd = rb_next(nd)) {
+ struct machine *pos = rb_entry(nd, struct machine, rb_node);
+
+ freed += machine__shrink(pos);
+ }
+ up_read(&machines->lock);
+
+ return freed;
+}
diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h
index 147ce343e370..1d1f46a01a38 100644
--- a/tools/perf/util/machine.h
+++ b/tools/perf/util/machine.h
@@ -337,4 +337,7 @@ int machine__resolve(struct machine *machine, struct addr_location *al,
int machine__hit_all_dsos(struct machine *machine);
+size_t machine__shrink(struct machine *machine);
+size_t machines__shrink(struct machines *machines);
+
#endif /* __PERF_MACHINE_H */
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
index 56a5e57ec512..3f35c7affc29 100644
--- a/tools/perf/util/session.c
+++ b/tools/perf/util/session.c
@@ -4266,3 +4266,11 @@ uint16_t perf_session__e_machine(struct perf_session *session, uint32_t *e_flags
*/
return perf_env__e_machine_nocache(env, e_flags);
}
+
+size_t perf_session__shrink(struct perf_session *session)
+{
+ if (!session)
+ return 0;
+
+ return machines__shrink(&session->machines);
+}
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h
index ac5803d5fb4e..fdec65fcfee6 100644
--- a/tools/perf/util/session.h
+++ b/tools/perf/util/session.h
@@ -217,4 +217,6 @@ int perf_event__process_finished_round(const struct perf_tool *tool,
struct perf_env *perf_session__env(struct perf_session *session);
uint16_t perf_session__e_machine(struct perf_session *session, uint32_t *e_flags);
+size_t perf_session__shrink(struct perf_session *session);
+
#endif /* __PERF_SESSION_H */
diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c
index 6082136c9662..946a2426933c 100644
--- a/tools/perf/util/symbol-elf.c
+++ b/tools/perf/util/symbol-elf.c
@@ -1973,7 +1973,7 @@ int dso__load_sym(struct dso *dso, struct map *map, struct symsrc *syms_ss,
* Modules may already have symbols from kallsyms, but those symbols
* have the wrong values for the dso maps, so remove them.
*/
- if (kmodule && syms_ss->symtab)
+ if (kmodule && syms_ss->symtab && !dso__symbols_shrunk(dso))
symbols__delete(dso__symbols(dso));
if (!syms_ss->symtab) {
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 7f1fcd5c64b2..e5338c8e03dc 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -164,6 +164,18 @@ static int choose_best_symbol(struct symbol *syma, struct symbol *symb)
s64 b;
size_t na, nb;
+ /*
+ * Prefer a symbol that already has external references (e.g. retained
+ * across a symbols__shrink() and subsequent reload) so existing
+ * callers continue pointing to the canonical symbol in the DSO.
+ */
+ a = refcount_read(symbol__refcnt(syma)) > 1 || symbol__annotated(syma);
+ b = refcount_read(symbol__refcnt(symb)) > 1 || symbol__annotated(symb);
+ if (a && !b)
+ return SYMBOL_A;
+ if (b && !a)
+ return SYMBOL_B;
+
/* Prefer a symbol with non zero length */
a = symbol__end(syma) - symbol__start(syma);
b = symbol__end(symb) - symbol__start(symb);
@@ -225,6 +237,7 @@ void symbols__init(struct symbols *symbols)
symbols->num_by_name = 0;
symbols->sorted = true;
symbols->sorted_by_name = false;
+ atomic_init(&symbols->shrunk, false);
}
static void symbols__delete_locked(struct symbols *symbols)
@@ -240,6 +253,7 @@ static void symbols__delete_locked(struct symbols *symbols)
symbols->num_by_name = 0;
symbols->sorted = true;
symbols->sorted_by_name = false;
+ atomic_store_explicit(&symbols->shrunk, false, memory_order_release);
}
void symbols__delete(struct symbols *symbols)
@@ -261,6 +275,70 @@ void symbols__exit(struct symbols *symbols)
exit_rwsem(&symbols->lock);
}
+/*
+ * Evict symbols whose only reference is the @symbols array itself
+ * (refcnt == 1), retaining symbols referenced by active callers (e.g.
+ * hist_entry, map_symbol, or callchain nodes with refcnt > 1).
+ *
+ * Uses down_write_trylock() so this is safe to call from OOM recovery
+ * paths where the current thread might already hold a lock on @symbols.
+ *
+ * Returns the number of symbols evicted.
+ */
+size_t symbols__shrink(struct symbols *symbols)
+{
+ unsigned int write_idx = 0;
+ unsigned int old_sorted_cnt;
+ unsigned int new_sorted_cnt = 0;
+ size_t freed = 0;
+
+ if (!down_write_trylock(&symbols->lock))
+ return 0;
+
+ old_sorted_cnt = symbols->sorted ? symbols->cnt : symbols->sorted_cnt;
+
+ for (unsigned int read_idx = 0; read_idx < symbols->cnt; read_idx++) {
+ struct symbol *sym = symbols->symbols[read_idx];
+
+ if (refcount_read(symbol__refcnt(sym)) > 1 || symbol__annotated(sym)) {
+ if (read_idx < old_sorted_cnt)
+ new_sorted_cnt++;
+ symbols->symbols[write_idx++] = sym;
+ } else {
+ symbol__put(sym);
+ freed++;
+ }
+ }
+
+ symbols->cnt = write_idx;
+ symbols->sorted_cnt = new_sorted_cnt;
+ if (new_sorted_cnt == write_idx)
+ symbols->sorted = true;
+
+ if (freed > 0) {
+ zfree(&symbols->symbols_by_name);
+ symbols->num_by_name = 0;
+ symbols->sorted_by_name = false;
+ atomic_store_explicit(&symbols->shrunk, true, memory_order_release);
+
+ if (write_idx == 0) {
+ zfree(&symbols->symbols);
+ symbols->allocated = 0;
+ symbols->sorted = true;
+ } else if (write_idx < symbols->allocated / 2) {
+ struct symbol **shrunk = realloc(symbols->symbols,
+ write_idx * sizeof(struct symbol *));
+ if (shrunk) {
+ symbols->symbols = shrunk;
+ symbols->allocated = write_idx;
+ }
+ }
+ }
+
+ up_write(&symbols->lock);
+ return freed;
+}
+
static inline bool symbol__less_or_equal(const struct symbol *a, const struct symbol *b)
{
if (symbol__start(a) != symbol__start(b))
@@ -602,9 +680,7 @@ void symbols__sort_read_lock(struct symbols *symbols)
void symbols__fixup_duplicate(struct symbols *symbols)
{
unsigned int write_idx = 0;
-
- if (symbol_conf.allow_aliases)
- return;
+ unsigned int group_start = 0;
down_write(&symbols->lock);
symbols__sort_locked(symbols);
@@ -614,14 +690,37 @@ void symbols__fixup_duplicate(struct symbols *symbols)
}
for (unsigned int read_idx = 1; read_idx < symbols->cnt; read_idx++) {
- struct symbol *curr = symbols->symbols[write_idx];
struct symbol *next = symbols->symbols[read_idx];
+ struct symbol *curr;
+ unsigned int match_idx = write_idx;
+ bool matched = false;
- if (symbol__start(curr) != symbol__start(next)) {
+ if (symbol__start(symbols->symbols[group_start]) != symbol__start(next)) {
symbols->symbols[++write_idx] = next;
+ group_start = write_idx;
continue;
}
+ if (!symbol_conf.allow_aliases) {
+ matched = true;
+ } else {
+ for (unsigned int k = group_start; k <= write_idx; k++) {
+ if (strcmp(symbol__name(symbols->symbols[k]),
+ symbol__name(next)) == 0) {
+ match_idx = k;
+ matched = true;
+ break;
+ }
+ }
+ }
+
+ if (!matched) {
+ symbols->symbols[++write_idx] = next;
+ continue;
+ }
+
+ curr = symbols->symbols[match_idx];
+
if (choose_best_symbol(curr, next) == SYMBOL_A) {
if (symbol__type(next) == STT_GNU_IFUNC)
symbol__set_ifunc_alias(curr, true);
@@ -630,7 +729,13 @@ void symbols__fixup_duplicate(struct symbols *symbols)
if (symbol__type(curr) == STT_GNU_IFUNC)
symbol__set_ifunc_alias(next, true);
symbol__put(curr);
- symbols->symbols[write_idx] = next;
+ symbols->symbols[match_idx] = next;
+ while (match_idx < write_idx &&
+ !symbol__less_or_equal(next, symbols->symbols[match_idx + 1])) {
+ symbols->symbols[match_idx] = symbols->symbols[match_idx + 1];
+ symbols->symbols[match_idx + 1] = next;
+ match_idx++;
+ }
}
}
symbols->cnt = write_idx + 1;
@@ -676,44 +781,47 @@ void symbols__fixup_end(struct symbols *symbols, bool is_kallsyms)
* like in:
* ffffffffc1937000 T hdmi_driver_init [snd_hda_codec_hdmi]
*/
- if (symbol__end(prev) == symbol__start(prev) &&
- symbol__start(prev) != symbol__start(curr)) {
- const char *prev_mod;
- const char *curr_mod;
+ if (symbol__start(prev) != symbol__start(curr)) {
+ if (symbol__end(prev) == symbol__start(prev)) {
+ const char *prev_mod;
+ const char *curr_mod;
- if (!is_kallsyms) {
- symbol__set_end(prev, symbol__start(curr));
- } else {
- prev_mod = strchr(symbol__name(prev), '[');
- curr_mod = strchr(symbol__name(curr), '[');
-
- /* Last kernel/module symbol mapped to end of page */
- if (!prev_mod != !curr_mod) {
- symbol__set_end(prev,
- roundup(symbol__end(prev) + 4096, 4096));
- /* Last symbol in the previous module */
- } else if (prev_mod && strcmp(prev_mod, curr_mod)) {
- symbol__set_end(prev,
- roundup(symbol__end(prev) + 4096, 4096));
- } else {
+ if (!is_kallsyms) {
symbol__set_end(prev, symbol__start(curr));
- }
+ } else {
+ prev_mod = strchr(symbol__name(prev), '[');
+ curr_mod = strchr(symbol__name(curr), '[');
+
+ /* Last kernel/module symbol mapped to end of page */
+ if (!prev_mod != !curr_mod) {
+ symbol__set_end(prev,
+ roundup(symbol__end(prev) + 4096,
+ 4096));
+ /* Last symbol in the previous module */
+ } else if (prev_mod && strcmp(prev_mod, curr_mod)) {
+ symbol__set_end(prev,
+ roundup(symbol__end(prev) + 4096,
+ 4096));
+ } else {
+ symbol__set_end(prev, symbol__start(curr));
+ }
- /* The next module can start within that page */
- if (symbol__end(prev) > symbol__start(curr))
- symbol__set_end(prev, symbol__start(curr));
+ /* The next module can start within that page */
+ if (symbol__end(prev) > symbol__start(curr))
+ symbol__set_end(prev, symbol__start(curr));
- pr_debug4("%s sym:%s end:%#" PRIx64 "\n",
- __func__, symbol__name(prev), symbol__end(prev));
- }
+ pr_debug4("%s sym:%s end:%#" PRIx64 "\n",
+ __func__, symbol__name(prev), symbol__end(prev));
+ }
- for (unsigned int k = i - 1; k > 0; k--) {
- struct symbol *same = symbols->symbols[k - 1];
+ for (unsigned int k = i - 1; k > 0; k--) {
+ struct symbol *same = symbols->symbols[k - 1];
- if (symbol__start(same) != symbol__start(prev) ||
- symbol__end(same) != symbol__start(same))
- break;
- symbol__set_end(same, symbol__end(prev));
+ if (symbol__start(same) != symbol__start(prev) ||
+ symbol__end(same) != symbol__start(same))
+ break;
+ symbol__set_end(same, symbol__end(prev));
+ }
}
}
}
@@ -860,6 +968,118 @@ static int __symbols__insert(struct symbols *symbols, struct symbol *sym)
return 0;
}
+static void symbols__detach_retained(struct symbols *symbols,
+ struct symbol ***retained,
+ unsigned int *retained_cnt,
+ unsigned int *retained_allocated)
+{
+ down_write(&symbols->lock);
+ symbols__sort_locked(symbols);
+ *retained = symbols->symbols;
+ *retained_cnt = symbols->cnt;
+ *retained_allocated = symbols->allocated;
+ symbols->symbols = NULL;
+ symbols->cnt = 0;
+ symbols->sorted_cnt = 0;
+ symbols->allocated = 0;
+ zfree(&symbols->symbols_by_name);
+ symbols->num_by_name = 0;
+ symbols->sorted = true;
+ symbols->sorted_by_name = false;
+ up_write(&symbols->lock);
+}
+
+static void symbols__restore_retained(struct symbols *symbols,
+ struct symbol **retained,
+ unsigned int retained_cnt,
+ unsigned int retained_allocated)
+{
+ down_write(&symbols->lock);
+ symbols__delete_locked(symbols);
+ symbols->symbols = retained;
+ symbols->cnt = retained_cnt;
+ symbols->sorted_cnt = retained_cnt;
+ symbols->allocated = retained_allocated;
+ symbols->sorted = true;
+ symbols__sort_locked(symbols);
+ atomic_store_explicit(&symbols->shrunk, false, memory_order_release);
+ up_write(&symbols->lock);
+}
+
+static void symbols__merge_retained(struct symbols *symbols,
+ struct symbol **retained,
+ unsigned int retained_cnt)
+{
+ unsigned int fresh_cnt;
+ bool need_sort = false;
+
+ if (!retained)
+ return;
+
+ down_write(&symbols->lock);
+ symbols__sort_locked(symbols);
+ fresh_cnt = symbols->cnt;
+
+ for (unsigned int i = 0; i < retained_cnt; i++) {
+ struct symbol *ret_sym = retained[i];
+ u64 start = symbol__start(ret_sym);
+ unsigned int low = 0, high = fresh_cnt;
+ unsigned int match_idx = fresh_cnt;
+
+ while (low < high) {
+ unsigned int mid = low + (high - low) / 2;
+
+ if (symbol__start(symbols->symbols[mid]) < start)
+ low = mid + 1;
+ else
+ high = mid;
+ }
+
+ for (unsigned int k = low;
+ k < fresh_cnt && symbol__start(symbols->symbols[k]) == start;
+ k++) {
+ bool already_retained = false;
+
+ if (strcmp(symbol__name(symbols->symbols[k]),
+ symbol__name(ret_sym)) != 0)
+ continue;
+
+ for (unsigned int j = 0; j < i; j++) {
+ if (RC_CHK_EQUAL(symbols->symbols[k], retained[j])) {
+ already_retained = true;
+ break;
+ }
+ }
+ if (!already_retained) {
+ match_idx = k;
+ break;
+ }
+ }
+
+ if (match_idx < fresh_cnt) {
+ struct symbol *fresh_sym = symbols->symbols[match_idx];
+
+ symbol__set_end(ret_sym, symbol__end(fresh_sym));
+ if (symbol__ifunc_alias(fresh_sym))
+ symbol__set_ifunc_alias(ret_sym, true);
+ symbols->symbols[match_idx] = ret_sym;
+ symbol__put(fresh_sym);
+ } else if (__symbols__insert(symbols, ret_sym) == 0) {
+ need_sort = true;
+ }
+ }
+
+ if (need_sort)
+ symbols__sort_locked(symbols);
+
+ zfree(&symbols->symbols_by_name);
+ symbols->num_by_name = 0;
+ symbols->sorted_by_name = false;
+ atomic_store_explicit(&symbols->shrunk, false, memory_order_release);
+ up_write(&symbols->lock);
+ free(retained);
+}
+
static int __symbols__insert_sorted(struct symbols *symbols, struct symbol *sym)
{
unsigned int idx;
@@ -1449,6 +1669,7 @@ static int maps__split_kallsyms_for_kcore(struct maps *kmaps, struct dso *dso)
zfree(&root->symbols_by_name);
root->num_by_name = 0;
root->sorted_by_name = false;
+ atomic_store_explicit(&root->shrunk, false, memory_order_release);
up_write(&root->lock);
for (unsigned int i = 0; i < old_cnt; i++) {
@@ -1679,6 +1900,7 @@ static int maps__split_kallsyms(struct maps *kmaps, struct dso *dso, u64 delta,
zfree(&root->symbols_by_name);
root->num_by_name = 0;
root->sorted_by_name = false;
+ atomic_store_explicit(&root->shrunk, false, memory_order_release);
up_write(&root->lock);
free(old_symbols);
@@ -1711,6 +1933,7 @@ static int maps__split_kallsyms(struct maps *kmaps, struct dso *dso, u64 delta,
zfree(&root->symbols_by_name);
root->num_by_name = 0;
root->sorted_by_name = false;
+ atomic_store_explicit(&root->shrunk, false, memory_order_release);
up_write(&root->lock);
free(old_symbols);
return -1;
@@ -2388,6 +2611,11 @@ int dso__load(struct dso *dso, struct map *map)
struct symsrc *syms_ss = NULL, *runtime_ss = NULL;
bool kmod;
bool perfmap;
+ bool was_shrunk;
+ bool saved_adjust_symbols;
+ struct symbol **retained = NULL;
+ unsigned int retained_cnt = 0;
+ unsigned int retained_allocated = 0;
struct nscookie nsc;
char newmapname[PATH_MAX];
const char *map_path = dso__long_name(dso);
@@ -2408,8 +2636,15 @@ int dso__load(struct dso *dso, struct map *map)
/* check again under the dso->lock */
if (dso__loaded(dso)) {
ret = 1;
- goto out;
+ goto out_unlock;
}
+ was_shrunk = dso__symbols_shrunk(dso);
+ saved_adjust_symbols = dso__adjust_symbols(dso);
+ if (was_shrunk) {
+ symbols__detach_retained(dso__symbols(dso), &retained,
+ &retained_cnt, &retained_allocated);
+ }
+ atomic_store_explicit(&RC_CHK_ACCESS(dso)->loaded, false, memory_order_release);
kmod = dso__is_kmod(dso);
@@ -2556,6 +2791,8 @@ int dso__load(struct dso *dso, struct map *map)
nr_plt = dso__synthesize_plt_symbols(dso, runtime_ss);
if (nr_plt > 0)
ret += nr_plt;
+ if (nr_plt > 0 || !dso__symbols(dso)->sorted)
+ symbols__fixup_duplicate(dso__symbols(dso));
}
out_destroy:
@@ -2566,7 +2803,22 @@ int dso__load(struct dso *dso, struct map *map)
if (ret < 0 && strstr(dso__name(dso), " (deleted)") != NULL)
ret = 0;
out:
- dso__set_loaded(dso);
+ if (was_shrunk) {
+ if (ret > 0) {
+ symbols__merge_retained(dso__symbols(dso), retained, retained_cnt);
+ } else {
+ dso__set_adjust_symbols(dso, saved_adjust_symbols);
+ symbols__restore_retained(dso__symbols(dso), retained,
+ retained_cnt, retained_allocated);
+ if (retained_cnt > 0)
+ ret = retained_cnt;
+ }
+ dso__set_symbols_shrunk(dso, false);
+ dso__set_loaded(dso);
+ } else {
+ dso__set_loaded(dso);
+ }
+out_unlock:
mutex_unlock(dso__lock(dso));
nsinfo__mountns_exit(&nsc);
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index 066697d8da8f..7d6469d6a976 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -135,12 +135,18 @@ struct symbols {
* by name.
*/
bool sorted_by_name;
+ /**
+ * @shrunk: True when symbols have been evicted by symbols__shrink()
+ * and may need to be reloaded on demand.
+ */
+ _Atomic bool shrunk;
};
void symbol__delete(struct symbol *sym);
void symbols__init(struct symbols *symbols);
void symbols__exit(struct symbols *symbols);
void symbols__delete(struct symbols *symbols);
+size_t symbols__shrink(struct symbols *symbols);
void symbols__sort_read_lock(struct symbols *symbols) SHARED_LOCK_FUNCTION(symbols->lock);
static inline refcount_t *symbol__refcnt(struct symbol *sym)
--
2.56.0.313.g2479322e68-goog
^ permalink raw reply [flat|nested] 37+ messages in thread* [PATCH v2 16/18] perf session: Add dso__session() and session-scoped allocation wrappers
2026-10-27 23:14 ` [PATCH v2 00/18] perf symbol: Reference counting, sorted arrays, and memory shrinking Ian Rogers
` (14 preceding siblings ...)
2026-10-27 23:15 ` [PATCH v2 15/18] perf symbol: Add memory shrinking for symbols, DSOs, and machines Ian Rogers
@ 2026-10-27 23:15 ` Ian Rogers
2026-10-27 23:15 ` [PATCH v2 17/18] perf symbol: Use session allocation wrappers in symbol, DSO, thread, and hist paths Ian Rogers
` (2 subsequent siblings)
18 siblings, 0 replies; 37+ messages in thread
From: Ian Rogers @ 2026-10-27 23:15 UTC (permalink / raw)
To: irogers, acme, namhyung
Cc: adrian.hunter, haghdoost, james.clark, jolsa, linux-kernel,
linux-perf-users, mingo, peterz
Add explicit session-scoped memory allocation wrappers:
- perf_session__malloc(session, size)
- perf_session__zalloc(session, size)
- perf_session__calloc(session, nmemb, size)
- perf_session__realloc(session, ptr, size)
- perf_session__strdup(session, s)
When an allocation fails and returns NULL, if a non-NULL struct
perf_session is provided, the wrapper invokes perf_session__shrink() to
evict unreferenced symbols, DSOs, and cached debuginfo across the
session's machines and then retries the allocation once. This avoids
the thrashing overhead of periodic heuristic shrinking while allowing
perf to recover from memory exhaustion.
Also plumb a back-pointer from struct dsos to its owning struct machine
so machine__session(machine) and dso__session(dso) can resolve the
active perf_session.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/dso.c | 7 ++++
tools/perf/util/dso.h | 2 ++
tools/perf/util/dsos.c | 1 +
tools/perf/util/dsos.h | 1 +
tools/perf/util/machine.c | 1 +
tools/perf/util/machine.h | 5 +++
tools/perf/util/session.c | 70 +++++++++++++++++++++++++++++++++++++++
tools/perf/util/session.h | 6 ++++
8 files changed, 93 insertions(+)
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c
index be257c77ab0a..c19de9ab4b24 100644
--- a/tools/perf/util/dso.c
+++ b/tools/perf/util/dso.c
@@ -1911,6 +1911,13 @@ void dso__delete(struct dso *dso)
RC_CHK_FREE(dso);
}
+struct perf_session *dso__session(const struct dso *dso)
+{
+ struct dsos *dsos = dso ? RC_CHK_ACCESS(dso)->dsos : NULL;
+
+ return dsos ? machine__session(dsos->machine) : NULL;
+}
+
bool dso__can_shrink_symbols(const struct dso *dso)
{
const char *name;
diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h
index 64a118ea2524..5fa059ba6cf1 100644
--- a/tools/perf/util/dso.h
+++ b/tools/perf/util/dso.h
@@ -19,6 +19,7 @@
struct machine;
struct map;
struct perf_env;
+struct perf_session;
#define DSO__NAME_KALLSYMS "[kernel.kallsyms]"
#define DSO__NAME_KCORE "[kernel.kcore]"
@@ -802,6 +803,7 @@ static inline refcount_t *dso__refcnt(struct dso *dso)
bool dso__can_shrink_symbols(const struct dso *dso);
size_t dso__shrink(struct dso *dso);
+struct perf_session *dso__session(const struct dso *dso);
char *dso__filename_with_chroot(const struct dso *dso, const char *filename);
diff --git a/tools/perf/util/dsos.c b/tools/perf/util/dsos.c
index 30ecc9a19244..8f3e3f3c6ce3 100644
--- a/tools/perf/util/dsos.c
+++ b/tools/perf/util/dsos.c
@@ -18,6 +18,7 @@ void dsos__init(struct dsos *dsos)
{
init_rwsem(&dsos->lock);
+ dsos->machine = NULL;
dsos->cnt = 0;
dsos->allocated = 0;
dsos->dsos = NULL;
diff --git a/tools/perf/util/dsos.h b/tools/perf/util/dsos.h
index 151efdba2aa4..d636e4c5cbaf 100644
--- a/tools/perf/util/dsos.h
+++ b/tools/perf/util/dsos.h
@@ -19,6 +19,7 @@ struct machine;
*/
struct dsos {
struct rw_semaphore lock;
+ struct machine *machine;
struct dso **dsos;
unsigned int cnt;
unsigned int allocated;
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index 8d0c545ee544..3e0d68fd5efe 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -81,6 +81,7 @@ int machine__init(struct machine *machine, const char *root_dir, pid_t pid)
memset(machine, 0, sizeof(*machine));
RB_CLEAR_NODE(&machine->rb_node);
dsos__init(&machine->dsos);
+ machine->dsos.machine = machine;
threads__init(&machine->threads);
machine->kmaps = maps__new(machine);
diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h
index 1d1f46a01a38..ab67cef67c80 100644
--- a/tools/perf/util/machine.h
+++ b/tools/perf/util/machine.h
@@ -164,6 +164,11 @@ static inline struct perf_session *machines__session(struct machines *machines)
return machines->session;
}
+static inline struct perf_session *machine__session(const struct machine *machine)
+{
+ return machine && machine->machines ? machine->machines->session : NULL;
+}
+
void machines__process_guests(struct machines *machines,
machine__process_t process, void *data);
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
index 3f35c7affc29..3d4bc9c0063a 100644
--- a/tools/perf/util/session.c
+++ b/tools/perf/util/session.c
@@ -4274,3 +4274,73 @@ size_t perf_session__shrink(struct perf_session *session)
return machines__shrink(&session->machines);
}
+
+static __thread bool perf_session__in_shrink;
+
+static bool perf_session__try_shrink(struct perf_session *session)
+{
+ size_t freed;
+
+ if (!session || perf_session__in_shrink)
+ return false;
+
+ perf_session__in_shrink = true;
+ freed = perf_session__shrink(session);
+ perf_session__in_shrink = false;
+
+ return freed > 0;
+}
+
+void *perf_session__malloc(struct perf_session *session, size_t size)
+{
+ void *ptr = malloc(size);
+
+ if (unlikely(!ptr && size != 0) && perf_session__try_shrink(session))
+ ptr = malloc(size);
+
+ return ptr;
+}
+
+void *perf_session__zalloc(struct perf_session *session, size_t size)
+{
+ void *ptr = zalloc(size);
+
+ if (unlikely(!ptr && size != 0) && perf_session__try_shrink(session))
+ ptr = zalloc(size);
+
+ return ptr;
+}
+
+void *perf_session__calloc(struct perf_session *session, size_t nmemb, size_t size)
+{
+ void *ptr = calloc(nmemb, size);
+
+ if (unlikely(!ptr && nmemb != 0 && size != 0) && perf_session__try_shrink(session))
+ ptr = calloc(nmemb, size);
+
+ return ptr;
+}
+
+void *perf_session__realloc(struct perf_session *session, void *ptr, size_t size)
+{
+ void *new_ptr = realloc(ptr, size);
+
+ if (unlikely(!new_ptr && size != 0) && perf_session__try_shrink(session))
+ new_ptr = realloc(ptr, size);
+
+ return new_ptr;
+}
+
+char *perf_session__strdup(struct perf_session *session, const char *s)
+{
+ char *dup;
+
+ if (!s)
+ return NULL;
+
+ dup = strdup(s);
+ if (unlikely(!dup) && perf_session__try_shrink(session))
+ dup = strdup(s);
+
+ return dup;
+}
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h
index fdec65fcfee6..90ae16a631b0 100644
--- a/tools/perf/util/session.h
+++ b/tools/perf/util/session.h
@@ -219,4 +219,10 @@ uint16_t perf_session__e_machine(struct perf_session *session, uint32_t *e_flags
size_t perf_session__shrink(struct perf_session *session);
+void *perf_session__malloc(struct perf_session *session, size_t size);
+void *perf_session__zalloc(struct perf_session *session, size_t size);
+void *perf_session__calloc(struct perf_session *session, size_t nmemb, size_t size);
+void *perf_session__realloc(struct perf_session *session, void *ptr, size_t size);
+char *perf_session__strdup(struct perf_session *session, const char *s);
+
#endif /* __PERF_SESSION_H */
--
2.56.0.313.g2479322e68-goog
^ permalink raw reply [flat|nested] 37+ messages in thread* [PATCH v2 17/18] perf symbol: Use session allocation wrappers in symbol, DSO, thread, and hist paths
2026-10-27 23:14 ` [PATCH v2 00/18] perf symbol: Reference counting, sorted arrays, and memory shrinking Ian Rogers
` (15 preceding siblings ...)
2026-10-27 23:15 ` [PATCH v2 16/18] perf session: Add dso__session() and session-scoped allocation wrappers Ian Rogers
@ 2026-10-27 23:15 ` Ian Rogers
2026-10-27 23:15 ` [PATCH v2 18/18] perf test symbols: Add tests for symbol shrinking and session alloc wrappers Ian Rogers
2026-10-28 20:29 ` [PATCH v2 00/18] perf symbol: Reference counting, sorted arrays, and memory shrinking Ian Rogers
18 siblings, 0 replies; 37+ messages in thread
From: Ian Rogers @ 2026-10-27 23:15 UTC (permalink / raw)
To: irogers, acme, namhyung
Cc: adrian.hunter, haghdoost, james.clark, jolsa, linux-kernel,
linux-perf-users, mingo, peterz
Connect the session-scoped allocation wrappers (perf_session__malloc,
perf_session__zalloc, perf_session__calloc, perf_session__realloc,
perf_session__strdup) to the primary heap allocation paths during sample
and symbol processing: symbol__new(), symbols->symbols array growth,
dso__new_id(), thread__new(), and hist_entry allocation.
In normal operation when memory is available, behavior is unchanged.
When an allocation fails with NULL, these paths now invoke
perf_session__shrink() on the owning perf_session to reclaim
unreferenced symbols and DSOs and retry the allocation once before
failing.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/dso.c | 4 ++-
tools/perf/util/dsos.c | 7 +++++
tools/perf/util/hist.c | 41 +++++++++++++++++++++---
tools/perf/util/machine.c | 8 ++++-
tools/perf/util/symbol-elf.c | 14 ++++++---
tools/perf/util/symbol.c | 60 +++++++++++++++++++++++-------------
tools/perf/util/symbol.h | 3 ++
7 files changed, 104 insertions(+), 33 deletions(-)
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c
index c19de9ab4b24..de694e119a6e 100644
--- a/tools/perf/util/dso.c
+++ b/tools/perf/util/dso.c
@@ -27,6 +27,7 @@
#include "map.h"
#include "namespaces.h"
#include "path.h"
+#include "session.h"
#include "srcline.h"
#include "string2.h"
#include "symbol.h"
@@ -1214,10 +1215,11 @@ static struct dso_cache *dso_cache__populate(struct dso *dso,
u64 offset, ssize_t *ret)
{
u64 cache_offset = offset & DSO__DATA_CACHE_MASK;
+ struct perf_session *session = machine ? machine__session(machine) : dso__session(dso);
struct dso_cache *cache;
struct dso_cache *old;
- cache = zalloc(sizeof(*cache) + DSO__DATA_CACHE_SIZE);
+ cache = perf_session__zalloc(session, sizeof(*cache) + DSO__DATA_CACHE_SIZE);
if (!cache) {
*ret = -ENOMEM;
return NULL;
diff --git a/tools/perf/util/dsos.c b/tools/perf/util/dsos.c
index 8f3e3f3c6ce3..374e1c5d2c32 100644
--- a/tools/perf/util/dsos.c
+++ b/tools/perf/util/dsos.c
@@ -2,6 +2,8 @@
#include "debug.h"
#include "dsos.h"
#include "dso.h"
+#include "machine.h"
+#include "session.h"
#include "util.h"
#include "vdso.h"
#include "namespaces.h"
@@ -350,6 +352,11 @@ struct dso *dsos__findnew_id(struct dsos *dsos, const char *name, const struct d
down_write(&dsos->lock);
dso = __dsos__findnew_id(dsos, name, id);
up_write(&dsos->lock);
+ if (!dso && dsos->machine && perf_session__shrink(machine__session(dsos->machine)) > 0) {
+ down_write(&dsos->lock);
+ dso = __dsos__findnew_id(dsos, name, id);
+ up_write(&dsos->lock);
+ }
return dso;
}
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c
index badae244638c..4242d3f6f955 100644
--- a/tools/perf/util/hist.c
+++ b/tools/perf/util/hist.c
@@ -524,16 +524,45 @@ struct hist_entry *hists__get_entry(struct hists *hists, int idx)
* histogram, sorted on item, collects periods
*/
+static struct perf_session *hist_entry__session(const struct hist_entry *he)
+{
+ struct maps *maps;
+
+ if (!he)
+ return NULL;
+
+ if (he->ms.map) {
+ struct dso *dso = map__dso(he->ms.map);
+ struct perf_session *session = dso__session(dso);
+
+ if (session)
+ return session;
+ }
+
+ maps = he->thread ? thread__maps(he->thread) : NULL;
+ if (maps) {
+ struct machine *machine = maps__machine(maps);
+ struct perf_session *session = machine__session(machine);
+
+ if (session)
+ return session;
+ }
+
+ return NULL;
+}
+
static int hist_entry__init(struct hist_entry *he,
struct hist_entry *template,
bool sample_self,
size_t callchain_size)
{
+ struct perf_session *session = hist_entry__session(template);
+
*he = *template;
he->callchain_size = callchain_size;
if (symbol_conf.cumulate_callchain) {
- he->stat_acc = malloc(sizeof(he->stat));
+ he->stat_acc = perf_session__malloc(session, sizeof(he->stat));
if (he->stat_acc == NULL)
return -ENOMEM;
memcpy(he->stat_acc, &he->stat, sizeof(he->stat));
@@ -551,7 +580,7 @@ static int hist_entry__init(struct hist_entry *he,
* sample__resolve_bstack() and will be freed after
* adding new entries. So we need to save a copy.
*/
- he->branch_info = malloc(sizeof(*he->branch_info));
+ he->branch_info = perf_session__malloc(session, sizeof(*he->branch_info));
if (he->branch_info == NULL)
goto err;
@@ -582,14 +611,14 @@ static int hist_entry__init(struct hist_entry *he,
}
if (he->srcline && he->srcline != SRCLINE_UNKNOWN) {
- he->srcline = strdup(he->srcline);
+ he->srcline = perf_session__strdup(session, he->srcline);
if (he->srcline == NULL)
goto err_rawdata;
}
if (symbol_conf.res_sample) {
- he->res_samples = calloc(symbol_conf.res_sample,
- sizeof(struct res_sample));
+ he->res_samples = perf_session__calloc(session, symbol_conf.res_sample,
+ sizeof(struct res_sample));
if (!he->res_samples)
goto err_srcline;
}
@@ -655,6 +684,8 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template,
callchain_size = sizeof(struct callchain_root);
he = ops->new(callchain_size);
+ if (!he && perf_session__shrink(hist_entry__session(template)) > 0)
+ he = ops->new(callchain_size);
if (he) {
err = hist_entry__init(he, template, sample_self, callchain_size);
if (err) {
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index 3e0d68fd5efe..f45befc35a7f 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -20,6 +20,7 @@
#include "mem-events.h"
#include "mem-info.h"
#include "path.h"
+#include "session.h"
#include "srcline.h"
#include "symbol.h"
#include "synthetic-events.h"
@@ -598,7 +599,12 @@ static struct thread *__machine__findnew_thread(struct machine *machine,
struct thread *machine__findnew_thread(struct machine *machine, pid_t pid, pid_t tid)
{
- return __machine__findnew_thread(machine, pid, tid, /*create=*/true);
+ struct thread *th = __machine__findnew_thread(machine, pid, tid, /*create=*/true);
+
+ if (!th && perf_session__shrink(machine__session(machine)) > 0)
+ th = __machine__findnew_thread(machine, pid, tid, /*create=*/true);
+
+ return th;
}
struct thread *machine__find_thread(struct machine *machine, pid_t pid,
diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c
index 946a2426933c..75139fab0748 100644
--- a/tools/perf/util/symbol-elf.c
+++ b/tools/perf/util/symbol-elf.c
@@ -15,6 +15,7 @@
#include "symbol.h"
#include "symsrc.h"
#include "machine.h"
+#include "session.h"
#include "vdso.h"
#include "debug.h"
#include "util/copyfile.h"
@@ -701,7 +702,7 @@ static int dso__synthesize_plt_got_symbols(struct dso *dso, Elf *elf,
sym = symbol__new(shdr.sh_offset + i, shdr.sh_entsize, STB_GLOBAL, STT_FUNC, buf);
if (!sym)
goto out;
- if (symbols__insert(dso__symbols(dso), sym))
+ if (symbols__insert_session(dso__symbols(dso), sym, dso__session(dso)))
goto out;
}
err = 0;
@@ -757,7 +758,7 @@ int dso__synthesize_plt_symbols(struct dso *dso, struct symsrc *ss)
plt_sym = symbol__new(shdr_plt.sh_offset, plt_header_size, STB_GLOBAL, STT_FUNC, ".plt");
if (!plt_sym)
goto out_elf_end;
- if (symbols__insert(dso__symbols(dso), plt_sym))
+ if (symbols__insert_session(dso__symbols(dso), plt_sym, dso__session(dso)))
goto out_elf_end;
/* Only x86 has .plt.got */
@@ -880,7 +881,7 @@ int dso__synthesize_plt_symbols(struct dso *dso, struct symsrc *ss)
goto out_elf_end;
plt_offset += plt_entry_size;
- if (symbols__insert(dso__symbols(dso), f))
+ if (symbols__insert_session(dso__symbols(dso), f, dso__session(dso)))
goto out_elf_end;
++nr;
}
@@ -1939,13 +1940,18 @@ dso__load_sym_internal(struct dso *dso, struct map *map, struct symsrc *syms_ss,
f = symbol__new(sym.st_value, sym.st_size,
GELF_ST_BIND(sym.st_info),
GELF_ST_TYPE(sym.st_info), elf_name);
+ if (!f && perf_session__shrink(dso__session(curr_dso)) > 0) {
+ f = symbol__new(sym.st_value, sym.st_size,
+ GELF_ST_BIND(sym.st_info),
+ GELF_ST_TYPE(sym.st_info), elf_name);
+ }
free(demangled);
if (!f)
goto out_elf_end;
arch__sym_update(f, &sym);
- if (symbols__insert(dso__symbols(curr_dso), f))
+ if (symbols__insert_session(dso__symbols(curr_dso), f, dso__session(curr_dso)))
goto out_elf_end;
nr++;
}
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index e5338c8e03dc..6bba489ff535 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -41,6 +41,7 @@
#include "namespaces.h"
#include "header.h"
#include "path.h"
+#include "session.h"
#include <linux/ctype.h>
#include <linux/log2.h>
#include <linux/zalloc.h>
@@ -938,12 +939,13 @@ static void symbol__set_idle(struct symbol *sym, bool idle)
} while (!atomic_compare_exchange_weak(&RC_CHK_ACCESS(sym)->flags, &old_flags, new_flags));
}
-static int __symbols__insert(struct symbols *symbols, struct symbol *sym)
+static int __symbols__insert(struct symbols *symbols, struct symbol *sym,
+ struct perf_session *session)
{
if (symbols->cnt == symbols->allocated) {
unsigned int to_allocate = symbols->allocated ? symbols->allocated * 2 : 32;
- struct symbol **temp = realloc(symbols->symbols,
- sizeof(struct symbol *) * to_allocate);
+ struct symbol **temp = perf_session__realloc(session, symbols->symbols,
+ sizeof(struct symbol *) * to_allocate);
if (!temp) {
symbol__put(sym);
@@ -1008,7 +1010,8 @@ static void symbols__restore_retained(struct symbols *symbols,
static void symbols__merge_retained(struct symbols *symbols,
struct symbol **retained,
- unsigned int retained_cnt)
+ unsigned int retained_cnt,
+ struct perf_session *session)
{
unsigned int fresh_cnt;
bool need_sort = false;
@@ -1064,7 +1067,7 @@ static void symbols__merge_retained(struct symbols *symbols,
symbol__set_ifunc_alias(ret_sym, true);
symbols->symbols[match_idx] = ret_sym;
symbol__put(fresh_sym);
- } else if (__symbols__insert(symbols, ret_sym) == 0) {
+ } else if (__symbols__insert(symbols, ret_sym, session) == 0) {
need_sort = true;
}
}
@@ -1080,17 +1083,18 @@ static void symbols__merge_retained(struct symbols *symbols,
free(retained);
}
-static int __symbols__insert_sorted(struct symbols *symbols, struct symbol *sym)
+static int __symbols__insert_sorted(struct symbols *symbols, struct symbol *sym,
+ struct perf_session *session)
{
unsigned int idx;
if (!symbols->sorted)
- return __symbols__insert(symbols, sym);
+ return __symbols__insert(symbols, sym, session);
if (symbols->cnt == symbols->allocated) {
unsigned int to_allocate = symbols->allocated ? symbols->allocated * 2 : 32;
- struct symbol **temp = realloc(symbols->symbols,
- sizeof(struct symbol *) * to_allocate);
+ struct symbol **temp = perf_session__realloc(session, symbols->symbols,
+ sizeof(struct symbol *) * to_allocate);
if (!temp) {
symbol__put(sym);
@@ -1118,16 +1122,22 @@ static int __symbols__insert_sorted(struct symbols *symbols, struct symbol *sym)
return 0;
}
-int symbols__insert(struct symbols *symbols, struct symbol *sym)
+int symbols__insert_session(struct symbols *symbols, struct symbol *sym,
+ struct perf_session *session)
{
int ret;
down_write(&symbols->lock);
- ret = __symbols__insert(symbols, sym);
+ ret = __symbols__insert(symbols, sym, session);
up_write(&symbols->lock);
return ret;
}
+int symbols__insert(struct symbols *symbols, struct symbol *sym)
+{
+ return symbols__insert_session(symbols, sym, NULL);
+}
+
/*
* Binary search @symbols->symbols for a symbol covering @ip. Lazily sorts
* @symbols under @symbols->lock if unsorted.
@@ -1288,7 +1298,7 @@ int dso__insert_symbol(struct dso *dso, struct symbol *sym)
int ret;
down_write(&symbols->lock);
- ret = __symbols__insert_sorted(symbols, sym);
+ ret = __symbols__insert_sorted(symbols, sym, dso__session(dso));
up_write(&symbols->lock);
return ret;
}
@@ -1633,7 +1643,7 @@ static int map__process_kallsym_symbol(void *arg, const char *name,
* We will pass the symbols to the filter later, in
* map__split_kallsyms, when we have split the maps per module
*/
- return symbols__insert(root, sym);
+ return symbols__insert_session(root, sym, dso__session(dso));
}
/*
@@ -1697,7 +1707,8 @@ static int maps__split_kallsyms_for_kcore(struct maps *kmaps, struct dso *dso)
symbol__set_end(pos, symbol__end(pos) -
(map__start(curr_map) - map__pgoff(curr_map)));
}
- if (symbols__insert(dso__symbols(curr_map_dso), pos) == 0)
+ if (symbols__insert_session(dso__symbols(curr_map_dso), pos,
+ dso__session(curr_map_dso)) == 0)
++count;
map__put(curr_map);
}
@@ -1873,7 +1884,8 @@ static int maps__split_kallsyms(struct maps *kmaps, struct dso *dso, u64 delta,
if (!RC_CHK_EQUAL(curr_map, initial_map)) {
struct dso *curr_map_dso = map__dso(curr_map);
- if (symbols__insert(dso__symbols(curr_map_dso), pos) == 0)
+ if (symbols__insert_session(dso__symbols(curr_map_dso), pos,
+ dso__session(curr_map_dso)) == 0)
++moved;
} else {
old_symbols[write_idx++] = pos;
@@ -1895,7 +1907,7 @@ static int maps__split_kallsyms(struct maps *kmaps, struct dso *dso, u64 delta,
old_symbols = NULL;
} else {
for (unsigned int j = 0; j < write_idx; j++)
- __symbols__insert(root, old_symbols[j]);
+ __symbols__insert(root, old_symbols[j], dso__session(dso));
}
zfree(&root->symbols_by_name);
root->num_by_name = 0;
@@ -1926,7 +1938,7 @@ static int maps__split_kallsyms(struct maps *kmaps, struct dso *dso, u64 delta,
old_symbols = NULL;
} else {
for (unsigned int j = 0; j < write_idx; j++)
- __symbols__insert(root, old_symbols[j]);
+ __symbols__insert(root, old_symbols[j], dso__session(dso));
root->sorted_cnt = 0;
root->sorted = false;
}
@@ -2497,7 +2509,7 @@ static int dso__load_perf_map(const char *map_path, struct dso *dso)
if (sym == NULL)
goto out_delete_line;
- if (symbols__insert(dso__symbols(dso), sym))
+ if (symbols__insert_session(dso__symbols(dso), sym, dso__session(dso)))
goto out_delete_line;
nr_syms++;
}
@@ -2674,7 +2686,7 @@ int dso__load(struct dso *dso, struct map *map)
if (machine)
root_dir = machine->root_dir;
- name = malloc(PATH_MAX);
+ name = perf_session__malloc(dso__session(dso), PATH_MAX);
if (!name)
goto out;
@@ -2750,8 +2762,11 @@ int dso__load(struct dso *dso, struct map *map)
if (!syms_ss && symsrc__has_symtab(ss)) {
syms_ss = ss;
next_slot = true;
- if (!dso__symsrc_filename(dso))
- dso__set_symsrc_filename(dso, strdup(name));
+ if (!dso__symsrc_filename(dso)) {
+ dso__set_symsrc_filename(dso,
+ perf_session__strdup(dso__session(dso),
+ name));
+ }
}
if (!runtime_ss && symsrc__possibly_runtime(ss)) {
@@ -2805,7 +2820,8 @@ int dso__load(struct dso *dso, struct map *map)
out:
if (was_shrunk) {
if (ret > 0) {
- symbols__merge_retained(dso__symbols(dso), retained, retained_cnt);
+ symbols__merge_retained(dso__symbols(dso), retained, retained_cnt,
+ dso__session(dso));
} else {
dso__set_adjust_symbols(dso, saved_adjust_symbols);
symbols__restore_retained(dso__symbols(dso), retained,
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index 7d6469d6a976..2db5eb84b268 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -33,6 +33,7 @@ struct maps;
struct option;
struct build_id;
struct perf_env;
+struct perf_session;
/*
* Ignore kernel mapping symbols, matching kernel is_mapping_symbol() logic.
@@ -376,6 +377,8 @@ int dso__synthesize_plt_symbols(struct dso *dso, struct symsrc *ss);
char *dso__demangle_sym(struct dso *dso, int kmodule, const char *elf_name);
int symbols__insert(struct symbols *symbols, struct symbol *sym);
+int symbols__insert_session(struct symbols *symbols, struct symbol *sym,
+ struct perf_session *session);
void symbols__fixup_duplicate(struct symbols *symbols);
void symbols__fixup_end(struct symbols *symbols, bool is_kallsyms);
--
2.56.0.313.g2479322e68-goog
^ permalink raw reply [flat|nested] 37+ messages in thread* [PATCH v2 18/18] perf test symbols: Add tests for symbol shrinking and session alloc wrappers
2026-10-27 23:14 ` [PATCH v2 00/18] perf symbol: Reference counting, sorted arrays, and memory shrinking Ian Rogers
` (16 preceding siblings ...)
2026-10-27 23:15 ` [PATCH v2 17/18] perf symbol: Use session allocation wrappers in symbol, DSO, thread, and hist paths Ian Rogers
@ 2026-10-27 23:15 ` Ian Rogers
2026-10-28 20:29 ` [PATCH v2 00/18] perf symbol: Reference counting, sorted arrays, and memory shrinking Ian Rogers
18 siblings, 0 replies; 37+ messages in thread
From: Ian Rogers @ 2026-10-27 23:15 UTC (permalink / raw)
To: irogers, acme, namhyung
Cc: adrian.hunter, haghdoost, james.clark, jolsa, linux-kernel,
linux-perf-users, mingo, peterz
Ensure the new symbol sorting, reference-counted symbol and DSO
shrinking, transparent DSO reload with retained-symbol pointer
preservation, and session-scoped allocation wrappers are verified by
'perf test symbols' (including under REFCNT_CHECKING=1).
Add unit tests to 'perf test symbols' covering:
- test_symbols_sorted_insert(): Verify ascending symbols__insert(),
out-of-order dso__insert_symbol() maintaining sorted order, and
unsorted symbols__insert() followed by sort-on-lookup in
dso__find_symbol().
- test_symbols_shrink(): Verify symbols__shrink() evicts unreferenced
symbols (refcnt == 1), retains symbols with active external references
(refcnt > 1), and sets symbols.shrunk.
- test_dsos_shrink(): Verify dsos__shrink() evicts unreferenced user
DSOs with no remaining symbols while retaining DSOs with active
external references.
- test_session_alloc_shrink(): Verify machine__session(),
dso__session(), perf_session__malloc/zalloc/calloc/realloc/strdup(),
and perf_session__shrink().
- test_dso_shrink_and_reload(): Verify dso__shrink() on a loaded ELF
binary followed by map__find_symbol() transparently reloads evicted
symbols while preserving pointer identity for retained symbols.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/tests/symbols.c | 372 +++++++++++++++++++++++++++++++++++++
1 file changed, 372 insertions(+)
diff --git a/tools/perf/tests/symbols.c b/tools/perf/tests/symbols.c
index d70adbad3978..1a773c535732 100644
--- a/tools/perf/tests/symbols.c
+++ b/tools/perf/tests/symbols.c
@@ -1,13 +1,21 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/compiler.h>
#include <linux/string.h>
+#include <linux/zalloc.h>
#include <sys/mman.h>
+#include <errno.h>
#include <inttypes.h>
#include <limits.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
#include "debug.h"
#include "dso.h"
#include "env.h"
#include "machine.h"
+#include "session.h"
#include "thread.h"
#include "symbol.h"
#include "map.h"
@@ -179,6 +187,264 @@ static int process_subdivided_dso(struct machine *machine, struct dso *dso)
return ret < 0 ? TEST_FAIL : TEST_OK;
}
+static int test_symbols_shrink(void)
+{
+ struct symbols symbols;
+ struct symbol *sym1, *sym2, *sym3, *held;
+ int ret = TEST_FAIL;
+
+ symbols__init(&symbols);
+
+ sym1 = symbol__new(0x100, 0x50, STB_GLOBAL, STT_FUNC, "sym1");
+ sym2 = symbol__new(0x200, 0x50, STB_GLOBAL, STT_FUNC, "sym2");
+ sym3 = symbol__new(0x300, 0x50, STB_GLOBAL, STT_FUNC, "sym3");
+ if (!sym1 || !sym2 || !sym3) {
+ symbol__put(sym1);
+ symbol__put(sym2);
+ symbol__put(sym3);
+ goto out;
+ }
+
+ /* Hold an active reference on sym1; sym2 and sym3 only have the table ref. */
+ held = symbol__get(sym1);
+
+ symbols__insert(&symbols, sym1);
+ symbols__insert(&symbols, sym2);
+ symbols__insert(&symbols, sym3);
+
+ /*
+ * First shrink: sym2 and sym3 (refcnt == 1) are evicted; sym1 has
+ * refcnt > 1 and is retained.
+ */
+ if (symbols__shrink(&symbols) != 2 || symbols.cnt != 1 || !symbols.shrunk) {
+ pr_debug("First symbols__shrink failed (cnt=%u)!\n", symbols.cnt);
+ symbol__put(held);
+ goto out;
+ }
+
+ /*
+ * Drop caller reference on sym1 and shrink again: sym1 is now evicted.
+ */
+ symbol__put(held);
+ if (symbols__shrink(&symbols) != 1 || symbols.cnt != 0) {
+ pr_debug("Second symbols__shrink failed (cnt=%u)!\n", symbols.cnt);
+ goto out;
+ }
+
+ ret = TEST_OK;
+out:
+ symbols__exit(&symbols);
+ return ret;
+}
+
+static int test_dsos_shrink(void)
+{
+ struct dsos dsos;
+ struct dso *dso1, *dso2;
+ int ret = TEST_FAIL;
+
+ dsos__init(&dsos);
+
+ dso1 = dsos__findnew_id(&dsos, "/tmp/perf_test_dso1", NULL);
+ dso2 = dsos__findnew_id(&dsos, "/tmp/perf_test_dso2", NULL);
+ if (!dso1 || !dso2) {
+ dso__put(dso1);
+ dso__put(dso2);
+ goto out;
+ }
+
+ /* Keep the caller's active reference on dso1; drop caller reference on dso2. */
+ dso__put(dso2);
+
+ /*
+ * First shrink: dso2 has refcnt == 1 and no symbols, so it is evicted;
+ * dso1 has refcnt == 2, so it is retained.
+ */
+ if (dsos__shrink(&dsos) != 1 || dsos.cnt != 1) {
+ pr_debug("First dsos__shrink failed (cnt=%u)!\n", dsos.cnt);
+ dso__put(dso1);
+ goto out;
+ }
+
+ dso__put(dso1);
+ if (dsos__shrink(&dsos) != 1 || dsos.cnt != 0) {
+ pr_debug("Second dsos__shrink failed (cnt=%u)!\n", dsos.cnt);
+ goto out;
+ }
+
+ ret = TEST_OK;
+out:
+ dsos__exit(&dsos);
+ return ret;
+}
+
+static int test_session_alloc_shrink(void)
+{
+ struct perf_session *session = zalloc(sizeof(*session));
+ struct dso *dso;
+ char *p1 = NULL, *p2 = NULL, *p3 = NULL, *s = NULL;
+ int ret = TEST_FAIL;
+
+ if (!session)
+ return TEST_FAIL;
+
+ if (machines__init(&session->machines, session))
+ goto out;
+ if (machine__session(&session->machines.host) != session) {
+ pr_debug("machine__session did not return owning session!\n");
+ goto out;
+ }
+
+ dso = machine__findnew_dso(&session->machines.host, "/tmp/perf_test_session_dso");
+ if (!dso)
+ goto out;
+ if (dso__session(dso) != session) {
+ pr_debug("dso__session did not return owning session!\n");
+ dso__put(dso);
+ goto out;
+ }
+ dso__put(dso);
+
+ p1 = perf_session__malloc(session, 64);
+ p2 = perf_session__zalloc(session, 64);
+ p3 = perf_session__calloc(session, 4, 16);
+ s = perf_session__strdup(session, "perf_session_alloc_test");
+ if (!p1 || !p2 || !p3 || !s || strcmp(s, "perf_session_alloc_test") != 0) {
+ pr_debug("perf_session allocation wrappers failed!\n");
+ goto out;
+ }
+
+ p1 = perf_session__realloc(session, p1, 128);
+ if (!p1) {
+ pr_debug("perf_session__realloc failed!\n");
+ goto out;
+ }
+
+ if (perf_session__shrink(session) == 0 || session->machines.host.dsos.cnt != 0) {
+ pr_debug("perf_session__shrink failed to evict unreferenced DSO!\n");
+ goto out;
+ }
+
+ ret = TEST_OK;
+out:
+ free(p1);
+ free(p2);
+ free(p3);
+ free(s);
+ machines__exit(&session->machines);
+ free(session);
+ return ret;
+}
+
+struct shrink_reload_find_args {
+ struct symbol *kept;
+ u64 first_start;
+ u64 second_start;
+ char *second_name;
+ u64 max_end;
+};
+
+static int shrink_reload_find_cb(struct symbol *sym, void *data)
+{
+ struct shrink_reload_find_args *args = data;
+
+ if (!args->kept) {
+ args->kept = symbol__get(sym);
+ args->first_start = symbol__start(sym);
+ args->max_end = symbol__end(sym);
+ return 0;
+ }
+ if (!args->second_name && symbol__start(sym) >= args->max_end) {
+ args->second_start = symbol__start(sym);
+ args->second_name = strdup(symbol__name(sym));
+ if (!args->second_name)
+ return -ENOMEM;
+ }
+ if (symbol__end(sym) > args->max_end)
+ args->max_end = symbol__end(sym);
+ return 0;
+}
+
+static int test_dso_shrink_and_reload(struct dso *dso, struct map *map)
+{
+ struct shrink_reload_find_args args = {
+ .kept = NULL,
+ .first_start = 0,
+ .second_start = 0,
+ .second_name = NULL,
+ .max_end = 0,
+ };
+ struct symbol *sym, *reloaded;
+ size_t removed;
+
+ if (!dso__can_shrink_symbols(dso) || dso__kernel(dso))
+ return TEST_OK;
+ if (dso__for_each_symbol(dso, shrink_reload_find_cb, &args) < 0) {
+ symbol__put(args.kept);
+ return TEST_FAIL;
+ }
+ if (!args.second_name) {
+ symbol__put(args.kept);
+ return TEST_OK;
+ }
+
+ /* Unreferenced symbols have refcnt == 1 and are evicted immediately. */
+ removed = dso__shrink(dso);
+ if (removed == 0 || !dso__symbols_shrunk(dso)) {
+ pr_debug("dso__shrink did not evict any symbols!\n");
+ free(args.second_name);
+ symbol__put(args.kept);
+ return TEST_FAIL;
+ }
+
+ /* Kept symbol is still found without reloading. */
+ sym = dso__find_symbol(dso, args.first_start);
+ if (!RC_CHK_EQUAL(sym, args.kept)) {
+ pr_debug("Kept symbol not found after dso__shrink!\n");
+ symbol__put(sym);
+ free(args.second_name);
+ symbol__put(args.kept);
+ return TEST_FAIL;
+ }
+ symbol__put(sym);
+
+ /* Evicted symbol is no longer in dso->symbols before reload. */
+ sym = dso__find_symbol(dso, args.second_start);
+ if (sym != NULL) {
+ pr_debug("Evicted symbol still found after dso__shrink!\n");
+ symbol__put(sym);
+ free(args.second_name);
+ symbol__put(args.kept);
+ return TEST_FAIL;
+ }
+
+ /* Lookup via map__find_symbol transparently reloads the DSO. */
+ reloaded = map__find_symbol(map, args.second_start);
+ if (!reloaded || strcmp(symbol__name(reloaded), args.second_name) != 0) {
+ pr_debug("Failed to reload evicted symbol %s!\n", args.second_name);
+ symbol__put(reloaded);
+ free(args.second_name);
+ symbol__put(args.kept);
+ return TEST_FAIL;
+ }
+ symbol__put(reloaded);
+
+ /* Kept symbol pointer was preserved across reload deduplication. */
+ sym = dso__find_symbol(dso, args.first_start);
+ if (!RC_CHK_EQUAL(sym, args.kept)) {
+ pr_debug("Kept symbol pointer was not preserved across reload!\n");
+ symbol__put(sym);
+ free(args.second_name);
+ symbol__put(args.kept);
+ return TEST_FAIL;
+ }
+ symbol__put(sym);
+ symbol__zput(args.kept);
+
+ free(args.second_name);
+ return TEST_OK;
+}
+
static int test_file(struct test_info *ti, char *filename)
{
struct map *map = NULL;
@@ -210,18 +476,124 @@ static int test_file(struct test_info *ti, char *filename)
/* Module dso is split into many dsos by section */
if (ret == TEST_OK && dso__kernel(dso) != DSO_SPACE__USER)
ret = process_subdivided_dso(ti->machine, dso);
+
+ if (ret == TEST_OK)
+ ret = test_dso_shrink_and_reload(dso, map);
out_put:
map__put(map);
return ret;
}
+static int test_symbols_sorted_insert(void)
+{
+ struct dso *dso = dso__new("/tmp/perf_test_sorted_insert");
+ struct symbols *symbols;
+ int ret = TEST_FAIL;
+
+ if (!dso)
+ return TEST_FAIL;
+
+ symbols = dso__symbols(dso);
+
+ /*
+ * Insert 100 odd-indexed symbols in ascending order via
+ * symbols__insert(), then insert 5 even-indexed symbols out of order via
+ * dso__insert_symbol() and verify symbols->sorted remains true.
+ */
+ for (unsigned int i = 0; i < 100; i++) {
+ char name[32];
+ u64 addr = (u64)(i * 2 + 1) * 0x100;
+ struct symbol *sym;
+
+ snprintf(name, sizeof(name), "sym_%u", i * 2 + 1);
+ sym = symbol__new(addr, 0x40, STB_GLOBAL, STT_FUNC, name);
+ if (!sym || symbols__insert(symbols, sym))
+ goto out;
+ }
+ if (!symbols->sorted || symbols->cnt != 100) {
+ pr_debug("Expected sorted=true, cnt=100 after ordered inserts\n");
+ goto out;
+ }
+
+ for (unsigned int i = 0; i < 5; i++) {
+ char name[32];
+ u64 addr = (u64)(i * 20) * 0x100;
+ struct symbol *sym;
+
+ snprintf(name, sizeof(name), "ins_%u", i * 20);
+ sym = symbol__new(addr, 0x40, STB_GLOBAL, STT_FUNC, name);
+ if (!sym || dso__insert_symbol(dso, sym))
+ goto out;
+ if (!symbols->sorted) {
+ pr_debug("dso__insert_symbol did not preserve sorted invariant!\n");
+ goto out;
+ }
+ }
+
+ /*
+ * Now append 95 remaining even-indexed symbols in descending order via
+ * symbols__insert() to exercise the unsorted append + sort-on-lookup path.
+ */
+ for (unsigned int i = 99; i > 0; i--) {
+ char name[32];
+ u64 addr;
+ struct symbol *sym;
+
+ if ((i * 2) % 20 == 0 && (i * 2) < 100)
+ continue;
+ addr = (u64)(i * 2) * 0x100;
+ snprintf(name, sizeof(name), "ins_%u", i * 2);
+ sym = symbol__new(addr, 0x40, STB_GLOBAL, STT_FUNC, name);
+ if (!sym || symbols__insert(symbols, sym))
+ goto out;
+ }
+ if (symbols->sorted || symbols->cnt != 200) {
+ pr_debug("Unexpected sorted=%d cnt=%u before lookup\n",
+ symbols->sorted, symbols->cnt);
+ goto out;
+ }
+
+ for (unsigned int i = 0; i < 200; i++) {
+ u64 addr = (u64)i * 0x100 + 0x10;
+ struct symbol *sym = dso__find_symbol(dso, addr);
+
+ if (!sym || symbol__start(sym) != (u64)i * 0x100) {
+ pr_debug("Lookup failed at idx %u (addr=%#" PRIx64 ")\n", i, addr);
+ symbol__put(sym);
+ goto out;
+ }
+ symbol__put(sym);
+ }
+
+ ret = TEST_OK;
+out:
+ dso__put(dso);
+ return ret;
+}
+
static int test__symbols(struct test_suite *test __maybe_unused, int subtest __maybe_unused)
{
char filename[PATH_MAX];
struct test_info ti;
int ret;
+ ret = test_symbols_sorted_insert();
+ if (ret != TEST_OK)
+ return ret;
+
+ ret = test_symbols_shrink();
+ if (ret != TEST_OK)
+ return ret;
+
+ ret = test_dsos_shrink();
+ if (ret != TEST_OK)
+ return ret;
+
+ ret = test_session_alloc_shrink();
+ if (ret != TEST_OK)
+ return ret;
+
ret = init_test_info(&ti);
if (ret != TEST_OK)
return ret;
--
2.56.0.313.g2479322e68-goog
^ permalink raw reply [flat|nested] 37+ messages in thread* Re: [PATCH v2 00/18] perf symbol: Reference counting, sorted arrays, and memory shrinking
2026-10-27 23:14 ` [PATCH v2 00/18] perf symbol: Reference counting, sorted arrays, and memory shrinking Ian Rogers
` (17 preceding siblings ...)
2026-10-27 23:15 ` [PATCH v2 18/18] perf test symbols: Add tests for symbol shrinking and session alloc wrappers Ian Rogers
@ 2026-10-28 20:29 ` Ian Rogers
18 siblings, 0 replies; 37+ messages in thread
From: Ian Rogers @ 2026-10-28 20:29 UTC (permalink / raw)
To: irogers, acme, namhyung
Cc: adrian.hunter, haghdoost, james.clark, jolsa, linux-kernel,
linux-perf-users, mingo, peterz
On Tue, Oct 27, 2026 at 4:15 PM Ian Rogers <irogers@google.com> wrote:
>
> When profiling large binaries or long-running workloads with many DSOs and
> threads, symbol and DSO tables frequently dominate perf's memory footprint
> and startup latency. Currently `struct symbol` has several structural
> limitations:
>
> 1. Every `struct symbol` embeds a 24-byte `struct rb_node` for the
> `dso->symbols` address rbtree, plus an optional negative-offset
> allocation prefix (`symbol_conf.priv_size`) for TUI browser indices,
> `db-export` IDs, or `struct annotation` state even when only a tiny
> fraction of symbols are ever annotated or exported.
> 2. Unlike `struct map`, `struct dso`, and `struct thread`, `struct symbol`
> is not reference-counted: once loaded into a `struct dso`, symbols can
> never be safely evicted while `hist_entry`, `callchain_cursor_node`, or
> `map_symbol` references exist, and `dso__symbols()` has no internal
> synchronization against concurrent readers/writers.
> 3. ELF symbol table loading (`dso__load_sym_internal()`) repeatedly
> invokes `libelf` section/program header lookups per symbol, calls
> `elf_sym__name()` before filtering out non-function/non-allocated
> symbols, runs Rust/Java demanglers on plain C symbols without fast-path
> prefix checks, and resorts `dso->symbols` with `qsort()` even when ELF
> symbol tables are already largely ordered in ascending address runs.
>
> This series modernizes `struct symbol` to match the rest of `tools/perf/util`
> (`DECLARE_RC_STRUCT`, `struct rw_semaphore`, sorted pointer arrays) and
> introduces symbol/DSO memory shrinking and faster ELF symbol loading:
>
> - Patch 1 adds `perf bench internals symbols` and `perf bench internals maps`
> to benchmark ELF symbol loading, bulk symbol insertion/sorting/lookup,
> incremental symbol appends, ascending map insertion, and incremental map
> appends.
> - Patch 2 adds fast-path prefix checks in `dso__demangle_sym()` (before
> calling `rust_demangle_demangle()`, keeping `demangle-rust-v0.c`
> unmodified relative to upstream `rustc-demangle`) and in
> `java_demangle_sym()`.
> - Patches 3-6 encapsulate `struct symbol` field access behind accessor
> functions (annotated with `__attribute__((nonnull))`) and replace the
> negative-offset `symbol_conf.priv_size` allocation prefix with on-demand
> browser index calculation and per-DSO/per-exporter hashmaps for
> `db-export` and `struct annotation`.
> - Patch 7 replaces `dso->symbols` (`rb_root_cached`) and `dso->symbol_names`
> with a locked `struct symbols` container backed by sorted pointer arrays
> (`symbols` and `symbols_by_name`) protected by a `struct rw_semaphore`,
> removing `struct rb_node` from `struct symbol`.
> - Patch 8 wraps `struct symbol` in `DECLARE_RC_STRUCT(symbol)` with
> `refcount_t` (`symbol__get()` / `symbol__put()` / `symbol__zput()`) for
> sanitizer-checked reference counting across `map_symbol`, `addr_location`,
> `hist_entry`, `callchain`, `thread_stack`, and `db-export`.
> - Patch 9 replaces `qsort()` in `struct symbols` with a natural-run merge
> sort and galloping merge (taking advantage of pre-ordered runs in
> `.symtab`/`.dynsym` and `kallsyms`) plus an in-place binary insertion
> fast path for small incremental appends.
> - Patch 10 applies the same incremental binary-insertion sort and an O(1)
> tail-insertion fast path to `struct maps` for `/proc/<pid>/maps` synthesis
> and runtime `PERF_RECORD_MMAP2` / JIT map insertions.
> - Patch 11 caches ELF program and section headers during
> `dso__load_sym_internal()`, defers `elf_sym__name()` string table lookups
> until after symbol filtering, and runs `symbols__fixup_end()` /
> `symbols__fixup_duplicate()` once per DSO load.
> - Patches 12-17 add `down_read_trylock()` / `down_write_trylock()`, protect
> `machines->guests` with a `rw_semaphore`, implement `symbols__shrink()`,
> `dso__shrink()`, `dsos__shrink()`, and `perf_session__shrink()` (with
> transparent on-demand reload in `dso__load()` that preserves pointer
> identity for retained symbols), and provide `perf_session__malloc()` /
> `zalloc()` / `calloc()` / `realloc()` / `strdup()` wrappers that
> automatically shrink unreferenced symbols and DSOs on allocation failure.
> - Patch 18 adds unit tests in `perf test symbols` covering sorted symbol
> insertion, symbol/DSO shrinking, transparent DSO reload after shrink, and
> session allocation wrappers.
>
> End-to-end benchmark results across the series:
>
> perf bench internals symbols (perf binary):
> Average DSO load + sort: 7.348 msec -> 5.324 msec (-27.5%)
> Average DSO memory usage: 1401 KB -> 1308 KB ( -6.6%)
> Average bulk insert + lookup: 1453.95 us -> 1161.00 us (-20.1%)
> Average append + lookup (200 interior inserts into 10,000 symbols):
> rbtree: 42.85 us -> array+qsort: 40429.75 us -> array+binary-insert: 152.70 us
>
> perf bench internals symbols -b libLLVM.so.21.1:
> Average DSO load + sort: 81.196 msec -> 62.582 msec (-22.9%)
> Average DSO memory usage: 9530 KB -> 9164 KB ( -3.8%)
>
> perf bench internals symbols -b libLLVM.so.21.1 --no-demangle:
> Average DSO load + sort: 32.675 msec -> 19.122 msec (-41.5%)
> Average DSO memory usage: 7589 KB -> 7229 KB ( -4.7%)
>
> perf bench internals maps:
> Average ascending insert + lookup: 63.37 us -> 58.97 us ( -6.9%)
> Average append + lookup: 7477.45 us -> 96.67 us (-98.7%, 77.3x faster)
>
> Net performance and memory trade-offs:
> - Replacing the per-symbol rbtree (`struct rb_node`) and negative-offset
> `symbol_conf.priv_size` allocation prefix with a contiguous sorted pointer
> array reduces per-symbol heap overhead by 16 to 24+ bytes (saving 3.8% to
> 6.6% of total DSO symbol table heap memory unconditionally, and allowing
> unreferenced symbols and DSOs to be shrunk on demand under memory
> pressure).
> - For the dominant symbol workloads—loading ELF binaries (`-22.9%` to
> `-41.5%`) and bulk symbol insertion, sorting, and lookup (`-20.1%`)—the
> contiguous array, natural-run merge sort, ELF header caching, and
> demangler fast paths yield substantial speedups over the rbtree baseline.
> - For synthetic single-symbol interior insertions interleaved with immediate
> lookups into an already-large 10,000-symbol DSO (`dso__insert_symbol()`),
> an rbtree rotation takes ~0.21 us/op (`42.85 us` for 200 ops) whereas
> shifting the contiguous pointer array tail with `memmove()` after binary
> search takes ~0.76 us/op (`152.70 us` for 200 ops, down from `40429.75 us`
> with naive `qsort()`). In real workloads, symbols are either loaded in
> bulk (`dso__load()`) or inserted into small per-prog BPF/JIT DSOs where
> the array fits in a few cache lines, making this sub-microsecond
> difference negligible in practice.
> - Overall, the series reduces memory consumption across all workloads while
> significantly improving—or remaining neutral on—runtime performance.
>
> v2:
> - Rebased onto perf-tools-next (dropping "perf symbols: Fix broken
> ELF_C_READ_MMAP fallback guard", which was merged upstream, and splitting
> "perf libdw: Optimize fallback logic for missing DWARF" into a separate
> fixes series).
> - Added 'perf bench internals symbols' and 'perf bench internals maps'
> benchmarks (Patch 1) and included before/after numbers in the optimization
> patches.
> - Added __attribute__((nonnull)) annotations to struct symbol accessor
> functions (Patch 3).
> - Split the symbol_conf.priv_size removal patch into three separate patches
> for map_browser (Patch 4), db-export (Patch 5), and annotate (Patch 6).
> - Added dedicated optimization patches for Rust/Java demangler prefix checks
> (Patch 2), natural-run merge sort and binary insertion in struct symbols
> (Patch 9), tail-insertion and incremental binary-insertion sort in struct
> maps (Patch 10), and ELF program/section header caching and deferred
> symbol name lookup (Patch 11).
> - Replaced periodic heuristic LRU shrinking and windowed ELF symbol loading
> with on-demand shrinking (symbols__shrink(), dso__shrink(),
> dsos__shrink(), perf_session__shrink()) triggered on allocation failure via
> session-scoped allocation wrappers, plus transparent DSO reload preserving
> retained symbol pointers (Patches 12-18).
>
> v1: https://lore.kernel.org/lkml/20260928075237.3055101-1-irogers@google.com/
>
> Assisted-by: Antigravity:gemini-3.1-pro
> Signed-off-by: Ian Rogers <irogers@google.com>
So Sashiko failed to apply the patches (local Sashiko runs were clean)
because I had these four patches applied locally:
- Alireza's "perf symbols: Fix broken ELF_C_READ_MMAP fallback guard"
https://lore.kernel.org/linux-perf-users/20261005-perf-symbol-memory-send-v5-1-165dceb2b049@uber.com/
- My fixes in "perf: Fix libdw addr2line fallback, pr_debugN
overhead, and cumulative hist decay"
https://lore.kernel.org/linux-perf-users/cover.1792995775.git.irogers@google.com/
Hopefully the patches above won't take too long to land, so I'll hold
off sending v3 until then.
Thanks,
Ian
> Ian Rogers (18):
> perf bench internals: Add symbols and maps benchmarks
> perf demangle: Add fast-path prefix checks for Rust and Java
> demanglers
> perf symbol: Add accessor functions for struct symbol fields
> perf map_browser: Compute symbol browser index on demand
> perf db-export: Store symbol database IDs in a hashmap
> perf annotate: Move struct annotation to a hashmap and remove
> symbol_conf.priv_size
> perf symbol: Switch backing storage from rbtree to struct symbols
> array
> perf symbol: Add reference counting and DECLARE_RC_STRUCT(symbol)
> perf symbol: Optimize symbol sorting with natural-run merge sort and
> binary insertion
> perf maps: Optimize tail insertion and small-append sorting
> perf symbol-elf: Cache ELF program/section headers and defer symbol
> name lookup
> perf rwsem: Add down_read_trylock() and down_write_trylock()
> perf vdso: Constify dso parameter in dso__is_vdso()
> perf machine: Protect machines->guests rbtree with a rw_semaphore
> perf symbol: Add memory shrinking for symbols, DSOs, and machines
> perf session: Add dso__session() and session-scoped allocation
> wrappers
> perf symbol: Use session allocation wrappers in symbol, DSO, thread,
> and hist paths
> perf test symbols: Add tests for symbol shrinking and session alloc
> wrappers
>
> tools/perf/Documentation/perf-bench.txt | 6 +
> .../arch/powerpc/util/skip-callchain-idx.c | 2 +-
> tools/perf/arch/powerpc/util/sym-handling.c | 40 +-
> tools/perf/bench/Build | 2 +
> tools/perf/bench/bench.h | 2 +
> tools/perf/bench/maps.c | 186 +++
> tools/perf/bench/symbols.c | 239 +++
> tools/perf/builtin-annotate.c | 22 +-
> tools/perf/builtin-bench.c | 2 +
> tools/perf/builtin-c2c.c | 14 -
> tools/perf/builtin-diff.c | 4 +-
> tools/perf/builtin-kallsyms.c | 9 +-
> tools/perf/builtin-kmem.c | 82 +-
> tools/perf/builtin-kwork.c | 4 +-
> tools/perf/builtin-lock.c | 25 +-
> tools/perf/builtin-mem.c | 2 +-
> tools/perf/builtin-report.c | 14 -
> tools/perf/builtin-sched.c | 6 +-
> tools/perf/builtin-script.c | 18 +-
> tools/perf/builtin-timechart.c | 2 +-
> tools/perf/builtin-top.c | 24 +-
> tools/perf/builtin-trace.c | 23 +-
> tools/perf/tests/builtin-test.c | 1 -
> tools/perf/tests/dlfilter-test.c | 12 +-
> tools/perf/tests/dwarf-unwind.c | 2 +-
> tools/perf/tests/hists_common.c | 9 +-
> tools/perf/tests/hists_cumulate.c | 8 +-
> tools/perf/tests/hists_filter.c | 10 +-
> tools/perf/tests/hists_link.c | 16 +-
> tools/perf/tests/hists_output.c | 9 +-
> tools/perf/tests/kallsyms-split.c | 5 +-
> tools/perf/tests/maps.c | 58 +
> tools/perf/tests/pe-file-parsing.c | 1 +
> tools/perf/tests/symbols.c | 454 +++++-
> tools/perf/tests/vmlinux-kallsyms.c | 187 ++-
> tools/perf/ui/browser.c | 3 +
> tools/perf/ui/browsers/annotate.c | 10 +-
> tools/perf/ui/browsers/hists.c | 35 +-
> tools/perf/ui/browsers/map.c | 88 +-
> tools/perf/ui/gtk/annotate.c | 6 +-
> tools/perf/ui/stdio/hist.c | 6 +-
> tools/perf/util/addr2line.c | 8 +-
> tools/perf/util/addr_location.c | 4 +
> .../perf/util/annotate-arch/annotate-alpha.c | 5 +-
> .../perf/util/annotate-arch/annotate-arm64.c | 12 +-
> .../util/annotate-arch/annotate-loongarch.c | 15 +-
> tools/perf/util/annotate-arch/annotate-s390.c | 6 +-
> tools/perf/util/annotate-arch/annotate-x86.c | 14 +-
> tools/perf/util/annotate-data.c | 12 +-
> tools/perf/util/annotate.c | 211 ++-
> tools/perf/util/annotate.h | 6 +-
> tools/perf/util/auxtrace.c | 111 +-
> tools/perf/util/block-info.c | 12 +-
> tools/perf/util/bpf_lock_contention.c | 35 +-
> tools/perf/util/c2c-function.c | 32 +-
> tools/perf/util/call-path.c | 15 +-
> tools/perf/util/callchain.c | 23 +-
> tools/perf/util/capstone.c | 22 +-
> tools/perf/util/data-convert-json.c | 4 +-
> tools/perf/util/db-export.c | 99 +-
> tools/perf/util/db-export.h | 8 +
> tools/perf/util/debug.c | 3 +-
> tools/perf/util/demangle-java.c | 23 +-
> tools/perf/util/disasm.c | 69 +-
> tools/perf/util/dlfilter.c | 12 +-
> tools/perf/util/dso.c | 257 ++-
> tools/perf/util/dso.h | 94 +-
> tools/perf/util/dsos.c | 76 +
> tools/perf/util/dsos.h | 3 +
> tools/perf/util/event.c | 12 +-
> tools/perf/util/evsel_fprintf.c | 2 +-
> tools/perf/util/hist.c | 70 +-
> tools/perf/util/intel-pt.c | 74 +-
> tools/perf/util/libbfd.c | 14 +-
> tools/perf/util/libdw.c | 5 +-
> tools/perf/util/llvm.c | 8 +-
> tools/perf/util/lock-contention.c | 8 +-
> tools/perf/util/machine.c | 189 ++-
> tools/perf/util/machine.h | 9 +
> tools/perf/util/map.c | 27 +-
> tools/perf/util/map.h | 18 +-
> tools/perf/util/map_symbol.c | 4 +-
> tools/perf/util/maps.c | 284 +++-
> tools/perf/util/mutex.h | 3 +
> tools/perf/util/probe-event.c | 148 +-
> tools/perf/util/python.c | 36 +-
> tools/perf/util/rwsem.c | 20 +
> tools/perf/util/rwsem.h | 2 +
> tools/perf/util/session.c | 78 +
> tools/perf/util/session.h | 8 +
> tools/perf/util/sort.c | 47 +-
> tools/perf/util/srcline.c | 22 +-
> tools/perf/util/symbol-elf.c | 311 +++-
> tools/perf/util/symbol.c | 1438 +++++++++++++----
> tools/perf/util/symbol.h | 187 ++-
> tools/perf/util/symbol_conf.h | 1 -
> tools/perf/util/symbol_fprintf.c | 37 +-
> tools/perf/util/thread-stack.c | 18 +-
> tools/perf/util/threads.c | 17 +-
> tools/perf/util/unwind-libdw.c | 4 +-
> tools/perf/util/unwind-libunwind.c | 2 +-
> tools/perf/util/vdso.c | 2 +-
> tools/perf/util/vdso.h | 2 +-
> 103 files changed, 4557 insertions(+), 1389 deletions(-)
> create mode 100644 tools/perf/bench/maps.c
> create mode 100644 tools/perf/bench/symbols.c
>
> --
> 2.56.0.313.g2479322e68-goog
>
^ permalink raw reply [flat|nested] 37+ messages in thread