[PATCH v1 2/2] perf libdw: Optimize fallback logic for missing DWARF
From: Ian Rogers
Date: Mon Aug 24 2026 - 02:30:30 EST
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@xxxxxxxxxx>
---
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