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: 33+ 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 16:03 ` [PATCH 01/15] perf debuginfo: Fetch debuginfo keyed by build ID using debuginfod sashiko-bot
2026-09-17 17:58 ` 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 16:07 ` sashiko-bot
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 16:11 ` sashiko-bot
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 16:05 ` sashiko-bot
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 16:09 ` sashiko-bot
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 16:08 ` sashiko-bot
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 16:04 ` sashiko-bot
2026-09-17 15:55 ` [PATCH 08/15] perf report: Add --progress option Arnaldo Carvalho de Melo
2026-09-17 16:02 ` sashiko-bot
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 16:04 ` sashiko-bot
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 16:03 ` sashiko-bot
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 16:09 ` sashiko-bot
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 16:09 ` sashiko-bot
2026-09-17 15:55 ` [PATCH 13/15] perf annotate-data: Bound the member nesting recursion Arnaldo Carvalho de Melo
2026-09-17 16:08 ` sashiko-bot
2026-09-17 15:55 ` [PATCH 14/15] perf mem record: Request PERF_SAMPLE_CPU by default Arnaldo Carvalho de Melo
2026-09-17 16:15 ` sashiko-bot
2026-09-17 15:55 ` [PATCH 15/15] perf mem record: Use the IBS swfilt filter when available Arnaldo Carvalho de Melo
2026-09-17 16:09 ` sashiko-bot
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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.