Linux Perf Users
 help / color / mirror / Atom feed
From: Matthew Wood <thepacketgeek@gmail.com>
To: Peter Zijlstra <peterz@infradead.org>,
	Ingo Molnar <mingo@redhat.com>,
	Arnaldo Carvalho de Melo <acme@kernel.org>,
	Namhyung Kim <namhyung@kernel.org>,
	Mark Rutland <mark.rutland@arm.com>,
	Alexander Shishkin <alexander.shishkin@linux.intel.com>,
	Jiri Olsa <jolsa@kernel.org>, Ian Rogers <irogers@google.com>,
	Adrian Hunter <adrian.hunter@intel.com>,
	James Clark <james.clark@linaro.org>
Cc: linux-perf-users@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: [PATCH v2 0/3] perf unwind: Don't load symbols when the caller doesn't need them
Date: Fri, 16 Oct 2026 17:01:51 -0700	[thread overview]
Message-ID: <20261017000158.1178269-1-thepacketgeek@gmail.com> (raw)

When perf only needs to know which DSOs a callchain passes through, as
build-id marking does at the end of 'perf record' and in 'perf inject
-b', it resolves the callchain without symbols. The DWARF unwinders
ignore that and look up the symbol of every frame anyway, which loads
and demangles the whole symbol table of each DSO they visit.

On arm64 every frame pointer sample also gets a short DWARF
unwind to find the leaf function's caller; since v6.17 (commit
29be60c93d2d, "perf build-id: Mark DSO in sample callchains") the end of
'perf record -a --call-graph fp' loads the symbols of every user binary
it sampled. On a 128-CPU arm64 host,

  perf record -a -e cycles:u --call-graph fp -- sleep 2

never finishes writing perf.data, with over 1GB of heap growth in 5
seconds.

Just skipping the symbol lookup isn't enough, because loading the
symbols had two side effects that the unwinders depended on:

 - it set dso__symsrc_filename(), which is how both unwinders find a
   separate debuginfo file, for instance to use its .debug_frame;

 - it read a missing build-id from inside the DSO's mount namespace,
   which libdw needs to fall back to the build-id cache.

Patches 1 and 2 provide both without loading symbols, so the frames
that come back don't change:

  1: move dso__read_build_id() from 'perf inject' to dso.c
  2: add dso__find_debuginfo() and use it in both unwinders
  3: pass the symbols flag down to the unwinders and honour it, which
     also makes the arm64 leaf frame caller unwind skip symbols

Testing compared v7.3-rc7 with this series, on x86 and natively on a
CentOS Stream 10 arm64 host:

 - The 'perf test' dwarf unwind and 'perf inject' DWARF callchain
   conversion tests pass on both. The libdw multi-threaded unwind test
   passes on x86 and is skipped on the arm64 host.

 - 'perf inject -b' on an x86 'perf record --call-graph dwarf' profile
   of a Python workload goes from 0.24s and 58.5MB max RSS to 0.04s and
   15.5MB, with the same build-ids injected.

 - A test library built with -fno-asynchronous-unwind-tables, so that
   its CFI is only in .debug_frame, then split with 'objcopy
   --only-keep-debug', sits between two other DSOs in the sampled
   stacks. With libdw, on both architectures, and with the debuginfo
   found either through .gnu_debuglink or only in perf's build-id cache,
   'perf script' output for --call-graph dwarf profiles, and on arm64
   for fp profiles, is unchanged, and 'perf inject -b' still unwinds
   past the library and marks the same DSOs. An earlier version that
   only passed the flag down lost every frame past the library in all
   but 5 of 4045 samples in 'perf script' on x86, and unwound 10127
   instead of 33738 frames in 'perf inject -b'.

 - libunwind on arm64 returns no frames with or without this series:
   since v7.2, libunwind-arm64.c checks HAVE_LIBUNWIND_ARM64_SUPPORT,
   but Makefile.config defines HAVE_LIBUNWIND_AARCH64_SUPPORT. With that
   macro defined by hand, and a cast to make its .debug_frame code
   build, libunwind results for the test library are the same with and
   without this series. Neither gets past the library: the .debug_frame
   code looks broken, as the table that dwarf_find_debug_frame() returns
   is passed back to libunwind as a remote table. On x86, EL9's
   libunwind lacks .debug_frame support, so only the lookup could be
   exercised there: libunwind now finds the debuginfo from the first
   sample, instead of once the DSO's symbols have been loaded. These
   libunwind problems are independent of this series.

Not tested: the build-id cache fallback for DSOs in another mount
namespace when the MMAP2 events don't carry build-ids.

Changes since v1:
  - Complete re-work on approach based on feedback
  - Link: https://lore.kernel.org/linux-perf-users/20261014151326.4142140-1-thepacketgeek@gmail.com/

Matthew Wood (3):
  perf dso: Move dso__read_build_id() from perf inject to dso.c
  perf unwind: Find separate debuginfo without loading symbols
  perf unwind: Don't look up symbols when the caller doesn't want them

 tools/perf/builtin-inject.c                   | 25 --------
 tools/perf/tests/dwarf-unwind.c               |  3 +-
 .../util/arm64-frame-pointer-unwind-support.c |  7 ++-
 tools/perf/util/dso.c                         | 60 +++++++++++++++++++
 tools/perf/util/dso.h                         |  2 +
 tools/perf/util/machine.c                     |  7 +--
 tools/perf/util/unwind-libdw.c                | 43 ++++++++++---
 tools/perf/util/unwind-libdw.h                |  1 +
 tools/perf/util/unwind-libunwind.c            | 43 +++++++------
 tools/perf/util/unwind.c                      |  7 ++-
 tools/perf/util/unwind.h                      | 11 +++-
 11 files changed, 140 insertions(+), 69 deletions(-)


base-commit: 24a19b953e2a3d0ea5a8c4951a0ab8f32720c907
--
2.53.0-Meta

             reply	other threads:[~2026-10-17  0:02 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-17  0:01 Matthew Wood [this message]
2026-10-17  0:01 ` [PATCH v2 1/3] perf dso: Move dso__read_build_id() from perf inject to dso.c Matthew Wood
2026-10-17  0:05   ` sashiko-bot
2026-10-17  0:01 ` [PATCH v2 2/3] perf unwind: Find separate debuginfo without loading symbols Matthew Wood
2026-10-17  0:18   ` sashiko-bot
2026-10-17  0:01 ` [PATCH v2 3/3] perf unwind: Don't look up symbols when the caller doesn't want them Matthew Wood
2026-10-17  0:15   ` 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=20261017000158.1178269-1-thepacketgeek@gmail.com \
    --to=thepacketgeek@gmail.com \
    --cc=acme@kernel.org \
    --cc=adrian.hunter@intel.com \
    --cc=alexander.shishkin@linux.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=mark.rutland@arm.com \
    --cc=mingo@redhat.com \
    --cc=namhyung@kernel.org \
    --cc=peterz@infradead.org \
    /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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox