The Linux Kernel Mailing List
 help / color / mirror / Atom feed
* [PATCH v1 0/2] perf libdw: Improve split-debug handling and error fallback
@ 2026-08-24  6:28 Ian Rogers
  2026-08-24  6:28 ` [PATCH v1 1/2] perf libdw: Pass dso_name explicitly to properly anchor split-debug builds Ian Rogers
  2026-08-24  6:28 ` [PATCH v1 2/2] perf libdw: Optimize fallback logic for missing DWARF Ian Rogers
  0 siblings, 2 replies; 3+ messages in thread
From: Ian Rogers @ 2026-08-24  6:28 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
	Namhyung Kim, Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
	Tanushree Shah, Michael Liang, Alessio Podda, Shimin Guo,
	linux-perf-users, linux-kernel

This patch series improves libdw's handling of split debuginfo files and its
fallback logic when DWARF data is unstructured or completely absent.

When perf's libdw backend failed to read unwinding or inlining states
due to un-mapped lines (returning 0), the addr2line_style routine fell
back to executing the system's `addr2line` potentially launching many
sub-processes.

Patch 1 Ensures split debug information is properly handled.
Patch 2 checks `dwfl_errmsg(-1)` for "no DWARF" presence to avoid
fallbacks.

Ian Rogers (2):
  perf libdw: Pass dso_name explicitly to properly anchor split-debug
    builds
  perf libdw: Optimize fallback logic for missing DWARF

 .../arch/powerpc/util/skip-callchain-idx.c    |  2 +-
 tools/perf/util/dso.h                         |  5 +++--
 tools/perf/util/libdw.c                       | 21 ++++++++++++-------
 tools/perf/util/libdw.h                       |  5 +++--
 tools/perf/util/srcline.c                     |  4 +++-
 tools/perf/util/unwind-libdw.c                | 15 ++++++++++---
 6 files changed, 36 insertions(+), 16 deletions(-)

-- 
2.55.0.766.g2966f0265a-goog

^ permalink raw reply	[flat|nested] 3+ messages in thread

* [PATCH v1 1/2] perf libdw: Pass dso_name explicitly to properly anchor split-debug builds
  2026-08-24  6:28 [PATCH v1 0/2] perf libdw: Improve split-debug handling and error fallback Ian Rogers
@ 2026-08-24  6:28 ` Ian Rogers
  2026-08-24  6:28 ` [PATCH v1 2/2] perf libdw: Optimize fallback logic for missing DWARF Ian Rogers
  1 sibling, 0 replies; 3+ messages in thread
From: Ian Rogers @ 2026-08-24  6:28 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
	Namhyung Kim, Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
	Tanushree Shah, Michael Liang, Alessio Podda, Shimin Guo,
	linux-perf-users, linux-kernel

When resolving source lines via get_srcline(), srcline_dso_name()
explicitly checks for and returns the split-debug file path
(dso__symsrc_filename, which is cached during dso__load()) rather than
the default executable path (dso__long_name). Because libdw__addr2line
defaulted to dso__long_name without accepting a passed dso_name, the
Dwfl_Module was hardcoded to initialize against the stripped
executable instead of the intended split debug file.

By explicitly passing the successfully resolved dso_name down the call
path from addr2line() to dso__libdw_dwfl(), libdw accurately anchors to
the dynamically discovered split-debug file, fixing dwarf resolution for
these binaries.

Assisted-by: Antigravity:gemini-3.6-flash
Signed-off-by: Ian Rogers <irogers@google.com>
---
 tools/perf/arch/powerpc/util/skip-callchain-idx.c |  2 +-
 tools/perf/util/dso.h                             |  5 +++--
 tools/perf/util/libdw.c                           | 10 +++++-----
 tools/perf/util/libdw.h                           |  5 +++--
 tools/perf/util/srcline.c                         |  2 +-
 5 files changed, 13 insertions(+), 11 deletions(-)

diff --git a/tools/perf/arch/powerpc/util/skip-callchain-idx.c b/tools/perf/arch/powerpc/util/skip-callchain-idx.c
index e57f10798fa6..f7dd27faaec2 100644
--- a/tools/perf/arch/powerpc/util/skip-callchain-idx.c
+++ b/tools/perf/arch/powerpc/util/skip-callchain-idx.c
@@ -152,7 +152,7 @@ static int check_return_addr(struct dso *dso, Dwarf_Addr mapped_pc)
 	Dwarf_Addr	end = mapped_pc;
 	bool		signalp;
 
-	dwfl = dso__libdw_dwfl(dso);
+	dwfl = dso__libdw_dwfl(dso, NULL);
 	if (!dwfl)
 		return -1;
 
diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h
index 55c4aaa53c38..3271e1ecaa9e 100644
--- a/tools/perf/util/dso.h
+++ b/tools/perf/util/dso.h
@@ -380,9 +380,10 @@ static inline void dso__set_libdw(struct dso *dso, void *val)
 
 struct Dwfl;
 #ifdef HAVE_LIBDW_SUPPORT
-struct Dwfl *dso__libdw_dwfl(struct dso *dso);
+struct Dwfl *dso__libdw_dwfl(struct dso *dso, const char *dso_name);
 #else
-static inline struct Dwfl *dso__libdw_dwfl(struct dso *dso __maybe_unused)
+static inline struct Dwfl *dso__libdw_dwfl(struct dso *dso __maybe_unused,
+					   const char *dso_name __maybe_unused)
 {
 	return NULL;
 }
diff --git a/tools/perf/util/libdw.c b/tools/perf/util/libdw.c
index 4ca7e7e4fbe9..f53caee980b0 100644
--- a/tools/perf/util/libdw.c
+++ b/tools/perf/util/libdw.c
@@ -25,17 +25,17 @@ void dso__free_libdw(struct dso *dso)
 	}
 }
 
-struct Dwfl *dso__libdw_dwfl(struct dso *dso)
+struct Dwfl *dso__libdw_dwfl(struct dso *dso, const char *dso_name)
 {
 	Dwfl *dwfl = dso__libdw(dso);
-	const char *dso_name;
 	Dwfl_Module *mod;
 	int fd;
 
 	if (dwfl)
 		return dwfl;
 
-	dso_name = dso__long_name(dso);
+	if (!dso_name)
+		dso_name = dso__long_name(dso);
 	/*
 	 * Initialize Dwfl session.
 	 * We need to open the DSO file to report it to libdw.
@@ -163,11 +163,11 @@ static int libdw_a2l_cb(Dwarf_Die *die, void *_args)
 	return DWARF_CB_ABORT;
 }
 
-int libdw__addr2line(u64 addr, char **file, unsigned int *line_nr,
+int libdw__addr2line(const char *dso_name, u64 addr, char **file, unsigned int *line_nr,
 		     struct dso *dso, bool unwind_inlines,
 		     struct inline_node *node, struct symbol *sym)
 {
-	Dwfl *dwfl = dso__libdw_dwfl(dso);
+	Dwfl *dwfl = dso__libdw_dwfl(dso, dso_name);
 	Dwfl_Module *mod;
 	Dwfl_Line *dwline;
 	Dwarf_Addr bias;
diff --git a/tools/perf/util/libdw.h b/tools/perf/util/libdw.h
index b12094737415..4bdd28d44e25 100644
--- a/tools/perf/util/libdw.h
+++ b/tools/perf/util/libdw.h
@@ -25,7 +25,7 @@ struct symbol;
  *
  * Returns 1 on success (found), 0 on failure (not found).
  */
-int libdw__addr2line(u64 addr, char **file,
+int libdw__addr2line(const char *dso_name, u64 addr, char **file,
 		     unsigned int *line_nr, struct dso *dso,
 		     bool unwind_inlines, struct inline_node *node,
 		     struct symbol *sym);
@@ -40,7 +40,8 @@ void dso__free_libdw(struct dso *dso);
 
 #else /* HAVE_LIBDW_SUPPORT */
 
-static inline int libdw__addr2line(u64 addr __maybe_unused, char **file __maybe_unused,
+static inline int libdw__addr2line(const char *dso_name, u64 addr __maybe_unused,
+				   char **file __maybe_unused,
 				   unsigned int *line_nr __maybe_unused,
 				   struct dso *dso __maybe_unused,
 				   bool unwind_inlines __maybe_unused,
diff --git a/tools/perf/util/srcline.c b/tools/perf/util/srcline.c
index b082178c279b..8e954f0c860a 100644
--- a/tools/perf/util/srcline.c
+++ b/tools/perf/util/srcline.c
@@ -155,7 +155,7 @@ static int addr2line(const char *dso_name, u64 addr, char **file, unsigned int *
 	for (size_t i = 0; i < ARRAY_SIZE(symbol_conf.addr2line_style); i++) {
 		switch (symbol_conf.addr2line_style[i]) {
 		case A2L_STYLE_LIBDW:
-			ret = libdw__addr2line(addr, file, line_nr, dso, unwind_inlines,
+			ret = libdw__addr2line(dso_name, addr, file, line_nr, dso, unwind_inlines,
 					       node, sym);
 			break;
 		case A2L_STYLE_LLVM:
-- 
2.55.0.766.g2966f0265a-goog


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* [PATCH v1 2/2] perf libdw: Optimize fallback logic for missing DWARF
  2026-08-24  6:28 [PATCH v1 0/2] perf libdw: Improve split-debug handling and error fallback Ian Rogers
  2026-08-24  6:28 ` [PATCH v1 1/2] perf libdw: Pass dso_name explicitly to properly anchor split-debug builds Ian Rogers
@ 2026-08-24  6:28 ` Ian Rogers
  1 sibling, 0 replies; 3+ messages in thread
From: Ian Rogers @ 2026-08-24  6:28 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
	Namhyung Kim, Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
	Tanushree Shah, Michael Liang, Alessio Podda, Shimin Guo,
	linux-perf-users, linux-kernel

When libdw encounters a DWARF definition it cannot accurately
un-inline or an address block natively missing structural line maps,
dwfl_module_getsrc() falls back immediately with 0. This causes perf's
addr2line_style loop to fall back to launching the cmd__addr2line()
sub-process wrapper. In environments generating massive numbers of
unmapped traces, this forces thousands of redundant `addr2line -e`
fork/execs in a polling loop.

This patch intercepts failure scenarios:
1. Returns -1 when dwfl_module_getsrc() returns NULL (meaning the DWARF
   is parsed but the line mapping is natively missing).
2. Evaluates `ret < 0` in addr2line() to dynamically bypass the subprocess
   fallback and return 0. This precisely acts as a failure condition,
   incrementing the dso->a2l_fails counter and accurately triggering the
   A2L_FAIL_LIMIT throttling without shelling out.
3. Incorporates exact error string checking for "no DWARF" to safely
   abort alternative fallbacks directly within both libdw resolving and
   the unwinder logic (unwind-libdw).

This preserves fallbacks if unwinder or DWARF parsing genuinely
aborts, but avoids falling back when mappings are absent.

Assisted-by: Antigravity:gemini-3.6-flash
Signed-off-by: Ian Rogers <irogers@google.com>
---
 tools/perf/util/libdw.c        | 11 +++++++++--
 tools/perf/util/srcline.c      |  2 ++
 tools/perf/util/unwind-libdw.c | 15 ++++++++++++---
 3 files changed, 23 insertions(+), 5 deletions(-)

diff --git a/tools/perf/util/libdw.c b/tools/perf/util/libdw.c
index f53caee980b0..7c681b03c654 100644
--- a/tools/perf/util/libdw.c
+++ b/tools/perf/util/libdw.c
@@ -186,8 +186,15 @@ int libdw__addr2line(const char *dso_name, u64 addr, char **file, unsigned int *
 	 * between the regular ELF addr2line addresses and those to use with
 	 * libdw.
 	 */
-	if (!dwfl_module_getdwarf(mod, &bias))
-		return 0;
+	if (!dwfl_module_getdwarf(mod, &bias)) {
+		const char *err = dwfl_errmsg(-1);
+
+		/*
+		 * Abort fallbacks specifically when DWARF is completely missing,
+		 * but allow alternative backends to try if parsing failed.
+		 */
+		return (err && strstr(err, "no DWARF")) ? -1 : 0;
+	}
 
 	/* Find source line information for the address. */
 	dwline = dwfl_module_getsrc(mod, addr + bias);
diff --git a/tools/perf/util/srcline.c b/tools/perf/util/srcline.c
index 8e954f0c860a..8283a741c018 100644
--- a/tools/perf/util/srcline.c
+++ b/tools/perf/util/srcline.c
@@ -176,6 +176,8 @@ static int addr2line(const char *dso_name, u64 addr, char **file, unsigned int *
 		}
 		if (ret > 0)
 			return ret;
+		if (ret < 0)
+			return 0;
 	}
 
 	return 0;
diff --git a/tools/perf/util/unwind-libdw.c b/tools/perf/util/unwind-libdw.c
index 63a5c2253174..69d4585c870c 100644
--- a/tools/perf/util/unwind-libdw.c
+++ b/tools/perf/util/unwind-libdw.c
@@ -441,8 +441,17 @@ int libdw__get_entries(unwind_entry_cb_t cb, void *arg,
 	}
 
  out:
-	if (err)
-		pr_debug("unwind: failed with '%s'\n", dwfl_errmsg(-1));
+	if (err) {
+		const char *msg = dwfl_errmsg(-1);
+
+		pr_debug("unwind: failed with '%s'\n", msg);
+		/*
+		 * Abort fallbacks specifically when unwinding information is completely
+		 * missing, but allow fallback unwinders to try if parsing simply failed.
+		 */
+		if (msg && strstr(msg, "no DWARF"))
+			err = -EINVAL;
+	}
 
 	for (i = 0; i < ui->idx; i++)
 		map_symbol__exit(&ui->entries[i].ms);
@@ -460,6 +469,6 @@ int libdw__get_entries(unwind_entry_cb_t cb, void *arg,
 	 *  < 0 : fatal error (e.g. -ENOMEM). Aborts unwinding entirely.
 	 */
 	if (err)
-		return (err == -ENOMEM) ? -ENOMEM : (entries > 0 ? 1 : 0);
+		return (err == -ENOMEM || err == -EINVAL) ? err : (entries > 0 ? 1 : 0);
 	return entries;
 }
-- 
2.55.0.766.g2966f0265a-goog


^ permalink raw reply related	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2026-08-24  6:28 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-24  6:28 [PATCH v1 0/2] perf libdw: Improve split-debug handling and error fallback Ian Rogers
2026-08-24  6:28 ` [PATCH v1 1/2] perf libdw: Pass dso_name explicitly to properly anchor split-debug builds Ian Rogers
2026-08-24  6:28 ` [PATCH v1 2/2] perf libdw: Optimize fallback logic for missing DWARF Ian Rogers

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox