[PATCH v1 2/2] perf libdw: Optimize fallback logic for missing DWARF
Ian Rogers <[email protected]>
| Newsgroups | org.kernel.vger.linux-kernel,org.kernel.vger.linux-perf-users |
|---|---|
| Message-ID | <[email protected]> |
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 <[email protected]> --- 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