From: Arnaldo Carvalho de Melo <acme@kernel.org>
To: Namhyung Kim <namhyung@kernel.org>
Cc: Ingo Molnar <mingo@kernel.org>,
Thomas Gleixner <tglx@linutronix.de>,
James Clark <james.clark@linaro.org>,
Jiri Olsa <jolsa@kernel.org>, Ian Rogers <irogers@google.com>,
Adrian Hunter <adrian.hunter@intel.com>,
Clark Williams <williams@redhat.com>,
linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org,
Arnaldo Carvalho de Melo <acme@redhat.com>
Subject: [PATCH 01/15] perf debuginfo: Fetch debuginfo keyed by build ID using debuginfod
Date: Thu, 17 Sep 2026 12:55:12 -0300 [thread overview]
Message-ID: <20260917155528.62607-2-acme@kernel.org> (raw)
In-Reply-To: <20260917155528.62607-1-acme@kernel.org>
From: Arnaldo Carvalho de Melo <acme@redhat.com>
Add debuginfo__find_build_id(), which uses the debuginfod client to
locate a debuginfo file keyed by the build ID - checking its local cache
first and then the servers in DEBUGINFOD_URLS - and
debuginfo__new_build_id(), which opens the DWARF in the file it finds.
dso__debuginfo() falls back to it for DSOs whose debuginfo is not
installed locally, e.g. the vmlinux of a kernel profiled on another
machine or upgraded since, doing the fetch outside dso__lock.
Querying servers, possibly third party ones, sends the build IDs of the
binaries being analysed off-box, and a fetch can take a while, so this
is opt-out: --no-debuginfod, core.debuginfod=false,
report.debuginfod/top.debuginfod, and off also when the build-id cache
is disabled. Fetches are serialized and misses are remembered, as both
the lookup state and the fetch interaction are process global.
Assisted-by: LLM
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
tools/perf/Documentation/perf-annotate.txt | 8 +
tools/perf/Documentation/perf-config.txt | 19 ++
tools/perf/Documentation/perf-report.txt | 12 +
tools/perf/Documentation/perf-top.txt | 11 +
tools/perf/builtin-annotate.c | 3 +
tools/perf/builtin-report.c | 7 +
tools/perf/builtin-top.c | 7 +
tools/perf/util/config.c | 3 +
tools/perf/util/debuginfo.c | 327 +++++++++++++++++++++
tools/perf/util/debuginfo.h | 35 +++
tools/perf/util/dso.c | 10 +
tools/perf/util/symbol.c | 2 +
tools/perf/util/symbol_conf.h | 1 +
13 files changed, 445 insertions(+)
diff --git a/tools/perf/Documentation/perf-annotate.txt b/tools/perf/Documentation/perf-annotate.txt
index 1a90b09a12d5abb1..12edc3337b86f099 100644
--- a/tools/perf/Documentation/perf-annotate.txt
+++ b/tools/perf/Documentation/perf-annotate.txt
@@ -58,6 +58,14 @@ OPTIONS
--ignore-vmlinux::
Ignore vmlinux files.
+--debuginfod::
+--no-debuginfod::
+ Fetch debuginfo keyed by build ID from the debuginfod servers
+ configured in DEBUGINFOD_URLS, checking the local debuginfod
+ client cache first, when it is not available locally, on for
+ these commands by default. See the --debuginfod option of
+ 'perf report' for how to turn it off.
+
--itrace::
Options for decoding instruction tracing data. The options are:
diff --git a/tools/perf/Documentation/perf-config.txt b/tools/perf/Documentation/perf-config.txt
index 9b223f8928299945..688306abe847a0df 100644
--- a/tools/perf/Documentation/perf-config.txt
+++ b/tools/perf/Documentation/perf-config.txt
@@ -216,6 +216,14 @@ core.*::
addr2line-timeout::
Sets a timeout (in milliseconds) for parsing 'addr2line'
output. The default timeout is 5s.
+ debuginfod::
+ When set to 'false', disable fetching debuginfo keyed by
+ build ID from the debuginfod servers configured in
+ DEBUGINFOD_URLS. It is on by default, can be overridden per
+ tool with the 'report.debuginfod' and 'top.debuginfod'
+ options and per invocation with --no-debuginfod; it is off
+ too when the local build-id cache is disabled, e.g.
+ 'buildid.dir' set to /dev/null.
tui.*, gtk.*::
Subcommands that can be configured here are 'top', 'report' and 'annotate'.
@@ -562,6 +570,14 @@ report.*::
This option can change default stat behavior with empty results.
If it's set true, 'perf report --stat' will not show 0 stats.
+ report.debuginfod::
+ Fetch debuginfo keyed by build ID from the debuginfod
+ servers configured in DEBUGINFOD_URLS, checking the local
+ debuginfod client cache first, when it is not available
+ locally. On by default, set to 'false' to disable it for
+ 'perf report', globally with 'core.debuginfod=false' or per
+ invocation with --no-debuginfod.
+
top.*::
top.children::
Same as 'report.children'. So if it is enabled, the output of 'top'
@@ -569,6 +585,9 @@ top.*::
column by default.
The default is 'true'.
+ top.debuginfod::
+ Same as 'report.debuginfod', for 'perf top'.
+
top.call-graph::
This is identical to 'call-graph.record-mode', except it is
applicable only for 'top' subcommand. This option ONLY setup
diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt
index 1a4706329c6cef1d..2e11c0a97a016853 100644
--- a/tools/perf/Documentation/perf-report.txt
+++ b/tools/perf/Documentation/perf-report.txt
@@ -366,6 +366,18 @@ OPTIONS
--ignore-vmlinux::
Ignore vmlinux files.
+--debuginfod::
+--no-debuginfod::
+ Fetch debuginfo keyed by build ID from the debuginfod servers
+ configured in DEBUGINFOD_URLS, checking the local debuginfod
+ client cache first, when it is not available locally, on for
+ these commands by default. It can be turned off per invocation
+ with --no-debuginfod, per tool with the "report.debuginfod"
+ config option or globally with "core.debuginfod" set to false.
+ It is disabled as well when the local build-id cache is turned
+ off, e.g. "buildid.dir" set to /dev/null, as that asks for
+ fetched files not to be kept on the box.
+
--kallsyms=<file>::
kallsyms pathname
diff --git a/tools/perf/Documentation/perf-top.txt b/tools/perf/Documentation/perf-top.txt
index 2da2a16bbf260685..345dd9ee9b6bab88 100644
--- a/tools/perf/Documentation/perf-top.txt
+++ b/tools/perf/Documentation/perf-top.txt
@@ -83,6 +83,17 @@ Default is to monitor all CPUS.
--ignore-vmlinux::
Ignore vmlinux files.
+--debuginfod::
+--no-debuginfod::
+ Fetch debuginfo keyed by build ID from the debuginfod servers
+ configured in DEBUGINFOD_URLS, checking the local debuginfod
+ client cache first, when it is not available locally, on for
+ these commands by default. Turn it off per invocation with
+ --no-debuginfod, with the "top.debuginfod" config option or
+ globally with "core.debuginfod" set to false. Disabled as well
+ when the build-id cache is off, e.g. "buildid.dir" set to
+ /dev/null.
+
--kallsyms=<file>::
kallsyms pathname
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c
index 850fc72fa75fe7df..d6a201c1866d363f 100644
--- a/tools/perf/builtin-annotate.c
+++ b/tools/perf/builtin-annotate.c
@@ -733,6 +733,9 @@ int cmd_annotate(int argc, const char **argv)
OPT_BOOLEAN(0, "stdio2", &annotate.use_stdio2, "Use the stdio interface"),
OPT_BOOLEAN(0, "ignore-vmlinux", &symbol_conf.ignore_vmlinux,
"don't load vmlinux even if found"),
+ OPT_BOOLEAN(0, "debuginfod", &symbol_conf.debuginfod,
+ "fetch debuginfo keyed by build ID from the debuginfod "
+ "servers, on by default, use --no-debuginfod to turn off"),
OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
"file", "vmlinux pathname"),
OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules,
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index 279e61c2366cb294..442c0822e614197f 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -134,6 +134,10 @@ static int report__config(const char *var, const char *value, void *cb)
symbol_conf.event_group = perf_config_bool(var, value);
return 0;
}
+ if (!strcmp(var, "report.debuginfod")) {
+ symbol_conf.debuginfod = perf_config_bool(var, value);
+ return 0;
+ }
if (!strcmp(var, "report.percent-limit")) {
double pcnt = strtof(value, NULL);
@@ -1346,6 +1350,9 @@ int cmd_report(int argc, const char **argv)
"file", "vmlinux pathname"),
OPT_BOOLEAN(0, "ignore-vmlinux", &symbol_conf.ignore_vmlinux,
"don't load vmlinux even if found"),
+ OPT_BOOLEAN(0, "debuginfod", &symbol_conf.debuginfod,
+ "fetch debuginfo keyed by build ID from the debuginfod "
+ "servers, on by default, use --no-debuginfod to turn off"),
OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name,
"file", "kallsyms pathname"),
OPT_BOOLEAN('f', "force", &symbol_conf.force, "don't complain, do it"),
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
index c2562d49be46a9a1..2be6859fd4aee141 100644
--- a/tools/perf/builtin-top.c
+++ b/tools/perf/builtin-top.c
@@ -1436,6 +1436,10 @@ static int perf_top_config(const char *var, const char *value, void *cb __maybe_
symbol_conf.cumulate_callchain = perf_config_bool(var, value);
return 0;
}
+ if (!strcmp(var, "top.debuginfod")) {
+ symbol_conf.debuginfod = perf_config_bool(var, value);
+ return 0;
+ }
return 0;
}
@@ -1508,6 +1512,9 @@ int cmd_top(int argc, const char **argv)
"file", "vmlinux pathname"),
OPT_BOOLEAN(0, "ignore-vmlinux", &symbol_conf.ignore_vmlinux,
"don't load vmlinux even if found"),
+ OPT_BOOLEAN(0, "debuginfod", &symbol_conf.debuginfod,
+ "fetch debuginfo keyed by build ID from the debuginfod "
+ "servers, on by default, use --no-debuginfod to turn off"),
OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name,
"file", "kallsyms pathname"),
OPT_BOOLEAN('K', "hide_kernel_symbols", &top.hide_kernel_symbols,
diff --git a/tools/perf/util/config.c b/tools/perf/util/config.c
index b2972c35c1eca68c..31c6618d3b3daf22 100644
--- a/tools/perf/util/config.c
+++ b/tools/perf/util/config.c
@@ -470,6 +470,9 @@ static int perf_default_core_config(const char *var, const char *value)
if (!strcmp(var, "core.addr2line-disable-warn"))
symbol_conf.addr2line_disable_warn = perf_config_bool(var, value);
+ if (!strcmp(var, "core.debuginfod"))
+ symbol_conf.debuginfod = perf_config_bool(var, value);
+
/* Add other config variables here. */
return 0;
}
diff --git a/tools/perf/util/debuginfo.c b/tools/perf/util/debuginfo.c
index 84a78b30ceac1066..be5882d9c2204797 100644
--- a/tools/perf/util/debuginfo.c
+++ b/tools/perf/util/debuginfo.c
@@ -7,16 +7,22 @@
#include <errno.h>
#include <fcntl.h>
+#include <limits.h>
+#include <pthread.h>
+#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#include <linux/list.h>
#include <linux/zalloc.h>
+#include <api/fs/fs.h>
#include "build-id.h"
#include "dso.h"
#include "debug.h"
#include "debuginfo.h"
+#include "mutex.h"
#include "symbol.h"
#ifdef HAVE_DEBUGINFOD_SUPPORT
@@ -139,6 +145,327 @@ struct debuginfo *debuginfo__new(const char *path)
return __debuginfo__new(buf);
}
+#ifdef HAVE_DEBUGINFOD_SUPPORT
+/*
+ * Users can disable the local build-id/.debug cache by setting
+ * buildid.dir to /dev/null, meaning they don't want fetched
+ * binaries/debuginfo stored on the box; the debuginfod client keeps
+ * its own cache in ~/.cache/debuginfod_client, so honour that intent
+ * and don't fetch at all in that case.
+ */
+static bool debuginfod__cache_disabled(void)
+{
+ return !strcmp(buildid_dir, "/dev/null");
+}
+
+/*
+ * Build IDs that shouldn't be searched for again in this session: the
+ * ones already searched for on the debuginfod servers without success,
+ * so that callers that see the same DSO over and over, such as the data
+ * type profiler switching between DSOs on every hist entry, don't pay a
+ * server round trip again for each miss. The cache of successes is the
+ * debuginfod client's own, in the local filesystem.
+ *
+ * Guarded by debuginfod__fetch_lock: it is only read by the lookups
+ * below, that run with that lock held, and written by fetches, that run
+ * with it held too.
+ */
+struct debuginfod_miss {
+ struct list_head node;
+ struct build_id bid;
+};
+
+static LIST_HEAD(debuginfod__misses);
+
+/*
+ * Was the search for this build ID already made and settled, i.e. the
+ * servers had nothing for it?
+ */
+static bool debuginfod__missed(const struct build_id *bid)
+{
+ struct debuginfod_miss *miss;
+
+ list_for_each_entry(miss, &debuginfod__misses, node) {
+ if (miss->bid.size == bid->size &&
+ !memcmp(miss->bid.data, bid->data, bid->size))
+ return true;
+ }
+
+ return false;
+}
+
+static void debuginfod__miss_add(const struct build_id *bid)
+{
+ struct debuginfod_miss *miss = zalloc(sizeof(*miss));
+
+ if (miss == NULL)
+ return;
+
+ miss->bid = *bid;
+
+ list_add(&miss->node, &debuginfod__misses);
+}
+
+/*
+ * One fetch at a time.
+ *
+ * The lookup state below is process global, so two concurrent fetches,
+ * which dso__debuginfo() makes possible by taking the fetch out of
+ * dso__lock, would race for it: the second one could answer from a list
+ * the first one is concurrently updating, and put the same build ID on
+ * the misses list twice.
+ *
+ * Serializing also means a second request for a build ID that is being
+ * fetched waits here for the fetch to finish, instead of starting a second
+ * download of the same file, and is then answered from the entry the fetch
+ * published, or from the misses list, with no client at all. Should the
+ * fetches ever run in parallel, that wait has to come back explicitly, with
+ * this lock split in two: one only for the lookup state, that the waiters
+ * sleep on, and one held around each fetch, with the thread that finds a
+ * fetch in progress waiting on the former for the entry to be published.
+ *
+ * What that costs is that a fetch for one build ID blocks a fetch for
+ * another one, and it is what parallel downloads would fix. Worth doing
+ * only if the wait turns out to be long, because it mostly is not: the
+ * lookups below answer the second and later requests for a build ID from
+ * memory, so after the first pass over the build IDs of a workload, which
+ * is the only time anything is fetched at all, the serialization has
+ * nothing left to serialize. Start there if a profile with many DSOs to
+ * fetch shows up in a profile of perf itself.
+ */
+static struct mutex debuginfod__fetch_lock;
+
+static void debuginfod__fetch_lock_setup(void)
+{
+ mutex_init(&debuginfod__fetch_lock);
+}
+
+static void debuginfod__fetch_lock_init(void)
+{
+ static pthread_once_t once = PTHREAD_ONCE_INIT;
+
+ pthread_once(&once, debuginfod__fetch_lock_setup);
+}
+
+/*
+ * The build IDs already fetched in this session, and the path of the file
+ * that came back for each, so that the repeated requests for the same build
+ * ID, dso__debuginfo() is called per symbol annotated, are answered with a
+ * strdup() instead of another client: the file is in the debuginfod client
+ * cache already and its path checked before being handed out, in case that
+ * cache is cleaned from under us.
+ *
+ * Guarded by debuginfod__fetch_lock. Only a fetch that brought a file back
+ * gets an entry: one that didn't is recorded in debuginfod__misses as a miss,
+ * and that is what keeps the rest of the session from asking for it again.
+ * Like debuginfod__misses this grows with the number of build IDs in the
+ * workload, one small entry each, and is not trimmed.
+ */
+struct debuginfo_lookup {
+ struct list_head node;
+ struct build_id bid;
+ char *path;
+};
+
+static LIST_HEAD(debuginfo_lookups);
+
+static bool build_id__equal(const struct build_id *a, const struct build_id *b)
+{
+ return a->size == b->size && memcmp(a->data, b->data, a->size) == 0;
+}
+
+static struct debuginfo_lookup *debuginfo_lookup__find(const struct build_id *bid)
+{
+ struct debuginfo_lookup *lookup;
+
+ list_for_each_entry(lookup, &debuginfo_lookups, node) {
+ if (build_id__equal(&lookup->bid, bid))
+ return lookup;
+ }
+
+ return NULL;
+}
+
+static void debuginfo_lookup__delete(struct debuginfo_lookup *lookup)
+{
+ list_del(&lookup->node);
+ zfree(&lookup->path);
+ free(lookup);
+}
+
+/*
+ * Remember that this build ID was fetched, with the file at @path, so that
+ * the next request for it is answered from memory. Called with
+ * debuginfod__fetch_lock held. Out of memory just means not sharing this
+ * one, the file is fetched and the caller has its path.
+ */
+static void debuginfo_lookup__add(const struct build_id *bid, const char *path)
+{
+ struct debuginfo_lookup *lookup = zalloc(sizeof(*lookup));
+
+ if (lookup == NULL)
+ return;
+
+ lookup->bid = *bid;
+ lookup->path = strdup(path);
+ if (lookup->path == NULL) {
+ free(lookup);
+ return;
+ }
+
+ list_add(&lookup->node, &debuginfo_lookups);
+}
+
+/*
+ * The fetch itself. Called with debuginfod__fetch_lock held for the
+ * whole of it, see the comment there.
+ */
+static int debuginfod__fetch(const struct build_id *bid, char **path)
+{
+ char sbuild_id[SBUILD_ID_SIZE];
+ debuginfod_client *c;
+ int fd;
+
+ c = debuginfod_begin();
+ if (c == NULL)
+ return -1;
+
+ fd = debuginfod_find_debuginfo(c, bid->data, bid->size, path);
+
+ debuginfod_end(c);
+
+ if (fd < 0) {
+ build_id__snprintf(bid, sbuild_id, sizeof(sbuild_id));
+ pr_debug("No debuginfo found for build ID %s in debuginfod\n",
+ sbuild_id);
+ debuginfod__miss_add(bid);
+ return -1;
+ }
+
+ close(fd);
+
+ return 0;
+}
+
+/*
+ * Look the build ID up in the files already fetched in this session,
+ * fetching it if it isn't there yet. Called, and left, with
+ * debuginfod__fetch_lock held, which also means that no fetch for this
+ * build ID can be running anywhere else: a second request for a build ID
+ * being fetched waits for the lock and is answered from the entry the fetch
+ * published, or from the misses list, with no client at all.
+ */
+static int debuginfo_lookup__find_build_id(const struct build_id *bid, char **path)
+{
+ struct debuginfo_lookup *lookup = debuginfo_lookup__find(bid);
+
+ if (lookup != NULL) {
+ /*
+ * The file stays in the debuginfod client cache, but that
+ * cache can be cleaned from under us, so check that it is
+ * still there before handing its path out. If it isn't,
+ * forget the entry and look for the file again below,
+ * remembering the new answer the same way the first fetch
+ * does, so that the next lookup shares it instead of
+ * fetching it a third time.
+ */
+ if (access(lookup->path, R_OK) == 0) {
+ *path = strdup(lookup->path);
+ return *path != NULL ? 0 : -1;
+ }
+
+ debuginfo_lookup__delete(lookup);
+ }
+
+ if (debuginfod__fetch(bid, path) < 0)
+ return -1;
+
+ debuginfo_lookup__add(bid, *path);
+
+ return 0;
+}
+
+/*
+ * Find a debuginfo file keyed by the build ID, using the debuginfod
+ * client, which checks its local cache first and then queries the
+ * servers in DEBUGINFOD_URLS. Used when the debuginfo is not available
+ * locally under the name the DSO was opened with, for instance the
+ * vmlinux for the kernel the profile was recorded on, when processing
+ * the profile on another machine or after the kernel or its debuginfo
+ * package got upgraded in between.
+ *
+ * Querying servers, possibly third party, sends the build IDs of the
+ * binaries being analysed off the box, so this is opt-out: on by
+ * default, switchable off with --no-debuginfod, with
+ * core.debuginfod=false, with the per-tool
+ * report.debuginfod/top.debuginfod, and it is off too when the user
+ * disabled the local build-id/.debug cache, e.g. with
+ * buildid.dir = /dev/null, as is the case for users that don't want
+ * any of this stored locally.
+ *
+ * On success the path is stored in *@path and must be freed by the
+ * caller, the file remains available in the debuginfod client cache.
+ */
+int debuginfo__find_build_id(const struct build_id *bid, char **path)
+{
+ int err = -1;
+
+ *path = NULL;
+
+ if (!build_id__is_defined(bid))
+ return -1;
+
+ /*
+ * The checks below have to be made with the lock held, as they look
+ * at the state the fetch changes: a build ID the fetch in progress
+ * just settled as a miss is settled for whoever is waiting for the
+ * lock as well. Deciding here and fetching there would repeat a
+ * fetch that was already made, and put the same build ID on the
+ * misses list twice.
+ */
+ debuginfod__fetch_lock_init();
+ mutex_lock(&debuginfod__fetch_lock);
+
+ if (symbol_conf.debuginfod) {
+ if (debuginfod__cache_disabled()) {
+ pr_debug("Build-id cache disabled (buildid dir is '%s'), not using debuginfod\n",
+ buildid_dir);
+ } else if (debuginfod__missed(bid)) {
+ char sbuild_id[SBUILD_ID_SIZE];
+
+ build_id__snprintf(bid, sbuild_id, sizeof(sbuild_id));
+ pr_debug("Not searching build ID %s in debuginfod again, it was a miss earlier\n",
+ sbuild_id);
+ } else {
+ err = debuginfo_lookup__find_build_id(bid, path);
+ }
+ }
+
+ mutex_unlock(&debuginfod__fetch_lock);
+
+ return err;
+}
+
+struct debuginfo *debuginfo__new_build_id(const struct build_id *bid)
+{
+ char sbuild_id[SBUILD_ID_SIZE];
+ char *path = NULL;
+ struct debuginfo *dbg;
+
+ if (debuginfo__find_build_id(bid, &path))
+ return NULL;
+
+ dbg = __debuginfo__new(path);
+ if (dbg == NULL) {
+ build_id__snprintf(bid, sbuild_id, sizeof(sbuild_id));
+ pr_debug("Failed to open DWARF in debuginfo fetched for build ID %s: %s\n",
+ sbuild_id, path);
+ }
+ free(path);
+ return dbg;
+}
+#endif /* HAVE_DEBUGINFOD_SUPPORT */
+
void debuginfo__delete(struct debuginfo *dbg)
{
if (dbg) {
diff --git a/tools/perf/util/debuginfo.h b/tools/perf/util/debuginfo.h
index a52d69932815cd72..ef44a7557a9d6192 100644
--- a/tools/perf/util/debuginfo.h
+++ b/tools/perf/util/debuginfo.h
@@ -5,6 +5,8 @@
#include <errno.h>
#include <linux/compiler.h>
+struct build_id;
+
#ifdef HAVE_LIBDW_SUPPORT
#include "dwarf-aux.h"
@@ -54,6 +56,27 @@ static inline int debuginfo__get_text_offset(struct debuginfo *dbg __maybe_unuse
#ifdef HAVE_DEBUGINFOD_SUPPORT
int get_source_from_debuginfod(const char *raw_path, const char *sbuild_id,
char **new_path);
+
+/*
+ * These need libdw to open the DWARF, so they live in debuginfo.o,
+ * built only with CONFIG_LIBDW.
+ */
+#ifdef HAVE_LIBDW_SUPPORT
+int debuginfo__find_build_id(const struct build_id *bid, char **path);
+struct debuginfo *debuginfo__new_build_id(const struct build_id *bid);
+#else
+static inline int debuginfo__find_build_id(const struct build_id *bid __maybe_unused,
+ char **path __maybe_unused)
+{
+ return -ENOTSUP;
+}
+
+static inline struct debuginfo *
+debuginfo__new_build_id(const struct build_id *bid __maybe_unused)
+{
+ return NULL;
+}
+#endif /* HAVE_LIBDW_SUPPORT */
#else /* HAVE_DEBUGINFOD_SUPPORT */
static inline int get_source_from_debuginfod(const char *raw_path __maybe_unused,
const char *sbuild_id __maybe_unused,
@@ -61,6 +84,18 @@ static inline int get_source_from_debuginfod(const char *raw_path __maybe_unused
{
return -ENOTSUP;
}
+
+static inline int debuginfo__find_build_id(const struct build_id *bid __maybe_unused,
+ char **path __maybe_unused)
+{
+ return -ENOTSUP;
+}
+
+static inline struct debuginfo *
+debuginfo__new_build_id(const struct build_id *bid __maybe_unused)
+{
+ return NULL;
+}
#endif /* HAVE_DEBUGINFOD_SUPPORT */
#endif /* _PERF_DEBUGINFO_H */
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c
index 42bfe30a3b518e80..b0079ef17c597011 100644
--- a/tools/perf/util/dso.c
+++ b/tools/perf/util/dso.c
@@ -32,6 +32,7 @@
#include "string2.h"
#include "vdso.h"
#include "annotate-data.h"
+#include "debuginfo.h"
#include "libdw.h"
static const char * const debuglink_paths[] = {
@@ -2073,5 +2074,14 @@ struct debuginfo *dso__debuginfo(struct dso *dso)
mutex_unlock(dso__lock(dso));
free(name);
+
+ /*
+ * The debuginfo for a DSO in the profile may not be installed locally,
+ * fall back to fetching it keyed by the build ID recorded in perf.data.
+ * Do it outside dso__lock, a fetch can take a while.
+ */
+ if (dinfo == NULL)
+ dinfo = debuginfo__new_build_id(dso__bid(dso));
+
return dinfo;
}
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 3587ad243159074f..b1a2684c813c5d8d 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -76,6 +76,8 @@ struct symbol_conf symbol_conf = {
.inline_name = true,
.res_sample = 0,
.addr2line_timeout_ms = 5 * 1000,
+ /* Fetching debuginfo by build ID, off via --no-debuginfod, etc */
+ .debuginfod = true,
};
struct map_list_node {
diff --git a/tools/perf/util/symbol_conf.h b/tools/perf/util/symbol_conf.h
index 71f60081a85bb18d..a56b1d2d843b9ff1 100644
--- a/tools/perf/util/symbol_conf.h
+++ b/tools/perf/util/symbol_conf.h
@@ -45,6 +45,7 @@ struct symbol_conf {
force,
ignore_vmlinux,
ignore_vmlinux_buildid,
+ debuginfod,
show_kernel_path,
use_modules,
allow_aliases,
--
2.55.0
next prev parent reply other threads:[~2026-09-17 15:55 UTC|newest]
Thread overview: 18+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-17 15:55 [PATCH v7 0/15] perf tools: Annotate fixes, stdio progress indication, debuginfo-client in more places Arnaldo Carvalho de Melo
2026-09-17 15:55 ` Arnaldo Carvalho de Melo [this message]
2026-09-17 17:58 ` [PATCH 01/15] perf debuginfo: Fetch debuginfo keyed by build ID using debuginfod Ian Rogers
2026-09-17 20:36 ` Arnaldo Carvalho de Melo
2026-09-17 15:55 ` [PATCH 02/15] perf debuginfo: Set DEBUGINFOD_URLS from /etc/debuginfod when unset Arnaldo Carvalho de Melo
2026-09-17 15:55 ` [PATCH 03/15] perf config: Move perf_config__set_variable() to util/config.c Arnaldo Carvalho de Melo
2026-09-17 15:55 ` [PATCH 04/15] perf debuginfo: Let the user skip and disable debuginfod fetches Arnaldo Carvalho de Melo
2026-09-17 15:55 ` [PATCH 05/15] perf debuginfo: Show the debuginfod fetch progress and keys in the TUI Arnaldo Carvalho de Melo
2026-09-17 15:55 ` [PATCH 06/15] perf symbol: Fall back to fetching the vmlinux by build ID Arnaldo Carvalho de Melo
2026-09-17 15:55 ` [PATCH 07/15] perf annotate-data: Show the sample count in the data-type browser Arnaldo Carvalho de Melo
2026-09-17 15:55 ` [PATCH 08/15] perf report: Add --progress option Arnaldo Carvalho de Melo
2026-09-17 15:55 ` [PATCH 09/15] perf scripts: Add perf-stuck, to tell where a running perf is stuck Arnaldo Carvalho de Melo
2026-09-17 15:55 ` [PATCH 10/15] perf dwarf-aux: Bound the type chases for broken debug info Arnaldo Carvalho de Melo
2026-09-17 15:55 ` [PATCH 11/15] perf dwarf-aux: Add die_same_file() and die_get_type_die() Arnaldo Carvalho de Melo
2026-09-17 15:55 ` [PATCH 12/15] perf annotate-data: Resolve type DIEs in the debug file they came from Arnaldo Carvalho de Melo
2026-09-17 15:55 ` [PATCH 13/15] perf annotate-data: Bound the member nesting recursion Arnaldo Carvalho de Melo
2026-09-17 15:55 ` [PATCH 14/15] perf mem record: Request PERF_SAMPLE_CPU by default Arnaldo Carvalho de Melo
2026-09-17 15:55 ` [PATCH 15/15] perf mem record: Use the IBS swfilt filter when available Arnaldo Carvalho de Melo
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260917155528.62607-2-acme@kernel.org \
--to=acme@kernel.org \
--cc=acme@redhat.com \
--cc=adrian.hunter@intel.com \
--cc=irogers@google.com \
--cc=james.clark@linaro.org \
--cc=jolsa@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-perf-users@vger.kernel.org \
--cc=mingo@kernel.org \
--cc=namhyung@kernel.org \
--cc=tglx@linutronix.de \
--cc=williams@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®