Re: [PATCH v2 1/4] perf libdw: Find the CU by iterating all CUs when aranges are missing

From: Namhyung Kim

Date: Sun Nov 08 2026 - 23:51:28 EST


Hello James,

On Fri, Nov 06, 2026 at 10:48:07AM +0000, James Clark wrote:
> libdw__addr2line() finds the CU with dwfl_module_getsrc() and
> dwfl_module_addrdie(), which use dwarf_getaranges(). In elfutils 0.190
> this only reads .debug_aranges, which can be partial. For example, when
> Perf is built with LLVM=1, clang doesn't emit .debug_aranges by default
> so the lookup then fails and no source lines are found for most of the
> Perf binary.
>
> This used to be hidden by falling back to the next addr2line style, but
> now missing information stops the fallback since commit (62d7cdc8d587
> "perf libdw: Optimize fallback logic for missing DWARF").
>
> find_cu_die() in annotate-data.c already handles partial aranges by
> iterating all CUs in .debug_info and checking them with dwarf_haspc().
> Move it to dwarf-aux.c, then look up the line in that CU with
> dwarf_getsrc_die() and use the same CU for unwinding inlines. While
> moving it, don't treat a dwarf_haspc() error as a match.
>
> This fixes the "test addr2line inline unwinding" test when Perf is built
> with LLVM:
>
> $ perf test -vvv "test addr2line inline unwinding"
> ...
> Inline unwinding fp verification test [Failed missing inlined functions]
> ...
> Inline unwinding dwarf verification test [Failed missing inlined functions]
> ...
> Inline unwinding lbr verification test [Failed missing inlined functions]
> ---- end(-1) ----
> 79: test addr2line inline unwinding : FAILED!
>
> Fixes: 88c51002d06f ("perf addr2line: Add a libdw implementation")
> Signed-off-by: James Clark <james.clark@xxxxxxxxxx>

Can you please rebase this one again? It seems to clash with the recent
C++ data type profiling support. I'm processing other patches as they
are independent fixes.

Thanks,
Namhyung


> ---
> tools/perf/util/annotate-data.c | 27 +--------------------------
> tools/perf/util/dwarf-aux.c | 37 +++++++++++++++++++++++++++++++++++++
> tools/perf/util/dwarf-aux.h | 3 +++
> tools/perf/util/libdw.c | 19 ++++++++++---------
> 4 files changed, 51 insertions(+), 35 deletions(-)
>
> diff --git a/tools/perf/util/annotate-data.c b/tools/perf/util/annotate-data.c
> index 25e3169ea8ad..8092ef9d2045 100644
> --- a/tools/perf/util/annotate-data.c
> +++ b/tools/perf/util/annotate-data.c
> @@ -523,31 +523,6 @@ static struct annotated_data_type *dso__findnew_data_type(struct dso *dso,
> return result;
> }
>
> -static bool find_cu_die(struct debuginfo *di, u64 pc, Dwarf_Die *cu_die)
> -{
> - Dwarf_Off off, next_off;
> - size_t header_size;
> -
> - if (dwarf_addrdie(di->dbg, pc, cu_die) != NULL)
> - return cu_die;
> -
> - /*
> - * There are some kernels don't have full aranges and contain only a few
> - * aranges entries. Fallback to iterate all CU entries in .debug_info
> - * in case it's missing.
> - */
> - off = 0;
> - while (dwarf_nextcu(di->dbg, off, &next_off, &header_size,
> - NULL, NULL, NULL) == 0) {
> - if (dwarf_offdie(di->dbg, off + header_size, cu_die) &&
> - dwarf_haspc(cu_die, pc))
> - return true;
> -
> - off = next_off;
> - }
> - return false;
> -}
> -
> enum type_match_result {
> PERF_TMR_UNKNOWN = 0,
> PERF_TMR_OK,
> @@ -1827,7 +1802,7 @@ static int find_data_type_die(struct data_loc_info *dloc, Dwarf_Die *type_die)
> pc = map__rip_2objdump(dloc->ms->map, dloc->ip);
>
> /* Get a compile_unit for this address */
> - if (!find_cu_die(dloc->di, pc, &cu_die)) {
> + if (!cu_find_by_addr(dloc->di->dbg, pc, &cu_die)) {
> pr_debug_dtp("cannot find CU for address %"PRIx64"\n", pc);
> ann_data_stat.no_cuinfo++;
> return -1;
> diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c
> index 54f8b5ec74a2..742fccba5376 100644
> --- a/tools/perf/util/dwarf-aux.c
> +++ b/tools/perf/util/dwarf-aux.c
> @@ -106,6 +106,43 @@ static Dwarf_Line *cu_getsrc_die(Dwarf_Die *cu_die, Dwarf_Addr addr)
> return line;
> }
>
> +/**
> + * cu_find_by_addr - Find the CU DIE that contains an address
> + * @dbg: a Dwarf handle
> + * @addr: An address
> + * @cu_die: a buffer to store the found CU DIE
> + *
> + * Find the CU DIE whose ranges contain @addr. dwarf_addrdie() uses
> + * dwarf_getaranges() which didn't work until elfutils 0.191. Release notes:
> + *
> + * * NEWS *
> + * libdw: dwarf_addrdie now supports binaries lacking a .debug_aranges
> + * section.
> + *
> + * Clang doesn't emit them by default, so fall back to iterating all CUs in
> + * case it's missing.
> + *
> + * Return @cu_die if found, otherwise NULL.
> + */
> +Dwarf_Die *cu_find_by_addr(Dwarf *dbg, Dwarf_Addr addr, Dwarf_Die *cu_die)
> +{
> + Dwarf_Off off = 0, next_off;
> + size_t header_size;
> +
> + if (dwarf_addrdie(dbg, addr, cu_die) && dwarf_haspc(cu_die, addr) == 1)
> + return cu_die;
> +
> + while (dwarf_nextcu(dbg, off, &next_off, &header_size,
> + NULL, NULL, NULL) == 0) {
> + if (dwarf_offdie(dbg, off + header_size, cu_die) &&
> + dwarf_haspc(cu_die, addr) == 1)
> + return cu_die;
> +
> + off = next_off;
> + }
> + return NULL;
> +}
> +
> /**
> * cu_find_lineinfo - Get a line number and file name for given address
> * @cu_die: a CU DIE
> diff --git a/tools/perf/util/dwarf-aux.h b/tools/perf/util/dwarf-aux.h
> index 6f9145510adc..cc3fd61374c4 100644
> --- a/tools/perf/util/dwarf-aux.h
> +++ b/tools/perf/util/dwarf-aux.h
> @@ -23,6 +23,9 @@ const char *cu_find_realpath(Dwarf_Die *cu_die, const char *fname);
> /* Get DW_AT_comp_dir (should be NULL with older gcc) */
> const char *cu_get_comp_dir(Dwarf_Die *cu_die);
>
> +/* Find the CU DIE that contains the given address */
> +Dwarf_Die *cu_find_by_addr(Dwarf *dbg, Dwarf_Addr addr, Dwarf_Die *cu_die);
> +
> /* Get a line number and file name for given address */
> int cu_find_lineinfo(Dwarf_Die *cudie, Dwarf_Addr addr,
> const char **fname, int *lineno);
> diff --git a/tools/perf/util/libdw.c b/tools/perf/util/libdw.c
> index e660623338d5..90767361f485 100644
> --- a/tools/perf/util/libdw.c
> +++ b/tools/perf/util/libdw.c
> @@ -170,7 +170,9 @@ int libdw__addr2line(u64 addr, char **file, unsigned int *line_nr,
> {
> Dwfl *dwfl;
> Dwfl_Module *mod;
> - Dwfl_Line *dwline;
> + Dwarf_Line *dwline;
> + Dwarf_Die cu_mem, *cudie;
> + Dwarf *dw;
> Dwarf_Addr bias;
> const char *src;
> int lineno = 0;
> @@ -197,7 +199,8 @@ int libdw__addr2line(u64 addr, char **file, unsigned int *line_nr,
> * between the regular ELF addr2line addresses and those to use with
> * libdw.
> */
> - if (!dwfl_module_getdwarf(mod, &bias)) {
> + dw = dwfl_module_getdwarf(mod, &bias);
> + if (!dw) {
> /*
> * Free the Dwfl session so the mmap'd ELF binary is unmapped
> * immediately, and mark the DSO as lacking srcline/DWARF info
> @@ -211,7 +214,8 @@ int libdw__addr2line(u64 addr, char **file, unsigned int *line_nr,
> }
>
> /* Find source line information for the address. */
> - dwline = dwfl_module_getsrc(mod, addr + bias);
> + cudie = cu_find_by_addr(dw, addr, &cu_mem);
> + dwline = cudie ? dwarf_getsrc_die(cudie, addr) : NULL;
> if (!dwline) {
> /* conclusively missing information -> abort fallbacks */
> ret = 0;
> @@ -219,8 +223,8 @@ int libdw__addr2line(u64 addr, char **file, unsigned int *line_nr,
> }
>
> /* Get line information. */
> - src = dwfl_lineinfo(dwline, /*addr=*/NULL, &lineno, /*col=*/NULL, /*mtime=*/NULL,
> - /*length=*/NULL);
> + src = dwarf_linesrc(dwline, /*mtime=*/NULL, /*length=*/NULL);
> + dwarf_lineno(dwline, &lineno);
>
> if (file)
> *file = src ? strdup(src) : NULL;
> @@ -229,8 +233,6 @@ int libdw__addr2line(u64 addr, char **file, unsigned int *line_nr,
>
> /* Optionally unwind inline function call chain. */
> if (unwind_inlines && node) {
> - Dwarf_Addr unused_bias;
> - Dwarf_Die *cudie = dwfl_module_addrdie(mod, addr + bias, &unused_bias);
> struct libdw_a2l_cb_args args = {
> .dso = dso,
> .sym = sym,
> @@ -248,8 +250,7 @@ int libdw__addr2line(u64 addr, char **file, unsigned int *line_nr,
> }
>
> /* Walk from the parent down to the leaf. */
> - if (cudie)
> - cu_walk_functions_at(cudie, addr, libdw_a2l_cb, &args);
> + cu_walk_functions_at(cudie, addr, libdw_a2l_cb, &args);
>
> if (!args.leaf_srcline_used)
> free(args.leaf_srcline);
>
> --
> 2.43.0
>