public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH v1 00/23] perf dwarf/libdw extra support, speed and clean ups
@ 2026-01-17  5:28 Ian Rogers
  2026-01-17  5:28 ` [PATCH v1 01/23] perf symbol-elf: Fix leak of ELF files with GNU debugdata Ian Rogers
                   ` (22 more replies)
  0 siblings, 23 replies; 32+ messages in thread
From: Ian Rogers @ 2026-01-17  5:28 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
	Namhyung Kim, Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
	John Garry, Will Deacon, Leo Yan, Guo Ren, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Shimin Guo,
	Athira Rajeev, Stephen Brennan, Howard Chu, Thomas Falcon,
	Andi Kleen, Dr. David Alan Gilbert, Dmitry Vyukov,
	Krzysztof Łopatowski, Chun-Tse Shao, Aditya Bodkhe, Haibo Xu,
	Sergei Trofimovich, linux-kernel, linux-perf-users,
	linux-arm-kernel, linux-csky, linux-riscv, Mark Wielaard

The first 6 patches are minor tweaks addressing a memory leak,
correctness and performance. Patch 5 incorportes the reviewed patch by
Shimin Guo [1] that makes the libdw unwinding support cross
architecture. The cross architecture files will be refactored in this
series.

Patch 7 unifies the dwfl for dsos and addr2line with that for the
powerpc skip-callchain-idx. An issue described in:
https://lore.kernel.org/lkml/CAP-5=fX+zw-CUEYEtrPA6vYUKqpp4LZp-p==QsbZaNwLcX0Whw@mail.gmail.com/

Patch 8 migrates the perf regs code away from using an architecture
substring to using the ELF machine, which is both more efficient and
more accurate.

Patches 9 to 21 clean up the libdw and dwarf register code removing
the need for different libdw dwarf register initialization routines by
introducing a perf reg to dwarf register mapping function. This
function is added for all perf register supporting architectures.

Patch 22 avoids discarding the dwfl for every libdw unwind, it can
speed up perf report by amounts like 18x.

Patch 23 adds inline information for frame pointers, an issue
described here:
https://lore.kernel.org/lkml/CAP-5=fWh5=AWq_Mav7B9nsuZ0TS2qPMMAwYhQKpt_BYFJRnf2w@mail.gmail.com/

[1] https://lore.kernel.org/lkml/20260107-cross-arch-unwind-v3-1-db0e37c8a652@skydio.com/

Ian Rogers (22):
  perf symbol-elf: Fix leak of ELF files with GNU debugdata
  perf dso: Extra validity checks that e_machine is valid
  perf record: Disable inline frames when marking build IDs
  perf libdw_addr2line: Fixes to srcline memory allocation
  perf unwind-libdw: Correct argument to dwfl_attach_state
  perf powerpc: Unify the skip-callchain-idx libdw with that for
    addr2line
  perf perf_regs: Switch from arch string to int e_machine
  perf dwarf-regs: Add util/dwarf-regs-arch for consistency with
    perf-regs
  perf dwarf-regs: Remove get_arch_regnum
  perf dwarf-regs: Clean up x86 dwarf_regnum code
  perf dwarf-regs: Add get_dwarf_regnum_for_perf_regnum and use for x86
    unwinding
  perf dwarf-regs: Add basic get_dwarf_regnum for most architectures
  perf dwarf-regs: Add ARM perf to dwarf register number mapping
    functions
  perf dwarf-regs: Add csky perf to dwarf register number mapping
    functions
  perf dwarf-regs: Add loongarch perf to dwarf register number mapping
    functions
  perf dwarf-regs: Add powerpc perf to dwarf register number mapping
    functions
  perf dwarf-regs: Add RISC-V perf to dwarf register number mapping
    functions
  perf dwarf-regs: Add S390 perf to dwarf register number mapping
    functions
  perf dwarf-regs: Add MIPS perf to dwarf register number mapping
    functions
  perf build: Remove NO_LIBDW_DWARF_UNWIND option
  perf unwind-libdw: Don't discard loaded ELF/Dwarf after every unwind
  perf machine: Add inline information to frame pointer and LBR
    callchains

Shimin Guo (1):
  perf unwind-libdw: fix a cross-arch unwinding bug

 tools/perf/Makefile.config                    |  19 +-
 tools/perf/arch/arm/util/Build                |   1 -
 tools/perf/arch/arm/util/unwind-libdw.c       |  39 ---
 tools/perf/arch/arm64/util/Build              |   1 -
 tools/perf/arch/arm64/util/unwind-libdw.c     |  61 ----
 tools/perf/arch/csky/util/Build               |   2 -
 tools/perf/arch/csky/util/unwind-libdw.c      |  78 ------
 tools/perf/arch/loongarch/util/unwind-libdw.c |  57 ----
 tools/perf/arch/powerpc/util/Build            |   1 -
 .../arch/powerpc/util/skip-callchain-idx.c    |  52 +---
 tools/perf/arch/powerpc/util/unwind-libdw.c   |  76 -----
 tools/perf/arch/riscv/util/Build              |   1 -
 tools/perf/arch/riscv/util/unwind-libdw.c     |  58 ----
 tools/perf/arch/s390/util/Build               |   2 -
 tools/perf/arch/s390/util/unwind-libdw.c      |  65 -----
 tools/perf/arch/x86/util/Build                |   1 -
 tools/perf/arch/x86/util/unwind-libdw.c       |  54 ----
 tools/perf/builtin-record.c                   |   3 +-
 tools/perf/builtin-script.c                   |  17 +-
 tools/perf/tests/make                         |   3 +-
 tools/perf/tests/shell/addr2line_inlines.sh   |  31 ++-
 tools/perf/util/Build                         |   6 +-
 tools/perf/util/dso.c                         |  33 ++-
 tools/perf/util/dso.h                         |  23 +-
 tools/perf/util/dwarf-regs-arch/Build         |   9 +
 .../util/dwarf-regs-arch/dwarf-regs-arm.c     |  12 +
 .../util/dwarf-regs-arch/dwarf-regs-arm64.c   |  12 +
 .../util/dwarf-regs-arch/dwarf-regs-csky.c    | 126 +++++++++
 .../dwarf-regs-arch/dwarf-regs-loongarch.c    |  12 +
 .../util/dwarf-regs-arch/dwarf-regs-mips.c    |  14 +
 .../util/dwarf-regs-arch/dwarf-regs-powerpc.c | 136 +++++++++
 .../util/dwarf-regs-arch/dwarf-regs-riscv.c   |  12 +
 .../util/dwarf-regs-arch/dwarf-regs-s390.c    |  53 ++++
 .../util/dwarf-regs-arch/dwarf-regs-x86.c     | 260 ++++++++++++++++++
 tools/perf/util/dwarf-regs-csky.c             |  50 ----
 tools/perf/util/dwarf-regs-powerpc.c          |  61 ----
 tools/perf/util/dwarf-regs-x86.c              |  50 ----
 tools/perf/util/dwarf-regs.c                  | 151 +++++++++-
 tools/perf/util/evsel.c                       |  14 +-
 tools/perf/util/include/dwarf-regs.h          |  28 +-
 tools/perf/util/libdw.c                       | 115 ++++----
 tools/perf/util/libdw.h                       |  12 +-
 tools/perf/util/machine.c                     | 104 ++++---
 tools/perf/util/maps.c                        |  36 ++-
 tools/perf/util/maps.h                        |   4 +
 tools/perf/util/perf_regs.c                   | 105 ++++---
 tools/perf/util/perf_regs.h                   |  10 +-
 .../scripting-engines/trace-event-python.c    |  21 +-
 tools/perf/util/session.c                     |  65 +++--
 tools/perf/util/session.h                     |   1 +
 tools/perf/util/srcline.c                     |   2 +-
 tools/perf/util/symbol-elf.c                  |   2 +-
 tools/perf/util/unwind-libdw.c                | 154 +++++++++--
 tools/perf/util/unwind-libdw.h                |  10 +-
 tools/perf/util/unwind-libunwind-local.c      |   7 +-
 55 files changed, 1347 insertions(+), 985 deletions(-)
 delete mode 100644 tools/perf/arch/arm/util/unwind-libdw.c
 delete mode 100644 tools/perf/arch/arm64/util/unwind-libdw.c
 delete mode 100644 tools/perf/arch/csky/util/unwind-libdw.c
 delete mode 100644 tools/perf/arch/loongarch/util/unwind-libdw.c
 delete mode 100644 tools/perf/arch/powerpc/util/unwind-libdw.c
 delete mode 100644 tools/perf/arch/riscv/util/unwind-libdw.c
 delete mode 100644 tools/perf/arch/s390/util/unwind-libdw.c
 delete mode 100644 tools/perf/arch/x86/util/unwind-libdw.c
 create mode 100644 tools/perf/util/dwarf-regs-arch/Build
 create mode 100644 tools/perf/util/dwarf-regs-arch/dwarf-regs-arm.c
 create mode 100644 tools/perf/util/dwarf-regs-arch/dwarf-regs-arm64.c
 create mode 100644 tools/perf/util/dwarf-regs-arch/dwarf-regs-csky.c
 create mode 100644 tools/perf/util/dwarf-regs-arch/dwarf-regs-loongarch.c
 create mode 100644 tools/perf/util/dwarf-regs-arch/dwarf-regs-mips.c
 create mode 100644 tools/perf/util/dwarf-regs-arch/dwarf-regs-powerpc.c
 create mode 100644 tools/perf/util/dwarf-regs-arch/dwarf-regs-riscv.c
 create mode 100644 tools/perf/util/dwarf-regs-arch/dwarf-regs-s390.c
 create mode 100644 tools/perf/util/dwarf-regs-arch/dwarf-regs-x86.c
 delete mode 100644 tools/perf/util/dwarf-regs-csky.c
 delete mode 100644 tools/perf/util/dwarf-regs-powerpc.c
 delete mode 100644 tools/perf/util/dwarf-regs-x86.c

-- 
2.52.0.457.g6b5491de43-goog


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

* [PATCH v1 01/23] perf symbol-elf: Fix leak of ELF files with GNU debugdata
  2026-01-17  5:28 [PATCH v1 00/23] perf dwarf/libdw extra support, speed and clean ups Ian Rogers
@ 2026-01-17  5:28 ` Ian Rogers
  2026-01-17  5:28 ` [PATCH v1 02/23] perf dso: Extra validity checks that e_machine is valid Ian Rogers
                   ` (21 subsequent siblings)
  22 siblings, 0 replies; 32+ messages in thread
From: Ian Rogers @ 2026-01-17  5:28 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
	Namhyung Kim, Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
	John Garry, Will Deacon, Leo Yan, Guo Ren, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Shimin Guo,
	Athira Rajeev, Stephen Brennan, Howard Chu, Thomas Falcon,
	Andi Kleen, Dr. David Alan Gilbert, Dmitry Vyukov,
	Krzysztof Łopatowski, Chun-Tse Shao, Aditya Bodkhe, Haibo Xu,
	Sergei Trofimovich, linux-kernel, linux-perf-users,
	linux-arm-kernel, linux-csky, linux-riscv, Mark Wielaard

The processing of DSO_BINARY_TYPE__GNU_DEBUGDATA in symsrc__init
happens with an open ELF file but the error path only closes the
associate fd. Fix the goto so that the ELF file is also ended and
memory released.

Fixes: b10f74308e13 ("perf symbol: Support .gnu_debugdata for symbols")
Signed-off-by: Ian Rogers <irogers@google.com>
---
 tools/perf/util/symbol-elf.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c
index b8fea12997a0..76912c62b6a0 100644
--- a/tools/perf/util/symbol-elf.c
+++ b/tools/perf/util/symbol-elf.c
@@ -1173,7 +1173,7 @@ int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name,
 		Elf *embedded = read_gnu_debugdata(dso, elf, name, &new_fd);
 
 		if (!embedded)
-			goto out_close;
+			goto out_elf_end;
 
 		elf_end(elf);
 		close(fd);
-- 
2.52.0.457.g6b5491de43-goog


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

* [PATCH v1 02/23] perf dso: Extra validity checks that e_machine is valid
  2026-01-17  5:28 [PATCH v1 00/23] perf dwarf/libdw extra support, speed and clean ups Ian Rogers
  2026-01-17  5:28 ` [PATCH v1 01/23] perf symbol-elf: Fix leak of ELF files with GNU debugdata Ian Rogers
@ 2026-01-17  5:28 ` Ian Rogers
  2026-01-17  5:28 ` [PATCH v1 03/23] perf record: Disable inline frames when marking build IDs Ian Rogers
                   ` (20 subsequent siblings)
  22 siblings, 0 replies; 32+ messages in thread
From: Ian Rogers @ 2026-01-17  5:28 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
	Namhyung Kim, Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
	John Garry, Will Deacon, Leo Yan, Guo Ren, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Shimin Guo,
	Athira Rajeev, Stephen Brennan, Howard Chu, Thomas Falcon,
	Andi Kleen, Dr. David Alan Gilbert, Dmitry Vyukov,
	Krzysztof Łopatowski, Chun-Tse Shao, Aditya Bodkhe, Haibo Xu,
	Sergei Trofimovich, linux-kernel, linux-perf-users,
	linux-arm-kernel, linux-csky, linux-riscv, Mark Wielaard

Better ensure a read e_machine is valid by checking the file appears
like an ELF file and the read e_machine value is less than
EM_NUM. This better avoids spurious e_machine values when looking for
an e_machine in say a thread.

Signed-off-by: Ian Rogers <irogers@google.com>
---
 tools/perf/util/dso.c | 31 +++++++++++++++++++++----------
 1 file changed, 21 insertions(+), 10 deletions(-)

diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c
index 18e656712f5a..143720d1ecb1 100644
--- a/tools/perf/util/dso.c
+++ b/tools/perf/util/dso.c
@@ -1236,17 +1236,28 @@ uint16_t dso__e_machine(struct dso *dso, struct machine *machine)
 	try_to_open_dso(dso, machine);
 	fd = dso__data(dso)->fd;
 	if (fd >= 0) {
-		_Static_assert(offsetof(Elf32_Ehdr, e_machine) == 18, "Unexpected offset");
-		_Static_assert(offsetof(Elf64_Ehdr, e_machine) == 18, "Unexpected offset");
-		if (dso__needs_swap(dso) == DSO_SWAP__UNSET) {
-			unsigned char eidata;
-
-			if (pread(fd, &eidata, sizeof(eidata), EI_DATA) == sizeof(eidata))
-				dso__swap_init(dso, eidata);
+		unsigned char e_ident[EI_NIDENT];
+
+		_Static_assert(offsetof(Elf32_Ehdr, e_ident) == 0, "Unexpected offset");
+		_Static_assert(offsetof(Elf64_Ehdr, e_ident) == 0, "Unexpected offset");
+		if (pread(fd, &e_ident, sizeof(e_ident), 0) == sizeof(e_ident) &&
+		    memcmp(e_ident, ELFMAG, SELFMAG) == 0 &&
+		    e_ident[EI_CLASS] > ELFCLASSNONE && e_ident[EI_CLASS] < ELFCLASSNUM &&
+		    e_ident[EI_DATA] > ELFDATANONE && e_ident[EI_DATA] < ELFDATANUM &&
+		    e_ident[EI_VERSION] == EV_CURRENT) {
+			_Static_assert(offsetof(Elf32_Ehdr, e_machine) == 18, "Unexpected offset");
+			_Static_assert(offsetof(Elf64_Ehdr, e_machine) == 18, "Unexpected offset");
+
+			if (dso__needs_swap(dso) == DSO_SWAP__UNSET)
+				dso__swap_init(dso, e_ident[EI_DATA]);
+
+			if (dso__needs_swap(dso) != DSO_SWAP__UNSET &&
+			    pread(fd, &e_machine, sizeof(e_machine), 18) == sizeof(e_machine) &&
+			    e_machine < EM_NUM)
+				e_machine = DSO__SWAP(dso, uint16_t, e_machine);
+			else
+				e_machine = EM_NONE;
 		}
-		if (dso__needs_swap(dso) != DSO_SWAP__UNSET &&
-		    pread(fd, &e_machine, sizeof(e_machine), 18) == sizeof(e_machine))
-			e_machine = DSO__SWAP(dso, uint16_t, e_machine);
 	}
 	mutex_unlock(dso__data_open_lock());
 	return e_machine;
-- 
2.52.0.457.g6b5491de43-goog


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

* [PATCH v1 03/23] perf record: Disable inline frames when marking build IDs
  2026-01-17  5:28 [PATCH v1 00/23] perf dwarf/libdw extra support, speed and clean ups Ian Rogers
  2026-01-17  5:28 ` [PATCH v1 01/23] perf symbol-elf: Fix leak of ELF files with GNU debugdata Ian Rogers
  2026-01-17  5:28 ` [PATCH v1 02/23] perf dso: Extra validity checks that e_machine is valid Ian Rogers
@ 2026-01-17  5:28 ` Ian Rogers
  2026-01-17  5:28 ` [PATCH v1 04/23] perf unwind-libdw: fix a cross-arch unwinding bug Ian Rogers
                   ` (19 subsequent siblings)
  22 siblings, 0 replies; 32+ messages in thread
From: Ian Rogers @ 2026-01-17  5:28 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
	Namhyung Kim, Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
	John Garry, Will Deacon, Leo Yan, Guo Ren, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Shimin Guo,
	Athira Rajeev, Stephen Brennan, Howard Chu, Thomas Falcon,
	Andi Kleen, Dr. David Alan Gilbert, Dmitry Vyukov,
	Krzysztof Łopatowski, Chun-Tse Shao, Aditya Bodkhe, Haibo Xu,
	Sergei Trofimovich, linux-kernel, linux-perf-users,
	linux-arm-kernel, linux-csky, linux-riscv, Mark Wielaard

Marking DSOs doesn't need inline frames traversing as the inline
frames are all part of the same DSO. Disable to improve performance
and also to avoid potential issues with dwarf information.

Signed-off-by: Ian Rogers <irogers@google.com>
---
 tools/perf/builtin-record.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index 003e47a4fc1d..663ca3a03396 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -1509,6 +1509,8 @@ static int process_buildids(struct record *rec)
 	if (perf_data__size(&rec->data) == 0)
 		return 0;
 
+	/* A single DSO is needed and not all inline frames. */
+	symbol_conf.inline_name = false;
 	/*
 	 * During this process, it'll load kernel map and replace the
 	 * dso->long_name to a real pathname it found.  In this case
@@ -1519,7 +1521,6 @@ static int process_buildids(struct record *rec)
 	 *   $HOME/.debug/.build-id/f0/6e17aa50adf4d00b88925e03775de107611551
 	 */
 	symbol_conf.ignore_vmlinux_buildid = true;
-
 	/*
 	 * If --buildid-all is given, it marks all DSO regardless of hits,
 	 * so no need to process samples. But if timestamp_boundary is enabled,
-- 
2.52.0.457.g6b5491de43-goog


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

* [PATCH v1 04/23] perf unwind-libdw: fix a cross-arch unwinding bug
  2026-01-17  5:28 [PATCH v1 00/23] perf dwarf/libdw extra support, speed and clean ups Ian Rogers
                   ` (2 preceding siblings ...)
  2026-01-17  5:28 ` [PATCH v1 03/23] perf record: Disable inline frames when marking build IDs Ian Rogers
@ 2026-01-17  5:28 ` Ian Rogers
  2026-01-20 16:02   ` Arnaldo Carvalho de Melo
  2026-01-17  5:28 ` [PATCH v1 05/23] perf libdw_addr2line: Fixes to srcline memory allocation Ian Rogers
                   ` (18 subsequent siblings)
  22 siblings, 1 reply; 32+ messages in thread
From: Ian Rogers @ 2026-01-17  5:28 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
	Namhyung Kim, Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
	John Garry, Will Deacon, Leo Yan, Guo Ren, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Shimin Guo,
	Athira Rajeev, Stephen Brennan, Howard Chu, Thomas Falcon,
	Andi Kleen, Dr. David Alan Gilbert, Dmitry Vyukov,
	Krzysztof Łopatowski, Chun-Tse Shao, Aditya Bodkhe, Haibo Xu,
	Sergei Trofimovich, linux-kernel, linux-perf-users,
	linux-arm-kernel, linux-csky, linux-riscv, Mark Wielaard

From: Shimin Guo <shimin.guo@skydio.com>

The set_initial_registers field of Dwfl_Thread_Callbacks needs to be set
according to the arch of the stack samples being analyzed, not the arch
that perf itself is built for. Currently perf fails to unwind stack samples
collected from archs different from that of the host perf is running on.
This patch moves the arch-specific implementations of set_initial_registers
from tools/perf/arch to tools/perf/utli/unwind-libdw-arch, similar to the
way the perf-regs-arch folder contains arch-specific functions related to
registers, and chooses the implementation based on the arch of the data
being processed.

Signed-off-by: Shimin Guo <shimin.guo@skydio.com>
Reviewed-by: Ian Rogers <irogers@google.com>
Acked-by: Namhyung Kim <namhyung@kernel.org>
---
 tools/perf/arch/arm/util/Build                |  1 -
 tools/perf/arch/arm64/util/Build              |  1 -
 tools/perf/arch/csky/util/Build               |  2 -
 tools/perf/arch/powerpc/util/Build            |  1 -
 tools/perf/arch/riscv/util/Build              |  1 -
 tools/perf/arch/s390/util/Build               |  2 -
 tools/perf/arch/x86/util/Build                |  1 -
 tools/perf/util/Build                         |  1 +
 tools/perf/util/unwind-libdw-arch/Build       |  8 +++
 .../unwind-libdw-arch/unwind-libdw-arm.c}     | 10 ++--
 .../unwind-libdw-arch/unwind-libdw-arm64.c}   | 10 ++--
 .../unwind-libdw-arch/unwind-libdw-csky.c}    | 10 ++--
 .../unwind-libdw-loongarch.c}                 | 10 ++--
 .../unwind-libdw-arch/unwind-libdw-powerpc.c} | 10 ++--
 .../unwind-libdw-arch/unwind-libdw-riscv.c}   | 10 ++--
 .../unwind-libdw-arch/unwind-libdw-s390.c}    | 14 ++---
 .../unwind-libdw-arch/unwind-libdw-x86.c}     |  8 +--
 tools/perf/util/unwind-libdw.c                | 51 ++++++++++++++++---
 tools/perf/util/unwind-libdw.h                | 10 +++-
 19 files changed, 104 insertions(+), 57 deletions(-)
 create mode 100644 tools/perf/util/unwind-libdw-arch/Build
 rename tools/perf/{arch/arm/util/unwind-libdw.c => util/unwind-libdw-arch/unwind-libdw-arm.c} (80%)
 rename tools/perf/{arch/arm64/util/unwind-libdw.c => util/unwind-libdw-arch/unwind-libdw-arm64.c} (87%)
 rename tools/perf/{arch/csky/util/unwind-libdw.c => util/unwind-libdw-arch/unwind-libdw-csky.c} (90%)
 rename tools/perf/{arch/loongarch/util/unwind-libdw.c => util/unwind-libdw-arch/unwind-libdw-loongarch.c} (86%)
 rename tools/perf/{arch/powerpc/util/unwind-libdw.c => util/unwind-libdw-arch/unwind-libdw-powerpc.c} (89%)
 rename tools/perf/{arch/riscv/util/unwind-libdw.c => util/unwind-libdw-arch/unwind-libdw-riscv.c} (87%)
 rename tools/perf/{arch/s390/util/unwind-libdw.c => util/unwind-libdw-arch/unwind-libdw-s390.c} (84%)
 rename tools/perf/{arch/x86/util/unwind-libdw.c => util/unwind-libdw-arch/unwind-libdw-x86.c} (87%)

diff --git a/tools/perf/arch/arm/util/Build b/tools/perf/arch/arm/util/Build
index fd695e1fdaee..3291f893b943 100644
--- a/tools/perf/arch/arm/util/Build
+++ b/tools/perf/arch/arm/util/Build
@@ -1,6 +1,5 @@
 perf-util-y += perf_regs.o
 
 perf-util-$(CONFIG_LOCAL_LIBUNWIND)    += unwind-libunwind.o
-perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
 
 perf-util-y += pmu.o auxtrace.o cs-etm.o
diff --git a/tools/perf/arch/arm64/util/Build b/tools/perf/arch/arm64/util/Build
index d63881081d2e..0177af19cc00 100644
--- a/tools/perf/arch/arm64/util/Build
+++ b/tools/perf/arch/arm64/util/Build
@@ -1,4 +1,3 @@
-perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
 perf-util-$(CONFIG_LIBTRACEEVENT) += kvm-stat.o
 perf-util-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o
 perf-util-y += ../../arm/util/auxtrace.o
diff --git a/tools/perf/arch/csky/util/Build b/tools/perf/arch/csky/util/Build
index 5e6ea82c4202..6b2d0e021b11 100644
--- a/tools/perf/arch/csky/util/Build
+++ b/tools/perf/arch/csky/util/Build
@@ -1,3 +1 @@
 perf-util-y += perf_regs.o
-
-perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
diff --git a/tools/perf/arch/powerpc/util/Build b/tools/perf/arch/powerpc/util/Build
index 3d0d5427aef7..5fd28ec713a4 100644
--- a/tools/perf/arch/powerpc/util/Build
+++ b/tools/perf/arch/powerpc/util/Build
@@ -9,5 +9,4 @@ perf-util-y += evsel.o
 perf-util-$(CONFIG_LIBDW) += skip-callchain-idx.o
 
 perf-util-$(CONFIG_LIBUNWIND) += unwind-libunwind.o
-perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
 perf-util-y += auxtrace.o
diff --git a/tools/perf/arch/riscv/util/Build b/tools/perf/arch/riscv/util/Build
index 58a672246024..628b9ebd418b 100644
--- a/tools/perf/arch/riscv/util/Build
+++ b/tools/perf/arch/riscv/util/Build
@@ -2,4 +2,3 @@ perf-util-y += perf_regs.o
 perf-util-y += header.o
 
 perf-util-$(CONFIG_LIBTRACEEVENT) += kvm-stat.o
-perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
diff --git a/tools/perf/arch/s390/util/Build b/tools/perf/arch/s390/util/Build
index c64eb18dbdae..5391d26fedd4 100644
--- a/tools/perf/arch/s390/util/Build
+++ b/tools/perf/arch/s390/util/Build
@@ -2,8 +2,6 @@ perf-util-y += header.o
 perf-util-$(CONFIG_LIBTRACEEVENT) += kvm-stat.o
 perf-util-y += perf_regs.o
 
-perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
-
 perf-util-y += machine.o
 perf-util-y += pmu.o
 
diff --git a/tools/perf/arch/x86/util/Build b/tools/perf/arch/x86/util/Build
index c0dc5965f362..fad256252bb9 100644
--- a/tools/perf/arch/x86/util/Build
+++ b/tools/perf/arch/x86/util/Build
@@ -12,7 +12,6 @@ perf-util-y += evsel.o
 perf-util-y += iostat.o
 
 perf-util-$(CONFIG_LOCAL_LIBUNWIND)    += unwind-libunwind.o
-perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
 
 perf-util-y += auxtrace.o
 perf-util-y += archinsn.o
diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index 4915f237ba9e..5efec73be474 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -227,6 +227,7 @@ perf-util-$(CONFIG_LIBDW) += annotate-data.o
 perf-util-$(CONFIG_LIBDW) += libdw.o
 
 perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
+perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw-arch/
 perf-util-$(CONFIG_LOCAL_LIBUNWIND)    += unwind-libunwind-local.o
 perf-util-$(CONFIG_LIBUNWIND)          += unwind-libunwind.o
 perf-util-$(CONFIG_LIBUNWIND_X86)      += libunwind/x86_32.o
diff --git a/tools/perf/util/unwind-libdw-arch/Build b/tools/perf/util/unwind-libdw-arch/Build
new file mode 100644
index 000000000000..ef17a83a7813
--- /dev/null
+++ b/tools/perf/util/unwind-libdw-arch/Build
@@ -0,0 +1,8 @@
+perf-util-y += unwind-libdw-x86.o
+perf-util-y += unwind-libdw-arm.o
+perf-util-y += unwind-libdw-arm64.o
+perf-util-y += unwind-libdw-csky.o
+perf-util-y += unwind-libdw-loongarch.o
+perf-util-y += unwind-libdw-powerpc.o
+perf-util-y += unwind-libdw-riscv.o
+perf-util-y += unwind-libdw-s390.o
diff --git a/tools/perf/arch/arm/util/unwind-libdw.c b/tools/perf/util/unwind-libdw-arch/unwind-libdw-arm.c
similarity index 80%
rename from tools/perf/arch/arm/util/unwind-libdw.c
rename to tools/perf/util/unwind-libdw-arch/unwind-libdw-arm.c
index fbb643f224ec..56e9b5975bcc 100644
--- a/tools/perf/arch/arm/util/unwind-libdw.c
+++ b/tools/perf/util/unwind-libdw-arch/unwind-libdw-arm.c
@@ -1,11 +1,11 @@
 // SPDX-License-Identifier: GPL-2.0
 #include <elfutils/libdwfl.h>
-#include "perf_regs.h"
-#include "../../../util/unwind-libdw.h"
-#include "../../../util/perf_regs.h"
-#include "../../../util/sample.h"
+#include "../arch/arm/include/uapi/asm/perf_regs.h"
+#include "util/unwind-libdw.h"
+#include "util/perf_regs.h"
+#include "util/sample.h"
 
-bool libdw__arch_set_initial_registers(Dwfl_Thread *thread, void *arg)
+bool libdw_set_initial_registers_arm(Dwfl_Thread *thread, void *arg)
 {
 	struct unwind_info *ui = arg;
 	struct regs_dump *user_regs = perf_sample__user_regs(ui->sample);
diff --git a/tools/perf/arch/arm64/util/unwind-libdw.c b/tools/perf/util/unwind-libdw-arch/unwind-libdw-arm64.c
similarity index 87%
rename from tools/perf/arch/arm64/util/unwind-libdw.c
rename to tools/perf/util/unwind-libdw-arch/unwind-libdw-arm64.c
index b89b0a7e5ad9..29b6833e036c 100644
--- a/tools/perf/arch/arm64/util/unwind-libdw.c
+++ b/tools/perf/util/unwind-libdw-arch/unwind-libdw-arm64.c
@@ -1,11 +1,11 @@
 // SPDX-License-Identifier: GPL-2.0
 #include <elfutils/libdwfl.h>
-#include "perf_regs.h"
-#include "../../../util/unwind-libdw.h"
-#include "../../../util/perf_regs.h"
-#include "../../../util/sample.h"
+#include "../arch/arm64/include/uapi/asm/perf_regs.h"
+#include "util/unwind-libdw.h"
+#include "util/perf_regs.h"
+#include "util/sample.h"
 
-bool libdw__arch_set_initial_registers(Dwfl_Thread *thread, void *arg)
+bool libdw_set_initial_registers_arm64(Dwfl_Thread *thread, void *arg)
 {
 	struct unwind_info *ui = arg;
 	struct regs_dump *user_regs = perf_sample__user_regs(ui->sample);
diff --git a/tools/perf/arch/csky/util/unwind-libdw.c b/tools/perf/util/unwind-libdw-arch/unwind-libdw-csky.c
similarity index 90%
rename from tools/perf/arch/csky/util/unwind-libdw.c
rename to tools/perf/util/unwind-libdw-arch/unwind-libdw-csky.c
index b20b1569783d..2556d034c32a 100644
--- a/tools/perf/arch/csky/util/unwind-libdw.c
+++ b/tools/perf/util/unwind-libdw-arch/unwind-libdw-csky.c
@@ -2,12 +2,12 @@
 // Copyright (C) 2019 Hangzhou C-SKY Microsystems co.,ltd.
 
 #include <elfutils/libdwfl.h>
-#include "perf_regs.h"
-#include "../../util/unwind-libdw.h"
-#include "../../util/perf_regs.h"
-#include "../../util/event.h"
+#include "../arch/csky/include/uapi/asm/perf_regs.h"
+#include "util/unwind-libdw.h"
+#include "util/perf_regs.h"
+#include "util/sample.h"
 
-bool libdw__arch_set_initial_registers(Dwfl_Thread *thread, void *arg)
+bool libdw_set_initial_registers_csky(Dwfl_Thread *thread, void *arg)
 {
 	struct unwind_info *ui = arg;
 	struct regs_dump *user_regs = perf_sample__user_regs(ui->sample);
diff --git a/tools/perf/arch/loongarch/util/unwind-libdw.c b/tools/perf/util/unwind-libdw-arch/unwind-libdw-loongarch.c
similarity index 86%
rename from tools/perf/arch/loongarch/util/unwind-libdw.c
rename to tools/perf/util/unwind-libdw-arch/unwind-libdw-loongarch.c
index 60b1144bedd5..5fca673508be 100644
--- a/tools/perf/arch/loongarch/util/unwind-libdw.c
+++ b/tools/perf/util/unwind-libdw-arch/unwind-libdw-loongarch.c
@@ -2,12 +2,12 @@
 /* Copyright (C) 2020-2023 Loongson Technology Corporation Limited */
 
 #include <elfutils/libdwfl.h>
-#include "perf_regs.h"
-#include "../../util/unwind-libdw.h"
-#include "../../util/perf_regs.h"
-#include "../../util/sample.h"
+#include "../arch/loongarch/include/uapi/asm/perf_regs.h"
+#include "util/unwind-libdw.h"
+#include "util/perf_regs.h"
+#include "util/sample.h"
 
-bool libdw__arch_set_initial_registers(Dwfl_Thread *thread, void *arg)
+bool libdw_set_initial_registers_loongarch(Dwfl_Thread *thread, void *arg)
 {
 	struct unwind_info *ui = arg;
 	struct regs_dump *user_regs = perf_sample__user_regs(ui->sample);
diff --git a/tools/perf/arch/powerpc/util/unwind-libdw.c b/tools/perf/util/unwind-libdw-arch/unwind-libdw-powerpc.c
similarity index 89%
rename from tools/perf/arch/powerpc/util/unwind-libdw.c
rename to tools/perf/util/unwind-libdw-arch/unwind-libdw-powerpc.c
index 82d0c28ae345..1560db45e7b4 100644
--- a/tools/perf/arch/powerpc/util/unwind-libdw.c
+++ b/tools/perf/util/unwind-libdw-arch/unwind-libdw-powerpc.c
@@ -1,10 +1,10 @@
 // SPDX-License-Identifier: GPL-2.0
 #include <elfutils/libdwfl.h>
 #include <linux/kernel.h>
-#include "perf_regs.h"
-#include "../../../util/unwind-libdw.h"
-#include "../../../util/perf_regs.h"
-#include "../../../util/sample.h"
+#include "../arch/powerpc/include/uapi/asm/perf_regs.h"
+#include "util/unwind-libdw.h"
+#include "util/perf_regs.h"
+#include "util/sample.h"
 
 /* See backends/ppc_initreg.c and backends/ppc_regs.c in elfutils.  */
 static const int special_regs[3][2] = {
@@ -13,7 +13,7 @@ static const int special_regs[3][2] = {
 	{ 109, PERF_REG_POWERPC_CTR },
 };
 
-bool libdw__arch_set_initial_registers(Dwfl_Thread *thread, void *arg)
+bool libdw_set_initial_registers_powerpc(Dwfl_Thread *thread, void *arg)
 {
 	struct unwind_info *ui = arg;
 	struct regs_dump *user_regs = perf_sample__user_regs(ui->sample);
diff --git a/tools/perf/arch/riscv/util/unwind-libdw.c b/tools/perf/util/unwind-libdw-arch/unwind-libdw-riscv.c
similarity index 87%
rename from tools/perf/arch/riscv/util/unwind-libdw.c
rename to tools/perf/util/unwind-libdw-arch/unwind-libdw-riscv.c
index dc1476e16321..c2e2c4b6b2e0 100644
--- a/tools/perf/arch/riscv/util/unwind-libdw.c
+++ b/tools/perf/util/unwind-libdw-arch/unwind-libdw-riscv.c
@@ -2,12 +2,12 @@
 /* Copyright (C) 2019 Hangzhou C-SKY Microsystems co.,ltd. */
 
 #include <elfutils/libdwfl.h>
-#include "perf_regs.h"
-#include "../../util/unwind-libdw.h"
-#include "../../util/perf_regs.h"
-#include "../../util/sample.h"
+#include "../arch/riscv/include/uapi/asm/perf_regs.h"
+#include "util/unwind-libdw.h"
+#include "util/perf_regs.h"
+#include "util/sample.h"
 
-bool libdw__arch_set_initial_registers(Dwfl_Thread *thread, void *arg)
+bool libdw_set_initial_registers_riscv(Dwfl_Thread *thread, void *arg)
 {
 	struct unwind_info *ui = arg;
 	struct regs_dump *user_regs = perf_sample__user_regs(ui->sample);
diff --git a/tools/perf/arch/s390/util/unwind-libdw.c b/tools/perf/util/unwind-libdw-arch/unwind-libdw-s390.c
similarity index 84%
rename from tools/perf/arch/s390/util/unwind-libdw.c
rename to tools/perf/util/unwind-libdw-arch/unwind-libdw-s390.c
index c27c7a0d1076..1e05e9d9d95f 100644
--- a/tools/perf/arch/s390/util/unwind-libdw.c
+++ b/tools/perf/util/unwind-libdw-arch/unwind-libdw-s390.c
@@ -1,14 +1,14 @@
 #include <linux/kernel.h>
 #include <elfutils/libdwfl.h>
-#include "../../util/unwind-libdw.h"
-#include "../../util/perf_regs.h"
-#include "../../util/event.h"
-#include "../../util/sample.h"
-#include "dwarf-regs-table.h"
-#include "perf_regs.h"
+#include "util/unwind-libdw.h"
+#include "util/perf_regs.h"
+#include "util/event.h"
+#include "util/sample.h"
+#include "../arch/s390/include/dwarf-regs-table.h"
+#include "../arch/s390/include/uapi/asm/perf_regs.h"
 
 
-bool libdw__arch_set_initial_registers(Dwfl_Thread *thread, void *arg)
+bool libdw_set_initial_registers_s390(Dwfl_Thread *thread, void *arg)
 {
 	struct unwind_info *ui = arg;
 	struct regs_dump *user_regs = perf_sample__user_regs(ui->sample);
diff --git a/tools/perf/arch/x86/util/unwind-libdw.c b/tools/perf/util/unwind-libdw-arch/unwind-libdw-x86.c
similarity index 87%
rename from tools/perf/arch/x86/util/unwind-libdw.c
rename to tools/perf/util/unwind-libdw-arch/unwind-libdw-x86.c
index 798493e887d7..dd27545a4a68 100644
--- a/tools/perf/arch/x86/util/unwind-libdw.c
+++ b/tools/perf/util/unwind-libdw-arch/unwind-libdw-x86.c
@@ -1,11 +1,11 @@
 // SPDX-License-Identifier: GPL-2.0
 #include <elfutils/libdwfl.h>
-#include "perf_regs.h"
-#include "../../../util/unwind-libdw.h"
-#include "../../../util/perf_regs.h"
+#include "../arch/x86/include/uapi/asm/perf_regs.h"
+#include "util/unwind-libdw.h"
+#include "util/perf_regs.h"
 #include "util/sample.h"
 
-bool libdw__arch_set_initial_registers(Dwfl_Thread *thread, void *arg)
+bool libdw_set_initial_registers_x86(Dwfl_Thread *thread, void *arg)
 {
 	struct unwind_info *ui = arg;
 	struct regs_dump *user_regs = perf_sample__user_regs(ui->sample);
diff --git a/tools/perf/util/unwind-libdw.c b/tools/perf/util/unwind-libdw.c
index 3ff427a49e4c..b2e194a8be39 100644
--- a/tools/perf/util/unwind-libdw.c
+++ b/tools/perf/util/unwind-libdw.c
@@ -225,11 +225,45 @@ static bool memory_read(Dwfl *dwfl __maybe_unused, Dwarf_Addr addr, Dwarf_Word *
 	return true;
 }
 
-static const Dwfl_Thread_Callbacks callbacks = {
-	.next_thread		= next_thread,
-	.memory_read		= memory_read,
-	.set_initial_registers	= libdw__arch_set_initial_registers,
-};
+#define DEFINE_DWFL_THREAD_CALLBACKS(arch)                           \
+static const Dwfl_Thread_Callbacks callbacks_##arch = {              \
+	.next_thread           = next_thread,                        \
+	.memory_read           = memory_read,                        \
+	.set_initial_registers = libdw_set_initial_registers_##arch, \
+}
+
+DEFINE_DWFL_THREAD_CALLBACKS(x86);
+DEFINE_DWFL_THREAD_CALLBACKS(arm);
+DEFINE_DWFL_THREAD_CALLBACKS(arm64);
+DEFINE_DWFL_THREAD_CALLBACKS(csky);
+DEFINE_DWFL_THREAD_CALLBACKS(loongarch);
+DEFINE_DWFL_THREAD_CALLBACKS(powerpc);
+DEFINE_DWFL_THREAD_CALLBACKS(riscv);
+DEFINE_DWFL_THREAD_CALLBACKS(s390);
+
+static const Dwfl_Thread_Callbacks *get_thread_callbacks(const char *arch)
+{
+	if (!strcmp(arch, "arm"))
+		return &callbacks_arm;
+	else if (!strcmp(arch, "arm64"))
+		return &callbacks_arm64;
+	else if (!strcmp(arch, "csky"))
+		return &callbacks_csky;
+	else if (!strcmp(arch, "loongarch"))
+		return &callbacks_loongarch;
+	else if (!strcmp(arch, "powerpc"))
+		return &callbacks_powerpc;
+	else if (!strcmp(arch, "riscv"))
+		return &callbacks_riscv;
+	else if (!strcmp(arch, "s390"))
+		return &callbacks_s390;
+	else if (!strcmp(arch, "x86"))
+		return &callbacks_x86;
+
+	pr_err("Fail to get thread callbacks for arch %s, returns NULL\n",
+	       arch);
+	return NULL;
+}
 
 static int
 frame_callback(Dwfl_Frame *state, void *arg)
@@ -278,6 +312,7 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
 	const char *arch = perf_env__arch(ui_buf.machine->env);
 	Dwarf_Word ip;
 	int err = -EINVAL, i;
+	const Dwfl_Thread_Callbacks *callbacks;
 
 	if (!data->user_regs || !data->user_regs->regs)
 		return -EINVAL;
@@ -300,7 +335,11 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
 	if (err)
 		goto out;
 
-	err = !dwfl_attach_state(ui->dwfl, EM_NONE, thread__tid(thread), &callbacks, ui);
+	callbacks = get_thread_callbacks(arch);
+	if (!callbacks)
+		goto out;
+
+	err = !dwfl_attach_state(ui->dwfl, EM_NONE, thread__tid(thread), callbacks, ui);
 	if (err)
 		goto out;
 
diff --git a/tools/perf/util/unwind-libdw.h b/tools/perf/util/unwind-libdw.h
index 8c88bc4f2304..574b29848cce 100644
--- a/tools/perf/util/unwind-libdw.h
+++ b/tools/perf/util/unwind-libdw.h
@@ -9,7 +9,15 @@ struct machine;
 struct perf_sample;
 struct thread;
 
-bool libdw__arch_set_initial_registers(Dwfl_Thread *thread, void *arg);
+bool libdw_set_initial_registers_x86(Dwfl_Thread *thread, void *arg);
+bool libdw_set_initial_registers_arm(Dwfl_Thread *thread, void *arg);
+bool libdw_set_initial_registers_arm64(Dwfl_Thread *thread, void *arg);
+bool libdw_set_initial_registers_csky(Dwfl_Thread *thread, void *arg);
+bool libdw_set_initial_registers_loongarch(Dwfl_Thread *thread, void *arg);
+bool libdw_set_initial_registers_mips(Dwfl_Thread *thread, void *arg);
+bool libdw_set_initial_registers_powerpc(Dwfl_Thread *thread, void *arg);
+bool libdw_set_initial_registers_riscv(Dwfl_Thread *thread, void *arg);
+bool libdw_set_initial_registers_s390(Dwfl_Thread *thread, void *arg);
 
 struct unwind_info {
 	Dwfl			*dwfl;
-- 
2.52.0.457.g6b5491de43-goog


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

* [PATCH v1 05/23] perf libdw_addr2line: Fixes to srcline memory allocation
  2026-01-17  5:28 [PATCH v1 00/23] perf dwarf/libdw extra support, speed and clean ups Ian Rogers
                   ` (3 preceding siblings ...)
  2026-01-17  5:28 ` [PATCH v1 04/23] perf unwind-libdw: fix a cross-arch unwinding bug Ian Rogers
@ 2026-01-17  5:28 ` Ian Rogers
  2026-01-17  5:28 ` [PATCH v1 06/23] perf unwind-libdw: Correct argument to dwfl_attach_state Ian Rogers
                   ` (17 subsequent siblings)
  22 siblings, 0 replies; 32+ messages in thread
From: Ian Rogers @ 2026-01-17  5:28 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
	Namhyung Kim, Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
	John Garry, Will Deacon, Leo Yan, Guo Ren, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Shimin Guo,
	Athira Rajeev, Stephen Brennan, Howard Chu, Thomas Falcon,
	Andi Kleen, Dr. David Alan Gilbert, Dmitry Vyukov,
	Krzysztof Łopatowski, Chun-Tse Shao, Aditya Bodkhe, Haibo Xu,
	Sergei Trofimovich, linux-kernel, linux-perf-users,
	linux-arm-kernel, linux-csky, linux-riscv, Mark Wielaard

Some irregular stack traces are causing double frees and memory
leaks. Make the code robust by proactively freeing and being more
careful with the memory management of the leaf_srcline.

Fixes: 88c51002d06f ("perf addr2line: Add a libdw implementation")
Signed-off-by: Ian Rogers <irogers@google.com>
---
 tools/perf/util/libdw.c | 14 +++++++++++---
 1 file changed, 11 insertions(+), 3 deletions(-)

diff --git a/tools/perf/util/libdw.c b/tools/perf/util/libdw.c
index e4bfd52bd172..b96c4e0d728f 100644
--- a/tools/perf/util/libdw.c
+++ b/tools/perf/util/libdw.c
@@ -42,16 +42,24 @@ static int libdw_a2l_cb(Dwarf_Die *die, void *_args)
 		call_srcline = srcline_from_fileline(call_fname, die_get_call_lineno(die));
 
 	list_for_each_entry(ilist, &args->node->val, list) {
+		if (args->leaf_srcline == ilist->srcline)
+			args->leaf_srcline_used = false;
+		else if (ilist->srcline != srcline__unknown)
+			free(ilist->srcline);
 		ilist->srcline =  call_srcline;
 		call_srcline = NULL;
 		break;
 	}
-	if (call_srcline && call_fname)
+	if (call_srcline && call_srcline != srcline__unknown)
 		free(call_srcline);
 
 	/* Add this symbol to the chain as the leaf. */
-	inline_list__append_tail(inline_sym, args->leaf_srcline, args->node);
-	args->leaf_srcline_used = true;
+	if (!args->leaf_srcline_used) {
+		inline_list__append_tail(inline_sym, args->leaf_srcline, args->node);
+		args->leaf_srcline_used = true;
+	} else {
+		inline_list__append_tail(inline_sym, strdup(args->leaf_srcline), args->node);
+	}
 	return 0;
 }
 
-- 
2.52.0.457.g6b5491de43-goog


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

* [PATCH v1 06/23] perf unwind-libdw: Correct argument to dwfl_attach_state
  2026-01-17  5:28 [PATCH v1 00/23] perf dwarf/libdw extra support, speed and clean ups Ian Rogers
                   ` (4 preceding siblings ...)
  2026-01-17  5:28 ` [PATCH v1 05/23] perf libdw_addr2line: Fixes to srcline memory allocation Ian Rogers
@ 2026-01-17  5:28 ` Ian Rogers
  2026-01-17  5:28 ` [PATCH v1 07/23] perf powerpc: Unify the skip-callchain-idx libdw with that for addr2line Ian Rogers
                   ` (16 subsequent siblings)
  22 siblings, 0 replies; 32+ messages in thread
From: Ian Rogers @ 2026-01-17  5:28 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
	Namhyung Kim, Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
	John Garry, Will Deacon, Leo Yan, Guo Ren, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Shimin Guo,
	Athira Rajeev, Stephen Brennan, Howard Chu, Thomas Falcon,
	Andi Kleen, Dr. David Alan Gilbert, Dmitry Vyukov,
	Krzysztof Łopatowski, Chun-Tse Shao, Aditya Bodkhe, Haibo Xu,
	Sergei Trofimovich, linux-kernel, linux-perf-users,
	linux-arm-kernel, linux-csky, linux-riscv, Mark Wielaard

Argument is a pointer but EM_NONE (0) was being passed. Correct by
passing NULL.

Signed-off-by: Ian Rogers <irogers@google.com>
---
 tools/perf/util/unwind-libdw.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tools/perf/util/unwind-libdw.c b/tools/perf/util/unwind-libdw.c
index b2e194a8be39..dc882f17f52d 100644
--- a/tools/perf/util/unwind-libdw.c
+++ b/tools/perf/util/unwind-libdw.c
@@ -339,7 +339,7 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
 	if (!callbacks)
 		goto out;
 
-	err = !dwfl_attach_state(ui->dwfl, EM_NONE, thread__tid(thread), callbacks, ui);
+	err = !dwfl_attach_state(ui->dwfl, /*elf=*/NULL, thread__tid(thread), callbacks, ui);
 	if (err)
 		goto out;
 
-- 
2.52.0.457.g6b5491de43-goog


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

* [PATCH v1 07/23] perf powerpc: Unify the skip-callchain-idx libdw with that for addr2line
  2026-01-17  5:28 [PATCH v1 00/23] perf dwarf/libdw extra support, speed and clean ups Ian Rogers
                   ` (5 preceding siblings ...)
  2026-01-17  5:28 ` [PATCH v1 06/23] perf unwind-libdw: Correct argument to dwfl_attach_state Ian Rogers
@ 2026-01-17  5:28 ` Ian Rogers
  2026-01-17  5:28 ` [PATCH v1 08/23] perf perf_regs: Switch from arch string to int e_machine Ian Rogers
                   ` (15 subsequent siblings)
  22 siblings, 0 replies; 32+ messages in thread
From: Ian Rogers @ 2026-01-17  5:28 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
	Namhyung Kim, Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
	John Garry, Will Deacon, Leo Yan, Guo Ren, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Shimin Guo,
	Athira Rajeev, Stephen Brennan, Howard Chu, Thomas Falcon,
	Andi Kleen, Dr. David Alan Gilbert, Dmitry Vyukov,
	Krzysztof Łopatowski, Chun-Tse Shao, Aditya Bodkhe, Haibo Xu,
	Sergei Trofimovich, linux-kernel, linux-perf-users,
	linux-arm-kernel, linux-csky, linux-riscv, Mark Wielaard

Rather than have 2 Dwfl unify the Dwfl in skip-callchain-idx with that
is used by libdw__addr2line. Rename that variable in struct dso from
a2l_libdw to just libdw as it is now used in more than addr2line.

The Dwfl in skip-callchain-idx uses a map address when being read with
dwfl_report_elf (rather than dwfl_report_offline that addr2line
uses). skip-callchain-idx is wrong as the map address can vary between
processes because of ASLR, ie it should need a different Dwfl per
process. In the code after this patch the base address becomes 0 and
the mapped PC is used with the dwfl functions. This should increase
the accuracy of skip-callchain-idx, but the impact has only been build
tested.

Signed-off-by: Ian Rogers <irogers@google.com>
---
 .../arch/powerpc/util/skip-callchain-idx.c    |  52 ++-------
 tools/perf/util/dso.c                         |   2 +-
 tools/perf/util/dso.h                         |  23 ++--
 tools/perf/util/libdw.c                       | 101 ++++++++++--------
 tools/perf/util/libdw.h                       |  12 +--
 tools/perf/util/srcline.c                     |   2 +-
 6 files changed, 89 insertions(+), 103 deletions(-)

diff --git a/tools/perf/arch/powerpc/util/skip-callchain-idx.c b/tools/perf/arch/powerpc/util/skip-callchain-idx.c
index 356786432fd3..e57f10798fa6 100644
--- a/tools/perf/arch/powerpc/util/skip-callchain-idx.c
+++ b/tools/perf/arch/powerpc/util/skip-callchain-idx.c
@@ -30,14 +30,6 @@
  * The libdwfl code in this file is based on code from elfutils
  * (libdwfl/argp-std.c, libdwfl/tests/addrcfi.c, etc).
  */
-static char *debuginfo_path;
-
-static const Dwfl_Callbacks offline_callbacks = {
-	.debuginfo_path = &debuginfo_path,
-	.find_debuginfo = dwfl_standard_find_debuginfo,
-	.section_address = dwfl_offline_section_address,
-};
-
 
 /*
  * Use the DWARF expression for the Call-frame-address and determine
@@ -149,44 +141,22 @@ static Dwarf_Frame *get_dwarf_frame(Dwfl_Module *mod, Dwarf_Addr pc)
  *		yet used)
  *	-1 in case of errors
  */
-static int check_return_addr(struct dso *dso, u64 map_start, Dwarf_Addr pc)
+static int check_return_addr(struct dso *dso, Dwarf_Addr mapped_pc)
 {
 	int		rc = -1;
 	Dwfl		*dwfl;
 	Dwfl_Module	*mod;
 	Dwarf_Frame	*frame;
 	int		ra_regno;
-	Dwarf_Addr	start = pc;
-	Dwarf_Addr	end = pc;
+	Dwarf_Addr	start = mapped_pc;
+	Dwarf_Addr	end = mapped_pc;
 	bool		signalp;
-	const char	*exec_file = dso__long_name(dso);
-
-	dwfl = RC_CHK_ACCESS(dso)->dwfl;
-
-	if (!dwfl) {
-		dwfl = dwfl_begin(&offline_callbacks);
-		if (!dwfl) {
-			pr_debug("dwfl_begin() failed: %s\n", dwarf_errmsg(-1));
-			return -1;
-		}
-
-		mod = dwfl_report_elf(dwfl, exec_file, exec_file, -1,
-						map_start, false);
-		if (!mod) {
-			pr_debug("dwfl_report_elf() failed %s\n",
-						dwarf_errmsg(-1));
-			/*
-			 * We normally cache the DWARF debug info and never
-			 * call dwfl_end(). But to prevent fd leak, free in
-			 * case of error.
-			 */
-			dwfl_end(dwfl);
-			goto out;
-		}
-		RC_CHK_ACCESS(dso)->dwfl = dwfl;
-	}
 
-	mod = dwfl_addrmodule(dwfl, pc);
+	dwfl = dso__libdw_dwfl(dso);
+	if (!dwfl)
+		return -1;
+
+	mod = dwfl_addrmodule(dwfl, mapped_pc);
 	if (!mod) {
 		pr_debug("dwfl_addrmodule() failed, %s\n", dwarf_errmsg(-1));
 		goto out;
@@ -196,9 +166,9 @@ static int check_return_addr(struct dso *dso, u64 map_start, Dwarf_Addr pc)
 	 * To work with split debug info files (eg: glibc), check both
 	 * .eh_frame and .debug_frame sections of the ELF header.
 	 */
-	frame = get_eh_frame(mod, pc);
+	frame = get_eh_frame(mod, mapped_pc);
 	if (!frame) {
-		frame = get_dwarf_frame(mod, pc);
+		frame = get_dwarf_frame(mod, mapped_pc);
 		if (!frame)
 			goto out;
 	}
@@ -264,7 +234,7 @@ int arch_skip_callchain_idx(struct thread *thread, struct ip_callchain *chain)
 		return skip_slot;
 	}
 
-	rc = check_return_addr(dso, map__start(al.map), ip);
+	rc = check_return_addr(dso, map__map_ip(al.map, ip));
 
 	pr_debug("[DSO %s, sym %s, ip 0x%" PRIx64 "] rc %d\n",
 		dso__long_name(dso), al.sym->name, ip, rc);
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c
index 143720d1ecb1..dce207c7f862 100644
--- a/tools/perf/util/dso.c
+++ b/tools/perf/util/dso.c
@@ -1612,7 +1612,7 @@ void dso__delete(struct dso *dso)
 	auxtrace_cache__free(RC_CHK_ACCESS(dso)->auxtrace_cache);
 	dso_cache__free(dso);
 	dso__free_a2l(dso);
-	dso__free_a2l_libdw(dso);
+	dso__free_libdw(dso);
 	dso__free_symsrc_filename(dso);
 	nsinfo__zput(RC_CHK_ACCESS(dso)->nsinfo);
 	mutex_destroy(dso__lock(dso));
diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h
index 4aee23775054..295388085031 100644
--- a/tools/perf/util/dso.h
+++ b/tools/perf/util/dso.h
@@ -268,11 +268,8 @@ DECLARE_RC_STRUCT(dso) {
 	const char	 *short_name;
 	const char	 *long_name;
 	void		 *a2l;
-	void		 *a2l_libdw;
+	void		 *libdw;
 	char		 *symsrc_filename;
-#if defined(__powerpc__)
-	void		*dwfl;			/* DWARF debug info */
-#endif
 	struct nsinfo	*nsinfo;
 	struct auxtrace_cache *auxtrace_cache;
 	union { /* Tool specific area */
@@ -335,16 +332,26 @@ static inline void dso__set_a2l(struct dso *dso, void *val)
 	RC_CHK_ACCESS(dso)->a2l = val;
 }
 
-static inline void *dso__a2l_libdw(const struct dso *dso)
+static inline void *dso__libdw(const struct dso *dso)
 {
-	return RC_CHK_ACCESS(dso)->a2l_libdw;
+	return RC_CHK_ACCESS(dso)->libdw;
 }
 
-static inline void dso__set_a2l_libdw(struct dso *dso, void *val)
+static inline void dso__set_libdw(struct dso *dso, void *val)
 {
-	RC_CHK_ACCESS(dso)->a2l_libdw = val;
+	RC_CHK_ACCESS(dso)->libdw = val;
 }
 
+struct Dwfl;
+#ifdef HAVE_LIBDW_SUPPORT
+struct Dwfl *dso__libdw_dwfl(struct dso *dso);
+#else
+static inline struct Dwfl *dso__libdw_dwfl(struct dso *dso __maybe_unused)
+{
+	return NULL;
+}
+#endif
+
 static inline unsigned int dso__a2l_fails(const struct dso *dso)
 {
 	return RC_CHK_ACCESS(dso)->a2l_fails;
diff --git a/tools/perf/util/libdw.c b/tools/perf/util/libdw.c
index b96c4e0d728f..216977884103 100644
--- a/tools/perf/util/libdw.c
+++ b/tools/perf/util/libdw.c
@@ -8,14 +8,62 @@
 #include <unistd.h>
 #include <elfutils/libdwfl.h>
 
-void dso__free_a2l_libdw(struct dso *dso)
+static const Dwfl_Callbacks offline_callbacks = {
+	.find_debuginfo = dwfl_standard_find_debuginfo,
+	.section_address = dwfl_offline_section_address,
+	.find_elf = dwfl_build_id_find_elf,
+};
+
+void dso__free_libdw(struct dso *dso)
 {
-	Dwfl *dwfl = dso__a2l_libdw(dso);
+	Dwfl *dwfl = dso__libdw(dso);
 
 	if (dwfl) {
 		dwfl_end(dwfl);
-		dso__set_a2l_libdw(dso, NULL);
+		dso__set_libdw(dso, NULL);
+	}
+}
+
+struct Dwfl *dso__libdw_dwfl(struct dso *dso)
+{
+	Dwfl *dwfl = dso__libdw(dso);
+	const char *dso_name;
+	Dwfl_Module *mod;
+	int fd;
+
+	if (dwfl)
+		return dwfl;
+
+	dso_name = dso__long_name(dso);
+	/*
+	 * Initialize Dwfl session.
+	 * We need to open the DSO file to report it to libdw.
+	 */
+	fd = open(dso_name, O_RDONLY);
+	if (fd < 0)
+		return NULL;
+
+	dwfl = dwfl_begin(&offline_callbacks);
+	if (!dwfl) {
+		close(fd);
+		return NULL;
+	}
+
+	/*
+	 * If the report is successful, the file descriptor fd is consumed
+	 * and closed by the Dwfl. If not, it is not closed.
+	 */
+	mod = dwfl_report_offline(dwfl, dso_name, dso_name, fd);
+	if (!mod) {
+		dwfl_end(dwfl);
+		close(fd);
+		return NULL;
 	}
+
+	dwfl_report_end(dwfl, /*removed=*/NULL, /*arg=*/NULL);
+	dso__set_libdw(dso, dwfl);
+
+	return dwfl;
 }
 
 struct libdw_a2l_cb_args {
@@ -63,58 +111,21 @@ static int libdw_a2l_cb(Dwarf_Die *die, void *_args)
 	return 0;
 }
 
-int libdw__addr2line(const char *dso_name, u64 addr,
-		     char **file, unsigned int *line_nr,
+int libdw__addr2line(u64 addr, char **file, unsigned int *line_nr,
 		     struct dso *dso, bool unwind_inlines,
 		     struct inline_node *node, struct symbol *sym)
 {
-	static const Dwfl_Callbacks offline_callbacks = {
-		.find_debuginfo = dwfl_standard_find_debuginfo,
-		.section_address = dwfl_offline_section_address,
-		.find_elf = dwfl_build_id_find_elf,
-	};
-	Dwfl *dwfl = dso__a2l_libdw(dso);
+	Dwfl *dwfl = dso__libdw_dwfl(dso);
 	Dwfl_Module *mod;
 	Dwfl_Line *dwline;
 	Dwarf_Addr bias;
 	const char *src;
 	int lineno = 0;
 
-	if (!dwfl) {
-		/*
-		 * Initialize Dwfl session.
-		 * We need to open the DSO file to report it to libdw.
-		 */
-		int fd;
-
-		fd = open(dso_name, O_RDONLY);
-		if (fd < 0)
-			return 0;
-
-		dwfl = dwfl_begin(&offline_callbacks);
-		if (!dwfl) {
-			close(fd);
-			return 0;
-		}
-
-		/*
-		 * If the report is successful, the file descriptor fd is consumed
-		 * and closed by the Dwfl. If not, it is not closed.
-		 */
-		mod = dwfl_report_offline(dwfl, dso_name, dso_name, fd);
-		if (!mod) {
-			dwfl_end(dwfl);
-			close(fd);
-			return 0;
-		}
-
-		dwfl_report_end(dwfl, /*removed=*/NULL, /*arg=*/NULL);
-		dso__set_a2l_libdw(dso, dwfl);
-	} else {
-		/* Dwfl session already initialized, get module for address. */
-		mod = dwfl_addrmodule(dwfl, addr);
-	}
+	if (!dwfl)
+		return 0;
 
+	mod = dwfl_addrmodule(dwfl, addr);
 	if (!mod)
 		return 0;
 
diff --git a/tools/perf/util/libdw.h b/tools/perf/util/libdw.h
index 0f8d7b4a11a5..b12094737415 100644
--- a/tools/perf/util/libdw.h
+++ b/tools/perf/util/libdw.h
@@ -11,7 +11,6 @@ struct symbol;
 #ifdef HAVE_LIBDW_SUPPORT
 /*
  * libdw__addr2line - Convert address to source location using libdw
- * @dso_name: Name of the DSO
  * @addr: Address to resolve
  * @file: Pointer to return filename (caller must free)
  * @line_nr: Pointer to return line number
@@ -26,23 +25,22 @@ struct symbol;
  *
  * Returns 1 on success (found), 0 on failure (not found).
  */
-int libdw__addr2line(const char *dso_name, u64 addr, char **file,
+int libdw__addr2line(u64 addr, char **file,
 		     unsigned int *line_nr, struct dso *dso,
 		     bool unwind_inlines, struct inline_node *node,
 		     struct symbol *sym);
 
 /*
- * dso__free_a2l_libdw - Free libdw resources associated with the DSO
+ * dso__free_libdw - Free libdw resources associated with the DSO
  * @dso: The dso to free resources for
  *
  * This function cleans up the Dwfl context used for addr2line lookups.
  */
-void dso__free_a2l_libdw(struct dso *dso);
+void dso__free_libdw(struct dso *dso);
 
 #else /* HAVE_LIBDW_SUPPORT */
 
-static inline int libdw__addr2line(const char *dso_name __maybe_unused,
-				   u64 addr __maybe_unused, char **file __maybe_unused,
+static inline int libdw__addr2line(u64 addr __maybe_unused, char **file __maybe_unused,
 				   unsigned int *line_nr __maybe_unused,
 				   struct dso *dso __maybe_unused,
 				   bool unwind_inlines __maybe_unused,
@@ -52,7 +50,7 @@ static inline int libdw__addr2line(const char *dso_name __maybe_unused,
 	return 0;
 }
 
-static inline void dso__free_a2l_libdw(struct dso *dso __maybe_unused)
+static inline void dso__free_libdw(struct dso *dso __maybe_unused)
 {
 }
 #endif /* HAVE_LIBDW_SUPPORT */
diff --git a/tools/perf/util/srcline.c b/tools/perf/util/srcline.c
index 28fa1abd1fd3..9be42f398440 100644
--- a/tools/perf/util/srcline.c
+++ b/tools/perf/util/srcline.c
@@ -161,7 +161,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(dso_name, addr, file, line_nr, dso, unwind_inlines,
+			ret = libdw__addr2line(addr, file, line_nr, dso, unwind_inlines,
 					       node, sym);
 			break;
 		case A2L_STYLE_LLVM:
-- 
2.52.0.457.g6b5491de43-goog


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

* [PATCH v1 08/23] perf perf_regs: Switch from arch string to int e_machine
  2026-01-17  5:28 [PATCH v1 00/23] perf dwarf/libdw extra support, speed and clean ups Ian Rogers
                   ` (6 preceding siblings ...)
  2026-01-17  5:28 ` [PATCH v1 07/23] perf powerpc: Unify the skip-callchain-idx libdw with that for addr2line Ian Rogers
@ 2026-01-17  5:28 ` Ian Rogers
  2026-01-20 18:49   ` Arnaldo Carvalho de Melo
  2026-01-17  5:28 ` [PATCH v1 09/23] perf dwarf-regs: Add util/dwarf-regs-arch for consistency with perf-regs Ian Rogers
                   ` (14 subsequent siblings)
  22 siblings, 1 reply; 32+ messages in thread
From: Ian Rogers @ 2026-01-17  5:28 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
	Namhyung Kim, Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
	John Garry, Will Deacon, Leo Yan, Guo Ren, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Shimin Guo,
	Athira Rajeev, Stephen Brennan, Howard Chu, Thomas Falcon,
	Andi Kleen, Dr. David Alan Gilbert, Dmitry Vyukov,
	Krzysztof Łopatowski, Chun-Tse Shao, Aditya Bodkhe, Haibo Xu,
	Sergei Trofimovich, linux-kernel, linux-perf-users,
	linux-arm-kernel, linux-csky, linux-riscv, Mark Wielaard

The arch string requires multiple strcmp to identify things like the
IP and SP. Switch to passing in an e_machine that in the bulk of cases
is computed using a current thread load. The e_machine also allows
identification of 32-bit vs 64-bit processes.

Signed-off-by: Ian Rogers <irogers@google.com>
---
 tools/perf/builtin-script.c                   |  17 ++-
 tools/perf/util/evsel.c                       |  14 ++-
 tools/perf/util/perf_regs.c                   | 105 +++++++++++-------
 tools/perf/util/perf_regs.h                   |  10 +-
 .../scripting-engines/trace-event-python.c    |  21 ++--
 tools/perf/util/session.c                     |  65 ++++++++---
 tools/perf/util/session.h                     |   1 +
 tools/perf/util/unwind-libdw.c                |  12 +-
 tools/perf/util/unwind-libunwind-local.c      |   7 +-
 9 files changed, 164 insertions(+), 88 deletions(-)

diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c
index 62e43d3c5ad7..372bede30230 100644
--- a/tools/perf/builtin-script.c
+++ b/tools/perf/builtin-script.c
@@ -717,7 +717,7 @@ static int perf_session__check_output_opt(struct perf_session *session)
 	return 0;
 }
 
-static int perf_sample__fprintf_regs(struct regs_dump *regs, uint64_t mask, const char *arch,
+static int perf_sample__fprintf_regs(struct regs_dump *regs, uint64_t mask, uint16_t e_machine,
 				     FILE *fp)
 {
 	unsigned i = 0, r;
@@ -730,7 +730,7 @@ static int perf_sample__fprintf_regs(struct regs_dump *regs, uint64_t mask, cons
 
 	for_each_set_bit(r, (unsigned long *) &mask, sizeof(mask) * 8) {
 		u64 val = regs->regs[i++];
-		printed += fprintf(fp, "%5s:0x%"PRIx64" ", perf_reg_name(r, arch), val);
+		printed += fprintf(fp, "%5s:0x%"PRIx64" ", perf_reg_name(r, e_machine), val);
 	}
 
 	return printed;
@@ -787,23 +787,23 @@ tod_scnprintf(struct perf_script *script, char *buf, int buflen,
 }
 
 static int perf_sample__fprintf_iregs(struct perf_sample *sample,
-				      struct perf_event_attr *attr, const char *arch, FILE *fp)
+				      struct perf_event_attr *attr, uint16_t e_machine, FILE *fp)
 {
 	if (!sample->intr_regs)
 		return 0;
 
 	return perf_sample__fprintf_regs(perf_sample__intr_regs(sample),
-					 attr->sample_regs_intr, arch, fp);
+					 attr->sample_regs_intr, e_machine, fp);
 }
 
 static int perf_sample__fprintf_uregs(struct perf_sample *sample,
-				      struct perf_event_attr *attr, const char *arch, FILE *fp)
+				      struct perf_event_attr *attr, uint16_t e_machine, FILE *fp)
 {
 	if (!sample->user_regs)
 		return 0;
 
 	return perf_sample__fprintf_regs(perf_sample__user_regs(sample),
-					 attr->sample_regs_user, arch, fp);
+					 attr->sample_regs_user, e_machine, fp);
 }
 
 static int perf_sample__fprintf_start(struct perf_script *script,
@@ -2418,7 +2418,6 @@ static void process_event(struct perf_script *script,
 	struct evsel_script *es = evsel->priv;
 	FILE *fp = es->fp;
 	char str[PAGE_SIZE_NAME_LEN];
-	const char *arch = perf_env__arch(machine->env);
 
 	if (output[type].fields == 0)
 		return;
@@ -2506,10 +2505,10 @@ static void process_event(struct perf_script *script,
 	}
 
 	if (PRINT_FIELD(IREGS))
-		perf_sample__fprintf_iregs(sample, attr, arch, fp);
+		perf_sample__fprintf_iregs(sample, attr, thread__e_machine(thread, machine), fp);
 
 	if (PRINT_FIELD(UREGS))
-		perf_sample__fprintf_uregs(sample, attr, arch, fp);
+		perf_sample__fprintf_uregs(sample, attr, thread__e_machine(thread, machine), fp);
 
 	if (PRINT_FIELD(BRSTACK))
 		perf_sample__fprintf_brstack(sample, thread, evsel, fp);
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index 6d324141588c..5ac1a05601b1 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -34,6 +34,7 @@
 #include "callchain.h"
 #include "cgroup.h"
 #include "counts.h"
+#include "dwarf-regs.h"
 #include "event.h"
 #include "evsel.h"
 #include "time-utils.h"
@@ -1007,6 +1008,13 @@ int evsel__group_desc(struct evsel *evsel, char *buf, size_t size)
 	return ret;
 }
 
+static uint16_t evsel__e_machine(struct evsel *evsel)
+{
+	struct perf_session *session = evsel__session(evsel);
+
+	return session ? perf_session__e_machine(session) : EM_HOST;
+}
+
 static void __evsel__config_callchain(struct evsel *evsel, struct record_opts *opts,
 				      struct callchain_param *param)
 {
@@ -1042,13 +1050,13 @@ static void __evsel__config_callchain(struct evsel *evsel, struct record_opts *o
 
 	if (param->record_mode == CALLCHAIN_DWARF) {
 		if (!function) {
-			const char *arch = perf_env__arch(evsel__env(evsel));
+			uint16_t e_machine = evsel__e_machine(evsel);
 
 			evsel__set_sample_bit(evsel, REGS_USER);
 			evsel__set_sample_bit(evsel, STACK_USER);
 			if (opts->sample_user_regs &&
-			    DWARF_MINIMAL_REGS(arch) != arch__user_reg_mask()) {
-				attr->sample_regs_user |= DWARF_MINIMAL_REGS(arch);
+			    DWARF_MINIMAL_REGS(e_machine) != arch__user_reg_mask()) {
+				attr->sample_regs_user |= DWARF_MINIMAL_REGS(e_machine);
 				pr_warning("WARNING: The use of --call-graph=dwarf may require all the user registers, "
 					   "specifying a subset with --user-regs may render DWARF unwinding unreliable, "
 					   "so the minimal registers set (IP, SP) is explicitly forced.\n");
diff --git a/tools/perf/util/perf_regs.c b/tools/perf/util/perf_regs.c
index 44b90bbf2d07..b58d59b84fb1 100644
--- a/tools/perf/util/perf_regs.c
+++ b/tools/perf/util/perf_regs.c
@@ -1,4 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0
+#include <elf.h>
 #include <errno.h>
 #include <string.h>
 #include "perf_regs.h"
@@ -30,30 +31,48 @@ const struct sample_reg * __weak arch__sample_reg_masks(void)
 	return sample_reg_masks;
 }
 
-const char *perf_reg_name(int id, const char *arch)
+const char *perf_reg_name(int id, uint16_t e_machine)
 {
 	const char *reg_name = NULL;
 
-	if (!strcmp(arch, "csky"))
+	switch (e_machine) {
+	case EM_ARM:
+		reg_name = __perf_reg_name_arm(id);
+		break;
+	case EM_AARCH64:
+		reg_name = __perf_reg_name_arm64(id);
+		break;
+	case EM_CSKY:
 		reg_name = __perf_reg_name_csky(id);
-	else if (!strcmp(arch, "loongarch"))
+		break;
+	case EM_LOONGARCH:
 		reg_name = __perf_reg_name_loongarch(id);
-	else if (!strcmp(arch, "mips"))
+		break;
+	case EM_MIPS:
 		reg_name = __perf_reg_name_mips(id);
-	else if (!strcmp(arch, "powerpc"))
+		break;
+	case EM_PPC:
+	case EM_PPC64:
 		reg_name = __perf_reg_name_powerpc(id);
-	else if (!strcmp(arch, "riscv"))
+		break;
+	case EM_RISCV:
 		reg_name = __perf_reg_name_riscv(id);
-	else if (!strcmp(arch, "s390"))
+		break;
+	case EM_S390:
 		reg_name = __perf_reg_name_s390(id);
-	else if (!strcmp(arch, "x86"))
+		break;
+	case EM_386:
+	case EM_X86_64:
 		reg_name = __perf_reg_name_x86(id);
-	else if (!strcmp(arch, "arm"))
-		reg_name = __perf_reg_name_arm(id);
-	else if (!strcmp(arch, "arm64"))
-		reg_name = __perf_reg_name_arm64(id);
+		break;
+	default:
+		break;
+	}
+	if (reg_name)
+		return reg_name;
 
-	return reg_name ?: "unknown";
+	pr_debug("Failed to find register %d for ELF machine type %u\n", id, e_machine);
+	return "unknown";
 }
 
 int perf_reg_value(u64 *valp, struct regs_dump *regs, int id)
@@ -83,52 +102,60 @@ int perf_reg_value(u64 *valp, struct regs_dump *regs, int id)
 	return 0;
 }
 
-uint64_t perf_arch_reg_ip(const char *arch)
+uint64_t perf_arch_reg_ip(uint16_t e_machine)
 {
-	if (!strcmp(arch, "arm"))
+	switch (e_machine) {
+	case EM_ARM:
 		return __perf_reg_ip_arm();
-	else if (!strcmp(arch, "arm64"))
+	case EM_AARCH64:
 		return __perf_reg_ip_arm64();
-	else if (!strcmp(arch, "csky"))
+	case EM_CSKY:
 		return __perf_reg_ip_csky();
-	else if (!strcmp(arch, "loongarch"))
+	case EM_LOONGARCH:
 		return __perf_reg_ip_loongarch();
-	else if (!strcmp(arch, "mips"))
+	case EM_MIPS:
 		return __perf_reg_ip_mips();
-	else if (!strcmp(arch, "powerpc"))
+	case EM_PPC:
+	case EM_PPC64:
 		return __perf_reg_ip_powerpc();
-	else if (!strcmp(arch, "riscv"))
+	case EM_RISCV:
 		return __perf_reg_ip_riscv();
-	else if (!strcmp(arch, "s390"))
+	case EM_S390:
 		return __perf_reg_ip_s390();
-	else if (!strcmp(arch, "x86"))
+	case EM_386:
+	case EM_X86_64:
 		return __perf_reg_ip_x86();
-
-	pr_err("Fail to find IP register for arch %s, returns 0\n", arch);
-	return 0;
+	default:
+		pr_err("Failed to find IP register for ELF machine type %u\n", e_machine);
+		return 0;
+	}
 }
 
-uint64_t perf_arch_reg_sp(const char *arch)
+uint64_t perf_arch_reg_sp(uint16_t e_machine)
 {
-	if (!strcmp(arch, "arm"))
+	switch (e_machine) {
+	case EM_ARM:
 		return __perf_reg_sp_arm();
-	else if (!strcmp(arch, "arm64"))
+	case EM_AARCH64:
 		return __perf_reg_sp_arm64();
-	else if (!strcmp(arch, "csky"))
+	case EM_CSKY:
 		return __perf_reg_sp_csky();
-	else if (!strcmp(arch, "loongarch"))
+	case EM_LOONGARCH:
 		return __perf_reg_sp_loongarch();
-	else if (!strcmp(arch, "mips"))
+	case EM_MIPS:
 		return __perf_reg_sp_mips();
-	else if (!strcmp(arch, "powerpc"))
+	case EM_PPC:
+	case EM_PPC64:
 		return __perf_reg_sp_powerpc();
-	else if (!strcmp(arch, "riscv"))
+	case EM_RISCV:
 		return __perf_reg_sp_riscv();
-	else if (!strcmp(arch, "s390"))
+	case EM_S390:
 		return __perf_reg_sp_s390();
-	else if (!strcmp(arch, "x86"))
+	case EM_386:
+	case EM_X86_64:
 		return __perf_reg_sp_x86();
-
-	pr_err("Fail to find SP register for arch %s, returns 0\n", arch);
-	return 0;
+	default:
+		pr_err("Failed to find SP register for ELF machine type %u\n", e_machine);
+		return 0;
+	}
 }
diff --git a/tools/perf/util/perf_regs.h b/tools/perf/util/perf_regs.h
index f2d0736d65cc..7bfc6a34c02b 100644
--- a/tools/perf/util/perf_regs.h
+++ b/tools/perf/util/perf_regs.h
@@ -28,10 +28,10 @@ uint64_t arch__intr_reg_mask(void);
 uint64_t arch__user_reg_mask(void);
 const struct sample_reg *arch__sample_reg_masks(void);
 
-const char *perf_reg_name(int id, const char *arch);
+const char *perf_reg_name(int id, uint16_t e_machine);
 int perf_reg_value(u64 *valp, struct regs_dump *regs, int id);
-uint64_t perf_arch_reg_ip(const char *arch);
-uint64_t perf_arch_reg_sp(const char *arch);
+uint64_t perf_arch_reg_ip(uint16_t e_machine);
+uint64_t perf_arch_reg_sp(uint16_t e_machine);
 const char *__perf_reg_name_arm64(int id);
 uint64_t __perf_reg_ip_arm64(void);
 uint64_t __perf_reg_sp_arm64(void);
@@ -60,9 +60,9 @@ const char *__perf_reg_name_x86(int id);
 uint64_t __perf_reg_ip_x86(void);
 uint64_t __perf_reg_sp_x86(void);
 
-static inline uint64_t DWARF_MINIMAL_REGS(const char *arch)
+static inline uint64_t DWARF_MINIMAL_REGS(uint16_t e_machine)
 {
-	return (1ULL << perf_arch_reg_ip(arch)) | (1ULL << perf_arch_reg_sp(arch));
+	return (1ULL << perf_arch_reg_ip(e_machine)) | (1ULL << perf_arch_reg_sp(e_machine));
 }
 
 #endif /* __PERF_REGS_H */
diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c
index 6655c0bbe0d8..b90edc147796 100644
--- a/tools/perf/util/scripting-engines/trace-event-python.c
+++ b/tools/perf/util/scripting-engines/trace-event-python.c
@@ -50,6 +50,7 @@
 #include "../thread-stack.h"
 #include "../trace-event.h"
 #include "../call-path.h"
+#include "dwarf-regs.h"
 #include "map.h"
 #include "symbol.h"
 #include "thread_map.h"
@@ -713,7 +714,7 @@ static void set_sample_datasrc_in_dict(PyObject *dict,
 			_PyUnicode_FromString(decode));
 }
 
-static void regs_map(struct regs_dump *regs, uint64_t mask, const char *arch, char *bf, int size)
+static void regs_map(struct regs_dump *regs, uint64_t mask, uint16_t e_machine, char *bf, int size)
 {
 	unsigned int i = 0, r;
 	int printed = 0;
@@ -731,7 +732,7 @@ static void regs_map(struct regs_dump *regs, uint64_t mask, const char *arch, ch
 
 		printed += scnprintf(bf + printed, size - printed,
 				     "%5s:0x%" PRIx64 " ",
-				     perf_reg_name(r, arch), val);
+				     perf_reg_name(r, e_machine), val);
 	}
 }
 
@@ -739,10 +740,10 @@ static void regs_map(struct regs_dump *regs, uint64_t mask, const char *arch, ch
 
 static int set_regs_in_dict(PyObject *dict,
 			     struct perf_sample *sample,
-			     struct evsel *evsel)
+			     struct evsel *evsel,
+			     uint16_t e_machine)
 {
 	struct perf_event_attr *attr = &evsel->core.attr;
-	const char *arch = perf_env__arch(evsel__env(evsel));
 
 	int size = (__sw_hweight64(attr->sample_regs_intr) * MAX_REG_SIZE) + 1;
 	char *bf = NULL;
@@ -752,7 +753,7 @@ static int set_regs_in_dict(PyObject *dict,
 		if (!bf)
 			return -1;
 
-		regs_map(sample->intr_regs, attr->sample_regs_intr, arch, bf, size);
+		regs_map(sample->intr_regs, attr->sample_regs_intr, e_machine, bf, size);
 
 		pydict_set_item_string_decref(dict, "iregs",
 					_PyUnicode_FromString(bf));
@@ -764,7 +765,7 @@ static int set_regs_in_dict(PyObject *dict,
 			if (!bf)
 				return -1;
 		}
-		regs_map(sample->user_regs, attr->sample_regs_user, arch, bf, size);
+		regs_map(sample->user_regs, attr->sample_regs_user, e_machine, bf, size);
 
 		pydict_set_item_string_decref(dict, "uregs",
 					_PyUnicode_FromString(bf));
@@ -834,6 +835,8 @@ static PyObject *get_perf_sample_dict(struct perf_sample *sample,
 					 PyObject *callchain)
 {
 	PyObject *dict, *dict_sample, *brstack, *brstacksym;
+	struct machine *machine;
+	uint16_t e_machine = EM_HOST;
 
 	dict = PyDict_New();
 	if (!dict)
@@ -920,7 +923,11 @@ static PyObject *get_perf_sample_dict(struct perf_sample *sample,
 			PyLong_FromUnsignedLongLong(sample->cyc_cnt));
 	}
 
-	if (set_regs_in_dict(dict, sample, evsel))
+	if (al->thread) {
+		machine = maps__machine(thread__maps(al->thread));
+		e_machine = thread__e_machine(al->thread, machine);
+	}
+	if (set_regs_in_dict(dict, sample, evsel, e_machine))
 		Py_FatalError("Failed to setting regs in dict");
 
 	return dict;
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
index 922ef6577bbb..d7b28cb4e672 100644
--- a/tools/perf/util/session.c
+++ b/tools/perf/util/session.c
@@ -17,6 +17,7 @@
 #include "map_symbol.h"
 #include "branch.h"
 #include "debug.h"
+#include "dwarf-regs.h"
 #include "env.h"
 #include "evlist.h"
 #include "evsel.h"
@@ -942,7 +943,7 @@ static void branch_stack__printf(struct perf_sample *sample,
 	}
 }
 
-static void regs_dump__printf(u64 mask, u64 *regs, const char *arch)
+static void regs_dump__printf(u64 mask, u64 *regs, uint16_t e_machine)
 {
 	unsigned rid, i = 0;
 
@@ -950,7 +951,7 @@ static void regs_dump__printf(u64 mask, u64 *regs, const char *arch)
 		u64 val = regs[i++];
 
 		printf(".... %-5s 0x%016" PRIx64 "\n",
-		       perf_reg_name(rid, arch), val);
+		       perf_reg_name(rid, e_machine), val);
 	}
 }
 
@@ -968,7 +969,7 @@ static inline const char *regs_dump_abi(struct regs_dump *d)
 	return regs_abi[d->abi];
 }
 
-static void regs__printf(const char *type, struct regs_dump *regs, const char *arch)
+static void regs__printf(const char *type, struct regs_dump *regs, uint16_t e_machine)
 {
 	u64 mask = regs->mask;
 
@@ -977,10 +978,10 @@ static void regs__printf(const char *type, struct regs_dump *regs, const char *a
 	       mask,
 	       regs_dump_abi(regs));
 
-	regs_dump__printf(mask, regs->regs, arch);
+	regs_dump__printf(mask, regs->regs, e_machine);
 }
 
-static void regs_user__printf(struct perf_sample *sample, const char *arch)
+static void regs_user__printf(struct perf_sample *sample, uint16_t e_machine)
 {
 	struct regs_dump *user_regs;
 
@@ -990,10 +991,10 @@ static void regs_user__printf(struct perf_sample *sample, const char *arch)
 	user_regs = perf_sample__user_regs(sample);
 
 	if (user_regs->regs)
-		regs__printf("user", user_regs, arch);
+		regs__printf("user", user_regs, e_machine);
 }
 
-static void regs_intr__printf(struct perf_sample *sample, const char *arch)
+static void regs_intr__printf(struct perf_sample *sample, uint16_t e_machine)
 {
 	struct regs_dump *intr_regs;
 
@@ -1003,7 +1004,7 @@ static void regs_intr__printf(struct perf_sample *sample, const char *arch)
 	intr_regs = perf_sample__intr_regs(sample);
 
 	if (intr_regs->regs)
-		regs__printf("intr", intr_regs, arch);
+		regs__printf("intr", intr_regs, e_machine);
 }
 
 static void stack_user__printf(struct stack_dump *dump)
@@ -1092,21 +1093,28 @@ char *get_page_size_name(u64 size, char *str)
 	return str;
 }
 
-static void dump_sample(struct evsel *evsel, union perf_event *event,
-			struct perf_sample *sample, const char *arch)
+static void dump_sample(struct machine *machine, struct evsel *evsel, union perf_event *event,
+			struct perf_sample *sample)
 {
 	u64 sample_type;
 	char str[PAGE_SIZE_NAME_LEN];
+	uint16_t e_machine = EM_NONE;
 
 	if (!dump_trace)
 		return;
 
+	sample_type = evsel->core.attr.sample_type;
+
+	if (sample_type & (PERF_SAMPLE_REGS_USER | PERF_SAMPLE_REGS_INTR)) {
+		struct thread *thread = machine__find_thread(machine, sample->pid, sample->pid);
+
+		e_machine = thread__e_machine(thread, machine);
+	}
+
 	printf("(IP, 0x%x): %d/%d: %#" PRIx64 " period: %" PRIu64 " addr: %#" PRIx64 "\n",
 	       event->header.misc, sample->pid, sample->tid, sample->ip,
 	       sample->period, sample->addr);
 
-	sample_type = evsel->core.attr.sample_type;
-
 	if (evsel__has_callchain(evsel))
 		callchain__printf(evsel, sample);
 
@@ -1114,10 +1122,10 @@ static void dump_sample(struct evsel *evsel, union perf_event *event,
 		branch_stack__printf(sample, evsel);
 
 	if (sample_type & PERF_SAMPLE_REGS_USER)
-		regs_user__printf(sample, arch);
+		regs_user__printf(sample, e_machine);
 
 	if (sample_type & PERF_SAMPLE_REGS_INTR)
-		regs_intr__printf(sample, arch);
+		regs_intr__printf(sample, e_machine);
 
 	if (sample_type & PERF_SAMPLE_STACK_USER)
 		stack_user__printf(&sample->user_stack);
@@ -1432,10 +1440,10 @@ static int machines__deliver_event(struct machines *machines,
 		}
 		if (machine == NULL) {
 			++evlist->stats.nr_unprocessable_samples;
-			dump_sample(evsel, event, sample, perf_env__arch(NULL));
+			dump_sample(machine, evsel, event, sample);
 			return 0;
 		}
-		dump_sample(evsel, event, sample, perf_env__arch(machine->env));
+		dump_sample(machine, evsel, event, sample);
 		if (sample->deferred_callchain && tool->merge_deferred_callchains) {
 			struct deferred_event *de = malloc(sizeof(*de));
 			size_t sz = event->header.size;
@@ -2928,3 +2936,28 @@ struct perf_env *perf_session__env(struct perf_session *session)
 {
 	return &session->header.env;
 }
+
+static int perf_session__e_machine_cb(struct thread *thread,
+				      void *arg __maybe_unused)
+{
+	uint16_t *result = arg;
+	struct machine *machine = maps__machine(thread__maps(thread));
+
+	*result = thread__e_machine(thread, machine);
+	return *result != EM_NONE ? 1 : 0;
+}
+
+/*
+ * Note, a machine may have mixed 32-bit and 64-bit processes and so mixed
+ * e_machines. Use thread__e_machine when this matters.
+ */
+uint16_t perf_session__e_machine(struct perf_session *session)
+{
+	uint16_t e_machine = EM_NONE;
+
+	machines__for_each_thread(&session->machines,
+					 perf_session__e_machine_cb,
+					 &e_machine);
+
+	return e_machine == EM_NONE ? EM_HOST : e_machine;
+}
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h
index 22d3ff877e83..eddc4c630b33 100644
--- a/tools/perf/util/session.h
+++ b/tools/perf/util/session.h
@@ -211,5 +211,6 @@ int perf_event__process_finished_round(const struct perf_tool *tool,
 				       struct ordered_events *oe);
 
 struct perf_env *perf_session__env(struct perf_session *session);
+uint16_t perf_session__e_machine(struct perf_session *session);
 
 #endif /* __PERF_SESSION_H */
diff --git a/tools/perf/util/unwind-libdw.c b/tools/perf/util/unwind-libdw.c
index dc882f17f52d..c25190cdceb4 100644
--- a/tools/perf/util/unwind-libdw.c
+++ b/tools/perf/util/unwind-libdw.c
@@ -187,7 +187,7 @@ static bool memory_read(Dwfl *dwfl __maybe_unused, Dwarf_Addr addr, Dwarf_Word *
 			void *arg)
 {
 	struct unwind_info *ui = arg;
-	const char *arch = perf_env__arch(ui->machine->env);
+	uint16_t e_machine = thread__e_machine(ui->thread, ui->machine);
 	struct stack_dump *stack = &ui->sample->user_stack;
 	u64 start, end;
 	int offset;
@@ -197,7 +197,7 @@ static bool memory_read(Dwfl *dwfl __maybe_unused, Dwarf_Addr addr, Dwarf_Word *
 		return false;
 
 	ret = perf_reg_value(&start, ui->sample->user_regs,
-			     perf_arch_reg_sp(arch));
+			     perf_arch_reg_sp(e_machine));
 	if (ret)
 		return false;
 
@@ -300,16 +300,18 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
 			int max_stack,
 			bool best_effort)
 {
+	struct machine *machine = maps__machine(thread__maps(thread));
 	struct unwind_info *ui, ui_buf = {
 		.sample		= data,
 		.thread		= thread,
-		.machine	= maps__machine((thread__maps(thread))),
+		.machine	= machine,
 		.cb		= cb,
 		.arg		= arg,
 		.max_stack	= max_stack,
 		.best_effort    = best_effort
 	};
-	const char *arch = perf_env__arch(ui_buf.machine->env);
+	uint16_t e_machine = thread__e_machine(thread, machine);
+	const char *arch = perf_env__arch(machine->env);
 	Dwarf_Word ip;
 	int err = -EINVAL, i;
 	const Dwfl_Thread_Callbacks *callbacks;
@@ -327,7 +329,7 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
 	if (!ui->dwfl)
 		goto out;
 
-	err = perf_reg_value(&ip, data->user_regs, perf_arch_reg_ip(arch));
+	err = perf_reg_value(&ip, data->user_regs, perf_arch_reg_ip(e_machine));
 	if (err)
 		goto out;
 
diff --git a/tools/perf/util/unwind-libunwind-local.c b/tools/perf/util/unwind-libunwind-local.c
index 0b037e7389a0..a24b45106acd 100644
--- a/tools/perf/util/unwind-libunwind-local.c
+++ b/tools/perf/util/unwind-libunwind-local.c
@@ -572,7 +572,6 @@ static int access_mem(unw_addr_space_t __maybe_unused as,
 		      int __write, void *arg)
 {
 	struct unwind_info *ui = arg;
-	const char *arch = perf_env__arch(ui->machine->env);
 	struct stack_dump *stack = &ui->sample->user_stack;
 	u64 start, end;
 	int offset;
@@ -585,7 +584,7 @@ static int access_mem(unw_addr_space_t __maybe_unused as,
 	}
 
 	ret = perf_reg_value(&start, perf_sample__user_regs(ui->sample),
-			     perf_arch_reg_sp(arch));
+			     perf_arch_reg_sp(thread__e_machine(ui->thread, ui->machine)));
 	if (ret)
 		return ret;
 
@@ -734,7 +733,7 @@ static void _unwind__finish_access(struct maps *maps)
 static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb,
 		       void *arg, int max_stack)
 {
-	const char *arch = perf_env__arch(ui->machine->env);
+	uint16_t e_machine = thread__e_machine(ui->thread, ui->machine);
 	u64 val;
 	unw_word_t ips[max_stack];
 	unw_addr_space_t addr_space;
@@ -742,7 +741,7 @@ static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb,
 	int ret, i = 0;
 
 	ret = perf_reg_value(&val, perf_sample__user_regs(ui->sample),
-			     perf_arch_reg_ip(arch));
+			     perf_arch_reg_ip(e_machine));
 	if (ret)
 		return ret;
 
-- 
2.52.0.457.g6b5491de43-goog


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

* [PATCH v1 09/23] perf dwarf-regs: Add util/dwarf-regs-arch for consistency with perf-regs
  2026-01-17  5:28 [PATCH v1 00/23] perf dwarf/libdw extra support, speed and clean ups Ian Rogers
                   ` (7 preceding siblings ...)
  2026-01-17  5:28 ` [PATCH v1 08/23] perf perf_regs: Switch from arch string to int e_machine Ian Rogers
@ 2026-01-17  5:28 ` Ian Rogers
  2026-01-17  5:28 ` [PATCH v1 10/23] perf dwarf-regs: Remove get_arch_regnum Ian Rogers
                   ` (13 subsequent siblings)
  22 siblings, 0 replies; 32+ messages in thread
From: Ian Rogers @ 2026-01-17  5:28 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
	Namhyung Kim, Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
	John Garry, Will Deacon, Leo Yan, Guo Ren, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Shimin Guo,
	Athira Rajeev, Stephen Brennan, Howard Chu, Thomas Falcon,
	Andi Kleen, Dr. David Alan Gilbert, Dmitry Vyukov,
	Krzysztof Łopatowski, Chun-Tse Shao, Aditya Bodkhe, Haibo Xu,
	Sergei Trofimovich, linux-kernel, linux-perf-users,
	linux-arm-kernel, linux-csky, linux-riscv, Mark Wielaard

perf_regs.h has cross architecture functions for operating with the
differing perf register constants. dwarf-regs.h is similar but for
cross architecture dwarf notions of registers. For consistency move
the arch parts of dwarf-regs out of util and into its own directory.

Signed-off-by: Ian Rogers <irogers@google.com>
---
 tools/perf/util/Build                                      | 4 +---
 tools/perf/util/dwarf-regs-arch/Build                      | 3 +++
 tools/perf/util/{ => dwarf-regs-arch}/dwarf-regs-csky.c    | 0
 tools/perf/util/{ => dwarf-regs-arch}/dwarf-regs-powerpc.c | 0
 tools/perf/util/{ => dwarf-regs-arch}/dwarf-regs-x86.c     | 0
 5 files changed, 4 insertions(+), 3 deletions(-)
 create mode 100644 tools/perf/util/dwarf-regs-arch/Build
 rename tools/perf/util/{ => dwarf-regs-arch}/dwarf-regs-csky.c (100%)
 rename tools/perf/util/{ => dwarf-regs-arch}/dwarf-regs-powerpc.c (100%)
 rename tools/perf/util/{ => dwarf-regs-arch}/dwarf-regs-x86.c (100%)

diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index 5efec73be474..3cb1edd263cf 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -219,9 +219,7 @@ endif
 perf-util-$(CONFIG_LIBDW) += probe-finder.o
 perf-util-$(CONFIG_LIBDW) += dwarf-aux.o
 perf-util-$(CONFIG_LIBDW) += dwarf-regs.o
-perf-util-$(CONFIG_LIBDW) += dwarf-regs-csky.o
-perf-util-$(CONFIG_LIBDW) += dwarf-regs-powerpc.o
-perf-util-$(CONFIG_LIBDW) += dwarf-regs-x86.o
+perf-util-$(CONFIG_LIBDW) += dwarf-regs-arch/
 perf-util-$(CONFIG_LIBDW) += debuginfo.o
 perf-util-$(CONFIG_LIBDW) += annotate-data.o
 perf-util-$(CONFIG_LIBDW) += libdw.o
diff --git a/tools/perf/util/dwarf-regs-arch/Build b/tools/perf/util/dwarf-regs-arch/Build
new file mode 100644
index 000000000000..98bec0032606
--- /dev/null
+++ b/tools/perf/util/dwarf-regs-arch/Build
@@ -0,0 +1,3 @@
+perf-util-$(CONFIG_LIBDW) += dwarf-regs-csky.o
+perf-util-$(CONFIG_LIBDW) += dwarf-regs-powerpc.o
+perf-util-$(CONFIG_LIBDW) += dwarf-regs-x86.o
diff --git a/tools/perf/util/dwarf-regs-csky.c b/tools/perf/util/dwarf-regs-arch/dwarf-regs-csky.c
similarity index 100%
rename from tools/perf/util/dwarf-regs-csky.c
rename to tools/perf/util/dwarf-regs-arch/dwarf-regs-csky.c
diff --git a/tools/perf/util/dwarf-regs-powerpc.c b/tools/perf/util/dwarf-regs-arch/dwarf-regs-powerpc.c
similarity index 100%
rename from tools/perf/util/dwarf-regs-powerpc.c
rename to tools/perf/util/dwarf-regs-arch/dwarf-regs-powerpc.c
diff --git a/tools/perf/util/dwarf-regs-x86.c b/tools/perf/util/dwarf-regs-arch/dwarf-regs-x86.c
similarity index 100%
rename from tools/perf/util/dwarf-regs-x86.c
rename to tools/perf/util/dwarf-regs-arch/dwarf-regs-x86.c
-- 
2.52.0.457.g6b5491de43-goog


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

* [PATCH v1 10/23] perf dwarf-regs: Remove get_arch_regnum
  2026-01-17  5:28 [PATCH v1 00/23] perf dwarf/libdw extra support, speed and clean ups Ian Rogers
                   ` (8 preceding siblings ...)
  2026-01-17  5:28 ` [PATCH v1 09/23] perf dwarf-regs: Add util/dwarf-regs-arch for consistency with perf-regs Ian Rogers
@ 2026-01-17  5:28 ` Ian Rogers
  2026-01-17  5:28 ` [PATCH v1 11/23] perf dwarf-regs: Clean up x86 dwarf_regnum code Ian Rogers
                   ` (12 subsequent siblings)
  22 siblings, 0 replies; 32+ messages in thread
From: Ian Rogers @ 2026-01-17  5:28 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
	Namhyung Kim, Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
	John Garry, Will Deacon, Leo Yan, Guo Ren, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Shimin Guo,
	Athira Rajeev, Stephen Brennan, Howard Chu, Thomas Falcon,
	Andi Kleen, Dr. David Alan Gilbert, Dmitry Vyukov,
	Krzysztof Łopatowski, Chun-Tse Shao, Aditya Bodkhe, Haibo Xu,
	Sergei Trofimovich, linux-kernel, linux-perf-users,
	linux-arm-kernel, linux-csky, linux-riscv, Mark Wielaard

Except in dwarf-regs the function is never called. The weak function
has no strong arch implementations. Remove so that the fall-through
case applies.

Signed-off-by: Ian Rogers <irogers@google.com>
---
 tools/perf/util/dwarf-regs.c         | 12 ------------
 tools/perf/util/include/dwarf-regs.h |  4 ----
 2 files changed, 16 deletions(-)

diff --git a/tools/perf/util/dwarf-regs.c b/tools/perf/util/dwarf-regs.c
index 28a1cfdf26d4..b2f37299147e 100644
--- a/tools/perf/util/dwarf-regs.c
+++ b/tools/perf/util/dwarf-regs.c
@@ -71,13 +71,6 @@ const char *get_dwarf_regstr(unsigned int n, unsigned int machine, unsigned int
 	return NULL;
 }
 
-#if EM_HOST != EM_X86_64 && EM_HOST != EM_386
-__weak int get_arch_regnum(const char *name __maybe_unused)
-{
-	return -ENOTSUP;
-}
-#endif
-
 /* Return DWARF register number from architecture register name */
 int get_dwarf_regnum(const char *name, unsigned int machine, unsigned int flags __maybe_unused)
 {
@@ -98,11 +91,6 @@ int get_dwarf_regnum(const char *name, unsigned int machine, unsigned int flags
 		machine = EM_HOST;
 	}
 	switch (machine) {
-#if EM_HOST != EM_X86_64 && EM_HOST != EM_386
-	case EM_HOST:
-		reg = get_arch_regnum(regname);
-		break;
-#endif
 	case EM_X86_64:
 		fallthrough;
 	case EM_386:
diff --git a/tools/perf/util/include/dwarf-regs.h b/tools/perf/util/include/dwarf-regs.h
index 6f1b9f6b2466..015d1ade645f 100644
--- a/tools/perf/util/include/dwarf-regs.h
+++ b/tools/perf/util/include/dwarf-regs.h
@@ -101,10 +101,6 @@ const char *get_dwarf_regstr(unsigned int n, unsigned int machine, unsigned int
 
 int get_x86_regnum(const char *name);
 
-#if !defined(__x86_64__) && !defined(__i386__)
-int get_arch_regnum(const char *name);
-#endif
-
 /*
  * get_dwarf_regnum - Returns DWARF regnum from register name
  * name: architecture register name
-- 
2.52.0.457.g6b5491de43-goog


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

* [PATCH v1 11/23] perf dwarf-regs: Clean up x86 dwarf_regnum code
  2026-01-17  5:28 [PATCH v1 00/23] perf dwarf/libdw extra support, speed and clean ups Ian Rogers
                   ` (9 preceding siblings ...)
  2026-01-17  5:28 ` [PATCH v1 10/23] perf dwarf-regs: Remove get_arch_regnum Ian Rogers
@ 2026-01-17  5:28 ` Ian Rogers
  2026-01-17  5:28 ` [PATCH v1 12/23] perf dwarf-regs: Add get_dwarf_regnum_for_perf_regnum and use for x86 unwinding Ian Rogers
                   ` (11 subsequent siblings)
  22 siblings, 0 replies; 32+ messages in thread
From: Ian Rogers @ 2026-01-17  5:28 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
	Namhyung Kim, Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
	John Garry, Will Deacon, Leo Yan, Guo Ren, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Shimin Guo,
	Athira Rajeev, Stephen Brennan, Howard Chu, Thomas Falcon,
	Andi Kleen, Dr. David Alan Gilbert, Dmitry Vyukov,
	Krzysztof Łopatowski, Chun-Tse Shao, Aditya Bodkhe, Haibo Xu,
	Sergei Trofimovich, linux-kernel, linux-perf-users,
	linux-arm-kernel, linux-csky, linux-riscv, Mark Wielaard

The i386 and x86-64 register numbers differ on x86, but previously x86
was a single arch string and so this couldn't be handled. The
transition to using ELF EM_* values means we can translate x86
registers correctly for either the x86-64 dwarf register mappings
(from the System V ABI) or i386 register mappings. Correct the mappings.

Signed-off-by: Ian Rogers <irogers@google.com>
---
 .../util/dwarf-regs-arch/dwarf-regs-x86.c     | 133 ++++++++++++++++--
 tools/perf/util/dwarf-regs.c                  |   5 +-
 tools/perf/util/include/dwarf-regs.h          |   3 +-
 3 files changed, 129 insertions(+), 12 deletions(-)

diff --git a/tools/perf/util/dwarf-regs-arch/dwarf-regs-x86.c b/tools/perf/util/dwarf-regs-arch/dwarf-regs-x86.c
index 7a55c65e8da6..f0c42e4d7423 100644
--- a/tools/perf/util/dwarf-regs-arch/dwarf-regs-x86.c
+++ b/tools/perf/util/dwarf-regs-arch/dwarf-regs-x86.c
@@ -13,10 +13,65 @@
 
 struct dwarf_regs_idx {
 	const char *name;
-	int idx;
+	int dwarf_regnum;
 };
 
-static const struct dwarf_regs_idx x86_regidx_table[] = {
+static const struct dwarf_regs_idx i386_regidx_table[] = {
+	{ "eax", 0 }, { "ax", 0 }, { "al", 0 },
+	{ "ecx", 1 }, { "cx", 1 }, { "cl", 1 },
+	{ "edx", 2 }, { "dx", 2 }, { "dl", 2 },
+	{ "ebx", 3 }, { "bx", 3 }, { "bl", 3 },
+	{ "esp", 4 }, { "sp", 4 }, { "$stack", 4},
+	{ "ebp", 5 }, { "bp", 5 },
+	{ "esi", 6 }, { "si", 6 },
+	{ "edi", 7 }, { "di", 7 },
+	// 8 - Return Address RA
+	{ "eflags", 9}, { "flags", 9},
+	// 10 - reserved
+	{ "st0", 11},
+	{ "st1", 12},
+	{ "st2", 13},
+	{ "st3", 14},
+	{ "st4", 15},
+	{ "st5", 16},
+	{ "st6", 17},
+	{ "st7", 18},
+	// 19-20 - reserved
+	{ "xmm0", 21},
+	{ "xmm1", 22},
+	{ "xmm2", 23},
+	{ "xmm3", 24},
+	{ "xmm4", 25},
+	{ "xmm5", 26},
+	{ "xmm6", 27},
+	{ "xmm7", 28},
+	{ "mm0", 29},
+	{ "mm1", 30},
+	{ "mm2", 31},
+	{ "mm3", 32},
+	{ "mm4", 33},
+	{ "mm5", 34},
+	{ "mm6", 35},
+	{ "mm7", 36},
+	// 37-38 - unknown
+	{ "mxcsr", 39}, // 128-bit Media Control and Status
+	{ "es", 40},
+	{ "cs", 41},
+	{ "ss", 42},
+	{ "ds", 43},
+	{ "fs", 44},
+	{ "gs", 45},
+	// 46-47 - reserved
+	{ "tr", 48}, // Task Register
+	{ "ldtr", 49}, // LDT Register
+	// 50-92 - reserved
+	{ "fs.base", 92},
+	{ "gs.base", 93},
+	// End of regular dwarf registers.
+	{ "eip", DWARF_REG_PC }, { "ip", DWARF_REG_PC },
+};
+
+static const struct dwarf_regs_idx x86_64_regidx_table[] = {
 	{ "rax", 0 }, { "eax", 0 }, { "ax", 0 }, { "al", 0 },
 	{ "rdx", 1 }, { "edx", 1 }, { "dx", 1 }, { "dl", 1 },
 	{ "rcx", 2 }, { "ecx", 2 }, { "cx", 2 }, { "cl", 2 },
@@ -33,18 +88,78 @@ static const struct dwarf_regs_idx x86_regidx_table[] = {
 	{ "r13", 13 }, { "r13d", 13 }, { "r13w", 13 }, { "r13b", 13 },
 	{ "r14", 14 }, { "r14d", 14 }, { "r14w", 14 }, { "r14b", 14 },
 	{ "r15", 15 }, { "r15d", 15 }, { "r15w", 15 }, { "r15b", 15 },
-	{ "rip", DWARF_REG_PC },
+	// 16 - Return Address RA
+	{ "xmm0", 17},
+	{ "xmm1", 18},
+	{ "xmm2", 19},
+	{ "xmm3", 20},
+	{ "xmm4", 21},
+	{ "xmm5", 22},
+	{ "xmm6", 23},
+	{ "xmm7", 24},
+	{ "xmm8", 25},
+	{ "xmm9", 26},
+	{ "xmm10", 27},
+	{ "xmm11", 28},
+	{ "xmm12", 29},
+	{ "xmm13", 30},
+	{ "xmm14", 31},
+	{ "xmm15", 32},
+	{ "st0", 33},
+	{ "st1", 34},
+	{ "st2", 35},
+	{ "st3", 36},
+	{ "st4", 37},
+	{ "st5", 38},
+	{ "st6", 39},
+	{ "st7", 40},
+	{ "mm0", 41},
+	{ "mm1", 42},
+	{ "mm2", 43},
+	{ "mm3", 44},
+	{ "mm4", 45},
+	{ "mm5", 46},
+	{ "mm6", 47},
+	{ "mm7", 48},
+	{ "rflags", 49}, { "eflags", 49}, { "flags", 49},
+	{ "es", 50},
+	{ "cs", 51},
+	{ "ss", 52},
+	{ "ds", 53},
+	{ "fs", 54},
+	{ "gs", 55},
+	// 56-47 - reserved
+	{ "fs.base", 58},
+	{ "gs.base", 59},
+	// 60-61 - reserved
+	{ "tr", 62}, // Task Register
+	{ "ldtr", 63}, // LDT Register
+	{ "mxcsr", 64}, // 128-bit Media Control and Status
+	{ "fcw", 65}, // x87 Control Word
+	{ "fsw", 66}, // x87 Status Word
+	// End of regular dwarf registers.
+	{ "rip", DWARF_REG_PC }, { "eip", DWARF_REG_PC }, { "ip", DWARF_REG_PC },
 };
 
-int get_x86_regnum(const char *name)
+static int get_regnum(const struct dwarf_regs_idx *entries, size_t num_entries, const char *name)
 {
-	unsigned int i;
-
 	if (*name != '%')
 		return -EINVAL;
 
-	for (i = 0; i < ARRAY_SIZE(x86_regidx_table); i++)
-		if (!strcmp(x86_regidx_table[i].name, name + 1))
-			return x86_regidx_table[i].idx;
+	name++;
+	for (size_t i = 0; i < num_entries; i++) {
+		if (!strcmp(entries[i].name, name))
+			return entries[i].dwarf_regnum;
+	}
 	return -ENOENT;
 }
+
+int __get_dwarf_regnum_i386(const char *name)
+{
+	return get_regnum(i386_regidx_table, ARRAY_SIZE(i386_regidx_table), name);
+}
+
+int __get_dwarf_regnum_x86_64(const char *name)
+{
+	return get_regnum(x86_64_regidx_table, ARRAY_SIZE(x86_64_regidx_table), name);
+}
diff --git a/tools/perf/util/dwarf-regs.c b/tools/perf/util/dwarf-regs.c
index b2f37299147e..ef249dd589e3 100644
--- a/tools/perf/util/dwarf-regs.c
+++ b/tools/perf/util/dwarf-regs.c
@@ -92,9 +92,10 @@ int get_dwarf_regnum(const char *name, unsigned int machine, unsigned int flags
 	}
 	switch (machine) {
 	case EM_X86_64:
-		fallthrough;
+		reg = __get_dwarf_regnum_x86_64(name);
+		break;
 	case EM_386:
-		reg = get_x86_regnum(regname);
+		reg = __get_dwarf_regnum_i386(name);
 		break;
 	default:
 		pr_err("ELF MACHINE %x is not supported.\n", machine);
diff --git a/tools/perf/util/include/dwarf-regs.h b/tools/perf/util/include/dwarf-regs.h
index 015d1ade645f..bb5413b0fee4 100644
--- a/tools/perf/util/include/dwarf-regs.h
+++ b/tools/perf/util/include/dwarf-regs.h
@@ -99,7 +99,8 @@ const char *get_csky_regstr(unsigned int n, unsigned int flags);
  */
 const char *get_dwarf_regstr(unsigned int n, unsigned int machine, unsigned int flags);
 
-int get_x86_regnum(const char *name);
+int __get_dwarf_regnum_i386(const char *name);
+int __get_dwarf_regnum_x86_64(const char *name);
 
 /*
  * get_dwarf_regnum - Returns DWARF regnum from register name
-- 
2.52.0.457.g6b5491de43-goog


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

* [PATCH v1 12/23] perf dwarf-regs: Add get_dwarf_regnum_for_perf_regnum and use for x86 unwinding
  2026-01-17  5:28 [PATCH v1 00/23] perf dwarf/libdw extra support, speed and clean ups Ian Rogers
                   ` (10 preceding siblings ...)
  2026-01-17  5:28 ` [PATCH v1 11/23] perf dwarf-regs: Clean up x86 dwarf_regnum code Ian Rogers
@ 2026-01-17  5:28 ` Ian Rogers
  2026-01-17  5:42   ` Ian Rogers
  2026-01-17  5:28 ` [PATCH v1 13/23] perf dwarf-regs: Add basic get_dwarf_regnum for most architectures Ian Rogers
                   ` (10 subsequent siblings)
  22 siblings, 1 reply; 32+ messages in thread
From: Ian Rogers @ 2026-01-17  5:28 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
	Namhyung Kim, Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
	John Garry, Will Deacon, Leo Yan, Guo Ren, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Shimin Guo,
	Athira Rajeev, Stephen Brennan, Howard Chu, Thomas Falcon,
	Andi Kleen, Dr. David Alan Gilbert, Dmitry Vyukov,
	Krzysztof Łopatowski, Chun-Tse Shao, Aditya Bodkhe, Haibo Xu,
	Sergei Trofimovich, linux-kernel, linux-perf-users,
	linux-arm-kernel, linux-csky, linux-riscv, Mark Wielaard

Add a utility to map a perf register number to a dwarf register number
for a particular ELF machine type.

Create a generic unwind-libdw initial register initialization routine
that uses this function and thereby avoids arch specific
initialization. The unwind-libdw code does:
1) compute the maximum dwarf register from the set of sampled user registers,
2) allocates a set of dwarf registers,
3) copies the sample registers into the appropriate dwarf registers.

This generic solution is initially implemented for use with x86 as
only get_dwarf_regnum_for_perf_regnum support for x86 is currently present.

Signed-off-by: Ian Rogers <irogers@google.com>
---
 .../util/dwarf-regs-arch/dwarf-regs-x86.c     | 95 +++++++++++++++++++
 tools/perf/util/dwarf-regs.c                  | 55 +++++++++++
 tools/perf/util/include/dwarf-regs.h          |  8 ++
 tools/perf/util/unwind-libdw-arch/Build       |  1 -
 .../util/unwind-libdw-arch/unwind-libdw-x86.c | 54 -----------
 tools/perf/util/unwind-libdw.c                | 70 ++++++++++++--
 tools/perf/util/unwind-libdw.h                |  2 +-
 7 files changed, 222 insertions(+), 63 deletions(-)
 delete mode 100644 tools/perf/util/unwind-libdw-arch/unwind-libdw-x86.c

diff --git a/tools/perf/util/dwarf-regs-arch/dwarf-regs-x86.c b/tools/perf/util/dwarf-regs-arch/dwarf-regs-x86.c
index f0c42e4d7423..cadef120aeb4 100644
--- a/tools/perf/util/dwarf-regs-arch/dwarf-regs-x86.c
+++ b/tools/perf/util/dwarf-regs-arch/dwarf-regs-x86.c
@@ -10,6 +10,7 @@
 #include <string.h> /* for strcmp */
 #include <linux/kernel.h> /* for ARRAY_SIZE */
 #include <dwarf-regs.h>
+#include "../../../arch/x86/include/uapi/asm/perf_regs.h"
 
 struct dwarf_regs_idx {
 	const char *name;
@@ -163,3 +164,97 @@ int __get_dwarf_regnum_x86_64(const char *name)
 {
 	return get_regnum(x86_64_regidx_table, ARRAY_SIZE(x86_64_regidx_table), name);
 }
+
+int __get_dwarf_regnum_for_perf_regnum_i386(int perf_regnum)
+{
+	static const int dwarf_i386_regnums[] = {
+		[PERF_REG_X86_AX] = 0,
+		[PERF_REG_X86_BX] = 3,
+		[PERF_REG_X86_CX] = 1,
+		[PERF_REG_X86_DX] = 2,
+		[PERF_REG_X86_SI] = 6,
+		[PERF_REG_X86_DI] = 7,
+		[PERF_REG_X86_BP] = 5,
+		[PERF_REG_X86_SP] = 4,
+		[PERF_REG_X86_IP] = 8,
+		[PERF_REG_X86_FLAGS] = 9,
+		[PERF_REG_X86_CS] = 41,
+		[PERF_REG_X86_SS] = 42,
+		[PERF_REG_X86_DS] = 43,
+		[PERF_REG_X86_ES] = 40,
+		[PERF_REG_X86_FS] = 44,
+		[PERF_REG_X86_GS] = 45,
+		[PERF_REG_X86_XMM0] = 21,
+		[PERF_REG_X86_XMM1] = 22,
+		[PERF_REG_X86_XMM2] = 23,
+		[PERF_REG_X86_XMM3] = 24,
+		[PERF_REG_X86_XMM4] = 25,
+		[PERF_REG_X86_XMM5] = 26,
+		[PERF_REG_X86_XMM6] = 27,
+		[PERF_REG_X86_XMM7] = 28,
+	};
+
+	if (perf_regnum == 0)
+		return 0;
+
+	if (perf_regnum <  0 || perf_regnum > (int)ARRAY_SIZE(dwarf_i386_regnums) ||
+	    dwarf_i386_regnums[perf_regnum] == 0)
+		return -ENOENT;
+
+	return dwarf_i386_regnums[perf_regnum];
+}
+
+int __get_dwarf_regnum_for_perf_regnum_x86_64(int perf_regnum)
+{
+	static const int dwarf_x86_64_regnums[] = {
+		[PERF_REG_X86_AX] = 0,
+		[PERF_REG_X86_BX] = 3,
+		[PERF_REG_X86_CX] = 2,
+		[PERF_REG_X86_DX] = 1,
+		[PERF_REG_X86_SI] = 4,
+		[PERF_REG_X86_DI] = 5,
+		[PERF_REG_X86_BP] = 6,
+		[PERF_REG_X86_SP] = 7,
+		[PERF_REG_X86_IP] = 16,
+		[PERF_REG_X86_FLAGS] = 49,
+		[PERF_REG_X86_CS] = 51,
+		[PERF_REG_X86_SS] = 52,
+		[PERF_REG_X86_DS] = 53,
+		[PERF_REG_X86_ES] = 50,
+		[PERF_REG_X86_FS] = 54,
+		[PERF_REG_X86_GS] = 55,
+		[PERF_REG_X86_R8] = 8,
+		[PERF_REG_X86_R9] = 9,
+		[PERF_REG_X86_R10] = 10,
+		[PERF_REG_X86_R11] = 11,
+		[PERF_REG_X86_R12] = 12,
+		[PERF_REG_X86_R13] = 13,
+		[PERF_REG_X86_R14] = 14,
+		[PERF_REG_X86_R15] = 15,
+		[PERF_REG_X86_XMM0] = 17,
+		[PERF_REG_X86_XMM1] = 18,
+		[PERF_REG_X86_XMM2] = 19,
+		[PERF_REG_X86_XMM3] = 20,
+		[PERF_REG_X86_XMM4] = 21,
+		[PERF_REG_X86_XMM5] = 22,
+		[PERF_REG_X86_XMM6] = 23,
+		[PERF_REG_X86_XMM7] = 24,
+		[PERF_REG_X86_XMM8] = 25,
+		[PERF_REG_X86_XMM9] = 26,
+		[PERF_REG_X86_XMM10] = 27,
+		[PERF_REG_X86_XMM11] = 28,
+		[PERF_REG_X86_XMM12] = 29,
+		[PERF_REG_X86_XMM13] = 30,
+		[PERF_REG_X86_XMM14] = 31,
+		[PERF_REG_X86_XMM15] = 32,
+	};
+
+	if (perf_regnum == 0)
+		return 0;
+
+	if (perf_regnum <  0 || perf_regnum > (int)ARRAY_SIZE(dwarf_x86_64_regnums) ||
+	    dwarf_x86_64_regnums[perf_regnum] == 0)
+		return -ENOENT;
+
+	return dwarf_x86_64_regnums[perf_regnum];
+}
diff --git a/tools/perf/util/dwarf-regs.c b/tools/perf/util/dwarf-regs.c
index ef249dd589e3..1f7d892612df 100644
--- a/tools/perf/util/dwarf-regs.c
+++ b/tools/perf/util/dwarf-regs.c
@@ -103,3 +103,58 @@ int get_dwarf_regnum(const char *name, unsigned int machine, unsigned int flags
 	free(regname);
 	return reg;
 }
+
+static int get_libdw_frame_nregs(unsigned int machine, unsigned int flags __maybe_unused)
+{
+	switch (machine) {
+	case EM_X86_64:
+		return 17;
+	case EM_386:
+		return 9;
+	case EM_ARM:
+		return 16;
+	case EM_AARCH64:
+		return 97;
+	case EM_CSKY:
+		return 38;
+	case EM_S390:
+		return 32;
+	case EM_PPC:
+	case EM_PPC64:
+		return 145;
+	case EM_RISCV:
+		return 66;
+	case EM_SPARC:
+	case EM_SPARCV9:
+		return 103;
+	case EM_LOONGARCH:
+		return 74;
+	default:
+		return 0;
+	}
+}
+
+int get_dwarf_regnum_for_perf_regnum(int perf_regnum, unsigned int machine,
+				     unsigned int flags, bool only_libdw_supported)
+{
+	int reg;
+
+	switch (machine) {
+	case EM_X86_64:
+		reg = __get_dwarf_regnum_for_perf_regnum_x86_64(perf_regnum);
+		break;
+	case EM_386:
+		reg = __get_dwarf_regnum_for_perf_regnum_i386(perf_regnum);
+		break;
+	default:
+		pr_err("ELF MACHINE %x is not supported.\n", machine);
+		return -ENOENT;
+	}
+	if (reg >= 0 && only_libdw_supported) {
+		int nregs = get_libdw_frame_nregs(machine, flags);
+
+		if (reg >= nregs)
+			reg = -ENOENT;
+	}
+	return reg;
+}
diff --git a/tools/perf/util/include/dwarf-regs.h b/tools/perf/util/include/dwarf-regs.h
index bb5413b0fee4..00881f1d45d6 100644
--- a/tools/perf/util/include/dwarf-regs.h
+++ b/tools/perf/util/include/dwarf-regs.h
@@ -101,6 +101,8 @@ const char *get_dwarf_regstr(unsigned int n, unsigned int machine, unsigned int
 
 int __get_dwarf_regnum_i386(const char *name);
 int __get_dwarf_regnum_x86_64(const char *name);
+int __get_dwarf_regnum_for_perf_regnum_i386(int perf_regnum);
+int __get_dwarf_regnum_for_perf_regnum_x86_64(int perf_regnum);
 
 /*
  * get_dwarf_regnum - Returns DWARF regnum from register name
@@ -109,6 +111,12 @@ int __get_dwarf_regnum_x86_64(const char *name);
  */
 int get_dwarf_regnum(const char *name, unsigned int machine, unsigned int flags);
 
+/*
+ * get_dwarf_regnum - Returns DWARF regnum from perf register number.
+ */
+int get_dwarf_regnum_for_perf_regnum(int perf_regnum, unsigned int machine, unsigned int flags,
+				     bool only_libdw_supported);
+
 void get_powerpc_regs(u32 raw_insn, int is_source, struct annotated_op_loc *op_loc);
 
 #else /* HAVE_LIBDW_SUPPORT */
diff --git a/tools/perf/util/unwind-libdw-arch/Build b/tools/perf/util/unwind-libdw-arch/Build
index ef17a83a7813..5b5682029953 100644
--- a/tools/perf/util/unwind-libdw-arch/Build
+++ b/tools/perf/util/unwind-libdw-arch/Build
@@ -1,4 +1,3 @@
-perf-util-y += unwind-libdw-x86.o
 perf-util-y += unwind-libdw-arm.o
 perf-util-y += unwind-libdw-arm64.o
 perf-util-y += unwind-libdw-csky.o
diff --git a/tools/perf/util/unwind-libdw-arch/unwind-libdw-x86.c b/tools/perf/util/unwind-libdw-arch/unwind-libdw-x86.c
deleted file mode 100644
index dd27545a4a68..000000000000
--- a/tools/perf/util/unwind-libdw-arch/unwind-libdw-x86.c
+++ /dev/null
@@ -1,54 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-#include <elfutils/libdwfl.h>
-#include "../arch/x86/include/uapi/asm/perf_regs.h"
-#include "util/unwind-libdw.h"
-#include "util/perf_regs.h"
-#include "util/sample.h"
-
-bool libdw_set_initial_registers_x86(Dwfl_Thread *thread, void *arg)
-{
-	struct unwind_info *ui = arg;
-	struct regs_dump *user_regs = perf_sample__user_regs(ui->sample);
-	Dwarf_Word dwarf_regs[17];
-	unsigned nregs;
-
-#define REG(r) ({						\
-	Dwarf_Word val = 0;					\
-	perf_reg_value(&val, user_regs, PERF_REG_X86_##r);	\
-	val;							\
-})
-
-	if (user_regs->abi == PERF_SAMPLE_REGS_ABI_32) {
-		dwarf_regs[0] = REG(AX);
-		dwarf_regs[1] = REG(CX);
-		dwarf_regs[2] = REG(DX);
-		dwarf_regs[3] = REG(BX);
-		dwarf_regs[4] = REG(SP);
-		dwarf_regs[5] = REG(BP);
-		dwarf_regs[6] = REG(SI);
-		dwarf_regs[7] = REG(DI);
-		dwarf_regs[8] = REG(IP);
-		nregs = 9;
-	} else {
-		dwarf_regs[0]  = REG(AX);
-		dwarf_regs[1]  = REG(DX);
-		dwarf_regs[2]  = REG(CX);
-		dwarf_regs[3]  = REG(BX);
-		dwarf_regs[4]  = REG(SI);
-		dwarf_regs[5]  = REG(DI);
-		dwarf_regs[6]  = REG(BP);
-		dwarf_regs[7]  = REG(SP);
-		dwarf_regs[8]  = REG(R8);
-		dwarf_regs[9]  = REG(R9);
-		dwarf_regs[10] = REG(R10);
-		dwarf_regs[11] = REG(R11);
-		dwarf_regs[12] = REG(R12);
-		dwarf_regs[13] = REG(R13);
-		dwarf_regs[14] = REG(R14);
-		dwarf_regs[15] = REG(R15);
-		dwarf_regs[16] = REG(IP);
-		nregs = 17;
-	}
-
-	return dwfl_thread_state_registers(thread, 0, nregs, dwarf_regs);
-}
diff --git a/tools/perf/util/unwind-libdw.c b/tools/perf/util/unwind-libdw.c
index c25190cdceb4..055dab921442 100644
--- a/tools/perf/util/unwind-libdw.c
+++ b/tools/perf/util/unwind-libdw.c
@@ -6,6 +6,7 @@
 #include <errno.h>
 #include "debug.h"
 #include "dso.h"
+#include <dwarf-regs.h>
 #include "unwind.h"
 #include "unwind-libdw.h"
 #include "machine.h"
@@ -225,6 +226,59 @@ static bool memory_read(Dwfl *dwfl __maybe_unused, Dwarf_Addr addr, Dwarf_Word *
 	return true;
 }
 
+static bool libdw_set_initial_registers_generic(Dwfl_Thread *thread, void *arg)
+{
+	struct unwind_info *ui = arg;
+	struct regs_dump *user_regs = perf_sample__user_regs(ui->sample);
+	Dwarf_Word *dwarf_regs;
+	int max_dwarf_reg = 0;
+	bool ret;
+	uint16_t e_machine = ui->e_machine;
+	int e_flags = 0;
+	uint64_t ip_perf_reg = perf_arch_reg_ip(e_machine);
+	Dwarf_Word val = 0;
+
+
+	/*
+	 * For every possible perf register in the bitmap determine the dwarf
+	 * register and use to compute the max.
+	 */
+	for (int perf_reg = 0; perf_reg < 64; perf_reg++) {
+		if (user_regs->mask & (1ULL << perf_reg)) {
+			int dwarf_reg =
+				get_dwarf_regnum_for_perf_regnum(perf_reg, e_machine,
+								 e_flags,
+								 /*only_libdw_supported=*/true);
+			if (dwarf_reg > max_dwarf_reg)
+				max_dwarf_reg = dwarf_reg;
+		}
+	}
+
+	dwarf_regs = calloc(max_dwarf_reg + 1, sizeof(*dwarf_regs));
+	if (!dwarf_regs)
+		return false;
+
+	for (int perf_reg = 0; perf_reg < 64; perf_reg++) {
+		if (user_regs->mask & (1ULL << perf_reg)) {
+			int dwarf_reg =
+				get_dwarf_regnum_for_perf_regnum(perf_reg, e_machine,
+								 e_flags,
+								 /*only_libdw_supported=*/true);
+			if (dwarf_reg >= 0) {
+				val = 0;
+				if (perf_reg_value(&val, user_regs, perf_reg) == 0)
+					dwarf_regs[dwarf_reg] = val;
+			}
+		}
+	}
+	if (perf_reg_value(&val, user_regs, ip_perf_reg) == 0)
+		dwfl_thread_state_register_pc(thread, val);
+
+	ret = dwfl_thread_state_registers(thread, 0, max_dwarf_reg + 1, dwarf_regs);
+	free(dwarf_regs);
+	return ret;
+}
+
 #define DEFINE_DWFL_THREAD_CALLBACKS(arch)                           \
 static const Dwfl_Thread_Callbacks callbacks_##arch = {              \
 	.next_thread           = next_thread,                        \
@@ -232,7 +286,12 @@ static const Dwfl_Thread_Callbacks callbacks_##arch = {              \
 	.set_initial_registers = libdw_set_initial_registers_##arch, \
 }
 
-DEFINE_DWFL_THREAD_CALLBACKS(x86);
+static const Dwfl_Thread_Callbacks callbacks_generic = {
+	.next_thread           = next_thread,
+	.memory_read           = memory_read,
+	.set_initial_registers = libdw_set_initial_registers_generic,
+};
+
 DEFINE_DWFL_THREAD_CALLBACKS(arm);
 DEFINE_DWFL_THREAD_CALLBACKS(arm64);
 DEFINE_DWFL_THREAD_CALLBACKS(csky);
@@ -257,12 +316,8 @@ static const Dwfl_Thread_Callbacks *get_thread_callbacks(const char *arch)
 		return &callbacks_riscv;
 	else if (!strcmp(arch, "s390"))
 		return &callbacks_s390;
-	else if (!strcmp(arch, "x86"))
-		return &callbacks_x86;
 
-	pr_err("Fail to get thread callbacks for arch %s, returns NULL\n",
-	       arch);
-	return NULL;
+	return &callbacks_generic;
 }
 
 static int
@@ -301,6 +356,7 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
 			bool best_effort)
 {
 	struct machine *machine = maps__machine(thread__maps(thread));
+	uint16_t e_machine = thread__e_machine(thread, machine);
 	struct unwind_info *ui, ui_buf = {
 		.sample		= data,
 		.thread		= thread,
@@ -308,9 +364,9 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
 		.cb		= cb,
 		.arg		= arg,
 		.max_stack	= max_stack,
+		.e_machine	= e_machine,
 		.best_effort    = best_effort
 	};
-	uint16_t e_machine = thread__e_machine(thread, machine);
 	const char *arch = perf_env__arch(machine->env);
 	Dwarf_Word ip;
 	int err = -EINVAL, i;
diff --git a/tools/perf/util/unwind-libdw.h b/tools/perf/util/unwind-libdw.h
index 574b29848cce..496e5898e7ef 100644
--- a/tools/perf/util/unwind-libdw.h
+++ b/tools/perf/util/unwind-libdw.h
@@ -9,7 +9,6 @@ struct machine;
 struct perf_sample;
 struct thread;
 
-bool libdw_set_initial_registers_x86(Dwfl_Thread *thread, void *arg);
 bool libdw_set_initial_registers_arm(Dwfl_Thread *thread, void *arg);
 bool libdw_set_initial_registers_arm64(Dwfl_Thread *thread, void *arg);
 bool libdw_set_initial_registers_csky(Dwfl_Thread *thread, void *arg);
@@ -28,6 +27,7 @@ struct unwind_info {
 	void			*arg;
 	int			max_stack;
 	int			idx;
+	uint16_t		e_machine;
 	bool			best_effort;
 	struct unwind_entry	entries[];
 };
-- 
2.52.0.457.g6b5491de43-goog


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

* [PATCH v1 13/23] perf dwarf-regs: Add basic get_dwarf_regnum for most architectures
  2026-01-17  5:28 [PATCH v1 00/23] perf dwarf/libdw extra support, speed and clean ups Ian Rogers
                   ` (11 preceding siblings ...)
  2026-01-17  5:28 ` [PATCH v1 12/23] perf dwarf-regs: Add get_dwarf_regnum_for_perf_regnum and use for x86 unwinding Ian Rogers
@ 2026-01-17  5:28 ` Ian Rogers
  2026-01-17  5:28 ` [PATCH v1 14/23] perf dwarf-regs: Add ARM perf to dwarf register number mapping functions Ian Rogers
                   ` (9 subsequent siblings)
  22 siblings, 0 replies; 32+ messages in thread
From: Ian Rogers @ 2026-01-17  5:28 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
	Namhyung Kim, Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
	John Garry, Will Deacon, Leo Yan, Guo Ren, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Shimin Guo,
	Athira Rajeev, Stephen Brennan, Howard Chu, Thomas Falcon,
	Andi Kleen, Dr. David Alan Gilbert, Dmitry Vyukov,
	Krzysztof Łopatowski, Chun-Tse Shao, Aditya Bodkhe, Haibo Xu,
	Sergei Trofimovich, linux-kernel, linux-perf-users,
	linux-arm-kernel, linux-csky, linux-riscv, Mark Wielaard

Add a basic get_dwarf_regnum implementation for most architectures by
using the get_dwarf_regstr tables and returning the index of the name
within the table.

Some minor name and constification clean up for csky.

Signed-off-by: Ian Rogers <irogers@google.com>
---
 .../util/dwarf-regs-arch/dwarf-regs-csky.c    | 24 +++++++-
 tools/perf/util/dwarf-regs.c                  | 58 +++++++++++++++++--
 tools/perf/util/include/dwarf-regs.h          |  5 +-
 3 files changed, 78 insertions(+), 9 deletions(-)

diff --git a/tools/perf/util/dwarf-regs-arch/dwarf-regs-csky.c b/tools/perf/util/dwarf-regs-arch/dwarf-regs-csky.c
index d38ef1f07f3e..86394ed46397 100644
--- a/tools/perf/util/dwarf-regs-arch/dwarf-regs-csky.c
+++ b/tools/perf/util/dwarf-regs-arch/dwarf-regs-csky.c
@@ -2,11 +2,12 @@
 // Copyright (C) 2019 Hangzhou C-SKY Microsystems co.,ltd.
 // Mapping of DWARF debug register numbers into register names.
 
+#include <errno.h>
 #include <stddef.h>
 #include <dwarf-regs.h>
 
 #define CSKY_ABIV2_MAX_REGS 73
-const char *csky_dwarf_regs_table_abiv2[CSKY_ABIV2_MAX_REGS] = {
+static const char * const csky_dwarf_regs_table_abiv2[CSKY_ABIV2_MAX_REGS] = {
 	/* r0 ~ r8 */
 	"%a0", "%a1", "%a2", "%a3", "%regs0", "%regs1", "%regs2", "%regs3",
 	/* r9 ~ r15 */
@@ -27,7 +28,7 @@ const char *csky_dwarf_regs_table_abiv2[CSKY_ABIV2_MAX_REGS] = {
 };
 
 #define CSKY_ABIV1_MAX_REGS 57
-const char *csky_dwarf_regs_table_abiv1[CSKY_ABIV1_MAX_REGS] = {
+static const char * const csky_dwarf_regs_table_abiv1[CSKY_ABIV1_MAX_REGS] = {
 	/* r0 ~ r8 */
 	"%sp", "%regs9", "%a0", "%a1", "%a2", "%a3", "%regs0", "%regs1",
 	/* r9 ~ r15 */
@@ -41,10 +42,27 @@ const char *csky_dwarf_regs_table_abiv1[CSKY_ABIV1_MAX_REGS] = {
 	"%epc",
 };
 
-const char *get_csky_regstr(unsigned int n, unsigned int flags)
+const char *__get_csky_regstr(unsigned int n, unsigned int flags)
 {
 	if (flags & EF_CSKY_ABIV2)
 		return (n < CSKY_ABIV2_MAX_REGS) ? csky_dwarf_regs_table_abiv2[n] : NULL;
 
 	return (n < CSKY_ABIV1_MAX_REGS) ? csky_dwarf_regs_table_abiv1[n] : NULL;
 }
+
+static int __get_dwarf_regnum(const char *const *regstr, size_t num_regstr, const char *name)
+{
+	for (size_t i = 0; i < num_regstr; i++) {
+		if (regstr[i] && !strcmp(regstr[i], name))
+			return i;
+	}
+	return -ENOENT;
+}
+
+int __get_csky_regnum(const char *name, unsigned int flags)
+{
+	if (flags & EF_CSKY_ABIV2)
+		return __get_dwarf_regnum(csky_dwarf_regs_table_abiv2, CSKY_ABIV2_MAX_REGS, name);
+
+	return __get_dwarf_regnum(csky_dwarf_regs_table_abiv1, CSKY_ABIV1_MAX_REGS, name);
+}
diff --git a/tools/perf/util/dwarf-regs.c b/tools/perf/util/dwarf-regs.c
index 1f7d892612df..dffa0c8bdd14 100644
--- a/tools/perf/util/dwarf-regs.c
+++ b/tools/perf/util/dwarf-regs.c
@@ -27,11 +27,11 @@
 #include "../arch/mips/include/dwarf-regs-table.h"
 #include "../arch/loongarch/include/dwarf-regs-table.h"
 
-#define __get_dwarf_regstr(tbl, n) (((n) < ARRAY_SIZE(tbl)) ? (tbl)[(n)] : NULL)
-
 /* Return architecture dependent register string (for kprobe-tracer) */
 const char *get_dwarf_regstr(unsigned int n, unsigned int machine, unsigned int flags)
 {
+	#define __get_dwarf_regstr(tbl, n) (((n) < ARRAY_SIZE(tbl)) ? (tbl)[(n)] : NULL)
+
 	if (machine == EM_NONE) {
 		/* Generic arch - use host arch */
 		machine = EM_HOST;
@@ -46,7 +46,7 @@ const char *get_dwarf_regstr(unsigned int n, unsigned int machine, unsigned int
 	case EM_AARCH64:
 		return __get_dwarf_regstr(aarch64_regstr_tbl, n);
 	case EM_CSKY:
-		return get_csky_regstr(n, flags);
+		return __get_csky_regstr(n, flags);
 	case EM_SH:
 		return __get_dwarf_regstr(sh_regstr_tbl, n);
 	case EM_S390:
@@ -69,15 +69,28 @@ const char *get_dwarf_regstr(unsigned int n, unsigned int machine, unsigned int
 		pr_err("ELF MACHINE %x is not supported.\n", machine);
 	}
 	return NULL;
+
+	#undef __get_dwarf_regstr
+}
+
+static int __get_dwarf_regnum(const char *const *regstr, size_t num_regstr, const char *name)
+{
+	for (size_t i = 0; i < num_regstr; i++) {
+		if (regstr[i] && !strcmp(regstr[i], name))
+			return i;
+	}
+	return -ENOENT;
 }
 
 /* Return DWARF register number from architecture register name */
-int get_dwarf_regnum(const char *name, unsigned int machine, unsigned int flags __maybe_unused)
+int get_dwarf_regnum(const char *name, unsigned int machine, unsigned int flags)
 {
 	char *regname = strdup(name);
 	int reg = -1;
 	char *p;
 
+	#define _get_dwarf_regnum(tbl, name) __get_dwarf_regnum(tbl, ARRAY_SIZE(tbl), name)
+
 	if (regname == NULL)
 		return -EINVAL;
 
@@ -97,11 +110,48 @@ int get_dwarf_regnum(const char *name, unsigned int machine, unsigned int flags
 	case EM_386:
 		reg = __get_dwarf_regnum_i386(name);
 		break;
+	case EM_ARM:
+		reg = _get_dwarf_regnum(arm_regstr_tbl, name);
+		break;
+	case EM_AARCH64:
+		reg = _get_dwarf_regnum(aarch64_regstr_tbl, name);
+		break;
+	case EM_CSKY:
+		reg = __get_csky_regnum(name, flags);
+		break;
+	case EM_SH:
+		reg = _get_dwarf_regnum(sh_regstr_tbl, name);
+		break;
+	case EM_S390:
+		reg = _get_dwarf_regnum(s390_regstr_tbl, name);
+		break;
+	case EM_PPC:
+	case EM_PPC64:
+		reg = _get_dwarf_regnum(powerpc_regstr_tbl, name);
+		break;
+	case EM_RISCV:
+		reg = _get_dwarf_regnum(riscv_regstr_tbl, name);
+		break;
+	case EM_SPARC:
+	case EM_SPARCV9:
+		reg = _get_dwarf_regnum(sparc_regstr_tbl, name);
+		break;
+	case EM_XTENSA:
+		reg = _get_dwarf_regnum(xtensa_regstr_tbl, name);
+		break;
+	case EM_MIPS:
+		reg = _get_dwarf_regnum(mips_regstr_tbl, name);
+		break;
+	case EM_LOONGARCH:
+		reg = _get_dwarf_regnum(loongarch_regstr_tbl, name);
+		break;
 	default:
 		pr_err("ELF MACHINE %x is not supported.\n", machine);
 	}
 	free(regname);
 	return reg;
+
+	#undef _get_dwarf_regnum
 }
 
 static int get_libdw_frame_nregs(unsigned int machine, unsigned int flags __maybe_unused)
diff --git a/tools/perf/util/include/dwarf-regs.h b/tools/perf/util/include/dwarf-regs.h
index 00881f1d45d6..a120c97a5fac 100644
--- a/tools/perf/util/include/dwarf-regs.h
+++ b/tools/perf/util/include/dwarf-regs.h
@@ -89,8 +89,6 @@
 #define DWARF_REG_FB  0xd3affb /* random number */
 
 #ifdef HAVE_LIBDW_SUPPORT
-const char *get_csky_regstr(unsigned int n, unsigned int flags);
-
 /**
  * get_dwarf_regstr() - Returns ftrace register string from DWARF regnum.
  * @n: DWARF register number.
@@ -99,6 +97,9 @@ const char *get_csky_regstr(unsigned int n, unsigned int flags);
  */
 const char *get_dwarf_regstr(unsigned int n, unsigned int machine, unsigned int flags);
 
+const char *__get_csky_regstr(unsigned int n, unsigned int flags);
+int __get_csky_regnum(const char *name, unsigned int flags);
+
 int __get_dwarf_regnum_i386(const char *name);
 int __get_dwarf_regnum_x86_64(const char *name);
 int __get_dwarf_regnum_for_perf_regnum_i386(int perf_regnum);
-- 
2.52.0.457.g6b5491de43-goog


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

* [PATCH v1 14/23] perf dwarf-regs: Add ARM perf to dwarf register number mapping functions
  2026-01-17  5:28 [PATCH v1 00/23] perf dwarf/libdw extra support, speed and clean ups Ian Rogers
                   ` (12 preceding siblings ...)
  2026-01-17  5:28 ` [PATCH v1 13/23] perf dwarf-regs: Add basic get_dwarf_regnum for most architectures Ian Rogers
@ 2026-01-17  5:28 ` Ian Rogers
  2026-01-17  5:28 ` [PATCH v1 15/23] perf dwarf-regs: Add csky " Ian Rogers
                   ` (8 subsequent siblings)
  22 siblings, 0 replies; 32+ messages in thread
From: Ian Rogers @ 2026-01-17  5:28 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
	Namhyung Kim, Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
	John Garry, Will Deacon, Leo Yan, Guo Ren, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Shimin Guo,
	Athira Rajeev, Stephen Brennan, Howard Chu, Thomas Falcon,
	Andi Kleen, Dr. David Alan Gilbert, Dmitry Vyukov,
	Krzysztof Łopatowski, Chun-Tse Shao, Aditya Bodkhe, Haibo Xu,
	Sergei Trofimovich, linux-kernel, linux-perf-users,
	linux-arm-kernel, linux-csky, linux-riscv, Mark Wielaard

These functions allow the generic initial register state code in
unwind-libdw to be used.

Signed-off-by: Ian Rogers <irogers@google.com>
---
 tools/perf/util/dwarf-regs-arch/Build         |  2 +
 .../util/dwarf-regs-arch/dwarf-regs-arm.c     | 12 ++++
 .../util/dwarf-regs-arch/dwarf-regs-arm64.c   | 12 ++++
 tools/perf/util/dwarf-regs.c                  |  6 ++
 tools/perf/util/include/dwarf-regs.h          |  3 +
 tools/perf/util/unwind-libdw-arch/Build       |  2 -
 .../util/unwind-libdw-arch/unwind-libdw-arm.c | 39 ------------
 .../unwind-libdw-arch/unwind-libdw-arm64.c    | 61 -------------------
 tools/perf/util/unwind-libdw.c                |  8 +--
 tools/perf/util/unwind-libdw.h                |  2 -
 10 files changed, 36 insertions(+), 111 deletions(-)
 create mode 100644 tools/perf/util/dwarf-regs-arch/dwarf-regs-arm.c
 create mode 100644 tools/perf/util/dwarf-regs-arch/dwarf-regs-arm64.c
 delete mode 100644 tools/perf/util/unwind-libdw-arch/unwind-libdw-arm.c
 delete mode 100644 tools/perf/util/unwind-libdw-arch/unwind-libdw-arm64.c

diff --git a/tools/perf/util/dwarf-regs-arch/Build b/tools/perf/util/dwarf-regs-arch/Build
index 98bec0032606..3f19a9ec47c7 100644
--- a/tools/perf/util/dwarf-regs-arch/Build
+++ b/tools/perf/util/dwarf-regs-arch/Build
@@ -1,3 +1,5 @@
+perf-util-$(CONFIG_LIBDW) += dwarf-regs-arm64.o
+perf-util-$(CONFIG_LIBDW) += dwarf-regs-arm.o
 perf-util-$(CONFIG_LIBDW) += dwarf-regs-csky.o
 perf-util-$(CONFIG_LIBDW) += dwarf-regs-powerpc.o
 perf-util-$(CONFIG_LIBDW) += dwarf-regs-x86.o
diff --git a/tools/perf/util/dwarf-regs-arch/dwarf-regs-arm.c b/tools/perf/util/dwarf-regs-arch/dwarf-regs-arm.c
new file mode 100644
index 000000000000..42c6c0635612
--- /dev/null
+++ b/tools/perf/util/dwarf-regs-arch/dwarf-regs-arm.c
@@ -0,0 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <errno.h>
+#include <dwarf-regs.h>
+#include "../../../arch/arm/include/uapi/asm/perf_regs.h"
+
+int __get_dwarf_regnum_for_perf_regnum_arm(int perf_regnum)
+{
+	if (perf_regnum < 0 || perf_regnum >= PERF_REG_ARM_MAX)
+		return -ENOENT;
+
+	return perf_regnum;
+}
diff --git a/tools/perf/util/dwarf-regs-arch/dwarf-regs-arm64.c b/tools/perf/util/dwarf-regs-arch/dwarf-regs-arm64.c
new file mode 100644
index 000000000000..593ca7d4fccc
--- /dev/null
+++ b/tools/perf/util/dwarf-regs-arch/dwarf-regs-arm64.c
@@ -0,0 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <errno.h>
+#include <dwarf-regs.h>
+#include "../../../arch/arm64/include/uapi/asm/perf_regs.h"
+
+int __get_dwarf_regnum_for_perf_regnum_arm64(int perf_regnum)
+{
+	if (perf_regnum < 0 || perf_regnum >= PERF_REG_ARM64_MAX)
+		return -ENOENT;
+
+	return perf_regnum;
+}
diff --git a/tools/perf/util/dwarf-regs.c b/tools/perf/util/dwarf-regs.c
index dffa0c8bdd14..c472ec5e4d1a 100644
--- a/tools/perf/util/dwarf-regs.c
+++ b/tools/perf/util/dwarf-regs.c
@@ -196,6 +196,12 @@ int get_dwarf_regnum_for_perf_regnum(int perf_regnum, unsigned int machine,
 	case EM_386:
 		reg = __get_dwarf_regnum_for_perf_regnum_i386(perf_regnum);
 		break;
+	case EM_ARM:
+		reg = __get_dwarf_regnum_for_perf_regnum_arm(perf_regnum);
+		break;
+	case EM_AARCH64:
+		reg = __get_dwarf_regnum_for_perf_regnum_arm64(perf_regnum);
+		break;
 	default:
 		pr_err("ELF MACHINE %x is not supported.\n", machine);
 		return -ENOENT;
diff --git a/tools/perf/util/include/dwarf-regs.h b/tools/perf/util/include/dwarf-regs.h
index a120c97a5fac..a52df8d1b138 100644
--- a/tools/perf/util/include/dwarf-regs.h
+++ b/tools/perf/util/include/dwarf-regs.h
@@ -105,6 +105,9 @@ int __get_dwarf_regnum_x86_64(const char *name);
 int __get_dwarf_regnum_for_perf_regnum_i386(int perf_regnum);
 int __get_dwarf_regnum_for_perf_regnum_x86_64(int perf_regnum);
 
+int __get_dwarf_regnum_for_perf_regnum_arm(int perf_regnum);
+int __get_dwarf_regnum_for_perf_regnum_arm64(int perf_regnum);
+
 /*
  * get_dwarf_regnum - Returns DWARF regnum from register name
  * name: architecture register name
diff --git a/tools/perf/util/unwind-libdw-arch/Build b/tools/perf/util/unwind-libdw-arch/Build
index 5b5682029953..79c3bbdc2dee 100644
--- a/tools/perf/util/unwind-libdw-arch/Build
+++ b/tools/perf/util/unwind-libdw-arch/Build
@@ -1,5 +1,3 @@
-perf-util-y += unwind-libdw-arm.o
-perf-util-y += unwind-libdw-arm64.o
 perf-util-y += unwind-libdw-csky.o
 perf-util-y += unwind-libdw-loongarch.o
 perf-util-y += unwind-libdw-powerpc.o
diff --git a/tools/perf/util/unwind-libdw-arch/unwind-libdw-arm.c b/tools/perf/util/unwind-libdw-arch/unwind-libdw-arm.c
deleted file mode 100644
index 56e9b5975bcc..000000000000
--- a/tools/perf/util/unwind-libdw-arch/unwind-libdw-arm.c
+++ /dev/null
@@ -1,39 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-#include <elfutils/libdwfl.h>
-#include "../arch/arm/include/uapi/asm/perf_regs.h"
-#include "util/unwind-libdw.h"
-#include "util/perf_regs.h"
-#include "util/sample.h"
-
-bool libdw_set_initial_registers_arm(Dwfl_Thread *thread, void *arg)
-{
-	struct unwind_info *ui = arg;
-	struct regs_dump *user_regs = perf_sample__user_regs(ui->sample);
-	Dwarf_Word dwarf_regs[PERF_REG_ARM_MAX];
-
-#define REG(r) ({						\
-	Dwarf_Word val = 0;					\
-	perf_reg_value(&val, user_regs, PERF_REG_ARM_##r);	\
-	val;							\
-})
-
-	dwarf_regs[0]  = REG(R0);
-	dwarf_regs[1]  = REG(R1);
-	dwarf_regs[2]  = REG(R2);
-	dwarf_regs[3]  = REG(R3);
-	dwarf_regs[4]  = REG(R4);
-	dwarf_regs[5]  = REG(R5);
-	dwarf_regs[6]  = REG(R6);
-	dwarf_regs[7]  = REG(R7);
-	dwarf_regs[8]  = REG(R8);
-	dwarf_regs[9]  = REG(R9);
-	dwarf_regs[10] = REG(R10);
-	dwarf_regs[11] = REG(FP);
-	dwarf_regs[12] = REG(IP);
-	dwarf_regs[13] = REG(SP);
-	dwarf_regs[14] = REG(LR);
-	dwarf_regs[15] = REG(PC);
-
-	return dwfl_thread_state_registers(thread, 0, PERF_REG_ARM_MAX,
-					   dwarf_regs);
-}
diff --git a/tools/perf/util/unwind-libdw-arch/unwind-libdw-arm64.c b/tools/perf/util/unwind-libdw-arch/unwind-libdw-arm64.c
deleted file mode 100644
index 29b6833e036c..000000000000
--- a/tools/perf/util/unwind-libdw-arch/unwind-libdw-arm64.c
+++ /dev/null
@@ -1,61 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-#include <elfutils/libdwfl.h>
-#include "../arch/arm64/include/uapi/asm/perf_regs.h"
-#include "util/unwind-libdw.h"
-#include "util/perf_regs.h"
-#include "util/sample.h"
-
-bool libdw_set_initial_registers_arm64(Dwfl_Thread *thread, void *arg)
-{
-	struct unwind_info *ui = arg;
-	struct regs_dump *user_regs = perf_sample__user_regs(ui->sample);
-	Dwarf_Word dwarf_regs[PERF_REG_ARM64_MAX], dwarf_pc;
-
-#define REG(r) ({						\
-	Dwarf_Word val = 0;					\
-	perf_reg_value(&val, user_regs, PERF_REG_ARM64_##r);	\
-	val;							\
-})
-
-	dwarf_regs[0]  = REG(X0);
-	dwarf_regs[1]  = REG(X1);
-	dwarf_regs[2]  = REG(X2);
-	dwarf_regs[3]  = REG(X3);
-	dwarf_regs[4]  = REG(X4);
-	dwarf_regs[5]  = REG(X5);
-	dwarf_regs[6]  = REG(X6);
-	dwarf_regs[7]  = REG(X7);
-	dwarf_regs[8]  = REG(X8);
-	dwarf_regs[9]  = REG(X9);
-	dwarf_regs[10] = REG(X10);
-	dwarf_regs[11] = REG(X11);
-	dwarf_regs[12] = REG(X12);
-	dwarf_regs[13] = REG(X13);
-	dwarf_regs[14] = REG(X14);
-	dwarf_regs[15] = REG(X15);
-	dwarf_regs[16] = REG(X16);
-	dwarf_regs[17] = REG(X17);
-	dwarf_regs[18] = REG(X18);
-	dwarf_regs[19] = REG(X19);
-	dwarf_regs[20] = REG(X20);
-	dwarf_regs[21] = REG(X21);
-	dwarf_regs[22] = REG(X22);
-	dwarf_regs[23] = REG(X23);
-	dwarf_regs[24] = REG(X24);
-	dwarf_regs[25] = REG(X25);
-	dwarf_regs[26] = REG(X26);
-	dwarf_regs[27] = REG(X27);
-	dwarf_regs[28] = REG(X28);
-	dwarf_regs[29] = REG(X29);
-	dwarf_regs[30] = REG(LR);
-	dwarf_regs[31] = REG(SP);
-
-	if (!dwfl_thread_state_registers(thread, 0, PERF_REG_ARM64_MAX,
-					 dwarf_regs))
-		return false;
-
-	dwarf_pc = REG(PC);
-	dwfl_thread_state_register_pc(thread, dwarf_pc);
-
-	return true;
-}
diff --git a/tools/perf/util/unwind-libdw.c b/tools/perf/util/unwind-libdw.c
index 055dab921442..8f291f9f9469 100644
--- a/tools/perf/util/unwind-libdw.c
+++ b/tools/perf/util/unwind-libdw.c
@@ -292,8 +292,6 @@ static const Dwfl_Thread_Callbacks callbacks_generic = {
 	.set_initial_registers = libdw_set_initial_registers_generic,
 };
 
-DEFINE_DWFL_THREAD_CALLBACKS(arm);
-DEFINE_DWFL_THREAD_CALLBACKS(arm64);
 DEFINE_DWFL_THREAD_CALLBACKS(csky);
 DEFINE_DWFL_THREAD_CALLBACKS(loongarch);
 DEFINE_DWFL_THREAD_CALLBACKS(powerpc);
@@ -302,11 +300,7 @@ DEFINE_DWFL_THREAD_CALLBACKS(s390);
 
 static const Dwfl_Thread_Callbacks *get_thread_callbacks(const char *arch)
 {
-	if (!strcmp(arch, "arm"))
-		return &callbacks_arm;
-	else if (!strcmp(arch, "arm64"))
-		return &callbacks_arm64;
-	else if (!strcmp(arch, "csky"))
+	if (!strcmp(arch, "csky"))
 		return &callbacks_csky;
 	else if (!strcmp(arch, "loongarch"))
 		return &callbacks_loongarch;
diff --git a/tools/perf/util/unwind-libdw.h b/tools/perf/util/unwind-libdw.h
index 496e5898e7ef..fe3ae2a768ad 100644
--- a/tools/perf/util/unwind-libdw.h
+++ b/tools/perf/util/unwind-libdw.h
@@ -9,8 +9,6 @@ struct machine;
 struct perf_sample;
 struct thread;
 
-bool libdw_set_initial_registers_arm(Dwfl_Thread *thread, void *arg);
-bool libdw_set_initial_registers_arm64(Dwfl_Thread *thread, void *arg);
 bool libdw_set_initial_registers_csky(Dwfl_Thread *thread, void *arg);
 bool libdw_set_initial_registers_loongarch(Dwfl_Thread *thread, void *arg);
 bool libdw_set_initial_registers_mips(Dwfl_Thread *thread, void *arg);
-- 
2.52.0.457.g6b5491de43-goog


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

* [PATCH v1 15/23] perf dwarf-regs: Add csky perf to dwarf register number mapping functions
  2026-01-17  5:28 [PATCH v1 00/23] perf dwarf/libdw extra support, speed and clean ups Ian Rogers
                   ` (13 preceding siblings ...)
  2026-01-17  5:28 ` [PATCH v1 14/23] perf dwarf-regs: Add ARM perf to dwarf register number mapping functions Ian Rogers
@ 2026-01-17  5:28 ` Ian Rogers
  2026-01-17  5:28 ` [PATCH v1 16/23] perf dwarf-regs: Add loongarch " Ian Rogers
                   ` (7 subsequent siblings)
  22 siblings, 0 replies; 32+ messages in thread
From: Ian Rogers @ 2026-01-17  5:28 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
	Namhyung Kim, Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
	John Garry, Will Deacon, Leo Yan, Guo Ren, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Shimin Guo,
	Athira Rajeev, Stephen Brennan, Howard Chu, Thomas Falcon,
	Andi Kleen, Dr. David Alan Gilbert, Dmitry Vyukov,
	Krzysztof Łopatowski, Chun-Tse Shao, Aditya Bodkhe, Haibo Xu,
	Sergei Trofimovich, linux-kernel, linux-perf-users,
	linux-arm-kernel, linux-csky, linux-riscv, Mark Wielaard

These functions allow the generic initial register state code in
unwind-libdw to be used.

Signed-off-by: Ian Rogers <irogers@google.com>
---
 .../util/dwarf-regs-arch/dwarf-regs-csky.c    | 58 ++++++++++++++
 tools/perf/util/dwarf-regs.c                  |  3 +
 tools/perf/util/include/dwarf-regs.h          |  2 +
 tools/perf/util/unwind-libdw-arch/Build       |  1 -
 .../unwind-libdw-arch/unwind-libdw-csky.c     | 78 -------------------
 tools/perf/util/unwind-libdw.c                |  5 +-
 tools/perf/util/unwind-libdw.h                |  1 -
 7 files changed, 64 insertions(+), 84 deletions(-)
 delete mode 100644 tools/perf/util/unwind-libdw-arch/unwind-libdw-csky.c

diff --git a/tools/perf/util/dwarf-regs-arch/dwarf-regs-csky.c b/tools/perf/util/dwarf-regs-arch/dwarf-regs-csky.c
index 86394ed46397..cb44b774f8d9 100644
--- a/tools/perf/util/dwarf-regs-arch/dwarf-regs-csky.c
+++ b/tools/perf/util/dwarf-regs-arch/dwarf-regs-csky.c
@@ -5,6 +5,10 @@
 #include <errno.h>
 #include <stddef.h>
 #include <dwarf-regs.h>
+// Ensure the V2 perf reg definitions are included.
+#undef __CSKYABIV2__
+#define __CSKYABIV2__ 1
+#include "../../../arch/csky/include/uapi/asm/perf_regs.h"
 
 #define CSKY_ABIV2_MAX_REGS 73
 static const char * const csky_dwarf_regs_table_abiv2[CSKY_ABIV2_MAX_REGS] = {
@@ -66,3 +70,57 @@ int __get_csky_regnum(const char *name, unsigned int flags)
 
 	return __get_dwarf_regnum(csky_dwarf_regs_table_abiv1, CSKY_ABIV1_MAX_REGS, name);
 }
+
+int __get_dwarf_regnum_for_perf_regnum_csky(int perf_regnum, unsigned int flags)
+{
+	static const int dwarf_csky_regnums[][2] = {
+		[PERF_REG_CSKY_TLS] = {-ENOENT, 31},
+		[PERF_REG_CSKY_LR] = {15, 15},
+		[PERF_REG_CSKY_PC] = {-ENOENT, 32},
+		/* TODO: PERF_REG_CSKY_SR */
+		[PERF_REG_CSKY_SP] = {0, 14},
+		/* TODO: PERF_REG_CSKY_ORIG_A0 */
+		[PERF_REG_CSKY_A0] = {2, 0},
+		[PERF_REG_CSKY_A1] = {3, 1},
+		[PERF_REG_CSKY_A2] = {4, 2},
+		[PERF_REG_CSKY_A3] = {5, 3},
+		[PERF_REG_CSKY_REGS0] = {6, 4},
+		[PERF_REG_CSKY_REGS1] = {7, 5},
+		[PERF_REG_CSKY_REGS2] = {8, 6},
+		[PERF_REG_CSKY_REGS3] = {9, 7},
+		[PERF_REG_CSKY_REGS4] = {10, 8},
+		[PERF_REG_CSKY_REGS5] = {11, 9},
+		[PERF_REG_CSKY_REGS6] = {12, 10},
+		[PERF_REG_CSKY_REGS7] = {13, 11},
+		[PERF_REG_CSKY_REGS8] = {14, 12},
+		[PERF_REG_CSKY_REGS9] = {1, 13},
+		[PERF_REG_CSKY_EXREGS0] = {-ENOENT, 16},
+		[PERF_REG_CSKY_EXREGS1] = {-ENOENT, 17},
+		[PERF_REG_CSKY_EXREGS2] = {-ENOENT, 18},
+		[PERF_REG_CSKY_EXREGS3] = {-ENOENT, 19},
+		[PERF_REG_CSKY_EXREGS4] = {-ENOENT, 20},
+		[PERF_REG_CSKY_EXREGS5] = {-ENOENT, 21},
+		[PERF_REG_CSKY_EXREGS6] = {-ENOENT, 22},
+		[PERF_REG_CSKY_EXREGS7] = {-ENOENT, 23},
+		[PERF_REG_CSKY_EXREGS8] = {-ENOENT, 24},
+		[PERF_REG_CSKY_EXREGS9] = {-ENOENT, 25},
+		[PERF_REG_CSKY_EXREGS10] = {-ENOENT, 26},
+		[PERF_REG_CSKY_EXREGS11] = {-ENOENT, 27},
+		[PERF_REG_CSKY_EXREGS12] = {-ENOENT, 28},
+		[PERF_REG_CSKY_EXREGS13] = {-ENOENT, 29},
+		[PERF_REG_CSKY_EXREGS14] = {-ENOENT, 30},
+		/* TODO: PERF_REG_CSKY_HI */
+		/* TODO: PERF_REG_CSKY_LO */
+		/* TODO: PERF_REG_CSKY_DCSR */
+	};
+	int idx = 0;
+
+	if (flags & EF_CSKY_ABIV2)
+		idx++;
+
+	if (perf_regnum <  0 || perf_regnum > (int)ARRAY_SIZE(dwarf_csky_regnums) ||
+	    dwarf_csky_regnums[perf_regnum][idx] == 0)
+		return -ENOENT;
+
+	return dwarf_csky_regnums[perf_regnum][idx];
+}
diff --git a/tools/perf/util/dwarf-regs.c b/tools/perf/util/dwarf-regs.c
index c472ec5e4d1a..7fa0930fd298 100644
--- a/tools/perf/util/dwarf-regs.c
+++ b/tools/perf/util/dwarf-regs.c
@@ -202,6 +202,9 @@ int get_dwarf_regnum_for_perf_regnum(int perf_regnum, unsigned int machine,
 	case EM_AARCH64:
 		reg = __get_dwarf_regnum_for_perf_regnum_arm64(perf_regnum);
 		break;
+	case EM_CSKY:
+		reg = __get_dwarf_regnum_for_perf_regnum_csky(perf_regnum, flags);
+		break;
 	default:
 		pr_err("ELF MACHINE %x is not supported.\n", machine);
 		return -ENOENT;
diff --git a/tools/perf/util/include/dwarf-regs.h b/tools/perf/util/include/dwarf-regs.h
index a52df8d1b138..7780bc07e70e 100644
--- a/tools/perf/util/include/dwarf-regs.h
+++ b/tools/perf/util/include/dwarf-regs.h
@@ -108,6 +108,8 @@ int __get_dwarf_regnum_for_perf_regnum_x86_64(int perf_regnum);
 int __get_dwarf_regnum_for_perf_regnum_arm(int perf_regnum);
 int __get_dwarf_regnum_for_perf_regnum_arm64(int perf_regnum);
 
+int __get_dwarf_regnum_for_perf_regnum_csky(int perf_regnum, unsigned int flags);
+
 /*
  * get_dwarf_regnum - Returns DWARF regnum from register name
  * name: architecture register name
diff --git a/tools/perf/util/unwind-libdw-arch/Build b/tools/perf/util/unwind-libdw-arch/Build
index 79c3bbdc2dee..5fa1754fca8d 100644
--- a/tools/perf/util/unwind-libdw-arch/Build
+++ b/tools/perf/util/unwind-libdw-arch/Build
@@ -1,4 +1,3 @@
-perf-util-y += unwind-libdw-csky.o
 perf-util-y += unwind-libdw-loongarch.o
 perf-util-y += unwind-libdw-powerpc.o
 perf-util-y += unwind-libdw-riscv.o
diff --git a/tools/perf/util/unwind-libdw-arch/unwind-libdw-csky.c b/tools/perf/util/unwind-libdw-arch/unwind-libdw-csky.c
deleted file mode 100644
index 2556d034c32a..000000000000
--- a/tools/perf/util/unwind-libdw-arch/unwind-libdw-csky.c
+++ /dev/null
@@ -1,78 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-// Copyright (C) 2019 Hangzhou C-SKY Microsystems co.,ltd.
-
-#include <elfutils/libdwfl.h>
-#include "../arch/csky/include/uapi/asm/perf_regs.h"
-#include "util/unwind-libdw.h"
-#include "util/perf_regs.h"
-#include "util/sample.h"
-
-bool libdw_set_initial_registers_csky(Dwfl_Thread *thread, void *arg)
-{
-	struct unwind_info *ui = arg;
-	struct regs_dump *user_regs = perf_sample__user_regs(ui->sample);
-	Dwarf_Word dwarf_regs[PERF_REG_CSKY_MAX];
-
-#define REG(r) ({						\
-	Dwarf_Word val = 0;					\
-	perf_reg_value(&val, user_regs, PERF_REG_CSKY_##r);	\
-	val;							\
-})
-
-#if defined(__CSKYABIV2__)
-	dwarf_regs[0]  = REG(A0);
-	dwarf_regs[1]  = REG(A1);
-	dwarf_regs[2]  = REG(A2);
-	dwarf_regs[3]  = REG(A3);
-	dwarf_regs[4]  = REG(REGS0);
-	dwarf_regs[5]  = REG(REGS1);
-	dwarf_regs[6]  = REG(REGS2);
-	dwarf_regs[7]  = REG(REGS3);
-	dwarf_regs[8]  = REG(REGS4);
-	dwarf_regs[9]  = REG(REGS5);
-	dwarf_regs[10] = REG(REGS6);
-	dwarf_regs[11] = REG(REGS7);
-	dwarf_regs[12] = REG(REGS8);
-	dwarf_regs[13] = REG(REGS9);
-	dwarf_regs[14] = REG(SP);
-	dwarf_regs[15] = REG(LR);
-	dwarf_regs[16] = REG(EXREGS0);
-	dwarf_regs[17] = REG(EXREGS1);
-	dwarf_regs[18] = REG(EXREGS2);
-	dwarf_regs[19] = REG(EXREGS3);
-	dwarf_regs[20] = REG(EXREGS4);
-	dwarf_regs[21] = REG(EXREGS5);
-	dwarf_regs[22] = REG(EXREGS6);
-	dwarf_regs[23] = REG(EXREGS7);
-	dwarf_regs[24] = REG(EXREGS8);
-	dwarf_regs[25] = REG(EXREGS9);
-	dwarf_regs[26] = REG(EXREGS10);
-	dwarf_regs[27] = REG(EXREGS11);
-	dwarf_regs[28] = REG(EXREGS12);
-	dwarf_regs[29] = REG(EXREGS13);
-	dwarf_regs[30] = REG(EXREGS14);
-	dwarf_regs[31] = REG(TLS);
-	dwarf_regs[32] = REG(PC);
-#else
-	dwarf_regs[0]  = REG(SP);
-	dwarf_regs[1]  = REG(REGS9);
-	dwarf_regs[2]  = REG(A0);
-	dwarf_regs[3]  = REG(A1);
-	dwarf_regs[4]  = REG(A2);
-	dwarf_regs[5]  = REG(A3);
-	dwarf_regs[6]  = REG(REGS0);
-	dwarf_regs[7]  = REG(REGS1);
-	dwarf_regs[8]  = REG(REGS2);
-	dwarf_regs[9]  = REG(REGS3);
-	dwarf_regs[10] = REG(REGS4);
-	dwarf_regs[11] = REG(REGS5);
-	dwarf_regs[12] = REG(REGS6);
-	dwarf_regs[13] = REG(REGS7);
-	dwarf_regs[14] = REG(REGS8);
-	dwarf_regs[15] = REG(LR);
-#endif
-	dwfl_thread_state_register_pc(thread, REG(PC));
-
-	return dwfl_thread_state_registers(thread, 0, PERF_REG_CSKY_MAX,
-					   dwarf_regs);
-}
diff --git a/tools/perf/util/unwind-libdw.c b/tools/perf/util/unwind-libdw.c
index 8f291f9f9469..a193163da707 100644
--- a/tools/perf/util/unwind-libdw.c
+++ b/tools/perf/util/unwind-libdw.c
@@ -292,7 +292,6 @@ static const Dwfl_Thread_Callbacks callbacks_generic = {
 	.set_initial_registers = libdw_set_initial_registers_generic,
 };
 
-DEFINE_DWFL_THREAD_CALLBACKS(csky);
 DEFINE_DWFL_THREAD_CALLBACKS(loongarch);
 DEFINE_DWFL_THREAD_CALLBACKS(powerpc);
 DEFINE_DWFL_THREAD_CALLBACKS(riscv);
@@ -300,9 +299,7 @@ DEFINE_DWFL_THREAD_CALLBACKS(s390);
 
 static const Dwfl_Thread_Callbacks *get_thread_callbacks(const char *arch)
 {
-	if (!strcmp(arch, "csky"))
-		return &callbacks_csky;
-	else if (!strcmp(arch, "loongarch"))
+	if (!strcmp(arch, "loongarch"))
 		return &callbacks_loongarch;
 	else if (!strcmp(arch, "powerpc"))
 		return &callbacks_powerpc;
diff --git a/tools/perf/util/unwind-libdw.h b/tools/perf/util/unwind-libdw.h
index fe3ae2a768ad..ee56f1e827e5 100644
--- a/tools/perf/util/unwind-libdw.h
+++ b/tools/perf/util/unwind-libdw.h
@@ -9,7 +9,6 @@ struct machine;
 struct perf_sample;
 struct thread;
 
-bool libdw_set_initial_registers_csky(Dwfl_Thread *thread, void *arg);
 bool libdw_set_initial_registers_loongarch(Dwfl_Thread *thread, void *arg);
 bool libdw_set_initial_registers_mips(Dwfl_Thread *thread, void *arg);
 bool libdw_set_initial_registers_powerpc(Dwfl_Thread *thread, void *arg);
-- 
2.52.0.457.g6b5491de43-goog


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

* [PATCH v1 16/23] perf dwarf-regs: Add loongarch perf to dwarf register number mapping functions
  2026-01-17  5:28 [PATCH v1 00/23] perf dwarf/libdw extra support, speed and clean ups Ian Rogers
                   ` (14 preceding siblings ...)
  2026-01-17  5:28 ` [PATCH v1 15/23] perf dwarf-regs: Add csky " Ian Rogers
@ 2026-01-17  5:28 ` Ian Rogers
  2026-01-17  5:28 ` [PATCH v1 17/23] perf dwarf-regs: Add powerpc " Ian Rogers
                   ` (6 subsequent siblings)
  22 siblings, 0 replies; 32+ messages in thread
From: Ian Rogers @ 2026-01-17  5:28 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
	Namhyung Kim, Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
	John Garry, Will Deacon, Leo Yan, Guo Ren, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Shimin Guo,
	Athira Rajeev, Stephen Brennan, Howard Chu, Thomas Falcon,
	Andi Kleen, Dr. David Alan Gilbert, Dmitry Vyukov,
	Krzysztof Łopatowski, Chun-Tse Shao, Aditya Bodkhe, Haibo Xu,
	Sergei Trofimovich, linux-kernel, linux-perf-users,
	linux-arm-kernel, linux-csky, linux-riscv, Mark Wielaard

These functions allow the generic initial register state code in
unwind-libdw to be used.

Signed-off-by: Ian Rogers <irogers@google.com>
---
 tools/perf/util/dwarf-regs-arch/Build         |  1 +
 .../dwarf-regs-arch/dwarf-regs-loongarch.c    | 12 ++++
 tools/perf/util/dwarf-regs.c                  |  3 +
 tools/perf/util/include/dwarf-regs.h          |  1 +
 tools/perf/util/unwind-libdw-arch/Build       |  1 -
 .../unwind-libdw-loongarch.c                  | 57 -------------------
 tools/perf/util/unwind-libdw.c                |  5 +-
 tools/perf/util/unwind-libdw.h                |  1 -
 8 files changed, 18 insertions(+), 63 deletions(-)
 create mode 100644 tools/perf/util/dwarf-regs-arch/dwarf-regs-loongarch.c
 delete mode 100644 tools/perf/util/unwind-libdw-arch/unwind-libdw-loongarch.c

diff --git a/tools/perf/util/dwarf-regs-arch/Build b/tools/perf/util/dwarf-regs-arch/Build
index 3f19a9ec47c7..188359376ea5 100644
--- a/tools/perf/util/dwarf-regs-arch/Build
+++ b/tools/perf/util/dwarf-regs-arch/Build
@@ -1,5 +1,6 @@
 perf-util-$(CONFIG_LIBDW) += dwarf-regs-arm64.o
 perf-util-$(CONFIG_LIBDW) += dwarf-regs-arm.o
 perf-util-$(CONFIG_LIBDW) += dwarf-regs-csky.o
+perf-util-$(CONFIG_LIBDW) += dwarf-regs-loongarch.o
 perf-util-$(CONFIG_LIBDW) += dwarf-regs-powerpc.o
 perf-util-$(CONFIG_LIBDW) += dwarf-regs-x86.o
diff --git a/tools/perf/util/dwarf-regs-arch/dwarf-regs-loongarch.c b/tools/perf/util/dwarf-regs-arch/dwarf-regs-loongarch.c
new file mode 100644
index 000000000000..203077b740a0
--- /dev/null
+++ b/tools/perf/util/dwarf-regs-arch/dwarf-regs-loongarch.c
@@ -0,0 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <errno.h>
+#include <dwarf-regs.h>
+#include "../../../arch/loongarch/include/uapi/asm/perf_regs.h"
+
+int __get_dwarf_regnum_for_perf_regnum_loongarch(int perf_regnum)
+{
+	if (perf_regnum < 0 || perf_regnum >= PERF_REG_LOONGARCH_MAX)
+		return -ENOENT;
+
+	return perf_regnum;
+}
diff --git a/tools/perf/util/dwarf-regs.c b/tools/perf/util/dwarf-regs.c
index 7fa0930fd298..033218f14b36 100644
--- a/tools/perf/util/dwarf-regs.c
+++ b/tools/perf/util/dwarf-regs.c
@@ -205,6 +205,9 @@ int get_dwarf_regnum_for_perf_regnum(int perf_regnum, unsigned int machine,
 	case EM_CSKY:
 		reg = __get_dwarf_regnum_for_perf_regnum_csky(perf_regnum, flags);
 		break;
+	case EM_LOONGARCH:
+		reg = __get_dwarf_regnum_for_perf_regnum_loongarch(perf_regnum);
+		break;
 	default:
 		pr_err("ELF MACHINE %x is not supported.\n", machine);
 		return -ENOENT;
diff --git a/tools/perf/util/include/dwarf-regs.h b/tools/perf/util/include/dwarf-regs.h
index 7780bc07e70e..bec15fb53e73 100644
--- a/tools/perf/util/include/dwarf-regs.h
+++ b/tools/perf/util/include/dwarf-regs.h
@@ -109,6 +109,7 @@ int __get_dwarf_regnum_for_perf_regnum_arm(int perf_regnum);
 int __get_dwarf_regnum_for_perf_regnum_arm64(int perf_regnum);
 
 int __get_dwarf_regnum_for_perf_regnum_csky(int perf_regnum, unsigned int flags);
+int __get_dwarf_regnum_for_perf_regnum_loongarch(int perf_regnum);
 
 /*
  * get_dwarf_regnum - Returns DWARF regnum from register name
diff --git a/tools/perf/util/unwind-libdw-arch/Build b/tools/perf/util/unwind-libdw-arch/Build
index 5fa1754fca8d..62a4cbf2dca8 100644
--- a/tools/perf/util/unwind-libdw-arch/Build
+++ b/tools/perf/util/unwind-libdw-arch/Build
@@ -1,4 +1,3 @@
-perf-util-y += unwind-libdw-loongarch.o
 perf-util-y += unwind-libdw-powerpc.o
 perf-util-y += unwind-libdw-riscv.o
 perf-util-y += unwind-libdw-s390.o
diff --git a/tools/perf/util/unwind-libdw-arch/unwind-libdw-loongarch.c b/tools/perf/util/unwind-libdw-arch/unwind-libdw-loongarch.c
deleted file mode 100644
index 5fca673508be..000000000000
--- a/tools/perf/util/unwind-libdw-arch/unwind-libdw-loongarch.c
+++ /dev/null
@@ -1,57 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/* Copyright (C) 2020-2023 Loongson Technology Corporation Limited */
-
-#include <elfutils/libdwfl.h>
-#include "../arch/loongarch/include/uapi/asm/perf_regs.h"
-#include "util/unwind-libdw.h"
-#include "util/perf_regs.h"
-#include "util/sample.h"
-
-bool libdw_set_initial_registers_loongarch(Dwfl_Thread *thread, void *arg)
-{
-	struct unwind_info *ui = arg;
-	struct regs_dump *user_regs = perf_sample__user_regs(ui->sample);
-	Dwarf_Word dwarf_regs[PERF_REG_LOONGARCH_MAX];
-
-#define REG(r) ({							\
-	Dwarf_Word val = 0;						\
-	perf_reg_value(&val, user_regs, PERF_REG_LOONGARCH_##r);	\
-	val;								\
-})
-
-	dwarf_regs[0]  = 0;
-	dwarf_regs[1]  = REG(R1);
-	dwarf_regs[2]  = REG(R2);
-	dwarf_regs[3]  = REG(R3);
-	dwarf_regs[4]  = REG(R4);
-	dwarf_regs[5]  = REG(R5);
-	dwarf_regs[6]  = REG(R6);
-	dwarf_regs[7]  = REG(R7);
-	dwarf_regs[8]  = REG(R8);
-	dwarf_regs[9]  = REG(R9);
-	dwarf_regs[10] = REG(R10);
-	dwarf_regs[11] = REG(R11);
-	dwarf_regs[12] = REG(R12);
-	dwarf_regs[13] = REG(R13);
-	dwarf_regs[14] = REG(R14);
-	dwarf_regs[15] = REG(R15);
-	dwarf_regs[16] = REG(R16);
-	dwarf_regs[17] = REG(R17);
-	dwarf_regs[18] = REG(R18);
-	dwarf_regs[19] = REG(R19);
-	dwarf_regs[20] = REG(R20);
-	dwarf_regs[21] = REG(R21);
-	dwarf_regs[22] = REG(R22);
-	dwarf_regs[23] = REG(R23);
-	dwarf_regs[24] = REG(R24);
-	dwarf_regs[25] = REG(R25);
-	dwarf_regs[26] = REG(R26);
-	dwarf_regs[27] = REG(R27);
-	dwarf_regs[28] = REG(R28);
-	dwarf_regs[29] = REG(R29);
-	dwarf_regs[30] = REG(R30);
-	dwarf_regs[31] = REG(R31);
-	dwfl_thread_state_register_pc(thread, REG(PC));
-
-	return dwfl_thread_state_registers(thread, 0, PERF_REG_LOONGARCH_MAX, dwarf_regs);
-}
diff --git a/tools/perf/util/unwind-libdw.c b/tools/perf/util/unwind-libdw.c
index a193163da707..9c8dad643cd0 100644
--- a/tools/perf/util/unwind-libdw.c
+++ b/tools/perf/util/unwind-libdw.c
@@ -292,16 +292,13 @@ static const Dwfl_Thread_Callbacks callbacks_generic = {
 	.set_initial_registers = libdw_set_initial_registers_generic,
 };
 
-DEFINE_DWFL_THREAD_CALLBACKS(loongarch);
 DEFINE_DWFL_THREAD_CALLBACKS(powerpc);
 DEFINE_DWFL_THREAD_CALLBACKS(riscv);
 DEFINE_DWFL_THREAD_CALLBACKS(s390);
 
 static const Dwfl_Thread_Callbacks *get_thread_callbacks(const char *arch)
 {
-	if (!strcmp(arch, "loongarch"))
-		return &callbacks_loongarch;
-	else if (!strcmp(arch, "powerpc"))
+	if (!strcmp(arch, "powerpc"))
 		return &callbacks_powerpc;
 	else if (!strcmp(arch, "riscv"))
 		return &callbacks_riscv;
diff --git a/tools/perf/util/unwind-libdw.h b/tools/perf/util/unwind-libdw.h
index ee56f1e827e5..9d177d70f15c 100644
--- a/tools/perf/util/unwind-libdw.h
+++ b/tools/perf/util/unwind-libdw.h
@@ -9,7 +9,6 @@ struct machine;
 struct perf_sample;
 struct thread;
 
-bool libdw_set_initial_registers_loongarch(Dwfl_Thread *thread, void *arg);
 bool libdw_set_initial_registers_mips(Dwfl_Thread *thread, void *arg);
 bool libdw_set_initial_registers_powerpc(Dwfl_Thread *thread, void *arg);
 bool libdw_set_initial_registers_riscv(Dwfl_Thread *thread, void *arg);
-- 
2.52.0.457.g6b5491de43-goog


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

* [PATCH v1 17/23] perf dwarf-regs: Add powerpc perf to dwarf register number mapping functions
  2026-01-17  5:28 [PATCH v1 00/23] perf dwarf/libdw extra support, speed and clean ups Ian Rogers
                   ` (15 preceding siblings ...)
  2026-01-17  5:28 ` [PATCH v1 16/23] perf dwarf-regs: Add loongarch " Ian Rogers
@ 2026-01-17  5:28 ` Ian Rogers
  2026-01-17  5:28 ` [PATCH v1 18/23] perf dwarf-regs: Add RISC-V " Ian Rogers
                   ` (5 subsequent siblings)
  22 siblings, 0 replies; 32+ messages in thread
From: Ian Rogers @ 2026-01-17  5:28 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
	Namhyung Kim, Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
	John Garry, Will Deacon, Leo Yan, Guo Ren, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Shimin Guo,
	Athira Rajeev, Stephen Brennan, Howard Chu, Thomas Falcon,
	Andi Kleen, Dr. David Alan Gilbert, Dmitry Vyukov,
	Krzysztof Łopatowski, Chun-Tse Shao, Aditya Bodkhe, Haibo Xu,
	Sergei Trofimovich, linux-kernel, linux-perf-users,
	linux-arm-kernel, linux-csky, linux-riscv, Mark Wielaard

These functions allow the generic initial register state code in
unwind-libdw to be used. Note, the link register was being coped to
dwarf register 65 that the SysV ABI spec claims is FPSCR. It is
corrected here to 108, but this is unlikely to matter as FPSCR has
little to no impact on unwinding.

Signed-off-by: Ian Rogers <irogers@google.com>
---
 .../util/dwarf-regs-arch/dwarf-regs-powerpc.c | 77 ++++++++++++++++++-
 tools/perf/util/dwarf-regs.c                  |  4 +
 tools/perf/util/include/dwarf-regs.h          |  1 +
 tools/perf/util/unwind-libdw-arch/Build       |  1 -
 .../unwind-libdw-arch/unwind-libdw-powerpc.c  | 76 ------------------
 tools/perf/util/unwind-libdw.c                |  5 +-
 tools/perf/util/unwind-libdw.h                |  1 -
 7 files changed, 82 insertions(+), 83 deletions(-)
 delete mode 100644 tools/perf/util/unwind-libdw-arch/unwind-libdw-powerpc.c

diff --git a/tools/perf/util/dwarf-regs-arch/dwarf-regs-powerpc.c b/tools/perf/util/dwarf-regs-arch/dwarf-regs-powerpc.c
index caf77a234c78..51892a09725b 100644
--- a/tools/perf/util/dwarf-regs-arch/dwarf-regs-powerpc.c
+++ b/tools/perf/util/dwarf-regs-arch/dwarf-regs-powerpc.c
@@ -4,8 +4,9 @@
  *
  * Copyright (C) 2010 Ian Munsie, IBM Corporation.
  */
-
+#include <errno.h>
 #include <dwarf-regs.h>
+#include "../../../arch/powerpc/include/uapi/asm/perf_regs.h"
 
 #define PPC_OP(op)	(((op) >> 26) & 0x3F)
 #define PPC_RA(a)	(((a) >> 16) & 0x1f)
@@ -59,3 +60,77 @@ void get_powerpc_regs(u32 raw_insn, int is_source,
 	if ((op_loc->mem_ref) && (PPC_OP(raw_insn) != 31))
 		op_loc->offset = get_offset_opcode(raw_insn);
 }
+
+int __get_dwarf_regnum_for_perf_regnum_powerpc(int perf_regnum)
+{
+	static const int dwarf_powerpc_regnums[] = {
+		[PERF_REG_POWERPC_R0] = 0,
+		[PERF_REG_POWERPC_R1] = 1,
+		[PERF_REG_POWERPC_R2] = 2,
+		[PERF_REG_POWERPC_R3] = 3,
+		[PERF_REG_POWERPC_R4] = 4,
+		[PERF_REG_POWERPC_R5] = 5,
+		[PERF_REG_POWERPC_R6] = 6,
+		[PERF_REG_POWERPC_R7] = 7,
+		[PERF_REG_POWERPC_R8] = 8,
+		[PERF_REG_POWERPC_R9] = 9,
+		[PERF_REG_POWERPC_R10] = 10,
+		[PERF_REG_POWERPC_R11] = 11,
+		[PERF_REG_POWERPC_R12] = 12,
+		[PERF_REG_POWERPC_R13] = 13,
+		[PERF_REG_POWERPC_R14] = 14,
+		[PERF_REG_POWERPC_R15] = 15,
+		[PERF_REG_POWERPC_R16] = 16,
+		[PERF_REG_POWERPC_R17] = 17,
+		[PERF_REG_POWERPC_R18] = 18,
+		[PERF_REG_POWERPC_R19] = 19,
+		[PERF_REG_POWERPC_R20] = 20,
+		[PERF_REG_POWERPC_R21] = 21,
+		[PERF_REG_POWERPC_R22] = 22,
+		[PERF_REG_POWERPC_R23] = 23,
+		[PERF_REG_POWERPC_R24] = 24,
+		[PERF_REG_POWERPC_R25] = 25,
+		[PERF_REG_POWERPC_R26] = 26,
+		[PERF_REG_POWERPC_R27] = 27,
+		[PERF_REG_POWERPC_R28] = 28,
+		[PERF_REG_POWERPC_R29] = 29,
+		[PERF_REG_POWERPC_R30] = 30,
+		[PERF_REG_POWERPC_R31] = 31,
+		/* TODO: PERF_REG_POWERPC_NIP */
+		[PERF_REG_POWERPC_MSR] = 66,
+		/* TODO: PERF_REG_POWERPC_ORIG_R3 */
+		[PERF_REG_POWERPC_CTR] = 109,
+		[PERF_REG_POWERPC_LINK] = 108, /* Note, previously in perf encoded as 65? */
+		[PERF_REG_POWERPC_XER] = 101,
+		/* TODO: PERF_REG_POWERPC_CCR */
+		/* TODO: PERF_REG_POWERPC_SOFTE */
+		/* TODO: PERF_REG_POWERPC_TRAP */
+		/* TODO: PERF_REG_POWERPC_DAR */
+		/* TODO: PERF_REG_POWERPC_DSISR */
+		/* TODO: PERF_REG_POWERPC_SIER */
+		/* TODO: PERF_REG_POWERPC_MMCRA */
+		/* TODO: PERF_REG_POWERPC_MMCR0 */
+		/* TODO: PERF_REG_POWERPC_MMCR1 */
+		/* TODO: PERF_REG_POWERPC_MMCR2 */
+		/* TODO: PERF_REG_POWERPC_MMCR3 */
+		/* TODO: PERF_REG_POWERPC_SIER2 */
+		/* TODO: PERF_REG_POWERPC_SIER3 */
+		/* TODO: PERF_REG_POWERPC_PMC1 */
+		/* TODO: PERF_REG_POWERPC_PMC2 */
+		/* TODO: PERF_REG_POWERPC_PMC3 */
+		/* TODO: PERF_REG_POWERPC_PMC4 */
+		/* TODO: PERF_REG_POWERPC_PMC5 */
+		/* TODO: PERF_REG_POWERPC_PMC6 */
+		/* TODO: PERF_REG_POWERPC_SDAR */
+		/* TODO: PERF_REG_POWERPC_SIAR */
+	};
+
+	if (perf_regnum == 0)
+		return 0;
+
+	if (perf_regnum <  0 || perf_regnum > (int)ARRAY_SIZE(dwarf_powerpc_regnums) ||
+	    dwarf_powerpc_regnums[perf_regnum] == 0)
+		return -ENOENT;
+
+	return dwarf_powerpc_regnums[perf_regnum];
+}
diff --git a/tools/perf/util/dwarf-regs.c b/tools/perf/util/dwarf-regs.c
index 033218f14b36..3b1c2a436806 100644
--- a/tools/perf/util/dwarf-regs.c
+++ b/tools/perf/util/dwarf-regs.c
@@ -205,6 +205,10 @@ int get_dwarf_regnum_for_perf_regnum(int perf_regnum, unsigned int machine,
 	case EM_CSKY:
 		reg = __get_dwarf_regnum_for_perf_regnum_csky(perf_regnum, flags);
 		break;
+	case EM_PPC:
+	case EM_PPC64:
+		reg = __get_dwarf_regnum_for_perf_regnum_powerpc(perf_regnum);
+		break;
 	case EM_LOONGARCH:
 		reg = __get_dwarf_regnum_for_perf_regnum_loongarch(perf_regnum);
 		break;
diff --git a/tools/perf/util/include/dwarf-regs.h b/tools/perf/util/include/dwarf-regs.h
index bec15fb53e73..9ebb3ba33fba 100644
--- a/tools/perf/util/include/dwarf-regs.h
+++ b/tools/perf/util/include/dwarf-regs.h
@@ -110,6 +110,7 @@ int __get_dwarf_regnum_for_perf_regnum_arm64(int perf_regnum);
 
 int __get_dwarf_regnum_for_perf_regnum_csky(int perf_regnum, unsigned int flags);
 int __get_dwarf_regnum_for_perf_regnum_loongarch(int perf_regnum);
+int __get_dwarf_regnum_for_perf_regnum_powerpc(int perf_regnum);
 
 /*
  * get_dwarf_regnum - Returns DWARF regnum from register name
diff --git a/tools/perf/util/unwind-libdw-arch/Build b/tools/perf/util/unwind-libdw-arch/Build
index 62a4cbf2dca8..e6c97e842cd6 100644
--- a/tools/perf/util/unwind-libdw-arch/Build
+++ b/tools/perf/util/unwind-libdw-arch/Build
@@ -1,3 +1,2 @@
-perf-util-y += unwind-libdw-powerpc.o
 perf-util-y += unwind-libdw-riscv.o
 perf-util-y += unwind-libdw-s390.o
diff --git a/tools/perf/util/unwind-libdw-arch/unwind-libdw-powerpc.c b/tools/perf/util/unwind-libdw-arch/unwind-libdw-powerpc.c
deleted file mode 100644
index 1560db45e7b4..000000000000
--- a/tools/perf/util/unwind-libdw-arch/unwind-libdw-powerpc.c
+++ /dev/null
@@ -1,76 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-#include <elfutils/libdwfl.h>
-#include <linux/kernel.h>
-#include "../arch/powerpc/include/uapi/asm/perf_regs.h"
-#include "util/unwind-libdw.h"
-#include "util/perf_regs.h"
-#include "util/sample.h"
-
-/* See backends/ppc_initreg.c and backends/ppc_regs.c in elfutils.  */
-static const int special_regs[3][2] = {
-	{ 65, PERF_REG_POWERPC_LINK },
-	{ 101, PERF_REG_POWERPC_XER },
-	{ 109, PERF_REG_POWERPC_CTR },
-};
-
-bool libdw_set_initial_registers_powerpc(Dwfl_Thread *thread, void *arg)
-{
-	struct unwind_info *ui = arg;
-	struct regs_dump *user_regs = perf_sample__user_regs(ui->sample);
-	Dwarf_Word dwarf_regs[32], dwarf_nip;
-	size_t i;
-
-#define REG(r) ({						\
-	Dwarf_Word val = 0;					\
-	perf_reg_value(&val, user_regs, PERF_REG_POWERPC_##r);	\
-	val;							\
-})
-
-	dwarf_regs[0]  = REG(R0);
-	dwarf_regs[1]  = REG(R1);
-	dwarf_regs[2]  = REG(R2);
-	dwarf_regs[3]  = REG(R3);
-	dwarf_regs[4]  = REG(R4);
-	dwarf_regs[5]  = REG(R5);
-	dwarf_regs[6]  = REG(R6);
-	dwarf_regs[7]  = REG(R7);
-	dwarf_regs[8]  = REG(R8);
-	dwarf_regs[9]  = REG(R9);
-	dwarf_regs[10] = REG(R10);
-	dwarf_regs[11] = REG(R11);
-	dwarf_regs[12] = REG(R12);
-	dwarf_regs[13] = REG(R13);
-	dwarf_regs[14] = REG(R14);
-	dwarf_regs[15] = REG(R15);
-	dwarf_regs[16] = REG(R16);
-	dwarf_regs[17] = REG(R17);
-	dwarf_regs[18] = REG(R18);
-	dwarf_regs[19] = REG(R19);
-	dwarf_regs[20] = REG(R20);
-	dwarf_regs[21] = REG(R21);
-	dwarf_regs[22] = REG(R22);
-	dwarf_regs[23] = REG(R23);
-	dwarf_regs[24] = REG(R24);
-	dwarf_regs[25] = REG(R25);
-	dwarf_regs[26] = REG(R26);
-	dwarf_regs[27] = REG(R27);
-	dwarf_regs[28] = REG(R28);
-	dwarf_regs[29] = REG(R29);
-	dwarf_regs[30] = REG(R30);
-	dwarf_regs[31] = REG(R31);
-	if (!dwfl_thread_state_registers(thread, 0, 32, dwarf_regs))
-		return false;
-
-	dwarf_nip = REG(NIP);
-	dwfl_thread_state_register_pc(thread, dwarf_nip);
-	for (i = 0; i < ARRAY_SIZE(special_regs); i++) {
-		Dwarf_Word val = 0;
-		perf_reg_value(&val, user_regs, special_regs[i][1]);
-		if (!dwfl_thread_state_registers(thread,
-						 special_regs[i][0], 1,
-						 &val))
-			return false;
-	}
-
-	return true;
-}
diff --git a/tools/perf/util/unwind-libdw.c b/tools/perf/util/unwind-libdw.c
index 9c8dad643cd0..e9ba050e7ab1 100644
--- a/tools/perf/util/unwind-libdw.c
+++ b/tools/perf/util/unwind-libdw.c
@@ -292,15 +292,12 @@ static const Dwfl_Thread_Callbacks callbacks_generic = {
 	.set_initial_registers = libdw_set_initial_registers_generic,
 };
 
-DEFINE_DWFL_THREAD_CALLBACKS(powerpc);
 DEFINE_DWFL_THREAD_CALLBACKS(riscv);
 DEFINE_DWFL_THREAD_CALLBACKS(s390);
 
 static const Dwfl_Thread_Callbacks *get_thread_callbacks(const char *arch)
 {
-	if (!strcmp(arch, "powerpc"))
-		return &callbacks_powerpc;
-	else if (!strcmp(arch, "riscv"))
+	if (!strcmp(arch, "riscv"))
 		return &callbacks_riscv;
 	else if (!strcmp(arch, "s390"))
 		return &callbacks_s390;
diff --git a/tools/perf/util/unwind-libdw.h b/tools/perf/util/unwind-libdw.h
index 9d177d70f15c..0ec1abdabbe7 100644
--- a/tools/perf/util/unwind-libdw.h
+++ b/tools/perf/util/unwind-libdw.h
@@ -10,7 +10,6 @@ struct perf_sample;
 struct thread;
 
 bool libdw_set_initial_registers_mips(Dwfl_Thread *thread, void *arg);
-bool libdw_set_initial_registers_powerpc(Dwfl_Thread *thread, void *arg);
 bool libdw_set_initial_registers_riscv(Dwfl_Thread *thread, void *arg);
 bool libdw_set_initial_registers_s390(Dwfl_Thread *thread, void *arg);
 
-- 
2.52.0.457.g6b5491de43-goog


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

* [PATCH v1 18/23] perf dwarf-regs: Add RISC-V perf to dwarf register number mapping functions
  2026-01-17  5:28 [PATCH v1 00/23] perf dwarf/libdw extra support, speed and clean ups Ian Rogers
                   ` (16 preceding siblings ...)
  2026-01-17  5:28 ` [PATCH v1 17/23] perf dwarf-regs: Add powerpc " Ian Rogers
@ 2026-01-17  5:28 ` Ian Rogers
  2026-01-17  5:28 ` [PATCH v1 19/23] perf dwarf-regs: Add S390 " Ian Rogers
                   ` (4 subsequent siblings)
  22 siblings, 0 replies; 32+ messages in thread
From: Ian Rogers @ 2026-01-17  5:28 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
	Namhyung Kim, Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
	John Garry, Will Deacon, Leo Yan, Guo Ren, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Shimin Guo,
	Athira Rajeev, Stephen Brennan, Howard Chu, Thomas Falcon,
	Andi Kleen, Dr. David Alan Gilbert, Dmitry Vyukov,
	Krzysztof Łopatowski, Chun-Tse Shao, Aditya Bodkhe, Haibo Xu,
	Sergei Trofimovich, linux-kernel, linux-perf-users,
	linux-arm-kernel, linux-csky, linux-riscv, Mark Wielaard

These functions allow the generic initial register state code in
unwind-libdw to be used.

Signed-off-by: Ian Rogers <irogers@google.com>
---
 tools/perf/util/dwarf-regs-arch/Build         |  1 +
 .../util/dwarf-regs-arch/dwarf-regs-riscv.c   | 12 ++++
 tools/perf/util/dwarf-regs.c                  |  3 +
 tools/perf/util/include/dwarf-regs.h          |  1 +
 tools/perf/util/unwind-libdw-arch/Build       |  1 -
 .../unwind-libdw-arch/unwind-libdw-riscv.c    | 58 -------------------
 tools/perf/util/unwind-libdw.c                |  5 +-
 tools/perf/util/unwind-libdw.h                |  1 -
 8 files changed, 18 insertions(+), 64 deletions(-)
 create mode 100644 tools/perf/util/dwarf-regs-arch/dwarf-regs-riscv.c
 delete mode 100644 tools/perf/util/unwind-libdw-arch/unwind-libdw-riscv.c

diff --git a/tools/perf/util/dwarf-regs-arch/Build b/tools/perf/util/dwarf-regs-arch/Build
index 188359376ea5..94e4dfceb4d1 100644
--- a/tools/perf/util/dwarf-regs-arch/Build
+++ b/tools/perf/util/dwarf-regs-arch/Build
@@ -3,4 +3,5 @@ perf-util-$(CONFIG_LIBDW) += dwarf-regs-arm.o
 perf-util-$(CONFIG_LIBDW) += dwarf-regs-csky.o
 perf-util-$(CONFIG_LIBDW) += dwarf-regs-loongarch.o
 perf-util-$(CONFIG_LIBDW) += dwarf-regs-powerpc.o
+perf-util-$(CONFIG_LIBDW) += dwarf-regs-riscv.o
 perf-util-$(CONFIG_LIBDW) += dwarf-regs-x86.o
diff --git a/tools/perf/util/dwarf-regs-arch/dwarf-regs-riscv.c b/tools/perf/util/dwarf-regs-arch/dwarf-regs-riscv.c
new file mode 100644
index 000000000000..090db51aba41
--- /dev/null
+++ b/tools/perf/util/dwarf-regs-arch/dwarf-regs-riscv.c
@@ -0,0 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <errno.h>
+#include <dwarf-regs.h>
+#include "../../../arch/riscv/include/uapi/asm/perf_regs.h"
+
+int __get_dwarf_regnum_for_perf_regnum_riscv(int perf_regnum)
+{
+	if (perf_regnum < 0 || perf_regnum >= PERF_REG_RISCV_MAX)
+		return -ENOENT;
+
+	return perf_regnum;
+}
diff --git a/tools/perf/util/dwarf-regs.c b/tools/perf/util/dwarf-regs.c
index 3b1c2a436806..137568e15018 100644
--- a/tools/perf/util/dwarf-regs.c
+++ b/tools/perf/util/dwarf-regs.c
@@ -209,6 +209,9 @@ int get_dwarf_regnum_for_perf_regnum(int perf_regnum, unsigned int machine,
 	case EM_PPC64:
 		reg = __get_dwarf_regnum_for_perf_regnum_powerpc(perf_regnum);
 		break;
+	case EM_RISCV:
+		reg = __get_dwarf_regnum_for_perf_regnum_riscv(perf_regnum);
+		break;
 	case EM_LOONGARCH:
 		reg = __get_dwarf_regnum_for_perf_regnum_loongarch(perf_regnum);
 		break;
diff --git a/tools/perf/util/include/dwarf-regs.h b/tools/perf/util/include/dwarf-regs.h
index 9ebb3ba33fba..ae76608da110 100644
--- a/tools/perf/util/include/dwarf-regs.h
+++ b/tools/perf/util/include/dwarf-regs.h
@@ -111,6 +111,7 @@ int __get_dwarf_regnum_for_perf_regnum_arm64(int perf_regnum);
 int __get_dwarf_regnum_for_perf_regnum_csky(int perf_regnum, unsigned int flags);
 int __get_dwarf_regnum_for_perf_regnum_loongarch(int perf_regnum);
 int __get_dwarf_regnum_for_perf_regnum_powerpc(int perf_regnum);
+int __get_dwarf_regnum_for_perf_regnum_riscv(int perf_regnum);
 
 /*
  * get_dwarf_regnum - Returns DWARF regnum from register name
diff --git a/tools/perf/util/unwind-libdw-arch/Build b/tools/perf/util/unwind-libdw-arch/Build
index e6c97e842cd6..6d6e319e1201 100644
--- a/tools/perf/util/unwind-libdw-arch/Build
+++ b/tools/perf/util/unwind-libdw-arch/Build
@@ -1,2 +1 @@
-perf-util-y += unwind-libdw-riscv.o
 perf-util-y += unwind-libdw-s390.o
diff --git a/tools/perf/util/unwind-libdw-arch/unwind-libdw-riscv.c b/tools/perf/util/unwind-libdw-arch/unwind-libdw-riscv.c
deleted file mode 100644
index c2e2c4b6b2e0..000000000000
--- a/tools/perf/util/unwind-libdw-arch/unwind-libdw-riscv.c
+++ /dev/null
@@ -1,58 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/* Copyright (C) 2019 Hangzhou C-SKY Microsystems co.,ltd. */
-
-#include <elfutils/libdwfl.h>
-#include "../arch/riscv/include/uapi/asm/perf_regs.h"
-#include "util/unwind-libdw.h"
-#include "util/perf_regs.h"
-#include "util/sample.h"
-
-bool libdw_set_initial_registers_riscv(Dwfl_Thread *thread, void *arg)
-{
-	struct unwind_info *ui = arg;
-	struct regs_dump *user_regs = perf_sample__user_regs(ui->sample);
-	Dwarf_Word dwarf_regs[32];
-
-#define REG(r) ({						\
-	Dwarf_Word val = 0;					\
-	perf_reg_value(&val, user_regs, PERF_REG_RISCV_##r);	\
-	val;							\
-})
-
-	dwarf_regs[0]  = 0;
-	dwarf_regs[1]  = REG(RA);
-	dwarf_regs[2]  = REG(SP);
-	dwarf_regs[3]  = REG(GP);
-	dwarf_regs[4]  = REG(TP);
-	dwarf_regs[5]  = REG(T0);
-	dwarf_regs[6]  = REG(T1);
-	dwarf_regs[7]  = REG(T2);
-	dwarf_regs[8]  = REG(S0);
-	dwarf_regs[9]  = REG(S1);
-	dwarf_regs[10] = REG(A0);
-	dwarf_regs[11] = REG(A1);
-	dwarf_regs[12] = REG(A2);
-	dwarf_regs[13] = REG(A3);
-	dwarf_regs[14] = REG(A4);
-	dwarf_regs[15] = REG(A5);
-	dwarf_regs[16] = REG(A6);
-	dwarf_regs[17] = REG(A7);
-	dwarf_regs[18] = REG(S2);
-	dwarf_regs[19] = REG(S3);
-	dwarf_regs[20] = REG(S4);
-	dwarf_regs[21] = REG(S5);
-	dwarf_regs[22] = REG(S6);
-	dwarf_regs[23] = REG(S7);
-	dwarf_regs[24] = REG(S8);
-	dwarf_regs[25] = REG(S9);
-	dwarf_regs[26] = REG(S10);
-	dwarf_regs[27] = REG(S11);
-	dwarf_regs[28] = REG(T3);
-	dwarf_regs[29] = REG(T4);
-	dwarf_regs[30] = REG(T5);
-	dwarf_regs[31] = REG(T6);
-	dwfl_thread_state_register_pc(thread, REG(PC));
-
-	return dwfl_thread_state_registers(thread, 0, PERF_REG_RISCV_MAX,
-					   dwarf_regs);
-}
diff --git a/tools/perf/util/unwind-libdw.c b/tools/perf/util/unwind-libdw.c
index e9ba050e7ab1..b3c4380d40b6 100644
--- a/tools/perf/util/unwind-libdw.c
+++ b/tools/perf/util/unwind-libdw.c
@@ -292,14 +292,11 @@ static const Dwfl_Thread_Callbacks callbacks_generic = {
 	.set_initial_registers = libdw_set_initial_registers_generic,
 };
 
-DEFINE_DWFL_THREAD_CALLBACKS(riscv);
 DEFINE_DWFL_THREAD_CALLBACKS(s390);
 
 static const Dwfl_Thread_Callbacks *get_thread_callbacks(const char *arch)
 {
-	if (!strcmp(arch, "riscv"))
-		return &callbacks_riscv;
-	else if (!strcmp(arch, "s390"))
+	if (!strcmp(arch, "s390"))
 		return &callbacks_s390;
 
 	return &callbacks_generic;
diff --git a/tools/perf/util/unwind-libdw.h b/tools/perf/util/unwind-libdw.h
index 0ec1abdabbe7..5c23080cb6c1 100644
--- a/tools/perf/util/unwind-libdw.h
+++ b/tools/perf/util/unwind-libdw.h
@@ -10,7 +10,6 @@ struct perf_sample;
 struct thread;
 
 bool libdw_set_initial_registers_mips(Dwfl_Thread *thread, void *arg);
-bool libdw_set_initial_registers_riscv(Dwfl_Thread *thread, void *arg);
 bool libdw_set_initial_registers_s390(Dwfl_Thread *thread, void *arg);
 
 struct unwind_info {
-- 
2.52.0.457.g6b5491de43-goog


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

* [PATCH v1 19/23] perf dwarf-regs: Add S390 perf to dwarf register number mapping functions
  2026-01-17  5:28 [PATCH v1 00/23] perf dwarf/libdw extra support, speed and clean ups Ian Rogers
                   ` (17 preceding siblings ...)
  2026-01-17  5:28 ` [PATCH v1 18/23] perf dwarf-regs: Add RISC-V " Ian Rogers
@ 2026-01-17  5:28 ` Ian Rogers
  2026-01-17  5:28 ` [PATCH v1 20/23] perf dwarf-regs: Add MIPS " Ian Rogers
                   ` (3 subsequent siblings)
  22 siblings, 0 replies; 32+ messages in thread
From: Ian Rogers @ 2026-01-17  5:28 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
	Namhyung Kim, Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
	John Garry, Will Deacon, Leo Yan, Guo Ren, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Shimin Guo,
	Athira Rajeev, Stephen Brennan, Howard Chu, Thomas Falcon,
	Andi Kleen, Dr. David Alan Gilbert, Dmitry Vyukov,
	Krzysztof Łopatowski, Chun-Tse Shao, Aditya Bodkhe, Haibo Xu,
	Sergei Trofimovich, linux-kernel, linux-perf-users,
	linux-arm-kernel, linux-csky, linux-riscv, Mark Wielaard

These functions allow the generic initial register state code in
unwind-libdw to be used. Now the non-generic code in unwind-libdw has
no uses remove it.

Signed-off-by: Ian Rogers <irogers@google.com>
---
 tools/perf/util/dwarf-regs-arch/Build         |  1 +
 .../util/dwarf-regs-arch/dwarf-regs-s390.c    | 53 +++++++++++++++
 tools/perf/util/dwarf-regs.c                  |  3 +
 tools/perf/util/include/dwarf-regs.h          |  1 +
 tools/perf/util/unwind-libdw-arch/Build       |  1 -
 .../unwind-libdw-arch/unwind-libdw-s390.c     | 65 -------------------
 tools/perf/util/unwind-libdw.c                | 31 ++-------
 tools/perf/util/unwind-libdw.h                |  1 -
 8 files changed, 62 insertions(+), 94 deletions(-)
 create mode 100644 tools/perf/util/dwarf-regs-arch/dwarf-regs-s390.c
 delete mode 100644 tools/perf/util/unwind-libdw-arch/Build
 delete mode 100644 tools/perf/util/unwind-libdw-arch/unwind-libdw-s390.c

diff --git a/tools/perf/util/dwarf-regs-arch/Build b/tools/perf/util/dwarf-regs-arch/Build
index 94e4dfceb4d1..10c2af3d933a 100644
--- a/tools/perf/util/dwarf-regs-arch/Build
+++ b/tools/perf/util/dwarf-regs-arch/Build
@@ -4,4 +4,5 @@ perf-util-$(CONFIG_LIBDW) += dwarf-regs-csky.o
 perf-util-$(CONFIG_LIBDW) += dwarf-regs-loongarch.o
 perf-util-$(CONFIG_LIBDW) += dwarf-regs-powerpc.o
 perf-util-$(CONFIG_LIBDW) += dwarf-regs-riscv.o
+perf-util-$(CONFIG_LIBDW) += dwarf-regs-s390.o
 perf-util-$(CONFIG_LIBDW) += dwarf-regs-x86.o
diff --git a/tools/perf/util/dwarf-regs-arch/dwarf-regs-s390.c b/tools/perf/util/dwarf-regs-arch/dwarf-regs-s390.c
new file mode 100644
index 000000000000..310a37451bdc
--- /dev/null
+++ b/tools/perf/util/dwarf-regs-arch/dwarf-regs-s390.c
@@ -0,0 +1,53 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <errno.h>
+#include <dwarf-regs.h>
+#include "../../../arch/s390/include/uapi/asm/perf_regs.h"
+
+int __get_dwarf_regnum_for_perf_regnum_s390(int perf_regnum)
+{
+	static const int dwarf_s390_regnums[] = {
+		[PERF_REG_S390_R0] = 0,
+		[PERF_REG_S390_R1] = 1,
+		[PERF_REG_S390_R2] = 2,
+		[PERF_REG_S390_R3] = 3,
+		[PERF_REG_S390_R4] = 4,
+		[PERF_REG_S390_R5] = 5,
+		[PERF_REG_S390_R6] = 6,
+		[PERF_REG_S390_R7] = 7,
+		[PERF_REG_S390_R8] = 8,
+		[PERF_REG_S390_R9] = 9,
+		[PERF_REG_S390_R10] = 10,
+		[PERF_REG_S390_R11] = 11,
+		[PERF_REG_S390_R12] = 12,
+		[PERF_REG_S390_R13] = 13,
+		[PERF_REG_S390_R14] = 14,
+		[PERF_REG_S390_R15] = 15,
+		[PERF_REG_S390_FP0] = 16,
+		[PERF_REG_S390_FP1] = 20,
+		[PERF_REG_S390_FP2] = 17,
+		[PERF_REG_S390_FP3] = 21,
+		[PERF_REG_S390_FP4] = 18,
+		[PERF_REG_S390_FP5] = 22,
+		[PERF_REG_S390_FP6] = 19,
+		[PERF_REG_S390_FP7] = 23,
+		[PERF_REG_S390_FP8] = 24,
+		[PERF_REG_S390_FP9] = 28,
+		[PERF_REG_S390_FP10] = 25,
+		[PERF_REG_S390_FP11] = 29,
+		[PERF_REG_S390_FP12] = 26,
+		[PERF_REG_S390_FP13] = 30,
+		[PERF_REG_S390_FP14] = 27,
+		[PERF_REG_S390_FP15] = 31,
+		[PERF_REG_S390_MASK] = 64,
+		[PERF_REG_S390_PC] = 65,
+	};
+
+	if (perf_regnum == 0)
+		return 0;
+
+	if (perf_regnum <  0 || perf_regnum > (int)ARRAY_SIZE(dwarf_s390_regnums) ||
+	    dwarf_s390_regnums[perf_regnum] == 0)
+		return -ENOENT;
+
+	return dwarf_s390_regnums[perf_regnum];
+}
diff --git a/tools/perf/util/dwarf-regs.c b/tools/perf/util/dwarf-regs.c
index 137568e15018..f86f76547592 100644
--- a/tools/perf/util/dwarf-regs.c
+++ b/tools/perf/util/dwarf-regs.c
@@ -212,6 +212,9 @@ int get_dwarf_regnum_for_perf_regnum(int perf_regnum, unsigned int machine,
 	case EM_RISCV:
 		reg = __get_dwarf_regnum_for_perf_regnum_riscv(perf_regnum);
 		break;
+	case EM_S390:
+		reg = __get_dwarf_regnum_for_perf_regnum_s390(perf_regnum);
+		break;
 	case EM_LOONGARCH:
 		reg = __get_dwarf_regnum_for_perf_regnum_loongarch(perf_regnum);
 		break;
diff --git a/tools/perf/util/include/dwarf-regs.h b/tools/perf/util/include/dwarf-regs.h
index ae76608da110..b95cf2d7b5b3 100644
--- a/tools/perf/util/include/dwarf-regs.h
+++ b/tools/perf/util/include/dwarf-regs.h
@@ -112,6 +112,7 @@ int __get_dwarf_regnum_for_perf_regnum_csky(int perf_regnum, unsigned int flags)
 int __get_dwarf_regnum_for_perf_regnum_loongarch(int perf_regnum);
 int __get_dwarf_regnum_for_perf_regnum_powerpc(int perf_regnum);
 int __get_dwarf_regnum_for_perf_regnum_riscv(int perf_regnum);
+int __get_dwarf_regnum_for_perf_regnum_s390(int perf_regnum);
 
 /*
  * get_dwarf_regnum - Returns DWARF regnum from register name
diff --git a/tools/perf/util/unwind-libdw-arch/Build b/tools/perf/util/unwind-libdw-arch/Build
deleted file mode 100644
index 6d6e319e1201..000000000000
--- a/tools/perf/util/unwind-libdw-arch/Build
+++ /dev/null
@@ -1 +0,0 @@
-perf-util-y += unwind-libdw-s390.o
diff --git a/tools/perf/util/unwind-libdw-arch/unwind-libdw-s390.c b/tools/perf/util/unwind-libdw-arch/unwind-libdw-s390.c
deleted file mode 100644
index 1e05e9d9d95f..000000000000
--- a/tools/perf/util/unwind-libdw-arch/unwind-libdw-s390.c
+++ /dev/null
@@ -1,65 +0,0 @@
-#include <linux/kernel.h>
-#include <elfutils/libdwfl.h>
-#include "util/unwind-libdw.h"
-#include "util/perf_regs.h"
-#include "util/event.h"
-#include "util/sample.h"
-#include "../arch/s390/include/dwarf-regs-table.h"
-#include "../arch/s390/include/uapi/asm/perf_regs.h"
-
-
-bool libdw_set_initial_registers_s390(Dwfl_Thread *thread, void *arg)
-{
-	struct unwind_info *ui = arg;
-	struct regs_dump *user_regs = perf_sample__user_regs(ui->sample);
-	Dwarf_Word dwarf_regs[ARRAY_SIZE(s390_dwarf_regs)];
-
-#define REG(r) ({						\
-	Dwarf_Word val = 0;					\
-	perf_reg_value(&val, user_regs, PERF_REG_S390_##r);	\
-	val;							\
-})
-	/*
-	 * For DWARF register mapping details,
-	 * see also perf/arch/s390/include/dwarf-regs-table.h
-	 */
-	dwarf_regs[0]  = REG(R0);
-	dwarf_regs[1]  = REG(R1);
-	dwarf_regs[2]  = REG(R2);
-	dwarf_regs[3]  = REG(R3);
-	dwarf_regs[4]  = REG(R4);
-	dwarf_regs[5]  = REG(R5);
-	dwarf_regs[6]  = REG(R6);
-	dwarf_regs[7]  = REG(R7);
-	dwarf_regs[8]  = REG(R8);
-	dwarf_regs[9]  = REG(R9);
-	dwarf_regs[10] = REG(R10);
-	dwarf_regs[11] = REG(R11);
-	dwarf_regs[12] = REG(R12);
-	dwarf_regs[13] = REG(R13);
-	dwarf_regs[14] = REG(R14);
-	dwarf_regs[15] = REG(R15);
-
-	dwarf_regs[16] = REG(FP0);
-	dwarf_regs[17] = REG(FP2);
-	dwarf_regs[18] = REG(FP4);
-	dwarf_regs[19] = REG(FP6);
-	dwarf_regs[20] = REG(FP1);
-	dwarf_regs[21] = REG(FP3);
-	dwarf_regs[22] = REG(FP5);
-	dwarf_regs[23] = REG(FP7);
-	dwarf_regs[24] = REG(FP8);
-	dwarf_regs[25] = REG(FP10);
-	dwarf_regs[26] = REG(FP12);
-	dwarf_regs[27] = REG(FP14);
-	dwarf_regs[28] = REG(FP9);
-	dwarf_regs[29] = REG(FP11);
-	dwarf_regs[30] = REG(FP13);
-	dwarf_regs[31] = REG(FP15);
-
-	dwarf_regs[64] = REG(MASK);
-	dwarf_regs[65] = REG(PC);
-
-	dwfl_thread_state_register_pc(thread, dwarf_regs[65]);
-	return dwfl_thread_state_registers(thread, 0, 32, dwarf_regs);
-}
diff --git a/tools/perf/util/unwind-libdw.c b/tools/perf/util/unwind-libdw.c
index b3c4380d40b6..e0321043af88 100644
--- a/tools/perf/util/unwind-libdw.c
+++ b/tools/perf/util/unwind-libdw.c
@@ -226,7 +226,7 @@ static bool memory_read(Dwfl *dwfl __maybe_unused, Dwarf_Addr addr, Dwarf_Word *
 	return true;
 }
 
-static bool libdw_set_initial_registers_generic(Dwfl_Thread *thread, void *arg)
+static bool libdw_set_initial_registers(Dwfl_Thread *thread, void *arg)
 {
 	struct unwind_info *ui = arg;
 	struct regs_dump *user_regs = perf_sample__user_regs(ui->sample);
@@ -279,29 +279,12 @@ static bool libdw_set_initial_registers_generic(Dwfl_Thread *thread, void *arg)
 	return ret;
 }
 
-#define DEFINE_DWFL_THREAD_CALLBACKS(arch)                           \
-static const Dwfl_Thread_Callbacks callbacks_##arch = {              \
-	.next_thread           = next_thread,                        \
-	.memory_read           = memory_read,                        \
-	.set_initial_registers = libdw_set_initial_registers_##arch, \
-}
-
-static const Dwfl_Thread_Callbacks callbacks_generic = {
+static const Dwfl_Thread_Callbacks callbacks = {
 	.next_thread           = next_thread,
 	.memory_read           = memory_read,
-	.set_initial_registers = libdw_set_initial_registers_generic,
+	.set_initial_registers = libdw_set_initial_registers,
 };
 
-DEFINE_DWFL_THREAD_CALLBACKS(s390);
-
-static const Dwfl_Thread_Callbacks *get_thread_callbacks(const char *arch)
-{
-	if (!strcmp(arch, "s390"))
-		return &callbacks_s390;
-
-	return &callbacks_generic;
-}
-
 static int
 frame_callback(Dwfl_Frame *state, void *arg)
 {
@@ -349,10 +332,8 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
 		.e_machine	= e_machine,
 		.best_effort    = best_effort
 	};
-	const char *arch = perf_env__arch(machine->env);
 	Dwarf_Word ip;
 	int err = -EINVAL, i;
-	const Dwfl_Thread_Callbacks *callbacks;
 
 	if (!data->user_regs || !data->user_regs->regs)
 		return -EINVAL;
@@ -375,11 +356,7 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
 	if (err)
 		goto out;
 
-	callbacks = get_thread_callbacks(arch);
-	if (!callbacks)
-		goto out;
-
-	err = !dwfl_attach_state(ui->dwfl, /*elf=*/NULL, thread__tid(thread), callbacks, ui);
+	err = !dwfl_attach_state(ui->dwfl, /*elf=*/NULL, thread__tid(thread), &callbacks, ui);
 	if (err)
 		goto out;
 
diff --git a/tools/perf/util/unwind-libdw.h b/tools/perf/util/unwind-libdw.h
index 5c23080cb6c1..20d63d881dff 100644
--- a/tools/perf/util/unwind-libdw.h
+++ b/tools/perf/util/unwind-libdw.h
@@ -10,7 +10,6 @@ struct perf_sample;
 struct thread;
 
 bool libdw_set_initial_registers_mips(Dwfl_Thread *thread, void *arg);
-bool libdw_set_initial_registers_s390(Dwfl_Thread *thread, void *arg);
 
 struct unwind_info {
 	Dwfl			*dwfl;
-- 
2.52.0.457.g6b5491de43-goog


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

* [PATCH v1 20/23] perf dwarf-regs: Add MIPS perf to dwarf register number mapping functions
  2026-01-17  5:28 [PATCH v1 00/23] perf dwarf/libdw extra support, speed and clean ups Ian Rogers
                   ` (18 preceding siblings ...)
  2026-01-17  5:28 ` [PATCH v1 19/23] perf dwarf-regs: Add S390 " Ian Rogers
@ 2026-01-17  5:28 ` Ian Rogers
  2026-01-17  5:28 ` [PATCH v1 21/23] perf build: Remove NO_LIBDW_DWARF_UNWIND option Ian Rogers
                   ` (2 subsequent siblings)
  22 siblings, 0 replies; 32+ messages in thread
From: Ian Rogers @ 2026-01-17  5:28 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
	Namhyung Kim, Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
	John Garry, Will Deacon, Leo Yan, Guo Ren, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Shimin Guo,
	Athira Rajeev, Stephen Brennan, Howard Chu, Thomas Falcon,
	Andi Kleen, Dr. David Alan Gilbert, Dmitry Vyukov,
	Krzysztof Łopatowski, Chun-Tse Shao, Aditya Bodkhe, Haibo Xu,
	Sergei Trofimovich, linux-kernel, linux-perf-users,
	linux-arm-kernel, linux-csky, linux-riscv, Mark Wielaard

Despite an unused function declaration, there was no unwind-libdw for
MIPS but there is a perf_regs.h and a libdw implementation. Fill in
the pieces so hopefully MIPS unwinding with libdw works.

Signed-off-by: Ian Rogers <irogers@google.com>
---
 tools/perf/util/dwarf-regs-arch/Build             |  1 +
 tools/perf/util/dwarf-regs-arch/dwarf-regs-mips.c | 14 ++++++++++++++
 tools/perf/util/dwarf-regs.c                      |  5 +++++
 tools/perf/util/include/dwarf-regs.h              |  1 +
 tools/perf/util/unwind-libdw.h                    |  2 --
 5 files changed, 21 insertions(+), 2 deletions(-)
 create mode 100644 tools/perf/util/dwarf-regs-arch/dwarf-regs-mips.c

diff --git a/tools/perf/util/dwarf-regs-arch/Build b/tools/perf/util/dwarf-regs-arch/Build
index 10c2af3d933a..ceb68ae86fd8 100644
--- a/tools/perf/util/dwarf-regs-arch/Build
+++ b/tools/perf/util/dwarf-regs-arch/Build
@@ -2,6 +2,7 @@ perf-util-$(CONFIG_LIBDW) += dwarf-regs-arm64.o
 perf-util-$(CONFIG_LIBDW) += dwarf-regs-arm.o
 perf-util-$(CONFIG_LIBDW) += dwarf-regs-csky.o
 perf-util-$(CONFIG_LIBDW) += dwarf-regs-loongarch.o
+perf-util-$(CONFIG_LIBDW) += dwarf-regs-mips.o
 perf-util-$(CONFIG_LIBDW) += dwarf-regs-powerpc.o
 perf-util-$(CONFIG_LIBDW) += dwarf-regs-riscv.o
 perf-util-$(CONFIG_LIBDW) += dwarf-regs-s390.o
diff --git a/tools/perf/util/dwarf-regs-arch/dwarf-regs-mips.c b/tools/perf/util/dwarf-regs-arch/dwarf-regs-mips.c
new file mode 100644
index 000000000000..3bb916b45c66
--- /dev/null
+++ b/tools/perf/util/dwarf-regs-arch/dwarf-regs-mips.c
@@ -0,0 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <errno.h>
+#include <dwarf-regs.h>
+#include "../../../arch/mips/include/uapi/asm/perf_regs.h"
+
+int __get_dwarf_regnum_for_perf_regnum_mips(int perf_regnum)
+{
+	if (perf_regnum == PERF_REG_MIPS_PC)
+		return 37;
+	if (perf_regnum < 0 || perf_regnum >= PERF_REG_MIPS_MAX)
+		return -ENOENT;
+
+	return perf_regnum;
+}
diff --git a/tools/perf/util/dwarf-regs.c b/tools/perf/util/dwarf-regs.c
index f86f76547592..797f455eba0d 100644
--- a/tools/perf/util/dwarf-regs.c
+++ b/tools/perf/util/dwarf-regs.c
@@ -179,6 +179,8 @@ static int get_libdw_frame_nregs(unsigned int machine, unsigned int flags __mayb
 		return 103;
 	case EM_LOONGARCH:
 		return 74;
+	case EM_MIPS:
+		return 71;
 	default:
 		return 0;
 	}
@@ -218,6 +220,9 @@ int get_dwarf_regnum_for_perf_regnum(int perf_regnum, unsigned int machine,
 	case EM_LOONGARCH:
 		reg = __get_dwarf_regnum_for_perf_regnum_loongarch(perf_regnum);
 		break;
+	case EM_MIPS:
+		reg = __get_dwarf_regnum_for_perf_regnum_mips(perf_regnum);
+		break;
 	default:
 		pr_err("ELF MACHINE %x is not supported.\n", machine);
 		return -ENOENT;
diff --git a/tools/perf/util/include/dwarf-regs.h b/tools/perf/util/include/dwarf-regs.h
index b95cf2d7b5b3..46a764cf322f 100644
--- a/tools/perf/util/include/dwarf-regs.h
+++ b/tools/perf/util/include/dwarf-regs.h
@@ -113,6 +113,7 @@ int __get_dwarf_regnum_for_perf_regnum_loongarch(int perf_regnum);
 int __get_dwarf_regnum_for_perf_regnum_powerpc(int perf_regnum);
 int __get_dwarf_regnum_for_perf_regnum_riscv(int perf_regnum);
 int __get_dwarf_regnum_for_perf_regnum_s390(int perf_regnum);
+int __get_dwarf_regnum_for_perf_regnum_mips(int perf_regnum);
 
 /*
  * get_dwarf_regnum - Returns DWARF regnum from register name
diff --git a/tools/perf/util/unwind-libdw.h b/tools/perf/util/unwind-libdw.h
index 20d63d881dff..9c5b5fcaaae8 100644
--- a/tools/perf/util/unwind-libdw.h
+++ b/tools/perf/util/unwind-libdw.h
@@ -9,8 +9,6 @@ struct machine;
 struct perf_sample;
 struct thread;
 
-bool libdw_set_initial_registers_mips(Dwfl_Thread *thread, void *arg);
-
 struct unwind_info {
 	Dwfl			*dwfl;
 	struct perf_sample      *sample;
-- 
2.52.0.457.g6b5491de43-goog


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

* [PATCH v1 21/23] perf build: Remove NO_LIBDW_DWARF_UNWIND option
  2026-01-17  5:28 [PATCH v1 00/23] perf dwarf/libdw extra support, speed and clean ups Ian Rogers
                   ` (19 preceding siblings ...)
  2026-01-17  5:28 ` [PATCH v1 20/23] perf dwarf-regs: Add MIPS " Ian Rogers
@ 2026-01-17  5:28 ` Ian Rogers
  2026-01-17  5:28 ` [PATCH v1 22/23] perf unwind-libdw: Don't discard loaded ELF/Dwarf after every unwind Ian Rogers
  2026-01-17  5:28 ` [PATCH v1 23/23] perf machine: Add inline information to frame pointer and LBR callchains Ian Rogers
  22 siblings, 0 replies; 32+ messages in thread
From: Ian Rogers @ 2026-01-17  5:28 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
	Namhyung Kim, Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
	John Garry, Will Deacon, Leo Yan, Guo Ren, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Shimin Guo,
	Athira Rajeev, Stephen Brennan, Howard Chu, Thomas Falcon,
	Andi Kleen, Dr. David Alan Gilbert, Dmitry Vyukov,
	Krzysztof Łopatowski, Chun-Tse Shao, Aditya Bodkhe, Haibo Xu,
	Sergei Trofimovich, linux-kernel, linux-perf-users,
	linux-arm-kernel, linux-csky, linux-riscv, Mark Wielaard

Libdw unwinding support is present for every architecture that has a
perf_regs.h - perf registers are needed for the initial frame to
unwind. Elfutils also supports sparc, arc and m68k but there is no
support in the Linux kernel for perf registers on these
architectures. As the perf supported dwarf unwinding architectures are
a subset of the elfutils ones, remove NO_LIBDW_DWARF_UNWIND as there
isn't a case of elfutils lacking the support need for perf.

Signed-off-by: Ian Rogers <irogers@google.com>
---
 tools/perf/Makefile.config | 19 +------------------
 tools/perf/tests/make      |  3 +--
 tools/perf/util/Build      |  3 +--
 3 files changed, 3 insertions(+), 22 deletions(-)

diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config
index 6f2c7bd36e74..5e4ae775987f 100644
--- a/tools/perf/Makefile.config
+++ b/tools/perf/Makefile.config
@@ -118,14 +118,6 @@ ifeq ($(ARCH),mips)
   endif
 endif
 
-# So far there's only x86 and arm libdw unwind support merged in perf.
-# Disable it on all other architectures in case libdw unwind
-# support is detected in system. Add supported architectures
-# to the check.
-ifneq ($(SRCARCH),$(filter $(SRCARCH),x86 arm arm64 powerpc s390 csky riscv loongarch))
-  NO_LIBDW_DWARF_UNWIND := 1
-endif
-
 ifneq ($(LIBUNWIND),1)
   NO_LIBUNWIND := 1
 endif
@@ -456,7 +448,6 @@ endif
 ifdef NO_LIBELF
   NO_LIBDW := 1
   NO_LIBUNWIND := 1
-  NO_LIBDW_DWARF_UNWIND := 1
   NO_LIBBPF := 1
   NO_JVMTI := 1
 else
@@ -504,10 +495,6 @@ ifeq ($(feature-libaio), 1)
   endif
 endif
 
-ifdef NO_LIBDW
-  NO_LIBDW_DWARF_UNWIND := 1
-endif
-
 ifeq ($(feature-scandirat), 1)
   # Ignore having scandirat with memory sanitizer that lacks an interceptor.
   ifeq ($(filter s% -fsanitize=memory%,$(EXTRA_CFLAGS),),)
@@ -757,7 +744,7 @@ dwarf-post-unwind-text := BUG
 
 # setup DWARF post unwinder
 ifdef NO_LIBUNWIND
-  ifdef NO_LIBDW_DWARF_UNWIND
+  ifdef NO_LIBDW
     $(warning Disabling post unwind, no support found.)
     dwarf-post-unwind := 0
   else
@@ -767,10 +754,6 @@ ifdef NO_LIBUNWIND
 else
   dwarf-post-unwind-text := libunwind
   $(call detected,CONFIG_LIBUNWIND)
-  # Enable libunwind support by default.
-  ifndef NO_LIBDW_DWARF_UNWIND
-    NO_LIBDW_DWARF_UNWIND := 1
-  endif
 endif
 
 ifeq ($(dwarf-post-unwind),1)
diff --git a/tools/perf/tests/make b/tools/perf/tests/make
index 36411b4b6d2b..767ad9e147a8 100644
--- a/tools/perf/tests/make
+++ b/tools/perf/tests/make
@@ -83,7 +83,6 @@ make_no_demangle    := NO_DEMANGLE=1
 make_no_libelf      := NO_LIBELF=1
 make_no_libdw       := NO_LIBDW=1
 make_libunwind      := LIBUNWIND=1
-make_no_libdw_dwarf_unwind := NO_LIBDW_DWARF_UNWIND=1
 make_no_backtrace   := NO_BACKTRACE=1
 make_no_libcapstone := NO_CAPSTONE=1
 make_no_libnuma     := NO_LIBNUMA=1
@@ -120,7 +119,7 @@ make_static         := LDFLAGS=-static NO_PERF_READ_VDSO32=1 NO_PERF_READ_VDSOX3
 make_minimal        := NO_LIBPYTHON=1 NO_GTK2=1
 make_minimal        += NO_DEMANGLE=1 NO_LIBELF=1 NO_BACKTRACE=1
 make_minimal        += NO_LIBNUMA=1 NO_LIBBIONIC=1 NO_LIBDW=1
-make_minimal        += NO_LIBDW_DWARF_UNWIND=1 NO_LIBBPF=1
+make_minimal        += NO_LIBBPF=1
 make_minimal        += NO_SDT=1 NO_JVMTI=1 NO_LIBZSTD=1
 make_minimal        += NO_LIBCAP=1 NO_CAPSTONE=1
 
diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index 3cb1edd263cf..c30ff257f8b4 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -223,9 +223,8 @@ perf-util-$(CONFIG_LIBDW) += dwarf-regs-arch/
 perf-util-$(CONFIG_LIBDW) += debuginfo.o
 perf-util-$(CONFIG_LIBDW) += annotate-data.o
 perf-util-$(CONFIG_LIBDW) += libdw.o
+perf-util-$(CONFIG_LIBDW) += unwind-libdw.o
 
-perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
-perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw-arch/
 perf-util-$(CONFIG_LOCAL_LIBUNWIND)    += unwind-libunwind-local.o
 perf-util-$(CONFIG_LIBUNWIND)          += unwind-libunwind.o
 perf-util-$(CONFIG_LIBUNWIND_X86)      += libunwind/x86_32.o
-- 
2.52.0.457.g6b5491de43-goog


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

* [PATCH v1 22/23] perf unwind-libdw: Don't discard loaded ELF/Dwarf after every unwind
  2026-01-17  5:28 [PATCH v1 00/23] perf dwarf/libdw extra support, speed and clean ups Ian Rogers
                   ` (20 preceding siblings ...)
  2026-01-17  5:28 ` [PATCH v1 21/23] perf build: Remove NO_LIBDW_DWARF_UNWIND option Ian Rogers
@ 2026-01-17  5:28 ` Ian Rogers
  2026-01-27 17:42   ` Serhei Makarov
  2026-01-17  5:28 ` [PATCH v1 23/23] perf machine: Add inline information to frame pointer and LBR callchains Ian Rogers
  22 siblings, 1 reply; 32+ messages in thread
From: Ian Rogers @ 2026-01-17  5:28 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
	Namhyung Kim, Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
	John Garry, Will Deacon, Leo Yan, Guo Ren, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Shimin Guo,
	Athira Rajeev, Stephen Brennan, Howard Chu, Thomas Falcon,
	Andi Kleen, Dr. David Alan Gilbert, Dmitry Vyukov,
	Krzysztof Łopatowski, Chun-Tse Shao, Aditya Bodkhe, Haibo Xu,
	Sergei Trofimovich, linux-kernel, linux-perf-users,
	linux-arm-kernel, linux-csky, linux-riscv, Mark Wielaard

The unwind-libdw dwfl has ELF binaries associated with mmap
addresses. Experimenting with using the per dso dwfl it is required to
alter the address to be 0 based variant. Unfortunately libdwfl doesn't
allow a single unwind and then an update to the return address to be 0
based as there are assertions that registers aren't updated once an
unwind has started, etc.

As removing the dwfl didn't prove possible, an alternative is to just
not discard the dwfl when the unwind ends. The dwfl is valid for a
process unless a dso is loaded at the same address as a previous
one. So keep the dwfl with the maps, invalidate it if a map is removed
(in case a new map replaces it) and recycle the dwfl in the unwinding
code. A wrinkly in the implementation of this is that the attached
thread argument is remembered by the dwfl and so it needs to be a
pointer to memory that also persists with the dwfl (struct
dwfl_ui_thread_info in the code).

Recording 10 seconds of system wide data with --call-graph=dwarf and
then processing with perf report shows a total runtime improvement
from 41.583s to 2.279s (an 18x speedup).

Signed-off-by: Ian Rogers <irogers@google.com>
---
 tools/perf/util/maps.c         | 36 +++++++++++++-
 tools/perf/util/maps.h         |  4 ++
 tools/perf/util/unwind-libdw.c | 90 +++++++++++++++++++++++++---------
 tools/perf/util/unwind-libdw.h |  9 +++-
 4 files changed, 112 insertions(+), 27 deletions(-)

diff --git a/tools/perf/util/maps.c b/tools/perf/util/maps.c
index c321d4f4d846..8ccc46d515b6 100644
--- a/tools/perf/util/maps.c
+++ b/tools/perf/util/maps.c
@@ -10,6 +10,7 @@
 #include "thread.h"
 #include "ui/ui.h"
 #include "unwind.h"
+#include "unwind-libdw.h"
 #include <internal/rc_check.h>
 
 /*
@@ -39,6 +40,9 @@ DECLARE_RC_STRUCT(maps) {
 #ifdef HAVE_LIBUNWIND_SUPPORT
 	void		*addr_space;
 	const struct unwind_libunwind_ops *unwind_libunwind_ops;
+#endif
+#ifdef HAVE_LIBDW_SUPPORT
+	void		*libdw_addr_space_dwfl;
 #endif
 	refcount_t	 refcnt;
 	/**
@@ -203,6 +207,17 @@ void maps__set_unwind_libunwind_ops(struct maps *maps, const struct unwind_libun
 	RC_CHK_ACCESS(maps)->unwind_libunwind_ops = ops;
 }
 #endif
+#ifdef HAVE_LIBDW_SUPPORT
+void *maps__libdw_addr_space_dwfl(const struct maps *maps)
+{
+	return RC_CHK_ACCESS(maps)->libdw_addr_space_dwfl;
+}
+
+void maps__set_libdw_addr_space_dwfl(struct maps *maps, void *dwfl)
+{
+	RC_CHK_ACCESS(maps)->libdw_addr_space_dwfl = dwfl;
+}
+#endif
 
 static struct rw_semaphore *maps__lock(struct maps *maps)
 {
@@ -218,6 +233,9 @@ static void maps__init(struct maps *maps, struct machine *machine)
 #ifdef HAVE_LIBUNWIND_SUPPORT
 	RC_CHK_ACCESS(maps)->addr_space = NULL;
 	RC_CHK_ACCESS(maps)->unwind_libunwind_ops = NULL;
+#endif
+#ifdef HAVE_LIBDW_SUPPORT
+	RC_CHK_ACCESS(maps)->libdw_addr_space_dwfl = NULL;
 #endif
 	refcount_set(maps__refcnt(maps), 1);
 	RC_CHK_ACCESS(maps)->nr_maps = 0;
@@ -240,6 +258,9 @@ static void maps__exit(struct maps *maps)
 	zfree(&maps_by_address);
 	zfree(&maps_by_name);
 	unwind__finish_access(maps);
+#ifdef HAVE_LIBDW_SUPPORT
+	libdw__invalidate_dwfl(maps, maps__libdw_addr_space_dwfl(maps));
+#endif
 }
 
 struct maps *maps__new(struct machine *machine)
@@ -549,6 +570,9 @@ void maps__remove(struct maps *maps, struct map *map)
 	__maps__remove(maps, map);
 	check_invariants(maps);
 	up_write(maps__lock(maps));
+#ifdef HAVE_LIBDW_SUPPORT
+	libdw__invalidate_dwfl(maps, maps__libdw_addr_space_dwfl(maps));
+#endif
 }
 
 bool maps__empty(struct maps *maps)
@@ -604,18 +628,26 @@ int maps__for_each_map(struct maps *maps, int (*cb)(struct map *map, void *data)
 void maps__remove_maps(struct maps *maps, bool (*cb)(struct map *map, void *data), void *data)
 {
 	struct map **maps_by_address;
+	bool removed = false;
 
 	down_write(maps__lock(maps));
 
 	maps_by_address = maps__maps_by_address(maps);
 	for (unsigned int i = 0; i < maps__nr_maps(maps);) {
-		if (cb(maps_by_address[i], data))
+		if (cb(maps_by_address[i], data)) {
 			__maps__remove(maps, maps_by_address[i]);
-		else
+			removed = true;
+		} else {
 			i++;
+		}
 	}
 	check_invariants(maps);
 	up_write(maps__lock(maps));
+	if (removed) {
+#ifdef HAVE_LIBDW_SUPPORT
+		libdw__invalidate_dwfl(maps, maps__libdw_addr_space_dwfl(maps));
+#endif
+	}
 }
 
 struct symbol *maps__find_symbol(struct maps *maps, u64 addr, struct map **mapp)
diff --git a/tools/perf/util/maps.h b/tools/perf/util/maps.h
index d9aa62ed968a..20c52084ba9e 100644
--- a/tools/perf/util/maps.h
+++ b/tools/perf/util/maps.h
@@ -52,6 +52,10 @@ void maps__set_addr_space(struct maps *maps, void *addr_space);
 const struct unwind_libunwind_ops *maps__unwind_libunwind_ops(const struct maps *maps);
 void maps__set_unwind_libunwind_ops(struct maps *maps, const struct unwind_libunwind_ops *ops);
 #endif
+#ifdef HAVE_LIBDW_SUPPORT
+void *maps__libdw_addr_space_dwfl(const struct maps *maps);
+void maps__set_libdw_addr_space_dwfl(struct maps *maps, void *dwfl);
+#endif
 
 size_t maps__fprintf(struct maps *maps, FILE *fp);
 
diff --git a/tools/perf/util/unwind-libdw.c b/tools/perf/util/unwind-libdw.c
index e0321043af88..c1646ef5f971 100644
--- a/tools/perf/util/unwind-libdw.c
+++ b/tools/perf/util/unwind-libdw.c
@@ -20,6 +20,17 @@
 #include "callchain.h"
 #include "util/env.h"
 
+/*
+ * The dwfl thread argument passed to functions like memory_read. Memory has to
+ * be allocated to persist of multiple uses of the dwfl.
+ */
+struct dwfl_ui_thread_info {
+	/* Back link to the dwfl. */
+	Dwfl *dwfl;
+	/* The current unwind info, only 1 is supported. */
+	struct unwind_info *ui;
+};
+
 static char *debuginfo_path;
 
 static int __find_debuginfo(Dwfl_Module *mod __maybe_unused, void **userdata,
@@ -35,6 +46,19 @@ static int __find_debuginfo(Dwfl_Module *mod __maybe_unused, void **userdata,
 	return -1;
 }
 
+void libdw__invalidate_dwfl(struct maps *maps, void *arg)
+{
+	struct dwfl_ui_thread_info *dwfl_ui_ti = arg;
+
+	if (!dwfl_ui_ti)
+		return;
+
+	assert(dwfl_ui_ti->ui == NULL);
+	maps__set_libdw_addr_space_dwfl(maps, NULL);
+	dwfl_end(dwfl_ui_ti->dwfl);
+	free(dwfl_ui_ti);
+}
+
 static const Dwfl_Callbacks offline_callbacks = {
 	.find_debuginfo		= __find_debuginfo,
 	.debuginfo_path		= &debuginfo_path,
@@ -187,7 +211,8 @@ static int access_dso_mem(struct unwind_info *ui, Dwarf_Addr addr,
 static bool memory_read(Dwfl *dwfl __maybe_unused, Dwarf_Addr addr, Dwarf_Word *result,
 			void *arg)
 {
-	struct unwind_info *ui = arg;
+	struct dwfl_ui_thread_info *dwfl_ui_ti = arg;
+	struct unwind_info *ui = dwfl_ui_ti->ui;
 	uint16_t e_machine = thread__e_machine(ui->thread, ui->machine);
 	struct stack_dump *stack = &ui->sample->user_stack;
 	u64 start, end;
@@ -228,7 +253,8 @@ static bool memory_read(Dwfl *dwfl __maybe_unused, Dwarf_Addr addr, Dwarf_Word *
 
 static bool libdw_set_initial_registers(Dwfl_Thread *thread, void *arg)
 {
-	struct unwind_info *ui = arg;
+	struct dwfl_ui_thread_info *dwfl_ui_ti = arg;
+	struct unwind_info *ui = dwfl_ui_ti->ui;
 	struct regs_dump *user_regs = perf_sample__user_regs(ui->sample);
 	Dwarf_Word *dwarf_regs;
 	int max_dwarf_reg = 0;
@@ -320,33 +346,50 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
 			int max_stack,
 			bool best_effort)
 {
-	struct machine *machine = maps__machine(thread__maps(thread));
+	struct maps *maps = thread__maps(thread);
+	struct machine *machine = maps__machine(maps);
 	uint16_t e_machine = thread__e_machine(thread, machine);
-	struct unwind_info *ui, ui_buf = {
-		.sample		= data,
-		.thread		= thread,
-		.machine	= machine,
-		.cb		= cb,
-		.arg		= arg,
-		.max_stack	= max_stack,
-		.e_machine	= e_machine,
-		.best_effort    = best_effort
-	};
+	struct dwfl_ui_thread_info *dwfl_ui_ti;
+	static struct unwind_info *ui;
+	Dwfl *dwfl;
 	Dwarf_Word ip;
 	int err = -EINVAL, i;
 
 	if (!data->user_regs || !data->user_regs->regs)
 		return -EINVAL;
 
-	ui = zalloc(sizeof(ui_buf) + sizeof(ui_buf.entries[0]) * max_stack);
+	ui = zalloc(sizeof(*ui) + sizeof(ui->entries[0]) * max_stack);
 	if (!ui)
 		return -ENOMEM;
 
-	*ui = ui_buf;
+	*ui = (struct unwind_info){
+		.sample		= data,
+		.thread		= thread,
+		.machine	= machine,
+		.cb		= cb,
+		.arg		= arg,
+		.max_stack	= max_stack,
+		.e_machine	= e_machine,
+		.best_effort    = best_effort
+	};
 
-	ui->dwfl = dwfl_begin(&offline_callbacks);
-	if (!ui->dwfl)
-		goto out;
+	dwfl_ui_ti = maps__libdw_addr_space_dwfl(maps);
+	if (dwfl_ui_ti) {
+		dwfl = dwfl_ui_ti->dwfl;
+	} else {
+		dwfl_ui_ti = zalloc(sizeof(*dwfl_ui_ti));
+		dwfl = dwfl_begin(&offline_callbacks);
+		if (!dwfl)
+			goto out;
+
+		dwfl_ui_ti->dwfl = dwfl;
+		maps__set_libdw_addr_space_dwfl(maps, dwfl_ui_ti);
+	}
+	assert(dwfl_ui_ti->ui == NULL);
+	assert(dwfl_ui_ti->dwfl == dwfl);
+	assert(dwfl_ui_ti == maps__libdw_addr_space_dwfl(maps));
+	dwfl_ui_ti->ui = ui;
+	ui->dwfl = dwfl;
 
 	err = perf_reg_value(&ip, data->user_regs, perf_arch_reg_ip(e_machine));
 	if (err)
@@ -356,11 +399,12 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
 	if (err)
 		goto out;
 
-	err = !dwfl_attach_state(ui->dwfl, /*elf=*/NULL, thread__tid(thread), &callbacks, ui);
-	if (err)
-		goto out;
+	dwfl_attach_state(dwfl, /*elf=*/NULL, thread__tid(thread), &callbacks,
+			  /* Dwfl thread function argument*/dwfl_ui_ti);
+	// Ignore thread already attached error.
 
-	err = dwfl_getthread_frames(ui->dwfl, thread__tid(thread), frame_callback, ui);
+	err = dwfl_getthread_frames(dwfl, thread__tid(thread), frame_callback,
+				    /* Dwfl frame function argument*/ui);
 
 	if (err && ui->max_stack != max_stack)
 		err = 0;
@@ -384,7 +428,7 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
 	for (i = 0; i < ui->idx; i++)
 		map_symbol__exit(&ui->entries[i].ms);
 
-	dwfl_end(ui->dwfl);
+	dwfl_ui_ti->ui = NULL;
 	free(ui);
 	return 0;
 }
diff --git a/tools/perf/util/unwind-libdw.h b/tools/perf/util/unwind-libdw.h
index 9c5b5fcaaae8..3dec0ab8bd50 100644
--- a/tools/perf/util/unwind-libdw.h
+++ b/tools/perf/util/unwind-libdw.h
@@ -2,15 +2,17 @@
 #ifndef __PERF_UNWIND_LIBDW_H
 #define __PERF_UNWIND_LIBDW_H
 
-#include <elfutils/libdwfl.h>
+#include <stdint.h>
 #include "unwind.h"
 
 struct machine;
 struct perf_sample;
 struct thread;
 
+#ifdef HAVE_LIBDW_SUPPORT
+
 struct unwind_info {
-	Dwfl			*dwfl;
+	void			*dwfl;
 	struct perf_sample      *sample;
 	struct machine          *machine;
 	struct thread           *thread;
@@ -23,4 +25,7 @@ struct unwind_info {
 	struct unwind_entry	entries[];
 };
 
+void libdw__invalidate_dwfl(struct maps *maps, void *dwfl);
+#endif
+
 #endif /* __PERF_UNWIND_LIBDW_H */
-- 
2.52.0.457.g6b5491de43-goog


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

* [PATCH v1 23/23] perf machine: Add inline information to frame pointer and LBR callchains
  2026-01-17  5:28 [PATCH v1 00/23] perf dwarf/libdw extra support, speed and clean ups Ian Rogers
                   ` (21 preceding siblings ...)
  2026-01-17  5:28 ` [PATCH v1 22/23] perf unwind-libdw: Don't discard loaded ELF/Dwarf after every unwind Ian Rogers
@ 2026-01-17  5:28 ` Ian Rogers
  22 siblings, 0 replies; 32+ messages in thread
From: Ian Rogers @ 2026-01-17  5:28 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
	Namhyung Kim, Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
	John Garry, Will Deacon, Leo Yan, Guo Ren, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Shimin Guo,
	Athira Rajeev, Stephen Brennan, Howard Chu, Thomas Falcon,
	Andi Kleen, Dr. David Alan Gilbert, Dmitry Vyukov,
	Krzysztof Łopatowski, Chun-Tse Shao, Aditya Bodkhe, Haibo Xu,
	Sergei Trofimovich, linux-kernel, linux-perf-users,
	linux-arm-kernel, linux-csky, linux-riscv, Mark Wielaard

Use append_inlines in frame pointer and LBR cases. Update the
addr2line test to also test frame pointers. LBR is also updated but
inaccuracy in the branched to IP means the inline information is
missing in the leaf. Leave LBR callchains untested for now.

Signed-off-by: Ian Rogers <irogers@google.com>
---
 tools/perf/tests/shell/addr2line_inlines.sh |  31 +++++-
 tools/perf/util/machine.c                   | 104 +++++++++++---------
 2 files changed, 86 insertions(+), 49 deletions(-)

diff --git a/tools/perf/tests/shell/addr2line_inlines.sh b/tools/perf/tests/shell/addr2line_inlines.sh
index 4a5b6f5be23d..ce30d9c7e0bf 100755
--- a/tools/perf/tests/shell/addr2line_inlines.sh
+++ b/tools/perf/tests/shell/addr2line_inlines.sh
@@ -21,8 +21,28 @@ trap_cleanup() {
 }
 trap trap_cleanup EXIT TERM INT
 
-test_inlinedloop() {
-    echo "Inline unwinding verification test"
+test_fp() {
+    echo "Inline unwinding fp verification test"
+    # Record data. Currently only dwarf callchains support inlined functions.
+    perf record --call-graph fp -e task-clock:u -o "${perf_data}" -- perf test -w inlineloop 1
+
+    # Check output with inline (default) and srcline
+    perf script -i "${perf_data}" --fields +srcline > "${perf_script_txt}"
+
+    # Expect the leaf and middle functions to occur on lines in the 20s, with
+    # the non-inlined parent function on a line in the 30s.
+    if grep -q "inlineloop.c:2. (inlined)" "${perf_script_txt}" &&
+       grep -q "inlineloop.c:3.$" "${perf_script_txt}"
+    then
+        echo "Inline unwinding fp verification test [Success]"
+    else
+        echo "Inline unwinding fp verification test [Failed missing inlined functions]"
+        err=1
+    fi
+}
+
+test_dwarf() {
+    echo "Inline unwinding dwarf verification test"
     # Record data. Currently only dwarf callchains support inlined functions.
     perf record --call-graph dwarf -e task-clock:u -o "${perf_data}" -- perf test -w inlineloop 1
 
@@ -34,14 +54,15 @@ test_inlinedloop() {
     if grep -q "inlineloop.c:2. (inlined)" "${perf_script_txt}" &&
        grep -q "inlineloop.c:3.$" "${perf_script_txt}"
     then
-        echo "Inline unwinding verification test [Success]"
+        echo "Inline unwinding dwarf verification test [Success]"
     else
-        echo "Inline unwinding verification test [Failed missing inlined functions]"
+        echo "Inline unwinding dwarf verification test [Failed missing inlined functions]"
         err=1
     fi
 }
 
-test_inlinedloop
+test_fp
+test_dwarf
 
 cleanup
 exit $err
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index 841b711d970e..30d606fbf040 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -2090,6 +2090,59 @@ struct iterations {
 	u64 cycles;
 };
 
+static int append_inlines(struct callchain_cursor *cursor, struct map_symbol *ms, u64 ip,
+			bool branch, struct branch_flags *flags, int nr_loop_iter,
+			u64 iter_cycles, u64 branch_from)
+{
+	struct symbol *sym = ms->sym;
+	struct map *map = ms->map;
+	struct inline_node *inline_node;
+	struct inline_list *ilist;
+	struct dso *dso;
+	u64 addr;
+	int ret = 1;
+	struct map_symbol ilist_ms;
+	bool first = true;
+
+	if (!symbol_conf.inline_name || !map || !sym)
+		return ret;
+
+	addr = map__dso_map_ip(map, ip);
+	addr = map__rip_2objdump(map, addr);
+	dso = map__dso(map);
+
+	inline_node = inlines__tree_find(dso__inlined_nodes(dso), addr);
+	if (!inline_node) {
+		inline_node = dso__parse_addr_inlines(dso, addr, sym);
+		if (!inline_node)
+			return ret;
+		inlines__tree_insert(dso__inlined_nodes(dso), inline_node);
+	}
+
+	ilist_ms = (struct map_symbol) {
+		.maps = maps__get(ms->maps),
+		.map = map__get(map),
+	};
+	list_for_each_entry(ilist, &inline_node->val, list) {
+		ilist_ms.sym = ilist->symbol;
+		if (first) {
+			ret = callchain_cursor_append(cursor, ip, &ilist_ms,
+						      branch, flags, nr_loop_iter,
+						      iter_cycles, branch_from, ilist->srcline);
+		} else {
+			ret = callchain_cursor_append(cursor, ip, &ilist_ms, false,
+						      NULL, 0, 0, 0, ilist->srcline);
+		}
+		first = false;
+
+		if (ret != 0)
+			return ret;
+	}
+	map_symbol__exit(&ilist_ms);
+
+	return ret;
+}
+
 static int add_callchain_ip(struct thread *thread,
 			    struct callchain_cursor *cursor,
 			    struct symbol **parent,
@@ -2170,6 +2223,11 @@ static int add_callchain_ip(struct thread *thread,
 	ms.maps = maps__get(al.maps);
 	ms.map = map__get(al.map);
 	ms.sym = al.sym;
+
+	if (append_inlines(cursor, &ms, ip, branch, flags, nr_loop_iter,
+			   iter_cycles, branch_from) == 0)
+		goto out;
+
 	srcline = callchain_srcline(&ms, al.addr);
 	err = callchain_cursor_append(cursor, ip, &ms,
 				      branch, flags, nr_loop_iter,
@@ -2888,49 +2946,6 @@ static int thread__resolve_callchain_sample(struct thread *thread,
 	return 0;
 }
 
-static int append_inlines(struct callchain_cursor *cursor, struct map_symbol *ms, u64 ip)
-{
-	struct symbol *sym = ms->sym;
-	struct map *map = ms->map;
-	struct inline_node *inline_node;
-	struct inline_list *ilist;
-	struct dso *dso;
-	u64 addr;
-	int ret = 1;
-	struct map_symbol ilist_ms;
-
-	if (!symbol_conf.inline_name || !map || !sym)
-		return ret;
-
-	addr = map__dso_map_ip(map, ip);
-	addr = map__rip_2objdump(map, addr);
-	dso = map__dso(map);
-
-	inline_node = inlines__tree_find(dso__inlined_nodes(dso), addr);
-	if (!inline_node) {
-		inline_node = dso__parse_addr_inlines(dso, addr, sym);
-		if (!inline_node)
-			return ret;
-		inlines__tree_insert(dso__inlined_nodes(dso), inline_node);
-	}
-
-	ilist_ms = (struct map_symbol) {
-		.maps = maps__get(ms->maps),
-		.map = map__get(map),
-	};
-	list_for_each_entry(ilist, &inline_node->val, list) {
-		ilist_ms.sym = ilist->symbol;
-		ret = callchain_cursor_append(cursor, ip, &ilist_ms, false,
-					      NULL, 0, 0, 0, ilist->srcline);
-
-		if (ret != 0)
-			return ret;
-	}
-	map_symbol__exit(&ilist_ms);
-
-	return ret;
-}
-
 static int unwind_entry(struct unwind_entry *entry, void *arg)
 {
 	struct callchain_cursor *cursor = arg;
@@ -2940,7 +2955,8 @@ static int unwind_entry(struct unwind_entry *entry, void *arg)
 	if (symbol_conf.hide_unresolved && entry->ms.sym == NULL)
 		return 0;
 
-	if (append_inlines(cursor, &entry->ms, entry->ip) == 0)
+	if (append_inlines(cursor, &entry->ms, entry->ip, /*branch=*/false, /*branch_flags=*/NULL,
+			   /*nr_loop_iter=*/0, /*iter_cycles=*/0, /*branch_from=*/0) == 0)
 		return 0;
 
 	/*
-- 
2.52.0.457.g6b5491de43-goog


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

* Re: [PATCH v1 12/23] perf dwarf-regs: Add get_dwarf_regnum_for_perf_regnum and use for x86 unwinding
  2026-01-17  5:28 ` [PATCH v1 12/23] perf dwarf-regs: Add get_dwarf_regnum_for_perf_regnum and use for x86 unwinding Ian Rogers
@ 2026-01-17  5:42   ` Ian Rogers
  0 siblings, 0 replies; 32+ messages in thread
From: Ian Rogers @ 2026-01-17  5:42 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
	Namhyung Kim, Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
	John Garry, Will Deacon, Leo Yan, Guo Ren, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Shimin Guo,
	Athira Rajeev, Stephen Brennan, Howard Chu, Thomas Falcon,
	Andi Kleen, Dr. David Alan Gilbert, Dmitry Vyukov,
	Krzysztof Łopatowski, Chun-Tse Shao, Aditya Bodkhe, Haibo Xu,
	Sergei Trofimovich, linux-kernel, linux-perf-users,
	linux-arm-kernel, linux-csky, linux-riscv, Mark Wielaard

On Fri, Jan 16, 2026 at 9:29 PM Ian Rogers <irogers@google.com> wrote:
>
> Add a utility to map a perf register number to a dwarf register number
> for a particular ELF machine type.
>
> Create a generic unwind-libdw initial register initialization routine
> that uses this function and thereby avoids arch specific
> initialization. The unwind-libdw code does:
> 1) compute the maximum dwarf register from the set of sampled user registers,
> 2) allocates a set of dwarf registers,
> 3) copies the sample registers into the appropriate dwarf registers.
>
> This generic solution is initially implemented for use with x86 as
> only get_dwarf_regnum_for_perf_regnum support for x86 is currently present.
>
> Signed-off-by: Ian Rogers <irogers@google.com>
> ---
>  .../util/dwarf-regs-arch/dwarf-regs-x86.c     | 95 +++++++++++++++++++
>  tools/perf/util/dwarf-regs.c                  | 55 +++++++++++
>  tools/perf/util/include/dwarf-regs.h          |  8 ++
>  tools/perf/util/unwind-libdw-arch/Build       |  1 -
>  .../util/unwind-libdw-arch/unwind-libdw-x86.c | 54 -----------
>  tools/perf/util/unwind-libdw.c                | 70 ++++++++++++--
>  tools/perf/util/unwind-libdw.h                |  2 +-
>  7 files changed, 222 insertions(+), 63 deletions(-)
>  delete mode 100644 tools/perf/util/unwind-libdw-arch/unwind-libdw-x86.c
>
> diff --git a/tools/perf/util/dwarf-regs-arch/dwarf-regs-x86.c b/tools/perf/util/dwarf-regs-arch/dwarf-regs-x86.c
> index f0c42e4d7423..cadef120aeb4 100644
> --- a/tools/perf/util/dwarf-regs-arch/dwarf-regs-x86.c
> +++ b/tools/perf/util/dwarf-regs-arch/dwarf-regs-x86.c
> @@ -10,6 +10,7 @@
>  #include <string.h> /* for strcmp */
>  #include <linux/kernel.h> /* for ARRAY_SIZE */
>  #include <dwarf-regs.h>
> +#include "../../../arch/x86/include/uapi/asm/perf_regs.h"
>
>  struct dwarf_regs_idx {
>         const char *name;
> @@ -163,3 +164,97 @@ int __get_dwarf_regnum_x86_64(const char *name)
>  {
>         return get_regnum(x86_64_regidx_table, ARRAY_SIZE(x86_64_regidx_table), name);
>  }
> +
> +int __get_dwarf_regnum_for_perf_regnum_i386(int perf_regnum)
> +{
> +       static const int dwarf_i386_regnums[] = {
> +               [PERF_REG_X86_AX] = 0,
> +               [PERF_REG_X86_BX] = 3,
> +               [PERF_REG_X86_CX] = 1,
> +               [PERF_REG_X86_DX] = 2,
> +               [PERF_REG_X86_SI] = 6,
> +               [PERF_REG_X86_DI] = 7,
> +               [PERF_REG_X86_BP] = 5,
> +               [PERF_REG_X86_SP] = 4,
> +               [PERF_REG_X86_IP] = 8,
> +               [PERF_REG_X86_FLAGS] = 9,
> +               [PERF_REG_X86_CS] = 41,
> +               [PERF_REG_X86_SS] = 42,
> +               [PERF_REG_X86_DS] = 43,
> +               [PERF_REG_X86_ES] = 40,
> +               [PERF_REG_X86_FS] = 44,
> +               [PERF_REG_X86_GS] = 45,
> +               [PERF_REG_X86_XMM0] = 21,
> +               [PERF_REG_X86_XMM1] = 22,
> +               [PERF_REG_X86_XMM2] = 23,
> +               [PERF_REG_X86_XMM3] = 24,
> +               [PERF_REG_X86_XMM4] = 25,
> +               [PERF_REG_X86_XMM5] = 26,
> +               [PERF_REG_X86_XMM6] = 27,
> +               [PERF_REG_X86_XMM7] = 28,
> +       };
> +
> +       if (perf_regnum == 0)
> +               return 0;
> +
> +       if (perf_regnum <  0 || perf_regnum > (int)ARRAY_SIZE(dwarf_i386_regnums) ||
> +           dwarf_i386_regnums[perf_regnum] == 0)
> +               return -ENOENT;
> +
> +       return dwarf_i386_regnums[perf_regnum];
> +}
> +
> +int __get_dwarf_regnum_for_perf_regnum_x86_64(int perf_regnum)
> +{
> +       static const int dwarf_x86_64_regnums[] = {
> +               [PERF_REG_X86_AX] = 0,
> +               [PERF_REG_X86_BX] = 3,
> +               [PERF_REG_X86_CX] = 2,
> +               [PERF_REG_X86_DX] = 1,
> +               [PERF_REG_X86_SI] = 4,
> +               [PERF_REG_X86_DI] = 5,
> +               [PERF_REG_X86_BP] = 6,
> +               [PERF_REG_X86_SP] = 7,
> +               [PERF_REG_X86_IP] = 16,
> +               [PERF_REG_X86_FLAGS] = 49,
> +               [PERF_REG_X86_CS] = 51,
> +               [PERF_REG_X86_SS] = 52,
> +               [PERF_REG_X86_DS] = 53,
> +               [PERF_REG_X86_ES] = 50,
> +               [PERF_REG_X86_FS] = 54,
> +               [PERF_REG_X86_GS] = 55,
> +               [PERF_REG_X86_R8] = 8,
> +               [PERF_REG_X86_R9] = 9,
> +               [PERF_REG_X86_R10] = 10,
> +               [PERF_REG_X86_R11] = 11,
> +               [PERF_REG_X86_R12] = 12,
> +               [PERF_REG_X86_R13] = 13,
> +               [PERF_REG_X86_R14] = 14,
> +               [PERF_REG_X86_R15] = 15,
> +               [PERF_REG_X86_XMM0] = 17,
> +               [PERF_REG_X86_XMM1] = 18,
> +               [PERF_REG_X86_XMM2] = 19,
> +               [PERF_REG_X86_XMM3] = 20,
> +               [PERF_REG_X86_XMM4] = 21,
> +               [PERF_REG_X86_XMM5] = 22,
> +               [PERF_REG_X86_XMM6] = 23,
> +               [PERF_REG_X86_XMM7] = 24,
> +               [PERF_REG_X86_XMM8] = 25,
> +               [PERF_REG_X86_XMM9] = 26,
> +               [PERF_REG_X86_XMM10] = 27,
> +               [PERF_REG_X86_XMM11] = 28,
> +               [PERF_REG_X86_XMM12] = 29,
> +               [PERF_REG_X86_XMM13] = 30,
> +               [PERF_REG_X86_XMM14] = 31,
> +               [PERF_REG_X86_XMM15] = 32,
> +       };
> +
> +       if (perf_regnum == 0)
> +               return 0;
> +
> +       if (perf_regnum <  0 || perf_regnum > (int)ARRAY_SIZE(dwarf_x86_64_regnums) ||
> +           dwarf_x86_64_regnums[perf_regnum] == 0)
> +               return -ENOENT;
> +
> +       return dwarf_x86_64_regnums[perf_regnum];
> +}
> diff --git a/tools/perf/util/dwarf-regs.c b/tools/perf/util/dwarf-regs.c
> index ef249dd589e3..1f7d892612df 100644
> --- a/tools/perf/util/dwarf-regs.c
> +++ b/tools/perf/util/dwarf-regs.c
> @@ -103,3 +103,58 @@ int get_dwarf_regnum(const char *name, unsigned int machine, unsigned int flags
>         free(regname);
>         return reg;
>  }
> +
> +static int get_libdw_frame_nregs(unsigned int machine, unsigned int flags __maybe_unused)
> +{
> +       switch (machine) {
> +       case EM_X86_64:
> +               return 17;
> +       case EM_386:
> +               return 9;
> +       case EM_ARM:
> +               return 16;
> +       case EM_AARCH64:
> +               return 97;
> +       case EM_CSKY:
> +               return 38;
> +       case EM_S390:
> +               return 32;
> +       case EM_PPC:
> +       case EM_PPC64:
> +               return 145;
> +       case EM_RISCV:
> +               return 66;
> +       case EM_SPARC:
> +       case EM_SPARCV9:
> +               return 103;
> +       case EM_LOONGARCH:
> +               return 74;
> +       default:
> +               return 0;
> +       }
> +}
> +
> +int get_dwarf_regnum_for_perf_regnum(int perf_regnum, unsigned int machine,
> +                                    unsigned int flags, bool only_libdw_supported)
> +{
> +       int reg;
> +
> +       switch (machine) {
> +       case EM_X86_64:
> +               reg = __get_dwarf_regnum_for_perf_regnum_x86_64(perf_regnum);
> +               break;
> +       case EM_386:
> +               reg = __get_dwarf_regnum_for_perf_regnum_i386(perf_regnum);
> +               break;
> +       default:
> +               pr_err("ELF MACHINE %x is not supported.\n", machine);
> +               return -ENOENT;
> +       }
> +       if (reg >= 0 && only_libdw_supported) {
> +               int nregs = get_libdw_frame_nregs(machine, flags);
> +
> +               if (reg >= nregs)
> +                       reg = -ENOENT;
> +       }
> +       return reg;
> +}
> diff --git a/tools/perf/util/include/dwarf-regs.h b/tools/perf/util/include/dwarf-regs.h
> index bb5413b0fee4..00881f1d45d6 100644
> --- a/tools/perf/util/include/dwarf-regs.h
> +++ b/tools/perf/util/include/dwarf-regs.h
> @@ -101,6 +101,8 @@ const char *get_dwarf_regstr(unsigned int n, unsigned int machine, unsigned int
>
>  int __get_dwarf_regnum_i386(const char *name);
>  int __get_dwarf_regnum_x86_64(const char *name);
> +int __get_dwarf_regnum_for_perf_regnum_i386(int perf_regnum);
> +int __get_dwarf_regnum_for_perf_regnum_x86_64(int perf_regnum);
>
>  /*
>   * get_dwarf_regnum - Returns DWARF regnum from register name
> @@ -109,6 +111,12 @@ int __get_dwarf_regnum_x86_64(const char *name);
>   */
>  int get_dwarf_regnum(const char *name, unsigned int machine, unsigned int flags);
>
> +/*
> + * get_dwarf_regnum - Returns DWARF regnum from perf register number.
> + */
> +int get_dwarf_regnum_for_perf_regnum(int perf_regnum, unsigned int machine, unsigned int flags,
> +                                    bool only_libdw_supported);
> +
>  void get_powerpc_regs(u32 raw_insn, int is_source, struct annotated_op_loc *op_loc);
>
>  #else /* HAVE_LIBDW_SUPPORT */
> diff --git a/tools/perf/util/unwind-libdw-arch/Build b/tools/perf/util/unwind-libdw-arch/Build
> index ef17a83a7813..5b5682029953 100644
> --- a/tools/perf/util/unwind-libdw-arch/Build
> +++ b/tools/perf/util/unwind-libdw-arch/Build
> @@ -1,4 +1,3 @@
> -perf-util-y += unwind-libdw-x86.o
>  perf-util-y += unwind-libdw-arm.o
>  perf-util-y += unwind-libdw-arm64.o
>  perf-util-y += unwind-libdw-csky.o
> diff --git a/tools/perf/util/unwind-libdw-arch/unwind-libdw-x86.c b/tools/perf/util/unwind-libdw-arch/unwind-libdw-x86.c
> deleted file mode 100644
> index dd27545a4a68..000000000000
> --- a/tools/perf/util/unwind-libdw-arch/unwind-libdw-x86.c
> +++ /dev/null
> @@ -1,54 +0,0 @@
> -// SPDX-License-Identifier: GPL-2.0
> -#include <elfutils/libdwfl.h>
> -#include "../arch/x86/include/uapi/asm/perf_regs.h"
> -#include "util/unwind-libdw.h"
> -#include "util/perf_regs.h"
> -#include "util/sample.h"
> -
> -bool libdw_set_initial_registers_x86(Dwfl_Thread *thread, void *arg)
> -{
> -       struct unwind_info *ui = arg;
> -       struct regs_dump *user_regs = perf_sample__user_regs(ui->sample);
> -       Dwarf_Word dwarf_regs[17];
> -       unsigned nregs;
> -
> -#define REG(r) ({                                              \
> -       Dwarf_Word val = 0;                                     \
> -       perf_reg_value(&val, user_regs, PERF_REG_X86_##r);      \
> -       val;                                                    \
> -})
> -
> -       if (user_regs->abi == PERF_SAMPLE_REGS_ABI_32) {
> -               dwarf_regs[0] = REG(AX);
> -               dwarf_regs[1] = REG(CX);
> -               dwarf_regs[2] = REG(DX);
> -               dwarf_regs[3] = REG(BX);
> -               dwarf_regs[4] = REG(SP);
> -               dwarf_regs[5] = REG(BP);
> -               dwarf_regs[6] = REG(SI);
> -               dwarf_regs[7] = REG(DI);
> -               dwarf_regs[8] = REG(IP);
> -               nregs = 9;
> -       } else {
> -               dwarf_regs[0]  = REG(AX);
> -               dwarf_regs[1]  = REG(DX);
> -               dwarf_regs[2]  = REG(CX);
> -               dwarf_regs[3]  = REG(BX);
> -               dwarf_regs[4]  = REG(SI);
> -               dwarf_regs[5]  = REG(DI);
> -               dwarf_regs[6]  = REG(BP);
> -               dwarf_regs[7]  = REG(SP);
> -               dwarf_regs[8]  = REG(R8);
> -               dwarf_regs[9]  = REG(R9);
> -               dwarf_regs[10] = REG(R10);
> -               dwarf_regs[11] = REG(R11);
> -               dwarf_regs[12] = REG(R12);
> -               dwarf_regs[13] = REG(R13);
> -               dwarf_regs[14] = REG(R14);
> -               dwarf_regs[15] = REG(R15);
> -               dwarf_regs[16] = REG(IP);
> -               nregs = 17;
> -       }
> -
> -       return dwfl_thread_state_registers(thread, 0, nregs, dwarf_regs);
> -}
> diff --git a/tools/perf/util/unwind-libdw.c b/tools/perf/util/unwind-libdw.c
> index c25190cdceb4..055dab921442 100644
> --- a/tools/perf/util/unwind-libdw.c
> +++ b/tools/perf/util/unwind-libdw.c
> @@ -6,6 +6,7 @@
>  #include <errno.h>
>  #include "debug.h"
>  #include "dso.h"
> +#include <dwarf-regs.h>
>  #include "unwind.h"
>  #include "unwind-libdw.h"
>  #include "machine.h"
> @@ -225,6 +226,59 @@ static bool memory_read(Dwfl *dwfl __maybe_unused, Dwarf_Addr addr, Dwarf_Word *
>         return true;
>  }
>
> +static bool libdw_set_initial_registers_generic(Dwfl_Thread *thread, void *arg)
> +{
> +       struct unwind_info *ui = arg;
> +       struct regs_dump *user_regs = perf_sample__user_regs(ui->sample);
> +       Dwarf_Word *dwarf_regs;
> +       int max_dwarf_reg = 0;
> +       bool ret;
> +       uint16_t e_machine = ui->e_machine;
> +       int e_flags = 0;
> +       uint64_t ip_perf_reg = perf_arch_reg_ip(e_machine);
> +       Dwarf_Word val = 0;
> +
> +
> +       /*
> +        * For every possible perf register in the bitmap determine the dwarf
> +        * register and use to compute the max.
> +        */
> +       for (int perf_reg = 0; perf_reg < 64; perf_reg++) {
> +               if (user_regs->mask & (1ULL << perf_reg)) {
> +                       int dwarf_reg =
> +                               get_dwarf_regnum_for_perf_regnum(perf_reg, e_machine,
> +                                                                e_flags,
> +                                                                /*only_libdw_supported=*/true);
> +                       if (dwarf_reg > max_dwarf_reg)
> +                               max_dwarf_reg = dwarf_reg;
> +               }
> +       }
> +
> +       dwarf_regs = calloc(max_dwarf_reg + 1, sizeof(*dwarf_regs));
> +       if (!dwarf_regs)
> +               return false;
> +
> +       for (int perf_reg = 0; perf_reg < 64; perf_reg++) {
> +               if (user_regs->mask & (1ULL << perf_reg)) {
> +                       int dwarf_reg =
> +                               get_dwarf_regnum_for_perf_regnum(perf_reg, e_machine,
> +                                                                e_flags,
> +                                                                /*only_libdw_supported=*/true);
> +                       if (dwarf_reg >= 0) {
> +                               val = 0;
> +                               if (perf_reg_value(&val, user_regs, perf_reg) == 0)
> +                                       dwarf_regs[dwarf_reg] = val;
> +                       }
> +               }
> +       }
> +       if (perf_reg_value(&val, user_regs, ip_perf_reg) == 0)
> +               dwfl_thread_state_register_pc(thread, val);
> +
> +       ret = dwfl_thread_state_registers(thread, 0, max_dwarf_reg + 1, dwarf_regs);

I always forget something. There's possibly a TODO/alternative here
where rather than computing the max dwarf register, copying the
registers and calling dwfl_thread_state_registers, the code could read
the register then call dwfl_thread_state_registers just for that 1
register. The code as it is here is closest to the code as it was
before the change, but removing a memory allocation/free may have some
benefit.

Thanks,
Ian

> +       free(dwarf_regs);
> +       return ret;
> +}
> +
>  #define DEFINE_DWFL_THREAD_CALLBACKS(arch)                           \
>  static const Dwfl_Thread_Callbacks callbacks_##arch = {              \
>         .next_thread           = next_thread,                        \
> @@ -232,7 +286,12 @@ static const Dwfl_Thread_Callbacks callbacks_##arch = {              \
>         .set_initial_registers = libdw_set_initial_registers_##arch, \
>  }
>
> -DEFINE_DWFL_THREAD_CALLBACKS(x86);
> +static const Dwfl_Thread_Callbacks callbacks_generic = {
> +       .next_thread           = next_thread,
> +       .memory_read           = memory_read,
> +       .set_initial_registers = libdw_set_initial_registers_generic,
> +};
> +
>  DEFINE_DWFL_THREAD_CALLBACKS(arm);
>  DEFINE_DWFL_THREAD_CALLBACKS(arm64);
>  DEFINE_DWFL_THREAD_CALLBACKS(csky);
> @@ -257,12 +316,8 @@ static const Dwfl_Thread_Callbacks *get_thread_callbacks(const char *arch)
>                 return &callbacks_riscv;
>         else if (!strcmp(arch, "s390"))
>                 return &callbacks_s390;
> -       else if (!strcmp(arch, "x86"))
> -               return &callbacks_x86;
>
> -       pr_err("Fail to get thread callbacks for arch %s, returns NULL\n",
> -              arch);
> -       return NULL;
> +       return &callbacks_generic;
>  }
>
>  static int
> @@ -301,6 +356,7 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
>                         bool best_effort)
>  {
>         struct machine *machine = maps__machine(thread__maps(thread));
> +       uint16_t e_machine = thread__e_machine(thread, machine);
>         struct unwind_info *ui, ui_buf = {
>                 .sample         = data,
>                 .thread         = thread,
> @@ -308,9 +364,9 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
>                 .cb             = cb,
>                 .arg            = arg,
>                 .max_stack      = max_stack,
> +               .e_machine      = e_machine,
>                 .best_effort    = best_effort
>         };
> -       uint16_t e_machine = thread__e_machine(thread, machine);
>         const char *arch = perf_env__arch(machine->env);
>         Dwarf_Word ip;
>         int err = -EINVAL, i;
> diff --git a/tools/perf/util/unwind-libdw.h b/tools/perf/util/unwind-libdw.h
> index 574b29848cce..496e5898e7ef 100644
> --- a/tools/perf/util/unwind-libdw.h
> +++ b/tools/perf/util/unwind-libdw.h
> @@ -9,7 +9,6 @@ struct machine;
>  struct perf_sample;
>  struct thread;
>
> -bool libdw_set_initial_registers_x86(Dwfl_Thread *thread, void *arg);
>  bool libdw_set_initial_registers_arm(Dwfl_Thread *thread, void *arg);
>  bool libdw_set_initial_registers_arm64(Dwfl_Thread *thread, void *arg);
>  bool libdw_set_initial_registers_csky(Dwfl_Thread *thread, void *arg);
> @@ -28,6 +27,7 @@ struct unwind_info {
>         void                    *arg;
>         int                     max_stack;
>         int                     idx;
> +       uint16_t                e_machine;
>         bool                    best_effort;
>         struct unwind_entry     entries[];
>  };
> --
> 2.52.0.457.g6b5491de43-goog
>

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

* Re: [PATCH v1 04/23] perf unwind-libdw: fix a cross-arch unwinding bug
  2026-01-17  5:28 ` [PATCH v1 04/23] perf unwind-libdw: fix a cross-arch unwinding bug Ian Rogers
@ 2026-01-20 16:02   ` Arnaldo Carvalho de Melo
  2026-01-20 17:53     ` Ian Rogers
  0 siblings, 1 reply; 32+ messages in thread
From: Arnaldo Carvalho de Melo @ 2026-01-20 16:02 UTC (permalink / raw)
  To: Ian Rogers
  Cc: Peter Zijlstra, Ingo Molnar, Namhyung Kim, Jiri Olsa,
	Adrian Hunter, James Clark, John Garry, Will Deacon, Leo Yan,
	Guo Ren, Paul Walmsley, Palmer Dabbelt, Albert Ou,
	Alexandre Ghiti, Shimin Guo, Athira Rajeev, Stephen Brennan,
	Howard Chu, Thomas Falcon, Andi Kleen, Dr. David Alan Gilbert,
	Dmitry Vyukov, Krzysztof Łopatowski, Chun-Tse Shao,
	Aditya Bodkhe, Haibo Xu, Sergei Trofimovich, linux-kernel,
	linux-perf-users, linux-arm-kernel, linux-csky, linux-riscv,
	Mark Wielaard

On Fri, Jan 16, 2026 at 09:28:30PM -0800, Ian Rogers wrote:
> From: Shimin Guo <shimin.guo@skydio.com>
> 
> The set_initial_registers field of Dwfl_Thread_Callbacks needs to be set
> according to the arch of the stack samples being analyzed, not the arch
> that perf itself is built for. Currently perf fails to unwind stack samples
> collected from archs different from that of the host perf is running on.
> This patch moves the arch-specific implementations of set_initial_registers
> from tools/perf/arch to tools/perf/utli/unwind-libdw-arch, similar to the
> way the perf-regs-arch folder contains arch-specific functions related to
> registers, and chooses the implementation based on the arch of the data
> being processed.

Since this one is coming thru you, we need your:

Signed-off-by: Ian Rogers <irogers@google.com>

Ok?

I'm adding it here, please ack.

- Arnaldo
 
> Signed-off-by: Shimin Guo <shimin.guo@skydio.com>
> Reviewed-by: Ian Rogers <irogers@google.com>
> Acked-by: Namhyung Kim <namhyung@kernel.org>
> ---
>  tools/perf/arch/arm/util/Build                |  1 -
>  tools/perf/arch/arm64/util/Build              |  1 -
>  tools/perf/arch/csky/util/Build               |  2 -
>  tools/perf/arch/powerpc/util/Build            |  1 -
>  tools/perf/arch/riscv/util/Build              |  1 -
>  tools/perf/arch/s390/util/Build               |  2 -
>  tools/perf/arch/x86/util/Build                |  1 -
>  tools/perf/util/Build                         |  1 +
>  tools/perf/util/unwind-libdw-arch/Build       |  8 +++
>  .../unwind-libdw-arch/unwind-libdw-arm.c}     | 10 ++--
>  .../unwind-libdw-arch/unwind-libdw-arm64.c}   | 10 ++--
>  .../unwind-libdw-arch/unwind-libdw-csky.c}    | 10 ++--
>  .../unwind-libdw-loongarch.c}                 | 10 ++--
>  .../unwind-libdw-arch/unwind-libdw-powerpc.c} | 10 ++--
>  .../unwind-libdw-arch/unwind-libdw-riscv.c}   | 10 ++--
>  .../unwind-libdw-arch/unwind-libdw-s390.c}    | 14 ++---
>  .../unwind-libdw-arch/unwind-libdw-x86.c}     |  8 +--
>  tools/perf/util/unwind-libdw.c                | 51 ++++++++++++++++---
>  tools/perf/util/unwind-libdw.h                | 10 +++-
>  19 files changed, 104 insertions(+), 57 deletions(-)
>  create mode 100644 tools/perf/util/unwind-libdw-arch/Build
>  rename tools/perf/{arch/arm/util/unwind-libdw.c => util/unwind-libdw-arch/unwind-libdw-arm.c} (80%)
>  rename tools/perf/{arch/arm64/util/unwind-libdw.c => util/unwind-libdw-arch/unwind-libdw-arm64.c} (87%)
>  rename tools/perf/{arch/csky/util/unwind-libdw.c => util/unwind-libdw-arch/unwind-libdw-csky.c} (90%)
>  rename tools/perf/{arch/loongarch/util/unwind-libdw.c => util/unwind-libdw-arch/unwind-libdw-loongarch.c} (86%)
>  rename tools/perf/{arch/powerpc/util/unwind-libdw.c => util/unwind-libdw-arch/unwind-libdw-powerpc.c} (89%)
>  rename tools/perf/{arch/riscv/util/unwind-libdw.c => util/unwind-libdw-arch/unwind-libdw-riscv.c} (87%)
>  rename tools/perf/{arch/s390/util/unwind-libdw.c => util/unwind-libdw-arch/unwind-libdw-s390.c} (84%)
>  rename tools/perf/{arch/x86/util/unwind-libdw.c => util/unwind-libdw-arch/unwind-libdw-x86.c} (87%)
> 
> diff --git a/tools/perf/arch/arm/util/Build b/tools/perf/arch/arm/util/Build
> index fd695e1fdaee..3291f893b943 100644
> --- a/tools/perf/arch/arm/util/Build
> +++ b/tools/perf/arch/arm/util/Build
> @@ -1,6 +1,5 @@
>  perf-util-y += perf_regs.o
>  
>  perf-util-$(CONFIG_LOCAL_LIBUNWIND)    += unwind-libunwind.o
> -perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
>  
>  perf-util-y += pmu.o auxtrace.o cs-etm.o
> diff --git a/tools/perf/arch/arm64/util/Build b/tools/perf/arch/arm64/util/Build
> index d63881081d2e..0177af19cc00 100644
> --- a/tools/perf/arch/arm64/util/Build
> +++ b/tools/perf/arch/arm64/util/Build
> @@ -1,4 +1,3 @@
> -perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
>  perf-util-$(CONFIG_LIBTRACEEVENT) += kvm-stat.o
>  perf-util-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o
>  perf-util-y += ../../arm/util/auxtrace.o
> diff --git a/tools/perf/arch/csky/util/Build b/tools/perf/arch/csky/util/Build
> index 5e6ea82c4202..6b2d0e021b11 100644
> --- a/tools/perf/arch/csky/util/Build
> +++ b/tools/perf/arch/csky/util/Build
> @@ -1,3 +1 @@
>  perf-util-y += perf_regs.o
> -
> -perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
> diff --git a/tools/perf/arch/powerpc/util/Build b/tools/perf/arch/powerpc/util/Build
> index 3d0d5427aef7..5fd28ec713a4 100644
> --- a/tools/perf/arch/powerpc/util/Build
> +++ b/tools/perf/arch/powerpc/util/Build
> @@ -9,5 +9,4 @@ perf-util-y += evsel.o
>  perf-util-$(CONFIG_LIBDW) += skip-callchain-idx.o
>  
>  perf-util-$(CONFIG_LIBUNWIND) += unwind-libunwind.o
> -perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
>  perf-util-y += auxtrace.o
> diff --git a/tools/perf/arch/riscv/util/Build b/tools/perf/arch/riscv/util/Build
> index 58a672246024..628b9ebd418b 100644
> --- a/tools/perf/arch/riscv/util/Build
> +++ b/tools/perf/arch/riscv/util/Build
> @@ -2,4 +2,3 @@ perf-util-y += perf_regs.o
>  perf-util-y += header.o
>  
>  perf-util-$(CONFIG_LIBTRACEEVENT) += kvm-stat.o
> -perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
> diff --git a/tools/perf/arch/s390/util/Build b/tools/perf/arch/s390/util/Build
> index c64eb18dbdae..5391d26fedd4 100644
> --- a/tools/perf/arch/s390/util/Build
> +++ b/tools/perf/arch/s390/util/Build
> @@ -2,8 +2,6 @@ perf-util-y += header.o
>  perf-util-$(CONFIG_LIBTRACEEVENT) += kvm-stat.o
>  perf-util-y += perf_regs.o
>  
> -perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
> -
>  perf-util-y += machine.o
>  perf-util-y += pmu.o
>  
> diff --git a/tools/perf/arch/x86/util/Build b/tools/perf/arch/x86/util/Build
> index c0dc5965f362..fad256252bb9 100644
> --- a/tools/perf/arch/x86/util/Build
> +++ b/tools/perf/arch/x86/util/Build
> @@ -12,7 +12,6 @@ perf-util-y += evsel.o
>  perf-util-y += iostat.o
>  
>  perf-util-$(CONFIG_LOCAL_LIBUNWIND)    += unwind-libunwind.o
> -perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
>  
>  perf-util-y += auxtrace.o
>  perf-util-y += archinsn.o
> diff --git a/tools/perf/util/Build b/tools/perf/util/Build
> index 4915f237ba9e..5efec73be474 100644
> --- a/tools/perf/util/Build
> +++ b/tools/perf/util/Build
> @@ -227,6 +227,7 @@ perf-util-$(CONFIG_LIBDW) += annotate-data.o
>  perf-util-$(CONFIG_LIBDW) += libdw.o
>  
>  perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
> +perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw-arch/
>  perf-util-$(CONFIG_LOCAL_LIBUNWIND)    += unwind-libunwind-local.o
>  perf-util-$(CONFIG_LIBUNWIND)          += unwind-libunwind.o
>  perf-util-$(CONFIG_LIBUNWIND_X86)      += libunwind/x86_32.o
> diff --git a/tools/perf/util/unwind-libdw-arch/Build b/tools/perf/util/unwind-libdw-arch/Build
> new file mode 100644
> index 000000000000..ef17a83a7813
> --- /dev/null
> +++ b/tools/perf/util/unwind-libdw-arch/Build
> @@ -0,0 +1,8 @@
> +perf-util-y += unwind-libdw-x86.o
> +perf-util-y += unwind-libdw-arm.o
> +perf-util-y += unwind-libdw-arm64.o
> +perf-util-y += unwind-libdw-csky.o
> +perf-util-y += unwind-libdw-loongarch.o
> +perf-util-y += unwind-libdw-powerpc.o
> +perf-util-y += unwind-libdw-riscv.o
> +perf-util-y += unwind-libdw-s390.o
> diff --git a/tools/perf/arch/arm/util/unwind-libdw.c b/tools/perf/util/unwind-libdw-arch/unwind-libdw-arm.c
> similarity index 80%
> rename from tools/perf/arch/arm/util/unwind-libdw.c
> rename to tools/perf/util/unwind-libdw-arch/unwind-libdw-arm.c
> index fbb643f224ec..56e9b5975bcc 100644
> --- a/tools/perf/arch/arm/util/unwind-libdw.c
> +++ b/tools/perf/util/unwind-libdw-arch/unwind-libdw-arm.c
> @@ -1,11 +1,11 @@
>  // SPDX-License-Identifier: GPL-2.0
>  #include <elfutils/libdwfl.h>
> -#include "perf_regs.h"
> -#include "../../../util/unwind-libdw.h"
> -#include "../../../util/perf_regs.h"
> -#include "../../../util/sample.h"
> +#include "../arch/arm/include/uapi/asm/perf_regs.h"
> +#include "util/unwind-libdw.h"
> +#include "util/perf_regs.h"
> +#include "util/sample.h"
>  
> -bool libdw__arch_set_initial_registers(Dwfl_Thread *thread, void *arg)
> +bool libdw_set_initial_registers_arm(Dwfl_Thread *thread, void *arg)
>  {
>  	struct unwind_info *ui = arg;
>  	struct regs_dump *user_regs = perf_sample__user_regs(ui->sample);
> diff --git a/tools/perf/arch/arm64/util/unwind-libdw.c b/tools/perf/util/unwind-libdw-arch/unwind-libdw-arm64.c
> similarity index 87%
> rename from tools/perf/arch/arm64/util/unwind-libdw.c
> rename to tools/perf/util/unwind-libdw-arch/unwind-libdw-arm64.c
> index b89b0a7e5ad9..29b6833e036c 100644
> --- a/tools/perf/arch/arm64/util/unwind-libdw.c
> +++ b/tools/perf/util/unwind-libdw-arch/unwind-libdw-arm64.c
> @@ -1,11 +1,11 @@
>  // SPDX-License-Identifier: GPL-2.0
>  #include <elfutils/libdwfl.h>
> -#include "perf_regs.h"
> -#include "../../../util/unwind-libdw.h"
> -#include "../../../util/perf_regs.h"
> -#include "../../../util/sample.h"
> +#include "../arch/arm64/include/uapi/asm/perf_regs.h"
> +#include "util/unwind-libdw.h"
> +#include "util/perf_regs.h"
> +#include "util/sample.h"
>  
> -bool libdw__arch_set_initial_registers(Dwfl_Thread *thread, void *arg)
> +bool libdw_set_initial_registers_arm64(Dwfl_Thread *thread, void *arg)
>  {
>  	struct unwind_info *ui = arg;
>  	struct regs_dump *user_regs = perf_sample__user_regs(ui->sample);
> diff --git a/tools/perf/arch/csky/util/unwind-libdw.c b/tools/perf/util/unwind-libdw-arch/unwind-libdw-csky.c
> similarity index 90%
> rename from tools/perf/arch/csky/util/unwind-libdw.c
> rename to tools/perf/util/unwind-libdw-arch/unwind-libdw-csky.c
> index b20b1569783d..2556d034c32a 100644
> --- a/tools/perf/arch/csky/util/unwind-libdw.c
> +++ b/tools/perf/util/unwind-libdw-arch/unwind-libdw-csky.c
> @@ -2,12 +2,12 @@
>  // Copyright (C) 2019 Hangzhou C-SKY Microsystems co.,ltd.
>  
>  #include <elfutils/libdwfl.h>
> -#include "perf_regs.h"
> -#include "../../util/unwind-libdw.h"
> -#include "../../util/perf_regs.h"
> -#include "../../util/event.h"
> +#include "../arch/csky/include/uapi/asm/perf_regs.h"
> +#include "util/unwind-libdw.h"
> +#include "util/perf_regs.h"
> +#include "util/sample.h"
>  
> -bool libdw__arch_set_initial_registers(Dwfl_Thread *thread, void *arg)
> +bool libdw_set_initial_registers_csky(Dwfl_Thread *thread, void *arg)
>  {
>  	struct unwind_info *ui = arg;
>  	struct regs_dump *user_regs = perf_sample__user_regs(ui->sample);
> diff --git a/tools/perf/arch/loongarch/util/unwind-libdw.c b/tools/perf/util/unwind-libdw-arch/unwind-libdw-loongarch.c
> similarity index 86%
> rename from tools/perf/arch/loongarch/util/unwind-libdw.c
> rename to tools/perf/util/unwind-libdw-arch/unwind-libdw-loongarch.c
> index 60b1144bedd5..5fca673508be 100644
> --- a/tools/perf/arch/loongarch/util/unwind-libdw.c
> +++ b/tools/perf/util/unwind-libdw-arch/unwind-libdw-loongarch.c
> @@ -2,12 +2,12 @@
>  /* Copyright (C) 2020-2023 Loongson Technology Corporation Limited */
>  
>  #include <elfutils/libdwfl.h>
> -#include "perf_regs.h"
> -#include "../../util/unwind-libdw.h"
> -#include "../../util/perf_regs.h"
> -#include "../../util/sample.h"
> +#include "../arch/loongarch/include/uapi/asm/perf_regs.h"
> +#include "util/unwind-libdw.h"
> +#include "util/perf_regs.h"
> +#include "util/sample.h"
>  
> -bool libdw__arch_set_initial_registers(Dwfl_Thread *thread, void *arg)
> +bool libdw_set_initial_registers_loongarch(Dwfl_Thread *thread, void *arg)
>  {
>  	struct unwind_info *ui = arg;
>  	struct regs_dump *user_regs = perf_sample__user_regs(ui->sample);
> diff --git a/tools/perf/arch/powerpc/util/unwind-libdw.c b/tools/perf/util/unwind-libdw-arch/unwind-libdw-powerpc.c
> similarity index 89%
> rename from tools/perf/arch/powerpc/util/unwind-libdw.c
> rename to tools/perf/util/unwind-libdw-arch/unwind-libdw-powerpc.c
> index 82d0c28ae345..1560db45e7b4 100644
> --- a/tools/perf/arch/powerpc/util/unwind-libdw.c
> +++ b/tools/perf/util/unwind-libdw-arch/unwind-libdw-powerpc.c
> @@ -1,10 +1,10 @@
>  // SPDX-License-Identifier: GPL-2.0
>  #include <elfutils/libdwfl.h>
>  #include <linux/kernel.h>
> -#include "perf_regs.h"
> -#include "../../../util/unwind-libdw.h"
> -#include "../../../util/perf_regs.h"
> -#include "../../../util/sample.h"
> +#include "../arch/powerpc/include/uapi/asm/perf_regs.h"
> +#include "util/unwind-libdw.h"
> +#include "util/perf_regs.h"
> +#include "util/sample.h"
>  
>  /* See backends/ppc_initreg.c and backends/ppc_regs.c in elfutils.  */
>  static const int special_regs[3][2] = {
> @@ -13,7 +13,7 @@ static const int special_regs[3][2] = {
>  	{ 109, PERF_REG_POWERPC_CTR },
>  };
>  
> -bool libdw__arch_set_initial_registers(Dwfl_Thread *thread, void *arg)
> +bool libdw_set_initial_registers_powerpc(Dwfl_Thread *thread, void *arg)
>  {
>  	struct unwind_info *ui = arg;
>  	struct regs_dump *user_regs = perf_sample__user_regs(ui->sample);
> diff --git a/tools/perf/arch/riscv/util/unwind-libdw.c b/tools/perf/util/unwind-libdw-arch/unwind-libdw-riscv.c
> similarity index 87%
> rename from tools/perf/arch/riscv/util/unwind-libdw.c
> rename to tools/perf/util/unwind-libdw-arch/unwind-libdw-riscv.c
> index dc1476e16321..c2e2c4b6b2e0 100644
> --- a/tools/perf/arch/riscv/util/unwind-libdw.c
> +++ b/tools/perf/util/unwind-libdw-arch/unwind-libdw-riscv.c
> @@ -2,12 +2,12 @@
>  /* Copyright (C) 2019 Hangzhou C-SKY Microsystems co.,ltd. */
>  
>  #include <elfutils/libdwfl.h>
> -#include "perf_regs.h"
> -#include "../../util/unwind-libdw.h"
> -#include "../../util/perf_regs.h"
> -#include "../../util/sample.h"
> +#include "../arch/riscv/include/uapi/asm/perf_regs.h"
> +#include "util/unwind-libdw.h"
> +#include "util/perf_regs.h"
> +#include "util/sample.h"
>  
> -bool libdw__arch_set_initial_registers(Dwfl_Thread *thread, void *arg)
> +bool libdw_set_initial_registers_riscv(Dwfl_Thread *thread, void *arg)
>  {
>  	struct unwind_info *ui = arg;
>  	struct regs_dump *user_regs = perf_sample__user_regs(ui->sample);
> diff --git a/tools/perf/arch/s390/util/unwind-libdw.c b/tools/perf/util/unwind-libdw-arch/unwind-libdw-s390.c
> similarity index 84%
> rename from tools/perf/arch/s390/util/unwind-libdw.c
> rename to tools/perf/util/unwind-libdw-arch/unwind-libdw-s390.c
> index c27c7a0d1076..1e05e9d9d95f 100644
> --- a/tools/perf/arch/s390/util/unwind-libdw.c
> +++ b/tools/perf/util/unwind-libdw-arch/unwind-libdw-s390.c
> @@ -1,14 +1,14 @@
>  #include <linux/kernel.h>
>  #include <elfutils/libdwfl.h>
> -#include "../../util/unwind-libdw.h"
> -#include "../../util/perf_regs.h"
> -#include "../../util/event.h"
> -#include "../../util/sample.h"
> -#include "dwarf-regs-table.h"
> -#include "perf_regs.h"
> +#include "util/unwind-libdw.h"
> +#include "util/perf_regs.h"
> +#include "util/event.h"
> +#include "util/sample.h"
> +#include "../arch/s390/include/dwarf-regs-table.h"
> +#include "../arch/s390/include/uapi/asm/perf_regs.h"
>  
>  
> -bool libdw__arch_set_initial_registers(Dwfl_Thread *thread, void *arg)
> +bool libdw_set_initial_registers_s390(Dwfl_Thread *thread, void *arg)
>  {
>  	struct unwind_info *ui = arg;
>  	struct regs_dump *user_regs = perf_sample__user_regs(ui->sample);
> diff --git a/tools/perf/arch/x86/util/unwind-libdw.c b/tools/perf/util/unwind-libdw-arch/unwind-libdw-x86.c
> similarity index 87%
> rename from tools/perf/arch/x86/util/unwind-libdw.c
> rename to tools/perf/util/unwind-libdw-arch/unwind-libdw-x86.c
> index 798493e887d7..dd27545a4a68 100644
> --- a/tools/perf/arch/x86/util/unwind-libdw.c
> +++ b/tools/perf/util/unwind-libdw-arch/unwind-libdw-x86.c
> @@ -1,11 +1,11 @@
>  // SPDX-License-Identifier: GPL-2.0
>  #include <elfutils/libdwfl.h>
> -#include "perf_regs.h"
> -#include "../../../util/unwind-libdw.h"
> -#include "../../../util/perf_regs.h"
> +#include "../arch/x86/include/uapi/asm/perf_regs.h"
> +#include "util/unwind-libdw.h"
> +#include "util/perf_regs.h"
>  #include "util/sample.h"
>  
> -bool libdw__arch_set_initial_registers(Dwfl_Thread *thread, void *arg)
> +bool libdw_set_initial_registers_x86(Dwfl_Thread *thread, void *arg)
>  {
>  	struct unwind_info *ui = arg;
>  	struct regs_dump *user_regs = perf_sample__user_regs(ui->sample);
> diff --git a/tools/perf/util/unwind-libdw.c b/tools/perf/util/unwind-libdw.c
> index 3ff427a49e4c..b2e194a8be39 100644
> --- a/tools/perf/util/unwind-libdw.c
> +++ b/tools/perf/util/unwind-libdw.c
> @@ -225,11 +225,45 @@ static bool memory_read(Dwfl *dwfl __maybe_unused, Dwarf_Addr addr, Dwarf_Word *
>  	return true;
>  }
>  
> -static const Dwfl_Thread_Callbacks callbacks = {
> -	.next_thread		= next_thread,
> -	.memory_read		= memory_read,
> -	.set_initial_registers	= libdw__arch_set_initial_registers,
> -};
> +#define DEFINE_DWFL_THREAD_CALLBACKS(arch)                           \
> +static const Dwfl_Thread_Callbacks callbacks_##arch = {              \
> +	.next_thread           = next_thread,                        \
> +	.memory_read           = memory_read,                        \
> +	.set_initial_registers = libdw_set_initial_registers_##arch, \
> +}
> +
> +DEFINE_DWFL_THREAD_CALLBACKS(x86);
> +DEFINE_DWFL_THREAD_CALLBACKS(arm);
> +DEFINE_DWFL_THREAD_CALLBACKS(arm64);
> +DEFINE_DWFL_THREAD_CALLBACKS(csky);
> +DEFINE_DWFL_THREAD_CALLBACKS(loongarch);
> +DEFINE_DWFL_THREAD_CALLBACKS(powerpc);
> +DEFINE_DWFL_THREAD_CALLBACKS(riscv);
> +DEFINE_DWFL_THREAD_CALLBACKS(s390);
> +
> +static const Dwfl_Thread_Callbacks *get_thread_callbacks(const char *arch)
> +{
> +	if (!strcmp(arch, "arm"))
> +		return &callbacks_arm;
> +	else if (!strcmp(arch, "arm64"))
> +		return &callbacks_arm64;
> +	else if (!strcmp(arch, "csky"))
> +		return &callbacks_csky;
> +	else if (!strcmp(arch, "loongarch"))
> +		return &callbacks_loongarch;
> +	else if (!strcmp(arch, "powerpc"))
> +		return &callbacks_powerpc;
> +	else if (!strcmp(arch, "riscv"))
> +		return &callbacks_riscv;
> +	else if (!strcmp(arch, "s390"))
> +		return &callbacks_s390;
> +	else if (!strcmp(arch, "x86"))
> +		return &callbacks_x86;
> +
> +	pr_err("Fail to get thread callbacks for arch %s, returns NULL\n",
> +	       arch);
> +	return NULL;
> +}
>  
>  static int
>  frame_callback(Dwfl_Frame *state, void *arg)
> @@ -278,6 +312,7 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
>  	const char *arch = perf_env__arch(ui_buf.machine->env);
>  	Dwarf_Word ip;
>  	int err = -EINVAL, i;
> +	const Dwfl_Thread_Callbacks *callbacks;
>  
>  	if (!data->user_regs || !data->user_regs->regs)
>  		return -EINVAL;
> @@ -300,7 +335,11 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
>  	if (err)
>  		goto out;
>  
> -	err = !dwfl_attach_state(ui->dwfl, EM_NONE, thread__tid(thread), &callbacks, ui);
> +	callbacks = get_thread_callbacks(arch);
> +	if (!callbacks)
> +		goto out;
> +
> +	err = !dwfl_attach_state(ui->dwfl, EM_NONE, thread__tid(thread), callbacks, ui);
>  	if (err)
>  		goto out;
>  
> diff --git a/tools/perf/util/unwind-libdw.h b/tools/perf/util/unwind-libdw.h
> index 8c88bc4f2304..574b29848cce 100644
> --- a/tools/perf/util/unwind-libdw.h
> +++ b/tools/perf/util/unwind-libdw.h
> @@ -9,7 +9,15 @@ struct machine;
>  struct perf_sample;
>  struct thread;
>  
> -bool libdw__arch_set_initial_registers(Dwfl_Thread *thread, void *arg);
> +bool libdw_set_initial_registers_x86(Dwfl_Thread *thread, void *arg);
> +bool libdw_set_initial_registers_arm(Dwfl_Thread *thread, void *arg);
> +bool libdw_set_initial_registers_arm64(Dwfl_Thread *thread, void *arg);
> +bool libdw_set_initial_registers_csky(Dwfl_Thread *thread, void *arg);
> +bool libdw_set_initial_registers_loongarch(Dwfl_Thread *thread, void *arg);
> +bool libdw_set_initial_registers_mips(Dwfl_Thread *thread, void *arg);
> +bool libdw_set_initial_registers_powerpc(Dwfl_Thread *thread, void *arg);
> +bool libdw_set_initial_registers_riscv(Dwfl_Thread *thread, void *arg);
> +bool libdw_set_initial_registers_s390(Dwfl_Thread *thread, void *arg);
>  
>  struct unwind_info {
>  	Dwfl			*dwfl;
> -- 
> 2.52.0.457.g6b5491de43-goog
> 

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

* Re: [PATCH v1 04/23] perf unwind-libdw: fix a cross-arch unwinding bug
  2026-01-20 16:02   ` Arnaldo Carvalho de Melo
@ 2026-01-20 17:53     ` Ian Rogers
  0 siblings, 0 replies; 32+ messages in thread
From: Ian Rogers @ 2026-01-20 17:53 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: Peter Zijlstra, Ingo Molnar, Namhyung Kim, Jiri Olsa,
	Adrian Hunter, James Clark, John Garry, Will Deacon, Leo Yan,
	Guo Ren, Paul Walmsley, Palmer Dabbelt, Albert Ou,
	Alexandre Ghiti, Shimin Guo, Athira Rajeev, Stephen Brennan,
	Howard Chu, Thomas Falcon, Andi Kleen, Dr. David Alan Gilbert,
	Dmitry Vyukov, Krzysztof Łopatowski, Chun-Tse Shao,
	Aditya Bodkhe, Haibo Xu, Sergei Trofimovich, linux-kernel,
	linux-perf-users, linux-arm-kernel, linux-csky, linux-riscv,
	Mark Wielaard

On Tue, Jan 20, 2026 at 8:02 AM Arnaldo Carvalho de Melo
<acme@kernel.org> wrote:
>
> On Fri, Jan 16, 2026 at 09:28:30PM -0800, Ian Rogers wrote:
> > From: Shimin Guo <shimin.guo@skydio.com>
> >
> > The set_initial_registers field of Dwfl_Thread_Callbacks needs to be set
> > according to the arch of the stack samples being analyzed, not the arch
> > that perf itself is built for. Currently perf fails to unwind stack samples
> > collected from archs different from that of the host perf is running on.
> > This patch moves the arch-specific implementations of set_initial_registers
> > from tools/perf/arch to tools/perf/utli/unwind-libdw-arch, similar to the
> > way the perf-regs-arch folder contains arch-specific functions related to
> > registers, and chooses the implementation based on the arch of the data
> > being processed.
>
> Since this one is coming thru you, we need your:
>
> Signed-off-by: Ian Rogers <irogers@google.com>
>
> Ok?
>
> I'm adding it here, please ack.

Acked. Sorry for the problem.

Ian

> - Arnaldo
>
> > Signed-off-by: Shimin Guo <shimin.guo@skydio.com>
> > Reviewed-by: Ian Rogers <irogers@google.com>
> > Acked-by: Namhyung Kim <namhyung@kernel.org>
> > ---
> >  tools/perf/arch/arm/util/Build                |  1 -
> >  tools/perf/arch/arm64/util/Build              |  1 -
> >  tools/perf/arch/csky/util/Build               |  2 -
> >  tools/perf/arch/powerpc/util/Build            |  1 -
> >  tools/perf/arch/riscv/util/Build              |  1 -
> >  tools/perf/arch/s390/util/Build               |  2 -
> >  tools/perf/arch/x86/util/Build                |  1 -
> >  tools/perf/util/Build                         |  1 +
> >  tools/perf/util/unwind-libdw-arch/Build       |  8 +++
> >  .../unwind-libdw-arch/unwind-libdw-arm.c}     | 10 ++--
> >  .../unwind-libdw-arch/unwind-libdw-arm64.c}   | 10 ++--
> >  .../unwind-libdw-arch/unwind-libdw-csky.c}    | 10 ++--
> >  .../unwind-libdw-loongarch.c}                 | 10 ++--
> >  .../unwind-libdw-arch/unwind-libdw-powerpc.c} | 10 ++--
> >  .../unwind-libdw-arch/unwind-libdw-riscv.c}   | 10 ++--
> >  .../unwind-libdw-arch/unwind-libdw-s390.c}    | 14 ++---
> >  .../unwind-libdw-arch/unwind-libdw-x86.c}     |  8 +--
> >  tools/perf/util/unwind-libdw.c                | 51 ++++++++++++++++---
> >  tools/perf/util/unwind-libdw.h                | 10 +++-
> >  19 files changed, 104 insertions(+), 57 deletions(-)
> >  create mode 100644 tools/perf/util/unwind-libdw-arch/Build
> >  rename tools/perf/{arch/arm/util/unwind-libdw.c => util/unwind-libdw-arch/unwind-libdw-arm.c} (80%)
> >  rename tools/perf/{arch/arm64/util/unwind-libdw.c => util/unwind-libdw-arch/unwind-libdw-arm64.c} (87%)
> >  rename tools/perf/{arch/csky/util/unwind-libdw.c => util/unwind-libdw-arch/unwind-libdw-csky.c} (90%)
> >  rename tools/perf/{arch/loongarch/util/unwind-libdw.c => util/unwind-libdw-arch/unwind-libdw-loongarch.c} (86%)
> >  rename tools/perf/{arch/powerpc/util/unwind-libdw.c => util/unwind-libdw-arch/unwind-libdw-powerpc.c} (89%)
> >  rename tools/perf/{arch/riscv/util/unwind-libdw.c => util/unwind-libdw-arch/unwind-libdw-riscv.c} (87%)
> >  rename tools/perf/{arch/s390/util/unwind-libdw.c => util/unwind-libdw-arch/unwind-libdw-s390.c} (84%)
> >  rename tools/perf/{arch/x86/util/unwind-libdw.c => util/unwind-libdw-arch/unwind-libdw-x86.c} (87%)
> >
> > diff --git a/tools/perf/arch/arm/util/Build b/tools/perf/arch/arm/util/Build
> > index fd695e1fdaee..3291f893b943 100644
> > --- a/tools/perf/arch/arm/util/Build
> > +++ b/tools/perf/arch/arm/util/Build
> > @@ -1,6 +1,5 @@
> >  perf-util-y += perf_regs.o
> >
> >  perf-util-$(CONFIG_LOCAL_LIBUNWIND)    += unwind-libunwind.o
> > -perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
> >
> >  perf-util-y += pmu.o auxtrace.o cs-etm.o
> > diff --git a/tools/perf/arch/arm64/util/Build b/tools/perf/arch/arm64/util/Build
> > index d63881081d2e..0177af19cc00 100644
> > --- a/tools/perf/arch/arm64/util/Build
> > +++ b/tools/perf/arch/arm64/util/Build
> > @@ -1,4 +1,3 @@
> > -perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
> >  perf-util-$(CONFIG_LIBTRACEEVENT) += kvm-stat.o
> >  perf-util-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o
> >  perf-util-y += ../../arm/util/auxtrace.o
> > diff --git a/tools/perf/arch/csky/util/Build b/tools/perf/arch/csky/util/Build
> > index 5e6ea82c4202..6b2d0e021b11 100644
> > --- a/tools/perf/arch/csky/util/Build
> > +++ b/tools/perf/arch/csky/util/Build
> > @@ -1,3 +1 @@
> >  perf-util-y += perf_regs.o
> > -
> > -perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
> > diff --git a/tools/perf/arch/powerpc/util/Build b/tools/perf/arch/powerpc/util/Build
> > index 3d0d5427aef7..5fd28ec713a4 100644
> > --- a/tools/perf/arch/powerpc/util/Build
> > +++ b/tools/perf/arch/powerpc/util/Build
> > @@ -9,5 +9,4 @@ perf-util-y += evsel.o
> >  perf-util-$(CONFIG_LIBDW) += skip-callchain-idx.o
> >
> >  perf-util-$(CONFIG_LIBUNWIND) += unwind-libunwind.o
> > -perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
> >  perf-util-y += auxtrace.o
> > diff --git a/tools/perf/arch/riscv/util/Build b/tools/perf/arch/riscv/util/Build
> > index 58a672246024..628b9ebd418b 100644
> > --- a/tools/perf/arch/riscv/util/Build
> > +++ b/tools/perf/arch/riscv/util/Build
> > @@ -2,4 +2,3 @@ perf-util-y += perf_regs.o
> >  perf-util-y += header.o
> >
> >  perf-util-$(CONFIG_LIBTRACEEVENT) += kvm-stat.o
> > -perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
> > diff --git a/tools/perf/arch/s390/util/Build b/tools/perf/arch/s390/util/Build
> > index c64eb18dbdae..5391d26fedd4 100644
> > --- a/tools/perf/arch/s390/util/Build
> > +++ b/tools/perf/arch/s390/util/Build
> > @@ -2,8 +2,6 @@ perf-util-y += header.o
> >  perf-util-$(CONFIG_LIBTRACEEVENT) += kvm-stat.o
> >  perf-util-y += perf_regs.o
> >
> > -perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
> > -
> >  perf-util-y += machine.o
> >  perf-util-y += pmu.o
> >
> > diff --git a/tools/perf/arch/x86/util/Build b/tools/perf/arch/x86/util/Build
> > index c0dc5965f362..fad256252bb9 100644
> > --- a/tools/perf/arch/x86/util/Build
> > +++ b/tools/perf/arch/x86/util/Build
> > @@ -12,7 +12,6 @@ perf-util-y += evsel.o
> >  perf-util-y += iostat.o
> >
> >  perf-util-$(CONFIG_LOCAL_LIBUNWIND)    += unwind-libunwind.o
> > -perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
> >
> >  perf-util-y += auxtrace.o
> >  perf-util-y += archinsn.o
> > diff --git a/tools/perf/util/Build b/tools/perf/util/Build
> > index 4915f237ba9e..5efec73be474 100644
> > --- a/tools/perf/util/Build
> > +++ b/tools/perf/util/Build
> > @@ -227,6 +227,7 @@ perf-util-$(CONFIG_LIBDW) += annotate-data.o
> >  perf-util-$(CONFIG_LIBDW) += libdw.o
> >
> >  perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
> > +perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw-arch/
> >  perf-util-$(CONFIG_LOCAL_LIBUNWIND)    += unwind-libunwind-local.o
> >  perf-util-$(CONFIG_LIBUNWIND)          += unwind-libunwind.o
> >  perf-util-$(CONFIG_LIBUNWIND_X86)      += libunwind/x86_32.o
> > diff --git a/tools/perf/util/unwind-libdw-arch/Build b/tools/perf/util/unwind-libdw-arch/Build
> > new file mode 100644
> > index 000000000000..ef17a83a7813
> > --- /dev/null
> > +++ b/tools/perf/util/unwind-libdw-arch/Build
> > @@ -0,0 +1,8 @@
> > +perf-util-y += unwind-libdw-x86.o
> > +perf-util-y += unwind-libdw-arm.o
> > +perf-util-y += unwind-libdw-arm64.o
> > +perf-util-y += unwind-libdw-csky.o
> > +perf-util-y += unwind-libdw-loongarch.o
> > +perf-util-y += unwind-libdw-powerpc.o
> > +perf-util-y += unwind-libdw-riscv.o
> > +perf-util-y += unwind-libdw-s390.o
> > diff --git a/tools/perf/arch/arm/util/unwind-libdw.c b/tools/perf/util/unwind-libdw-arch/unwind-libdw-arm.c
> > similarity index 80%
> > rename from tools/perf/arch/arm/util/unwind-libdw.c
> > rename to tools/perf/util/unwind-libdw-arch/unwind-libdw-arm.c
> > index fbb643f224ec..56e9b5975bcc 100644
> > --- a/tools/perf/arch/arm/util/unwind-libdw.c
> > +++ b/tools/perf/util/unwind-libdw-arch/unwind-libdw-arm.c
> > @@ -1,11 +1,11 @@
> >  // SPDX-License-Identifier: GPL-2.0
> >  #include <elfutils/libdwfl.h>
> > -#include "perf_regs.h"
> > -#include "../../../util/unwind-libdw.h"
> > -#include "../../../util/perf_regs.h"
> > -#include "../../../util/sample.h"
> > +#include "../arch/arm/include/uapi/asm/perf_regs.h"
> > +#include "util/unwind-libdw.h"
> > +#include "util/perf_regs.h"
> > +#include "util/sample.h"
> >
> > -bool libdw__arch_set_initial_registers(Dwfl_Thread *thread, void *arg)
> > +bool libdw_set_initial_registers_arm(Dwfl_Thread *thread, void *arg)
> >  {
> >       struct unwind_info *ui = arg;
> >       struct regs_dump *user_regs = perf_sample__user_regs(ui->sample);
> > diff --git a/tools/perf/arch/arm64/util/unwind-libdw.c b/tools/perf/util/unwind-libdw-arch/unwind-libdw-arm64.c
> > similarity index 87%
> > rename from tools/perf/arch/arm64/util/unwind-libdw.c
> > rename to tools/perf/util/unwind-libdw-arch/unwind-libdw-arm64.c
> > index b89b0a7e5ad9..29b6833e036c 100644
> > --- a/tools/perf/arch/arm64/util/unwind-libdw.c
> > +++ b/tools/perf/util/unwind-libdw-arch/unwind-libdw-arm64.c
> > @@ -1,11 +1,11 @@
> >  // SPDX-License-Identifier: GPL-2.0
> >  #include <elfutils/libdwfl.h>
> > -#include "perf_regs.h"
> > -#include "../../../util/unwind-libdw.h"
> > -#include "../../../util/perf_regs.h"
> > -#include "../../../util/sample.h"
> > +#include "../arch/arm64/include/uapi/asm/perf_regs.h"
> > +#include "util/unwind-libdw.h"
> > +#include "util/perf_regs.h"
> > +#include "util/sample.h"
> >
> > -bool libdw__arch_set_initial_registers(Dwfl_Thread *thread, void *arg)
> > +bool libdw_set_initial_registers_arm64(Dwfl_Thread *thread, void *arg)
> >  {
> >       struct unwind_info *ui = arg;
> >       struct regs_dump *user_regs = perf_sample__user_regs(ui->sample);
> > diff --git a/tools/perf/arch/csky/util/unwind-libdw.c b/tools/perf/util/unwind-libdw-arch/unwind-libdw-csky.c
> > similarity index 90%
> > rename from tools/perf/arch/csky/util/unwind-libdw.c
> > rename to tools/perf/util/unwind-libdw-arch/unwind-libdw-csky.c
> > index b20b1569783d..2556d034c32a 100644
> > --- a/tools/perf/arch/csky/util/unwind-libdw.c
> > +++ b/tools/perf/util/unwind-libdw-arch/unwind-libdw-csky.c
> > @@ -2,12 +2,12 @@
> >  // Copyright (C) 2019 Hangzhou C-SKY Microsystems co.,ltd.
> >
> >  #include <elfutils/libdwfl.h>
> > -#include "perf_regs.h"
> > -#include "../../util/unwind-libdw.h"
> > -#include "../../util/perf_regs.h"
> > -#include "../../util/event.h"
> > +#include "../arch/csky/include/uapi/asm/perf_regs.h"
> > +#include "util/unwind-libdw.h"
> > +#include "util/perf_regs.h"
> > +#include "util/sample.h"
> >
> > -bool libdw__arch_set_initial_registers(Dwfl_Thread *thread, void *arg)
> > +bool libdw_set_initial_registers_csky(Dwfl_Thread *thread, void *arg)
> >  {
> >       struct unwind_info *ui = arg;
> >       struct regs_dump *user_regs = perf_sample__user_regs(ui->sample);
> > diff --git a/tools/perf/arch/loongarch/util/unwind-libdw.c b/tools/perf/util/unwind-libdw-arch/unwind-libdw-loongarch.c
> > similarity index 86%
> > rename from tools/perf/arch/loongarch/util/unwind-libdw.c
> > rename to tools/perf/util/unwind-libdw-arch/unwind-libdw-loongarch.c
> > index 60b1144bedd5..5fca673508be 100644
> > --- a/tools/perf/arch/loongarch/util/unwind-libdw.c
> > +++ b/tools/perf/util/unwind-libdw-arch/unwind-libdw-loongarch.c
> > @@ -2,12 +2,12 @@
> >  /* Copyright (C) 2020-2023 Loongson Technology Corporation Limited */
> >
> >  #include <elfutils/libdwfl.h>
> > -#include "perf_regs.h"
> > -#include "../../util/unwind-libdw.h"
> > -#include "../../util/perf_regs.h"
> > -#include "../../util/sample.h"
> > +#include "../arch/loongarch/include/uapi/asm/perf_regs.h"
> > +#include "util/unwind-libdw.h"
> > +#include "util/perf_regs.h"
> > +#include "util/sample.h"
> >
> > -bool libdw__arch_set_initial_registers(Dwfl_Thread *thread, void *arg)
> > +bool libdw_set_initial_registers_loongarch(Dwfl_Thread *thread, void *arg)
> >  {
> >       struct unwind_info *ui = arg;
> >       struct regs_dump *user_regs = perf_sample__user_regs(ui->sample);
> > diff --git a/tools/perf/arch/powerpc/util/unwind-libdw.c b/tools/perf/util/unwind-libdw-arch/unwind-libdw-powerpc.c
> > similarity index 89%
> > rename from tools/perf/arch/powerpc/util/unwind-libdw.c
> > rename to tools/perf/util/unwind-libdw-arch/unwind-libdw-powerpc.c
> > index 82d0c28ae345..1560db45e7b4 100644
> > --- a/tools/perf/arch/powerpc/util/unwind-libdw.c
> > +++ b/tools/perf/util/unwind-libdw-arch/unwind-libdw-powerpc.c
> > @@ -1,10 +1,10 @@
> >  // SPDX-License-Identifier: GPL-2.0
> >  #include <elfutils/libdwfl.h>
> >  #include <linux/kernel.h>
> > -#include "perf_regs.h"
> > -#include "../../../util/unwind-libdw.h"
> > -#include "../../../util/perf_regs.h"
> > -#include "../../../util/sample.h"
> > +#include "../arch/powerpc/include/uapi/asm/perf_regs.h"
> > +#include "util/unwind-libdw.h"
> > +#include "util/perf_regs.h"
> > +#include "util/sample.h"
> >
> >  /* See backends/ppc_initreg.c and backends/ppc_regs.c in elfutils.  */
> >  static const int special_regs[3][2] = {
> > @@ -13,7 +13,7 @@ static const int special_regs[3][2] = {
> >       { 109, PERF_REG_POWERPC_CTR },
> >  };
> >
> > -bool libdw__arch_set_initial_registers(Dwfl_Thread *thread, void *arg)
> > +bool libdw_set_initial_registers_powerpc(Dwfl_Thread *thread, void *arg)
> >  {
> >       struct unwind_info *ui = arg;
> >       struct regs_dump *user_regs = perf_sample__user_regs(ui->sample);
> > diff --git a/tools/perf/arch/riscv/util/unwind-libdw.c b/tools/perf/util/unwind-libdw-arch/unwind-libdw-riscv.c
> > similarity index 87%
> > rename from tools/perf/arch/riscv/util/unwind-libdw.c
> > rename to tools/perf/util/unwind-libdw-arch/unwind-libdw-riscv.c
> > index dc1476e16321..c2e2c4b6b2e0 100644
> > --- a/tools/perf/arch/riscv/util/unwind-libdw.c
> > +++ b/tools/perf/util/unwind-libdw-arch/unwind-libdw-riscv.c
> > @@ -2,12 +2,12 @@
> >  /* Copyright (C) 2019 Hangzhou C-SKY Microsystems co.,ltd. */
> >
> >  #include <elfutils/libdwfl.h>
> > -#include "perf_regs.h"
> > -#include "../../util/unwind-libdw.h"
> > -#include "../../util/perf_regs.h"
> > -#include "../../util/sample.h"
> > +#include "../arch/riscv/include/uapi/asm/perf_regs.h"
> > +#include "util/unwind-libdw.h"
> > +#include "util/perf_regs.h"
> > +#include "util/sample.h"
> >
> > -bool libdw__arch_set_initial_registers(Dwfl_Thread *thread, void *arg)
> > +bool libdw_set_initial_registers_riscv(Dwfl_Thread *thread, void *arg)
> >  {
> >       struct unwind_info *ui = arg;
> >       struct regs_dump *user_regs = perf_sample__user_regs(ui->sample);
> > diff --git a/tools/perf/arch/s390/util/unwind-libdw.c b/tools/perf/util/unwind-libdw-arch/unwind-libdw-s390.c
> > similarity index 84%
> > rename from tools/perf/arch/s390/util/unwind-libdw.c
> > rename to tools/perf/util/unwind-libdw-arch/unwind-libdw-s390.c
> > index c27c7a0d1076..1e05e9d9d95f 100644
> > --- a/tools/perf/arch/s390/util/unwind-libdw.c
> > +++ b/tools/perf/util/unwind-libdw-arch/unwind-libdw-s390.c
> > @@ -1,14 +1,14 @@
> >  #include <linux/kernel.h>
> >  #include <elfutils/libdwfl.h>
> > -#include "../../util/unwind-libdw.h"
> > -#include "../../util/perf_regs.h"
> > -#include "../../util/event.h"
> > -#include "../../util/sample.h"
> > -#include "dwarf-regs-table.h"
> > -#include "perf_regs.h"
> > +#include "util/unwind-libdw.h"
> > +#include "util/perf_regs.h"
> > +#include "util/event.h"
> > +#include "util/sample.h"
> > +#include "../arch/s390/include/dwarf-regs-table.h"
> > +#include "../arch/s390/include/uapi/asm/perf_regs.h"
> >
> >
> > -bool libdw__arch_set_initial_registers(Dwfl_Thread *thread, void *arg)
> > +bool libdw_set_initial_registers_s390(Dwfl_Thread *thread, void *arg)
> >  {
> >       struct unwind_info *ui = arg;
> >       struct regs_dump *user_regs = perf_sample__user_regs(ui->sample);
> > diff --git a/tools/perf/arch/x86/util/unwind-libdw.c b/tools/perf/util/unwind-libdw-arch/unwind-libdw-x86.c
> > similarity index 87%
> > rename from tools/perf/arch/x86/util/unwind-libdw.c
> > rename to tools/perf/util/unwind-libdw-arch/unwind-libdw-x86.c
> > index 798493e887d7..dd27545a4a68 100644
> > --- a/tools/perf/arch/x86/util/unwind-libdw.c
> > +++ b/tools/perf/util/unwind-libdw-arch/unwind-libdw-x86.c
> > @@ -1,11 +1,11 @@
> >  // SPDX-License-Identifier: GPL-2.0
> >  #include <elfutils/libdwfl.h>
> > -#include "perf_regs.h"
> > -#include "../../../util/unwind-libdw.h"
> > -#include "../../../util/perf_regs.h"
> > +#include "../arch/x86/include/uapi/asm/perf_regs.h"
> > +#include "util/unwind-libdw.h"
> > +#include "util/perf_regs.h"
> >  #include "util/sample.h"
> >
> > -bool libdw__arch_set_initial_registers(Dwfl_Thread *thread, void *arg)
> > +bool libdw_set_initial_registers_x86(Dwfl_Thread *thread, void *arg)
> >  {
> >       struct unwind_info *ui = arg;
> >       struct regs_dump *user_regs = perf_sample__user_regs(ui->sample);
> > diff --git a/tools/perf/util/unwind-libdw.c b/tools/perf/util/unwind-libdw.c
> > index 3ff427a49e4c..b2e194a8be39 100644
> > --- a/tools/perf/util/unwind-libdw.c
> > +++ b/tools/perf/util/unwind-libdw.c
> > @@ -225,11 +225,45 @@ static bool memory_read(Dwfl *dwfl __maybe_unused, Dwarf_Addr addr, Dwarf_Word *
> >       return true;
> >  }
> >
> > -static const Dwfl_Thread_Callbacks callbacks = {
> > -     .next_thread            = next_thread,
> > -     .memory_read            = memory_read,
> > -     .set_initial_registers  = libdw__arch_set_initial_registers,
> > -};
> > +#define DEFINE_DWFL_THREAD_CALLBACKS(arch)                           \
> > +static const Dwfl_Thread_Callbacks callbacks_##arch = {              \
> > +     .next_thread           = next_thread,                        \
> > +     .memory_read           = memory_read,                        \
> > +     .set_initial_registers = libdw_set_initial_registers_##arch, \
> > +}
> > +
> > +DEFINE_DWFL_THREAD_CALLBACKS(x86);
> > +DEFINE_DWFL_THREAD_CALLBACKS(arm);
> > +DEFINE_DWFL_THREAD_CALLBACKS(arm64);
> > +DEFINE_DWFL_THREAD_CALLBACKS(csky);
> > +DEFINE_DWFL_THREAD_CALLBACKS(loongarch);
> > +DEFINE_DWFL_THREAD_CALLBACKS(powerpc);
> > +DEFINE_DWFL_THREAD_CALLBACKS(riscv);
> > +DEFINE_DWFL_THREAD_CALLBACKS(s390);
> > +
> > +static const Dwfl_Thread_Callbacks *get_thread_callbacks(const char *arch)
> > +{
> > +     if (!strcmp(arch, "arm"))
> > +             return &callbacks_arm;
> > +     else if (!strcmp(arch, "arm64"))
> > +             return &callbacks_arm64;
> > +     else if (!strcmp(arch, "csky"))
> > +             return &callbacks_csky;
> > +     else if (!strcmp(arch, "loongarch"))
> > +             return &callbacks_loongarch;
> > +     else if (!strcmp(arch, "powerpc"))
> > +             return &callbacks_powerpc;
> > +     else if (!strcmp(arch, "riscv"))
> > +             return &callbacks_riscv;
> > +     else if (!strcmp(arch, "s390"))
> > +             return &callbacks_s390;
> > +     else if (!strcmp(arch, "x86"))
> > +             return &callbacks_x86;
> > +
> > +     pr_err("Fail to get thread callbacks for arch %s, returns NULL\n",
> > +            arch);
> > +     return NULL;
> > +}
> >
> >  static int
> >  frame_callback(Dwfl_Frame *state, void *arg)
> > @@ -278,6 +312,7 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
> >       const char *arch = perf_env__arch(ui_buf.machine->env);
> >       Dwarf_Word ip;
> >       int err = -EINVAL, i;
> > +     const Dwfl_Thread_Callbacks *callbacks;
> >
> >       if (!data->user_regs || !data->user_regs->regs)
> >               return -EINVAL;
> > @@ -300,7 +335,11 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
> >       if (err)
> >               goto out;
> >
> > -     err = !dwfl_attach_state(ui->dwfl, EM_NONE, thread__tid(thread), &callbacks, ui);
> > +     callbacks = get_thread_callbacks(arch);
> > +     if (!callbacks)
> > +             goto out;
> > +
> > +     err = !dwfl_attach_state(ui->dwfl, EM_NONE, thread__tid(thread), callbacks, ui);
> >       if (err)
> >               goto out;
> >
> > diff --git a/tools/perf/util/unwind-libdw.h b/tools/perf/util/unwind-libdw.h
> > index 8c88bc4f2304..574b29848cce 100644
> > --- a/tools/perf/util/unwind-libdw.h
> > +++ b/tools/perf/util/unwind-libdw.h
> > @@ -9,7 +9,15 @@ struct machine;
> >  struct perf_sample;
> >  struct thread;
> >
> > -bool libdw__arch_set_initial_registers(Dwfl_Thread *thread, void *arg);
> > +bool libdw_set_initial_registers_x86(Dwfl_Thread *thread, void *arg);
> > +bool libdw_set_initial_registers_arm(Dwfl_Thread *thread, void *arg);
> > +bool libdw_set_initial_registers_arm64(Dwfl_Thread *thread, void *arg);
> > +bool libdw_set_initial_registers_csky(Dwfl_Thread *thread, void *arg);
> > +bool libdw_set_initial_registers_loongarch(Dwfl_Thread *thread, void *arg);
> > +bool libdw_set_initial_registers_mips(Dwfl_Thread *thread, void *arg);
> > +bool libdw_set_initial_registers_powerpc(Dwfl_Thread *thread, void *arg);
> > +bool libdw_set_initial_registers_riscv(Dwfl_Thread *thread, void *arg);
> > +bool libdw_set_initial_registers_s390(Dwfl_Thread *thread, void *arg);
> >
> >  struct unwind_info {
> >       Dwfl                    *dwfl;
> > --
> > 2.52.0.457.g6b5491de43-goog
> >

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

* Re: [PATCH v1 08/23] perf perf_regs: Switch from arch string to int e_machine
  2026-01-17  5:28 ` [PATCH v1 08/23] perf perf_regs: Switch from arch string to int e_machine Ian Rogers
@ 2026-01-20 18:49   ` Arnaldo Carvalho de Melo
  2026-01-21  6:58     ` Mi, Dapeng
  0 siblings, 1 reply; 32+ messages in thread
From: Arnaldo Carvalho de Melo @ 2026-01-20 18:49 UTC (permalink / raw)
  To: Ian Rogers
  Cc: Peter Zijlstra, Ingo Molnar, Namhyung Kim, Jiri Olsa,
	Adrian Hunter, James Clark, John Garry, Will Deacon, Leo Yan,
	Guo Ren, Paul Walmsley, Palmer Dabbelt, Albert Ou,
	Alexandre Ghiti, Shimin Guo, Athira Rajeev, Stephen Brennan,
	Howard Chu, Thomas Falcon, Andi Kleen, Dr. David Alan Gilbert,
	Dmitry Vyukov, Krzysztof Łopatowski, Chun-Tse Shao,
	Aditya Bodkhe, Haibo Xu, Sergei Trofimovich, linux-kernel,
	linux-perf-users, linux-arm-kernel, linux-csky, linux-riscv,
	Mark Wielaard

On Fri, Jan 16, 2026 at 09:28:34PM -0800, Ian Rogers wrote:
> The arch string requires multiple strcmp to identify things like the
> IP and SP. Switch to passing in an e_machine that in the bulk of cases
> is computed using a current thread load. The e_machine also allows
> identification of 32-bit vs 64-bit processes.
> 
> Signed-off-by: Ian Rogers <irogers@google.com>
> ---
>  tools/perf/builtin-script.c                   |  17 ++-
>  tools/perf/util/evsel.c                       |  14 ++-
>  tools/perf/util/perf_regs.c                   | 105 +++++++++++-------

[ Include dwarf-regs.h to get conditional defines for EM_CSKY and EM_LOONGARCH, not available in old distros ]

- Arnaldo

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

* Re: [PATCH v1 08/23] perf perf_regs: Switch from arch string to int e_machine
  2026-01-20 18:49   ` Arnaldo Carvalho de Melo
@ 2026-01-21  6:58     ` Mi, Dapeng
  2026-01-21  7:10       ` Ian Rogers
  0 siblings, 1 reply; 32+ messages in thread
From: Mi, Dapeng @ 2026-01-21  6:58 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo, Ian Rogers
  Cc: Peter Zijlstra, Ingo Molnar, Namhyung Kim, Jiri Olsa,
	Adrian Hunter, James Clark, John Garry, Will Deacon, Leo Yan,
	Guo Ren, Paul Walmsley, Palmer Dabbelt, Albert Ou,
	Alexandre Ghiti, Shimin Guo, Athira Rajeev, Stephen Brennan,
	Howard Chu, Thomas Falcon, Andi Kleen, Dr. David Alan Gilbert,
	Dmitry Vyukov, Krzysztof Łopatowski, Chun-Tse Shao,
	Aditya Bodkhe, Haibo Xu, Sergei Trofimovich, linux-kernel,
	linux-perf-users, linux-arm-kernel, linux-csky, linux-riscv,
	Mark Wielaard


On 1/21/2026 2:49 AM, Arnaldo Carvalho de Melo wrote:
> On Fri, Jan 16, 2026 at 09:28:34PM -0800, Ian Rogers wrote:
>> The arch string requires multiple strcmp to identify things like the
>> IP and SP. Switch to passing in an e_machine that in the bulk of cases
>> is computed using a current thread load. The e_machine also allows
>> identification of 32-bit vs 64-bit processes.
>>
>> Signed-off-by: Ian Rogers <irogers@google.com>
>> ---
>>  tools/perf/builtin-script.c                   |  17 ++-
>>  tools/perf/util/evsel.c                       |  14 ++-
>>  tools/perf/util/perf_regs.c                   | 105 +++++++++++-------
> [ Include dwarf-regs.h to get conditional defines for EM_CSKY and EM_LOONGARCH, not available in old distros ]
>
> - Arnaldo

Seems the change would introduce the below building error.

util/perf_regs.c: In function ‘perf_reg_name’:
util/perf_regs.c:48:14: error: ‘EM_LOONGARCH’ undeclared (first use in this
function)
   48 |         case EM_LOONGARCH:
      |              ^~~~~~~~~~~~
util/perf_regs.c:48:14: note: each undeclared identifier is reported only
once for each function it appears in
util/perf_regs.c: In function ‘perf_arch_reg_ip’:
util/perf_regs.c:114:14: error: ‘EM_LOONGARCH’ undeclared (first use in
this function)
  114 |         case EM_LOONGARCH:
      |              ^~~~~~~~~~~~
util/perf_regs.c: In function ‘perf_arch_reg_sp’:
util/perf_regs.c:143:14: error: ‘EM_LOONGARCH’ undeclared (first use in
this function)
  143 |         case EM_LOONGARCH:
      |              ^~~~~~~~~~~~

It looks EM_LOONGARCH is not defined in the <elf.h> header file, not sure
which one is better, include  dwarf-regs.h suggested by Arnaldo or direct
add the define,

diff --git a/tools/perf/util/perf_regs.c b/tools/perf/util/perf_regs.c
index b58d59b84fb1..ce839df19fa3 100644
--- a/tools/perf/util/perf_regs.c
+++ b/tools/perf/util/perf_regs.c
@@ -6,6 +6,10 @@
 #include "util/sample.h"
 #include "debug.h"

+#ifndef EM_LOONGARCH
+#define EM_LOONGARCH           258
+#endif
+
 int __weak arch_sdt_arg_parse_op(char *old_op __maybe_unused,
                                 char **new_op __maybe_unused)
 {


>

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

* Re: [PATCH v1 08/23] perf perf_regs: Switch from arch string to int e_machine
  2026-01-21  6:58     ` Mi, Dapeng
@ 2026-01-21  7:10       ` Ian Rogers
  0 siblings, 0 replies; 32+ messages in thread
From: Ian Rogers @ 2026-01-21  7:10 UTC (permalink / raw)
  To: Mi, Dapeng
  Cc: Arnaldo Carvalho de Melo, Peter Zijlstra, Ingo Molnar,
	Namhyung Kim, Jiri Olsa, Adrian Hunter, James Clark, John Garry,
	Will Deacon, Leo Yan, Guo Ren, Paul Walmsley, Palmer Dabbelt,
	Albert Ou, Alexandre Ghiti, Shimin Guo, Athira Rajeev,
	Stephen Brennan, Howard Chu, Thomas Falcon, Andi Kleen,
	Dr. David Alan Gilbert, Dmitry Vyukov, Krzysztof Łopatowski,
	Chun-Tse Shao, Aditya Bodkhe, Haibo Xu, Sergei Trofimovich,
	linux-kernel, linux-perf-users, linux-arm-kernel, linux-csky,
	linux-riscv, Mark Wielaard

On Tue, Jan 20, 2026 at 10:58 PM Mi, Dapeng <dapeng1.mi@linux.intel.com> wrote:
>
>
> On 1/21/2026 2:49 AM, Arnaldo Carvalho de Melo wrote:
> > On Fri, Jan 16, 2026 at 09:28:34PM -0800, Ian Rogers wrote:
> >> The arch string requires multiple strcmp to identify things like the
> >> IP and SP. Switch to passing in an e_machine that in the bulk of cases
> >> is computed using a current thread load. The e_machine also allows
> >> identification of 32-bit vs 64-bit processes.
> >>
> >> Signed-off-by: Ian Rogers <irogers@google.com>
> >> ---
> >>  tools/perf/builtin-script.c                   |  17 ++-
> >>  tools/perf/util/evsel.c                       |  14 ++-
> >>  tools/perf/util/perf_regs.c                   | 105 +++++++++++-------
> > [ Include dwarf-regs.h to get conditional defines for EM_CSKY and EM_LOONGARCH, not available in old distros ]
> >
> > - Arnaldo
>
> Seems the change would introduce the below building error.
>
> util/perf_regs.c: In function ‘perf_reg_name’:
> util/perf_regs.c:48:14: error: ‘EM_LOONGARCH’ undeclared (first use in this
> function)
>    48 |         case EM_LOONGARCH:
>       |              ^~~~~~~~~~~~
> util/perf_regs.c:48:14: note: each undeclared identifier is reported only
> once for each function it appears in
> util/perf_regs.c: In function ‘perf_arch_reg_ip’:
> util/perf_regs.c:114:14: error: ‘EM_LOONGARCH’ undeclared (first use in
> this function)
>   114 |         case EM_LOONGARCH:
>       |              ^~~~~~~~~~~~
> util/perf_regs.c: In function ‘perf_arch_reg_sp’:
> util/perf_regs.c:143:14: error: ‘EM_LOONGARCH’ undeclared (first use in
> this function)
>   143 |         case EM_LOONGARCH:
>       |              ^~~~~~~~~~~~
>
> It looks EM_LOONGARCH is not defined in the <elf.h> header file, not sure
> which one is better, include  dwarf-regs.h suggested by Arnaldo or direct
> add the define,
>
> diff --git a/tools/perf/util/perf_regs.c b/tools/perf/util/perf_regs.c
> index b58d59b84fb1..ce839df19fa3 100644
> --- a/tools/perf/util/perf_regs.c
> +++ b/tools/perf/util/perf_regs.c
> @@ -6,6 +6,10 @@
>  #include "util/sample.h"
>  #include "debug.h"
>
> +#ifndef EM_LOONGARCH
> +#define EM_LOONGARCH           258
> +#endif

Or #include <dwarf-regs.h> which I think is the fix Arnaldo is
carrying at the moment.

Thanks,
Ian

> +
>  int __weak arch_sdt_arg_parse_op(char *old_op __maybe_unused,
>                                  char **new_op __maybe_unused)
>  {
>
>
> >

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

* Re: [PATCH v1 22/23] perf unwind-libdw: Don't discard loaded ELF/Dwarf  after every unwind
  2026-01-17  5:28 ` [PATCH v1 22/23] perf unwind-libdw: Don't discard loaded ELF/Dwarf after every unwind Ian Rogers
@ 2026-01-27 17:42   ` Serhei Makarov
  2026-01-27 18:08     ` Ian Rogers
  0 siblings, 1 reply; 32+ messages in thread
From: Serhei Makarov @ 2026-01-27 17:42 UTC (permalink / raw)
  To: irogers
  Cc: acme, aditya.b1, adrian.hunter, ak, alex, aou, atrajeev, ctshao,
	dvyukov, guoren, haibo1.xu, howardchu95, james.clark,
	john.g.garry, jolsa, krzysztof.m.lopatowski, leo.yan,
	linux-arm-kernel, linux-csky, linux-kernel, linux-perf-users,
	linux-riscv, linux, mark, mingo, namhyung, palmer, peterz, pjw,
	shimin.guo, slyich, stephen.s.brennan, thomas.falcon, will

> As removing the dwfl didn't prove possible, an alternative is to just not discard the dwfl when the unwind ends. The dwfl is valid for a process unless a dso is loaded at the same address as a previous one. So keep the dwfl with the maps, invalidate it if a map is removed (in case a new map replaces it) and recycle the dwfl in the unwinding code.

Relevant note:

I'm looking at whether elfutils libdwfl_stacktrace might further help with these issues. The libdwfl_stacktrace library is currently shipped as an experimental addon to libdwfl in elfutils 0.194, but I'm intending to stabilize the API this year. There's code for some analogous functions: translating perf->dwarf register files, and caching Dwfl and Elf data to speed up unwinding across multiple processes. (Thus, if unwinding a collection of perf_events from 16 processes that use libc, we don't need to load the CFI for libc.so 16 times.)

I think once I've stabilized the libdwfl_stacktrace API and expanded the register support to a larger set of perf-supported architectures, it makes sense for me to author a corresponding set of patches for perf. I'll see if it's worth adding more libdwflst functionality to elfutils 0.195 in anticipation of this.

-- Serhei Makarov

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

* Re: [PATCH v1 22/23] perf unwind-libdw: Don't discard loaded ELF/Dwarf after every unwind
  2026-01-27 17:42   ` Serhei Makarov
@ 2026-01-27 18:08     ` Ian Rogers
  0 siblings, 0 replies; 32+ messages in thread
From: Ian Rogers @ 2026-01-27 18:08 UTC (permalink / raw)
  To: Serhei Makarov
  Cc: acme, aditya.b1, adrian.hunter, ak, alex, aou, atrajeev, ctshao,
	dvyukov, guoren, haibo1.xu, howardchu95, james.clark,
	john.g.garry, jolsa, krzysztof.m.lopatowski, leo.yan,
	linux-arm-kernel, linux-csky, linux-kernel, linux-perf-users,
	linux-riscv, linux, mark, mingo, namhyung, palmer, peterz, pjw,
	shimin.guo, slyich, stephen.s.brennan, thomas.falcon, will

On Tue, Jan 27, 2026 at 9:43 AM Serhei Makarov <serhei@serhei.io> wrote:
>
> > As removing the dwfl didn't prove possible, an alternative is to just not discard the dwfl when the unwind ends. The dwfl is valid for a process unless a dso is loaded at the same address as a previous one. So keep the dwfl with the maps, invalidate it if a map is removed (in case a new map replaces it) and recycle the dwfl in the unwinding code.
>
> Relevant note:
>
> I'm looking at whether elfutils libdwfl_stacktrace might further help with these issues. The libdwfl_stacktrace library is currently shipped as an experimental addon to libdwfl in elfutils 0.194, but I'm intending to stabilize the API this year. There's code for some analogous functions: translating perf->dwarf register files, and caching Dwfl and Elf data to speed up unwinding across multiple processes. (Thus, if unwinding a collection of perf_events from 16 processes that use libc, we don't need to load the CFI for libc.so 16 times.)
>
> I think once I've stabilized the libdwfl_stacktrace API and expanded the register support to a larger set of perf-supported architectures, it makes sense for me to author a corresponding set of patches for perf. I'll see if it's worth adding more libdwflst functionality to elfutils 0.195 in anticipation of this.

Thanks Serhei, this would be really awesome! I've done my best wrt
trying to optimize around libdwfl, but I wasn't able to have a single
dwfl for the addr2line case with a dso and the stack unwinding code.
Just to give too many details :-)

With addr2line we have the dso and an offset into the dso of some
point in code. We want to get the symbol for that address and inline
information. The "bias" notion in dwfl was something of a speed bump
in writing that code and was broken in an early version:
https://web.git.kernel.org/pub/scm/linux/kernel/git/perf/perf-tools-next.git/tree/tools/perf/util/libdw.c?h=perf-tools-next#n133
Just documenting libdw/libdwfl addresses better to say whether a bias
is or isn't necessary would be useful. Walking the inline information
is also (strangely to me) done outside of libdw, which makes me wonder
about support for things like partial compilation units:
https://web.git.kernel.org/pub/scm/linux/kernel/git/perf/perf-tools-next.git/tree/tools/perf/util/dwarf-aux.c?h=perf-tools-next#n147

For the unwinding:
https://web.git.kernel.org/pub/scm/linux/kernel/git/perf/perf-tools-next.git/tree/tools/perf/util/unwind-libdw.c?h=perf-tools-next#n407
I experimented with failing each unwind step so that then I could take
the return PC, make it an offset into a dso and reuse the dso dwfl.
This breaks many asserts in libdwfl and so I gave it up.

There's also the use of a dwfl for type profiling and probes in debuginfo:
https://web.git.kernel.org/pub/scm/linux/kernel/git/perf/perf-tools-next.git/tree/tools/perf/util/debuginfo.c?h=perf-tools-next#n55

For making the code generic we needed mapping functions between perf
and dwarf numbering, as well as number and string mapping. A problem
with the generic code was that if a register was encoded that libdwfl
didn't support it broke libdwfl. This led to clipping the registers:
https://web.git.kernel.org/pub/scm/linux/kernel/git/perf/perf-tools-next.git/tree/tools/perf/util/dwarf-regs.c?h=perf-tools-next#n157
but this feels like code that's going to bitrot, cause elfutil version
problems, etc.

Something I noticed is that there was a reduction in crashes from
0.192 to 0.194. I'm a little concerned that people may package perf
without updating elfutils and witness crashes because of these recent
perf changes. Hopefully distributions will move forward to 0.194 asap.

Thanks,
Ian

> -- Serhei Makarov

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

end of thread, other threads:[~2026-01-27 18:08 UTC | newest]

Thread overview: 32+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-01-17  5:28 [PATCH v1 00/23] perf dwarf/libdw extra support, speed and clean ups Ian Rogers
2026-01-17  5:28 ` [PATCH v1 01/23] perf symbol-elf: Fix leak of ELF files with GNU debugdata Ian Rogers
2026-01-17  5:28 ` [PATCH v1 02/23] perf dso: Extra validity checks that e_machine is valid Ian Rogers
2026-01-17  5:28 ` [PATCH v1 03/23] perf record: Disable inline frames when marking build IDs Ian Rogers
2026-01-17  5:28 ` [PATCH v1 04/23] perf unwind-libdw: fix a cross-arch unwinding bug Ian Rogers
2026-01-20 16:02   ` Arnaldo Carvalho de Melo
2026-01-20 17:53     ` Ian Rogers
2026-01-17  5:28 ` [PATCH v1 05/23] perf libdw_addr2line: Fixes to srcline memory allocation Ian Rogers
2026-01-17  5:28 ` [PATCH v1 06/23] perf unwind-libdw: Correct argument to dwfl_attach_state Ian Rogers
2026-01-17  5:28 ` [PATCH v1 07/23] perf powerpc: Unify the skip-callchain-idx libdw with that for addr2line Ian Rogers
2026-01-17  5:28 ` [PATCH v1 08/23] perf perf_regs: Switch from arch string to int e_machine Ian Rogers
2026-01-20 18:49   ` Arnaldo Carvalho de Melo
2026-01-21  6:58     ` Mi, Dapeng
2026-01-21  7:10       ` Ian Rogers
2026-01-17  5:28 ` [PATCH v1 09/23] perf dwarf-regs: Add util/dwarf-regs-arch for consistency with perf-regs Ian Rogers
2026-01-17  5:28 ` [PATCH v1 10/23] perf dwarf-regs: Remove get_arch_regnum Ian Rogers
2026-01-17  5:28 ` [PATCH v1 11/23] perf dwarf-regs: Clean up x86 dwarf_regnum code Ian Rogers
2026-01-17  5:28 ` [PATCH v1 12/23] perf dwarf-regs: Add get_dwarf_regnum_for_perf_regnum and use for x86 unwinding Ian Rogers
2026-01-17  5:42   ` Ian Rogers
2026-01-17  5:28 ` [PATCH v1 13/23] perf dwarf-regs: Add basic get_dwarf_regnum for most architectures Ian Rogers
2026-01-17  5:28 ` [PATCH v1 14/23] perf dwarf-regs: Add ARM perf to dwarf register number mapping functions Ian Rogers
2026-01-17  5:28 ` [PATCH v1 15/23] perf dwarf-regs: Add csky " Ian Rogers
2026-01-17  5:28 ` [PATCH v1 16/23] perf dwarf-regs: Add loongarch " Ian Rogers
2026-01-17  5:28 ` [PATCH v1 17/23] perf dwarf-regs: Add powerpc " Ian Rogers
2026-01-17  5:28 ` [PATCH v1 18/23] perf dwarf-regs: Add RISC-V " Ian Rogers
2026-01-17  5:28 ` [PATCH v1 19/23] perf dwarf-regs: Add S390 " Ian Rogers
2026-01-17  5:28 ` [PATCH v1 20/23] perf dwarf-regs: Add MIPS " Ian Rogers
2026-01-17  5:28 ` [PATCH v1 21/23] perf build: Remove NO_LIBDW_DWARF_UNWIND option Ian Rogers
2026-01-17  5:28 ` [PATCH v1 22/23] perf unwind-libdw: Don't discard loaded ELF/Dwarf after every unwind Ian Rogers
2026-01-27 17:42   ` Serhei Makarov
2026-01-27 18:08     ` Ian Rogers
2026-01-17  5:28 ` [PATCH v1 23/23] perf machine: Add inline information to frame pointer and LBR callchains Ian Rogers

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