* [RFC PATCH v1 0/7] perf libunwind multiple remote support
@ 2026-02-24 14:29 Ian Rogers
2026-02-24 14:29 ` [RFC PATCH v1 1/7] tools build: Deduplicate test-libunwind for different architectures Ian Rogers
` (6 more replies)
0 siblings, 7 replies; 27+ messages in thread
From: Ian Rogers @ 2026-02-24 14:29 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Alexander Shishkin, Jiri Olsa, Ian Rogers,
Adrian Hunter, James Clark, John Garry, Will Deacon, Leo Yan,
Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
Dmitrii Dolgov, Blake Jones, Tomas Glozar, Yuzhuo Jing, Dapeng Mi,
Shimin Guo, Athira Rajeev, Chun-Tse Shao, Howard Chu,
linux-kernel, linux-perf-users, linux-arm-kernel, linux-riscv,
Thomas Richter, libunwind-devel
Refactor the libunwind support so that whenever a remote target is
available, perf functions using the ELF machine can use that remote
target regardless of what the host/local machine is. Migrate existing
libunwind supported architectures like powerpc, arm64 and loongarch so
that they can work in a cross-architecture way. Add support for
RISC-V. Make the code more regular in function names, etc. and avoid
including a C-file. This increases the lines of code. It is similar in
style to the unwind-libdw implementation. It is hoped that the more
uniform nature of the code with help with refactoring the perf
registers for SIMD/APX support.
Aside from local host testing these patches are under tested, in part
as I'm failing to see how to build libunwind with support for multiple
remote targets. Please could I get help in testing, hence this being
an RFC.
Ian Rogers (7):
tools build: Deduplicate test-libunwind for different architectures
perf build: Be more programmatic when setting up libunwind variables
perf build loongarch: Remove reference to missing file
perf unwind-libunwind: Make libunwind register reading cross platform
perf unwind-libunwind: Move flush/finish access out of local
perf unwind-libunwind: Remove libunwind-local
perf unwind-libunwind: Add RISC-V libunwind support
tools/build/feature/Makefile | 38 +-
tools/build/feature/test-libunwind-aarch64.c | 27 -
tools/build/feature/test-libunwind-arm.c | 28 -
.../test-libunwind-debug-frame-aarch64.c | 17 -
.../feature/test-libunwind-debug-frame-arm.c | 17 -
.../feature/test-libunwind-debug-frame.c | 1 -
tools/build/feature/test-libunwind-x86.c | 28 -
tools/build/feature/test-libunwind-x86_64.c | 28 -
tools/build/feature/test-libunwind.c | 1 -
tools/perf/Makefile.config | 215 ++---
tools/perf/arch/arm/util/Build | 2 -
tools/perf/arch/arm/util/unwind-libunwind.c | 50 --
tools/perf/arch/arm64/util/Build | 1 -
tools/perf/arch/arm64/util/unwind-libunwind.c | 17 -
tools/perf/arch/loongarch/util/Build | 3 -
.../arch/loongarch/util/unwind-libunwind.c | 82 --
tools/perf/arch/mips/Build | 1 -
tools/perf/arch/mips/util/Build | 1 -
tools/perf/arch/mips/util/unwind-libunwind.c | 22 -
tools/perf/arch/powerpc/util/Build | 1 -
.../perf/arch/powerpc/util/unwind-libunwind.c | 92 --
tools/perf/arch/x86/util/Build | 3 -
tools/perf/arch/x86/util/unwind-libunwind.c | 115 ---
tools/perf/util/Build | 4 +-
tools/perf/util/libunwind-arch/Build | 11 +
.../perf/util/libunwind-arch/libunwind-arch.c | 319 +++++++
.../perf/util/libunwind-arch/libunwind-arch.h | 296 +++++++
.../perf/util/libunwind-arch/libunwind-arm.c | 290 ++++++
.../util/libunwind-arch/libunwind-arm64.c | 289 ++++++
.../perf/util/libunwind-arch/libunwind-i386.c | 312 +++++++
.../util/libunwind-arch/libunwind-loongarch.c | 297 +++++++
.../perf/util/libunwind-arch/libunwind-mips.c | 299 +++++++
.../util/libunwind-arch/libunwind-ppc32.c | 301 +++++++
.../util/libunwind-arch/libunwind-ppc64.c | 303 +++++++
.../util/libunwind-arch/libunwind-riscv.c | 297 +++++++
.../perf/util/libunwind-arch/libunwind-s390.c | 299 +++++++
.../util/libunwind-arch/libunwind-x86_64.c | 320 +++++++
tools/perf/util/libunwind/arm64.c | 40 -
tools/perf/util/libunwind/x86_32.c | 41 -
tools/perf/util/maps.c | 29 +-
tools/perf/util/maps.h | 4 +-
tools/perf/util/thread.c | 29 +-
tools/perf/util/unwind-libunwind-local.c | 832 ------------------
tools/perf/util/unwind-libunwind.c | 677 ++++++++++++--
tools/perf/util/unwind.h | 13 +-
45 files changed, 4389 insertions(+), 1703 deletions(-)
delete mode 100644 tools/build/feature/test-libunwind-aarch64.c
delete mode 100644 tools/build/feature/test-libunwind-arm.c
delete mode 100644 tools/build/feature/test-libunwind-debug-frame-aarch64.c
delete mode 100644 tools/build/feature/test-libunwind-debug-frame-arm.c
delete mode 100644 tools/build/feature/test-libunwind-x86.c
delete mode 100644 tools/build/feature/test-libunwind-x86_64.c
delete mode 100644 tools/perf/arch/arm/util/unwind-libunwind.c
delete mode 100644 tools/perf/arch/arm64/util/unwind-libunwind.c
delete mode 100644 tools/perf/arch/loongarch/util/unwind-libunwind.c
delete mode 100644 tools/perf/arch/mips/Build
delete mode 100644 tools/perf/arch/mips/util/Build
delete mode 100644 tools/perf/arch/mips/util/unwind-libunwind.c
delete mode 100644 tools/perf/arch/powerpc/util/unwind-libunwind.c
delete mode 100644 tools/perf/arch/x86/util/unwind-libunwind.c
create mode 100644 tools/perf/util/libunwind-arch/Build
create mode 100644 tools/perf/util/libunwind-arch/libunwind-arch.c
create mode 100644 tools/perf/util/libunwind-arch/libunwind-arch.h
create mode 100644 tools/perf/util/libunwind-arch/libunwind-arm.c
create mode 100644 tools/perf/util/libunwind-arch/libunwind-arm64.c
create mode 100644 tools/perf/util/libunwind-arch/libunwind-i386.c
create mode 100644 tools/perf/util/libunwind-arch/libunwind-loongarch.c
create mode 100644 tools/perf/util/libunwind-arch/libunwind-mips.c
create mode 100644 tools/perf/util/libunwind-arch/libunwind-ppc32.c
create mode 100644 tools/perf/util/libunwind-arch/libunwind-ppc64.c
create mode 100644 tools/perf/util/libunwind-arch/libunwind-riscv.c
create mode 100644 tools/perf/util/libunwind-arch/libunwind-s390.c
create mode 100644 tools/perf/util/libunwind-arch/libunwind-x86_64.c
delete mode 100644 tools/perf/util/libunwind/arm64.c
delete mode 100644 tools/perf/util/libunwind/x86_32.c
delete mode 100644 tools/perf/util/unwind-libunwind-local.c
--
2.53.0.371.g1d285c8824-goog
^ permalink raw reply [flat|nested] 27+ messages in thread
* [RFC PATCH v1 1/7] tools build: Deduplicate test-libunwind for different architectures
2026-02-24 14:29 [RFC PATCH v1 0/7] perf libunwind multiple remote support Ian Rogers
@ 2026-02-24 14:29 ` Ian Rogers
2026-02-24 14:29 ` [RFC PATCH v1 2/7] perf build: Be more programmatic when setting up libunwind variables Ian Rogers
` (5 subsequent siblings)
6 siblings, 0 replies; 27+ messages in thread
From: Ian Rogers @ 2026-02-24 14:29 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Alexander Shishkin, Jiri Olsa, Ian Rogers,
Adrian Hunter, James Clark, John Garry, Will Deacon, Leo Yan,
Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
Dmitrii Dolgov, Blake Jones, Tomas Glozar, Yuzhuo Jing, Dapeng Mi,
Shimin Guo, Athira Rajeev, Chun-Tse Shao, Howard Chu,
linux-kernel, linux-perf-users, linux-arm-kernel, linux-riscv,
Thomas Richter, libunwind-devel
The separate test files only exist to pass a different #include,
instead have a single source file and pass -include to $(CC) to
include the relevant header file for the architecture being
tested. Generate the rules using a foreach loop. Include tests for all
current libunwind architectures.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/build/feature/Makefile | 38 +++++++++----------
tools/build/feature/test-libunwind-aarch64.c | 27 -------------
tools/build/feature/test-libunwind-arm.c | 28 --------------
.../test-libunwind-debug-frame-aarch64.c | 17 ---------
.../feature/test-libunwind-debug-frame-arm.c | 17 ---------
.../feature/test-libunwind-debug-frame.c | 1 -
tools/build/feature/test-libunwind-x86.c | 28 --------------
tools/build/feature/test-libunwind-x86_64.c | 28 --------------
tools/build/feature/test-libunwind.c | 1 -
9 files changed, 19 insertions(+), 166 deletions(-)
delete mode 100644 tools/build/feature/test-libunwind-aarch64.c
delete mode 100644 tools/build/feature/test-libunwind-arm.c
delete mode 100644 tools/build/feature/test-libunwind-debug-frame-aarch64.c
delete mode 100644 tools/build/feature/test-libunwind-debug-frame-arm.c
delete mode 100644 tools/build/feature/test-libunwind-x86.c
delete mode 100644 tools/build/feature/test-libunwind-x86_64.c
diff --git a/tools/build/feature/Makefile b/tools/build/feature/Makefile
index 1fbcb3ce74d2..a0b40e826688 100644
--- a/tools/build/feature/Makefile
+++ b/tools/build/feature/Makefile
@@ -204,27 +204,27 @@ $(OUTPUT)test-numa_num_possible_cpus.bin:
$(BUILD) -lnuma
$(OUTPUT)test-libunwind.bin:
- $(BUILD) -lelf -llzma
+ $(BUILD) -include libunwind.h -lelf -llzma -lunwind
$(OUTPUT)test-libunwind-debug-frame.bin:
- $(BUILD) -lelf -llzma
-$(OUTPUT)test-libunwind-x86.bin:
- $(BUILD) -lelf -llzma -lunwind-x86
-
-$(OUTPUT)test-libunwind-x86_64.bin:
- $(BUILD) -lelf -llzma -lunwind-x86_64
-
-$(OUTPUT)test-libunwind-arm.bin:
- $(BUILD) -lelf -llzma -lunwind-arm
-
-$(OUTPUT)test-libunwind-aarch64.bin:
- $(BUILD) -lelf -llzma -lunwind-aarch64
-
-$(OUTPUT)test-libunwind-debug-frame-arm.bin:
- $(BUILD) -lelf -llzma -lunwind-arm
-
-$(OUTPUT)test-libunwind-debug-frame-aarch64.bin:
- $(BUILD) -lelf -llzma -lunwind-aarch64
+ $(BUILD) -include libunwind.h -lelf -llzma -lunwind
+
+LIBUNWIND_ARCHS:=aarch64 arm loongarch64 mips ppc32 ppc64 riscv s390x x86 x86_64
+define LIBUNWIND_RULE
+$$(OUTPUT)test-libunwind-$(1).bin:
+ $$(CC) $$(CFLAGS) -MD -Wall -Werror -include libunwind-$(1).h -o $$@ \
+ test-libunwind.c $$(LDFLAGS) -lelf -llzma -lunwind-$(1) \
+ > $$(@:.bin=.make.output) 2>&1
+
+$$(OUTPUT)test-libunwind-debug-frame-$(1).bin:
+ $$(CC) $$(CFLAGS) -MD -Wall -Werror -include libunwind-$(1).h -o $$@ \
+ test-libunwind-debug-frame.c $$(LDFLAGS) -lelf -llzma -lunwind-$(1) \
+ > $$(@:.bin=.make.output) 2>&1
+
+endef
+$(foreach arch,$(LIBUNWIND_ARCHS), \
+ $(eval $(call LIBUNWIND_RULE,$(arch))) \
+)
$(OUTPUT)test-libslang.bin:
$(BUILD) -lslang
diff --git a/tools/build/feature/test-libunwind-aarch64.c b/tools/build/feature/test-libunwind-aarch64.c
deleted file mode 100644
index 323803f49212..000000000000
--- a/tools/build/feature/test-libunwind-aarch64.c
+++ /dev/null
@@ -1,27 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-#include <libunwind-aarch64.h>
-#include <stdlib.h>
-
-extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,
- unw_word_t ip,
- unw_dyn_info_t *di,
- unw_proc_info_t *pi,
- int need_unwind_info, void *arg);
-
-#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table)
-
-static unw_accessors_t accessors;
-
-int main(void)
-{
- unw_addr_space_t addr_space;
-
- addr_space = unw_create_addr_space(&accessors, 0);
- if (addr_space)
- return 0;
-
- unw_init_remote(NULL, addr_space, NULL);
- dwarf_search_unwind_table(addr_space, 0, NULL, NULL, 0, NULL);
-
- return 0;
-}
diff --git a/tools/build/feature/test-libunwind-arm.c b/tools/build/feature/test-libunwind-arm.c
deleted file mode 100644
index cb378b7d6866..000000000000
--- a/tools/build/feature/test-libunwind-arm.c
+++ /dev/null
@@ -1,28 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-#include <libunwind-arm.h>
-#include <stdlib.h>
-
-extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,
- unw_word_t ip,
- unw_dyn_info_t *di,
- unw_proc_info_t *pi,
- int need_unwind_info, void *arg);
-
-
-#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table)
-
-static unw_accessors_t accessors;
-
-int main(void)
-{
- unw_addr_space_t addr_space;
-
- addr_space = unw_create_addr_space(&accessors, 0);
- if (addr_space)
- return 0;
-
- unw_init_remote(NULL, addr_space, NULL);
- dwarf_search_unwind_table(addr_space, 0, NULL, NULL, 0, NULL);
-
- return 0;
-}
diff --git a/tools/build/feature/test-libunwind-debug-frame-aarch64.c b/tools/build/feature/test-libunwind-debug-frame-aarch64.c
deleted file mode 100644
index 36d6646c185e..000000000000
--- a/tools/build/feature/test-libunwind-debug-frame-aarch64.c
+++ /dev/null
@@ -1,17 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-#include <libunwind-aarch64.h>
-#include <stdlib.h>
-
-extern int
-UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug,
- unw_word_t ip, unw_word_t segbase,
- const char *obj_name, unw_word_t start,
- unw_word_t end);
-
-#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame)
-
-int main(void)
-{
- dwarf_find_debug_frame(0, NULL, 0, 0, NULL, 0, 0);
- return 0;
-}
diff --git a/tools/build/feature/test-libunwind-debug-frame-arm.c b/tools/build/feature/test-libunwind-debug-frame-arm.c
deleted file mode 100644
index 8696e48e1268..000000000000
--- a/tools/build/feature/test-libunwind-debug-frame-arm.c
+++ /dev/null
@@ -1,17 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-#include <libunwind-arm.h>
-#include <stdlib.h>
-
-extern int
-UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug,
- unw_word_t ip, unw_word_t segbase,
- const char *obj_name, unw_word_t start,
- unw_word_t end);
-
-#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame)
-
-int main(void)
-{
- dwarf_find_debug_frame(0, NULL, 0, 0, NULL, 0, 0);
- return 0;
-}
diff --git a/tools/build/feature/test-libunwind-debug-frame.c b/tools/build/feature/test-libunwind-debug-frame.c
index efb55cdd8d01..4c57e37004b3 100644
--- a/tools/build/feature/test-libunwind-debug-frame.c
+++ b/tools/build/feature/test-libunwind-debug-frame.c
@@ -1,5 +1,4 @@
// SPDX-License-Identifier: GPL-2.0
-#include <libunwind.h>
#include <stdlib.h>
extern int
diff --git a/tools/build/feature/test-libunwind-x86.c b/tools/build/feature/test-libunwind-x86.c
deleted file mode 100644
index e5e0f6c89637..000000000000
--- a/tools/build/feature/test-libunwind-x86.c
+++ /dev/null
@@ -1,28 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-#include <libunwind-x86.h>
-#include <stdlib.h>
-
-extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,
- unw_word_t ip,
- unw_dyn_info_t *di,
- unw_proc_info_t *pi,
- int need_unwind_info, void *arg);
-
-
-#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table)
-
-static unw_accessors_t accessors;
-
-int main(void)
-{
- unw_addr_space_t addr_space;
-
- addr_space = unw_create_addr_space(&accessors, 0);
- if (addr_space)
- return 0;
-
- unw_init_remote(NULL, addr_space, NULL);
- dwarf_search_unwind_table(addr_space, 0, NULL, NULL, 0, NULL);
-
- return 0;
-}
diff --git a/tools/build/feature/test-libunwind-x86_64.c b/tools/build/feature/test-libunwind-x86_64.c
deleted file mode 100644
index 62ae4db597dc..000000000000
--- a/tools/build/feature/test-libunwind-x86_64.c
+++ /dev/null
@@ -1,28 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-#include <libunwind-x86_64.h>
-#include <stdlib.h>
-
-extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,
- unw_word_t ip,
- unw_dyn_info_t *di,
- unw_proc_info_t *pi,
- int need_unwind_info, void *arg);
-
-
-#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table)
-
-static unw_accessors_t accessors;
-
-int main(void)
-{
- unw_addr_space_t addr_space;
-
- addr_space = unw_create_addr_space(&accessors, 0);
- if (addr_space)
- return 0;
-
- unw_init_remote(NULL, addr_space, NULL);
- dwarf_search_unwind_table(addr_space, 0, NULL, NULL, 0, NULL);
-
- return 0;
-}
diff --git a/tools/build/feature/test-libunwind.c b/tools/build/feature/test-libunwind.c
index 53fd26614ff0..5af5dc3a73d4 100644
--- a/tools/build/feature/test-libunwind.c
+++ b/tools/build/feature/test-libunwind.c
@@ -1,5 +1,4 @@
// SPDX-License-Identifier: GPL-2.0
-#include <libunwind.h>
#include <stdlib.h>
extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,
--
2.53.0.371.g1d285c8824-goog
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [RFC PATCH v1 2/7] perf build: Be more programmatic when setting up libunwind variables
2026-02-24 14:29 [RFC PATCH v1 0/7] perf libunwind multiple remote support Ian Rogers
2026-02-24 14:29 ` [RFC PATCH v1 1/7] tools build: Deduplicate test-libunwind for different architectures Ian Rogers
@ 2026-02-24 14:29 ` Ian Rogers
2026-02-24 14:29 ` [RFC PATCH v1 3/7] perf build loongarch: Remove reference to missing file Ian Rogers
` (4 subsequent siblings)
6 siblings, 0 replies; 27+ messages in thread
From: Ian Rogers @ 2026-02-24 14:29 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Alexander Shishkin, Jiri Olsa, Ian Rogers,
Adrian Hunter, James Clark, John Garry, Will Deacon, Leo Yan,
Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
Dmitrii Dolgov, Blake Jones, Tomas Glozar, Yuzhuo Jing, Dapeng Mi,
Shimin Guo, Athira Rajeev, Chun-Tse Shao, Howard Chu,
linux-kernel, linux-perf-users, linux-arm-kernel, linux-riscv,
Thomas Richter, libunwind-devel
Iterate LIBUNWIND_ARCHS when setting up CONFIG_ and HAVE_ definitions
rather than treating each architecture individually. This sets up the
libunwind build variables and C definitions beyond x86 and
arm/aarch64. The existing naming convention is followed for
compatibility.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/Makefile.config | 215 +++++++++++++++----------------------
1 file changed, 89 insertions(+), 126 deletions(-)
diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config
index a8dc72cfe48e..f38213ecce08 100644
--- a/tools/perf/Makefile.config
+++ b/tools/perf/Makefile.config
@@ -65,95 +65,83 @@ $(call detected_var,SRCARCH)
CFLAGS += -I$(OUTPUT)arch/$(SRCARCH)/include/generated
-# Additional ARCH settings for ppc
-ifeq ($(SRCARCH),powerpc)
- ifndef NO_LIBUNWIND
- LIBUNWIND_LIBS := -lunwind -lunwind-ppc64
- endif
-endif
-
# Additional ARCH settings for x86
ifeq ($(SRCARCH),x86)
$(call detected,CONFIG_X86)
ifeq (${IS_64_BIT}, 1)
CFLAGS += -DHAVE_ARCH_X86_64_SUPPORT
ARCH_INCLUDE = ../../arch/x86/lib/memcpy_64.S ../../arch/x86/lib/memset_64.S
- ifndef NO_LIBUNWIND
- LIBUNWIND_LIBS = -lunwind-x86_64 -lunwind -llzma
- endif
$(call detected,CONFIG_X86_64)
- else
- ifndef NO_LIBUNWIND
- LIBUNWIND_LIBS = -lunwind-x86 -llzma -lunwind
- endif
endif
endif
-ifeq ($(SRCARCH),arm)
- ifndef NO_LIBUNWIND
- LIBUNWIND_LIBS = -lunwind -lunwind-arm
- endif
+ifeq ($(ARCH),s390)
+ CFLAGS += -fPIC
endif
-ifeq ($(SRCARCH),arm64)
- ifndef NO_LIBUNWIND
- LIBUNWIND_LIBS = -lunwind -lunwind-aarch64
- endif
+ifneq ($(LIBUNWIND),1)
+ NO_LIBUNWIND := 1
endif
-ifeq ($(SRCARCH),loongarch)
- ifndef NO_LIBUNWIND
+ifndef NO_LIBUNWIND
+ ifeq ($(SRCARCH),arm)
+ LIBUNWIND_LIBS = -lunwind -lunwind-arm
+ endif
+ ifeq ($(SRCARCH),arm64)
+ LIBUNWIND_LIBS = -lunwind -lunwind-aarch64
+ endif
+ ifeq ($(SRCARCH),loongarch)
LIBUNWIND_LIBS = -lunwind -lunwind-loongarch64
endif
-endif
-
-ifeq ($(ARCH),s390)
- CFLAGS += -fPIC
-endif
-
-ifeq ($(ARCH),mips)
- ifndef NO_LIBUNWIND
+ ifeq ($(ARCH),mips)
LIBUNWIND_LIBS = -lunwind -lunwind-mips
endif
+ ifeq ($(SRCARCH),powerpc)
+ LIBUNWIND_LIBS := -lunwind -lunwind-ppc64
+ endif
+ ifeq ($(SRCARCH),riscv)
+ LIBUNWIND_LIBS := -lunwind -lunwind-riscv
+ endif
+ ifeq ($(SRCARCH),s390)
+ LIBUNWIND_LIBS := -lunwind -lunwind-s390x
+ endif
+ ifeq ($(SRCARCH),x86)
+ ifeq (${IS_64_BIT}, 1)
+ LIBUNWIND_LIBS = -lunwind-x86_64 -lunwind -llzma
+ else
+ LIBUNWIND_LIBS = -lunwind-x86 -lunwind -llzma
+ endif
+ endif
+ ifeq ($(LIBUNWIND_LIBS),)
+ NO_LIBUNWIND := 1
+ endif
endif
-ifneq ($(LIBUNWIND),1)
- NO_LIBUNWIND := 1
-endif
-
-ifeq ($(LIBUNWIND_LIBS),)
- NO_LIBUNWIND := 1
-endif
#
# For linking with debug library, run like:
#
# make DEBUG=1 LIBUNWIND_DIR=/opt/libunwind/
#
-
-libunwind_arch_set_flags = $(eval $(libunwind_arch_set_flags_code))
-define libunwind_arch_set_flags_code
- FEATURE_CHECK_CFLAGS-libunwind-$(1) = -I$(LIBUNWIND_DIR)/include
- FEATURE_CHECK_LDFLAGS-libunwind-$(1) = -L$(LIBUNWIND_DIR)/lib
-endef
-
-ifdef LIBUNWIND_DIR
- LIBUNWIND_CFLAGS = -I$(LIBUNWIND_DIR)/include
- LIBUNWIND_LDFLAGS = -L$(LIBUNWIND_DIR)/lib
- LIBUNWIND_ARCHS = x86 x86_64 arm aarch64 debug-frame-arm debug-frame-aarch64 loongarch
- $(foreach libunwind_arch,$(LIBUNWIND_ARCHS),$(call libunwind_arch_set_flags,$(libunwind_arch)))
-endif
+LIBUNWIND_ARCHS:=aarch64 arm loongarch64 mips ppc32 ppc64 riscv s390x x86 x86_64
ifndef NO_LIBUNWIND
- # Set per-feature check compilation flags
FEATURE_CHECK_CFLAGS-libunwind = $(LIBUNWIND_CFLAGS)
FEATURE_CHECK_LDFLAGS-libunwind = $(LIBUNWIND_LDFLAGS) $(LIBUNWIND_LIBS)
FEATURE_CHECK_CFLAGS-libunwind-debug-frame = $(LIBUNWIND_CFLAGS)
FEATURE_CHECK_LDFLAGS-libunwind-debug-frame = $(LIBUNWIND_LDFLAGS) $(LIBUNWIND_LIBS)
-
- FEATURE_CHECK_LDFLAGS-libunwind-arm += -lunwind -lunwind-arm
- FEATURE_CHECK_LDFLAGS-libunwind-aarch64 += -lunwind -lunwind-aarch64
- FEATURE_CHECK_LDFLAGS-libunwind-x86 += -lunwind -llzma -lunwind-x86
- FEATURE_CHECK_LDFLAGS-libunwind-x86_64 += -lunwind -llzma -lunwind-x86_64
+
+ ifdef LIBUNWIND_DIR
+ LIBUNWIND_CFLAGS = -I$(LIBUNWIND_DIR)/include
+ LIBUNWIND_LDFLAGS = -L$(LIBUNWIND_DIR)/lib
+
+ define libunwind_arch_set_flags
+ FEATURE_CHECK_CFLAGS-libunwind-$(1) = -I$(LIBUNWIND_DIR)/include
+ FEATURE_CHECK_LDFLAGS-libunwind-$(1) = -L$(LIBUNWIND_DIR)/lib
+ endef
+ $(foreach arch,$(LIBUNWIND_ARCHS), \
+ $(eval $(call libunwind_arch_set_flags,$(arch))) \
+ )
+ endif
endif
ifdef CSINCLUDES
@@ -637,49 +625,6 @@ ifeq ($(SRCARCH),powerpc)
endif
endif
-ifndef NO_LIBUNWIND
- have_libunwind :=
-
- $(call feature_check,libunwind)
-
- $(call feature_check,libunwind-x86)
- ifeq ($(feature-libunwind-x86), 1)
- $(call detected,CONFIG_LIBUNWIND_X86)
- CFLAGS += -DHAVE_LIBUNWIND_X86_SUPPORT
- LDFLAGS += -lunwind-x86
- EXTLIBS_LIBUNWIND += -lunwind-x86
- have_libunwind = 1
- endif
-
- $(call feature_check,libunwind-aarch64)
- ifeq ($(feature-libunwind-aarch64), 1)
- $(call detected,CONFIG_LIBUNWIND_AARCH64)
- CFLAGS += -DHAVE_LIBUNWIND_AARCH64_SUPPORT
- LDFLAGS += -lunwind-aarch64
- EXTLIBS_LIBUNWIND += -lunwind-aarch64
- have_libunwind = 1
- $(call feature_check,libunwind-debug-frame-aarch64)
- ifneq ($(feature-libunwind-debug-frame-aarch64), 1)
- $(warning No debug_frame support found in libunwind-aarch64)
- CFLAGS += -DNO_LIBUNWIND_DEBUG_FRAME_AARCH64
- endif
- endif
-
- ifneq ($(feature-libunwind), 1)
- $(warning No libunwind found. Please install libunwind-dev[el] >= 1.1 and/or set LIBUNWIND_DIR and set LIBUNWIND=1 in the make command line as it is opt-in now)
- NO_LOCAL_LIBUNWIND := 1
- else
- have_libunwind := 1
- $(call detected,CONFIG_LOCAL_LIBUNWIND)
- endif
-
- ifneq ($(have_libunwind), 1)
- NO_LIBUNWIND := 1
- endif
-else
- NO_LOCAL_LIBUNWIND := 1
-endif
-
ifndef NO_LIBBPF
ifneq ($(feature-bpf), 1)
$(warning BPF API too old. Please install recent kernel headers. BPF support in 'perf record' is disabled.)
@@ -738,6 +683,49 @@ ifndef GEN_VMLINUX_H
VMLINUX_H=$(src-perf)/util/bpf_skel/vmlinux/vmlinux.h
endif
+ifndef NO_LIBUNWIND
+ have_libunwind :=
+
+ $(call feature_check,libunwind)
+ ifneq ($(feature-libunwind), 1)
+ $(warning No libunwind found. Please install libunwind-dev[el] >= 1.1 and/or set LIBUNWIND_DIR and set LIBUNWIND=1 in the make command line as it is opt-in now)
+ NO_LOCAL_LIBUNWIND := 1
+ else
+ have_libunwind := 1
+ $(call detected,CONFIG_LOCAL_LIBUNWIND)
+ CFLAGS += -DHAVE_LIBUNWIND_SUPPORT
+ CFLAGS += $(LIBUNWIND_CFLAGS)
+ LDFLAGS += $(LIBUNWIND_LDFLAGS)
+ EXTLIBS += $(EXTLIBS_LIBUNWIND)
+ $(call feature_check,libunwind-debug-frame)
+ ifneq ($(feature-libunwind-debug-frame), 1)
+ CFLAGS += -DNO_LIBUNWIND_DEBUG_FRAME
+ endif
+ endif
+
+ define PROCESS_REMOTE_LIBUNWIND_ARCH
+ $(call feature_check,libunwind-$(1))
+
+ ifeq ($$(feature-libunwind-$(1)), 1)
+ upper_arch := $$(shell echo $(1) | tr '[:lower:]' '[:upper:]')
+ $$(call detected,CONFIG_LIBUNWIND_$$(upper_arch))
+
+ CFLAGS += -DHAVE_LIBUNWIND_$$(upper_arch)_SUPPORT
+ LDFLAGS += -lunwind-$(1)
+ EXTLIBS_LIBUNWIND += -lunwind-$(1)
+ have_libunwind := 1
+
+ $$(call feature_check,libunwind-debug-frame-$(1))
+ ifneq ($$(feature-libunwind-debug-frame-$(1)), 1)
+ CFLAGS += -DNO_LIBUNWIND_DEBUG_FRAME_$$(upper_arch)
+ endif
+ endif
+ endef
+ $(foreach arch,$(LIBUNWIND_ARCHS), \
+ $(eval $(call PROCESS_REMOTE_LIBUNWIND_ARCH,$(arch))) \
+ )
+endif
+
dwarf-post-unwind := 1
dwarf-post-unwind-text := BUG
@@ -760,31 +748,6 @@ ifeq ($(dwarf-post-unwind),1)
$(call detected,CONFIG_DWARF_UNWIND)
endif
-ifndef NO_LIBUNWIND
- ifndef NO_LOCAL_LIBUNWIND
- ifeq ($(SRCARCH),$(filter $(SRCARCH),arm arm64))
- $(call feature_check,libunwind-debug-frame)
- ifneq ($(feature-libunwind-debug-frame), 1)
- $(warning No debug_frame support found in libunwind)
- CFLAGS += -DNO_LIBUNWIND_DEBUG_FRAME
- endif
- else
- # non-ARM has no dwarf_find_debug_frame() function:
- CFLAGS += -DNO_LIBUNWIND_DEBUG_FRAME
- endif
- EXTLIBS += $(LIBUNWIND_LIBS)
- LDFLAGS += $(LIBUNWIND_LIBS)
- endif
- ifeq ($(findstring -static,${LDFLAGS}),-static)
- # gcc -static links libgcc_eh which contans piece of libunwind
- LIBUNWIND_LDFLAGS += -Wl,--allow-multiple-definition
- endif
- CFLAGS += -DHAVE_LIBUNWIND_SUPPORT
- CFLAGS += $(LIBUNWIND_CFLAGS)
- LDFLAGS += $(LIBUNWIND_LDFLAGS)
- EXTLIBS += $(EXTLIBS_LIBUNWIND)
-endif
-
ifneq ($(NO_LIBTRACEEVENT),1)
$(call detected,CONFIG_TRACE)
endif
--
2.53.0.371.g1d285c8824-goog
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [RFC PATCH v1 3/7] perf build loongarch: Remove reference to missing file
2026-02-24 14:29 [RFC PATCH v1 0/7] perf libunwind multiple remote support Ian Rogers
2026-02-24 14:29 ` [RFC PATCH v1 1/7] tools build: Deduplicate test-libunwind for different architectures Ian Rogers
2026-02-24 14:29 ` [RFC PATCH v1 2/7] perf build: Be more programmatic when setting up libunwind variables Ian Rogers
@ 2026-02-24 14:29 ` Ian Rogers
2026-02-24 14:29 ` [RFC PATCH v1 4/7] perf unwind-libunwind: Make libunwind register reading cross platform Ian Rogers
` (3 subsequent siblings)
6 siblings, 0 replies; 27+ messages in thread
From: Ian Rogers @ 2026-02-24 14:29 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Alexander Shishkin, Jiri Olsa, Ian Rogers,
Adrian Hunter, James Clark, John Garry, Will Deacon, Leo Yan,
Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
Dmitrii Dolgov, Blake Jones, Tomas Glozar, Yuzhuo Jing, Dapeng Mi,
Shimin Guo, Athira Rajeev, Chun-Tse Shao, Howard Chu,
linux-kernel, linux-perf-users, linux-arm-kernel, linux-riscv,
Thomas Richter, libunwind-devel
The file was removed in commit e62fae9d9e85 ("perf unwind-libdw: Fix a
cross-arch unwinding bug") but the Build file not updated.
Fixes: commit e62fae9d9e85 ("perf unwind-libdw: Fix a cross-arch unwinding bug")
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/arch/loongarch/util/Build | 1 -
1 file changed, 1 deletion(-)
diff --git a/tools/perf/arch/loongarch/util/Build b/tools/perf/arch/loongarch/util/Build
index 3ad73d0289f3..8d91e78d31c9 100644
--- a/tools/perf/arch/loongarch/util/Build
+++ b/tools/perf/arch/loongarch/util/Build
@@ -1,4 +1,3 @@
perf-util-y += header.o
perf-util-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o
-perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
--
2.53.0.371.g1d285c8824-goog
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [RFC PATCH v1 4/7] perf unwind-libunwind: Make libunwind register reading cross platform
2026-02-24 14:29 [RFC PATCH v1 0/7] perf libunwind multiple remote support Ian Rogers
` (2 preceding siblings ...)
2026-02-24 14:29 ` [RFC PATCH v1 3/7] perf build loongarch: Remove reference to missing file Ian Rogers
@ 2026-02-24 14:29 ` Ian Rogers
2026-02-24 14:29 ` [RFC PATCH v1 5/7] perf unwind-libunwind: Move flush/finish access out of local Ian Rogers
` (2 subsequent siblings)
6 siblings, 0 replies; 27+ messages in thread
From: Ian Rogers @ 2026-02-24 14:29 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Alexander Shishkin, Jiri Olsa, Ian Rogers,
Adrian Hunter, James Clark, John Garry, Will Deacon, Leo Yan,
Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
Dmitrii Dolgov, Blake Jones, Tomas Glozar, Yuzhuo Jing, Dapeng Mi,
Shimin Guo, Athira Rajeev, Chun-Tse Shao, Howard Chu,
linux-kernel, linux-perf-users, linux-arm-kernel, linux-riscv,
Thomas Richter, libunwind-devel
Move the libunwind register to perf register mapping functions in
arch/../util/unwind-libunwind.c into a new libunwind-arch
directory. Rename the functions to
__get_perf_regnum_for_unw_regnum_<arch>. Add untested ppc32 and s390
functions. Add a get_perf_regnum_for_unw_regnum function that takes an
ELF machine as well as a register number and chooses the appropriate
architecture implementation.
Split the x86 and powerpc 32 and 64-bit implementations apart so that
a single libunwind-<arch>.h header is included.
Move the e_machine into the unwind_info struct to make it easier to
pass.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/arch/arm/util/Build | 2 -
tools/perf/arch/arm/util/unwind-libunwind.c | 50 --------
tools/perf/arch/arm64/util/Build | 1 -
tools/perf/arch/arm64/util/unwind-libunwind.c | 17 ---
tools/perf/arch/loongarch/util/Build | 2 -
.../arch/loongarch/util/unwind-libunwind.c | 82 -------------
tools/perf/arch/mips/Build | 1 -
tools/perf/arch/mips/util/Build | 1 -
tools/perf/arch/mips/util/unwind-libunwind.c | 22 ----
tools/perf/arch/powerpc/util/Build | 1 -
.../perf/arch/powerpc/util/unwind-libunwind.c | 92 --------------
tools/perf/arch/x86/util/Build | 3 -
tools/perf/arch/x86/util/unwind-libunwind.c | 115 ------------------
tools/perf/util/Build | 1 +
tools/perf/util/libunwind-arch/Build | 10 ++
.../perf/util/libunwind-arch/libunwind-arch.c | 32 +++++
.../perf/util/libunwind-arch/libunwind-arch.h | 16 +++
.../perf/util/libunwind-arch/libunwind-arm.c | 15 +++
.../util/libunwind-arch/libunwind-arm64.c | 14 +++
.../perf/util/libunwind-arch/libunwind-i386.c | 43 +++++++
.../util/libunwind-arch/libunwind-loongarch.c | 27 ++++
.../perf/util/libunwind-arch/libunwind-mips.c | 29 +++++
.../util/libunwind-arch/libunwind-ppc32.c | 31 +++++
.../util/libunwind-arch/libunwind-ppc64.c | 33 +++++
.../perf/util/libunwind-arch/libunwind-s390.c | 29 +++++
.../util/libunwind-arch/libunwind-x86_64.c | 52 ++++++++
tools/perf/util/libunwind/arm64.c | 5 -
tools/perf/util/libunwind/x86_32.c | 12 --
tools/perf/util/unwind-libunwind-local.c | 12 +-
tools/perf/util/unwind.h | 5 -
30 files changed, 338 insertions(+), 417 deletions(-)
delete mode 100644 tools/perf/arch/arm/util/unwind-libunwind.c
delete mode 100644 tools/perf/arch/arm64/util/unwind-libunwind.c
delete mode 100644 tools/perf/arch/loongarch/util/unwind-libunwind.c
delete mode 100644 tools/perf/arch/mips/Build
delete mode 100644 tools/perf/arch/mips/util/Build
delete mode 100644 tools/perf/arch/mips/util/unwind-libunwind.c
delete mode 100644 tools/perf/arch/powerpc/util/unwind-libunwind.c
delete mode 100644 tools/perf/arch/x86/util/unwind-libunwind.c
create mode 100644 tools/perf/util/libunwind-arch/Build
create mode 100644 tools/perf/util/libunwind-arch/libunwind-arch.c
create mode 100644 tools/perf/util/libunwind-arch/libunwind-arch.h
create mode 100644 tools/perf/util/libunwind-arch/libunwind-arm.c
create mode 100644 tools/perf/util/libunwind-arch/libunwind-arm64.c
create mode 100644 tools/perf/util/libunwind-arch/libunwind-i386.c
create mode 100644 tools/perf/util/libunwind-arch/libunwind-loongarch.c
create mode 100644 tools/perf/util/libunwind-arch/libunwind-mips.c
create mode 100644 tools/perf/util/libunwind-arch/libunwind-ppc32.c
create mode 100644 tools/perf/util/libunwind-arch/libunwind-ppc64.c
create mode 100644 tools/perf/util/libunwind-arch/libunwind-s390.c
create mode 100644 tools/perf/util/libunwind-arch/libunwind-x86_64.c
diff --git a/tools/perf/arch/arm/util/Build b/tools/perf/arch/arm/util/Build
index b94bf3c5279a..768ae5d16553 100644
--- a/tools/perf/arch/arm/util/Build
+++ b/tools/perf/arch/arm/util/Build
@@ -1,3 +1 @@
-perf-util-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o
-
perf-util-y += pmu.o auxtrace.o cs-etm.o
diff --git a/tools/perf/arch/arm/util/unwind-libunwind.c b/tools/perf/arch/arm/util/unwind-libunwind.c
deleted file mode 100644
index 438906bf0014..000000000000
--- a/tools/perf/arch/arm/util/unwind-libunwind.c
+++ /dev/null
@@ -1,50 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-
-#include <errno.h>
-#include <libunwind.h>
-#include "perf_regs.h"
-#include "../../../util/unwind.h"
-#include "../../../util/debug.h"
-
-int libunwind__arch_reg_id(int regnum)
-{
- switch (regnum) {
- case UNW_ARM_R0:
- return PERF_REG_ARM_R0;
- case UNW_ARM_R1:
- return PERF_REG_ARM_R1;
- case UNW_ARM_R2:
- return PERF_REG_ARM_R2;
- case UNW_ARM_R3:
- return PERF_REG_ARM_R3;
- case UNW_ARM_R4:
- return PERF_REG_ARM_R4;
- case UNW_ARM_R5:
- return PERF_REG_ARM_R5;
- case UNW_ARM_R6:
- return PERF_REG_ARM_R6;
- case UNW_ARM_R7:
- return PERF_REG_ARM_R7;
- case UNW_ARM_R8:
- return PERF_REG_ARM_R8;
- case UNW_ARM_R9:
- return PERF_REG_ARM_R9;
- case UNW_ARM_R10:
- return PERF_REG_ARM_R10;
- case UNW_ARM_R11:
- return PERF_REG_ARM_FP;
- case UNW_ARM_R12:
- return PERF_REG_ARM_IP;
- case UNW_ARM_R13:
- return PERF_REG_ARM_SP;
- case UNW_ARM_R14:
- return PERF_REG_ARM_LR;
- case UNW_ARM_R15:
- return PERF_REG_ARM_PC;
- default:
- pr_err("unwind: invalid reg id %d\n", regnum);
- return -EINVAL;
- }
-
- return -EINVAL;
-}
diff --git a/tools/perf/arch/arm64/util/Build b/tools/perf/arch/arm64/util/Build
index 4e06a08d281a..4b70c4788c80 100644
--- a/tools/perf/arch/arm64/util/Build
+++ b/tools/perf/arch/arm64/util/Build
@@ -1,4 +1,3 @@
-perf-util-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o
perf-util-y += ../../arm/util/auxtrace.o
perf-util-y += ../../arm/util/cs-etm.o
perf-util-y += ../../arm/util/pmu.o
diff --git a/tools/perf/arch/arm64/util/unwind-libunwind.c b/tools/perf/arch/arm64/util/unwind-libunwind.c
deleted file mode 100644
index 871af5992298..000000000000
--- a/tools/perf/arch/arm64/util/unwind-libunwind.c
+++ /dev/null
@@ -1,17 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-#include <errno.h>
-
-#ifndef REMOTE_UNWIND_LIBUNWIND
-#include <libunwind.h>
-#include "perf_regs.h"
-#include "../../../util/unwind.h"
-#endif
-#include "../../../util/debug.h"
-
-int LIBUNWIND__ARCH_REG_ID(int regnum)
-{
- if (regnum < 0 || regnum >= PERF_REG_ARM64_EXTENDED_MAX)
- return -EINVAL;
-
- return regnum;
-}
diff --git a/tools/perf/arch/loongarch/util/Build b/tools/perf/arch/loongarch/util/Build
index 8d91e78d31c9..2328fb9a30a3 100644
--- a/tools/perf/arch/loongarch/util/Build
+++ b/tools/perf/arch/loongarch/util/Build
@@ -1,3 +1 @@
perf-util-y += header.o
-
-perf-util-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o
diff --git a/tools/perf/arch/loongarch/util/unwind-libunwind.c b/tools/perf/arch/loongarch/util/unwind-libunwind.c
deleted file mode 100644
index f693167b86ef..000000000000
--- a/tools/perf/arch/loongarch/util/unwind-libunwind.c
+++ /dev/null
@@ -1,82 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-
-#include <errno.h>
-#include <libunwind.h>
-#include "perf_regs.h"
-#include "../../util/unwind.h"
-#include "util/debug.h"
-
-int libunwind__arch_reg_id(int regnum)
-{
- switch (regnum) {
- case UNW_LOONGARCH64_R1:
- return PERF_REG_LOONGARCH_R1;
- case UNW_LOONGARCH64_R2:
- return PERF_REG_LOONGARCH_R2;
- case UNW_LOONGARCH64_R3:
- return PERF_REG_LOONGARCH_R3;
- case UNW_LOONGARCH64_R4:
- return PERF_REG_LOONGARCH_R4;
- case UNW_LOONGARCH64_R5:
- return PERF_REG_LOONGARCH_R5;
- case UNW_LOONGARCH64_R6:
- return PERF_REG_LOONGARCH_R6;
- case UNW_LOONGARCH64_R7:
- return PERF_REG_LOONGARCH_R7;
- case UNW_LOONGARCH64_R8:
- return PERF_REG_LOONGARCH_R8;
- case UNW_LOONGARCH64_R9:
- return PERF_REG_LOONGARCH_R9;
- case UNW_LOONGARCH64_R10:
- return PERF_REG_LOONGARCH_R10;
- case UNW_LOONGARCH64_R11:
- return PERF_REG_LOONGARCH_R11;
- case UNW_LOONGARCH64_R12:
- return PERF_REG_LOONGARCH_R12;
- case UNW_LOONGARCH64_R13:
- return PERF_REG_LOONGARCH_R13;
- case UNW_LOONGARCH64_R14:
- return PERF_REG_LOONGARCH_R14;
- case UNW_LOONGARCH64_R15:
- return PERF_REG_LOONGARCH_R15;
- case UNW_LOONGARCH64_R16:
- return PERF_REG_LOONGARCH_R16;
- case UNW_LOONGARCH64_R17:
- return PERF_REG_LOONGARCH_R17;
- case UNW_LOONGARCH64_R18:
- return PERF_REG_LOONGARCH_R18;
- case UNW_LOONGARCH64_R19:
- return PERF_REG_LOONGARCH_R19;
- case UNW_LOONGARCH64_R20:
- return PERF_REG_LOONGARCH_R20;
- case UNW_LOONGARCH64_R21:
- return PERF_REG_LOONGARCH_R21;
- case UNW_LOONGARCH64_R22:
- return PERF_REG_LOONGARCH_R22;
- case UNW_LOONGARCH64_R23:
- return PERF_REG_LOONGARCH_R23;
- case UNW_LOONGARCH64_R24:
- return PERF_REG_LOONGARCH_R24;
- case UNW_LOONGARCH64_R25:
- return PERF_REG_LOONGARCH_R25;
- case UNW_LOONGARCH64_R26:
- return PERF_REG_LOONGARCH_R26;
- case UNW_LOONGARCH64_R27:
- return PERF_REG_LOONGARCH_R27;
- case UNW_LOONGARCH64_R28:
- return PERF_REG_LOONGARCH_R28;
- case UNW_LOONGARCH64_R29:
- return PERF_REG_LOONGARCH_R29;
- case UNW_LOONGARCH64_R30:
- return PERF_REG_LOONGARCH_R30;
- case UNW_LOONGARCH64_R31:
- return PERF_REG_LOONGARCH_R31;
- case UNW_LOONGARCH64_PC:
- return PERF_REG_LOONGARCH_PC;
- default:
- pr_err("unwind: invalid reg id %d\n", regnum);
- return -EINVAL;
- }
-
- return -EINVAL;
-}
diff --git a/tools/perf/arch/mips/Build b/tools/perf/arch/mips/Build
deleted file mode 100644
index e63eabc2c8f4..000000000000
--- a/tools/perf/arch/mips/Build
+++ /dev/null
@@ -1 +0,0 @@
-perf-util-y += util/
diff --git a/tools/perf/arch/mips/util/Build b/tools/perf/arch/mips/util/Build
deleted file mode 100644
index 818b808a8247..000000000000
--- a/tools/perf/arch/mips/util/Build
+++ /dev/null
@@ -1 +0,0 @@
-perf-util-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o
diff --git a/tools/perf/arch/mips/util/unwind-libunwind.c b/tools/perf/arch/mips/util/unwind-libunwind.c
deleted file mode 100644
index 0d8c99c29da6..000000000000
--- a/tools/perf/arch/mips/util/unwind-libunwind.c
+++ /dev/null
@@ -1,22 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-
-#include <errno.h>
-#include <libunwind.h>
-#include "perf_regs.h"
-#include "../../util/unwind.h"
-#include "util/debug.h"
-
-int libunwind__arch_reg_id(int regnum)
-{
- switch (regnum) {
- case UNW_MIPS_R1 ... UNW_MIPS_R25:
- return regnum - UNW_MIPS_R1 + PERF_REG_MIPS_R1;
- case UNW_MIPS_R28 ... UNW_MIPS_R31:
- return regnum - UNW_MIPS_R28 + PERF_REG_MIPS_R28;
- case UNW_MIPS_PC:
- return PERF_REG_MIPS_PC;
- default:
- pr_err("unwind: invalid reg id %d\n", regnum);
- return -EINVAL;
- }
-}
diff --git a/tools/perf/arch/powerpc/util/Build b/tools/perf/arch/powerpc/util/Build
index d66574cbb9a9..ae928050e07a 100644
--- a/tools/perf/arch/powerpc/util/Build
+++ b/tools/perf/arch/powerpc/util/Build
@@ -6,5 +6,4 @@ perf-util-y += evsel.o
perf-util-$(CONFIG_LIBDW) += skip-callchain-idx.o
-perf-util-$(CONFIG_LIBUNWIND) += unwind-libunwind.o
perf-util-y += auxtrace.o
diff --git a/tools/perf/arch/powerpc/util/unwind-libunwind.c b/tools/perf/arch/powerpc/util/unwind-libunwind.c
deleted file mode 100644
index 90a6beda20de..000000000000
--- a/tools/perf/arch/powerpc/util/unwind-libunwind.c
+++ /dev/null
@@ -1,92 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Copyright 2016 Chandan Kumar, IBM Corporation.
- */
-
-#include <errno.h>
-#include <libunwind.h>
-#include <asm/perf_regs.h>
-#include "../../util/unwind.h"
-#include "../../util/debug.h"
-
-int libunwind__arch_reg_id(int regnum)
-{
- switch (regnum) {
- case UNW_PPC64_R0:
- return PERF_REG_POWERPC_R0;
- case UNW_PPC64_R1:
- return PERF_REG_POWERPC_R1;
- case UNW_PPC64_R2:
- return PERF_REG_POWERPC_R2;
- case UNW_PPC64_R3:
- return PERF_REG_POWERPC_R3;
- case UNW_PPC64_R4:
- return PERF_REG_POWERPC_R4;
- case UNW_PPC64_R5:
- return PERF_REG_POWERPC_R5;
- case UNW_PPC64_R6:
- return PERF_REG_POWERPC_R6;
- case UNW_PPC64_R7:
- return PERF_REG_POWERPC_R7;
- case UNW_PPC64_R8:
- return PERF_REG_POWERPC_R8;
- case UNW_PPC64_R9:
- return PERF_REG_POWERPC_R9;
- case UNW_PPC64_R10:
- return PERF_REG_POWERPC_R10;
- case UNW_PPC64_R11:
- return PERF_REG_POWERPC_R11;
- case UNW_PPC64_R12:
- return PERF_REG_POWERPC_R12;
- case UNW_PPC64_R13:
- return PERF_REG_POWERPC_R13;
- case UNW_PPC64_R14:
- return PERF_REG_POWERPC_R14;
- case UNW_PPC64_R15:
- return PERF_REG_POWERPC_R15;
- case UNW_PPC64_R16:
- return PERF_REG_POWERPC_R16;
- case UNW_PPC64_R17:
- return PERF_REG_POWERPC_R17;
- case UNW_PPC64_R18:
- return PERF_REG_POWERPC_R18;
- case UNW_PPC64_R19:
- return PERF_REG_POWERPC_R19;
- case UNW_PPC64_R20:
- return PERF_REG_POWERPC_R20;
- case UNW_PPC64_R21:
- return PERF_REG_POWERPC_R21;
- case UNW_PPC64_R22:
- return PERF_REG_POWERPC_R22;
- case UNW_PPC64_R23:
- return PERF_REG_POWERPC_R23;
- case UNW_PPC64_R24:
- return PERF_REG_POWERPC_R24;
- case UNW_PPC64_R25:
- return PERF_REG_POWERPC_R25;
- case UNW_PPC64_R26:
- return PERF_REG_POWERPC_R26;
- case UNW_PPC64_R27:
- return PERF_REG_POWERPC_R27;
- case UNW_PPC64_R28:
- return PERF_REG_POWERPC_R28;
- case UNW_PPC64_R29:
- return PERF_REG_POWERPC_R29;
- case UNW_PPC64_R30:
- return PERF_REG_POWERPC_R30;
- case UNW_PPC64_R31:
- return PERF_REG_POWERPC_R31;
- case UNW_PPC64_LR:
- return PERF_REG_POWERPC_LINK;
- case UNW_PPC64_CTR:
- return PERF_REG_POWERPC_CTR;
- case UNW_PPC64_XER:
- return PERF_REG_POWERPC_XER;
- case UNW_PPC64_NIP:
- return PERF_REG_POWERPC_NIP;
- default:
- pr_err("unwind: invalid reg id %d\n", regnum);
- return -EINVAL;
- }
- return -EINVAL;
-}
diff --git a/tools/perf/arch/x86/util/Build b/tools/perf/arch/x86/util/Build
index b94c91984c66..7f89fffe4615 100644
--- a/tools/perf/arch/x86/util/Build
+++ b/tools/perf/arch/x86/util/Build
@@ -8,9 +8,6 @@ perf-util-y += evlist.o
perf-util-y += mem-events.o
perf-util-y += evsel.o
perf-util-y += iostat.o
-
-perf-util-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o
-
perf-util-y += auxtrace.o
perf-util-y += intel-pt.o
perf-util-y += intel-bts.o
diff --git a/tools/perf/arch/x86/util/unwind-libunwind.c b/tools/perf/arch/x86/util/unwind-libunwind.c
deleted file mode 100644
index 47357973b55b..000000000000
--- a/tools/perf/arch/x86/util/unwind-libunwind.c
+++ /dev/null
@@ -1,115 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-
-#include <errno.h>
-#include "../../util/debug.h"
-#ifndef REMOTE_UNWIND_LIBUNWIND
-#include <libunwind.h>
-#include "perf_regs.h"
-#include "../../util/unwind.h"
-#endif
-
-#ifdef HAVE_ARCH_X86_64_SUPPORT
-int LIBUNWIND__ARCH_REG_ID(int regnum)
-{
- int id;
-
- switch (regnum) {
- case UNW_X86_64_RAX:
- id = PERF_REG_X86_AX;
- break;
- case UNW_X86_64_RDX:
- id = PERF_REG_X86_DX;
- break;
- case UNW_X86_64_RCX:
- id = PERF_REG_X86_CX;
- break;
- case UNW_X86_64_RBX:
- id = PERF_REG_X86_BX;
- break;
- case UNW_X86_64_RSI:
- id = PERF_REG_X86_SI;
- break;
- case UNW_X86_64_RDI:
- id = PERF_REG_X86_DI;
- break;
- case UNW_X86_64_RBP:
- id = PERF_REG_X86_BP;
- break;
- case UNW_X86_64_RSP:
- id = PERF_REG_X86_SP;
- break;
- case UNW_X86_64_R8:
- id = PERF_REG_X86_R8;
- break;
- case UNW_X86_64_R9:
- id = PERF_REG_X86_R9;
- break;
- case UNW_X86_64_R10:
- id = PERF_REG_X86_R10;
- break;
- case UNW_X86_64_R11:
- id = PERF_REG_X86_R11;
- break;
- case UNW_X86_64_R12:
- id = PERF_REG_X86_R12;
- break;
- case UNW_X86_64_R13:
- id = PERF_REG_X86_R13;
- break;
- case UNW_X86_64_R14:
- id = PERF_REG_X86_R14;
- break;
- case UNW_X86_64_R15:
- id = PERF_REG_X86_R15;
- break;
- case UNW_X86_64_RIP:
- id = PERF_REG_X86_IP;
- break;
- default:
- pr_err("unwind: invalid reg id %d\n", regnum);
- return -EINVAL;
- }
-
- return id;
-}
-#else
-int LIBUNWIND__ARCH_REG_ID(int regnum)
-{
- int id;
-
- switch (regnum) {
- case UNW_X86_EAX:
- id = PERF_REG_X86_AX;
- break;
- case UNW_X86_EDX:
- id = PERF_REG_X86_DX;
- break;
- case UNW_X86_ECX:
- id = PERF_REG_X86_CX;
- break;
- case UNW_X86_EBX:
- id = PERF_REG_X86_BX;
- break;
- case UNW_X86_ESI:
- id = PERF_REG_X86_SI;
- break;
- case UNW_X86_EDI:
- id = PERF_REG_X86_DI;
- break;
- case UNW_X86_EBP:
- id = PERF_REG_X86_BP;
- break;
- case UNW_X86_ESP:
- id = PERF_REG_X86_SP;
- break;
- case UNW_X86_EIP:
- id = PERF_REG_X86_IP;
- break;
- default:
- pr_err("unwind: invalid reg id %d\n", regnum);
- return -EINVAL;
- }
-
- return id;
-}
-#endif /* HAVE_ARCH_X86_64_SUPPORT */
diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index bcccad7487a9..1964e13e3085 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -229,6 +229,7 @@ perf-util-$(CONFIG_LIBDW) += unwind-libdw.o
perf-util-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind-local.o
perf-util-$(CONFIG_LIBUNWIND) += unwind-libunwind.o
+perf-util-$(CONFIG_LIBUNWIND) += libunwind-arch/
perf-util-$(CONFIG_LIBUNWIND_X86) += libunwind/x86_32.o
perf-util-$(CONFIG_LIBUNWIND_AARCH64) += libunwind/arm64.o
diff --git a/tools/perf/util/libunwind-arch/Build b/tools/perf/util/libunwind-arch/Build
new file mode 100644
index 000000000000..87fd657a3248
--- /dev/null
+++ b/tools/perf/util/libunwind-arch/Build
@@ -0,0 +1,10 @@
+perf-util-$(CONFIG_LIBUNWIND) += libunwind-arch.o
+perf-util-$(CONFIG_LIBUNWIND) += libunwind-arm64.o
+perf-util-$(CONFIG_LIBUNWIND) += libunwind-arm.o
+perf-util-$(CONFIG_LIBUNWIND) += libunwind-loongarch.o
+perf-util-$(CONFIG_LIBUNWIND) += libunwind-mips.o
+perf-util-$(CONFIG_LIBUNWIND) += libunwind-ppc32.o
+perf-util-$(CONFIG_LIBUNWIND) += libunwind-ppc64.o
+perf-util-$(CONFIG_LIBUNWIND) += libunwind-s390.o
+perf-util-$(CONFIG_LIBUNWIND) += libunwind-i386.o
+perf-util-$(CONFIG_LIBUNWIND) += libunwind-x86_64.o
diff --git a/tools/perf/util/libunwind-arch/libunwind-arch.c b/tools/perf/util/libunwind-arch/libunwind-arch.c
new file mode 100644
index 000000000000..5439bf90d161
--- /dev/null
+++ b/tools/perf/util/libunwind-arch/libunwind-arch.c
@@ -0,0 +1,32 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "libunwind-arch.h"
+#include "../debug.h"
+#include <elf.h>
+#include <errno.h>
+
+int get_perf_regnum_for_unw_regnum(unsigned int e_machine, int unw_regnum)
+{
+ switch (e_machine) {
+ case EM_ARM:
+ return __get_perf_regnum_for_unw_regnum_arm(unw_regnum);
+ case EM_AARCH64:
+ return __get_perf_regnum_for_unw_regnum_arm64(unw_regnum);
+ case EM_LOONGARCH:
+ return __get_perf_regnum_for_unw_regnum_loongarch(unw_regnum);
+ case EM_MIPS:
+ return __get_perf_regnum_for_unw_regnum_mips(unw_regnum);
+ case EM_PPC:
+ return __get_perf_regnum_for_unw_regnum_ppc32(unw_regnum);
+ case EM_PPC64:
+ return __get_perf_regnum_for_unw_regnum_ppc64(unw_regnum);
+ case EM_S390:
+ return __get_perf_regnum_for_unw_regnum_s390(unw_regnum);
+ case EM_386:
+ return __get_perf_regnum_for_unw_regnum_i386(unw_regnum);
+ case EM_X86_64:
+ return __get_perf_regnum_for_unw_regnum_x86_64(unw_regnum);
+ default:
+ pr_err("ELF MACHINE %x is not supported.\n", e_machine);
+ return -EINVAL;
+ }
+}
diff --git a/tools/perf/util/libunwind-arch/libunwind-arch.h b/tools/perf/util/libunwind-arch/libunwind-arch.h
new file mode 100644
index 000000000000..e1009c6cb965
--- /dev/null
+++ b/tools/perf/util/libunwind-arch/libunwind-arch.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __LIBUNWIND_ARCH_H
+#define __LIBUNWIND_ARCH_H
+
+int __get_perf_regnum_for_unw_regnum_arm(int unw_regnum);
+int __get_perf_regnum_for_unw_regnum_arm64(int unw_regnum);
+int __get_perf_regnum_for_unw_regnum_loongarch(int unw_regnum);
+int __get_perf_regnum_for_unw_regnum_mips(int unw_regnum);
+int __get_perf_regnum_for_unw_regnum_ppc32(int unw_regnum);
+int __get_perf_regnum_for_unw_regnum_ppc64(int unw_regnum);
+int __get_perf_regnum_for_unw_regnum_s390(int unw_regnum);
+int __get_perf_regnum_for_unw_regnum_i386(int unw_regnum);
+int __get_perf_regnum_for_unw_regnum_x86_64(int unw_regnum);
+int get_perf_regnum_for_unw_regnum(unsigned int e_machine, int unw_regnum);
+
+#endif /* __LIBUNWIND_ARCH_H */
diff --git a/tools/perf/util/libunwind-arch/libunwind-arm.c b/tools/perf/util/libunwind-arch/libunwind-arm.c
new file mode 100644
index 000000000000..6740ee55b043
--- /dev/null
+++ b/tools/perf/util/libunwind-arch/libunwind-arm.c
@@ -0,0 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "libunwind-arch.h"
+#include "../debug.h"
+#include "../../../arch/arm/include/uapi/asm/perf_regs.h"
+#include <linux/compiler.h>
+#include <errno.h>
+
+int __get_perf_regnum_for_unw_regnum_arm(int unw_regnum)
+{
+ if (unw_regnum < 0 || unw_regnum >= PERF_REG_ARM_MAX) {
+ pr_err("unwind: invalid reg id %d\n", unw_regnum);
+ return -EINVAL;
+ }
+ return unw_regnum;
+}
diff --git a/tools/perf/util/libunwind-arch/libunwind-arm64.c b/tools/perf/util/libunwind-arch/libunwind-arm64.c
new file mode 100644
index 000000000000..53b1877dfa04
--- /dev/null
+++ b/tools/perf/util/libunwind-arch/libunwind-arm64.c
@@ -0,0 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "libunwind-arch.h"
+#include "../debug.h"
+#include "../../../arch/arm64/include/uapi/asm/perf_regs.h"
+#include <errno.h>
+
+int __get_perf_regnum_for_unw_regnum_arm64(int unw_regnum)
+{
+ if (unw_regnum < 0 || unw_regnum >= PERF_REG_ARM64_EXTENDED_MAX) {
+ pr_err("unwind: invalid reg id %d\n", unw_regnum);
+ return -EINVAL;
+ }
+ return unw_regnum;
+}
diff --git a/tools/perf/util/libunwind-arch/libunwind-i386.c b/tools/perf/util/libunwind-arch/libunwind-i386.c
new file mode 100644
index 000000000000..83bef77bddfd
--- /dev/null
+++ b/tools/perf/util/libunwind-arch/libunwind-i386.c
@@ -0,0 +1,43 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "libunwind-arch.h"
+#include "../debug.h"
+#include "../../../arch/x86/include/uapi/asm/perf_regs.h"
+#include <linux/compiler.h>
+#include <linux/kernel.h>
+#include <errno.h>
+
+#ifdef HAVE_LIBUNWIND_X86_SUPPORT
+#include <libunwind-x86.h>
+#endif
+
+int __get_perf_regnum_for_unw_regnum_i386(int unw_regnum __maybe_unused)
+{
+#ifndef HAVE_LIBUNWIND_X86_SUPPORT
+ return -EINVAL;
+#else
+ static const int perf_i386_regnums[] = {
+#define REGNUM(reg) [UNW_X86_E ## reg] = PERF_REG_X86_ ## reg
+ REGNUM(AX),
+ REGNUM(DX),
+ REGNUM(CX),
+ REGNUM(BX),
+ REGNUM(SI),
+ REGNUM(DI),
+ REGNUM(BP),
+ REGNUM(SP),
+ REGNUM(IP),
+#undef REGNUM
+ };
+
+ if (unw_regnum == UNW_X86_64_EAX)
+ return PERF_REG_X86_AX;
+
+ if (unw_regnum < 0 || unw_regnum > (int)ARRAY_SIZE(perf_i386_regnums) ||
+ perf_i386_regnums[unw_regnum] == 0) {
+ pr_err("unwind: invalid reg id %d\n", unw_regnum);
+ return -EINVAL;
+ }
+
+ return perf_i386_regnums[unw_regnum];
+#endif // HAVE_LIBUNWIND_X86_SUPPORT
+}
diff --git a/tools/perf/util/libunwind-arch/libunwind-loongarch.c b/tools/perf/util/libunwind-arch/libunwind-loongarch.c
new file mode 100644
index 000000000000..7009410989bc
--- /dev/null
+++ b/tools/perf/util/libunwind-arch/libunwind-loongarch.c
@@ -0,0 +1,27 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "libunwind-arch.h"
+#include "../debug.h"
+#include "../../../arch/loongarch/include/uapi/asm/perf_regs.h"
+#include <linux/compiler.h>
+#include <errno.h>
+
+#ifdef HAVE_LIBUNWIND_LOONGARCH64_SUPPORT
+#include <libunwind-loongarch64.h>
+#endif
+
+int __get_perf_regnum_for_unw_regnum_loongarch(int unw_regnum __maybe_unused)
+{
+#ifndef HAVE_LIBUNWIND_LOONGARCH64_SUPPORT
+ return -EINVAL;
+#else
+ switch (unw_regnum) {
+ case UNW_LOONGARCH64_R1 ... UNW_LOONGARCH64_31:
+ return unw_regnum - UNW_LOONGARCH64_R1 + PERF_REG_LOONGARCH_R1;
+ case UNW_LOONGARCH64_PC:
+ return PERF_REG_LOONGARCH_PC;
+ default:
+ pr_err("unwind: invalid reg id %d\n", unw_regnum);
+ return -EINVAL;
+ }
+#endif // HAVE_LIBUNWIND_LOONGARCH64_SUPPORT
+}
diff --git a/tools/perf/util/libunwind-arch/libunwind-mips.c b/tools/perf/util/libunwind-arch/libunwind-mips.c
new file mode 100644
index 000000000000..01a506c8079c
--- /dev/null
+++ b/tools/perf/util/libunwind-arch/libunwind-mips.c
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "libunwind-arch.h"
+#include "../debug.h"
+#include "../../../arch/mips/include/uapi/asm/perf_regs.h"
+#include <linux/compiler.h>
+#include <errno.h>
+
+#ifdef HAVE_LIBUNWIND_MIPS_SUPPORT
+#include <libunwind-mips.h>
+#endif
+
+int __get_perf_regnum_for_unw_regnum_mips(int unw_regnum __maybe_unused)
+{
+#ifndef HAVE_LIBUNWIND_MIPS_SUPPORT
+ return -EINVAL;
+#else
+ switch (unw_regnum) {
+ case UNW_MIPS_R1 ... UNW_MIPS_R25:
+ return unw_regnum - UNW_MIPS_R1 + PERF_REG_MIPS_R1;
+ case UNW_MIPS_R28 ... UNW_MIPS_R31:
+ return unw_regnum - UNW_MIPS_R28 + PERF_REG_MIPS_R28;
+ case UNW_MIPS_PC:
+ return PERF_REG_MIPS_PC;
+ default:
+ pr_err("unwind: invalid reg id %d\n", unw_regnum);
+ return -EINVAL;
+ }
+#endif // HAVE_LIBUNWIND_MIPS_SUPPORT
+}
diff --git a/tools/perf/util/libunwind-arch/libunwind-ppc32.c b/tools/perf/util/libunwind-arch/libunwind-ppc32.c
new file mode 100644
index 000000000000..edcb0ec95dd7
--- /dev/null
+++ b/tools/perf/util/libunwind-arch/libunwind-ppc32.c
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#include "libunwind-arch.h"
+#include "../debug.h"
+#include "../../../arch/powerpc/include/uapi/asm/perf_regs.h"
+#include <linux/compiler.h>
+#include <errno.h>
+
+#ifdef HAVE_LIBUNWIND_PPC32_SUPPORT
+#include <libunwind-ppc32.h>
+#endif
+
+int __get_perf_regnum_for_unw_regnum_ppc32(int unw_regnum __maybe_unused)
+{
+#ifndef HAVE_LIBUNWIND_PPC32_SUPPORT
+ return -EINVAL;
+#else
+ switch (unw_regnum) {
+ case UNW_PPC32_R0 ... UNW_PPC32_31:
+ return unw_regnum - UNW_PPC32_R0 + PERF_REG_POWERPC_R0;
+ case UNW_PPC32_LR:
+ return PERF_REG_POWERPC_LINK;
+ case UNW_PPC32_CTR:
+ return PERF_REG_POWERPC_CTR;
+ case UNW_PPC32_XER:
+ return PERF_REG_POWERPC_XER;
+ default:
+ pr_err("unwind: invalid reg id %d\n", unw_regnum);
+ return -EINVAL;
+ }
+#endif // HAVE_LIBUNWIND_PPC32_SUPPORT
+}
diff --git a/tools/perf/util/libunwind-arch/libunwind-ppc64.c b/tools/perf/util/libunwind-arch/libunwind-ppc64.c
new file mode 100644
index 000000000000..9f57a049600b
--- /dev/null
+++ b/tools/perf/util/libunwind-arch/libunwind-ppc64.c
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#include "libunwind-arch.h"
+#include "../debug.h"
+#include "../../../arch/powerpc/include/uapi/asm/perf_regs.h"
+#include <linux/compiler.h>
+#include <errno.h>
+
+#ifdef HAVE_LIBUNWIND_PPC64_SUPPORT
+#include <libunwind-ppc64.h>
+#endif
+
+int __get_perf_regnum_for_unw_regnum_ppc64(int unw_regnum __maybe_unused)
+{
+#ifndef HAVE_LIBUNWIND_PPC64_SUPPORT
+ return -EINVAL;
+#else
+ switch (unw_regnum) {
+ case UNW_PPC64_R0 ... UNW_PPC64_31:
+ return unw_regnum - UNW_PPC64_R0 + PERF_REG_POWERPC_R0;
+ case UNW_PPC64_LR:
+ return PERF_REG_POWERPC_LINK;
+ case UNW_PPC64_CTR:
+ return PERF_REG_POWERPC_CTR;
+ case UNW_PPC64_XER:
+ return PERF_REG_POWERPC_XER;
+ case UNW_PPC64_NIP:
+ return PERF_REG_POWERPC_NIP;
+ default:
+ pr_err("unwind: invalid reg id %d\n", unw_regnum);
+ return -EINVAL;
+ }
+#endif // HAVE_LIBUNWIND_PPC64_SUPPORT
+}
diff --git a/tools/perf/util/libunwind-arch/libunwind-s390.c b/tools/perf/util/libunwind-arch/libunwind-s390.c
new file mode 100644
index 000000000000..9fcc7885ca55
--- /dev/null
+++ b/tools/perf/util/libunwind-arch/libunwind-s390.c
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "libunwind-arch.h"
+#include "../debug.h"
+#include "../../../arch/s390/include/uapi/asm/perf_regs.h"
+#include <linux/compiler.h>
+#include <errno.h>
+
+#ifdef HAVE_LIBUNWIND_S390X_SUPPORT
+#include <libunwind-s390x.h>
+#endif
+
+int __get_perf_regnum_for_unw_regnum_s390(int unw_regnum __maybe_unused)
+{
+#ifndef HAVE_LIBUNWIND_S390X_SUPPORT
+ return -EINVAL;
+#else
+ switch (unw_regnum) {
+ case UNW_S390X_R0 ... UNW_S390_R15:
+ return unw_regnum - UNW_S390_R0 + PERF_REG_S390_R0;
+ case UNW_S390X_F0 ... UNW_S390_F15:
+ return unw_regnum - UNW_S390_F0 + PERF_REG_S390_FP0;
+ case UNW_S390X_IP:
+ return PERF_REG_S390_PC;
+ default:
+ pr_err("unwind: invalid reg id %d\n", unw_regnum);
+ return -EINVAL;
+ }
+#endif // HAVE_LIBUNWIND_S390X_SUPPORT
+}
diff --git a/tools/perf/util/libunwind-arch/libunwind-x86_64.c b/tools/perf/util/libunwind-arch/libunwind-x86_64.c
new file mode 100644
index 000000000000..6072e3597e61
--- /dev/null
+++ b/tools/perf/util/libunwind-arch/libunwind-x86_64.c
@@ -0,0 +1,52 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "libunwind-arch.h"
+#include "../debug.h"
+#include "../../../arch/x86/include/uapi/asm/perf_regs.h"
+#include <linux/compiler.h>
+#include <linux/kernel.h>
+#include <errno.h>
+
+#ifdef HAVE_LIBUNWIND_X86_64_SUPPORT
+#include <libunwind-x86_64.h>
+#endif
+
+int __get_perf_regnum_for_unw_regnum_x86_64(int unw_regnum __maybe_unused)
+{
+#ifndef HAVE_LIBUNWIND_X86_64_SUPPORT
+ return -EINVAL;
+#else
+ static const int perf_x86_64_regnums[] = {
+#define REGNUM(reg) [UNW_X86_64_R ## reg] = PERF_REG_X86_ ## reg
+ REGNUM(AX),
+ REGNUM(DX),
+ REGNUM(CX),
+ REGNUM(BX),
+ REGNUM(SI),
+ REGNUM(DI),
+ REGNUM(BP),
+ REGNUM(SP),
+ REGNUM(IP),
+#undef REGNUM
+#define REGNUM(reg) [UNW_X86_64_ ## reg] = PERF_REG_X86_ ## reg
+ REGNUM(R8),
+ REGNUM(R9),
+ REGNUM(R10),
+ REGNUM(R11),
+ REGNUM(R12),
+ REGNUM(R13),
+ REGNUM(R14),
+ REGNUM(R15),
+#undef REGNUM
+ };
+
+ if (unw_regnum == UNW_X86_64_RAX)
+ return PERF_REG_X86_AX;
+
+ if (unw_regnum < 0 || unw_regnum > (int)ARRAY_SIZE(perf_x86_64_regnums) ||
+ perf_x86_64_regnums[unw_regnum] == 0) {
+ pr_err("unwind: invalid reg id %d\n", unw_regnum);
+ return -EINVAL;
+ }
+ return perf_x86_64_regnums[unw_regnum];
+#endif // HAVE_LIBUNWIND_X86_64_SUPPORT
+}
diff --git a/tools/perf/util/libunwind/arm64.c b/tools/perf/util/libunwind/arm64.c
index 37ecef0c53b9..15670a964495 100644
--- a/tools/perf/util/libunwind/arm64.c
+++ b/tools/perf/util/libunwind/arm64.c
@@ -14,11 +14,6 @@
#define REMOTE_UNWIND_LIBUNWIND
-/* Define arch specific functions & regs for libunwind, should be
- * defined before including "unwind.h"
- */
-#define LIBUNWIND__ARCH_REG_ID(regnum) libunwind__arm64_reg_id(regnum)
-
#include "unwind.h"
#include "libunwind-aarch64.h"
#define perf_event_arm_regs perf_event_arm64_regs
diff --git a/tools/perf/util/libunwind/x86_32.c b/tools/perf/util/libunwind/x86_32.c
index 1697dece1b74..1e9fb8bfec44 100644
--- a/tools/perf/util/libunwind/x86_32.c
+++ b/tools/perf/util/libunwind/x86_32.c
@@ -14,20 +14,8 @@
#define REMOTE_UNWIND_LIBUNWIND
-/* Define arch specific functions & regs for libunwind, should be
- * defined before including "unwind.h"
- */
-#define LIBUNWIND__ARCH_REG_ID(regnum) libunwind__x86_reg_id(regnum)
-
#include "unwind.h"
#include "libunwind-x86.h"
-#include <../../../../arch/x86/include/uapi/asm/perf_regs.h>
-
-/* HAVE_ARCH_X86_64_SUPPORT is used in'arch/x86/util/unwind-libunwind.c'
- * for x86_32, we undef it to compile code for x86_32 only.
- */
-#undef HAVE_ARCH_X86_64_SUPPORT
-#include "../../arch/x86/util/unwind-libunwind.c"
/* Explicitly define NO_LIBUNWIND_DEBUG_FRAME, because non-ARM has no
* dwarf_find_debug_frame() function.
diff --git a/tools/perf/util/unwind-libunwind-local.c b/tools/perf/util/unwind-libunwind-local.c
index 5b39ce21e333..3ecdb468b859 100644
--- a/tools/perf/util/unwind-libunwind-local.c
+++ b/tools/perf/util/unwind-libunwind-local.c
@@ -40,6 +40,7 @@
#include "debug.h"
#include "asm/bug.h"
#include "dso.h"
+#include "libunwind-arch/libunwind-arch.h"
extern int
UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,
@@ -96,6 +97,7 @@ struct unwind_info {
struct perf_sample *sample;
struct machine *machine;
struct thread *thread;
+ uint16_t e_machine;
bool best_effort;
};
@@ -584,9 +586,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(thread__e_machine(ui->thread,
- ui->machine,
- /*e_flags=*/NULL)));
+ perf_arch_reg_sp(ui->e_machine));
if (ret)
return ret;
@@ -634,7 +634,7 @@ static int access_reg(unw_addr_space_t __maybe_unused as,
return 0;
}
- id = LIBUNWIND__ARCH_REG_ID(regnum);
+ id = get_perf_regnum_for_unw_regnum(ui->e_machine, regnum);
if (id < 0)
return -EINVAL;
@@ -735,7 +735,6 @@ 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)
{
- uint16_t e_machine = thread__e_machine(ui->thread, ui->machine, /*e_flags=*/NULL);
u64 val;
unw_word_t ips[max_stack];
unw_addr_space_t addr_space;
@@ -743,7 +742,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(e_machine));
+ perf_arch_reg_ip(ui->e_machine));
if (ret)
return ret;
@@ -806,6 +805,7 @@ static int _unwind__get_entries(unwind_entry_cb_t cb, void *arg,
.sample = data,
.thread = thread,
.machine = maps__machine(thread__maps(thread)),
+ .e_machine = thread__e_machine(thread, /*machine=*/NULL, /*e_flags=*/NULL),
.best_effort = best_effort
};
diff --git a/tools/perf/util/unwind.h b/tools/perf/util/unwind.h
index 9f7164c6d9aa..f13cb6390088 100644
--- a/tools/perf/util/unwind.h
+++ b/tools/perf/util/unwind.h
@@ -38,11 +38,6 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
bool best_effort);
/* libunwind specific */
#ifdef HAVE_LIBUNWIND_SUPPORT
-#ifndef LIBUNWIND__ARCH_REG_ID
-#define LIBUNWIND__ARCH_REG_ID(regnum) libunwind__arch_reg_id(regnum)
-#endif
-
-int LIBUNWIND__ARCH_REG_ID(int regnum);
int unwind__prepare_access(struct maps *maps, struct map *map, bool *initialized);
void unwind__flush_access(struct maps *maps);
void unwind__finish_access(struct maps *maps);
--
2.53.0.371.g1d285c8824-goog
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [RFC PATCH v1 5/7] perf unwind-libunwind: Move flush/finish access out of local
2026-02-24 14:29 [RFC PATCH v1 0/7] perf libunwind multiple remote support Ian Rogers
` (3 preceding siblings ...)
2026-02-24 14:29 ` [RFC PATCH v1 4/7] perf unwind-libunwind: Make libunwind register reading cross platform Ian Rogers
@ 2026-02-24 14:29 ` Ian Rogers
2026-02-24 14:29 ` [RFC PATCH v1 6/7] perf unwind-libunwind: Remove libunwind-local Ian Rogers
2026-02-24 14:29 ` [RFC PATCH v1 7/7] perf unwind-libunwind: Add RISC-V libunwind support Ian Rogers
6 siblings, 0 replies; 27+ messages in thread
From: Ian Rogers @ 2026-02-24 14:29 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Alexander Shishkin, Jiri Olsa, Ian Rogers,
Adrian Hunter, James Clark, John Garry, Will Deacon, Leo Yan,
Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
Dmitrii Dolgov, Blake Jones, Tomas Glozar, Yuzhuo Jing, Dapeng Mi,
Shimin Guo, Athira Rajeev, Chun-Tse Shao, Howard Chu,
linux-kernel, linux-perf-users, linux-arm-kernel, linux-riscv,
Thomas Richter, libunwind-devel
Flush and finish access are relatively simple calls into libunwind,
move them out struct unwind_libunwind_ops. So that the correct version
can be called, add an e_machine variable to maps. This size regression
will go away when the unwind_libunwind_ops no longer need stashing in
the maps. To set the e_machine up pass it into unwind__prepare_access,
which no longer needs to determine the unwind operations based on a
map dso because of this. This also means the maps copying code can
call unwind__prepare_access once for the e_machine rather than once
per map.
Signed-off-by: Ian Rogers <irogers@google.com>
---
.../perf/util/libunwind-arch/libunwind-arch.c | 82 +++++++++++++++++++
.../perf/util/libunwind-arch/libunwind-arch.h | 24 ++++++
.../perf/util/libunwind-arch/libunwind-arm.c | 19 +++++
.../util/libunwind-arch/libunwind-arm64.c | 20 +++++
.../perf/util/libunwind-arch/libunwind-i386.c | 15 ++++
.../util/libunwind-arch/libunwind-loongarch.c | 15 ++++
.../perf/util/libunwind-arch/libunwind-mips.c | 15 ++++
.../util/libunwind-arch/libunwind-ppc32.c | 15 ++++
.../util/libunwind-arch/libunwind-ppc64.c | 15 ++++
.../perf/util/libunwind-arch/libunwind-s390.c | 15 ++++
.../util/libunwind-arch/libunwind-x86_64.c | 15 ++++
tools/perf/util/maps.c | 31 ++++---
tools/perf/util/maps.h | 2 +
tools/perf/util/thread.c | 29 +------
tools/perf/util/unwind-libunwind-local.c | 12 ---
tools/perf/util/unwind-libunwind.c | 61 +++++---------
tools/perf/util/unwind.h | 8 +-
17 files changed, 299 insertions(+), 94 deletions(-)
diff --git a/tools/perf/util/libunwind-arch/libunwind-arch.c b/tools/perf/util/libunwind-arch/libunwind-arch.c
index 5439bf90d161..9692e6c81492 100644
--- a/tools/perf/util/libunwind-arch/libunwind-arch.c
+++ b/tools/perf/util/libunwind-arch/libunwind-arch.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
#include "libunwind-arch.h"
#include "../debug.h"
+#include "../maps.h"
#include <elf.h>
#include <errno.h>
@@ -30,3 +31,84 @@ int get_perf_regnum_for_unw_regnum(unsigned int e_machine, int unw_regnum)
return -EINVAL;
}
}
+
+
+void libunwind_arch__flush_access(struct maps *maps)
+{
+ unsigned int e_machine = maps__e_machine(maps);
+
+ switch (e_machine) {
+ case EM_NONE:
+ break; // No libunwind info on the maps.
+ case EM_ARM:
+ __libunwind_arch__flush_access_arm(maps);
+ break;
+ case EM_AARCH64:
+ __libunwind_arch__flush_access_arm64(maps);
+ break;
+ case EM_LOONGARCH:
+ __libunwind_arch__flush_access_loongarch(maps);
+ break;
+ case EM_MIPS:
+ __libunwind_arch__flush_access_mips(maps);
+ break;
+ case EM_PPC:
+ __libunwind_arch__flush_access_ppc32(maps);
+ break;
+ case EM_PPC64:
+ __libunwind_arch__flush_access_ppc64(maps);
+ break;
+ case EM_S390:
+ __libunwind_arch__flush_access_s390(maps);
+ break;
+ case EM_386:
+ __libunwind_arch__flush_access_i386(maps);
+ break;
+ case EM_X86_64:
+ __libunwind_arch__flush_access_x86_64(maps);
+ break;
+ default:
+ pr_err("ELF MACHINE %x is not supported.\n", e_machine);
+ break;
+ }
+}
+
+void libunwind_arch__finish_access(struct maps *maps)
+{
+ unsigned int e_machine = maps__e_machine(maps);
+
+ switch (e_machine) {
+ case EM_NONE:
+ break; // No libunwind info on the maps.
+ case EM_ARM:
+ __libunwind_arch__finish_access_arm(maps);
+ break;
+ case EM_AARCH64:
+ __libunwind_arch__finish_access_arm64(maps);
+ break;
+ case EM_LOONGARCH:
+ __libunwind_arch__finish_access_loongarch(maps);
+ break;
+ case EM_MIPS:
+ __libunwind_arch__finish_access_mips(maps);
+ break;
+ case EM_PPC:
+ __libunwind_arch__finish_access_ppc32(maps);
+ break;
+ case EM_PPC64:
+ __libunwind_arch__finish_access_ppc64(maps);
+ break;
+ case EM_S390:
+ __libunwind_arch__finish_access_s390(maps);
+ break;
+ case EM_386:
+ __libunwind_arch__finish_access_i386(maps);
+ break;
+ case EM_X86_64:
+ __libunwind_arch__finish_access_x86_64(maps);
+ break;
+ default:
+ pr_err("ELF MACHINE %x is not supported.\n", e_machine);
+ break;
+ }
+}
diff --git a/tools/perf/util/libunwind-arch/libunwind-arch.h b/tools/perf/util/libunwind-arch/libunwind-arch.h
index e1009c6cb965..c00277a5e914 100644
--- a/tools/perf/util/libunwind-arch/libunwind-arch.h
+++ b/tools/perf/util/libunwind-arch/libunwind-arch.h
@@ -2,6 +2,8 @@
#ifndef __LIBUNWIND_ARCH_H
#define __LIBUNWIND_ARCH_H
+struct maps;
+
int __get_perf_regnum_for_unw_regnum_arm(int unw_regnum);
int __get_perf_regnum_for_unw_regnum_arm64(int unw_regnum);
int __get_perf_regnum_for_unw_regnum_loongarch(int unw_regnum);
@@ -13,4 +15,26 @@ int __get_perf_regnum_for_unw_regnum_i386(int unw_regnum);
int __get_perf_regnum_for_unw_regnum_x86_64(int unw_regnum);
int get_perf_regnum_for_unw_regnum(unsigned int e_machine, int unw_regnum);
+void __libunwind_arch__flush_access_arm(struct maps *maps);
+void __libunwind_arch__flush_access_arm64(struct maps *maps);
+void __libunwind_arch__flush_access_loongarch(struct maps *maps);
+void __libunwind_arch__flush_access_mips(struct maps *maps);
+void __libunwind_arch__flush_access_ppc32(struct maps *maps);
+void __libunwind_arch__flush_access_ppc64(struct maps *maps);
+void __libunwind_arch__flush_access_s390(struct maps *maps);
+void __libunwind_arch__flush_access_i386(struct maps *maps);
+void __libunwind_arch__flush_access_x86_64(struct maps *maps);
+void libunwind_arch__flush_access(struct maps *maps);
+
+void __libunwind_arch__finish_access_arm(struct maps *maps);
+void __libunwind_arch__finish_access_arm64(struct maps *maps);
+void __libunwind_arch__finish_access_loongarch(struct maps *maps);
+void __libunwind_arch__finish_access_mips(struct maps *maps);
+void __libunwind_arch__finish_access_ppc32(struct maps *maps);
+void __libunwind_arch__finish_access_ppc64(struct maps *maps);
+void __libunwind_arch__finish_access_s390(struct maps *maps);
+void __libunwind_arch__finish_access_i386(struct maps *maps);
+void __libunwind_arch__finish_access_x86_64(struct maps *maps);
+void libunwind_arch__finish_access(struct maps *maps);
+
#endif /* __LIBUNWIND_ARCH_H */
diff --git a/tools/perf/util/libunwind-arch/libunwind-arm.c b/tools/perf/util/libunwind-arch/libunwind-arm.c
index 6740ee55b043..bbaf01406c52 100644
--- a/tools/perf/util/libunwind-arch/libunwind-arm.c
+++ b/tools/perf/util/libunwind-arch/libunwind-arm.c
@@ -1,10 +1,15 @@
// SPDX-License-Identifier: GPL-2.0
#include "libunwind-arch.h"
#include "../debug.h"
+#include "../maps.h"
#include "../../../arch/arm/include/uapi/asm/perf_regs.h"
#include <linux/compiler.h>
#include <errno.h>
+#ifdef HAVE_LIBUNWIND_ARM_SUPPORT
+#include <libunwind-arm.h>
+#endif
+
int __get_perf_regnum_for_unw_regnum_arm(int unw_regnum)
{
if (unw_regnum < 0 || unw_regnum >= PERF_REG_ARM_MAX) {
@@ -13,3 +18,17 @@ int __get_perf_regnum_for_unw_regnum_arm(int unw_regnum)
}
return unw_regnum;
}
+
+void __libunwind_arch__flush_access_arm(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_ARM_SUPPORT
+ unw_flush_cache(maps__addr_space(maps), 0, 0);
+#endif
+}
+
+void __libunwind_arch__finish_access_arm(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_ARM_SUPPORT
+ unw_destroy_addr_space(maps__addr_space(maps));
+#endif
+}
diff --git a/tools/perf/util/libunwind-arch/libunwind-arm64.c b/tools/perf/util/libunwind-arch/libunwind-arm64.c
index 53b1877dfa04..8ba510089736 100644
--- a/tools/perf/util/libunwind-arch/libunwind-arm64.c
+++ b/tools/perf/util/libunwind-arch/libunwind-arm64.c
@@ -1,9 +1,15 @@
// SPDX-License-Identifier: GPL-2.0
#include "libunwind-arch.h"
#include "../debug.h"
+#include "../maps.h"
#include "../../../arch/arm64/include/uapi/asm/perf_regs.h"
+#include <linux/compiler.h>
#include <errno.h>
+#ifdef HAVE_LIBUNWIND_AARCH64_SUPPORT
+#include <libunwind-aarch64.h>
+#endif
+
int __get_perf_regnum_for_unw_regnum_arm64(int unw_regnum)
{
if (unw_regnum < 0 || unw_regnum >= PERF_REG_ARM64_EXTENDED_MAX) {
@@ -12,3 +18,17 @@ int __get_perf_regnum_for_unw_regnum_arm64(int unw_regnum)
}
return unw_regnum;
}
+
+void __libunwind_arch__flush_access_arm64(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_AARCH64_SUPPORT
+ unw_flush_cache(maps__addr_space(maps), 0, 0);
+#endif
+}
+
+void __libunwind_arch__finish_access_arm64(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_AARCH64_SUPPORT
+ unw_destroy_addr_space(maps__addr_space(maps));
+#endif
+}
diff --git a/tools/perf/util/libunwind-arch/libunwind-i386.c b/tools/perf/util/libunwind-arch/libunwind-i386.c
index 83bef77bddfd..a0dcaa10578e 100644
--- a/tools/perf/util/libunwind-arch/libunwind-i386.c
+++ b/tools/perf/util/libunwind-arch/libunwind-i386.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
#include "libunwind-arch.h"
#include "../debug.h"
+#include "../maps.h"
#include "../../../arch/x86/include/uapi/asm/perf_regs.h"
#include <linux/compiler.h>
#include <linux/kernel.h>
@@ -41,3 +42,17 @@ int __get_perf_regnum_for_unw_regnum_i386(int unw_regnum __maybe_unused)
return perf_i386_regnums[unw_regnum];
#endif // HAVE_LIBUNWIND_X86_SUPPORT
}
+
+void __libunwind_arch__flush_access_i386(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_X86_SUPPORT
+ unw_flush_cache(maps__addr_space(maps), 0, 0);
+#endif
+}
+
+void __libunwind_arch__finish_access_i386(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_X86_SUPPORT
+ unw_destroy_addr_space(maps__addr_space(maps));
+#endif
+}
diff --git a/tools/perf/util/libunwind-arch/libunwind-loongarch.c b/tools/perf/util/libunwind-arch/libunwind-loongarch.c
index 7009410989bc..837aa11e2b9f 100644
--- a/tools/perf/util/libunwind-arch/libunwind-loongarch.c
+++ b/tools/perf/util/libunwind-arch/libunwind-loongarch.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
#include "libunwind-arch.h"
#include "../debug.h"
+#include "../maps.h"
#include "../../../arch/loongarch/include/uapi/asm/perf_regs.h"
#include <linux/compiler.h>
#include <errno.h>
@@ -25,3 +26,17 @@ int __get_perf_regnum_for_unw_regnum_loongarch(int unw_regnum __maybe_unused)
}
#endif // HAVE_LIBUNWIND_LOONGARCH64_SUPPORT
}
+
+void __libunwind_arch__flush_access_loongarch(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_LOONGARCH64_SUPPORT
+ unw_flush_cache(maps__addr_space(maps), 0, 0);
+#endif
+}
+
+void __libunwind_arch__finish_access_loongarch(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_LOONGARCH64_SUPPORT
+ unw_destroy_addr_space(maps__addr_space(maps));
+#endif
+}
diff --git a/tools/perf/util/libunwind-arch/libunwind-mips.c b/tools/perf/util/libunwind-arch/libunwind-mips.c
index 01a506c8079c..1fa81742ff4a 100644
--- a/tools/perf/util/libunwind-arch/libunwind-mips.c
+++ b/tools/perf/util/libunwind-arch/libunwind-mips.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
#include "libunwind-arch.h"
#include "../debug.h"
+#include "../maps.h"
#include "../../../arch/mips/include/uapi/asm/perf_regs.h"
#include <linux/compiler.h>
#include <errno.h>
@@ -27,3 +28,17 @@ int __get_perf_regnum_for_unw_regnum_mips(int unw_regnum __maybe_unused)
}
#endif // HAVE_LIBUNWIND_MIPS_SUPPORT
}
+
+void __libunwind_arch__flush_access_mips(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_MIPS_SUPPORT
+ unw_flush_cache(maps__addr_space(maps), 0, 0);
+#endif
+}
+
+void __libunwind_arch__finish_access_mips(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_MIPS_SUPPORT
+ unw_destroy_addr_space(maps__addr_space(maps));
+#endif
+}
diff --git a/tools/perf/util/libunwind-arch/libunwind-ppc32.c b/tools/perf/util/libunwind-arch/libunwind-ppc32.c
index edcb0ec95dd7..fa8471c74bf3 100644
--- a/tools/perf/util/libunwind-arch/libunwind-ppc32.c
+++ b/tools/perf/util/libunwind-arch/libunwind-ppc32.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "libunwind-arch.h"
#include "../debug.h"
+#include "../maps.h"
#include "../../../arch/powerpc/include/uapi/asm/perf_regs.h"
#include <linux/compiler.h>
#include <errno.h>
@@ -29,3 +30,17 @@ int __get_perf_regnum_for_unw_regnum_ppc32(int unw_regnum __maybe_unused)
}
#endif // HAVE_LIBUNWIND_PPC32_SUPPORT
}
+
+void __libunwind_arch__flush_access_ppc32(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_PPC32_SUPPORT
+ unw_flush_cache(maps__addr_space(maps), 0, 0);
+#endif
+}
+
+void __libunwind_arch__finish_access_ppc32(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_PPC32_SUPPORT
+ unw_destroy_addr_space(maps__addr_space(maps));
+#endif
+}
diff --git a/tools/perf/util/libunwind-arch/libunwind-ppc64.c b/tools/perf/util/libunwind-arch/libunwind-ppc64.c
index 9f57a049600b..2f746e347336 100644
--- a/tools/perf/util/libunwind-arch/libunwind-ppc64.c
+++ b/tools/perf/util/libunwind-arch/libunwind-ppc64.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "libunwind-arch.h"
#include "../debug.h"
+#include "../maps.h"
#include "../../../arch/powerpc/include/uapi/asm/perf_regs.h"
#include <linux/compiler.h>
#include <errno.h>
@@ -31,3 +32,17 @@ int __get_perf_regnum_for_unw_regnum_ppc64(int unw_regnum __maybe_unused)
}
#endif // HAVE_LIBUNWIND_PPC64_SUPPORT
}
+
+void __libunwind_arch__flush_access_ppc64(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_PPC64_SUPPORT
+ unw_flush_cache(maps__addr_space(maps), 0, 0);
+#endif
+}
+
+void __libunwind_arch__finish_access_ppc64(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_PPC64_SUPPORT
+ unw_destroy_addr_space(maps__addr_space(maps));
+#endif
+}
diff --git a/tools/perf/util/libunwind-arch/libunwind-s390.c b/tools/perf/util/libunwind-arch/libunwind-s390.c
index 9fcc7885ca55..9f68d15438b2 100644
--- a/tools/perf/util/libunwind-arch/libunwind-s390.c
+++ b/tools/perf/util/libunwind-arch/libunwind-s390.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
#include "libunwind-arch.h"
#include "../debug.h"
+#include "../maps.h"
#include "../../../arch/s390/include/uapi/asm/perf_regs.h"
#include <linux/compiler.h>
#include <errno.h>
@@ -27,3 +28,17 @@ int __get_perf_regnum_for_unw_regnum_s390(int unw_regnum __maybe_unused)
}
#endif // HAVE_LIBUNWIND_S390X_SUPPORT
}
+
+void __libunwind_arch__flush_access_s390(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_S390X_SUPPORT
+ unw_flush_cache(maps__addr_space(maps), 0, 0);
+#endif
+}
+
+void __libunwind_arch__finish_access_s390(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_S390X_SUPPORT
+ unw_destroy_addr_space(maps__addr_space(maps));
+#endif
+}
diff --git a/tools/perf/util/libunwind-arch/libunwind-x86_64.c b/tools/perf/util/libunwind-arch/libunwind-x86_64.c
index 6072e3597e61..25e326bd3e15 100644
--- a/tools/perf/util/libunwind-arch/libunwind-x86_64.c
+++ b/tools/perf/util/libunwind-arch/libunwind-x86_64.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
#include "libunwind-arch.h"
#include "../debug.h"
+#include "../maps.h"
#include "../../../arch/x86/include/uapi/asm/perf_regs.h"
#include <linux/compiler.h>
#include <linux/kernel.h>
@@ -50,3 +51,17 @@ int __get_perf_regnum_for_unw_regnum_x86_64(int unw_regnum __maybe_unused)
return perf_x86_64_regnums[unw_regnum];
#endif // HAVE_LIBUNWIND_X86_64_SUPPORT
}
+
+void __libunwind_arch__flush_access_x86_64(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_X86_64_SUPPORT
+ unw_flush_cache(maps__addr_space(maps), 0, 0);
+#endif
+}
+
+void __libunwind_arch__finish_access_x86_64(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_X86_64_SUPPORT
+ unw_destroy_addr_space(maps__addr_space(maps));
+#endif
+}
diff --git a/tools/perf/util/maps.c b/tools/perf/util/maps.c
index 4092211cff62..8c7b2a1e7642 100644
--- a/tools/perf/util/maps.c
+++ b/tools/perf/util/maps.c
@@ -40,6 +40,7 @@ DECLARE_RC_STRUCT(maps) {
#ifdef HAVE_LIBUNWIND_SUPPORT
void *addr_space;
const struct unwind_libunwind_ops *unwind_libunwind_ops;
+ uint16_t e_machine;
#endif
#ifdef HAVE_LIBDW_SUPPORT
void *libdw_addr_space_dwfl;
@@ -206,6 +207,16 @@ void maps__set_unwind_libunwind_ops(struct maps *maps, const struct unwind_libun
{
RC_CHK_ACCESS(maps)->unwind_libunwind_ops = ops;
}
+
+uint16_t maps__e_machine(const struct maps *maps)
+{
+ return RC_CHK_ACCESS(maps)->e_machine;
+}
+
+void maps__set_e_machine(struct maps *maps, uint16_t e_machine)
+{
+ RC_CHK_ACCESS(maps)->e_machine = e_machine;
+}
#endif
#ifdef HAVE_LIBDW_SUPPORT
void *maps__libdw_addr_space_dwfl(const struct maps *maps)
@@ -1038,6 +1049,9 @@ int maps__copy_from(struct maps *dest, struct maps *parent)
down_write(maps__lock(dest));
down_read(maps__lock(parent));
+#ifdef HAVE_LIBUNWIND_SUPPORT
+ unwind__prepare_access(dest, maps__e_machine(parent));
+#endif
parent_maps_by_address = maps__maps_by_address(parent);
n = maps__nr_maps(parent);
if (maps__nr_maps(dest) == 0) {
@@ -1067,14 +1081,11 @@ int maps__copy_from(struct maps *dest, struct maps *parent)
if (!new)
err = -ENOMEM;
else {
- err = unwind__prepare_access(dest, new, NULL);
- if (!err) {
- dest_maps_by_address[i] = new;
- map__set_kmap_maps(new, dest);
- if (dest_maps_by_name)
- dest_maps_by_name[i] = map__get(new);
- RC_CHK_ACCESS(dest)->nr_maps = i + 1;
- }
+ dest_maps_by_address[i] = new;
+ map__set_kmap_maps(new, dest);
+ if (dest_maps_by_name)
+ dest_maps_by_name[i] = map__get(new);
+ RC_CHK_ACCESS(dest)->nr_maps = i + 1;
}
if (err)
map__put(new);
@@ -1099,9 +1110,7 @@ int maps__copy_from(struct maps *dest, struct maps *parent)
if (!new)
err = -ENOMEM;
else {
- err = unwind__prepare_access(dest, new, NULL);
- if (!err)
- err = __maps__insert(dest, new);
+ err = __maps__insert(dest, new);
}
map__put(new);
}
diff --git a/tools/perf/util/maps.h b/tools/perf/util/maps.h
index 20c52084ba9e..6469f62c41a8 100644
--- a/tools/perf/util/maps.h
+++ b/tools/perf/util/maps.h
@@ -51,6 +51,8 @@ void *maps__addr_space(const struct maps *maps);
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);
+uint16_t maps__e_machine(const struct maps *maps);
+void maps__set_e_machine(struct maps *maps, uint16_t e_machine);
#endif
#ifdef HAVE_LIBDW_SUPPORT
void *maps__libdw_addr_space_dwfl(const struct maps *maps);
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c
index 22be77225bb0..c5df11ad329c 100644
--- a/tools/perf/util/thread.c
+++ b/tools/perf/util/thread.c
@@ -358,41 +358,20 @@ size_t thread__fprintf(struct thread *thread, FILE *fp)
int thread__insert_map(struct thread *thread, struct map *map)
{
int ret;
+ uint16_t e_machine = thread__e_machine(thread, /*machine=*/NULL, /*e_flags=*/NULL);
- ret = unwind__prepare_access(thread__maps(thread), map, NULL);
+ ret = unwind__prepare_access(thread__maps(thread), e_machine);
if (ret)
return ret;
return maps__fixup_overlap_and_insert(thread__maps(thread), map);
}
-struct thread__prepare_access_maps_cb_args {
- int err;
- struct maps *maps;
-};
-
-static int thread__prepare_access_maps_cb(struct map *map, void *data)
-{
- bool initialized = false;
- struct thread__prepare_access_maps_cb_args *args = data;
-
- args->err = unwind__prepare_access(args->maps, map, &initialized);
-
- return (args->err || initialized) ? 1 : 0;
-}
-
static int thread__prepare_access(struct thread *thread)
{
- struct thread__prepare_access_maps_cb_args args = {
- .err = 0,
- };
-
- if (dwarf_callchain_users) {
- args.maps = thread__maps(thread);
- maps__for_each_map(thread__maps(thread), thread__prepare_access_maps_cb, &args);
- }
+ uint16_t e_machine = thread__e_machine(thread, /*machine=*/NULL, /*e_flags=*/NULL);
- return args.err;
+ return unwind__prepare_access(thread__maps(thread), e_machine);
}
static int thread__clone_maps(struct thread *thread, struct thread *parent, bool do_maps_clone)
diff --git a/tools/perf/util/unwind-libunwind-local.c b/tools/perf/util/unwind-libunwind-local.c
index 3ecdb468b859..8f0128ba05a7 100644
--- a/tools/perf/util/unwind-libunwind-local.c
+++ b/tools/perf/util/unwind-libunwind-local.c
@@ -722,16 +722,6 @@ static int _unwind__prepare_access(struct maps *maps)
return 0;
}
-static void _unwind__flush_access(struct maps *maps)
-{
- unw_flush_cache(maps__addr_space(maps), 0, 0);
-}
-
-static void _unwind__finish_access(struct maps *maps)
-{
- unw_destroy_addr_space(maps__addr_space(maps));
-}
-
static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb,
void *arg, int max_stack)
{
@@ -821,8 +811,6 @@ static int _unwind__get_entries(unwind_entry_cb_t cb, void *arg,
static struct unwind_libunwind_ops
_unwind_libunwind_ops = {
.prepare_access = _unwind__prepare_access,
- .flush_access = _unwind__flush_access,
- .finish_access = _unwind__finish_access,
.get_entries = _unwind__get_entries,
};
diff --git a/tools/perf/util/unwind-libunwind.c b/tools/perf/util/unwind-libunwind.c
index cb8be6acfb6f..816891ecfa5e 100644
--- a/tools/perf/util/unwind-libunwind.c
+++ b/tools/perf/util/unwind-libunwind.c
@@ -7,76 +7,55 @@
#include "debug.h"
#include "env.h"
#include "callchain.h"
+#include "libunwind-arch/libunwind-arch.h"
+#include <dwarf-regs.h>
+#include <elf.h>
struct unwind_libunwind_ops __weak *local_unwind_libunwind_ops;
struct unwind_libunwind_ops __weak *x86_32_unwind_libunwind_ops;
struct unwind_libunwind_ops __weak *arm64_unwind_libunwind_ops;
-int unwind__prepare_access(struct maps *maps, struct map *map, bool *initialized)
+int unwind__prepare_access(struct maps *maps, uint16_t e_machine)
{
- const char *arch;
- enum dso_type dso_type;
struct unwind_libunwind_ops *ops = local_unwind_libunwind_ops;
- struct dso *dso = map__dso(map);
- struct machine *machine;
- int err;
if (!dwarf_callchain_users)
return 0;
if (maps__addr_space(maps)) {
- pr_debug("unwind: thread map already set, dso=%s\n", dso__name(dso));
- if (initialized)
- *initialized = true;
+ pr_debug3("unwind: thread map already set\n");
return 0;
}
- machine = maps__machine(maps);
- /* env->arch is NULL for live-mode (i.e. perf top) */
- if (!machine->env || !machine->env->arch)
- goto out_register;
-
- dso_type = dso__type(dso, machine);
- if (dso_type == DSO__TYPE_UNKNOWN)
- return 0;
-
- arch = perf_env__arch(machine->env);
-
- if (!strcmp(arch, "x86")) {
- if (dso_type != DSO__TYPE_64BIT)
+ if (e_machine != EM_HOST) {
+ /* If not live/local mode. */
+ switch (e_machine) {
+ case EM_386:
ops = x86_32_unwind_libunwind_ops;
- } else if (!strcmp(arch, "arm64") || !strcmp(arch, "arm")) {
- if (dso_type == DSO__TYPE_64BIT)
+ break;
+ case EM_AARCH64:
ops = arm64_unwind_libunwind_ops;
- }
-
- if (!ops) {
- pr_warning_once("unwind: target platform=%s is not supported\n", arch);
+ break;
+ default:
+ pr_warning_once("unwind: ELF machine type %d is not supported\n",
+ e_machine);
return 0;
+ }
}
-out_register:
maps__set_unwind_libunwind_ops(maps, ops);
+ maps__set_e_machine(maps, e_machine);
- err = maps__unwind_libunwind_ops(maps)->prepare_access(maps);
- if (initialized)
- *initialized = err ? false : true;
- return err;
+ return maps__unwind_libunwind_ops(maps)->prepare_access(maps);
}
void unwind__flush_access(struct maps *maps)
{
- const struct unwind_libunwind_ops *ops = maps__unwind_libunwind_ops(maps);
-
- if (ops)
- ops->flush_access(maps);
+ libunwind_arch__flush_access(maps);
}
void unwind__finish_access(struct maps *maps)
{
- const struct unwind_libunwind_ops *ops = maps__unwind_libunwind_ops(maps);
-
- if (ops)
- ops->finish_access(maps);
+ libunwind_arch__finish_access(maps);
}
int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
diff --git a/tools/perf/util/unwind.h b/tools/perf/util/unwind.h
index f13cb6390088..d3d0f3650f83 100644
--- a/tools/perf/util/unwind.h
+++ b/tools/perf/util/unwind.h
@@ -2,6 +2,7 @@
#ifndef __UNWIND_H
#define __UNWIND_H
+#include <stdint.h>
#include <linux/compiler.h>
#include <linux/types.h>
#include "util/map_symbol.h"
@@ -19,8 +20,6 @@ typedef int (*unwind_entry_cb_t)(struct unwind_entry *entry, void *arg);
struct unwind_libunwind_ops {
int (*prepare_access)(struct maps *maps);
- void (*flush_access)(struct maps *maps);
- void (*finish_access)(struct maps *maps);
int (*get_entries)(unwind_entry_cb_t cb, void *arg,
struct thread *thread,
struct perf_sample *data, int max_stack, bool best_effort);
@@ -38,13 +37,12 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
bool best_effort);
/* libunwind specific */
#ifdef HAVE_LIBUNWIND_SUPPORT
-int unwind__prepare_access(struct maps *maps, struct map *map, bool *initialized);
+int unwind__prepare_access(struct maps *maps, uint16_t e_machine);
void unwind__flush_access(struct maps *maps);
void unwind__finish_access(struct maps *maps);
#else
static inline int unwind__prepare_access(struct maps *maps __maybe_unused,
- struct map *map __maybe_unused,
- bool *initialized __maybe_unused)
+ uint16_t e_machine __maybe_unused)
{
return 0;
}
--
2.53.0.371.g1d285c8824-goog
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [RFC PATCH v1 6/7] perf unwind-libunwind: Remove libunwind-local
2026-02-24 14:29 [RFC PATCH v1 0/7] perf libunwind multiple remote support Ian Rogers
` (4 preceding siblings ...)
2026-02-24 14:29 ` [RFC PATCH v1 5/7] perf unwind-libunwind: Move flush/finish access out of local Ian Rogers
@ 2026-02-24 14:29 ` Ian Rogers
2026-02-24 14:29 ` [RFC PATCH v1 7/7] perf unwind-libunwind: Add RISC-V libunwind support Ian Rogers
6 siblings, 0 replies; 27+ messages in thread
From: Ian Rogers @ 2026-02-24 14:29 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Alexander Shishkin, Jiri Olsa, Ian Rogers,
Adrian Hunter, James Clark, John Garry, Will Deacon, Leo Yan,
Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
Dmitrii Dolgov, Blake Jones, Tomas Glozar, Yuzhuo Jing, Dapeng Mi,
Shimin Guo, Athira Rajeev, Chun-Tse Shao, Howard Chu,
linux-kernel, linux-perf-users, linux-arm-kernel, linux-riscv,
Thomas Richter, libunwind-devel
Local unwinding only works on the machine libunwind is built for,
rather than cross platform, the APIs for remote and local unwinding
are similar but types like unw_word_t depend on the included
header. Place the architecture specific code into the appropriate
libunwind-<arch>.c file. Put generic code in unwind-libunwind.c and
use libunwind-arch to choose the correct implementation based on the
thread's e_machine. Structuring the code this way avoids including the
unwind-libunwind-local.c for each architecture of remote
unwinding. Data is moved into the struct unwind_info to simplify the
architecture and generic code, trying to keep as much code as possible
generic.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/Build | 3 -
.../perf/util/libunwind-arch/libunwind-arch.c | 184 ++++
.../perf/util/libunwind-arch/libunwind-arch.h | 234 +++++
.../perf/util/libunwind-arch/libunwind-arm.c | 256 ++++++
.../util/libunwind-arch/libunwind-arm64.c | 255 ++++++
.../perf/util/libunwind-arch/libunwind-i386.c | 254 ++++++
.../util/libunwind-arch/libunwind-loongarch.c | 255 ++++++
.../perf/util/libunwind-arch/libunwind-mips.c | 255 ++++++
.../util/libunwind-arch/libunwind-ppc32.c | 255 ++++++
.../util/libunwind-arch/libunwind-ppc64.c | 255 ++++++
.../perf/util/libunwind-arch/libunwind-s390.c | 255 ++++++
.../util/libunwind-arch/libunwind-x86_64.c | 253 ++++++
tools/perf/util/libunwind/arm64.c | 35 -
tools/perf/util/libunwind/x86_32.c | 29 -
tools/perf/util/maps.c | 10 -
tools/perf/util/maps.h | 2 -
tools/perf/util/unwind-libunwind-local.c | 820 ------------------
tools/perf/util/unwind-libunwind.c | 654 +++++++++++++-
18 files changed, 3334 insertions(+), 930 deletions(-)
delete mode 100644 tools/perf/util/libunwind/arm64.c
delete mode 100644 tools/perf/util/libunwind/x86_32.c
delete mode 100644 tools/perf/util/unwind-libunwind-local.c
diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index 1964e13e3085..44685eec7615 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -227,11 +227,8 @@ perf-util-$(CONFIG_LIBDW) += annotate-data.o
perf-util-$(CONFIG_LIBDW) += libdw.o
perf-util-$(CONFIG_LIBDW) += unwind-libdw.o
-perf-util-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind-local.o
perf-util-$(CONFIG_LIBUNWIND) += unwind-libunwind.o
perf-util-$(CONFIG_LIBUNWIND) += libunwind-arch/
-perf-util-$(CONFIG_LIBUNWIND_X86) += libunwind/x86_32.o
-perf-util-$(CONFIG_LIBUNWIND_AARCH64) += libunwind/arm64.o
ifeq ($(CONFIG_LIBTRACEEVENT),y)
perf-util-$(CONFIG_LIBBABELTRACE) += data-convert-bt.o
diff --git a/tools/perf/util/libunwind-arch/libunwind-arch.c b/tools/perf/util/libunwind-arch/libunwind-arch.c
index 9692e6c81492..8539b4233df4 100644
--- a/tools/perf/util/libunwind-arch/libunwind-arch.c
+++ b/tools/perf/util/libunwind-arch/libunwind-arch.c
@@ -112,3 +112,187 @@ void libunwind_arch__finish_access(struct maps *maps)
break;
}
}
+
+void *libunwind_arch__create_addr_space(unsigned int e_machine)
+{
+ switch (e_machine) {
+ case EM_ARM:
+ return __libunwind_arch__create_addr_space_arm();
+ case EM_AARCH64:
+ return __libunwind_arch__create_addr_space_arm64();
+ case EM_LOONGARCH:
+ return __libunwind_arch__create_addr_space_loongarch();
+ case EM_MIPS:
+ return __libunwind_arch__create_addr_space_mips();
+ case EM_PPC:
+ return __libunwind_arch__create_addr_space_ppc32();
+ case EM_PPC64:
+ return __libunwind_arch__create_addr_space_ppc64();
+ case EM_S390:
+ return __libunwind_arch__create_addr_space_s390();
+ case EM_386:
+ return __libunwind_arch__create_addr_space_i386();
+ case EM_X86_64:
+ return __libunwind_arch__create_addr_space_x86_64();
+ default:
+ pr_err("ELF MACHINE %x is not supported.\n", e_machine);
+ return NULL;
+ }
+}
+
+int libunwind_arch__dwarf_search_unwind_table(unsigned int e_machine,
+ void *as,
+ uint64_t ip,
+ struct libarch_unwind__dyn_info *di,
+ void *pi,
+ int need_unwind_info,
+ void *arg)
+{
+ switch (e_machine) {
+ case EM_ARM:
+ return __libunwind_arch__dwarf_search_unwind_table_arm(as, ip, di, pi,
+ need_unwind_info, arg);
+ case EM_AARCH64:
+ return __libunwind_arch__dwarf_search_unwind_table_arm64(as, ip, di, pi,
+ need_unwind_info, arg);
+ case EM_LOONGARCH:
+ return __libunwind_arch__dwarf_search_unwind_table_loongarch(as, ip, di, pi,
+ need_unwind_info, arg);
+ case EM_MIPS:
+ return __libunwind_arch__dwarf_search_unwind_table_mips(as, ip, di, pi,
+ need_unwind_info, arg);
+ case EM_PPC:
+ return __libunwind_arch__dwarf_search_unwind_table_ppc32(as, ip, di, pi,
+ need_unwind_info, arg);
+ case EM_PPC64:
+ return __libunwind_arch__dwarf_search_unwind_table_ppc64(as, ip, di, pi,
+ need_unwind_info, arg);
+ case EM_S390:
+ return __libunwind_arch__dwarf_search_unwind_table_s390(as, ip, di, pi,
+ need_unwind_info, arg);
+ case EM_386:
+ return __libunwind_arch__dwarf_search_unwind_table_i386(as, ip, di, pi,
+ need_unwind_info, arg);
+ case EM_X86_64:
+ return __libunwind_arch__dwarf_search_unwind_table_x86_64(as, ip, di, pi,
+ need_unwind_info, arg);
+ default:
+ pr_err("ELF MACHINE %x is not supported.\n", e_machine);
+ return -EINVAL;
+ }
+}
+
+int libunwind_arch__dwarf_find_debug_frame(unsigned int e_machine,
+ int found,
+ struct libarch_unwind__dyn_info *di_debug,
+ uint64_t ip,
+ uint64_t segbase,
+ const char *obj_name,
+ uint64_t start,
+ uint64_t end)
+{
+ switch (e_machine) {
+ case EM_ARM:
+ return __libunwind_arch__dwarf_find_debug_frame_arm(found, di_debug, ip, segbase,
+ obj_name, start, end);
+ case EM_AARCH64:
+ return __libunwind_arch__dwarf_find_debug_frame_arm64(found, di_debug, ip, segbase,
+ obj_name, start, end);
+ case EM_LOONGARCH:
+ return __libunwind_arch__dwarf_find_debug_frame_loongarch(found, di_debug, ip,
+ segbase, obj_name,
+ start, end);
+ case EM_MIPS:
+ return __libunwind_arch__dwarf_find_debug_frame_mips(found, di_debug, ip, segbase,
+ obj_name, start, end);
+ case EM_PPC:
+ return __libunwind_arch__dwarf_find_debug_frame_ppc32(found, di_debug, ip, segbase,
+ obj_name, start, end);
+ case EM_PPC64:
+ return __libunwind_arch__dwarf_find_debug_frame_ppc64(found, di_debug, ip, segbase,
+ obj_name, start, end);
+ case EM_S390:
+ return __libunwind_arch__dwarf_find_debug_frame_s390(found, di_debug, ip, segbase,
+ obj_name, start, end);
+ case EM_386:
+ return __libunwind_arch__dwarf_find_debug_frame_i386(found, di_debug, ip, segbase,
+ obj_name, start, end);
+ case EM_X86_64:
+ return __libunwind_arch__dwarf_find_debug_frame_x86_64(found, di_debug, ip, segbase,
+ obj_name, start, end);
+ default:
+ pr_err("ELF MACHINE %x is not supported.\n", e_machine);
+ return -EINVAL;
+ }
+}
+
+struct unwind_info *libunwind_arch_unwind_info__new(struct thread *thread,
+ struct perf_sample *sample, int max_stack,
+ bool best_effort, uint16_t e_machine,
+ uint64_t first_ip)
+{
+ switch (e_machine) {
+ case EM_ARM:
+ return __libunwind_arch_unwind_info__new_arm(thread, sample, max_stack,
+ best_effort, first_ip);
+ case EM_AARCH64:
+ return __libunwind_arch_unwind_info__new_arm64(thread, sample, max_stack,
+ best_effort, first_ip);
+ case EM_LOONGARCH:
+ return __libunwind_arch_unwind_info__new_loongarch(thread, sample, max_stack,
+ best_effort, first_ip);
+ case EM_MIPS:
+ return __libunwind_arch_unwind_info__new_mips(thread, sample, max_stack,
+ best_effort, first_ip);
+ case EM_PPC:
+ return __libunwind_arch_unwind_info__new_ppc32(thread, sample, max_stack,
+ best_effort, first_ip);
+ case EM_PPC64:
+ return __libunwind_arch_unwind_info__new_ppc64(thread, sample, max_stack,
+ best_effort, first_ip);
+ case EM_S390:
+ return __libunwind_arch_unwind_info__new_s390(thread, sample, max_stack,
+ best_effort, first_ip);
+ case EM_386:
+ return __libunwind_arch_unwind_info__new_i386(thread, sample, max_stack,
+ best_effort, first_ip);
+ case EM_X86_64:
+ return __libunwind_arch_unwind_info__new_x86_64(thread, sample, max_stack,
+ best_effort, first_ip);
+ default:
+ pr_err("ELF MACHINE %x is not supported.\n", e_machine);
+ return NULL;
+ }
+}
+
+void libunwind_arch_unwind_info__delete(struct unwind_info *ui)
+{
+ free(ui);
+}
+
+int libunwind_arch__unwind_step(struct unwind_info *ui)
+{
+ switch (ui->e_machine) {
+ case EM_ARM:
+ return __libunwind_arch__unwind_step_arm(ui);
+ case EM_AARCH64:
+ return __libunwind_arch__unwind_step_arm64(ui);
+ case EM_LOONGARCH:
+ return __libunwind_arch__unwind_step_loongarch(ui);
+ case EM_MIPS:
+ return __libunwind_arch__unwind_step_mips(ui);
+ case EM_PPC:
+ return __libunwind_arch__unwind_step_ppc32(ui);
+ case EM_PPC64:
+ return __libunwind_arch__unwind_step_ppc64(ui);
+ case EM_S390:
+ return __libunwind_arch__unwind_step_s390(ui);
+ case EM_386:
+ return __libunwind_arch__unwind_step_i386(ui);
+ case EM_X86_64:
+ return __libunwind_arch__unwind_step_x86_64(ui);
+ default:
+ pr_err("ELF MACHINE %x is not supported.\n", ui->e_machine);
+ return -EINVAL;
+ }
+}
diff --git a/tools/perf/util/libunwind-arch/libunwind-arch.h b/tools/perf/util/libunwind-arch/libunwind-arch.h
index c00277a5e914..2bf7fc33313b 100644
--- a/tools/perf/util/libunwind-arch/libunwind-arch.h
+++ b/tools/perf/util/libunwind-arch/libunwind-arch.h
@@ -2,7 +2,36 @@
#ifndef __LIBUNWIND_ARCH_H
#define __LIBUNWIND_ARCH_H
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+struct machine;
struct maps;
+struct perf_sample;
+struct thread;
+
+struct unwind_info {
+ struct machine *machine;
+ struct thread *thread;
+ struct perf_sample *sample;
+ void *cursor;
+ uint64_t *ips;
+ int cur_ip;
+ int max_ips;
+ unsigned int unw_word_t_size;
+ uint16_t e_machine;
+ bool best_effort;
+};
+
+struct libarch_unwind__dyn_info {
+ uint64_t start_ip;
+ uint64_t end_ip;
+ uint64_t segbase;
+ uint64_t table_data;
+ uint64_t table_len;
+};
+struct libarch_unwind__proc_info;
int __get_perf_regnum_for_unw_regnum_arm(int unw_regnum);
int __get_perf_regnum_for_unw_regnum_arm64(int unw_regnum);
@@ -37,4 +66,209 @@ void __libunwind_arch__finish_access_i386(struct maps *maps);
void __libunwind_arch__finish_access_x86_64(struct maps *maps);
void libunwind_arch__finish_access(struct maps *maps);
+void *__libunwind_arch__create_addr_space_arm(void);
+void *__libunwind_arch__create_addr_space_arm64(void);
+void *__libunwind_arch__create_addr_space_loongarch(void);
+void *__libunwind_arch__create_addr_space_mips(void);
+void *__libunwind_arch__create_addr_space_ppc32(void);
+void *__libunwind_arch__create_addr_space_ppc64(void);
+void *__libunwind_arch__create_addr_space_s390(void);
+void *__libunwind_arch__create_addr_space_i386(void);
+void *__libunwind_arch__create_addr_space_x86_64(void);
+void *libunwind_arch__create_addr_space(unsigned int e_machine);
+
+int __libunwind__find_proc_info(void *as, uint64_t ip, void *pi, int need_unwind_info, void *arg);
+int __libunwind__access_mem(void *as, uint64_t addr, void *valp_word, int __write, void *arg);
+int __libunwind__access_reg(void *as, int regnum, void *valp_word, int __write, void *arg);
+
+int __libunwind_arch__dwarf_search_unwind_table_arm(void *as, uint64_t ip,
+ struct libarch_unwind__dyn_info *di,
+ void *pi,
+ int need_unwind_info,
+ void *arg);
+int __libunwind_arch__dwarf_search_unwind_table_arm64(void *as, uint64_t ip,
+ struct libarch_unwind__dyn_info *di,
+ void *pi,
+ int need_unwind_info,
+ void *arg);
+int __libunwind_arch__dwarf_search_unwind_table_loongarch(void *as, uint64_t ip,
+ struct libarch_unwind__dyn_info *di,
+ void *pi,
+ int need_unwind_info,
+ void *arg);
+int __libunwind_arch__dwarf_search_unwind_table_mips(void *as, uint64_t ip,
+ struct libarch_unwind__dyn_info *di,
+ void *pi,
+ int need_unwind_info,
+ void *arg);
+int __libunwind_arch__dwarf_search_unwind_table_ppc32(void *as, uint64_t ip,
+ struct libarch_unwind__dyn_info *di,
+ void *pi,
+ int need_unwind_info,
+ void *arg);
+int __libunwind_arch__dwarf_search_unwind_table_ppc64(void *as, uint64_t ip,
+ struct libarch_unwind__dyn_info *di,
+ void *pi,
+ int need_unwind_info,
+ void *arg);
+int __libunwind_arch__dwarf_search_unwind_table_s390(void *as, uint64_t ip,
+ struct libarch_unwind__dyn_info *di,
+ void *pi,
+ int need_unwind_info,
+ void *arg);
+int __libunwind_arch__dwarf_search_unwind_table_i386(void *as, uint64_t ip,
+ struct libarch_unwind__dyn_info *di,
+ void *pi,
+ int need_unwind_info,
+ void *arg);
+int __libunwind_arch__dwarf_search_unwind_table_x86_64(void *as, uint64_t ip,
+ struct libarch_unwind__dyn_info *di,
+ void *pi,
+ int need_unwind_info,
+ void *arg);
+int libunwind_arch__dwarf_search_unwind_table(unsigned int e_machine,
+ void *as,
+ uint64_t ip,
+ struct libarch_unwind__dyn_info *di,
+ void *pi,
+ int need_unwind_info,
+ void *arg);
+
+int __libunwind_arch__dwarf_find_debug_frame_arm(int found,
+ struct libarch_unwind__dyn_info *di_debug,
+ uint64_t ip,
+ uint64_t segbase,
+ const char *obj_name,
+ uint64_t start,
+ uint64_t end);
+int __libunwind_arch__dwarf_find_debug_frame_arm64(int found,
+ struct libarch_unwind__dyn_info *di_debug,
+ uint64_t ip,
+ uint64_t segbase,
+ const char *obj_name,
+ uint64_t start,
+ uint64_t end);
+int __libunwind_arch__dwarf_find_debug_frame_loongarch(int found,
+ struct libarch_unwind__dyn_info *di_debug,
+ uint64_t ip,
+ uint64_t segbase,
+ const char *obj_name,
+ uint64_t start,
+ uint64_t end);
+int __libunwind_arch__dwarf_find_debug_frame_mips(int found,
+ struct libarch_unwind__dyn_info *di_debug,
+ uint64_t ip,
+ uint64_t segbase,
+ const char *obj_name,
+ uint64_t start,
+ uint64_t end);
+int __libunwind_arch__dwarf_find_debug_frame_ppc32(int found,
+ struct libarch_unwind__dyn_info *di_debug,
+ uint64_t ip,
+ uint64_t segbase,
+ const char *obj_name,
+ uint64_t start,
+ uint64_t end);
+int __libunwind_arch__dwarf_find_debug_frame_ppc64(int found,
+ struct libarch_unwind__dyn_info *di_debug,
+ uint64_t ip,
+ uint64_t segbase,
+ const char *obj_name,
+ uint64_t start,
+ uint64_t end);
+int __libunwind_arch__dwarf_find_debug_frame_s390(int found,
+ struct libarch_unwind__dyn_info *di_debug,
+ uint64_t ip,
+ uint64_t segbase,
+ const char *obj_name,
+ uint64_t start,
+ uint64_t end);
+int __libunwind_arch__dwarf_find_debug_frame_i386(int found,
+ struct libarch_unwind__dyn_info *di_debug,
+ uint64_t ip,
+ uint64_t segbase,
+ const char *obj_name,
+ uint64_t start,
+ uint64_t end);
+int __libunwind_arch__dwarf_find_debug_frame_x86_64(int found,
+ struct libarch_unwind__dyn_info *di_debug,
+ uint64_t ip,
+ uint64_t segbase,
+ const char *obj_name,
+ uint64_t start,
+ uint64_t end);
+int libunwind_arch__dwarf_find_debug_frame(unsigned int e_machine,
+ int found,
+ struct libarch_unwind__dyn_info *di_debug,
+ uint64_t ip,
+ uint64_t segbase,
+ const char *obj_name,
+ uint64_t start,
+ uint64_t end);
+
+struct unwind_info *__libunwind_arch_unwind_info__new_arm(struct thread *thread,
+ struct perf_sample *sample,
+ int max_stack,
+ bool best_effort,
+ uint64_t first_ip);
+struct unwind_info *__libunwind_arch_unwind_info__new_arm64(struct thread *thread,
+ struct perf_sample *sample,
+ int max_stack,
+ bool best_effort,
+ uint64_t first_ip);
+struct unwind_info *__libunwind_arch_unwind_info__new_loongarch(struct thread *thread,
+ struct perf_sample *sample,
+ int max_stack,
+ bool best_effort,
+ uint64_t first_ip);
+struct unwind_info *__libunwind_arch_unwind_info__new_mips(struct thread *thread,
+ struct perf_sample *sample,
+ int max_stack,
+ bool best_effort,
+ uint64_t first_ip);
+struct unwind_info *__libunwind_arch_unwind_info__new_ppc32(struct thread *thread,
+ struct perf_sample *sample,
+ int max_stack,
+ bool best_effort,
+ uint64_t first_ip);
+struct unwind_info *__libunwind_arch_unwind_info__new_ppc64(struct thread *thread,
+ struct perf_sample *sample,
+ int max_stack,
+ bool best_effort,
+ uint64_t first_ip);
+struct unwind_info *__libunwind_arch_unwind_info__new_s390(struct thread *thread,
+ struct perf_sample *sample,
+ int max_stack,
+ bool best_effort,
+ uint64_t first_ip);
+struct unwind_info *__libunwind_arch_unwind_info__new_i386(struct thread *thread,
+ struct perf_sample *sample,
+ int max_stack,
+ bool best_effort,
+ uint64_t first_ip);
+struct unwind_info *__libunwind_arch_unwind_info__new_x86_64(struct thread *thread,
+ struct perf_sample *sample,
+ int max_stack,
+ bool best_effort,
+ uint64_t first_ip);
+struct unwind_info *libunwind_arch_unwind_info__new(struct thread *thread,
+ struct perf_sample *sample,
+ int max_stack,
+ bool best_effort,
+ uint16_t e_machine,
+ uint64_t first_ip);
+
+void libunwind_arch_unwind_info__delete(struct unwind_info *ui);
+
+int __libunwind_arch__unwind_step_arm(struct unwind_info *ui);
+int __libunwind_arch__unwind_step_arm64(struct unwind_info *ui);
+int __libunwind_arch__unwind_step_loongarch(struct unwind_info *ui);
+int __libunwind_arch__unwind_step_mips(struct unwind_info *ui);
+int __libunwind_arch__unwind_step_ppc32(struct unwind_info *ui);
+int __libunwind_arch__unwind_step_ppc64(struct unwind_info *ui);
+int __libunwind_arch__unwind_step_s390(struct unwind_info *ui);
+int __libunwind_arch__unwind_step_i386(struct unwind_info *ui);
+int __libunwind_arch__unwind_step_x86_64(struct unwind_info *ui);
+int libunwind_arch__unwind_step(struct unwind_info *ui);
+
#endif /* __LIBUNWIND_ARCH_H */
diff --git a/tools/perf/util/libunwind-arch/libunwind-arm.c b/tools/perf/util/libunwind-arch/libunwind-arm.c
index bbaf01406c52..97a735488efe 100644
--- a/tools/perf/util/libunwind-arch/libunwind-arm.c
+++ b/tools/perf/util/libunwind-arch/libunwind-arm.c
@@ -2,8 +2,12 @@
#include "libunwind-arch.h"
#include "../debug.h"
#include "../maps.h"
+#include "../thread.h"
#include "../../../arch/arm/include/uapi/asm/perf_regs.h"
#include <linux/compiler.h>
+#include <linux/kernel.h>
+#include <linux/zalloc.h>
+#include <elf.h>
#include <errno.h>
#ifdef HAVE_LIBUNWIND_ARM_SUPPORT
@@ -32,3 +36,255 @@ void __libunwind_arch__finish_access_arm(struct maps *maps __maybe_unused)
unw_destroy_addr_space(maps__addr_space(maps));
#endif
}
+
+
+#ifdef HAVE_LIBUNWIND_ARM_SUPPORT
+static int find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
+ int need_unwind_info, void *arg)
+{
+ return __libunwind__find_proc_info(as, ip, pi, need_unwind_info, arg, sizeof(unw_word_t));
+}
+
+static void put_unwind_info(unw_addr_space_t __maybe_unused as,
+ unw_proc_info_t *pi __maybe_unused,
+ void *arg __maybe_unused)
+{
+ pr_debug("unwind: put_unwind_info called\n");
+}
+
+static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as,
+ unw_word_t __maybe_unused *dil_addr,
+ void __maybe_unused *arg)
+{
+ return -UNW_ENOINFO;
+}
+
+static int access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *valp,
+ int __write, void *arg)
+{
+ return __libunwind__access_mem(as, addr, valp, __write, arg, sizeof(unw_word_t));
+}
+
+static int access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t *valp,
+ int __write, void *arg)
+{
+ return __libunwind__access_reg(as, regnum, valp, __write, arg, sizeof(unw_word_t));
+}
+
+static int access_fpreg(unw_addr_space_t __maybe_unused as,
+ unw_regnum_t __maybe_unused num,
+ unw_fpreg_t __maybe_unused *val,
+ int __maybe_unused __write,
+ void __maybe_unused *arg)
+{
+ pr_err("unwind: access_fpreg unsupported\n");
+ return -UNW_EINVAL;
+}
+
+static int resume(unw_addr_space_t __maybe_unused as,
+ unw_cursor_t __maybe_unused *cu,
+ void __maybe_unused *arg)
+{
+ pr_err("unwind: resume unsupported\n");
+ return -UNW_EINVAL;
+}
+
+static int get_proc_name(unw_addr_space_t __maybe_unused as,
+ unw_word_t __maybe_unused addr,
+ char __maybe_unused *bufp, size_t __maybe_unused buf_len,
+ unw_word_t __maybe_unused *offp, void __maybe_unused *arg)
+{
+ pr_err("unwind: get_proc_name unsupported\n");
+ return -UNW_EINVAL;
+}
+#endif
+
+void *__libunwind_arch__create_addr_space_arm(void)
+{
+#ifdef HAVE_LIBUNWIND_ARM_SUPPORT
+ static unw_accessors_t accessors = {
+ .find_proc_info = find_proc_info,
+ .put_unwind_info = put_unwind_info,
+ .get_dyn_info_list_addr = get_dyn_info_list_addr,
+ .access_mem = access_mem,
+ .access_reg = access_reg,
+ .access_fpreg = access_fpreg,
+ .resume = resume,
+ .get_proc_name = get_proc_name,
+ };
+ unw_addr_space_t addr_space;
+
+ addr_space = unw_create_addr_space(&accessors, /*byte_order=*/0);
+ unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL);
+ return addr_space;
+#else
+ return NULL;
+#endif
+}
+
+#ifdef HAVE_LIBUNWIND_ARM_SUPPORT
+extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,
+ unw_word_t ip,
+ unw_dyn_info_t *di,
+ unw_proc_info_t *pi,
+ int need_unwind_info, void *arg);
+#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table)
+#endif
+
+int __libunwind_arch__dwarf_search_unwind_table_arm(void *as __maybe_unused,
+ uint64_t ip __maybe_unused,
+ struct libarch_unwind__dyn_info *_di __maybe_unused,
+ void *pi __maybe_unused,
+ int need_unwind_info __maybe_unused,
+ void *arg __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_ARM_SUPPORT
+ unw_dyn_info_t di = {
+ .format = UNW_INFO_FORMAT_REMOTE_TABLE,
+ .start_ip = _di->start_ip,
+ .end_ip = _di->end_ip,
+ .u = {
+ .rti = {
+ .segbase = _di->segbase,
+ .table_data = _di->table_data,
+ .table_len = _di->table_len,
+ },
+ },
+ };
+ int ret = dwarf_search_unwind_table(as, ip, &di, pi, need_unwind_info, arg);
+
+ _di->start_ip = di.start_ip;
+ _di->end_ip = di.end_ip;
+ _di->segbase = di.u.rti.segbase;
+ _di->table_data = di.u.rti.table_data;
+ _di->table_len = di.u.rti.table_len;
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
+
+#if defined(HAVE_LIBUNWIND_ARM_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_ARM)
+extern int UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug,
+ unw_word_t ip,
+ unw_word_t segbase,
+ const char *obj_name, unw_word_t start,
+ unw_word_t end);
+#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame)
+#endif
+
+int __libunwind_arch__dwarf_find_debug_frame_arm(int found __maybe_unused,
+ struct libarch_unwind__dyn_info *_di __maybe_unused,
+ uint64_t ip __maybe_unused,
+ uint64_t segbase __maybe_unused,
+ const char *obj_name __maybe_unused,
+ uint64_t start __maybe_unused,
+ uint64_t end __maybe_unused)
+{
+#if defined(HAVE_LIBUNWIND_ARM_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_ARM)
+ unw_dyn_info_t di = {
+ .format = UNW_INFO_FORMAT_REMOTE_TABLE,
+ .start_ip = _di->start_ip,
+ .end_ip = _di->end_ip,
+ .u = {
+ .rti = {
+ .segbase = _di->segbase,
+ .table_data = _di->table_data,
+ .table_len = _di->len,
+ },
+ },
+ };
+ int ret = dwarf_find_debug_frame(found, &di, ip, segvase, obj_name, start, end);
+
+ _di->start_ip = di.start_ip;
+ _di->end_ip = di.end_ip;
+ _di->segbase = di.u.rti.segbase;
+ _di->table_data = di.u.rti.table_data;
+ _di->table_len = di.u.rti.table_len;
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
+
+struct unwind_info *__libunwind_arch_unwind_info__new_arm(struct thread *thread __maybe_unused,
+ struct perf_sample *sample __maybe_unused,
+ int max_stack __maybe_unused,
+ bool best_effort __maybe_unused,
+ uint64_t first_ip __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_ARM_SUPPORT
+ struct x86_64_unwind_info {
+ struct unwind_info ui;
+ unw_cursor_t _cursor;
+ uint64_t _ips[];
+ };
+
+ struct maps *maps = thread__maps(thread);
+ void *addr_space = maps__addr_space(maps);
+ struct x86_64_unwind_info *ui;
+ int ret;
+
+ if (addr_space == NULL)
+ return NULL;
+
+ ui = zalloc(sizeof(*ui) + sizeof(ui->_ips[0]) * max_stack);
+ if (!ui)
+ return NULL;
+
+ ui->ui.machine = maps__machine(maps);
+ ui->ui.thread = thread;
+ ui->ui.sample = sample;
+ ui->ui.cursor = &ui->_cursor;
+ ui->ui.ips = &ui->_ips[0];
+ ui->ui.ips[0] = first_ip;
+ ui->ui.cur_ip = 1;
+ ui->ui.max_ips = max_stack;
+ ui->ui.unw_word_t_size = sizeof(unw_word_t);
+ ui->ui.e_machine = EM_ARM;
+ ui->ui.best_effort = best_effort;
+
+ ret = unw_init_remote(&ui->_cursor, addr_space, &ui->ui);
+ if (ret) {
+ if (!best_effort)
+ pr_err("libunwind: %s\n", unw_strerror(ret));
+ free(ui);
+ return NULL;
+ }
+
+ return &ui->ui;
+#else
+ return NULL;
+#endif
+}
+
+int __libunwind_arch__unwind_step_arm(struct unwind_info *ui __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_ARM_SUPPORT
+ int ret;
+
+ if (ui->cur_ip >= ui->max_ips)
+ return -1;
+
+ ret = unw_step(ui->cursor);
+ if (ret > 0) {
+ uint64_t ip;
+
+ unw_get_reg(ui->cursor, UNW_REG_IP, &ip);
+
+ if (unw_is_signal_frame(ui->cursor) <= 0) {
+ /*
+ * Decrement the IP for any non-activation frames. This
+ * is required to properly find the srcline for caller
+ * frames. See also the documentation for
+ * dwfl_frame_pc(), which this code tries to replicate.
+ */
+ --ip;
+ }
+ ui->ips[ui->cur_ip++] = ip;
+ }
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
diff --git a/tools/perf/util/libunwind-arch/libunwind-arm64.c b/tools/perf/util/libunwind-arch/libunwind-arm64.c
index 8ba510089736..1f6e674b6565 100644
--- a/tools/perf/util/libunwind-arch/libunwind-arm64.c
+++ b/tools/perf/util/libunwind-arch/libunwind-arm64.c
@@ -2,8 +2,12 @@
#include "libunwind-arch.h"
#include "../debug.h"
#include "../maps.h"
+#include "../thread.h"
#include "../../../arch/arm64/include/uapi/asm/perf_regs.h"
#include <linux/compiler.h>
+#include <linux/kernel.h>
+#include <linux/zalloc.h>
+#include <elf.h>
#include <errno.h>
#ifdef HAVE_LIBUNWIND_AARCH64_SUPPORT
@@ -32,3 +36,254 @@ void __libunwind_arch__finish_access_arm64(struct maps *maps __maybe_unused)
unw_destroy_addr_space(maps__addr_space(maps));
#endif
}
+
+#ifdef HAVE_LIBUNWIND_AARCH64_SUPPORT
+static int find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
+ int need_unwind_info, void *arg)
+{
+ return __libunwind__find_proc_info(as, ip, pi, need_unwind_info, arg, sizeof(unw_word_t));
+}
+
+static void put_unwind_info(unw_addr_space_t __maybe_unused as,
+ unw_proc_info_t *pi __maybe_unused,
+ void *arg __maybe_unused)
+{
+ pr_debug("unwind: put_unwind_info called\n");
+}
+
+static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as,
+ unw_word_t __maybe_unused *dil_addr,
+ void __maybe_unused *arg)
+{
+ return -UNW_ENOINFO;
+}
+
+static int access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *valp,
+ int __write, void *arg)
+{
+ return __libunwind__access_mem(as, addr, valp, __write, arg, sizeof(unw_word_t));
+}
+
+static int access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t *valp,
+ int __write, void *arg)
+{
+ return __libunwind__access_reg(as, regnum, valp, __write, arg, sizeof(unw_word_t));
+}
+
+static int access_fpreg(unw_addr_space_t __maybe_unused as,
+ unw_regnum_t __maybe_unused num,
+ unw_fpreg_t __maybe_unused *val,
+ int __maybe_unused __write,
+ void __maybe_unused *arg)
+{
+ pr_err("unwind: access_fpreg unsupported\n");
+ return -UNW_EINVAL;
+}
+
+static int resume(unw_addr_space_t __maybe_unused as,
+ unw_cursor_t __maybe_unused *cu,
+ void __maybe_unused *arg)
+{
+ pr_err("unwind: resume unsupported\n");
+ return -UNW_EINVAL;
+}
+
+static int get_proc_name(unw_addr_space_t __maybe_unused as,
+ unw_word_t __maybe_unused addr,
+ char __maybe_unused *bufp, size_t __maybe_unused buf_len,
+ unw_word_t __maybe_unused *offp, void __maybe_unused *arg)
+{
+ pr_err("unwind: get_proc_name unsupported\n");
+ return -UNW_EINVAL;
+}
+#endif
+
+void *__libunwind_arch__create_addr_space_arm64(void)
+{
+#ifdef HAVE_LIBUNWIND_AARCH64_SUPPORT
+ static unw_accessors_t accessors = {
+ .find_proc_info = find_proc_info,
+ .put_unwind_info = put_unwind_info,
+ .get_dyn_info_list_addr = get_dyn_info_list_addr,
+ .access_mem = access_mem,
+ .access_reg = access_reg,
+ .access_fpreg = access_fpreg,
+ .resume = resume,
+ .get_proc_name = get_proc_name,
+ };
+ unw_addr_space_t addr_space;
+
+ addr_space = unw_create_addr_space(&accessors, /*byte_order=*/0);
+ unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL);
+ return addr_space;
+#else
+ return NULL;
+#endif
+}
+
+#ifdef HAVE_LIBUNWIND_ARM64_SUPPORT
+extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,
+ unw_word_t ip,
+ unw_dyn_info_t *di,
+ unw_proc_info_t *pi,
+ int need_unwind_info, void *arg);
+#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table)
+#endif
+
+int __libunwind_arch__dwarf_search_unwind_table_arm64(void *as __maybe_unused,
+ uint64_t ip __maybe_unused,
+ struct libarch_unwind__dyn_info *_di __maybe_unused,
+ void *pi __maybe_unused,
+ int need_unwind_info __maybe_unused,
+ void *arg __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_ARM64_SUPPORT
+ unw_dyn_info_t di = {
+ .format = UNW_INFO_FORMAT_REMOTE_TABLE,
+ .start_ip = _di->start_ip,
+ .end_ip = _di->end_ip,
+ .u = {
+ .rti = {
+ .segbase = _di->segbase,
+ .table_data = _di->table_data,
+ .table_len = _di->table_len,
+ },
+ },
+ };
+ int ret = dwarf_search_unwind_table(as, ip, &di, pi, need_unwind_info, arg);
+
+ _di->start_ip = di.start_ip;
+ _di->end_ip = di.end_ip;
+ _di->segbase = di.u.rti.segbase;
+ _di->table_data = di.u.rti.table_data;
+ _di->table_len = di.u.rti.table_len;
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
+
+#if defined(HAVE_LIBUNWIND_ARM64_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_ARM64)
+extern int UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug,
+ unw_word_t ip,
+ unw_word_t segbase,
+ const char *obj_name, unw_word_t start,
+ unw_word_t end);
+#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame)
+#endif
+
+int __libunwind_arch__dwarf_find_debug_frame_arm64(int found __maybe_unused,
+ struct libarch_unwind__dyn_info *_di __maybe_unused,
+ uint64_t ip __maybe_unused,
+ uint64_t segbase __maybe_unused,
+ const char *obj_name __maybe_unused,
+ uint64_t start __maybe_unused,
+ uint64_t end __maybe_unused)
+{
+#if defined(HAVE_LIBUNWIND_ARM64_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_ARM64)
+ unw_dyn_info_t di = {
+ .format = UNW_INFO_FORMAT_REMOTE_TABLE,
+ .start_ip = _di->start_ip,
+ .end_ip = _di->end_ip,
+ .u = {
+ .rti = {
+ .segbase = _di->segbase,
+ .table_data = _di->table_data,
+ .table_len = _di->len,
+ },
+ },
+ };
+ int ret = dwarf_find_debug_frame(found, &di, ip, segvase, obj_name, start, end);
+
+ _di->start_ip = di.start_ip;
+ _di->end_ip = di.end_ip;
+ _di->segbase = di.u.rti.segbase;
+ _di->table_data = di.u.rti.table_data;
+ _di->table_len = di.u.rti.table_len;
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
+
+struct unwind_info *__libunwind_arch_unwind_info__new_arm64(struct thread *thread __maybe_unused,
+ struct perf_sample *sample __maybe_unused,
+ int max_stack __maybe_unused,
+ bool best_effort __maybe_unused,
+ uint64_t first_ip __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_ARM64_SUPPORT
+ struct x86_64_unwind_info {
+ struct unwind_info ui;
+ unw_cursor_t _cursor;
+ uint64_t _ips[];
+ };
+
+ struct maps *maps = thread__maps(thread);
+ void *addr_space = maps__addr_space(maps);
+ struct x86_64_unwind_info *ui;
+ int ret;
+
+ if (addr_space == NULL)
+ return NULL;
+
+ ui = zalloc(sizeof(*ui) + sizeof(ui->_ips[0]) * max_stack);
+ if (!ui)
+ return NULL;
+
+ ui->ui.machine = maps__machine(maps);
+ ui->ui.thread = thread;
+ ui->ui.sample = sample;
+ ui->ui.cursor = &ui->_cursor;
+ ui->ui.ips = &ui->_ips[0];
+ ui->ui.ips[0] = first_ip;
+ ui->ui.cur_ip = 1;
+ ui->ui.max_ips = max_stack;
+ ui->ui.unw_word_t_size = sizeof(unw_word_t);
+ ui->ui.e_machine = EM_AARCH64;
+ ui->ui.best_effort = best_effort;
+
+ ret = unw_init_remote(&ui->_cursor, addr_space, &ui->ui);
+ if (ret) {
+ if (!best_effort)
+ pr_err("libunwind: %s\n", unw_strerror(ret));
+ free(ui);
+ return NULL;
+ }
+
+ return &ui->ui;
+#else
+ return NULL;
+#endif
+}
+
+int __libunwind_arch__unwind_step_arm64(struct unwind_info *ui __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_ARM64_SUPPORT
+ int ret;
+
+ if (ui->cur_ip >= ui->max_ips)
+ return -1;
+
+ ret = unw_step(ui->cursor);
+ if (ret > 0) {
+ uint64_t ip;
+
+ unw_get_reg(ui->cursor, UNW_REG_IP, &ip);
+
+ if (unw_is_signal_frame(ui->cursor) <= 0) {
+ /*
+ * Decrement the IP for any non-activation frames. This
+ * is required to properly find the srcline for caller
+ * frames. See also the documentation for
+ * dwfl_frame_pc(), which this code tries to replicate.
+ */
+ --ip;
+ }
+ ui->ips[ui->cur_ip++] = ip;
+ }
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
diff --git a/tools/perf/util/libunwind-arch/libunwind-i386.c b/tools/perf/util/libunwind-arch/libunwind-i386.c
index a0dcaa10578e..c52a41447ef0 100644
--- a/tools/perf/util/libunwind-arch/libunwind-i386.c
+++ b/tools/perf/util/libunwind-arch/libunwind-i386.c
@@ -2,9 +2,12 @@
#include "libunwind-arch.h"
#include "../debug.h"
#include "../maps.h"
+#include "../thread.h"
#include "../../../arch/x86/include/uapi/asm/perf_regs.h"
#include <linux/compiler.h>
#include <linux/kernel.h>
+#include <linux/zalloc.h>
+#include <elf.h>
#include <errno.h>
#ifdef HAVE_LIBUNWIND_X86_SUPPORT
@@ -56,3 +59,254 @@ void __libunwind_arch__finish_access_i386(struct maps *maps __maybe_unused)
unw_destroy_addr_space(maps__addr_space(maps));
#endif
}
+
+#ifdef HAVE_LIBUNWIND_X86_SUPPORT
+static int find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
+ int need_unwind_info, void *arg)
+{
+ return __libunwind__find_proc_info(as, ip, pi, need_unwind_info, arg, sizeof(unw_word_t));
+}
+
+static void put_unwind_info(unw_addr_space_t __maybe_unused as,
+ unw_proc_info_t *pi __maybe_unused,
+ void *arg __maybe_unused)
+{
+ pr_debug("unwind: put_unwind_info called\n");
+}
+
+static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as,
+ unw_word_t __maybe_unused *dil_addr,
+ void __maybe_unused *arg)
+{
+ return -UNW_ENOINFO;
+}
+
+static int access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *valp,
+ int __write, void *arg)
+{
+ return __libunwind__access_mem(as, addr, valp, __write, arg, sizeof(unw_word_t));
+}
+
+static int access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t *valp,
+ int __write, void *arg)
+{
+ return __libunwind__access_reg(as, regnum, valp, __write, arg, sizeof(unw_word_t));
+}
+
+static int access_fpreg(unw_addr_space_t __maybe_unused as,
+ unw_regnum_t __maybe_unused num,
+ unw_fpreg_t __maybe_unused *val,
+ int __maybe_unused __write,
+ void __maybe_unused *arg)
+{
+ pr_err("unwind: access_fpreg unsupported\n");
+ return -UNW_EINVAL;
+}
+
+static int resume(unw_addr_space_t __maybe_unused as,
+ unw_cursor_t __maybe_unused *cu,
+ void __maybe_unused *arg)
+{
+ pr_err("unwind: resume unsupported\n");
+ return -UNW_EINVAL;
+}
+
+static int get_proc_name(unw_addr_space_t __maybe_unused as,
+ unw_word_t __maybe_unused addr,
+ char __maybe_unused *bufp, size_t __maybe_unused buf_len,
+ unw_word_t __maybe_unused *offp, void __maybe_unused *arg)
+{
+ pr_err("unwind: get_proc_name unsupported\n");
+ return -UNW_EINVAL;
+}
+#endif
+
+void *__libunwind_arch__create_addr_space_i386(void)
+{
+#ifdef HAVE_LIBUNWIND_X86_SUPPORT
+ static unw_accessors_t accessors = {
+ .find_proc_info = find_proc_info,
+ .put_unwind_info = put_unwind_info,
+ .get_dyn_info_list_addr = get_dyn_info_list_addr,
+ .access_mem = access_mem,
+ .access_reg = access_reg,
+ .access_fpreg = access_fpreg,
+ .resume = resume,
+ .get_proc_name = get_proc_name,
+ };
+ unw_addr_space_t addr_space;
+
+ addr_space = unw_create_addr_space(&accessors, /*byte_order=*/0);
+ unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL);
+ return addr_space;
+#else
+ return NULL;
+#endif
+}
+
+#ifdef HAVE_LIBUNWIND_X86_SUPPORT
+extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,
+ unw_word_t ip,
+ unw_dyn_info_t *di,
+ unw_proc_info_t *pi,
+ int need_unwind_info, void *arg);
+#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table)
+#endif
+
+int __libunwind_arch__dwarf_search_unwind_table_i386(void *as __maybe_unused,
+ uint64_t ip __maybe_unused,
+ struct libarch_unwind__dyn_info *_di __maybe_unused,
+ void *pi __maybe_unused,
+ int need_unwind_info __maybe_unused,
+ void *arg __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_X86_SUPPORT
+ unw_dyn_info_t di = {
+ .format = UNW_INFO_FORMAT_REMOTE_TABLE,
+ .start_ip = _di->start_ip,
+ .end_ip = _di->end_ip,
+ .u = {
+ .rti = {
+ .segbase = _di->segbase,
+ .table_data = _di->table_data,
+ .table_len = _di->table_len,
+ },
+ },
+ };
+ int ret = dwarf_search_unwind_table(as, ip, &di, pi, need_unwind_info, arg);
+
+ _di->start_ip = di.start_ip;
+ _di->end_ip = di.end_ip;
+ _di->segbase = di.u.rti.segbase;
+ _di->table_data = di.u.rti.table_data;
+ _di->table_len = di.u.rti.table_len;
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
+
+#if defined(HAVE_LIBUNWIND_X86_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_X86)
+extern int UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug,
+ unw_word_t ip,
+ unw_word_t segbase,
+ const char *obj_name, unw_word_t start,
+ unw_word_t end);
+#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame)
+#endif
+
+int __libunwind_arch__dwarf_find_debug_frame_i386(int found __maybe_unused,
+ struct libarch_unwind__dyn_info *_di __maybe_unused,
+ uint64_t ip __maybe_unused,
+ uint64_t segbase __maybe_unused,
+ const char *obj_name __maybe_unused,
+ uint64_t start __maybe_unused,
+ uint64_t end __maybe_unused)
+{
+#if defined(HAVE_LIBUNWIND_X86_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_X86)
+ unw_dyn_info_t di = {
+ .format = UNW_INFO_FORMAT_REMOTE_TABLE,
+ .start_ip = _di->start_ip,
+ .end_ip = _di->end_ip,
+ .u = {
+ .rti = {
+ .segbase = _di->segbase,
+ .table_data = _di->table_data,
+ .table_len = _di->len,
+ },
+ },
+ };
+ int ret = dwarf_find_debug_frame(found, &di, ip, segvase, obj_name, start, end);
+
+ _di->start_ip = di.start_ip;
+ _di->end_ip = di.end_ip;
+ _di->segbase = di.u.rti.segbase;
+ _di->table_data = di.u.rti.table_data;
+ _di->table_len = di.u.rti.table_len;
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
+
+struct unwind_info *__libunwind_arch_unwind_info__new_i386(struct thread *thread __maybe_unused,
+ struct perf_sample *sample __maybe_unused,
+ int max_stack __maybe_unused,
+ bool best_effort __maybe_unused,
+ uint64_t first_ip __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_X86_SUPPORT
+ struct x86_64_unwind_info {
+ struct unwind_info ui;
+ unw_cursor_t _cursor;
+ uint64_t _ips[];
+ };
+
+ struct maps *maps = thread__maps(thread);
+ void *addr_space = maps__addr_space(maps);
+ struct x86_64_unwind_info *ui;
+ int ret;
+
+ if (addr_space == NULL)
+ return NULL;
+
+ ui = zalloc(sizeof(*ui) + sizeof(ui->_ips[0]) * max_stack);
+ if (!ui)
+ return NULL;
+
+ ui->ui.machine = maps__machine(maps);
+ ui->ui.thread = thread;
+ ui->ui.sample = sample;
+ ui->ui.cursor = &ui->_cursor;
+ ui->ui.ips = &ui->_ips[0];
+ ui->ui.ips[0] = first_ip;
+ ui->ui.cur_ip = 1;
+ ui->ui.max_ips = max_stack;
+ ui->ui.unw_word_t_size = sizeof(unw_word_t);
+ ui->ui.e_machine = EM_I386;
+ ui->ui.best_effort = best_effort;
+
+ ret = unw_init_remote(&ui->_cursor, addr_space, &ui->ui);
+ if (ret) {
+ if (!best_effort)
+ pr_err("libunwind: %s\n", unw_strerror(ret));
+ free(ui);
+ return NULL;
+ }
+
+ return &ui->ui;
+#else
+ return NULL;
+#endif
+}
+
+int __libunwind_arch__unwind_step_i386(struct unwind_info *ui __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_X86_SUPPORT
+ int ret;
+
+ if (ui->cur_ip >= ui->max_ips)
+ return -1;
+
+ ret = unw_step(ui->cursor);
+ if (ret > 0) {
+ uint64_t ip;
+
+ unw_get_reg(ui->cursor, UNW_REG_IP, &ip);
+
+ if (unw_is_signal_frame(ui->cursor) <= 0) {
+ /*
+ * Decrement the IP for any non-activation frames. This
+ * is required to properly find the srcline for caller
+ * frames. See also the documentation for
+ * dwfl_frame_pc(), which this code tries to replicate.
+ */
+ --ip;
+ }
+ ui->ips[ui->cur_ip++] = ip;
+ }
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
diff --git a/tools/perf/util/libunwind-arch/libunwind-loongarch.c b/tools/perf/util/libunwind-arch/libunwind-loongarch.c
index 837aa11e2b9f..cccac749256d 100644
--- a/tools/perf/util/libunwind-arch/libunwind-loongarch.c
+++ b/tools/perf/util/libunwind-arch/libunwind-loongarch.c
@@ -2,8 +2,12 @@
#include "libunwind-arch.h"
#include "../debug.h"
#include "../maps.h"
+#include "../thread.h"
#include "../../../arch/loongarch/include/uapi/asm/perf_regs.h"
#include <linux/compiler.h>
+#include <linux/kernel.h>
+#include <linux/zalloc.h>
+#include <elf.h>
#include <errno.h>
#ifdef HAVE_LIBUNWIND_LOONGARCH64_SUPPORT
@@ -40,3 +44,254 @@ void __libunwind_arch__finish_access_loongarch(struct maps *maps __maybe_unused)
unw_destroy_addr_space(maps__addr_space(maps));
#endif
}
+
+#ifdef HAVE_LIBUNWIND_LOONGARCH64_SUPPORT
+static int find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
+ int need_unwind_info, void *arg)
+{
+ return __libunwind__find_proc_info(as, ip, pi, need_unwind_info, arg, sizeof(unw_word_t));
+}
+
+static void put_unwind_info(unw_addr_space_t __maybe_unused as,
+ unw_proc_info_t *pi __maybe_unused,
+ void *arg __maybe_unused)
+{
+ pr_debug("unwind: put_unwind_info called\n");
+}
+
+static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as,
+ unw_word_t __maybe_unused *dil_addr,
+ void __maybe_unused *arg)
+{
+ return -UNW_ENOINFO;
+}
+
+static int access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *valp,
+ int __write, void *arg)
+{
+ return __libunwind__access_mem(as, addr, valp, __write, arg, sizeof(unw_word_t));
+}
+
+static int access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t *valp,
+ int __write, void *arg)
+{
+ return __libunwind__access_reg(as, regnum, valp, __write, arg, sizeof(unw_word_t));
+}
+
+static int access_fpreg(unw_addr_space_t __maybe_unused as,
+ unw_regnum_t __maybe_unused num,
+ unw_fpreg_t __maybe_unused *val,
+ int __maybe_unused __write,
+ void __maybe_unused *arg)
+{
+ pr_err("unwind: access_fpreg unsupported\n");
+ return -UNW_EINVAL;
+}
+
+static int resume(unw_addr_space_t __maybe_unused as,
+ unw_cursor_t __maybe_unused *cu,
+ void __maybe_unused *arg)
+{
+ pr_err("unwind: resume unsupported\n");
+ return -UNW_EINVAL;
+}
+
+static int get_proc_name(unw_addr_space_t __maybe_unused as,
+ unw_word_t __maybe_unused addr,
+ char __maybe_unused *bufp, size_t __maybe_unused buf_len,
+ unw_word_t __maybe_unused *offp, void __maybe_unused *arg)
+{
+ pr_err("unwind: get_proc_name unsupported\n");
+ return -UNW_EINVAL;
+}
+#endif
+
+void *__libunwind_arch__create_addr_space_loongarch(void)
+{
+#ifdef HAVE_LIBUNWIND_LOONGARCH64_SUPPORT
+ static unw_accessors_t accessors = {
+ .find_proc_info = find_proc_info,
+ .put_unwind_info = put_unwind_info,
+ .get_dyn_info_list_addr = get_dyn_info_list_addr,
+ .access_mem = access_mem,
+ .access_reg = access_reg,
+ .access_fpreg = access_fpreg,
+ .resume = resume,
+ .get_proc_name = get_proc_name,
+ };
+ unw_addr_space_t addr_space;
+
+ addr_space = unw_create_addr_space(&accessors, /*byte_order=*/0);
+ unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL);
+ return addr_space;
+#else
+ return NULL;
+#endif
+}
+
+#ifdef HAVE_LIBUNWIND_LOONGARCH_SUPPORT
+extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,
+ unw_word_t ip,
+ unw_dyn_info_t *di,
+ unw_proc_info_t *pi,
+ int need_unwind_info, void *arg);
+#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table)
+#endif
+
+int __libunwind_arch__dwarf_search_unwind_table_loongarch(void *as __maybe_unused,
+ uint64_t ip __maybe_unused,
+ struct libarch_unwind__dyn_info *_di __maybe_unused,
+ void *pi __maybe_unused,
+ int need_unwind_info __maybe_unused,
+ void *arg __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_LOONGARCH_SUPPORT
+ unw_dyn_info_t di = {
+ .format = UNW_INFO_FORMAT_REMOTE_TABLE,
+ .start_ip = _di->start_ip,
+ .end_ip = _di->end_ip,
+ .u = {
+ .rti = {
+ .segbase = _di->segbase,
+ .table_data = _di->table_data,
+ .table_len = _di->table_len,
+ },
+ },
+ };
+ int ret = dwarf_search_unwind_table(as, ip, &di, pi, need_unwind_info, arg);
+
+ _di->start_ip = di.start_ip;
+ _di->end_ip = di.end_ip;
+ _di->segbase = di.u.rti.segbase;
+ _di->table_data = di.u.rti.table_data;
+ _di->table_len = di.u.rti.table_len;
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
+
+#if defined(HAVE_LIBUNWIND_LOONGARCH_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_LOONGARCH)
+extern int UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug,
+ unw_word_t ip,
+ unw_word_t segbase,
+ const char *obj_name, unw_word_t start,
+ unw_word_t end);
+#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame)
+#endif
+
+int __libunwind_arch__dwarf_find_debug_frame_loongarch(int found __maybe_unused,
+ struct libarch_unwind__dyn_info *_di __maybe_unused,
+ uint64_t ip __maybe_unused,
+ uint64_t segbase __maybe_unused,
+ const char *obj_name __maybe_unused,
+ uint64_t start __maybe_unused,
+ uint64_t end __maybe_unused)
+{
+#if defined(HAVE_LIBUNWIND_LOONGARCH_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_LOONGARCH)
+ unw_dyn_info_t di = {
+ .format = UNW_INFO_FORMAT_REMOTE_TABLE,
+ .start_ip = _di->start_ip,
+ .end_ip = _di->end_ip,
+ .u = {
+ .rti = {
+ .segbase = _di->segbase,
+ .table_data = _di->table_data,
+ .table_len = _di->len,
+ },
+ },
+ };
+ int ret = dwarf_find_debug_frame(found, &di, ip, segvase, obj_name, start, end);
+
+ _di->start_ip = di.start_ip;
+ _di->end_ip = di.end_ip;
+ _di->segbase = di.u.rti.segbase;
+ _di->table_data = di.u.rti.table_data;
+ _di->table_len = di.u.rti.table_len;
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
+
+struct unwind_info *__libunwind_arch_unwind_info__new_loongarch(struct thread *thread __maybe_unused,
+ struct perf_sample *sample __maybe_unused,
+ int max_stack __maybe_unused,
+ bool best_effort __maybe_unused,
+ uint64_t first_ip __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_LOONGARCH_SUPPORT
+ struct x86_64_unwind_info {
+ struct unwind_info ui;
+ unw_cursor_t _cursor;
+ uint64_t _ips[];
+ };
+
+ struct maps *maps = thread__maps(thread);
+ void *addr_space = maps__addr_space(maps);
+ struct x86_64_unwind_info *ui;
+ int ret;
+
+ if (addr_space == NULL)
+ return NULL;
+
+ ui = zalloc(sizeof(*ui) + sizeof(ui->_ips[0]) * max_stack);
+ if (!ui)
+ return NULL;
+
+ ui->ui.machine = maps__machine(maps);
+ ui->ui.thread = thread;
+ ui->ui.sample = sample;
+ ui->ui.cursor = &ui->_cursor;
+ ui->ui.ips = &ui->_ips[0];
+ ui->ui.ips[0] = first_ip;
+ ui->ui.cur_ip = 1;
+ ui->ui.max_ips = max_stack;
+ ui->ui.unw_word_t_size = sizeof(unw_word_t);
+ ui->ui.e_machine = EM_LOONGARCH;
+ ui->ui.best_effort = best_effort;
+
+ ret = unw_init_remote(&ui->_cursor, addr_space, &ui->ui);
+ if (ret) {
+ if (!best_effort)
+ pr_err("libunwind: %s\n", unw_strerror(ret));
+ free(ui);
+ return NULL;
+ }
+
+ return &ui->ui;
+#else
+ return NULL;
+#endif
+}
+
+int __libunwind_arch__unwind_step_loongarch(struct unwind_info *ui __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_LOONGARCH_SUPPORT
+ int ret;
+
+ if (ui->cur_ip >= ui->max_ips)
+ return -1;
+
+ ret = unw_step(ui->cursor);
+ if (ret > 0) {
+ uint64_t ip;
+
+ unw_get_reg(ui->cursor, UNW_REG_IP, &ip);
+
+ if (unw_is_signal_frame(ui->cursor) <= 0) {
+ /*
+ * Decrement the IP for any non-activation frames. This
+ * is required to properly find the srcline for caller
+ * frames. See also the documentation for
+ * dwfl_frame_pc(), which this code tries to replicate.
+ */
+ --ip;
+ }
+ ui->ips[ui->cur_ip++] = ip;
+ }
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
diff --git a/tools/perf/util/libunwind-arch/libunwind-mips.c b/tools/perf/util/libunwind-arch/libunwind-mips.c
index 1fa81742ff4a..5136dcc81964 100644
--- a/tools/perf/util/libunwind-arch/libunwind-mips.c
+++ b/tools/perf/util/libunwind-arch/libunwind-mips.c
@@ -2,8 +2,12 @@
#include "libunwind-arch.h"
#include "../debug.h"
#include "../maps.h"
+#include "../thread.h"
#include "../../../arch/mips/include/uapi/asm/perf_regs.h"
#include <linux/compiler.h>
+#include <linux/kernel.h>
+#include <linux/zalloc.h>
+#include <elf.h>
#include <errno.h>
#ifdef HAVE_LIBUNWIND_MIPS_SUPPORT
@@ -42,3 +46,254 @@ void __libunwind_arch__finish_access_mips(struct maps *maps __maybe_unused)
unw_destroy_addr_space(maps__addr_space(maps));
#endif
}
+
+#ifdef HAVE_LIBUNWIND_MIPS_SUPPORT
+static int find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
+ int need_unwind_info, void *arg)
+{
+ return __libunwind__find_proc_info(as, ip, pi, need_unwind_info, arg, sizeof(unw_word_t));
+}
+
+static void put_unwind_info(unw_addr_space_t __maybe_unused as,
+ unw_proc_info_t *pi __maybe_unused,
+ void *arg __maybe_unused)
+{
+ pr_debug("unwind: put_unwind_info called\n");
+}
+
+static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as,
+ unw_word_t __maybe_unused *dil_addr,
+ void __maybe_unused *arg)
+{
+ return -UNW_ENOINFO;
+}
+
+static int access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *valp,
+ int __write, void *arg)
+{
+ return __libunwind__access_mem(as, addr, valp, __write, arg, sizeof(unw_word_t));
+}
+
+static int access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t *valp,
+ int __write, void *arg)
+{
+ return __libunwind__access_reg(as, regnum, valp, __write, arg, sizeof(unw_word_t));
+}
+
+static int access_fpreg(unw_addr_space_t __maybe_unused as,
+ unw_regnum_t __maybe_unused num,
+ unw_fpreg_t __maybe_unused *val,
+ int __maybe_unused __write,
+ void __maybe_unused *arg)
+{
+ pr_err("unwind: access_fpreg unsupported\n");
+ return -UNW_EINVAL;
+}
+
+static int resume(unw_addr_space_t __maybe_unused as,
+ unw_cursor_t __maybe_unused *cu,
+ void __maybe_unused *arg)
+{
+ pr_err("unwind: resume unsupported\n");
+ return -UNW_EINVAL;
+}
+
+static int get_proc_name(unw_addr_space_t __maybe_unused as,
+ unw_word_t __maybe_unused addr,
+ char __maybe_unused *bufp, size_t __maybe_unused buf_len,
+ unw_word_t __maybe_unused *offp, void __maybe_unused *arg)
+{
+ pr_err("unwind: get_proc_name unsupported\n");
+ return -UNW_EINVAL;
+}
+#endif
+
+void *__libunwind_arch__create_addr_space_mips(void)
+{
+#ifdef HAVE_LIBUNWIND_MIPS_SUPPORT
+ static unw_accessors_t accessors = {
+ .find_proc_info = find_proc_info,
+ .put_unwind_info = put_unwind_info,
+ .get_dyn_info_list_addr = get_dyn_info_list_addr,
+ .access_mem = access_mem,
+ .access_reg = access_reg,
+ .access_fpreg = access_fpreg,
+ .resume = resume,
+ .get_proc_name = get_proc_name,
+ };
+ unw_addr_space_t addr_space;
+
+ addr_space = unw_create_addr_space(&accessors, /*byte_order=*/0);
+ unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL);
+ return addr_space;
+#else
+ return NULL;
+#endif
+}
+
+#ifdef HAVE_LIBUNWIND_MIPS_SUPPORT
+extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,
+ unw_word_t ip,
+ unw_dyn_info_t *di,
+ unw_proc_info_t *pi,
+ int need_unwind_info, void *arg);
+#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table)
+#endif
+
+int __libunwind_arch__dwarf_search_unwind_table_mips(void *as __maybe_unused,
+ uint64_t ip __maybe_unused,
+ struct libarch_unwind__dyn_info *_di __maybe_unused,
+ void *pi __maybe_unused,
+ int need_unwind_info __maybe_unused,
+ void *arg __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_MIPS_SUPPORT
+ unw_dyn_info_t di = {
+ .format = UNW_INFO_FORMAT_REMOTE_TABLE,
+ .start_ip = _di->start_ip,
+ .end_ip = _di->end_ip,
+ .u = {
+ .rti = {
+ .segbase = _di->segbase,
+ .table_data = _di->table_data,
+ .table_len = _di->table_len,
+ },
+ },
+ };
+ int ret = dwarf_search_unwind_table(as, ip, &di, pi, need_unwind_info, arg);
+
+ _di->start_ip = di.start_ip;
+ _di->end_ip = di.end_ip;
+ _di->segbase = di.u.rti.segbase;
+ _di->table_data = di.u.rti.table_data;
+ _di->table_len = di.u.rti.table_len;
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
+
+#if defined(HAVE_LIBUNWIND_MIPS_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_MIPS)
+extern int UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug,
+ unw_word_t ip,
+ unw_word_t segbase,
+ const char *obj_name, unw_word_t start,
+ unw_word_t end);
+#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame)
+#endif
+
+int __libunwind_arch__dwarf_find_debug_frame_mips(int found __maybe_unused,
+ struct libarch_unwind__dyn_info *_di __maybe_unused,
+ uint64_t ip __maybe_unused,
+ uint64_t segbase __maybe_unused,
+ const char *obj_name __maybe_unused,
+ uint64_t start __maybe_unused,
+ uint64_t end __maybe_unused)
+{
+#if defined(HAVE_LIBUNWIND_MIPS_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_MIPS)
+ unw_dyn_info_t di = {
+ .format = UNW_INFO_FORMAT_REMOTE_TABLE,
+ .start_ip = _di->start_ip,
+ .end_ip = _di->end_ip,
+ .u = {
+ .rti = {
+ .segbase = _di->segbase,
+ .table_data = _di->table_data,
+ .table_len = _di->len,
+ },
+ },
+ };
+ int ret = dwarf_find_debug_frame(found, &di, ip, segvase, obj_name, start, end);
+
+ _di->start_ip = di.start_ip;
+ _di->end_ip = di.end_ip;
+ _di->segbase = di.u.rti.segbase;
+ _di->table_data = di.u.rti.table_data;
+ _di->table_len = di.u.rti.table_len;
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
+
+struct unwind_info *__libunwind_arch_unwind_info__new_mips(struct thread *thread __maybe_unused,
+ struct perf_sample *sample __maybe_unused,
+ int max_stack __maybe_unused,
+ bool best_effort __maybe_unused,
+ uint64_t first_ip __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_MIPS_SUPPORT
+ struct x86_64_unwind_info {
+ struct unwind_info ui;
+ unw_cursor_t _cursor;
+ uint64_t _ips[];
+ };
+
+ struct maps *maps = thread__maps(thread);
+ void *addr_space = maps__addr_space(maps);
+ struct x86_64_unwind_info *ui;
+ int ret;
+
+ if (addr_space == NULL)
+ return NULL;
+
+ ui = zalloc(sizeof(*ui) + sizeof(ui->_ips[0]) * max_stack);
+ if (!ui)
+ return NULL;
+
+ ui->ui.machine = maps__machine(maps);
+ ui->ui.thread = thread;
+ ui->ui.sample = sample;
+ ui->ui.cursor = &ui->_cursor;
+ ui->ui.ips = &ui->_ips[0];
+ ui->ui.ips[0] = first_ip;
+ ui->ui.cur_ip = 1;
+ ui->ui.max_ips = max_stack;
+ ui->ui.unw_word_t_size = sizeof(unw_word_t);
+ ui->ui.e_machine = EM_MIPS;
+ ui->ui.best_effort = best_effort;
+
+ ret = unw_init_remote(&ui->_cursor, addr_space, &ui->ui);
+ if (ret) {
+ if (!best_effort)
+ pr_err("libunwind: %s\n", unw_strerror(ret));
+ free(ui);
+ return NULL;
+ }
+
+ return &ui->ui;
+#else
+ return NULL;
+#endif
+}
+
+int __libunwind_arch__unwind_step_mips(struct unwind_info *ui __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_MIPS_SUPPORT
+ int ret;
+
+ if (ui->cur_ip >= ui->max_ips)
+ return -1;
+
+ ret = unw_step(ui->cursor);
+ if (ret > 0) {
+ uint64_t ip;
+
+ unw_get_reg(ui->cursor, UNW_REG_IP, &ip);
+
+ if (unw_is_signal_frame(ui->cursor) <= 0) {
+ /*
+ * Decrement the IP for any non-activation frames. This
+ * is required to properly find the srcline for caller
+ * frames. See also the documentation for
+ * dwfl_frame_pc(), which this code tries to replicate.
+ */
+ --ip;
+ }
+ ui->ips[ui->cur_ip++] = ip;
+ }
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
diff --git a/tools/perf/util/libunwind-arch/libunwind-ppc32.c b/tools/perf/util/libunwind-arch/libunwind-ppc32.c
index fa8471c74bf3..36e8036f7a3f 100644
--- a/tools/perf/util/libunwind-arch/libunwind-ppc32.c
+++ b/tools/perf/util/libunwind-arch/libunwind-ppc32.c
@@ -2,8 +2,12 @@
#include "libunwind-arch.h"
#include "../debug.h"
#include "../maps.h"
+#include "../thread.h"
#include "../../../arch/powerpc/include/uapi/asm/perf_regs.h"
#include <linux/compiler.h>
+#include <linux/kernel.h>
+#include <linux/zalloc.h>
+#include <elf.h>
#include <errno.h>
#ifdef HAVE_LIBUNWIND_PPC32_SUPPORT
@@ -44,3 +48,254 @@ void __libunwind_arch__finish_access_ppc32(struct maps *maps __maybe_unused)
unw_destroy_addr_space(maps__addr_space(maps));
#endif
}
+
+#ifdef HAVE_LIBUNWIND_PPC32_SUPPORT
+static int find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
+ int need_unwind_info, void *arg)
+{
+ return __libunwind__find_proc_info(as, ip, pi, need_unwind_info, arg, sizeof(unw_word_t));
+}
+
+static void put_unwind_info(unw_addr_space_t __maybe_unused as,
+ unw_proc_info_t *pi __maybe_unused,
+ void *arg __maybe_unused)
+{
+ pr_debug("unwind: put_unwind_info called\n");
+}
+
+static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as,
+ unw_word_t __maybe_unused *dil_addr,
+ void __maybe_unused *arg)
+{
+ return -UNW_ENOINFO;
+}
+
+static int access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *valp,
+ int __write, void *arg)
+{
+ return __libunwind__access_mem(as, addr, valp, __write, arg, sizeof(unw_word_t));
+}
+
+static int access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t *valp,
+ int __write, void *arg)
+{
+ return __libunwind__access_reg(as, regnum, valp, __write, arg, sizeof(unw_word_t));
+}
+
+static int access_fpreg(unw_addr_space_t __maybe_unused as,
+ unw_regnum_t __maybe_unused num,
+ unw_fpreg_t __maybe_unused *val,
+ int __maybe_unused __write,
+ void __maybe_unused *arg)
+{
+ pr_err("unwind: access_fpreg unsupported\n");
+ return -UNW_EINVAL;
+}
+
+static int resume(unw_addr_space_t __maybe_unused as,
+ unw_cursor_t __maybe_unused *cu,
+ void __maybe_unused *arg)
+{
+ pr_err("unwind: resume unsupported\n");
+ return -UNW_EINVAL;
+}
+
+static int get_proc_name(unw_addr_space_t __maybe_unused as,
+ unw_word_t __maybe_unused addr,
+ char __maybe_unused *bufp, size_t __maybe_unused buf_len,
+ unw_word_t __maybe_unused *offp, void __maybe_unused *arg)
+{
+ pr_err("unwind: get_proc_name unsupported\n");
+ return -UNW_EINVAL;
+}
+#endif
+
+void *__libunwind_arch__create_addr_space_ppc32(void)
+{
+#ifdef HAVE_LIBUNWIND_PPC32_SUPPORT
+ static unw_accessors_t accessors = {
+ .find_proc_info = find_proc_info,
+ .put_unwind_info = put_unwind_info,
+ .get_dyn_info_list_addr = get_dyn_info_list_addr,
+ .access_mem = access_mem,
+ .access_reg = access_reg,
+ .access_fpreg = access_fpreg,
+ .resume = resume,
+ .get_proc_name = get_proc_name,
+ };
+ unw_addr_space_t addr_space;
+
+ addr_space = unw_create_addr_space(&accessors, /*byte_order=*/0);
+ unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL);
+ return addr_space;
+#else
+ return NULL;
+#endif
+}
+
+#ifdef HAVE_LIBUNWIND_PPC32_SUPPORT
+extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,
+ unw_word_t ip,
+ unw_dyn_info_t *di,
+ unw_proc_info_t *pi,
+ int need_unwind_info, void *arg);
+#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table)
+#endif
+
+int __libunwind_arch__dwarf_search_unwind_table_ppc32(void *as __maybe_unused,
+ uint64_t ip __maybe_unused,
+ struct libarch_unwind__dyn_info *_di __maybe_unused,
+ void *pi __maybe_unused,
+ int need_unwind_info __maybe_unused,
+ void *arg __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_PPC32_SUPPORT
+ unw_dyn_info_t di = {
+ .format = UNW_INFO_FORMAT_REMOTE_TABLE,
+ .start_ip = _di->start_ip,
+ .end_ip = _di->end_ip,
+ .u = {
+ .rti = {
+ .segbase = _di->segbase,
+ .table_data = _di->table_data,
+ .table_len = _di->table_len,
+ },
+ },
+ };
+ int ret = dwarf_search_unwind_table(as, ip, &di, pi, need_unwind_info, arg);
+
+ _di->start_ip = di.start_ip;
+ _di->end_ip = di.end_ip;
+ _di->segbase = di.u.rti.segbase;
+ _di->table_data = di.u.rti.table_data;
+ _di->table_len = di.u.rti.table_len;
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
+
+#if defined(HAVE_LIBUNWIND_PPC32_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_PPC32)
+extern int UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug,
+ unw_word_t ip,
+ unw_word_t segbase,
+ const char *obj_name, unw_word_t start,
+ unw_word_t end);
+#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame)
+#endif
+
+int __libunwind_arch__dwarf_find_debug_frame_ppc32(int found __maybe_unused,
+ struct libarch_unwind__dyn_info *_di __maybe_unused,
+ uint64_t ip __maybe_unused,
+ uint64_t segbase __maybe_unused,
+ const char *obj_name __maybe_unused,
+ uint64_t start __maybe_unused,
+ uint64_t end __maybe_unused)
+{
+#if defined(HAVE_LIBUNWIND_PPC32_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_PPC32)
+ unw_dyn_info_t di = {
+ .format = UNW_INFO_FORMAT_REMOTE_TABLE,
+ .start_ip = _di->start_ip,
+ .end_ip = _di->end_ip,
+ .u = {
+ .rti = {
+ .segbase = _di->segbase,
+ .table_data = _di->table_data,
+ .table_len = _di->len,
+ },
+ },
+ };
+ int ret = dwarf_find_debug_frame(found, &di, ip, segvase, obj_name, start, end);
+
+ _di->start_ip = di.start_ip;
+ _di->end_ip = di.end_ip;
+ _di->segbase = di.u.rti.segbase;
+ _di->table_data = di.u.rti.table_data;
+ _di->table_len = di.u.rti.table_len;
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
+
+struct unwind_info *__libunwind_arch_unwind_info__new_ppc32(struct thread *thread __maybe_unused,
+ struct perf_sample *sample __maybe_unused,
+ int max_stack __maybe_unused,
+ bool best_effort __maybe_unused,
+ uint64_t first_ip __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_PPC32_SUPPORT
+ struct x86_64_unwind_info {
+ struct unwind_info ui;
+ unw_cursor_t _cursor;
+ uint64_t _ips[];
+ };
+
+ struct maps *maps = thread__maps(thread);
+ void *addr_space = maps__addr_space(maps);
+ struct x86_64_unwind_info *ui;
+ int ret;
+
+ if (addr_space == NULL)
+ return NULL;
+
+ ui = zalloc(sizeof(*ui) + sizeof(ui->_ips[0]) * max_stack);
+ if (!ui)
+ return NULL;
+
+ ui->ui.machine = maps__machine(maps);
+ ui->ui.thread = thread;
+ ui->ui.sample = sample;
+ ui->ui.cursor = &ui->_cursor;
+ ui->ui.ips = &ui->_ips[0];
+ ui->ui.ips[0] = first_ip;
+ ui->ui.cur_ip = 1;
+ ui->ui.max_ips = max_stack;
+ ui->ui.unw_word_t_size = sizeof(unw_word_t);
+ ui->ui.e_machine = EM_PPC32;
+ ui->ui.best_effort = best_effort;
+
+ ret = unw_init_remote(&ui->_cursor, addr_space, &ui->ui);
+ if (ret) {
+ if (!best_effort)
+ pr_err("libunwind: %s\n", unw_strerror(ret));
+ free(ui);
+ return NULL;
+ }
+
+ return &ui->ui;
+#else
+ return NULL;
+#endif
+}
+
+int __libunwind_arch__unwind_step_ppc32(struct unwind_info *ui __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_PPC32_SUPPORT
+ int ret;
+
+ if (ui->cur_ip >= ui->max_ips)
+ return -1;
+
+ ret = unw_step(ui->cursor);
+ if (ret > 0) {
+ uint64_t ip;
+
+ unw_get_reg(ui->cursor, UNW_REG_IP, &ip);
+
+ if (unw_is_signal_frame(ui->cursor) <= 0) {
+ /*
+ * Decrement the IP for any non-activation frames. This
+ * is required to properly find the srcline for caller
+ * frames. See also the documentation for
+ * dwfl_frame_pc(), which this code tries to replicate.
+ */
+ --ip;
+ }
+ ui->ips[ui->cur_ip++] = ip;
+ }
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
diff --git a/tools/perf/util/libunwind-arch/libunwind-ppc64.c b/tools/perf/util/libunwind-arch/libunwind-ppc64.c
index 2f746e347336..8e59956d105c 100644
--- a/tools/perf/util/libunwind-arch/libunwind-ppc64.c
+++ b/tools/perf/util/libunwind-arch/libunwind-ppc64.c
@@ -2,8 +2,12 @@
#include "libunwind-arch.h"
#include "../debug.h"
#include "../maps.h"
+#include "../thread.h"
#include "../../../arch/powerpc/include/uapi/asm/perf_regs.h"
#include <linux/compiler.h>
+#include <linux/kernel.h>
+#include <linux/zalloc.h>
+#include <elf.h>
#include <errno.h>
#ifdef HAVE_LIBUNWIND_PPC64_SUPPORT
@@ -46,3 +50,254 @@ void __libunwind_arch__finish_access_ppc64(struct maps *maps __maybe_unused)
unw_destroy_addr_space(maps__addr_space(maps));
#endif
}
+
+#ifdef HAVE_LIBUNWIND_PPC64_SUPPORT
+static int find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
+ int need_unwind_info, void *arg)
+{
+ return __libunwind__find_proc_info(as, ip, pi, need_unwind_info, arg, sizeof(unw_word_t));
+}
+
+static void put_unwind_info(unw_addr_space_t __maybe_unused as,
+ unw_proc_info_t *pi __maybe_unused,
+ void *arg __maybe_unused)
+{
+ pr_debug("unwind: put_unwind_info called\n");
+}
+
+static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as,
+ unw_word_t __maybe_unused *dil_addr,
+ void __maybe_unused *arg)
+{
+ return -UNW_ENOINFO;
+}
+
+static int access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *valp,
+ int __write, void *arg)
+{
+ return __libunwind__access_mem(as, addr, valp, __write, arg, sizeof(unw_word_t));
+}
+
+static int access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t *valp,
+ int __write, void *arg)
+{
+ return __libunwind__access_reg(as, regnum, valp, __write, arg, sizeof(unw_word_t));
+}
+
+static int access_fpreg(unw_addr_space_t __maybe_unused as,
+ unw_regnum_t __maybe_unused num,
+ unw_fpreg_t __maybe_unused *val,
+ int __maybe_unused __write,
+ void __maybe_unused *arg)
+{
+ pr_err("unwind: access_fpreg unsupported\n");
+ return -UNW_EINVAL;
+}
+
+static int resume(unw_addr_space_t __maybe_unused as,
+ unw_cursor_t __maybe_unused *cu,
+ void __maybe_unused *arg)
+{
+ pr_err("unwind: resume unsupported\n");
+ return -UNW_EINVAL;
+}
+
+static int get_proc_name(unw_addr_space_t __maybe_unused as,
+ unw_word_t __maybe_unused addr,
+ char __maybe_unused *bufp, size_t __maybe_unused buf_len,
+ unw_word_t __maybe_unused *offp, void __maybe_unused *arg)
+{
+ pr_err("unwind: get_proc_name unsupported\n");
+ return -UNW_EINVAL;
+}
+#endif
+
+void *__libunwind_arch__create_addr_space_ppc64(void)
+{
+#ifdef HAVE_LIBUNWIND_PPC64_SUPPORT
+ static unw_accessors_t accessors = {
+ .find_proc_info = find_proc_info,
+ .put_unwind_info = put_unwind_info,
+ .get_dyn_info_list_addr = get_dyn_info_list_addr,
+ .access_mem = access_mem,
+ .access_reg = access_reg,
+ .access_fpreg = access_fpreg,
+ .resume = resume,
+ .get_proc_name = get_proc_name,
+ };
+ unw_addr_space_t addr_space;
+
+ addr_space = unw_create_addr_space(&accessors, /*byte_order=*/0);
+ unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL);
+ return addr_space;
+#else
+ return NULL;
+#endif
+}
+
+#ifdef HAVE_LIBUNWIND_PPC64_SUPPORT
+extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,
+ unw_word_t ip,
+ unw_dyn_info_t *di,
+ unw_proc_info_t *pi,
+ int need_unwind_info, void *arg);
+#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table)
+#endif
+
+int __libunwind_arch__dwarf_search_unwind_table_ppc64(void *as __maybe_unused,
+ uint64_t ip __maybe_unused,
+ struct libarch_unwind__dyn_info *_di __maybe_unused,
+ void *pi __maybe_unused,
+ int need_unwind_info __maybe_unused,
+ void *arg __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_PPC64_SUPPORT
+ unw_dyn_info_t di = {
+ .format = UNW_INFO_FORMAT_REMOTE_TABLE,
+ .start_ip = _di->start_ip,
+ .end_ip = _di->end_ip,
+ .u = {
+ .rti = {
+ .segbase = _di->segbase,
+ .table_data = _di->table_data,
+ .table_len = _di->table_len,
+ },
+ },
+ };
+ int ret = dwarf_search_unwind_table(as, ip, &di, pi, need_unwind_info, arg);
+
+ _di->start_ip = di.start_ip;
+ _di->end_ip = di.end_ip;
+ _di->segbase = di.u.rti.segbase;
+ _di->table_data = di.u.rti.table_data;
+ _di->table_len = di.u.rti.table_len;
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
+
+#if defined(HAVE_LIBUNWIND_PPC64_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_PPC64)
+extern int UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug,
+ unw_word_t ip,
+ unw_word_t segbase,
+ const char *obj_name, unw_word_t start,
+ unw_word_t end);
+#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame)
+#endif
+
+int __libunwind_arch__dwarf_find_debug_frame_ppc64(int found __maybe_unused,
+ struct libarch_unwind__dyn_info *_di __maybe_unused,
+ uint64_t ip __maybe_unused,
+ uint64_t segbase __maybe_unused,
+ const char *obj_name __maybe_unused,
+ uint64_t start __maybe_unused,
+ uint64_t end __maybe_unused)
+{
+#if defined(HAVE_LIBUNWIND_PPC64_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_PPC64)
+ unw_dyn_info_t di = {
+ .format = UNW_INFO_FORMAT_REMOTE_TABLE,
+ .start_ip = _di->start_ip,
+ .end_ip = _di->end_ip,
+ .u = {
+ .rti = {
+ .segbase = _di->segbase,
+ .table_data = _di->table_data,
+ .table_len = _di->len,
+ },
+ },
+ };
+ int ret = dwarf_find_debug_frame(found, &di, ip, segvase, obj_name, start, end);
+
+ _di->start_ip = di.start_ip;
+ _di->end_ip = di.end_ip;
+ _di->segbase = di.u.rti.segbase;
+ _di->table_data = di.u.rti.table_data;
+ _di->table_len = di.u.rti.table_len;
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
+
+struct unwind_info *__libunwind_arch_unwind_info__new_ppc64(struct thread *thread __maybe_unused,
+ struct perf_sample *sample __maybe_unused,
+ int max_stack __maybe_unused,
+ bool best_effort __maybe_unused,
+ uint64_t first_ip __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_PPC64_SUPPORT
+ struct x86_64_unwind_info {
+ struct unwind_info ui;
+ unw_cursor_t _cursor;
+ uint64_t _ips[];
+ };
+
+ struct maps *maps = thread__maps(thread);
+ void *addr_space = maps__addr_space(maps);
+ struct x86_64_unwind_info *ui;
+ int ret;
+
+ if (addr_space == NULL)
+ return NULL;
+
+ ui = zalloc(sizeof(*ui) + sizeof(ui->_ips[0]) * max_stack);
+ if (!ui)
+ return NULL;
+
+ ui->ui.machine = maps__machine(maps);
+ ui->ui.thread = thread;
+ ui->ui.sample = sample;
+ ui->ui.cursor = &ui->_cursor;
+ ui->ui.ips = &ui->_ips[0];
+ ui->ui.ips[0] = first_ip;
+ ui->ui.cur_ip = 1;
+ ui->ui.max_ips = max_stack;
+ ui->ui.unw_word_t_size = sizeof(unw_word_t);
+ ui->ui.e_machine = EM_PPC64;
+ ui->ui.best_effort = best_effort;
+
+ ret = unw_init_remote(&ui->_cursor, addr_space, &ui->ui);
+ if (ret) {
+ if (!best_effort)
+ pr_err("libunwind: %s\n", unw_strerror(ret));
+ free(ui);
+ return NULL;
+ }
+
+ return &ui->ui;
+#else
+ return NULL;
+#endif
+}
+
+int __libunwind_arch__unwind_step_ppc64(struct unwind_info *ui __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_PPC64_SUPPORT
+ int ret;
+
+ if (ui->cur_ip >= ui->max_ips)
+ return -1;
+
+ ret = unw_step(ui->cursor);
+ if (ret > 0) {
+ uint64_t ip;
+
+ unw_get_reg(ui->cursor, UNW_REG_IP, &ip);
+
+ if (unw_is_signal_frame(ui->cursor) <= 0) {
+ /*
+ * Decrement the IP for any non-activation frames. This
+ * is required to properly find the srcline for caller
+ * frames. See also the documentation for
+ * dwfl_frame_pc(), which this code tries to replicate.
+ */
+ --ip;
+ }
+ ui->ips[ui->cur_ip++] = ip;
+ }
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
diff --git a/tools/perf/util/libunwind-arch/libunwind-s390.c b/tools/perf/util/libunwind-arch/libunwind-s390.c
index 9f68d15438b2..320d1b8a11bc 100644
--- a/tools/perf/util/libunwind-arch/libunwind-s390.c
+++ b/tools/perf/util/libunwind-arch/libunwind-s390.c
@@ -2,8 +2,12 @@
#include "libunwind-arch.h"
#include "../debug.h"
#include "../maps.h"
+#include "../thread.h"
#include "../../../arch/s390/include/uapi/asm/perf_regs.h"
#include <linux/compiler.h>
+#include <linux/kernel.h>
+#include <linux/zalloc.h>
+#include <elf.h>
#include <errno.h>
#ifdef HAVE_LIBUNWIND_S390X_SUPPORT
@@ -42,3 +46,254 @@ void __libunwind_arch__finish_access_s390(struct maps *maps __maybe_unused)
unw_destroy_addr_space(maps__addr_space(maps));
#endif
}
+
+#ifdef HAVE_LIBUNWIND_S390X_SUPPORT
+static int find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
+ int need_unwind_info, void *arg)
+{
+ return __libunwind__find_proc_info(as, ip, pi, need_unwind_info, arg, sizeof(unw_word_t));
+}
+
+static void put_unwind_info(unw_addr_space_t __maybe_unused as,
+ unw_proc_info_t *pi __maybe_unused,
+ void *arg __maybe_unused)
+{
+ pr_debug("unwind: put_unwind_info called\n");
+}
+
+static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as,
+ unw_word_t __maybe_unused *dil_addr,
+ void __maybe_unused *arg)
+{
+ return -UNW_ENOINFO;
+}
+
+static int access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *valp,
+ int __write, void *arg)
+{
+ return __libunwind__access_mem(as, addr, valp, __write, arg, sizeof(unw_word_t));
+}
+
+static int access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t *valp,
+ int __write, void *arg)
+{
+ return __libunwind__access_reg(as, regnum, valp, __write, arg, sizeof(unw_word_t));
+}
+
+static int access_fpreg(unw_addr_space_t __maybe_unused as,
+ unw_regnum_t __maybe_unused num,
+ unw_fpreg_t __maybe_unused *val,
+ int __maybe_unused __write,
+ void __maybe_unused *arg)
+{
+ pr_err("unwind: access_fpreg unsupported\n");
+ return -UNW_EINVAL;
+}
+
+static int resume(unw_addr_space_t __maybe_unused as,
+ unw_cursor_t __maybe_unused *cu,
+ void __maybe_unused *arg)
+{
+ pr_err("unwind: resume unsupported\n");
+ return -UNW_EINVAL;
+}
+
+static int get_proc_name(unw_addr_space_t __maybe_unused as,
+ unw_word_t __maybe_unused addr,
+ char __maybe_unused *bufp, size_t __maybe_unused buf_len,
+ unw_word_t __maybe_unused *offp, void __maybe_unused *arg)
+{
+ pr_err("unwind: get_proc_name unsupported\n");
+ return -UNW_EINVAL;
+}
+#endif
+
+void *__libunwind_arch__create_addr_space_s390(void)
+{
+#ifdef HAVE_LIBUNWIND_S390X_SUPPORT
+ static unw_accessors_t accessors = {
+ .find_proc_info = find_proc_info,
+ .put_unwind_info = put_unwind_info,
+ .get_dyn_info_list_addr = get_dyn_info_list_addr,
+ .access_mem = access_mem,
+ .access_reg = access_reg,
+ .access_fpreg = access_fpreg,
+ .resume = resume,
+ .get_proc_name = get_proc_name,
+ };
+ unw_addr_space_t addr_space;
+
+ addr_space = unw_create_addr_space(&accessors, /*byte_order=*/0);
+ unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL);
+ return addr_space;
+#else
+ return NULL;
+#endif
+}
+
+#ifdef HAVE_LIBUNWIND_S390X_SUPPORT
+extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,
+ unw_word_t ip,
+ unw_dyn_info_t *di,
+ unw_proc_info_t *pi,
+ int need_unwind_info, void *arg);
+#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table)
+#endif
+
+int __libunwind_arch__dwarf_search_unwind_table_s390(void *as __maybe_unused,
+ uint64_t ip __maybe_unused,
+ struct libarch_unwind__dyn_info *_di __maybe_unused,
+ void *pi __maybe_unused,
+ int need_unwind_info __maybe_unused,
+ void *arg __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_S390X_SUPPORT
+ unw_dyn_info_t di = {
+ .format = UNW_INFO_FORMAT_REMOTE_TABLE,
+ .start_ip = _di->start_ip,
+ .end_ip = _di->end_ip,
+ .u = {
+ .rti = {
+ .segbase = _di->segbase,
+ .table_data = _di->table_data,
+ .table_len = _di->table_len,
+ },
+ },
+ };
+ int ret = dwarf_search_unwind_table(as, ip, &di, pi, need_unwind_info, arg);
+
+ _di->start_ip = di.start_ip;
+ _di->end_ip = di.end_ip;
+ _di->segbase = di.u.rti.segbase;
+ _di->table_data = di.u.rti.table_data;
+ _di->table_len = di.u.rti.table_len;
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
+
+#if defined(HAVE_LIBUNWIND_S390X_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_S390X)
+extern int UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug,
+ unw_word_t ip,
+ unw_word_t segbase,
+ const char *obj_name, unw_word_t start,
+ unw_word_t end);
+#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame)
+#endif
+
+int __libunwind_arch__dwarf_find_debug_frame_s390(int found __maybe_unused,
+ struct libarch_unwind__dyn_info *_di __maybe_unused,
+ uint64_t ip __maybe_unused,
+ uint64_t segbase __maybe_unused,
+ const char *obj_name __maybe_unused,
+ uint64_t start __maybe_unused,
+ uint64_t end __maybe_unused)
+{
+#if defined(HAVE_LIBUNWIND_S390X_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_S390X)
+ unw_dyn_info_t di = {
+ .format = UNW_INFO_FORMAT_REMOTE_TABLE,
+ .start_ip = _di->start_ip,
+ .end_ip = _di->end_ip,
+ .u = {
+ .rti = {
+ .segbase = _di->segbase,
+ .table_data = _di->table_data,
+ .table_len = _di->len,
+ },
+ },
+ };
+ int ret = dwarf_find_debug_frame(found, &di, ip, segvase, obj_name, start, end);
+
+ _di->start_ip = di.start_ip;
+ _di->end_ip = di.end_ip;
+ _di->segbase = di.u.rti.segbase;
+ _di->table_data = di.u.rti.table_data;
+ _di->table_len = di.u.rti.table_len;
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
+
+struct unwind_info *__libunwind_arch_unwind_info__new_s390(struct thread *thread __maybe_unused,
+ struct perf_sample *sample __maybe_unused,
+ int max_stack __maybe_unused,
+ bool best_effort __maybe_unused,
+ uint64_t first_ip __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_S390X_SUPPORT
+ struct x86_64_unwind_info {
+ struct unwind_info ui;
+ unw_cursor_t _cursor;
+ uint64_t _ips[];
+ };
+
+ struct maps *maps = thread__maps(thread);
+ void *addr_space = maps__addr_space(maps);
+ struct x86_64_unwind_info *ui;
+ int ret;
+
+ if (addr_space == NULL)
+ return NULL;
+
+ ui = zalloc(sizeof(*ui) + sizeof(ui->_ips[0]) * max_stack);
+ if (!ui)
+ return NULL;
+
+ ui->ui.machine = maps__machine(maps);
+ ui->ui.thread = thread;
+ ui->ui.sample = sample;
+ ui->ui.cursor = &ui->_cursor;
+ ui->ui.ips = &ui->_ips[0];
+ ui->ui.ips[0] = first_ip;
+ ui->ui.cur_ip = 1;
+ ui->ui.max_ips = max_stack;
+ ui->ui.unw_word_t_size = sizeof(unw_word_t);
+ ui->ui.e_machine = EM_S390;
+ ui->ui.best_effort = best_effort;
+
+ ret = unw_init_remote(&ui->_cursor, addr_space, &ui->ui);
+ if (ret) {
+ if (!best_effort)
+ pr_err("libunwind: %s\n", unw_strerror(ret));
+ free(ui);
+ return NULL;
+ }
+
+ return &ui->ui;
+#else
+ return NULL;
+#endif
+}
+
+int __libunwind_arch__unwind_step_s390(struct unwind_info *ui __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_S390X_SUPPORT
+ int ret;
+
+ if (ui->cur_ip >= ui->max_ips)
+ return -1;
+
+ ret = unw_step(ui->cursor);
+ if (ret > 0) {
+ uint64_t ip;
+
+ unw_get_reg(ui->cursor, UNW_REG_IP, &ip);
+
+ if (unw_is_signal_frame(ui->cursor) <= 0) {
+ /*
+ * Decrement the IP for any non-activation frames. This
+ * is required to properly find the srcline for caller
+ * frames. See also the documentation for
+ * dwfl_frame_pc(), which this code tries to replicate.
+ */
+ --ip;
+ }
+ ui->ips[ui->cur_ip++] = ip;
+ }
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
diff --git a/tools/perf/util/libunwind-arch/libunwind-x86_64.c b/tools/perf/util/libunwind-arch/libunwind-x86_64.c
index 25e326bd3e15..4250001d021e 100644
--- a/tools/perf/util/libunwind-arch/libunwind-x86_64.c
+++ b/tools/perf/util/libunwind-arch/libunwind-x86_64.c
@@ -2,9 +2,12 @@
#include "libunwind-arch.h"
#include "../debug.h"
#include "../maps.h"
+#include "../thread.h"
#include "../../../arch/x86/include/uapi/asm/perf_regs.h"
#include <linux/compiler.h>
#include <linux/kernel.h>
+#include <linux/zalloc.h>
+#include <elf.h>
#include <errno.h>
#ifdef HAVE_LIBUNWIND_X86_64_SUPPORT
@@ -65,3 +68,253 @@ void __libunwind_arch__finish_access_x86_64(struct maps *maps __maybe_unused)
unw_destroy_addr_space(maps__addr_space(maps));
#endif
}
+
+#ifdef HAVE_LIBUNWIND_X86_64_SUPPORT
+static int find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
+ int need_unwind_info, void *arg)
+{
+ return __libunwind__find_proc_info(as, ip, pi, need_unwind_info, arg);
+}
+
+static void put_unwind_info(unw_addr_space_t __maybe_unused as,
+ unw_proc_info_t *pi __maybe_unused,
+ void *arg __maybe_unused)
+{
+ pr_debug("unwind: put_unwind_info called\n");
+}
+
+static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as,
+ unw_word_t __maybe_unused *dil_addr,
+ void __maybe_unused *arg)
+{
+ return -UNW_ENOINFO;
+}
+
+static int access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *valp,
+ int __write, void *arg)
+{
+ return __libunwind__access_mem(as, addr, valp, __write, arg);
+}
+
+static int access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t *valp,
+ int __write, void *arg)
+{
+ return __libunwind__access_reg(as, regnum, valp, __write, arg);
+}
+
+static int access_fpreg(unw_addr_space_t __maybe_unused as,
+ unw_regnum_t __maybe_unused num,
+ unw_fpreg_t __maybe_unused *val,
+ int __maybe_unused __write,
+ void __maybe_unused *arg)
+{
+ pr_err("unwind: access_fpreg unsupported\n");
+ return -UNW_EINVAL;
+}
+
+static int resume(unw_addr_space_t __maybe_unused as,
+ unw_cursor_t __maybe_unused *cu,
+ void __maybe_unused *arg)
+{
+ pr_err("unwind: resume unsupported\n");
+ return -UNW_EINVAL;
+}
+
+static int get_proc_name(unw_addr_space_t __maybe_unused as,
+ unw_word_t __maybe_unused addr,
+ char __maybe_unused *bufp, size_t __maybe_unused buf_len,
+ unw_word_t __maybe_unused *offp, void __maybe_unused *arg)
+{
+ pr_err("unwind: get_proc_name unsupported\n");
+ return -UNW_EINVAL;
+}
+#endif
+
+void *__libunwind_arch__create_addr_space_x86_64(void)
+{
+#ifdef HAVE_LIBUNWIND_X86_64_SUPPORT
+ static unw_accessors_t accessors = {
+ .find_proc_info = find_proc_info,
+ .put_unwind_info = put_unwind_info,
+ .get_dyn_info_list_addr = get_dyn_info_list_addr,
+ .access_mem = access_mem,
+ .access_reg = access_reg,
+ .access_fpreg = access_fpreg,
+ .resume = resume,
+ .get_proc_name = get_proc_name,
+ };
+ unw_addr_space_t addr_space;
+
+ addr_space = unw_create_addr_space(&accessors, /*byte_order=*/0);
+ unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL);
+ return addr_space;
+#else
+ return NULL;
+#endif
+}
+
+#ifdef HAVE_LIBUNWIND_X86_64_SUPPORT
+extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,
+ unw_word_t ip,
+ unw_dyn_info_t *di,
+ unw_proc_info_t *pi,
+ int need_unwind_info, void *arg);
+#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table)
+#endif
+
+int __libunwind_arch__dwarf_search_unwind_table_x86_64(void *as __maybe_unused,
+ uint64_t ip __maybe_unused,
+ struct libarch_unwind__dyn_info *_di __maybe_unused,
+ void *pi __maybe_unused,
+ int need_unwind_info __maybe_unused,
+ void *arg __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_X86_64_SUPPORT
+ unw_dyn_info_t di = {
+ .format = UNW_INFO_FORMAT_REMOTE_TABLE,
+ .start_ip = _di->start_ip,
+ .end_ip = _di->end_ip,
+ .u = {
+ .rti = {
+ .segbase = _di->segbase,
+ .table_data = _di->table_data,
+ .table_len = _di->table_len,
+ },
+ },
+ };
+ int ret = dwarf_search_unwind_table(as, ip, &di, pi, need_unwind_info, arg);
+
+ _di->start_ip = di.start_ip;
+ _di->end_ip = di.end_ip;
+ _di->segbase = di.u.rti.segbase;
+ _di->table_data = di.u.rti.table_data;
+ _di->table_len = di.u.rti.table_len;
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
+
+#if defined(HAVE_LIBUNWIND_X86_64_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_X86_64)
+extern int UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug,
+ unw_word_t ip,
+ unw_word_t segbase,
+ const char *obj_name, unw_word_t start,
+ unw_word_t end);
+#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame)
+#endif
+
+int __libunwind_arch__dwarf_find_debug_frame_x86_64(int found __maybe_unused,
+ struct libarch_unwind__dyn_info *_di __maybe_unused,
+ uint64_t ip __maybe_unused,
+ uint64_t segbase __maybe_unused,
+ const char *obj_name __maybe_unused,
+ uint64_t start __maybe_unused,
+ uint64_t end __maybe_unused)
+{
+#if defined(HAVE_LIBUNWIND_X86_64_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_X86_64)
+ unw_dyn_info_t di = {
+ .format = UNW_INFO_FORMAT_REMOTE_TABLE,
+ .start_ip = _di->start_ip,
+ .end_ip = _di->end_ip,
+ .u = {
+ .rti = {
+ .segbase = _di->segbase,
+ .table_data = _di->table_data,
+ .table_len = _di->len,
+ },
+ },
+ };
+ int ret = dwarf_find_debug_frame(found, &di, ip, segvase, obj_name, start, end);
+
+ _di->start_ip = di.start_ip;
+ _di->end_ip = di.end_ip;
+ _di->segbase = di.u.rti.segbase;
+ _di->table_data = di.u.rti.table_data;
+ _di->table_len = di.u.rti.table_len;
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
+
+struct unwind_info *__libunwind_arch_unwind_info__new_x86_64(struct thread *thread __maybe_unused,
+ struct perf_sample *sample __maybe_unused,
+ int max_stack __maybe_unused,
+ bool best_effort __maybe_unused,
+ uint64_t first_ip __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_X86_64_SUPPORT
+ struct x86_64_unwind_info {
+ struct unwind_info ui;
+ unw_cursor_t _cursor;
+ uint64_t _ips[];
+ };
+
+ struct maps *maps = thread__maps(thread);
+ void *addr_space = maps__addr_space(maps);
+ struct x86_64_unwind_info *ui;
+ int ret;
+
+ if (addr_space == NULL)
+ return NULL;
+
+ ui = zalloc(sizeof(*ui) + sizeof(ui->_ips[0]) * max_stack);
+ if (!ui)
+ return NULL;
+
+ ui->ui.machine = maps__machine(maps);
+ ui->ui.thread = thread;
+ ui->ui.sample = sample;
+ ui->ui.cursor = &ui->_cursor;
+ ui->ui.ips = &ui->_ips[0];
+ ui->ui.ips[0] = first_ip;
+ ui->ui.cur_ip = 1;
+ ui->ui.max_ips = max_stack;
+ ui->ui.unw_word_t_size = sizeof(unw_word_t);
+ ui->ui.e_machine = EM_X86_64;
+ ui->ui.best_effort = best_effort;
+
+ ret = unw_init_remote(&ui->_cursor, addr_space, &ui->ui);
+ if (ret) {
+ if (!best_effort)
+ pr_err("libunwind: %s\n", unw_strerror(ret));
+ free(ui);
+ return NULL;
+ }
+ return &ui->ui;
+#else
+ return NULL;
+#endif
+}
+
+int __libunwind_arch__unwind_step_x86_64(struct unwind_info *ui __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_X86_64_SUPPORT
+ int ret;
+
+ if (ui->cur_ip >= ui->max_ips)
+ return 0;
+
+ ret = unw_step(ui->cursor);
+ if (ret > 0) {
+ uint64_t ip;
+
+ unw_get_reg(ui->cursor, UNW_REG_IP, &ip);
+
+ if (unw_is_signal_frame(ui->cursor) <= 0) {
+ /*
+ * Decrement the IP for any non-activation frames. This
+ * is required to properly find the srcline for caller
+ * frames. See also the documentation for
+ * dwfl_frame_pc(), which this code tries to replicate.
+ */
+ --ip;
+ }
+ ui->ips[ui->cur_ip++] = ip;
+ }
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
diff --git a/tools/perf/util/libunwind/arm64.c b/tools/perf/util/libunwind/arm64.c
deleted file mode 100644
index 15670a964495..000000000000
--- a/tools/perf/util/libunwind/arm64.c
+++ /dev/null
@@ -1,35 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * This file setups defines to compile arch specific binary from the
- * generic one.
- *
- * The function 'LIBUNWIND__ARCH_REG_ID' name is set according to arch
- * name and the definition of this function is included directly from
- * 'arch/arm64/util/unwind-libunwind.c', to make sure that this function
- * is defined no matter what arch the host is.
- *
- * Finally, the arch specific unwind methods are exported which will
- * be assigned to each arm64 thread.
- */
-
-#define REMOTE_UNWIND_LIBUNWIND
-
-#include "unwind.h"
-#include "libunwind-aarch64.h"
-#define perf_event_arm_regs perf_event_arm64_regs
-#include <../../../arch/arm64/include/uapi/asm/perf_regs.h>
-#undef perf_event_arm_regs
-#include "../../arch/arm64/util/unwind-libunwind.c"
-
-/* NO_LIBUNWIND_DEBUG_FRAME is a feature flag for local libunwind,
- * assign NO_LIBUNWIND_DEBUG_FRAME_AARCH64 to it for compiling arm64
- * unwind methods.
- */
-#undef NO_LIBUNWIND_DEBUG_FRAME
-#ifdef NO_LIBUNWIND_DEBUG_FRAME_AARCH64
-#define NO_LIBUNWIND_DEBUG_FRAME
-#endif
-#include "util/unwind-libunwind-local.c"
-
-struct unwind_libunwind_ops *
-arm64_unwind_libunwind_ops = &_unwind_libunwind_ops;
diff --git a/tools/perf/util/libunwind/x86_32.c b/tools/perf/util/libunwind/x86_32.c
deleted file mode 100644
index 1e9fb8bfec44..000000000000
--- a/tools/perf/util/libunwind/x86_32.c
+++ /dev/null
@@ -1,29 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * This file setups defines to compile arch specific binary from the
- * generic one.
- *
- * The function 'LIBUNWIND__ARCH_REG_ID' name is set according to arch
- * name and the definition of this function is included directly from
- * 'arch/x86/util/unwind-libunwind.c', to make sure that this function
- * is defined no matter what arch the host is.
- *
- * Finally, the arch specific unwind methods are exported which will
- * be assigned to each x86 thread.
- */
-
-#define REMOTE_UNWIND_LIBUNWIND
-
-#include "unwind.h"
-#include "libunwind-x86.h"
-
-/* Explicitly define NO_LIBUNWIND_DEBUG_FRAME, because non-ARM has no
- * dwarf_find_debug_frame() function.
- */
-#ifndef NO_LIBUNWIND_DEBUG_FRAME
-#define NO_LIBUNWIND_DEBUG_FRAME
-#endif
-#include "util/unwind-libunwind-local.c"
-
-struct unwind_libunwind_ops *
-x86_32_unwind_libunwind_ops = &_unwind_libunwind_ops;
diff --git a/tools/perf/util/maps.c b/tools/perf/util/maps.c
index 8c7b2a1e7642..7ba82024fb11 100644
--- a/tools/perf/util/maps.c
+++ b/tools/perf/util/maps.c
@@ -198,16 +198,6 @@ void maps__set_addr_space(struct maps *maps, void *addr_space)
RC_CHK_ACCESS(maps)->addr_space = addr_space;
}
-const struct unwind_libunwind_ops *maps__unwind_libunwind_ops(const struct maps *maps)
-{
- return RC_CHK_ACCESS(maps)->unwind_libunwind_ops;
-}
-
-void maps__set_unwind_libunwind_ops(struct maps *maps, const struct unwind_libunwind_ops *ops)
-{
- RC_CHK_ACCESS(maps)->unwind_libunwind_ops = ops;
-}
-
uint16_t maps__e_machine(const struct maps *maps)
{
return RC_CHK_ACCESS(maps)->e_machine;
diff --git a/tools/perf/util/maps.h b/tools/perf/util/maps.h
index 6469f62c41a8..5b80b199685e 100644
--- a/tools/perf/util/maps.h
+++ b/tools/perf/util/maps.h
@@ -49,8 +49,6 @@ refcount_t *maps__refcnt(struct maps *maps); /* Test only. */
#ifdef HAVE_LIBUNWIND_SUPPORT
void *maps__addr_space(const struct maps *maps);
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);
uint16_t maps__e_machine(const struct maps *maps);
void maps__set_e_machine(struct maps *maps, uint16_t e_machine);
#endif
diff --git a/tools/perf/util/unwind-libunwind-local.c b/tools/perf/util/unwind-libunwind-local.c
deleted file mode 100644
index 8f0128ba05a7..000000000000
--- a/tools/perf/util/unwind-libunwind-local.c
+++ /dev/null
@@ -1,820 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Post mortem Dwarf CFI based unwinding on top of regs and stack dumps.
- *
- * Lots of this code have been borrowed or heavily inspired from parts of
- * the libunwind 0.99 code which are (amongst other contributors I may have
- * forgotten):
- *
- * Copyright (C) 2002-2007 Hewlett-Packard Co
- * Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
- *
- * And the bugs have been added by:
- *
- * Copyright (C) 2010, Frederic Weisbecker <fweisbec@gmail.com>
- * Copyright (C) 2012, Jiri Olsa <jolsa@redhat.com>
- *
- */
-
-#include <elf.h>
-#include <errno.h>
-#include <gelf.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <string.h>
-#include <unistd.h>
-#include <sys/mman.h>
-#include <linux/list.h>
-#include <linux/zalloc.h>
-#ifndef REMOTE_UNWIND_LIBUNWIND
-#include <libunwind.h>
-#include <libunwind-ptrace.h>
-#endif
-#include "callchain.h"
-#include "thread.h"
-#include "session.h"
-#include "perf_regs.h"
-#include "unwind.h"
-#include "map.h"
-#include "symbol.h"
-#include "debug.h"
-#include "asm/bug.h"
-#include "dso.h"
-#include "libunwind-arch/libunwind-arch.h"
-
-extern int
-UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,
- unw_word_t ip,
- unw_dyn_info_t *di,
- unw_proc_info_t *pi,
- int need_unwind_info, void *arg);
-
-#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table)
-
-extern int
-UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug,
- unw_word_t ip,
- unw_word_t segbase,
- const char *obj_name, unw_word_t start,
- unw_word_t end);
-
-#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame)
-
-#define DW_EH_PE_FORMAT_MASK 0x0f /* format of the encoded value */
-#define DW_EH_PE_APPL_MASK 0x70 /* how the value is to be applied */
-
-/* Pointer-encoding formats: */
-#define DW_EH_PE_omit 0xff
-#define DW_EH_PE_ptr 0x00 /* pointer-sized unsigned value */
-#define DW_EH_PE_udata4 0x03 /* unsigned 32-bit value */
-#define DW_EH_PE_udata8 0x04 /* unsigned 64-bit value */
-#define DW_EH_PE_sdata4 0x0b /* signed 32-bit value */
-#define DW_EH_PE_sdata8 0x0c /* signed 64-bit value */
-
-/* Pointer-encoding application: */
-#define DW_EH_PE_absptr 0x00 /* absolute value */
-#define DW_EH_PE_pcrel 0x10 /* rel. to addr. of encoded value */
-
-/*
- * The following are not documented by LSB v1.3, yet they are used by
- * GCC, presumably they aren't documented by LSB since they aren't
- * used on Linux:
- */
-#define DW_EH_PE_funcrel 0x40 /* start-of-procedure-relative */
-#define DW_EH_PE_aligned 0x50 /* aligned pointer */
-
-/* Flags intentionally not handled, since they're not needed:
- * #define DW_EH_PE_indirect 0x80
- * #define DW_EH_PE_uleb128 0x01
- * #define DW_EH_PE_udata2 0x02
- * #define DW_EH_PE_sleb128 0x09
- * #define DW_EH_PE_sdata2 0x0a
- * #define DW_EH_PE_textrel 0x20
- * #define DW_EH_PE_datarel 0x30
- */
-
-struct unwind_info {
- struct perf_sample *sample;
- struct machine *machine;
- struct thread *thread;
- uint16_t e_machine;
- bool best_effort;
-};
-
-#define dw_read(ptr, type, end) ({ \
- type *__p = (type *) ptr; \
- type __v; \
- if ((__p + 1) > (type *) end) \
- return -EINVAL; \
- __v = *__p++; \
- ptr = (typeof(ptr)) __p; \
- __v; \
- })
-
-static int __dw_read_encoded_value(u8 **p, u8 *end, u64 *val,
- u8 encoding)
-{
- u8 *cur = *p;
- *val = 0;
-
- switch (encoding) {
- case DW_EH_PE_omit:
- *val = 0;
- goto out;
- case DW_EH_PE_ptr:
- *val = dw_read(cur, unsigned long, end);
- goto out;
- default:
- break;
- }
-
- switch (encoding & DW_EH_PE_APPL_MASK) {
- case DW_EH_PE_absptr:
- break;
- case DW_EH_PE_pcrel:
- *val = (unsigned long) cur;
- break;
- default:
- return -EINVAL;
- }
-
- if ((encoding & 0x07) == 0x00)
- encoding |= DW_EH_PE_udata4;
-
- switch (encoding & DW_EH_PE_FORMAT_MASK) {
- case DW_EH_PE_sdata4:
- *val += dw_read(cur, s32, end);
- break;
- case DW_EH_PE_udata4:
- *val += dw_read(cur, u32, end);
- break;
- case DW_EH_PE_sdata8:
- *val += dw_read(cur, s64, end);
- break;
- case DW_EH_PE_udata8:
- *val += dw_read(cur, u64, end);
- break;
- default:
- return -EINVAL;
- }
-
- out:
- *p = cur;
- return 0;
-}
-
-#define dw_read_encoded_value(ptr, end, enc) ({ \
- u64 __v; \
- if (__dw_read_encoded_value(&ptr, end, &__v, enc)) { \
- return -EINVAL; \
- } \
- __v; \
- })
-
-static int elf_section_address_and_offset(int fd, const char *name, u64 *address, u64 *offset)
-{
- Elf *elf;
- GElf_Ehdr ehdr;
- GElf_Shdr shdr;
- int ret = -1;
-
- elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
- if (elf == NULL)
- return -1;
-
- if (gelf_getehdr(elf, &ehdr) == NULL)
- goto out_err;
-
- if (!elf_section_by_name(elf, &ehdr, &shdr, name, NULL))
- goto out_err;
-
- *address = shdr.sh_addr;
- *offset = shdr.sh_offset;
- ret = 0;
-out_err:
- elf_end(elf);
- return ret;
-}
-
-#ifndef NO_LIBUNWIND_DEBUG_FRAME
-static u64 elf_section_offset(int fd, const char *name)
-{
- u64 address, offset = 0;
-
- if (elf_section_address_and_offset(fd, name, &address, &offset))
- return 0;
-
- return offset;
-}
-#endif
-
-static u64 elf_base_address(int fd)
-{
- Elf *elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
- GElf_Phdr phdr;
- u64 retval = 0;
- size_t i, phdrnum = 0;
-
- if (elf == NULL)
- return 0;
- (void)elf_getphdrnum(elf, &phdrnum);
- /* PT_LOAD segments are sorted by p_vaddr, so the first has the minimum p_vaddr. */
- for (i = 0; i < phdrnum; i++) {
- if (gelf_getphdr(elf, i, &phdr) && phdr.p_type == PT_LOAD) {
- retval = phdr.p_vaddr & -getpagesize();
- break;
- }
- }
-
- elf_end(elf);
- return retval;
-}
-
-#ifndef NO_LIBUNWIND_DEBUG_FRAME
-static int elf_is_exec(int fd, const char *name)
-{
- Elf *elf;
- GElf_Ehdr ehdr;
- int retval = 0;
-
- elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
- if (elf == NULL)
- return 0;
- if (gelf_getehdr(elf, &ehdr) == NULL)
- goto out;
-
- retval = (ehdr.e_type == ET_EXEC);
-
-out:
- elf_end(elf);
- pr_debug("unwind: elf_is_exec(%s): %d\n", name, retval);
- return retval;
-}
-#endif
-
-struct table_entry {
- u32 start_ip_offset;
- u32 fde_offset;
-};
-
-struct eh_frame_hdr {
- unsigned char version;
- unsigned char eh_frame_ptr_enc;
- unsigned char fde_count_enc;
- unsigned char table_enc;
-
- /*
- * The rest of the header is variable-length and consists of the
- * following members:
- *
- * encoded_t eh_frame_ptr;
- * encoded_t fde_count;
- */
-
- /* A single encoded pointer should not be more than 8 bytes. */
- u64 enc[2];
-
- /*
- * struct {
- * encoded_t start_ip;
- * encoded_t fde_addr;
- * } binary_search_table[fde_count];
- */
- char data[];
-} __packed;
-
-static int unwind_spec_ehframe(struct dso *dso, struct machine *machine,
- u64 offset, u64 *table_data_offset, u64 *fde_count)
-{
- struct eh_frame_hdr hdr;
- u8 *enc = (u8 *) &hdr.enc;
- u8 *end = (u8 *) &hdr.data;
- ssize_t r;
-
- r = dso__data_read_offset(dso, machine, offset,
- (u8 *) &hdr, sizeof(hdr));
- if (r != sizeof(hdr))
- return -EINVAL;
-
- /* We dont need eh_frame_ptr, just skip it. */
- dw_read_encoded_value(enc, end, hdr.eh_frame_ptr_enc);
-
- *fde_count = dw_read_encoded_value(enc, end, hdr.fde_count_enc);
- *table_data_offset = enc - (u8 *) &hdr;
- return 0;
-}
-
-struct read_unwind_spec_eh_frame_maps_cb_args {
- struct dso *dso;
- u64 base_addr;
-};
-
-static int read_unwind_spec_eh_frame_maps_cb(struct map *map, void *data)
-{
-
- struct read_unwind_spec_eh_frame_maps_cb_args *args = data;
-
- if (map__dso(map) == args->dso && map__start(map) - map__pgoff(map) < args->base_addr)
- args->base_addr = map__start(map) - map__pgoff(map);
-
- return 0;
-}
-
-
-static int read_unwind_spec_eh_frame(struct dso *dso, struct unwind_info *ui,
- u64 *table_data, u64 *segbase,
- u64 *fde_count)
-{
- struct read_unwind_spec_eh_frame_maps_cb_args args = {
- .dso = dso,
- .base_addr = UINT64_MAX,
- };
- int ret, fd;
-
- if (dso__data(dso)->eh_frame_hdr_offset == 0) {
- if (!dso__data_get_fd(dso, ui->machine, &fd))
- return -EINVAL;
-
- /* Check the .eh_frame section for unwinding info */
- ret = elf_section_address_and_offset(fd, ".eh_frame_hdr",
- &dso__data(dso)->eh_frame_hdr_addr,
- &dso__data(dso)->eh_frame_hdr_offset);
- dso__data(dso)->elf_base_addr = elf_base_address(fd);
- dso__data_put_fd(dso);
- if (ret || dso__data(dso)->eh_frame_hdr_offset == 0)
- return -EINVAL;
- }
-
- maps__for_each_map(thread__maps(ui->thread), read_unwind_spec_eh_frame_maps_cb, &args);
-
- args.base_addr -= dso__data(dso)->elf_base_addr;
- /* Address of .eh_frame_hdr */
- *segbase = args.base_addr + dso__data(dso)->eh_frame_hdr_addr;
- ret = unwind_spec_ehframe(dso, ui->machine, dso__data(dso)->eh_frame_hdr_offset,
- table_data, fde_count);
- if (ret)
- return ret;
- /* binary_search_table offset plus .eh_frame_hdr address */
- *table_data += *segbase;
- return 0;
-}
-
-#ifndef NO_LIBUNWIND_DEBUG_FRAME
-static int read_unwind_spec_debug_frame(struct dso *dso,
- struct machine *machine, u64 *offset)
-{
- int fd;
- u64 ofs = dso__data(dso)->debug_frame_offset;
-
- /* debug_frame can reside in:
- * - dso
- * - debug pointed by symsrc_filename
- * - gnu_debuglink, which doesn't necessary
- * has to be pointed by symsrc_filename
- */
- if (ofs == 0) {
- if (dso__data_get_fd(dso, machine, &fd)) {
- ofs = elf_section_offset(fd, ".debug_frame");
- dso__data_put_fd(dso);
- }
-
- if (ofs <= 0) {
- fd = open(dso__symsrc_filename(dso), O_RDONLY);
- if (fd >= 0) {
- ofs = elf_section_offset(fd, ".debug_frame");
- close(fd);
- }
- }
-
- if (ofs <= 0) {
- char *debuglink = malloc(PATH_MAX);
- int ret = 0;
-
- if (debuglink == NULL) {
- pr_err("unwind: Can't read unwind spec debug frame.\n");
- return -ENOMEM;
- }
-
- ret = dso__read_binary_type_filename(
- dso, DSO_BINARY_TYPE__DEBUGLINK,
- machine->root_dir, debuglink, PATH_MAX);
- if (!ret) {
- fd = open(debuglink, O_RDONLY);
- if (fd >= 0) {
- ofs = elf_section_offset(fd,
- ".debug_frame");
- close(fd);
- }
- }
- if (ofs > 0) {
- if (dso__symsrc_filename(dso) != NULL) {
- pr_warning(
- "%s: overwrite symsrc(%s,%s)\n",
- __func__,
- dso__symsrc_filename(dso),
- debuglink);
- dso__free_symsrc_filename(dso);
- }
- dso__set_symsrc_filename(dso, debuglink);
- } else {
- free(debuglink);
- }
- }
-
- dso__data(dso)->debug_frame_offset = ofs;
- }
-
- *offset = ofs;
- if (*offset)
- return 0;
-
- return -EINVAL;
-}
-#endif
-
-static struct map *find_map(unw_word_t ip, struct unwind_info *ui)
-{
- struct addr_location al;
- struct map *ret;
-
- addr_location__init(&al);
- thread__find_map(ui->thread, PERF_RECORD_MISC_USER, ip, &al);
- ret = map__get(al.map);
- addr_location__exit(&al);
- return ret;
-}
-
-static int
-find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
- int need_unwind_info, void *arg)
-{
- struct unwind_info *ui = arg;
- struct map *map;
- struct dso *dso;
- unw_dyn_info_t di;
- u64 table_data, segbase, fde_count;
- int ret = -EINVAL;
-
- map = find_map(ip, ui);
- if (!map)
- return -EINVAL;
-
- dso = map__dso(map);
- if (!dso) {
- map__put(map);
- return -EINVAL;
- }
-
- pr_debug("unwind: find_proc_info dso %s\n", dso__name(dso));
-
- /* Check the .eh_frame section for unwinding info */
- if (!read_unwind_spec_eh_frame(dso, ui, &table_data, &segbase, &fde_count)) {
- memset(&di, 0, sizeof(di));
- di.format = UNW_INFO_FORMAT_REMOTE_TABLE;
- di.start_ip = map__start(map);
- di.end_ip = map__end(map);
- di.u.rti.segbase = segbase;
- di.u.rti.table_data = table_data;
- di.u.rti.table_len = fde_count * sizeof(struct table_entry)
- / sizeof(unw_word_t);
- ret = dwarf_search_unwind_table(as, ip, &di, pi,
- need_unwind_info, arg);
- }
-
-#ifndef NO_LIBUNWIND_DEBUG_FRAME
- /* Check the .debug_frame section for unwinding info */
- if (ret < 0 &&
- !read_unwind_spec_debug_frame(dso, ui->machine, &segbase)) {
- int fd;
- u64 start = map__start(map);
- unw_word_t base = start;
- const char *symfile;
-
- if (dso__data_get_fd(dso, ui->machine, &fd)) {
- if (elf_is_exec(fd, dso__name(dso)))
- base = 0;
- dso__data_put_fd(dso);
- }
-
- symfile = dso__symsrc_filename(dso) ?: dso__name(dso);
-
- memset(&di, 0, sizeof(di));
- if (dwarf_find_debug_frame(0, &di, ip, base, symfile, start, map__end(map)))
- ret = dwarf_search_unwind_table(as, ip, &di, pi,
- need_unwind_info, arg);
- }
-#endif
- map__put(map);
- return ret;
-}
-
-static int access_fpreg(unw_addr_space_t __maybe_unused as,
- unw_regnum_t __maybe_unused num,
- unw_fpreg_t __maybe_unused *val,
- int __maybe_unused __write,
- void __maybe_unused *arg)
-{
- pr_err("unwind: access_fpreg unsupported\n");
- return -UNW_EINVAL;
-}
-
-static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as,
- unw_word_t __maybe_unused *dil_addr,
- void __maybe_unused *arg)
-{
- return -UNW_ENOINFO;
-}
-
-static int resume(unw_addr_space_t __maybe_unused as,
- unw_cursor_t __maybe_unused *cu,
- void __maybe_unused *arg)
-{
- pr_err("unwind: resume unsupported\n");
- return -UNW_EINVAL;
-}
-
-static int
-get_proc_name(unw_addr_space_t __maybe_unused as,
- unw_word_t __maybe_unused addr,
- char __maybe_unused *bufp, size_t __maybe_unused buf_len,
- unw_word_t __maybe_unused *offp, void __maybe_unused *arg)
-{
- pr_err("unwind: get_proc_name unsupported\n");
- return -UNW_EINVAL;
-}
-
-static int access_dso_mem(struct unwind_info *ui, unw_word_t addr,
- unw_word_t *data)
-{
- struct map *map;
- struct dso *dso;
- ssize_t size;
-
- map = find_map(addr, ui);
- if (!map) {
- pr_debug("unwind: no map for %lx\n", (unsigned long)addr);
- return -1;
- }
-
- dso = map__dso(map);
-
- if (!dso) {
- map__put(map);
- return -1;
- }
-
- size = dso__data_read_addr(dso, map, ui->machine,
- addr, (u8 *) data, sizeof(*data));
- map__put(map);
- return !(size == sizeof(*data));
-}
-
-static int access_mem(unw_addr_space_t __maybe_unused as,
- unw_word_t addr, unw_word_t *valp,
- int __write, void *arg)
-{
- struct unwind_info *ui = arg;
- struct stack_dump *stack = &ui->sample->user_stack;
- u64 start, end;
- int offset;
- int ret;
-
- /* Don't support write, probably not needed. */
- if (__write || !stack || !ui->sample->user_regs || !ui->sample->user_regs->regs) {
- *valp = 0;
- return 0;
- }
-
- ret = perf_reg_value(&start, perf_sample__user_regs(ui->sample),
- perf_arch_reg_sp(ui->e_machine));
- if (ret)
- return ret;
-
- end = start + stack->size;
-
- /* Check overflow. */
- if (addr + sizeof(unw_word_t) < addr)
- return -EINVAL;
-
- if (addr < start || addr + sizeof(unw_word_t) >= end) {
- ret = access_dso_mem(ui, addr, valp);
- if (ret) {
- pr_debug("unwind: access_mem %p not inside range"
- " 0x%" PRIx64 "-0x%" PRIx64 "\n",
- (void *) (uintptr_t) addr, start, end);
- *valp = 0;
- return ret;
- }
- return 0;
- }
-
- offset = addr - start;
- *valp = *(unw_word_t *)&stack->data[offset];
- pr_debug("unwind: access_mem addr %p val %lx, offset %d\n",
- (void *) (uintptr_t) addr, (unsigned long)*valp, offset);
- return 0;
-}
-
-static int access_reg(unw_addr_space_t __maybe_unused as,
- unw_regnum_t regnum, unw_word_t *valp,
- int __write, void *arg)
-{
- struct unwind_info *ui = arg;
- int id, ret;
- u64 val;
-
- /* Don't support write, I suspect we don't need it. */
- if (__write) {
- pr_err("unwind: access_reg w %d\n", regnum);
- return 0;
- }
-
- if (!ui->sample->user_regs || !ui->sample->user_regs->regs) {
- *valp = 0;
- return 0;
- }
-
- id = get_perf_regnum_for_unw_regnum(ui->e_machine, regnum);
- if (id < 0)
- return -EINVAL;
-
- ret = perf_reg_value(&val, perf_sample__user_regs(ui->sample), id);
- if (ret) {
- if (!ui->best_effort)
- pr_err("unwind: can't read reg %d\n", regnum);
- return ret;
- }
-
- *valp = (unw_word_t) val;
- pr_debug("unwind: reg %d, val %lx\n", regnum, (unsigned long)*valp);
- return 0;
-}
-
-static void put_unwind_info(unw_addr_space_t __maybe_unused as,
- unw_proc_info_t *pi __maybe_unused,
- void *arg __maybe_unused)
-{
- pr_debug("unwind: put_unwind_info called\n");
-}
-
-static int entry(u64 ip, struct thread *thread,
- unwind_entry_cb_t cb, void *arg)
-{
- struct unwind_entry e;
- struct addr_location al;
- int ret;
-
- addr_location__init(&al);
- e.ms.sym = thread__find_symbol(thread, PERF_RECORD_MISC_USER, ip, &al);
- e.ip = ip;
- e.ms.map = al.map;
- e.ms.thread = thread__get(al.thread);
-
- pr_debug("unwind: %s:ip = 0x%" PRIx64 " (0x%" PRIx64 ")\n",
- al.sym ? al.sym->name : "''",
- ip,
- al.map ? map__map_ip(al.map, ip) : (u64) 0);
-
- ret = cb(&e, arg);
- addr_location__exit(&al);
- return ret;
-}
-
-static void display_error(int err)
-{
- switch (err) {
- case UNW_EINVAL:
- pr_err("unwind: Only supports local.\n");
- break;
- case UNW_EUNSPEC:
- pr_err("unwind: Unspecified error.\n");
- break;
- case UNW_EBADREG:
- pr_err("unwind: Register unavailable.\n");
- break;
- default:
- break;
- }
-}
-
-static unw_accessors_t accessors = {
- .find_proc_info = find_proc_info,
- .put_unwind_info = put_unwind_info,
- .get_dyn_info_list_addr = get_dyn_info_list_addr,
- .access_mem = access_mem,
- .access_reg = access_reg,
- .access_fpreg = access_fpreg,
- .resume = resume,
- .get_proc_name = get_proc_name,
-};
-
-static int _unwind__prepare_access(struct maps *maps)
-{
- void *addr_space = unw_create_addr_space(&accessors, 0);
-
- maps__set_addr_space(maps, addr_space);
- if (!addr_space) {
- pr_err("unwind: Can't create unwind address space.\n");
- return -ENOMEM;
- }
-
- unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL);
- return 0;
-}
-
-static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb,
- void *arg, int max_stack)
-{
- u64 val;
- unw_word_t ips[max_stack];
- unw_addr_space_t addr_space;
- unw_cursor_t c;
- int ret, i = 0;
-
- ret = perf_reg_value(&val, perf_sample__user_regs(ui->sample),
- perf_arch_reg_ip(ui->e_machine));
- if (ret)
- return ret;
-
- ips[i++] = (unw_word_t) val;
-
- /*
- * If we need more than one entry, do the DWARF
- * unwind itself.
- */
- if (max_stack - 1 > 0) {
- WARN_ONCE(!ui->thread, "WARNING: ui->thread is NULL");
- addr_space = maps__addr_space(thread__maps(ui->thread));
-
- if (addr_space == NULL)
- return -1;
-
- ret = unw_init_remote(&c, addr_space, ui);
- if (ret && !ui->best_effort)
- display_error(ret);
-
- while (!ret && (unw_step(&c) > 0) && i < max_stack) {
- unw_get_reg(&c, UNW_REG_IP, &ips[i]);
-
- /*
- * Decrement the IP for any non-activation frames.
- * this is required to properly find the srcline
- * for caller frames.
- * See also the documentation for dwfl_frame_pc(),
- * which this code tries to replicate.
- */
- if (unw_is_signal_frame(&c) <= 0)
- --ips[i];
-
- ++i;
- }
-
- max_stack = i;
- }
-
- /*
- * Display what we got based on the order setup.
- */
- for (i = 0; i < max_stack && !ret; i++) {
- int j = i;
-
- if (callchain_param.order == ORDER_CALLER)
- j = max_stack - i - 1;
- ret = ips[j] ? entry(ips[j], ui->thread, cb, arg) : 0;
- }
-
- return ret;
-}
-
-static int _unwind__get_entries(unwind_entry_cb_t cb, void *arg,
- struct thread *thread,
- struct perf_sample *data, int max_stack,
- bool best_effort)
-{
- struct unwind_info ui = {
- .sample = data,
- .thread = thread,
- .machine = maps__machine(thread__maps(thread)),
- .e_machine = thread__e_machine(thread, /*machine=*/NULL, /*e_flags=*/NULL),
- .best_effort = best_effort
- };
-
- if (!data->user_regs || !data->user_regs->regs)
- return -EINVAL;
-
- if (max_stack <= 0)
- return -EINVAL;
-
- return get_entries(&ui, cb, arg, max_stack);
-}
-
-static struct unwind_libunwind_ops
-_unwind_libunwind_ops = {
- .prepare_access = _unwind__prepare_access,
- .get_entries = _unwind__get_entries,
-};
-
-#ifndef REMOTE_UNWIND_LIBUNWIND
-struct unwind_libunwind_ops *
-local_unwind_libunwind_ops = &_unwind_libunwind_ops;
-#endif
diff --git a/tools/perf/util/unwind-libunwind.c b/tools/perf/util/unwind-libunwind.c
index 816891ecfa5e..a29ba14e7a30 100644
--- a/tools/perf/util/unwind-libunwind.c
+++ b/tools/perf/util/unwind-libunwind.c
@@ -1,23 +1,557 @@
// SPDX-License-Identifier: GPL-2.0
-#include "unwind.h"
+#include "callchain.h"
+#include "debug.h"
#include "dso.h"
+#include "env.h"
#include "map.h"
-#include "thread.h"
+#include "perf_regs.h"
#include "session.h"
-#include "debug.h"
-#include "env.h"
-#include "callchain.h"
+#include "symbol.h"
+#include "thread.h"
+#include "unwind.h"
#include "libunwind-arch/libunwind-arch.h"
#include <dwarf-regs.h>
#include <elf.h>
+#include <fcntl.h>
+#include <gelf.h>
+#include <inttypes.h>
+
+#define DW_EH_PE_FORMAT_MASK 0x0f /* format of the encoded value */
+#define DW_EH_PE_APPL_MASK 0x70 /* how the value is to be applied */
+
+/* Pointer-encoding formats: */
+#define DW_EH_PE_omit 0xff
+#define DW_EH_PE_ptr 0x00 /* pointer-sized unsigned value */
+#define DW_EH_PE_udata4 0x03 /* unsigned 32-bit value */
+#define DW_EH_PE_udata8 0x04 /* unsigned 64-bit value */
+#define DW_EH_PE_sdata4 0x0b /* signed 32-bit value */
+#define DW_EH_PE_sdata8 0x0c /* signed 64-bit value */
+
+/* Pointer-encoding application: */
+#define DW_EH_PE_absptr 0x00 /* absolute value */
+#define DW_EH_PE_pcrel 0x10 /* rel. to addr. of encoded value */
+
+/*
+ * The following are not documented by LSB v1.3, yet they are used by
+ * GCC, presumably they aren't documented by LSB since they aren't
+ * used on Linux:
+ */
+#define DW_EH_PE_funcrel 0x40 /* start-of-procedure-relative */
+#define DW_EH_PE_aligned 0x50 /* aligned pointer */
+
+/* Flags intentionally not handled, since they're not needed:
+ * #define DW_EH_PE_indirect 0x80
+ * #define DW_EH_PE_uleb128 0x01
+ * #define DW_EH_PE_udata2 0x02
+ * #define DW_EH_PE_sleb128 0x09
+ * #define DW_EH_PE_sdata2 0x0a
+ * #define DW_EH_PE_textrel 0x20
+ * #define DW_EH_PE_datarel 0x30
+ */
+
+#define dw_read(ptr, type, end) ({ \
+ type *__p = (type *) ptr; \
+ type __v; \
+ if ((__p + 1) > (type *) end) \
+ return -EINVAL; \
+ __v = *__p++; \
+ ptr = (typeof(ptr)) __p; \
+ __v; \
+ })
+
+static int __dw_read_encoded_value(u8 **p, u8 *end, u64 *val,
+ u8 encoding)
+{
+ u8 *cur = *p;
+ *val = 0;
+
+ switch (encoding) {
+ case DW_EH_PE_omit:
+ *val = 0;
+ goto out;
+ case DW_EH_PE_ptr:
+ *val = dw_read(cur, unsigned long, end);
+ goto out;
+ default:
+ break;
+ }
+
+ switch (encoding & DW_EH_PE_APPL_MASK) {
+ case DW_EH_PE_absptr:
+ break;
+ case DW_EH_PE_pcrel:
+ *val = (unsigned long) cur;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if ((encoding & 0x07) == 0x00)
+ encoding |= DW_EH_PE_udata4;
+
+ switch (encoding & DW_EH_PE_FORMAT_MASK) {
+ case DW_EH_PE_sdata4:
+ *val += dw_read(cur, s32, end);
+ break;
+ case DW_EH_PE_udata4:
+ *val += dw_read(cur, u32, end);
+ break;
+ case DW_EH_PE_sdata8:
+ *val += dw_read(cur, s64, end);
+ break;
+ case DW_EH_PE_udata8:
+ *val += dw_read(cur, u64, end);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ out:
+ *p = cur;
+ return 0;
+}
+
+#define dw_read_encoded_value(ptr, end, enc) ({ \
+ u64 __v; \
+ if (__dw_read_encoded_value(&ptr, end, &__v, enc)) { \
+ return -EINVAL; \
+ } \
+ __v; \
+ })
+
+static u64 elf_base_address(int fd)
+{
+ Elf *elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
+ GElf_Phdr phdr;
+ u64 retval = 0;
+ size_t i, phdrnum = 0;
+
+ if (elf == NULL)
+ return 0;
+ (void)elf_getphdrnum(elf, &phdrnum);
+ /* PT_LOAD segments are sorted by p_vaddr, so the first has the minimum p_vaddr. */
+ for (i = 0; i < phdrnum; i++) {
+ if (gelf_getphdr(elf, i, &phdr) && phdr.p_type == PT_LOAD) {
+ retval = phdr.p_vaddr & -getpagesize();
+ break;
+ }
+ }
+
+ elf_end(elf);
+ return retval;
+}
+
+static int unwind_spec_ehframe(struct dso *dso, struct machine *machine,
+ u64 offset, u64 *table_data_offset, u64 *fde_count)
+{
+ struct eh_frame_hdr {
+ unsigned char version;
+ unsigned char eh_frame_ptr_enc;
+ unsigned char fde_count_enc;
+ unsigned char table_enc;
+
+ /*
+ * The rest of the header is variable-length and consists of the
+ * following members:
+ *
+ * encoded_t eh_frame_ptr;
+ * encoded_t fde_count;
+ */
+
+ /* A single encoded pointer should not be more than 8 bytes. */
+ u64 enc[2];
+
+ /*
+ * struct {
+ * encoded_t start_ip;
+ * encoded_t fde_addr;
+ * } binary_search_table[fde_count];
+ */
+ char data[];
+ } __packed hdr;
+ u8 *enc = (u8 *) &hdr.enc;
+ u8 *end = (u8 *) &hdr.data;
+ ssize_t r;
+
+ r = dso__data_read_offset(dso, machine, offset, (u8 *) &hdr, sizeof(hdr));
+ if (r != sizeof(hdr))
+ return -EINVAL;
+
+ /* We dont need eh_frame_ptr, just skip it. */
+ dw_read_encoded_value(enc, end, hdr.eh_frame_ptr_enc);
+
+ *fde_count = dw_read_encoded_value(enc, end, hdr.fde_count_enc);
+ *table_data_offset = enc - (u8 *) &hdr;
+ return 0;
+}
+
+struct read_unwind_spec_eh_frame_maps_cb_args {
+ struct dso *dso;
+ u64 base_addr;
+};
+
+static int read_unwind_spec_eh_frame_maps_cb(struct map *map, void *data)
+{
+
+ struct read_unwind_spec_eh_frame_maps_cb_args *args = data;
+
+ if (map__dso(map) == args->dso && map__start(map) - map__pgoff(map) < args->base_addr)
+ args->base_addr = map__start(map) - map__pgoff(map);
+
+ return 0;
+}
+
+static int elf_section_address_and_offset(int fd, const char *name, u64 *address, u64 *offset)
+{
+ Elf *elf;
+ GElf_Ehdr ehdr;
+ GElf_Shdr shdr;
+ int ret = -1;
+
+ elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
+ if (elf == NULL)
+ return -1;
-struct unwind_libunwind_ops __weak *local_unwind_libunwind_ops;
-struct unwind_libunwind_ops __weak *x86_32_unwind_libunwind_ops;
-struct unwind_libunwind_ops __weak *arm64_unwind_libunwind_ops;
+ if (gelf_getehdr(elf, &ehdr) == NULL)
+ goto out_err;
+
+ if (!elf_section_by_name(elf, &ehdr, &shdr, name, NULL))
+ goto out_err;
+
+ *address = shdr.sh_addr;
+ *offset = shdr.sh_offset;
+ ret = 0;
+out_err:
+ elf_end(elf);
+ return ret;
+}
+
+static int read_unwind_spec_eh_frame(struct dso *dso, struct unwind_info *ui,
+ u64 *table_data, u64 *segbase,
+ u64 *fde_count)
+{
+ struct read_unwind_spec_eh_frame_maps_cb_args args = {
+ .dso = dso,
+ .base_addr = UINT64_MAX,
+ };
+ int ret, fd;
+
+ if (dso__data(dso)->eh_frame_hdr_offset == 0) {
+ if (!dso__data_get_fd(dso, ui->machine, &fd))
+ return -EINVAL;
+
+ /* Check the .eh_frame section for unwinding info */
+ ret = elf_section_address_and_offset(fd, ".eh_frame_hdr",
+ &dso__data(dso)->eh_frame_hdr_addr,
+ &dso__data(dso)->eh_frame_hdr_offset);
+ dso__data(dso)->elf_base_addr = elf_base_address(fd);
+ dso__data_put_fd(dso);
+ if (ret || dso__data(dso)->eh_frame_hdr_offset == 0)
+ return -EINVAL;
+ }
+
+ maps__for_each_map(thread__maps(ui->thread), read_unwind_spec_eh_frame_maps_cb, &args);
+
+ args.base_addr -= dso__data(dso)->elf_base_addr;
+ /* Address of .eh_frame_hdr */
+ *segbase = args.base_addr + dso__data(dso)->eh_frame_hdr_addr;
+ ret = unwind_spec_ehframe(dso, ui->machine, dso__data(dso)->eh_frame_hdr_offset,
+ table_data, fde_count);
+ if (ret)
+ return ret;
+ /* binary_search_table offset plus .eh_frame_hdr address */
+ *table_data += *segbase;
+ return 0;
+}
+
+static u64 elf_section_offset(int fd, const char *name)
+{
+ u64 address, offset = 0;
+
+ if (elf_section_address_and_offset(fd, name, &address, &offset))
+ return 0;
+
+ return offset;
+}
+
+static int read_unwind_spec_debug_frame(struct dso *dso,
+ struct machine *machine, u64 *offset)
+{
+ int fd;
+ u64 ofs = dso__data(dso)->debug_frame_offset;
+
+ /* debug_frame can reside in:
+ * - dso
+ * - debug pointed by symsrc_filename
+ * - gnu_debuglink, which doesn't necessary
+ * has to be pointed by symsrc_filename
+ */
+ if (ofs == 0) {
+ if (dso__data_get_fd(dso, machine, &fd)) {
+ ofs = elf_section_offset(fd, ".debug_frame");
+ dso__data_put_fd(dso);
+ }
+
+ if (ofs <= 0) {
+ fd = open(dso__symsrc_filename(dso), O_RDONLY);
+ if (fd >= 0) {
+ ofs = elf_section_offset(fd, ".debug_frame");
+ close(fd);
+ }
+ }
+
+ if (ofs <= 0) {
+ char *debuglink = malloc(PATH_MAX);
+ int ret = 0;
+
+ if (debuglink == NULL) {
+ pr_err("unwind: Can't read unwind spec debug frame.\n");
+ return -ENOMEM;
+ }
+
+ ret = dso__read_binary_type_filename(
+ dso, DSO_BINARY_TYPE__DEBUGLINK,
+ machine->root_dir, debuglink, PATH_MAX);
+ if (!ret) {
+ fd = open(debuglink, O_RDONLY);
+ if (fd >= 0) {
+ ofs = elf_section_offset(fd,
+ ".debug_frame");
+ close(fd);
+ }
+ }
+ if (ofs > 0) {
+ if (dso__symsrc_filename(dso) != NULL) {
+ pr_warning(
+ "%s: overwrite symsrc(%s,%s)\n",
+ __func__,
+ dso__symsrc_filename(dso),
+ debuglink);
+ dso__free_symsrc_filename(dso);
+ }
+ dso__set_symsrc_filename(dso, debuglink);
+ } else {
+ free(debuglink);
+ }
+ }
+
+ dso__data(dso)->debug_frame_offset = ofs;
+ }
+
+ *offset = ofs;
+ if (*offset)
+ return 0;
+
+ return -EINVAL;
+}
+
+static struct map *find_map(uint64_t ip, struct unwind_info *ui)
+{
+ struct addr_location al;
+ struct map *ret;
+
+ addr_location__init(&al);
+ thread__find_map(ui->thread, PERF_RECORD_MISC_USER, ip, &al);
+ ret = map__get(al.map);
+ addr_location__exit(&al);
+ return ret;
+}
+
+static int elf_is_exec(int fd, const char *name)
+{
+ Elf *elf;
+ GElf_Ehdr ehdr;
+ int retval = 0;
+
+ elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
+ if (elf == NULL)
+ return 0;
+ if (gelf_getehdr(elf, &ehdr) == NULL)
+ goto out;
+
+ retval = (ehdr.e_type == ET_EXEC);
+
+out:
+ elf_end(elf);
+ pr_debug3("unwind: elf_is_exec(%s): %d\n", name, retval);
+ return retval;
+}
+
+int __libunwind__find_proc_info(void *as, uint64_t ip, void *pi, int need_unwind_info, void *arg)
+{
+ struct unwind_info *ui = arg;
+ struct map *map;
+ struct dso *dso;
+ u64 table_data, segbase, fde_count;
+ int ret = -EINVAL;
+
+ map = find_map(ip, ui);
+ if (!map)
+ return -EINVAL;
+
+ dso = map__dso(map);
+ if (!dso) {
+ map__put(map);
+ return -EINVAL;
+ }
+
+ pr_debug3("unwind: find_proc_info dso %s\n", dso__name(dso));
+
+ /* Check the .eh_frame section for unwinding info */
+ if (!read_unwind_spec_eh_frame(dso, ui, &table_data, &segbase, &fde_count)) {
+ struct table_entry {
+ u32 start_ip_offset;
+ u32 fde_offset;
+ };
+ struct libarch_unwind__dyn_info di = {
+ .start_ip = map__start(map),
+ .end_ip = map__end(map),
+ .segbase = segbase,
+ .table_data = table_data,
+ .table_len = fde_count * sizeof(struct table_entry) / ui->unw_word_t_size,
+ };
+
+ ret = libunwind_arch__dwarf_search_unwind_table(ui->e_machine, as, ip, &di, pi,
+ need_unwind_info, arg);
+ }
+
+ /* Check the .debug_frame section for unwinding info */
+ if (ret < 0 && !read_unwind_spec_debug_frame(dso, ui->machine, &segbase)) {
+ int fd;
+ u64 start = map__start(map);
+ u64 base = start;
+ const char *symfile;
+ struct libarch_unwind__dyn_info di = {};
+
+ if (dso__data_get_fd(dso, ui->machine, &fd)) {
+ if (elf_is_exec(fd, dso__name(dso)))
+ base = 0;
+ dso__data_put_fd(dso);
+ }
+
+ symfile = dso__symsrc_filename(dso) ?: dso__name(dso);
+
+ if (libunwind_arch__dwarf_find_debug_frame(ui->e_machine, /*found=*/0, &di, ip,
+ base, symfile, start, map__end(map))) {
+ ret = libunwind_arch__dwarf_search_unwind_table(ui->e_machine, as, ip, &di, pi,
+ need_unwind_info, arg);
+ }
+ }
+ map__put(map);
+ return ret;
+}
+
+static int access_dso_mem(struct unwind_info *ui, uint64_t addr, void *data_word)
+{
+ struct map *map;
+ struct dso *dso;
+ ssize_t size;
+
+ map = find_map(addr, ui);
+ if (!map) {
+ pr_debug("unwind: no map for %lx\n", (unsigned long)addr);
+ return -1;
+ }
+
+ dso = map__dso(map);
+
+ if (!dso) {
+ map__put(map);
+ return -1;
+ }
+
+ size = dso__data_read_addr(dso, map, ui->machine,
+ addr,
+ (u8 *) data_word,
+ ui->unw_word_t_size);
+ map__put(map);
+ return !((size_t)size == ui->unw_word_t_size);
+}
+
+int __libunwind__access_mem(void *as __maybe_unused, uint64_t addr, void *valp_word,
+ int __write, void *arg)
+{
+ struct unwind_info *ui = arg;
+ struct stack_dump *stack = &ui->sample->user_stack;
+ u64 start, end;
+ int offset;
+ int ret;
+
+ /* Don't support write, probably not needed. */
+ if (__write || !stack || !ui->sample->user_regs || !ui->sample->user_regs->regs) {
+ uint64_t zero = 0;
+
+ memcpy(valp_word, &zero, ui->unw_word_t_size);
+ return 0;
+ }
+
+ ret = perf_reg_value(&start, perf_sample__user_regs(ui->sample),
+ perf_arch_reg_sp(ui->e_machine));
+ if (ret)
+ return ret;
+
+ end = start + stack->size;
+
+ /* Check overflow. */
+ if (addr + ui->unw_word_t_size < addr)
+ return -EINVAL;
+
+ if (addr < start || addr + ui->unw_word_t_size >= end) {
+ ret = access_dso_mem(ui, addr, valp_word);
+ if (ret) {
+ pr_debug3("unwind: access_mem %p not inside range"
+ " 0x%" PRIx64 "-0x%" PRIx64 "\n",
+ (void *) (uintptr_t) addr, start, end);
+ memset(valp_word, 0, ui->unw_word_t_size);
+ return ret;
+ }
+ return 0;
+ }
+
+ offset = addr - start;
+ memcpy(valp_word, &stack->data[offset], ui->unw_word_t_size);
+ pr_debug3("unwind: access_mem addr %p val %lx, offset %d\n",
+ (void *) (uintptr_t) addr, *((unsigned long *)valp_word), offset);
+ return 0;
+}
+
+int __libunwind__access_reg(void *as __maybe_unused, int regnum, void *valp_word, int __write,
+ void *arg)
+{
+ struct unwind_info *ui = arg;
+ int id, ret;
+ u64 val;
+
+ /* Don't support write, I suspect we don't need it. */
+ if (__write) {
+ pr_err("unwind: access_reg w %d\n", regnum);
+ return 0;
+ }
+
+ if (!ui->sample->user_regs || !ui->sample->user_regs->regs) {
+ memset(valp_word, 0, ui->unw_word_t_size);
+ return 0;
+ }
+
+ id = get_perf_regnum_for_unw_regnum(ui->e_machine, regnum);
+ if (id < 0)
+ return -EINVAL;
+
+ ret = perf_reg_value(&val, perf_sample__user_regs(ui->sample), id);
+ if (ret) {
+ if (!ui->best_effort)
+ pr_err("unwind: can't read reg %d\n", regnum);
+ return ret;
+ }
+
+ memcpy(valp_word, &val, ui->unw_word_t_size);
+ pr_debug3("unwind: reg %d, val %lx\n", regnum, val);
+ return 0;
+}
int unwind__prepare_access(struct maps *maps, uint16_t e_machine)
{
- struct unwind_libunwind_ops *ops = local_unwind_libunwind_ops;
+ void *addr_space;
if (!dwarf_callchain_users)
return 0;
@@ -27,25 +561,16 @@ int unwind__prepare_access(struct maps *maps, uint16_t e_machine)
return 0;
}
- if (e_machine != EM_HOST) {
- /* If not live/local mode. */
- switch (e_machine) {
- case EM_386:
- ops = x86_32_unwind_libunwind_ops;
- break;
- case EM_AARCH64:
- ops = arm64_unwind_libunwind_ops;
- break;
- default:
- pr_warning_once("unwind: ELF machine type %d is not supported\n",
- e_machine);
- return 0;
- }
- }
- maps__set_unwind_libunwind_ops(maps, ops);
maps__set_e_machine(maps, e_machine);
+ addr_space = libunwind_arch__create_addr_space(e_machine);
+
+ maps__set_addr_space(maps, addr_space);
+ if (!addr_space) {
+ pr_err("unwind: Can't create unwind address space.\n");
+ return -ENOMEM;
+ }
- return maps__unwind_libunwind_ops(maps)->prepare_access(maps);
+ return 0;
}
void unwind__flush_access(struct maps *maps)
@@ -58,14 +583,81 @@ void unwind__finish_access(struct maps *maps)
libunwind_arch__finish_access(maps);
}
+static int entry(uint64_t ip, struct thread *thread, unwind_entry_cb_t cb, void *arg)
+{
+ struct unwind_entry e;
+ struct addr_location al;
+ int ret;
+
+ addr_location__init(&al);
+ e.ms.sym = thread__find_symbol(thread, PERF_RECORD_MISC_USER, ip, &al);
+ e.ip = ip;
+ e.ms.map = al.map;
+ e.ms.thread = thread__get(al.thread);
+
+ pr_debug("unwind: %s:ip = 0x%" PRIx64 " (0x%" PRIx64 ")\n",
+ al.sym ? al.sym->name : "''",
+ ip,
+ al.map ? map__map_ip(al.map, ip) : (u64) 0);
+
+ ret = cb(&e, arg);
+ addr_location__exit(&al);
+ return ret;
+}
+
int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
struct thread *thread,
- struct perf_sample *data, int max_stack,
+ struct perf_sample *sample, int max_stack,
bool best_effort)
{
- const struct unwind_libunwind_ops *ops = maps__unwind_libunwind_ops(thread__maps(thread));
+ struct unwind_info *ui;
+ uint64_t first_ip;
+ int ret, i = 0;
+ uint16_t e_machine;
- if (ops)
- return ops->get_entries(cb, arg, thread, data, max_stack, best_effort);
- return 0;
+ if (!sample->user_regs || !sample->user_regs->regs)
+ return -EINVAL;
+
+ if (max_stack <= 0)
+ return -EINVAL;
+
+ if (!thread) {
+ pr_warning_once("WARNING: thread is NULL");
+ return -EINVAL;
+ }
+
+ e_machine = thread__e_machine(thread, /*machine=*/NULL, /*e_flags=*/NULL);
+ ret = perf_reg_value(&first_ip, perf_sample__user_regs(sample),
+ perf_arch_reg_ip(e_machine));
+ if (ret)
+ return ret;
+
+ if (max_stack == 1) {
+ /* Special case for a single entry. */
+ return entry(first_ip, thread, cb, arg);
+ }
+
+ ui = libunwind_arch_unwind_info__new(thread, sample, max_stack, best_effort, e_machine, first_ip);
+ if (!ui)
+ return -1;
+
+ do {
+ ret = libunwind_arch__unwind_step(ui);
+ if (ret < 0)
+ goto out;
+
+ } while (ret);
+
+ /*
+ * Display what we got based on the order setup.
+ */
+ for (i = 0; i < ui->cur_ip; i++) {
+ int j = callchain_param.order == ORDER_CALLEE ? i : ui->cur_ip - i - 1;
+
+ if (ui->ips[j])
+ ret = entry(ui->ips[j], thread, cb, arg);
+ }
+out:
+ libunwind_arch_unwind_info__delete(ui);
+ return ret;
}
--
2.53.0.371.g1d285c8824-goog
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [RFC PATCH v1 7/7] perf unwind-libunwind: Add RISC-V libunwind support
2026-02-24 14:29 [RFC PATCH v1 0/7] perf libunwind multiple remote support Ian Rogers
` (5 preceding siblings ...)
2026-02-24 14:29 ` [RFC PATCH v1 6/7] perf unwind-libunwind: Remove libunwind-local Ian Rogers
@ 2026-02-24 14:29 ` Ian Rogers
2026-02-25 21:08 ` Andrew Jones
6 siblings, 1 reply; 27+ messages in thread
From: Ian Rogers @ 2026-02-24 14:29 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Alexander Shishkin, Jiri Olsa, Ian Rogers,
Adrian Hunter, James Clark, John Garry, Will Deacon, Leo Yan,
Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
Dmitrii Dolgov, Blake Jones, Tomas Glozar, Yuzhuo Jing, Dapeng Mi,
Shimin Guo, Athira Rajeev, Chun-Tse Shao, Howard Chu,
linux-kernel, linux-perf-users, linux-arm-kernel, linux-riscv,
Thomas Richter, libunwind-devel
Add a RISC-V implementation for unwinding.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/libunwind-arch/Build | 1 +
.../perf/util/libunwind-arch/libunwind-arch.c | 21 ++
.../perf/util/libunwind-arch/libunwind-arch.h | 22 ++
.../util/libunwind-arch/libunwind-riscv.c | 297 ++++++++++++++++++
4 files changed, 341 insertions(+)
create mode 100644 tools/perf/util/libunwind-arch/libunwind-riscv.c
diff --git a/tools/perf/util/libunwind-arch/Build b/tools/perf/util/libunwind-arch/Build
index 87fd657a3248..80d3571918b1 100644
--- a/tools/perf/util/libunwind-arch/Build
+++ b/tools/perf/util/libunwind-arch/Build
@@ -5,6 +5,7 @@ perf-util-$(CONFIG_LIBUNWIND) += libunwind-loongarch.o
perf-util-$(CONFIG_LIBUNWIND) += libunwind-mips.o
perf-util-$(CONFIG_LIBUNWIND) += libunwind-ppc32.o
perf-util-$(CONFIG_LIBUNWIND) += libunwind-ppc64.o
+perf-util-$(CONFIG_LIBUNWIND) += libunwind-riscv.o
perf-util-$(CONFIG_LIBUNWIND) += libunwind-s390.o
perf-util-$(CONFIG_LIBUNWIND) += libunwind-i386.o
perf-util-$(CONFIG_LIBUNWIND) += libunwind-x86_64.o
diff --git a/tools/perf/util/libunwind-arch/libunwind-arch.c b/tools/perf/util/libunwind-arch/libunwind-arch.c
index 8539b4233df4..9a74cf3c8729 100644
--- a/tools/perf/util/libunwind-arch/libunwind-arch.c
+++ b/tools/perf/util/libunwind-arch/libunwind-arch.c
@@ -20,6 +20,8 @@ int get_perf_regnum_for_unw_regnum(unsigned int e_machine, int unw_regnum)
return __get_perf_regnum_for_unw_regnum_ppc32(unw_regnum);
case EM_PPC64:
return __get_perf_regnum_for_unw_regnum_ppc64(unw_regnum);
+ case EM_RISCV:
+ return __get_perf_regnum_for_unw_regnum_riscv(unw_regnum);
case EM_S390:
return __get_perf_regnum_for_unw_regnum_s390(unw_regnum);
case EM_386:
@@ -58,6 +60,9 @@ void libunwind_arch__flush_access(struct maps *maps)
case EM_PPC64:
__libunwind_arch__flush_access_ppc64(maps);
break;
+ case EM_RISCV:
+ __libunwind_arch__flush_access_riscv(maps);
+ break;
case EM_S390:
__libunwind_arch__flush_access_s390(maps);
break;
@@ -98,6 +103,9 @@ void libunwind_arch__finish_access(struct maps *maps)
case EM_PPC64:
__libunwind_arch__finish_access_ppc64(maps);
break;
+ case EM_RISCV:
+ __libunwind_arch__finish_access_riscv(maps);
+ break;
case EM_S390:
__libunwind_arch__finish_access_s390(maps);
break;
@@ -128,6 +136,8 @@ void *libunwind_arch__create_addr_space(unsigned int e_machine)
return __libunwind_arch__create_addr_space_ppc32();
case EM_PPC64:
return __libunwind_arch__create_addr_space_ppc64();
+ case EM_RISCV:
+ return __libunwind_arch__create_addr_space_riscv();
case EM_S390:
return __libunwind_arch__create_addr_space_s390();
case EM_386:
@@ -167,6 +177,9 @@ int libunwind_arch__dwarf_search_unwind_table(unsigned int e_machine,
case EM_PPC64:
return __libunwind_arch__dwarf_search_unwind_table_ppc64(as, ip, di, pi,
need_unwind_info, arg);
+ case EM_RISCV:
+ return __libunwind_arch__dwarf_search_unwind_table_riscv(as, ip, di, pi,
+ need_unwind_info, arg);
case EM_S390:
return __libunwind_arch__dwarf_search_unwind_table_s390(as, ip, di, pi,
need_unwind_info, arg);
@@ -211,6 +224,9 @@ int libunwind_arch__dwarf_find_debug_frame(unsigned int e_machine,
case EM_PPC64:
return __libunwind_arch__dwarf_find_debug_frame_ppc64(found, di_debug, ip, segbase,
obj_name, start, end);
+ case EM_RISCV:
+ return __libunwind_arch__dwarf_find_debug_frame_riscv(found, di_debug, ip, segbase,
+ obj_name, start, end);
case EM_S390:
return __libunwind_arch__dwarf_find_debug_frame_s390(found, di_debug, ip, segbase,
obj_name, start, end);
@@ -250,6 +266,9 @@ struct unwind_info *libunwind_arch_unwind_info__new(struct thread *thread,
case EM_PPC64:
return __libunwind_arch_unwind_info__new_ppc64(thread, sample, max_stack,
best_effort, first_ip);
+ case EM_RISCV:
+ return __libunwind_arch_unwind_info__new_riscv(thread, sample, max_stack,
+ best_effort, first_ip);
case EM_S390:
return __libunwind_arch_unwind_info__new_s390(thread, sample, max_stack,
best_effort, first_ip);
@@ -285,6 +304,8 @@ int libunwind_arch__unwind_step(struct unwind_info *ui)
return __libunwind_arch__unwind_step_ppc32(ui);
case EM_PPC64:
return __libunwind_arch__unwind_step_ppc64(ui);
+ case EM_RISCV:
+ return __libunwind_arch__unwind_step_riscv(ui);
case EM_S390:
return __libunwind_arch__unwind_step_s390(ui);
case EM_386:
diff --git a/tools/perf/util/libunwind-arch/libunwind-arch.h b/tools/perf/util/libunwind-arch/libunwind-arch.h
index 2bf7fc33313b..74a09cd58f38 100644
--- a/tools/perf/util/libunwind-arch/libunwind-arch.h
+++ b/tools/perf/util/libunwind-arch/libunwind-arch.h
@@ -39,6 +39,7 @@ int __get_perf_regnum_for_unw_regnum_loongarch(int unw_regnum);
int __get_perf_regnum_for_unw_regnum_mips(int unw_regnum);
int __get_perf_regnum_for_unw_regnum_ppc32(int unw_regnum);
int __get_perf_regnum_for_unw_regnum_ppc64(int unw_regnum);
+int __get_perf_regnum_for_unw_regnum_riscv(int unw_regnum);
int __get_perf_regnum_for_unw_regnum_s390(int unw_regnum);
int __get_perf_regnum_for_unw_regnum_i386(int unw_regnum);
int __get_perf_regnum_for_unw_regnum_x86_64(int unw_regnum);
@@ -50,6 +51,7 @@ void __libunwind_arch__flush_access_loongarch(struct maps *maps);
void __libunwind_arch__flush_access_mips(struct maps *maps);
void __libunwind_arch__flush_access_ppc32(struct maps *maps);
void __libunwind_arch__flush_access_ppc64(struct maps *maps);
+void __libunwind_arch__flush_access_riscv(struct maps *maps);
void __libunwind_arch__flush_access_s390(struct maps *maps);
void __libunwind_arch__flush_access_i386(struct maps *maps);
void __libunwind_arch__flush_access_x86_64(struct maps *maps);
@@ -61,6 +63,7 @@ void __libunwind_arch__finish_access_loongarch(struct maps *maps);
void __libunwind_arch__finish_access_mips(struct maps *maps);
void __libunwind_arch__finish_access_ppc32(struct maps *maps);
void __libunwind_arch__finish_access_ppc64(struct maps *maps);
+void __libunwind_arch__finish_access_riscv(struct maps *maps);
void __libunwind_arch__finish_access_s390(struct maps *maps);
void __libunwind_arch__finish_access_i386(struct maps *maps);
void __libunwind_arch__finish_access_x86_64(struct maps *maps);
@@ -72,6 +75,7 @@ void *__libunwind_arch__create_addr_space_loongarch(void);
void *__libunwind_arch__create_addr_space_mips(void);
void *__libunwind_arch__create_addr_space_ppc32(void);
void *__libunwind_arch__create_addr_space_ppc64(void);
+void *__libunwind_arch__create_addr_space_riscv(void);
void *__libunwind_arch__create_addr_space_s390(void);
void *__libunwind_arch__create_addr_space_i386(void);
void *__libunwind_arch__create_addr_space_x86_64(void);
@@ -111,6 +115,11 @@ int __libunwind_arch__dwarf_search_unwind_table_ppc64(void *as, uint64_t ip,
void *pi,
int need_unwind_info,
void *arg);
+int __libunwind_arch__dwarf_search_unwind_table_riscv(void *as, uint64_t ip,
+ struct libarch_unwind__dyn_info *di,
+ void *pi,
+ int need_unwind_info,
+ void *arg);
int __libunwind_arch__dwarf_search_unwind_table_s390(void *as, uint64_t ip,
struct libarch_unwind__dyn_info *di,
void *pi,
@@ -176,6 +185,13 @@ int __libunwind_arch__dwarf_find_debug_frame_ppc64(int found,
const char *obj_name,
uint64_t start,
uint64_t end);
+int __libunwind_arch__dwarf_find_debug_frame_riscv(int found,
+ struct libarch_unwind__dyn_info *di_debug,
+ uint64_t ip,
+ uint64_t segbase,
+ const char *obj_name,
+ uint64_t start,
+ uint64_t end);
int __libunwind_arch__dwarf_find_debug_frame_s390(int found,
struct libarch_unwind__dyn_info *di_debug,
uint64_t ip,
@@ -236,6 +252,11 @@ struct unwind_info *__libunwind_arch_unwind_info__new_ppc64(struct thread *threa
int max_stack,
bool best_effort,
uint64_t first_ip);
+struct unwind_info *__libunwind_arch_unwind_info__new_riscv(struct thread *thread,
+ struct perf_sample *sample,
+ int max_stack,
+ bool best_effort,
+ uint64_t first_ip);
struct unwind_info *__libunwind_arch_unwind_info__new_s390(struct thread *thread,
struct perf_sample *sample,
int max_stack,
@@ -266,6 +287,7 @@ int __libunwind_arch__unwind_step_loongarch(struct unwind_info *ui);
int __libunwind_arch__unwind_step_mips(struct unwind_info *ui);
int __libunwind_arch__unwind_step_ppc32(struct unwind_info *ui);
int __libunwind_arch__unwind_step_ppc64(struct unwind_info *ui);
+int __libunwind_arch__unwind_step_riscv(struct unwind_info *ui);
int __libunwind_arch__unwind_step_s390(struct unwind_info *ui);
int __libunwind_arch__unwind_step_i386(struct unwind_info *ui);
int __libunwind_arch__unwind_step_x86_64(struct unwind_info *ui);
diff --git a/tools/perf/util/libunwind-arch/libunwind-riscv.c b/tools/perf/util/libunwind-arch/libunwind-riscv.c
new file mode 100644
index 000000000000..dbca802b511c
--- /dev/null
+++ b/tools/perf/util/libunwind-arch/libunwind-riscv.c
@@ -0,0 +1,297 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "libunwind-arch.h"
+#include "../debug.h"
+#include "../maps.h"
+#include "../thread.h"
+#include "../../../arch/riscv/include/uapi/asm/perf_regs.h"
+#include <linux/compiler.h>
+#include <linux/kernel.h>
+#include <linux/zalloc.h>
+#include <elf.h>
+#include <errno.h>
+
+#ifdef HAVE_LIBUNWIND_RISCV_SUPPORT
+#include <libunwind-riscv.h>
+#endif
+
+int __get_perf_regnum_for_unw_regnum_riscv(int unw_regnum __maybe_unused)
+{
+#ifndef HAVE_LIBUNWIND_RISCV_SUPPORT
+ return -EINVAL;
+#else
+ switch (unw_regnum) {
+ case UNW_RISCV_X1 ... UNW_RISCV_X31:
+ return unw_regnum - UNW_RISCV_X1 + PERF_REG_RISCV_RA;
+ case UNW_RISCV_PC:
+ return PERF_REG_RISCV_PC;
+ default:
+ pr_err("unwind: invalid reg id %d\n", unw_regnum);
+ return -EINVAL;
+ }
+#endif // HAVE_LIBUNWIND_RISCV_SUPPORT
+}
+
+void __libunwind_arch__flush_access_riscv(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_RISCV_SUPPORT
+ unw_flush_cache(maps__addr_space(maps), 0, 0);
+#endif
+}
+
+void __libunwind_arch__finish_access_riscv(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_RISCV_SUPPORT
+ unw_destroy_addr_space(maps__addr_space(maps));
+#endif
+}
+
+#ifdef HAVE_LIBUNWIND_RISCV_SUPPORT
+static int find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
+ int need_unwind_info, void *arg)
+{
+ return __libunwind__find_proc_info(as, ip, pi, need_unwind_info, arg, sizeof(unw_word_t));
+}
+
+static void put_unwind_info(unw_addr_space_t __maybe_unused as,
+ unw_proc_info_t *pi __maybe_unused,
+ void *arg __maybe_unused)
+{
+ pr_debug("unwind: put_unwind_info called\n");
+}
+
+static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as,
+ unw_word_t __maybe_unused *dil_addr,
+ void __maybe_unused *arg)
+{
+ return -UNW_ENOINFO;
+}
+
+static int access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *valp,
+ int __write, void *arg)
+{
+ return __libunwind__access_mem(as, addr, valp, __write, arg, sizeof(unw_word_t));
+}
+
+static int access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t *valp,
+ int __write, void *arg)
+{
+ return __libunwind__access_reg(as, regnum, valp, __write, arg, sizeof(unw_word_t));
+}
+
+static int access_fpreg(unw_addr_space_t __maybe_unused as,
+ unw_regnum_t __maybe_unused num,
+ unw_fpreg_t __maybe_unused *val,
+ int __maybe_unused __write,
+ void __maybe_unused *arg)
+{
+ pr_err("unwind: access_fpreg unsupported\n");
+ return -UNW_EINVAL;
+}
+
+static int resume(unw_addr_space_t __maybe_unused as,
+ unw_cursor_t __maybe_unused *cu,
+ void __maybe_unused *arg)
+{
+ pr_err("unwind: resume unsupported\n");
+ return -UNW_EINVAL;
+}
+
+static int get_proc_name(unw_addr_space_t __maybe_unused as,
+ unw_word_t __maybe_unused addr,
+ char __maybe_unused *bufp, size_t __maybe_unused buf_len,
+ unw_word_t __maybe_unused *offp, void __maybe_unused *arg)
+{
+ pr_err("unwind: get_proc_name unsupported\n");
+ return -UNW_EINVAL;
+}
+#endif
+
+void *__libunwind_arch__create_addr_space_riscv(void)
+{
+#ifdef HAVE_LIBUNWIND_RISCV_SUPPORT
+ static unw_accessors_t accessors = {
+ .find_proc_info = find_proc_info,
+ .put_unwind_info = put_unwind_info,
+ .get_dyn_info_list_addr = get_dyn_info_list_addr,
+ .access_mem = access_mem,
+ .access_reg = access_reg,
+ .access_fpreg = access_fpreg,
+ .resume = resume,
+ .get_proc_name = get_proc_name,
+ };
+ unw_addr_space_t addr_space;
+
+ addr_space = unw_create_addr_space(&accessors, /*byte_order=*/0);
+ unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL);
+ return addr_space;
+#else
+ return NULL;
+#endif
+}
+
+#ifdef HAVE_LIBUNWIND_RISCV_SUPPORT
+extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,
+ unw_word_t ip,
+ unw_dyn_info_t *di,
+ unw_proc_info_t *pi,
+ int need_unwind_info, void *arg);
+#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table)
+#endif
+
+int __libunwind_arch__dwarf_search_unwind_table_riscv(void *as __maybe_unused,
+ uint64_t ip __maybe_unused,
+ struct libarch_unwind__dyn_info *_di __maybe_unused,
+ void *pi __maybe_unused,
+ int need_unwind_info __maybe_unused,
+ void *arg __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_RISCV_SUPPORT
+ unw_dyn_info_t di = {
+ .format = UNW_INFO_FORMAT_REMOTE_TABLE,
+ .start_ip = _di->start_ip,
+ .end_ip = _di->end_ip,
+ .u = {
+ .rti = {
+ .segbase = _di->segbase,
+ .table_data = _di->table_data,
+ .table_len = _di->table_len,
+ },
+ },
+ };
+ int ret = dwarf_search_unwind_table(as, ip, &di, pi, need_unwind_info, arg);
+
+ _di->start_ip = di.start_ip;
+ _di->end_ip = di.end_ip;
+ _di->segbase = di.u.rti.segbase;
+ _di->table_data = di.u.rti.table_data;
+ _di->table_len = di.u.rti.table_len;
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
+
+#if defined(HAVE_LIBUNWIND_RISCV_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_RISCV)
+extern int UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug,
+ unw_word_t ip,
+ unw_word_t segbase,
+ const char *obj_name, unw_word_t start,
+ unw_word_t end);
+#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame)
+#endif
+
+int __libunwind_arch__dwarf_find_debug_frame_riscv(int found __maybe_unused,
+ struct libarch_unwind__dyn_info *_di __maybe_unused,
+ uint64_t ip __maybe_unused,
+ uint64_t segbase __maybe_unused,
+ const char *obj_name __maybe_unused,
+ uint64_t start __maybe_unused,
+ uint64_t end __maybe_unused)
+{
+#if defined(HAVE_LIBUNWIND_RISCV_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_RISCV)
+ unw_dyn_info_t di = {
+ .format = UNW_INFO_FORMAT_REMOTE_TABLE,
+ .start_ip = _di->start_ip,
+ .end_ip = _di->end_ip,
+ .u = {
+ .rti = {
+ .segbase = _di->segbase,
+ .table_data = _di->table_data,
+ .table_len = _di->len,
+ },
+ },
+ };
+ int ret = dwarf_find_debug_frame(found, &di, ip, segvase, obj_name, start, end);
+
+ _di->start_ip = di.start_ip;
+ _di->end_ip = di.end_ip;
+ _di->segbase = di.u.rti.segbase;
+ _di->table_data = di.u.rti.table_data;
+ _di->table_len = di.u.rti.table_len;
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
+
+struct unwind_info *__libunwind_arch_unwind_info__new_riscv(struct thread *thread __maybe_unused,
+ struct perf_sample *sample __maybe_unused,
+ int max_stack __maybe_unused,
+ bool best_effort __maybe_unused,
+ uint64_t first_ip __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_RISCV_SUPPORT
+ struct x86_64_unwind_info {
+ struct unwind_info ui;
+ unw_cursor_t _cursor;
+ uint64_t _ips[];
+ };
+
+ struct maps *maps = thread__maps(thread);
+ void *addr_space = maps__addr_space(maps);
+ struct x86_64_unwind_info *ui;
+ int ret;
+
+ if (addr_space == NULL)
+ return NULL;
+
+ ui = zalloc(sizeof(*ui) + sizeof(ui->_ips[0]) * max_stack);
+ if (!ui)
+ return NULL;
+
+ ui->ui.machine = maps__machine(maps);
+ ui->ui.thread = thread;
+ ui->ui.sample = sample;
+ ui->ui.cursor = &ui->_cursor;
+ ui->ui.ips = &ui->_ips[0];
+ ui->ui.ips[0] = first_ip;
+ ui->ui.cur_ip = 1;
+ ui->ui.max_ips = max_stack;
+ ui->ui.unw_word_t_size = sizeof(unw_word_t);
+ ui->ui.e_machine = EM_RISCV;
+ ui->ui.best_effort = best_effort;
+
+ ret = unw_init_remote(&ui->_cursor, addr_space, &ui->ui);
+ if (ret) {
+ if (!best_effort)
+ pr_err("libunwind: %s\n", unw_strerror(ret));
+ free(ui);
+ return NULL;
+ }
+
+ return &ui->ui;
+#else
+ return NULL;
+#endif
+}
+
+int __libunwind_arch__unwind_step_riscv(struct unwind_info *ui __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_RISCV_SUPPORT
+ int ret;
+
+ if (ui->cur_ip >= ui->max_ips)
+ return -1;
+
+ ret = unw_step(ui->cursor);
+ if (ret > 0) {
+ uint64_t ip;
+
+ unw_get_reg(ui->cursor, UNW_REG_IP, &ip);
+
+ if (unw_is_signal_frame(ui->cursor) <= 0) {
+ /*
+ * Decrement the IP for any non-activation frames. This
+ * is required to properly find the srcline for caller
+ * frames. See also the documentation for
+ * dwfl_frame_pc(), which this code tries to replicate.
+ */
+ --ip;
+ }
+ ui->ips[ui->cur_ip++] = ip;
+ }
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
--
2.53.0.371.g1d285c8824-goog
^ permalink raw reply related [flat|nested] 27+ messages in thread
* Re: [RFC PATCH v1 7/7] perf unwind-libunwind: Add RISC-V libunwind support
2026-02-24 14:29 ` [RFC PATCH v1 7/7] perf unwind-libunwind: Add RISC-V libunwind support Ian Rogers
@ 2026-02-25 21:08 ` Andrew Jones
2026-02-26 1:34 ` Ian Rogers
0 siblings, 1 reply; 27+ messages in thread
From: Andrew Jones @ 2026-02-25 21:08 UTC (permalink / raw)
To: Ian Rogers
Cc: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Alexander Shishkin, Jiri Olsa, Adrian Hunter,
James Clark, John Garry, Will Deacon, Leo Yan, Paul Walmsley,
Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Dmitrii Dolgov,
Blake Jones, Tomas Glozar, Yuzhuo Jing, Dapeng Mi, Shimin Guo,
Athira Rajeev, Chun-Tse Shao, Howard Chu, linux-kernel,
linux-perf-users, linux-arm-kernel, linux-riscv, Thomas Richter,
libunwind-devel
On Tue, Feb 24, 2026 at 06:29:37AM -0800, Ian Rogers wrote:
...
> +struct unwind_info *__libunwind_arch_unwind_info__new_riscv(struct thread *thread __maybe_unused,
> + struct perf_sample *sample __maybe_unused,
> + int max_stack __maybe_unused,
> + bool best_effort __maybe_unused,
> + uint64_t first_ip __maybe_unused)
> +{
> +#ifdef HAVE_LIBUNWIND_RISCV_SUPPORT
> + struct x86_64_unwind_info {
^ copy+paste issue?
Thanks,
drew
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [RFC PATCH v1 7/7] perf unwind-libunwind: Add RISC-V libunwind support
2026-02-25 21:08 ` Andrew Jones
@ 2026-02-26 1:34 ` Ian Rogers
2026-03-05 22:19 ` [PATCH v2 0/8] perf libunwind multiple remote support Ian Rogers
0 siblings, 1 reply; 27+ messages in thread
From: Ian Rogers @ 2026-02-26 1:34 UTC (permalink / raw)
To: Andrew Jones
Cc: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Alexander Shishkin, Jiri Olsa, Adrian Hunter,
James Clark, John Garry, Will Deacon, Leo Yan, Paul Walmsley,
Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Dmitrii Dolgov,
Blake Jones, Tomas Glozar, Yuzhuo Jing, Dapeng Mi, Shimin Guo,
Athira Rajeev, Chun-Tse Shao, Howard Chu, linux-kernel,
linux-perf-users, linux-arm-kernel, linux-riscv, Thomas Richter,
libunwind-devel
On Wed, Feb 25, 2026 at 1:08 PM Andrew Jones
<andrew.jones@oss.qualcomm.com> wrote:
>
> On Tue, Feb 24, 2026 at 06:29:37AM -0800, Ian Rogers wrote:
> ...
> > +struct unwind_info *__libunwind_arch_unwind_info__new_riscv(struct thread *thread __maybe_unused,
> > + struct perf_sample *sample __maybe_unused,
> > + int max_stack __maybe_unused,
> > + bool best_effort __maybe_unused,
> > + uint64_t first_ip __maybe_unused)
> > +{
> > +#ifdef HAVE_LIBUNWIND_RISCV_SUPPORT
> > + struct x86_64_unwind_info {
> ^ copy+paste issue?
Seems very likely :-) Will fix in v2. I'm still after a way to
generate a compile of libunwind from its github with all the remote
headers built and not just the one for the host I'm on. It seems there
is an Android solution for this, but I was hoping for something more
upstream. At this point it is looking to me that libunwind is a dead
project and we may be better just deleting the associated code.
Thanks,
Ian
> Thanks,
> drew
^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH v2 0/8] perf libunwind multiple remote support
2026-02-26 1:34 ` Ian Rogers
@ 2026-03-05 22:19 ` Ian Rogers
2026-03-05 22:19 ` [PATCH v2 1/8] perf unwind: Refactor get_entries to allow dynamic libdw/libunwind selection Ian Rogers
` (8 more replies)
0 siblings, 9 replies; 27+ messages in thread
From: Ian Rogers @ 2026-03-05 22:19 UTC (permalink / raw)
To: irogers
Cc: 9erthalion6, acme, adrian.hunter, alex, alexander.shishkin,
andrew.jones, aou, atrajeev, blakejones, ctshao, dapeng1.mi,
howardchu95, james.clark, john.g.garry, jolsa, leo.yan,
libunwind-devel, linux-arm-kernel, linux-kernel, linux-perf-users,
linux-riscv, mingo, namhyung, palmer, peterz, pjw, shimin.guo,
tglozar, tmricht, will, yuzhuo
Fix the libunwind build for when libdw and libunwind are feature
detected, currently failing with a duplicate symbol.
Refactor the libunwind support so that whenever a remote target is
available, perf functions using the ELF machine can use that remote
target regardless of what the host/local machine is. Migrate existing
libunwind supported architectures like powerpc, arm64 and loongarch so
that they can work in a cross-architecture way. Add support for
RISC-V. Make the code more regular in function names, etc. and avoid
including a C-file. This increases the lines of code. It is similar in
style to the unwind-libdw implementation. It is hoped that the more
uniform nature of the code with help with refactoring the perf
registers for SIMD/APX support.
Aside from local host testing these patches are under tested, in part
as I'm failing to see how to build libunwind with support for multiple
remote targets. Please could I get help in testing.
v2: Move two fixes patches to position 1 and 2 in the series. Fix
struct naming inconsistency, Andrew Jones
<andrew.jones@oss.qualcomm.com>. Fix other inconsistencies and
potential non-x86 build issues.
v1: https://lore.kernel.org/lkml/20260224142938.26088-1-irogers@google.com/
Ian Rogers (8):
perf unwind: Refactor get_entries to allow dynamic libdw/libunwind
selection
perf build loongarch: Remove reference to missing file
tools build: Deduplicate test-libunwind for different architectures
perf build: Be more programmatic when setting up libunwind variables
perf unwind-libunwind: Make libunwind register reading cross platform
perf unwind-libunwind: Move flush/finish access out of local
perf unwind-libunwind: Remove libunwind-local
perf unwind-libunwind: Add RISC-V libunwind support
tools/build/feature/Makefile | 38 +-
tools/build/feature/test-libunwind-aarch64.c | 27 -
tools/build/feature/test-libunwind-arm.c | 28 -
.../test-libunwind-debug-frame-aarch64.c | 17 -
.../feature/test-libunwind-debug-frame-arm.c | 17 -
.../feature/test-libunwind-debug-frame.c | 1 -
tools/build/feature/test-libunwind-x86.c | 28 -
tools/build/feature/test-libunwind-x86_64.c | 28 -
tools/build/feature/test-libunwind.c | 1 -
tools/perf/Makefile.config | 215 ++---
tools/perf/arch/arm/util/Build | 2 -
tools/perf/arch/arm/util/unwind-libunwind.c | 50 --
tools/perf/arch/arm64/util/Build | 1 -
tools/perf/arch/arm64/util/unwind-libunwind.c | 17 -
tools/perf/arch/loongarch/util/Build | 3 -
.../arch/loongarch/util/unwind-libunwind.c | 82 --
tools/perf/arch/mips/Build | 1 -
tools/perf/arch/mips/util/Build | 1 -
tools/perf/arch/mips/util/unwind-libunwind.c | 22 -
tools/perf/arch/powerpc/util/Build | 1 -
.../perf/arch/powerpc/util/unwind-libunwind.c | 92 --
tools/perf/arch/x86/util/Build | 3 -
tools/perf/arch/x86/util/unwind-libunwind.c | 115 ---
tools/perf/builtin-inject.c | 4 +
tools/perf/builtin-report.c | 4 +
tools/perf/builtin-script.c | 4 +
tools/perf/util/Build | 5 +-
tools/perf/util/libunwind-arch/Build | 11 +
.../perf/util/libunwind-arch/libunwind-arch.c | 319 +++++++
.../perf/util/libunwind-arch/libunwind-arch.h | 296 +++++++
.../perf/util/libunwind-arch/libunwind-arm.c | 290 ++++++
.../util/libunwind-arch/libunwind-arm64.c | 289 ++++++
.../perf/util/libunwind-arch/libunwind-i386.c | 312 +++++++
.../util/libunwind-arch/libunwind-loongarch.c | 297 +++++++
.../perf/util/libunwind-arch/libunwind-mips.c | 299 +++++++
.../util/libunwind-arch/libunwind-ppc32.c | 301 +++++++
.../util/libunwind-arch/libunwind-ppc64.c | 303 +++++++
.../util/libunwind-arch/libunwind-riscv.c | 297 +++++++
.../perf/util/libunwind-arch/libunwind-s390.c | 299 +++++++
.../util/libunwind-arch/libunwind-x86_64.c | 320 +++++++
tools/perf/util/libunwind/arm64.c | 40 -
tools/perf/util/libunwind/x86_32.c | 41 -
tools/perf/util/maps.c | 29 +-
tools/perf/util/maps.h | 4 +-
tools/perf/util/symbol_conf.h | 15 +
tools/perf/util/thread.c | 29 +-
tools/perf/util/unwind-libdw.c | 2 +-
tools/perf/util/unwind-libunwind-local.c | 832 ------------------
tools/perf/util/unwind-libunwind.c | 679 ++++++++++++--
tools/perf/util/unwind.c | 102 +++
tools/perf/util/unwind.h | 56 +-
51 files changed, 4536 insertions(+), 1733 deletions(-)
delete mode 100644 tools/build/feature/test-libunwind-aarch64.c
delete mode 100644 tools/build/feature/test-libunwind-arm.c
delete mode 100644 tools/build/feature/test-libunwind-debug-frame-aarch64.c
delete mode 100644 tools/build/feature/test-libunwind-debug-frame-arm.c
delete mode 100644 tools/build/feature/test-libunwind-x86.c
delete mode 100644 tools/build/feature/test-libunwind-x86_64.c
delete mode 100644 tools/perf/arch/arm/util/unwind-libunwind.c
delete mode 100644 tools/perf/arch/arm64/util/unwind-libunwind.c
delete mode 100644 tools/perf/arch/loongarch/util/unwind-libunwind.c
delete mode 100644 tools/perf/arch/mips/Build
delete mode 100644 tools/perf/arch/mips/util/Build
delete mode 100644 tools/perf/arch/mips/util/unwind-libunwind.c
delete mode 100644 tools/perf/arch/powerpc/util/unwind-libunwind.c
delete mode 100644 tools/perf/arch/x86/util/unwind-libunwind.c
create mode 100644 tools/perf/util/libunwind-arch/Build
create mode 100644 tools/perf/util/libunwind-arch/libunwind-arch.c
create mode 100644 tools/perf/util/libunwind-arch/libunwind-arch.h
create mode 100644 tools/perf/util/libunwind-arch/libunwind-arm.c
create mode 100644 tools/perf/util/libunwind-arch/libunwind-arm64.c
create mode 100644 tools/perf/util/libunwind-arch/libunwind-i386.c
create mode 100644 tools/perf/util/libunwind-arch/libunwind-loongarch.c
create mode 100644 tools/perf/util/libunwind-arch/libunwind-mips.c
create mode 100644 tools/perf/util/libunwind-arch/libunwind-ppc32.c
create mode 100644 tools/perf/util/libunwind-arch/libunwind-ppc64.c
create mode 100644 tools/perf/util/libunwind-arch/libunwind-riscv.c
create mode 100644 tools/perf/util/libunwind-arch/libunwind-s390.c
create mode 100644 tools/perf/util/libunwind-arch/libunwind-x86_64.c
delete mode 100644 tools/perf/util/libunwind/arm64.c
delete mode 100644 tools/perf/util/libunwind/x86_32.c
delete mode 100644 tools/perf/util/unwind-libunwind-local.c
create mode 100644 tools/perf/util/unwind.c
--
2.53.0.473.g4a7958ca14-goog
^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH v2 1/8] perf unwind: Refactor get_entries to allow dynamic libdw/libunwind selection
2026-03-05 22:19 ` [PATCH v2 0/8] perf libunwind multiple remote support Ian Rogers
@ 2026-03-05 22:19 ` Ian Rogers
2026-03-05 22:19 ` [PATCH v2 2/8] perf build loongarch: Remove reference to missing file Ian Rogers
` (7 subsequent siblings)
8 siblings, 0 replies; 27+ messages in thread
From: Ian Rogers @ 2026-03-05 22:19 UTC (permalink / raw)
To: irogers
Cc: 9erthalion6, acme, adrian.hunter, alex, alexander.shishkin,
andrew.jones, aou, atrajeev, blakejones, ctshao, dapeng1.mi,
howardchu95, james.clark, john.g.garry, jolsa, leo.yan,
libunwind-devel, linux-arm-kernel, linux-kernel, linux-perf-users,
linux-riscv, mingo, namhyung, palmer, peterz, pjw, shimin.guo,
tglozar, tmricht, will, yuzhuo
Currently, both libdw and libunwind define 'unwind__get_entries'. This
causes a duplicate symbol build failure when both are compiled into
perf.
This commit refactors the DWARF unwind post-processing to be
configurable at runtime via the .perfconfig file option
'unwind.style', or using the argument '--unwind-style' in the commands
'perf report', 'perf script' and 'perf inject', in a similar manner to
the addr2line or the disassembler style.
The file 'tools/perf/util/unwind.c' adds the top-level dispatch
function 'unwind__get_entries'. The backend implementations are
renamed to 'libdw__get_entries' and 'libunwind__get_entries'. Both are
attempted as fallbacks if not configured, or if the primary backend
fails.
Fixes: 2e9191573a69 ("perf build: Remove NO_LIBDW_DWARF_UNWIND option")
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/builtin-inject.c | 4 ++
tools/perf/builtin-report.c | 4 ++
tools/perf/builtin-script.c | 4 ++
tools/perf/util/Build | 1 +
tools/perf/util/symbol_conf.h | 15 +++++
tools/perf/util/unwind-libdw.c | 2 +-
tools/perf/util/unwind-libunwind.c | 2 +-
tools/perf/util/unwind.c | 102 +++++++++++++++++++++++++++++
tools/perf/util/unwind.h | 40 +++++------
9 files changed, 149 insertions(+), 25 deletions(-)
create mode 100644 tools/perf/util/unwind.c
diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c
index 5b29f4296861..9ad681b3c0dc 100644
--- a/tools/perf/builtin-inject.c
+++ b/tools/perf/builtin-inject.c
@@ -26,6 +26,7 @@
#include "util/synthetic-events.h"
#include "util/thread.h"
#include "util/namespaces.h"
+#include "util/unwind.h"
#include "util/util.h"
#include "util/tsc.h"
@@ -2539,6 +2540,9 @@ int cmd_inject(int argc, const char **argv)
OPT_STRING(0, "guestmount", &symbol_conf.guestmount, "directory",
"guest mount directory under which every guest os"
" instance has a subdir"),
+ OPT_CALLBACK(0, "unwind-style", NULL, "unwind style",
+ "unwind styles (libdw,libunwind)",
+ unwind__option),
OPT_BOOLEAN(0, "convert-callchain", &inject.convert_callchain,
"Generate callchains using DWARF and drop register/stack data"),
OPT_END()
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index 3b81f4b3dc49..ae20c0679990 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -48,6 +48,7 @@
#include "util/time-utils.h"
#include "util/auxtrace.h"
#include "util/units.h"
+#include "util/unwind.h"
#include "util/util.h" // perf_tip()
#include "ui/ui.h"
#include "ui/progress.h"
@@ -1455,6 +1456,9 @@ int cmd_report(int argc, const char **argv)
OPT_CALLBACK(0, "addr2line-style", NULL, "addr2line style",
"addr2line styles (libdw,llvm,libbfd,addr2line)",
report_parse_addr2line_config),
+ OPT_CALLBACK(0, "unwind-style", NULL, "unwind style",
+ "unwind styles (libdw,libunwind)",
+ unwind__option),
OPT_BOOLEAN(0, "demangle", &symbol_conf.demangle,
"Symbol demangling. Enabled by default, use --no-demangle to disable."),
OPT_BOOLEAN(0, "demangle-kernel", &symbol_conf.demangle_kernel,
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c
index 9f8b0fd27a0a..b6c7c164d02d 100644
--- a/tools/perf/builtin-script.c
+++ b/tools/perf/builtin-script.c
@@ -63,6 +63,7 @@
#include <linux/err.h>
#include "util/dlfilter.h"
#include "util/record.h"
+#include "util/unwind.h"
#include "util/util.h"
#include "util/cgroup.h"
#include "util/annotate.h"
@@ -4164,6 +4165,9 @@ int cmd_script(int argc, const char **argv)
"Enable symbol demangling"),
OPT_BOOLEAN(0, "demangle-kernel", &symbol_conf.demangle_kernel,
"Enable kernel symbol demangling"),
+ OPT_CALLBACK(0, "unwind-style", NULL, "unwind style",
+ "unwind styles (libdw,libunwind)",
+ unwind__option),
OPT_STRING(0, "addr2line", &symbol_conf.addr2line_path, "path",
"addr2line binary to use for line numbers"),
OPT_STRING(0, "time", &script.time_str, "str",
diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index bcccad7487a9..6190a8f5b0fa 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -218,6 +218,7 @@ ifndef CONFIG_SETNS
perf-util-y += setns.o
endif
+perf-util-y += unwind.o
perf-util-$(CONFIG_LIBDW) += probe-finder.o
perf-util-$(CONFIG_LIBDW) += dwarf-aux.o
perf-util-$(CONFIG_LIBDW) += dwarf-regs.o
diff --git a/tools/perf/util/symbol_conf.h b/tools/perf/util/symbol_conf.h
index 71bb17372a6c..25d92bbbfee7 100644
--- a/tools/perf/util/symbol_conf.h
+++ b/tools/perf/util/symbol_conf.h
@@ -9,6 +9,19 @@
struct strlist;
struct intlist;
+enum unwind_style {
+
+ UNWIND_STYLE_UNKNOWN = 0,
+
+ UNWIND_STYLE_LIBDW,
+
+ UNWIND_STYLE_LIBUNWIND,
+
+};
+
+#define MAX_UNWIND_STYLE (UNWIND_STYLE_LIBUNWIND + 1)
+
+
enum a2l_style {
A2L_STYLE_UNKNOWN = 0,
A2L_STYLE_LIBDW,
@@ -80,6 +93,7 @@ struct symbol_conf {
*bt_stop_list_str;
const char *addr2line_path;
enum a2l_style addr2line_style[MAX_A2L_STYLE];
+ enum unwind_style unwind_style[MAX_UNWIND_STYLE];
unsigned long time_quantum;
struct strlist *dso_list,
*comm_list,
@@ -103,3 +117,4 @@ struct symbol_conf {
extern struct symbol_conf symbol_conf;
#endif // __PERF_SYMBOL_CONF
+
diff --git a/tools/perf/util/unwind-libdw.c b/tools/perf/util/unwind-libdw.c
index 05e8e68bd49c..d8a5b7d54192 100644
--- a/tools/perf/util/unwind-libdw.c
+++ b/tools/perf/util/unwind-libdw.c
@@ -339,7 +339,7 @@ frame_callback(Dwfl_Frame *state, void *arg)
DWARF_CB_ABORT : DWARF_CB_OK;
}
-int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
+int libdw__get_entries(unwind_entry_cb_t cb, void *arg,
struct thread *thread,
struct perf_sample *data,
int max_stack,
diff --git a/tools/perf/util/unwind-libunwind.c b/tools/perf/util/unwind-libunwind.c
index cb8be6acfb6f..a0016b897dae 100644
--- a/tools/perf/util/unwind-libunwind.c
+++ b/tools/perf/util/unwind-libunwind.c
@@ -79,7 +79,7 @@ void unwind__finish_access(struct maps *maps)
ops->finish_access(maps);
}
-int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
+int libunwind__get_entries(unwind_entry_cb_t cb, void *arg,
struct thread *thread,
struct perf_sample *data, int max_stack,
bool best_effort)
diff --git a/tools/perf/util/unwind.c b/tools/perf/util/unwind.c
new file mode 100644
index 000000000000..9ae7d5ad246d
--- /dev/null
+++ b/tools/perf/util/unwind.c
@@ -0,0 +1,102 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "debug.h"
+#include "symbol_conf.h"
+#include "unwind.h"
+#include <linux/string.h>
+#include <string.h>
+#include <stdlib.h>
+
+int unwind__get_entries(unwind_entry_cb_t cb __maybe_unused, void *arg __maybe_unused,
+ struct thread *thread __maybe_unused,
+ struct perf_sample *data __maybe_unused,
+ int max_stack __maybe_unused,
+ bool best_effort __maybe_unused)
+{
+ int ret = 0;
+
+#if defined(HAVE_LIBDW_SUPPORT) || defined(HAVE_LIBUNWIND_SUPPORT)
+ if (symbol_conf.unwind_style[0] == UNWIND_STYLE_UNKNOWN) {
+ int i = 0;
+#ifdef HAVE_LIBDW_SUPPORT
+ symbol_conf.unwind_style[i++] = UNWIND_STYLE_LIBDW;
+#endif
+#ifdef HAVE_LIBUNWIND_SUPPORT
+ symbol_conf.unwind_style[i++] = UNWIND_STYLE_LIBUNWIND;
+#endif
+ }
+#endif //defined(HAVE_LIBDW_SUPPORT) || defined(HAVE_LIBUNWIND_SUPPORT)
+
+ for (size_t i = 0; i < ARRAY_SIZE(symbol_conf.unwind_style); i++) {
+ switch (symbol_conf.unwind_style[i]) {
+ case UNWIND_STYLE_LIBDW:
+#ifdef HAVE_LIBDW_SUPPORT
+ ret = libdw__get_entries(cb, arg, thread, data, max_stack, best_effort);
+#endif
+ break;
+ case UNWIND_STYLE_LIBUNWIND:
+#ifdef HAVE_LIBUNWIND_SUPPORT
+ ret = libunwind__get_entries(cb, arg, thread, data, max_stack, best_effort);
+#endif
+ break;
+ case UNWIND_STYLE_UNKNOWN:
+ default:
+#if !defined(HAVE_LIBDW_SUPPORT) && !defined(HAVE_LIBUNWIND_SUPPORT)
+ pr_warning_once(
+ "Error: dwarf unwinding not supported, build perf with libdw or libunwind.\n");
+#endif
+ ret = -1;
+ break;
+ }
+ if (ret == 0)
+ break;
+ }
+ return ret;
+}
+
+int unwind__configure(const char *var, const char *value, void *cb __maybe_unused)
+{
+ static const char * const unwind_style_names[] = {
+ [UNWIND_STYLE_LIBDW] = "libdw",
+ [UNWIND_STYLE_LIBUNWIND] = "libunwind",
+ NULL
+ };
+ char *s, *p, *saveptr;
+ size_t i = 0;
+
+ if (strcmp(var, "unwind.style"))
+ return 0;
+
+ if (!value)
+ return -1;
+
+ s = strdup(value);
+ if (!s)
+ return -1;
+
+ p = strtok_r(s, ",", &saveptr);
+ while (p && i < ARRAY_SIZE(symbol_conf.unwind_style)) {
+ bool found = false;
+ char *q = strim(p);
+
+ for (size_t j = UNWIND_STYLE_LIBDW; j < MAX_UNWIND_STYLE; j++) {
+ if (!strcasecmp(q, unwind_style_names[j])) {
+ symbol_conf.unwind_style[i++] = j;
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ pr_warning("Unknown unwind style: %s\n", q);
+ p = strtok_r(NULL, ",", &saveptr);
+ }
+
+ free(s);
+ return 0;
+}
+
+int unwind__option(const struct option *opt __maybe_unused,
+ const char *arg,
+ int unset __maybe_unused)
+{
+ return unwind__configure("unwind.style", arg, NULL);
+}
diff --git a/tools/perf/util/unwind.h b/tools/perf/util/unwind.h
index 9f7164c6d9aa..581d042e170a 100644
--- a/tools/perf/util/unwind.h
+++ b/tools/perf/util/unwind.h
@@ -7,6 +7,7 @@
#include "util/map_symbol.h"
struct maps;
+struct option;
struct perf_sample;
struct thread;
@@ -26,7 +27,9 @@ struct unwind_libunwind_ops {
struct perf_sample *data, int max_stack, bool best_effort);
};
-#ifdef HAVE_DWARF_UNWIND_SUPPORT
+int unwind__configure(const char *var, const char *value, void *cb);
+int unwind__option(const struct option *opt, const char *arg, int unset);
+
/*
* When best_effort is set, don't report errors and fail silently. This could
* be expanded in the future to be more permissive about things other than
@@ -36,8 +39,20 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
struct thread *thread,
struct perf_sample *data, int max_stack,
bool best_effort);
-/* libunwind specific */
+
+#ifdef HAVE_LIBDW_SUPPORT
+int libdw__get_entries(unwind_entry_cb_t cb, void *arg,
+ struct thread *thread,
+ struct perf_sample *data, int max_stack,
+ bool best_effort);
+#endif
+
#ifdef HAVE_LIBUNWIND_SUPPORT
+/* libunwind specific */
+int libunwind__get_entries(unwind_entry_cb_t cb, void *arg,
+ struct thread *thread,
+ struct perf_sample *data, int max_stack,
+ bool best_effort);
#ifndef LIBUNWIND__ARCH_REG_ID
#define LIBUNWIND__ARCH_REG_ID(regnum) libunwind__arch_reg_id(regnum)
#endif
@@ -57,26 +72,5 @@ static inline int unwind__prepare_access(struct maps *maps __maybe_unused,
static inline void unwind__flush_access(struct maps *maps __maybe_unused) {}
static inline void unwind__finish_access(struct maps *maps __maybe_unused) {}
#endif
-#else
-static inline int
-unwind__get_entries(unwind_entry_cb_t cb __maybe_unused,
- void *arg __maybe_unused,
- struct thread *thread __maybe_unused,
- struct perf_sample *data __maybe_unused,
- int max_stack __maybe_unused,
- bool best_effort __maybe_unused)
-{
- return 0;
-}
-
-static inline int unwind__prepare_access(struct maps *maps __maybe_unused,
- struct map *map __maybe_unused,
- bool *initialized __maybe_unused)
-{
- return 0;
-}
-static inline void unwind__flush_access(struct maps *maps __maybe_unused) {}
-static inline void unwind__finish_access(struct maps *maps __maybe_unused) {}
-#endif /* HAVE_DWARF_UNWIND_SUPPORT */
#endif /* __UNWIND_H */
--
2.53.0.473.g4a7958ca14-goog
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH v2 2/8] perf build loongarch: Remove reference to missing file
2026-03-05 22:19 ` [PATCH v2 0/8] perf libunwind multiple remote support Ian Rogers
2026-03-05 22:19 ` [PATCH v2 1/8] perf unwind: Refactor get_entries to allow dynamic libdw/libunwind selection Ian Rogers
@ 2026-03-05 22:19 ` Ian Rogers
2026-03-05 22:19 ` [PATCH v2 3/8] tools build: Deduplicate test-libunwind for different architectures Ian Rogers
` (6 subsequent siblings)
8 siblings, 0 replies; 27+ messages in thread
From: Ian Rogers @ 2026-03-05 22:19 UTC (permalink / raw)
To: irogers
Cc: 9erthalion6, acme, adrian.hunter, alex, alexander.shishkin,
andrew.jones, aou, atrajeev, blakejones, ctshao, dapeng1.mi,
howardchu95, james.clark, john.g.garry, jolsa, leo.yan,
libunwind-devel, linux-arm-kernel, linux-kernel, linux-perf-users,
linux-riscv, mingo, namhyung, palmer, peterz, pjw, shimin.guo,
tglozar, tmricht, will, yuzhuo
The file was removed in commit e62fae9d9e85 ("perf unwind-libdw: Fix a
cross-arch unwinding bug") but the Build file not updated.
Fixes: commit e62fae9d9e85 ("perf unwind-libdw: Fix a cross-arch unwinding bug")
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/arch/loongarch/util/Build | 1 -
1 file changed, 1 deletion(-)
diff --git a/tools/perf/arch/loongarch/util/Build b/tools/perf/arch/loongarch/util/Build
index 3ad73d0289f3..8d91e78d31c9 100644
--- a/tools/perf/arch/loongarch/util/Build
+++ b/tools/perf/arch/loongarch/util/Build
@@ -1,4 +1,3 @@
perf-util-y += header.o
perf-util-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o
-perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
--
2.53.0.473.g4a7958ca14-goog
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH v2 3/8] tools build: Deduplicate test-libunwind for different architectures
2026-03-05 22:19 ` [PATCH v2 0/8] perf libunwind multiple remote support Ian Rogers
2026-03-05 22:19 ` [PATCH v2 1/8] perf unwind: Refactor get_entries to allow dynamic libdw/libunwind selection Ian Rogers
2026-03-05 22:19 ` [PATCH v2 2/8] perf build loongarch: Remove reference to missing file Ian Rogers
@ 2026-03-05 22:19 ` Ian Rogers
2026-03-05 22:19 ` [PATCH v2 4/8] perf build: Be more programmatic when setting up libunwind variables Ian Rogers
` (5 subsequent siblings)
8 siblings, 0 replies; 27+ messages in thread
From: Ian Rogers @ 2026-03-05 22:19 UTC (permalink / raw)
To: irogers
Cc: 9erthalion6, acme, adrian.hunter, alex, alexander.shishkin,
andrew.jones, aou, atrajeev, blakejones, ctshao, dapeng1.mi,
howardchu95, james.clark, john.g.garry, jolsa, leo.yan,
libunwind-devel, linux-arm-kernel, linux-kernel, linux-perf-users,
linux-riscv, mingo, namhyung, palmer, peterz, pjw, shimin.guo,
tglozar, tmricht, will, yuzhuo
The separate test files only exist to pass a different #include,
instead have a single source file and pass -include to $(CC) to
include the relevant header file for the architecture being
tested. Generate the rules using a foreach loop. Include tests for all
current libunwind architectures.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/build/feature/Makefile | 38 +++++++++----------
tools/build/feature/test-libunwind-aarch64.c | 27 -------------
tools/build/feature/test-libunwind-arm.c | 28 --------------
.../test-libunwind-debug-frame-aarch64.c | 17 ---------
.../feature/test-libunwind-debug-frame-arm.c | 17 ---------
.../feature/test-libunwind-debug-frame.c | 1 -
tools/build/feature/test-libunwind-x86.c | 28 --------------
tools/build/feature/test-libunwind-x86_64.c | 28 --------------
tools/build/feature/test-libunwind.c | 1 -
9 files changed, 19 insertions(+), 166 deletions(-)
delete mode 100644 tools/build/feature/test-libunwind-aarch64.c
delete mode 100644 tools/build/feature/test-libunwind-arm.c
delete mode 100644 tools/build/feature/test-libunwind-debug-frame-aarch64.c
delete mode 100644 tools/build/feature/test-libunwind-debug-frame-arm.c
delete mode 100644 tools/build/feature/test-libunwind-x86.c
delete mode 100644 tools/build/feature/test-libunwind-x86_64.c
diff --git a/tools/build/feature/Makefile b/tools/build/feature/Makefile
index 1fbcb3ce74d2..a0b40e826688 100644
--- a/tools/build/feature/Makefile
+++ b/tools/build/feature/Makefile
@@ -204,27 +204,27 @@ $(OUTPUT)test-numa_num_possible_cpus.bin:
$(BUILD) -lnuma
$(OUTPUT)test-libunwind.bin:
- $(BUILD) -lelf -llzma
+ $(BUILD) -include libunwind.h -lelf -llzma -lunwind
$(OUTPUT)test-libunwind-debug-frame.bin:
- $(BUILD) -lelf -llzma
-$(OUTPUT)test-libunwind-x86.bin:
- $(BUILD) -lelf -llzma -lunwind-x86
-
-$(OUTPUT)test-libunwind-x86_64.bin:
- $(BUILD) -lelf -llzma -lunwind-x86_64
-
-$(OUTPUT)test-libunwind-arm.bin:
- $(BUILD) -lelf -llzma -lunwind-arm
-
-$(OUTPUT)test-libunwind-aarch64.bin:
- $(BUILD) -lelf -llzma -lunwind-aarch64
-
-$(OUTPUT)test-libunwind-debug-frame-arm.bin:
- $(BUILD) -lelf -llzma -lunwind-arm
-
-$(OUTPUT)test-libunwind-debug-frame-aarch64.bin:
- $(BUILD) -lelf -llzma -lunwind-aarch64
+ $(BUILD) -include libunwind.h -lelf -llzma -lunwind
+
+LIBUNWIND_ARCHS:=aarch64 arm loongarch64 mips ppc32 ppc64 riscv s390x x86 x86_64
+define LIBUNWIND_RULE
+$$(OUTPUT)test-libunwind-$(1).bin:
+ $$(CC) $$(CFLAGS) -MD -Wall -Werror -include libunwind-$(1).h -o $$@ \
+ test-libunwind.c $$(LDFLAGS) -lelf -llzma -lunwind-$(1) \
+ > $$(@:.bin=.make.output) 2>&1
+
+$$(OUTPUT)test-libunwind-debug-frame-$(1).bin:
+ $$(CC) $$(CFLAGS) -MD -Wall -Werror -include libunwind-$(1).h -o $$@ \
+ test-libunwind-debug-frame.c $$(LDFLAGS) -lelf -llzma -lunwind-$(1) \
+ > $$(@:.bin=.make.output) 2>&1
+
+endef
+$(foreach arch,$(LIBUNWIND_ARCHS), \
+ $(eval $(call LIBUNWIND_RULE,$(arch))) \
+)
$(OUTPUT)test-libslang.bin:
$(BUILD) -lslang
diff --git a/tools/build/feature/test-libunwind-aarch64.c b/tools/build/feature/test-libunwind-aarch64.c
deleted file mode 100644
index 323803f49212..000000000000
--- a/tools/build/feature/test-libunwind-aarch64.c
+++ /dev/null
@@ -1,27 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-#include <libunwind-aarch64.h>
-#include <stdlib.h>
-
-extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,
- unw_word_t ip,
- unw_dyn_info_t *di,
- unw_proc_info_t *pi,
- int need_unwind_info, void *arg);
-
-#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table)
-
-static unw_accessors_t accessors;
-
-int main(void)
-{
- unw_addr_space_t addr_space;
-
- addr_space = unw_create_addr_space(&accessors, 0);
- if (addr_space)
- return 0;
-
- unw_init_remote(NULL, addr_space, NULL);
- dwarf_search_unwind_table(addr_space, 0, NULL, NULL, 0, NULL);
-
- return 0;
-}
diff --git a/tools/build/feature/test-libunwind-arm.c b/tools/build/feature/test-libunwind-arm.c
deleted file mode 100644
index cb378b7d6866..000000000000
--- a/tools/build/feature/test-libunwind-arm.c
+++ /dev/null
@@ -1,28 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-#include <libunwind-arm.h>
-#include <stdlib.h>
-
-extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,
- unw_word_t ip,
- unw_dyn_info_t *di,
- unw_proc_info_t *pi,
- int need_unwind_info, void *arg);
-
-
-#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table)
-
-static unw_accessors_t accessors;
-
-int main(void)
-{
- unw_addr_space_t addr_space;
-
- addr_space = unw_create_addr_space(&accessors, 0);
- if (addr_space)
- return 0;
-
- unw_init_remote(NULL, addr_space, NULL);
- dwarf_search_unwind_table(addr_space, 0, NULL, NULL, 0, NULL);
-
- return 0;
-}
diff --git a/tools/build/feature/test-libunwind-debug-frame-aarch64.c b/tools/build/feature/test-libunwind-debug-frame-aarch64.c
deleted file mode 100644
index 36d6646c185e..000000000000
--- a/tools/build/feature/test-libunwind-debug-frame-aarch64.c
+++ /dev/null
@@ -1,17 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-#include <libunwind-aarch64.h>
-#include <stdlib.h>
-
-extern int
-UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug,
- unw_word_t ip, unw_word_t segbase,
- const char *obj_name, unw_word_t start,
- unw_word_t end);
-
-#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame)
-
-int main(void)
-{
- dwarf_find_debug_frame(0, NULL, 0, 0, NULL, 0, 0);
- return 0;
-}
diff --git a/tools/build/feature/test-libunwind-debug-frame-arm.c b/tools/build/feature/test-libunwind-debug-frame-arm.c
deleted file mode 100644
index 8696e48e1268..000000000000
--- a/tools/build/feature/test-libunwind-debug-frame-arm.c
+++ /dev/null
@@ -1,17 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-#include <libunwind-arm.h>
-#include <stdlib.h>
-
-extern int
-UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug,
- unw_word_t ip, unw_word_t segbase,
- const char *obj_name, unw_word_t start,
- unw_word_t end);
-
-#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame)
-
-int main(void)
-{
- dwarf_find_debug_frame(0, NULL, 0, 0, NULL, 0, 0);
- return 0;
-}
diff --git a/tools/build/feature/test-libunwind-debug-frame.c b/tools/build/feature/test-libunwind-debug-frame.c
index efb55cdd8d01..4c57e37004b3 100644
--- a/tools/build/feature/test-libunwind-debug-frame.c
+++ b/tools/build/feature/test-libunwind-debug-frame.c
@@ -1,5 +1,4 @@
// SPDX-License-Identifier: GPL-2.0
-#include <libunwind.h>
#include <stdlib.h>
extern int
diff --git a/tools/build/feature/test-libunwind-x86.c b/tools/build/feature/test-libunwind-x86.c
deleted file mode 100644
index e5e0f6c89637..000000000000
--- a/tools/build/feature/test-libunwind-x86.c
+++ /dev/null
@@ -1,28 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-#include <libunwind-x86.h>
-#include <stdlib.h>
-
-extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,
- unw_word_t ip,
- unw_dyn_info_t *di,
- unw_proc_info_t *pi,
- int need_unwind_info, void *arg);
-
-
-#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table)
-
-static unw_accessors_t accessors;
-
-int main(void)
-{
- unw_addr_space_t addr_space;
-
- addr_space = unw_create_addr_space(&accessors, 0);
- if (addr_space)
- return 0;
-
- unw_init_remote(NULL, addr_space, NULL);
- dwarf_search_unwind_table(addr_space, 0, NULL, NULL, 0, NULL);
-
- return 0;
-}
diff --git a/tools/build/feature/test-libunwind-x86_64.c b/tools/build/feature/test-libunwind-x86_64.c
deleted file mode 100644
index 62ae4db597dc..000000000000
--- a/tools/build/feature/test-libunwind-x86_64.c
+++ /dev/null
@@ -1,28 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-#include <libunwind-x86_64.h>
-#include <stdlib.h>
-
-extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,
- unw_word_t ip,
- unw_dyn_info_t *di,
- unw_proc_info_t *pi,
- int need_unwind_info, void *arg);
-
-
-#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table)
-
-static unw_accessors_t accessors;
-
-int main(void)
-{
- unw_addr_space_t addr_space;
-
- addr_space = unw_create_addr_space(&accessors, 0);
- if (addr_space)
- return 0;
-
- unw_init_remote(NULL, addr_space, NULL);
- dwarf_search_unwind_table(addr_space, 0, NULL, NULL, 0, NULL);
-
- return 0;
-}
diff --git a/tools/build/feature/test-libunwind.c b/tools/build/feature/test-libunwind.c
index 53fd26614ff0..5af5dc3a73d4 100644
--- a/tools/build/feature/test-libunwind.c
+++ b/tools/build/feature/test-libunwind.c
@@ -1,5 +1,4 @@
// SPDX-License-Identifier: GPL-2.0
-#include <libunwind.h>
#include <stdlib.h>
extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,
--
2.53.0.473.g4a7958ca14-goog
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH v2 4/8] perf build: Be more programmatic when setting up libunwind variables
2026-03-05 22:19 ` [PATCH v2 0/8] perf libunwind multiple remote support Ian Rogers
` (2 preceding siblings ...)
2026-03-05 22:19 ` [PATCH v2 3/8] tools build: Deduplicate test-libunwind for different architectures Ian Rogers
@ 2026-03-05 22:19 ` Ian Rogers
2026-03-05 22:19 ` [PATCH v2 5/8] perf unwind-libunwind: Make libunwind register reading cross platform Ian Rogers
` (4 subsequent siblings)
8 siblings, 0 replies; 27+ messages in thread
From: Ian Rogers @ 2026-03-05 22:19 UTC (permalink / raw)
To: irogers
Cc: 9erthalion6, acme, adrian.hunter, alex, alexander.shishkin,
andrew.jones, aou, atrajeev, blakejones, ctshao, dapeng1.mi,
howardchu95, james.clark, john.g.garry, jolsa, leo.yan,
libunwind-devel, linux-arm-kernel, linux-kernel, linux-perf-users,
linux-riscv, mingo, namhyung, palmer, peterz, pjw, shimin.guo,
tglozar, tmricht, will, yuzhuo
Iterate LIBUNWIND_ARCHS when setting up CONFIG_ and HAVE_ definitions
rather than treating each architecture individually. This sets up the
libunwind build variables and C definitions beyond x86 and
arm/aarch64. The existing naming convention is followed for
compatibility.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/Makefile.config | 215 +++++++++++++++----------------------
1 file changed, 89 insertions(+), 126 deletions(-)
diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config
index a8dc72cfe48e..f38213ecce08 100644
--- a/tools/perf/Makefile.config
+++ b/tools/perf/Makefile.config
@@ -65,95 +65,83 @@ $(call detected_var,SRCARCH)
CFLAGS += -I$(OUTPUT)arch/$(SRCARCH)/include/generated
-# Additional ARCH settings for ppc
-ifeq ($(SRCARCH),powerpc)
- ifndef NO_LIBUNWIND
- LIBUNWIND_LIBS := -lunwind -lunwind-ppc64
- endif
-endif
-
# Additional ARCH settings for x86
ifeq ($(SRCARCH),x86)
$(call detected,CONFIG_X86)
ifeq (${IS_64_BIT}, 1)
CFLAGS += -DHAVE_ARCH_X86_64_SUPPORT
ARCH_INCLUDE = ../../arch/x86/lib/memcpy_64.S ../../arch/x86/lib/memset_64.S
- ifndef NO_LIBUNWIND
- LIBUNWIND_LIBS = -lunwind-x86_64 -lunwind -llzma
- endif
$(call detected,CONFIG_X86_64)
- else
- ifndef NO_LIBUNWIND
- LIBUNWIND_LIBS = -lunwind-x86 -llzma -lunwind
- endif
endif
endif
-ifeq ($(SRCARCH),arm)
- ifndef NO_LIBUNWIND
- LIBUNWIND_LIBS = -lunwind -lunwind-arm
- endif
+ifeq ($(ARCH),s390)
+ CFLAGS += -fPIC
endif
-ifeq ($(SRCARCH),arm64)
- ifndef NO_LIBUNWIND
- LIBUNWIND_LIBS = -lunwind -lunwind-aarch64
- endif
+ifneq ($(LIBUNWIND),1)
+ NO_LIBUNWIND := 1
endif
-ifeq ($(SRCARCH),loongarch)
- ifndef NO_LIBUNWIND
+ifndef NO_LIBUNWIND
+ ifeq ($(SRCARCH),arm)
+ LIBUNWIND_LIBS = -lunwind -lunwind-arm
+ endif
+ ifeq ($(SRCARCH),arm64)
+ LIBUNWIND_LIBS = -lunwind -lunwind-aarch64
+ endif
+ ifeq ($(SRCARCH),loongarch)
LIBUNWIND_LIBS = -lunwind -lunwind-loongarch64
endif
-endif
-
-ifeq ($(ARCH),s390)
- CFLAGS += -fPIC
-endif
-
-ifeq ($(ARCH),mips)
- ifndef NO_LIBUNWIND
+ ifeq ($(ARCH),mips)
LIBUNWIND_LIBS = -lunwind -lunwind-mips
endif
+ ifeq ($(SRCARCH),powerpc)
+ LIBUNWIND_LIBS := -lunwind -lunwind-ppc64
+ endif
+ ifeq ($(SRCARCH),riscv)
+ LIBUNWIND_LIBS := -lunwind -lunwind-riscv
+ endif
+ ifeq ($(SRCARCH),s390)
+ LIBUNWIND_LIBS := -lunwind -lunwind-s390x
+ endif
+ ifeq ($(SRCARCH),x86)
+ ifeq (${IS_64_BIT}, 1)
+ LIBUNWIND_LIBS = -lunwind-x86_64 -lunwind -llzma
+ else
+ LIBUNWIND_LIBS = -lunwind-x86 -lunwind -llzma
+ endif
+ endif
+ ifeq ($(LIBUNWIND_LIBS),)
+ NO_LIBUNWIND := 1
+ endif
endif
-ifneq ($(LIBUNWIND),1)
- NO_LIBUNWIND := 1
-endif
-
-ifeq ($(LIBUNWIND_LIBS),)
- NO_LIBUNWIND := 1
-endif
#
# For linking with debug library, run like:
#
# make DEBUG=1 LIBUNWIND_DIR=/opt/libunwind/
#
-
-libunwind_arch_set_flags = $(eval $(libunwind_arch_set_flags_code))
-define libunwind_arch_set_flags_code
- FEATURE_CHECK_CFLAGS-libunwind-$(1) = -I$(LIBUNWIND_DIR)/include
- FEATURE_CHECK_LDFLAGS-libunwind-$(1) = -L$(LIBUNWIND_DIR)/lib
-endef
-
-ifdef LIBUNWIND_DIR
- LIBUNWIND_CFLAGS = -I$(LIBUNWIND_DIR)/include
- LIBUNWIND_LDFLAGS = -L$(LIBUNWIND_DIR)/lib
- LIBUNWIND_ARCHS = x86 x86_64 arm aarch64 debug-frame-arm debug-frame-aarch64 loongarch
- $(foreach libunwind_arch,$(LIBUNWIND_ARCHS),$(call libunwind_arch_set_flags,$(libunwind_arch)))
-endif
+LIBUNWIND_ARCHS:=aarch64 arm loongarch64 mips ppc32 ppc64 riscv s390x x86 x86_64
ifndef NO_LIBUNWIND
- # Set per-feature check compilation flags
FEATURE_CHECK_CFLAGS-libunwind = $(LIBUNWIND_CFLAGS)
FEATURE_CHECK_LDFLAGS-libunwind = $(LIBUNWIND_LDFLAGS) $(LIBUNWIND_LIBS)
FEATURE_CHECK_CFLAGS-libunwind-debug-frame = $(LIBUNWIND_CFLAGS)
FEATURE_CHECK_LDFLAGS-libunwind-debug-frame = $(LIBUNWIND_LDFLAGS) $(LIBUNWIND_LIBS)
-
- FEATURE_CHECK_LDFLAGS-libunwind-arm += -lunwind -lunwind-arm
- FEATURE_CHECK_LDFLAGS-libunwind-aarch64 += -lunwind -lunwind-aarch64
- FEATURE_CHECK_LDFLAGS-libunwind-x86 += -lunwind -llzma -lunwind-x86
- FEATURE_CHECK_LDFLAGS-libunwind-x86_64 += -lunwind -llzma -lunwind-x86_64
+
+ ifdef LIBUNWIND_DIR
+ LIBUNWIND_CFLAGS = -I$(LIBUNWIND_DIR)/include
+ LIBUNWIND_LDFLAGS = -L$(LIBUNWIND_DIR)/lib
+
+ define libunwind_arch_set_flags
+ FEATURE_CHECK_CFLAGS-libunwind-$(1) = -I$(LIBUNWIND_DIR)/include
+ FEATURE_CHECK_LDFLAGS-libunwind-$(1) = -L$(LIBUNWIND_DIR)/lib
+ endef
+ $(foreach arch,$(LIBUNWIND_ARCHS), \
+ $(eval $(call libunwind_arch_set_flags,$(arch))) \
+ )
+ endif
endif
ifdef CSINCLUDES
@@ -637,49 +625,6 @@ ifeq ($(SRCARCH),powerpc)
endif
endif
-ifndef NO_LIBUNWIND
- have_libunwind :=
-
- $(call feature_check,libunwind)
-
- $(call feature_check,libunwind-x86)
- ifeq ($(feature-libunwind-x86), 1)
- $(call detected,CONFIG_LIBUNWIND_X86)
- CFLAGS += -DHAVE_LIBUNWIND_X86_SUPPORT
- LDFLAGS += -lunwind-x86
- EXTLIBS_LIBUNWIND += -lunwind-x86
- have_libunwind = 1
- endif
-
- $(call feature_check,libunwind-aarch64)
- ifeq ($(feature-libunwind-aarch64), 1)
- $(call detected,CONFIG_LIBUNWIND_AARCH64)
- CFLAGS += -DHAVE_LIBUNWIND_AARCH64_SUPPORT
- LDFLAGS += -lunwind-aarch64
- EXTLIBS_LIBUNWIND += -lunwind-aarch64
- have_libunwind = 1
- $(call feature_check,libunwind-debug-frame-aarch64)
- ifneq ($(feature-libunwind-debug-frame-aarch64), 1)
- $(warning No debug_frame support found in libunwind-aarch64)
- CFLAGS += -DNO_LIBUNWIND_DEBUG_FRAME_AARCH64
- endif
- endif
-
- ifneq ($(feature-libunwind), 1)
- $(warning No libunwind found. Please install libunwind-dev[el] >= 1.1 and/or set LIBUNWIND_DIR and set LIBUNWIND=1 in the make command line as it is opt-in now)
- NO_LOCAL_LIBUNWIND := 1
- else
- have_libunwind := 1
- $(call detected,CONFIG_LOCAL_LIBUNWIND)
- endif
-
- ifneq ($(have_libunwind), 1)
- NO_LIBUNWIND := 1
- endif
-else
- NO_LOCAL_LIBUNWIND := 1
-endif
-
ifndef NO_LIBBPF
ifneq ($(feature-bpf), 1)
$(warning BPF API too old. Please install recent kernel headers. BPF support in 'perf record' is disabled.)
@@ -738,6 +683,49 @@ ifndef GEN_VMLINUX_H
VMLINUX_H=$(src-perf)/util/bpf_skel/vmlinux/vmlinux.h
endif
+ifndef NO_LIBUNWIND
+ have_libunwind :=
+
+ $(call feature_check,libunwind)
+ ifneq ($(feature-libunwind), 1)
+ $(warning No libunwind found. Please install libunwind-dev[el] >= 1.1 and/or set LIBUNWIND_DIR and set LIBUNWIND=1 in the make command line as it is opt-in now)
+ NO_LOCAL_LIBUNWIND := 1
+ else
+ have_libunwind := 1
+ $(call detected,CONFIG_LOCAL_LIBUNWIND)
+ CFLAGS += -DHAVE_LIBUNWIND_SUPPORT
+ CFLAGS += $(LIBUNWIND_CFLAGS)
+ LDFLAGS += $(LIBUNWIND_LDFLAGS)
+ EXTLIBS += $(EXTLIBS_LIBUNWIND)
+ $(call feature_check,libunwind-debug-frame)
+ ifneq ($(feature-libunwind-debug-frame), 1)
+ CFLAGS += -DNO_LIBUNWIND_DEBUG_FRAME
+ endif
+ endif
+
+ define PROCESS_REMOTE_LIBUNWIND_ARCH
+ $(call feature_check,libunwind-$(1))
+
+ ifeq ($$(feature-libunwind-$(1)), 1)
+ upper_arch := $$(shell echo $(1) | tr '[:lower:]' '[:upper:]')
+ $$(call detected,CONFIG_LIBUNWIND_$$(upper_arch))
+
+ CFLAGS += -DHAVE_LIBUNWIND_$$(upper_arch)_SUPPORT
+ LDFLAGS += -lunwind-$(1)
+ EXTLIBS_LIBUNWIND += -lunwind-$(1)
+ have_libunwind := 1
+
+ $$(call feature_check,libunwind-debug-frame-$(1))
+ ifneq ($$(feature-libunwind-debug-frame-$(1)), 1)
+ CFLAGS += -DNO_LIBUNWIND_DEBUG_FRAME_$$(upper_arch)
+ endif
+ endif
+ endef
+ $(foreach arch,$(LIBUNWIND_ARCHS), \
+ $(eval $(call PROCESS_REMOTE_LIBUNWIND_ARCH,$(arch))) \
+ )
+endif
+
dwarf-post-unwind := 1
dwarf-post-unwind-text := BUG
@@ -760,31 +748,6 @@ ifeq ($(dwarf-post-unwind),1)
$(call detected,CONFIG_DWARF_UNWIND)
endif
-ifndef NO_LIBUNWIND
- ifndef NO_LOCAL_LIBUNWIND
- ifeq ($(SRCARCH),$(filter $(SRCARCH),arm arm64))
- $(call feature_check,libunwind-debug-frame)
- ifneq ($(feature-libunwind-debug-frame), 1)
- $(warning No debug_frame support found in libunwind)
- CFLAGS += -DNO_LIBUNWIND_DEBUG_FRAME
- endif
- else
- # non-ARM has no dwarf_find_debug_frame() function:
- CFLAGS += -DNO_LIBUNWIND_DEBUG_FRAME
- endif
- EXTLIBS += $(LIBUNWIND_LIBS)
- LDFLAGS += $(LIBUNWIND_LIBS)
- endif
- ifeq ($(findstring -static,${LDFLAGS}),-static)
- # gcc -static links libgcc_eh which contans piece of libunwind
- LIBUNWIND_LDFLAGS += -Wl,--allow-multiple-definition
- endif
- CFLAGS += -DHAVE_LIBUNWIND_SUPPORT
- CFLAGS += $(LIBUNWIND_CFLAGS)
- LDFLAGS += $(LIBUNWIND_LDFLAGS)
- EXTLIBS += $(EXTLIBS_LIBUNWIND)
-endif
-
ifneq ($(NO_LIBTRACEEVENT),1)
$(call detected,CONFIG_TRACE)
endif
--
2.53.0.473.g4a7958ca14-goog
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH v2 5/8] perf unwind-libunwind: Make libunwind register reading cross platform
2026-03-05 22:19 ` [PATCH v2 0/8] perf libunwind multiple remote support Ian Rogers
` (3 preceding siblings ...)
2026-03-05 22:19 ` [PATCH v2 4/8] perf build: Be more programmatic when setting up libunwind variables Ian Rogers
@ 2026-03-05 22:19 ` Ian Rogers
2026-03-05 22:19 ` [PATCH v2 6/8] perf unwind-libunwind: Move flush/finish access out of local Ian Rogers
` (3 subsequent siblings)
8 siblings, 0 replies; 27+ messages in thread
From: Ian Rogers @ 2026-03-05 22:19 UTC (permalink / raw)
To: irogers
Cc: 9erthalion6, acme, adrian.hunter, alex, alexander.shishkin,
andrew.jones, aou, atrajeev, blakejones, ctshao, dapeng1.mi,
howardchu95, james.clark, john.g.garry, jolsa, leo.yan,
libunwind-devel, linux-arm-kernel, linux-kernel, linux-perf-users,
linux-riscv, mingo, namhyung, palmer, peterz, pjw, shimin.guo,
tglozar, tmricht, will, yuzhuo
Move the libunwind register to perf register mapping functions in
arch/../util/unwind-libunwind.c into a new libunwind-arch
directory. Rename the functions to
__get_perf_regnum_for_unw_regnum_<arch>. Add untested ppc32 and s390
functions. Add a get_perf_regnum_for_unw_regnum function that takes an
ELF machine as well as a register number and chooses the appropriate
architecture implementation.
Split the x86 and powerpc 32 and 64-bit implementations apart so that
a single libunwind-<arch>.h header is included.
Move the e_machine into the unwind_info struct to make it easier to
pass.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/arch/arm/util/Build | 2 -
tools/perf/arch/arm/util/unwind-libunwind.c | 50 --------
tools/perf/arch/arm64/util/Build | 1 -
tools/perf/arch/arm64/util/unwind-libunwind.c | 17 ---
tools/perf/arch/loongarch/util/Build | 2 -
.../arch/loongarch/util/unwind-libunwind.c | 82 -------------
tools/perf/arch/mips/Build | 1 -
tools/perf/arch/mips/util/Build | 1 -
tools/perf/arch/mips/util/unwind-libunwind.c | 22 ----
tools/perf/arch/powerpc/util/Build | 1 -
.../perf/arch/powerpc/util/unwind-libunwind.c | 92 --------------
tools/perf/arch/x86/util/Build | 3 -
tools/perf/arch/x86/util/unwind-libunwind.c | 115 ------------------
tools/perf/util/Build | 1 +
tools/perf/util/libunwind-arch/Build | 10 ++
.../perf/util/libunwind-arch/libunwind-arch.c | 32 +++++
.../perf/util/libunwind-arch/libunwind-arch.h | 16 +++
.../perf/util/libunwind-arch/libunwind-arm.c | 15 +++
.../util/libunwind-arch/libunwind-arm64.c | 14 +++
.../perf/util/libunwind-arch/libunwind-i386.c | 43 +++++++
.../util/libunwind-arch/libunwind-loongarch.c | 27 ++++
.../perf/util/libunwind-arch/libunwind-mips.c | 29 +++++
.../util/libunwind-arch/libunwind-ppc32.c | 31 +++++
.../util/libunwind-arch/libunwind-ppc64.c | 33 +++++
.../perf/util/libunwind-arch/libunwind-s390.c | 29 +++++
.../util/libunwind-arch/libunwind-x86_64.c | 52 ++++++++
tools/perf/util/libunwind/arm64.c | 5 -
tools/perf/util/libunwind/x86_32.c | 12 --
tools/perf/util/unwind-libunwind-local.c | 12 +-
tools/perf/util/unwind.h | 5 -
30 files changed, 338 insertions(+), 417 deletions(-)
delete mode 100644 tools/perf/arch/arm/util/unwind-libunwind.c
delete mode 100644 tools/perf/arch/arm64/util/unwind-libunwind.c
delete mode 100644 tools/perf/arch/loongarch/util/unwind-libunwind.c
delete mode 100644 tools/perf/arch/mips/Build
delete mode 100644 tools/perf/arch/mips/util/Build
delete mode 100644 tools/perf/arch/mips/util/unwind-libunwind.c
delete mode 100644 tools/perf/arch/powerpc/util/unwind-libunwind.c
delete mode 100644 tools/perf/arch/x86/util/unwind-libunwind.c
create mode 100644 tools/perf/util/libunwind-arch/Build
create mode 100644 tools/perf/util/libunwind-arch/libunwind-arch.c
create mode 100644 tools/perf/util/libunwind-arch/libunwind-arch.h
create mode 100644 tools/perf/util/libunwind-arch/libunwind-arm.c
create mode 100644 tools/perf/util/libunwind-arch/libunwind-arm64.c
create mode 100644 tools/perf/util/libunwind-arch/libunwind-i386.c
create mode 100644 tools/perf/util/libunwind-arch/libunwind-loongarch.c
create mode 100644 tools/perf/util/libunwind-arch/libunwind-mips.c
create mode 100644 tools/perf/util/libunwind-arch/libunwind-ppc32.c
create mode 100644 tools/perf/util/libunwind-arch/libunwind-ppc64.c
create mode 100644 tools/perf/util/libunwind-arch/libunwind-s390.c
create mode 100644 tools/perf/util/libunwind-arch/libunwind-x86_64.c
diff --git a/tools/perf/arch/arm/util/Build b/tools/perf/arch/arm/util/Build
index b94bf3c5279a..768ae5d16553 100644
--- a/tools/perf/arch/arm/util/Build
+++ b/tools/perf/arch/arm/util/Build
@@ -1,3 +1 @@
-perf-util-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o
-
perf-util-y += pmu.o auxtrace.o cs-etm.o
diff --git a/tools/perf/arch/arm/util/unwind-libunwind.c b/tools/perf/arch/arm/util/unwind-libunwind.c
deleted file mode 100644
index 438906bf0014..000000000000
--- a/tools/perf/arch/arm/util/unwind-libunwind.c
+++ /dev/null
@@ -1,50 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-
-#include <errno.h>
-#include <libunwind.h>
-#include "perf_regs.h"
-#include "../../../util/unwind.h"
-#include "../../../util/debug.h"
-
-int libunwind__arch_reg_id(int regnum)
-{
- switch (regnum) {
- case UNW_ARM_R0:
- return PERF_REG_ARM_R0;
- case UNW_ARM_R1:
- return PERF_REG_ARM_R1;
- case UNW_ARM_R2:
- return PERF_REG_ARM_R2;
- case UNW_ARM_R3:
- return PERF_REG_ARM_R3;
- case UNW_ARM_R4:
- return PERF_REG_ARM_R4;
- case UNW_ARM_R5:
- return PERF_REG_ARM_R5;
- case UNW_ARM_R6:
- return PERF_REG_ARM_R6;
- case UNW_ARM_R7:
- return PERF_REG_ARM_R7;
- case UNW_ARM_R8:
- return PERF_REG_ARM_R8;
- case UNW_ARM_R9:
- return PERF_REG_ARM_R9;
- case UNW_ARM_R10:
- return PERF_REG_ARM_R10;
- case UNW_ARM_R11:
- return PERF_REG_ARM_FP;
- case UNW_ARM_R12:
- return PERF_REG_ARM_IP;
- case UNW_ARM_R13:
- return PERF_REG_ARM_SP;
- case UNW_ARM_R14:
- return PERF_REG_ARM_LR;
- case UNW_ARM_R15:
- return PERF_REG_ARM_PC;
- default:
- pr_err("unwind: invalid reg id %d\n", regnum);
- return -EINVAL;
- }
-
- return -EINVAL;
-}
diff --git a/tools/perf/arch/arm64/util/Build b/tools/perf/arch/arm64/util/Build
index 4e06a08d281a..4b70c4788c80 100644
--- a/tools/perf/arch/arm64/util/Build
+++ b/tools/perf/arch/arm64/util/Build
@@ -1,4 +1,3 @@
-perf-util-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o
perf-util-y += ../../arm/util/auxtrace.o
perf-util-y += ../../arm/util/cs-etm.o
perf-util-y += ../../arm/util/pmu.o
diff --git a/tools/perf/arch/arm64/util/unwind-libunwind.c b/tools/perf/arch/arm64/util/unwind-libunwind.c
deleted file mode 100644
index 871af5992298..000000000000
--- a/tools/perf/arch/arm64/util/unwind-libunwind.c
+++ /dev/null
@@ -1,17 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-#include <errno.h>
-
-#ifndef REMOTE_UNWIND_LIBUNWIND
-#include <libunwind.h>
-#include "perf_regs.h"
-#include "../../../util/unwind.h"
-#endif
-#include "../../../util/debug.h"
-
-int LIBUNWIND__ARCH_REG_ID(int regnum)
-{
- if (regnum < 0 || regnum >= PERF_REG_ARM64_EXTENDED_MAX)
- return -EINVAL;
-
- return regnum;
-}
diff --git a/tools/perf/arch/loongarch/util/Build b/tools/perf/arch/loongarch/util/Build
index 8d91e78d31c9..2328fb9a30a3 100644
--- a/tools/perf/arch/loongarch/util/Build
+++ b/tools/perf/arch/loongarch/util/Build
@@ -1,3 +1 @@
perf-util-y += header.o
-
-perf-util-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o
diff --git a/tools/perf/arch/loongarch/util/unwind-libunwind.c b/tools/perf/arch/loongarch/util/unwind-libunwind.c
deleted file mode 100644
index f693167b86ef..000000000000
--- a/tools/perf/arch/loongarch/util/unwind-libunwind.c
+++ /dev/null
@@ -1,82 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-
-#include <errno.h>
-#include <libunwind.h>
-#include "perf_regs.h"
-#include "../../util/unwind.h"
-#include "util/debug.h"
-
-int libunwind__arch_reg_id(int regnum)
-{
- switch (regnum) {
- case UNW_LOONGARCH64_R1:
- return PERF_REG_LOONGARCH_R1;
- case UNW_LOONGARCH64_R2:
- return PERF_REG_LOONGARCH_R2;
- case UNW_LOONGARCH64_R3:
- return PERF_REG_LOONGARCH_R3;
- case UNW_LOONGARCH64_R4:
- return PERF_REG_LOONGARCH_R4;
- case UNW_LOONGARCH64_R5:
- return PERF_REG_LOONGARCH_R5;
- case UNW_LOONGARCH64_R6:
- return PERF_REG_LOONGARCH_R6;
- case UNW_LOONGARCH64_R7:
- return PERF_REG_LOONGARCH_R7;
- case UNW_LOONGARCH64_R8:
- return PERF_REG_LOONGARCH_R8;
- case UNW_LOONGARCH64_R9:
- return PERF_REG_LOONGARCH_R9;
- case UNW_LOONGARCH64_R10:
- return PERF_REG_LOONGARCH_R10;
- case UNW_LOONGARCH64_R11:
- return PERF_REG_LOONGARCH_R11;
- case UNW_LOONGARCH64_R12:
- return PERF_REG_LOONGARCH_R12;
- case UNW_LOONGARCH64_R13:
- return PERF_REG_LOONGARCH_R13;
- case UNW_LOONGARCH64_R14:
- return PERF_REG_LOONGARCH_R14;
- case UNW_LOONGARCH64_R15:
- return PERF_REG_LOONGARCH_R15;
- case UNW_LOONGARCH64_R16:
- return PERF_REG_LOONGARCH_R16;
- case UNW_LOONGARCH64_R17:
- return PERF_REG_LOONGARCH_R17;
- case UNW_LOONGARCH64_R18:
- return PERF_REG_LOONGARCH_R18;
- case UNW_LOONGARCH64_R19:
- return PERF_REG_LOONGARCH_R19;
- case UNW_LOONGARCH64_R20:
- return PERF_REG_LOONGARCH_R20;
- case UNW_LOONGARCH64_R21:
- return PERF_REG_LOONGARCH_R21;
- case UNW_LOONGARCH64_R22:
- return PERF_REG_LOONGARCH_R22;
- case UNW_LOONGARCH64_R23:
- return PERF_REG_LOONGARCH_R23;
- case UNW_LOONGARCH64_R24:
- return PERF_REG_LOONGARCH_R24;
- case UNW_LOONGARCH64_R25:
- return PERF_REG_LOONGARCH_R25;
- case UNW_LOONGARCH64_R26:
- return PERF_REG_LOONGARCH_R26;
- case UNW_LOONGARCH64_R27:
- return PERF_REG_LOONGARCH_R27;
- case UNW_LOONGARCH64_R28:
- return PERF_REG_LOONGARCH_R28;
- case UNW_LOONGARCH64_R29:
- return PERF_REG_LOONGARCH_R29;
- case UNW_LOONGARCH64_R30:
- return PERF_REG_LOONGARCH_R30;
- case UNW_LOONGARCH64_R31:
- return PERF_REG_LOONGARCH_R31;
- case UNW_LOONGARCH64_PC:
- return PERF_REG_LOONGARCH_PC;
- default:
- pr_err("unwind: invalid reg id %d\n", regnum);
- return -EINVAL;
- }
-
- return -EINVAL;
-}
diff --git a/tools/perf/arch/mips/Build b/tools/perf/arch/mips/Build
deleted file mode 100644
index e63eabc2c8f4..000000000000
--- a/tools/perf/arch/mips/Build
+++ /dev/null
@@ -1 +0,0 @@
-perf-util-y += util/
diff --git a/tools/perf/arch/mips/util/Build b/tools/perf/arch/mips/util/Build
deleted file mode 100644
index 818b808a8247..000000000000
--- a/tools/perf/arch/mips/util/Build
+++ /dev/null
@@ -1 +0,0 @@
-perf-util-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o
diff --git a/tools/perf/arch/mips/util/unwind-libunwind.c b/tools/perf/arch/mips/util/unwind-libunwind.c
deleted file mode 100644
index 0d8c99c29da6..000000000000
--- a/tools/perf/arch/mips/util/unwind-libunwind.c
+++ /dev/null
@@ -1,22 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-
-#include <errno.h>
-#include <libunwind.h>
-#include "perf_regs.h"
-#include "../../util/unwind.h"
-#include "util/debug.h"
-
-int libunwind__arch_reg_id(int regnum)
-{
- switch (regnum) {
- case UNW_MIPS_R1 ... UNW_MIPS_R25:
- return regnum - UNW_MIPS_R1 + PERF_REG_MIPS_R1;
- case UNW_MIPS_R28 ... UNW_MIPS_R31:
- return regnum - UNW_MIPS_R28 + PERF_REG_MIPS_R28;
- case UNW_MIPS_PC:
- return PERF_REG_MIPS_PC;
- default:
- pr_err("unwind: invalid reg id %d\n", regnum);
- return -EINVAL;
- }
-}
diff --git a/tools/perf/arch/powerpc/util/Build b/tools/perf/arch/powerpc/util/Build
index d66574cbb9a9..ae928050e07a 100644
--- a/tools/perf/arch/powerpc/util/Build
+++ b/tools/perf/arch/powerpc/util/Build
@@ -6,5 +6,4 @@ perf-util-y += evsel.o
perf-util-$(CONFIG_LIBDW) += skip-callchain-idx.o
-perf-util-$(CONFIG_LIBUNWIND) += unwind-libunwind.o
perf-util-y += auxtrace.o
diff --git a/tools/perf/arch/powerpc/util/unwind-libunwind.c b/tools/perf/arch/powerpc/util/unwind-libunwind.c
deleted file mode 100644
index 90a6beda20de..000000000000
--- a/tools/perf/arch/powerpc/util/unwind-libunwind.c
+++ /dev/null
@@ -1,92 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Copyright 2016 Chandan Kumar, IBM Corporation.
- */
-
-#include <errno.h>
-#include <libunwind.h>
-#include <asm/perf_regs.h>
-#include "../../util/unwind.h"
-#include "../../util/debug.h"
-
-int libunwind__arch_reg_id(int regnum)
-{
- switch (regnum) {
- case UNW_PPC64_R0:
- return PERF_REG_POWERPC_R0;
- case UNW_PPC64_R1:
- return PERF_REG_POWERPC_R1;
- case UNW_PPC64_R2:
- return PERF_REG_POWERPC_R2;
- case UNW_PPC64_R3:
- return PERF_REG_POWERPC_R3;
- case UNW_PPC64_R4:
- return PERF_REG_POWERPC_R4;
- case UNW_PPC64_R5:
- return PERF_REG_POWERPC_R5;
- case UNW_PPC64_R6:
- return PERF_REG_POWERPC_R6;
- case UNW_PPC64_R7:
- return PERF_REG_POWERPC_R7;
- case UNW_PPC64_R8:
- return PERF_REG_POWERPC_R8;
- case UNW_PPC64_R9:
- return PERF_REG_POWERPC_R9;
- case UNW_PPC64_R10:
- return PERF_REG_POWERPC_R10;
- case UNW_PPC64_R11:
- return PERF_REG_POWERPC_R11;
- case UNW_PPC64_R12:
- return PERF_REG_POWERPC_R12;
- case UNW_PPC64_R13:
- return PERF_REG_POWERPC_R13;
- case UNW_PPC64_R14:
- return PERF_REG_POWERPC_R14;
- case UNW_PPC64_R15:
- return PERF_REG_POWERPC_R15;
- case UNW_PPC64_R16:
- return PERF_REG_POWERPC_R16;
- case UNW_PPC64_R17:
- return PERF_REG_POWERPC_R17;
- case UNW_PPC64_R18:
- return PERF_REG_POWERPC_R18;
- case UNW_PPC64_R19:
- return PERF_REG_POWERPC_R19;
- case UNW_PPC64_R20:
- return PERF_REG_POWERPC_R20;
- case UNW_PPC64_R21:
- return PERF_REG_POWERPC_R21;
- case UNW_PPC64_R22:
- return PERF_REG_POWERPC_R22;
- case UNW_PPC64_R23:
- return PERF_REG_POWERPC_R23;
- case UNW_PPC64_R24:
- return PERF_REG_POWERPC_R24;
- case UNW_PPC64_R25:
- return PERF_REG_POWERPC_R25;
- case UNW_PPC64_R26:
- return PERF_REG_POWERPC_R26;
- case UNW_PPC64_R27:
- return PERF_REG_POWERPC_R27;
- case UNW_PPC64_R28:
- return PERF_REG_POWERPC_R28;
- case UNW_PPC64_R29:
- return PERF_REG_POWERPC_R29;
- case UNW_PPC64_R30:
- return PERF_REG_POWERPC_R30;
- case UNW_PPC64_R31:
- return PERF_REG_POWERPC_R31;
- case UNW_PPC64_LR:
- return PERF_REG_POWERPC_LINK;
- case UNW_PPC64_CTR:
- return PERF_REG_POWERPC_CTR;
- case UNW_PPC64_XER:
- return PERF_REG_POWERPC_XER;
- case UNW_PPC64_NIP:
- return PERF_REG_POWERPC_NIP;
- default:
- pr_err("unwind: invalid reg id %d\n", regnum);
- return -EINVAL;
- }
- return -EINVAL;
-}
diff --git a/tools/perf/arch/x86/util/Build b/tools/perf/arch/x86/util/Build
index b94c91984c66..7f89fffe4615 100644
--- a/tools/perf/arch/x86/util/Build
+++ b/tools/perf/arch/x86/util/Build
@@ -8,9 +8,6 @@ perf-util-y += evlist.o
perf-util-y += mem-events.o
perf-util-y += evsel.o
perf-util-y += iostat.o
-
-perf-util-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o
-
perf-util-y += auxtrace.o
perf-util-y += intel-pt.o
perf-util-y += intel-bts.o
diff --git a/tools/perf/arch/x86/util/unwind-libunwind.c b/tools/perf/arch/x86/util/unwind-libunwind.c
deleted file mode 100644
index 47357973b55b..000000000000
--- a/tools/perf/arch/x86/util/unwind-libunwind.c
+++ /dev/null
@@ -1,115 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-
-#include <errno.h>
-#include "../../util/debug.h"
-#ifndef REMOTE_UNWIND_LIBUNWIND
-#include <libunwind.h>
-#include "perf_regs.h"
-#include "../../util/unwind.h"
-#endif
-
-#ifdef HAVE_ARCH_X86_64_SUPPORT
-int LIBUNWIND__ARCH_REG_ID(int regnum)
-{
- int id;
-
- switch (regnum) {
- case UNW_X86_64_RAX:
- id = PERF_REG_X86_AX;
- break;
- case UNW_X86_64_RDX:
- id = PERF_REG_X86_DX;
- break;
- case UNW_X86_64_RCX:
- id = PERF_REG_X86_CX;
- break;
- case UNW_X86_64_RBX:
- id = PERF_REG_X86_BX;
- break;
- case UNW_X86_64_RSI:
- id = PERF_REG_X86_SI;
- break;
- case UNW_X86_64_RDI:
- id = PERF_REG_X86_DI;
- break;
- case UNW_X86_64_RBP:
- id = PERF_REG_X86_BP;
- break;
- case UNW_X86_64_RSP:
- id = PERF_REG_X86_SP;
- break;
- case UNW_X86_64_R8:
- id = PERF_REG_X86_R8;
- break;
- case UNW_X86_64_R9:
- id = PERF_REG_X86_R9;
- break;
- case UNW_X86_64_R10:
- id = PERF_REG_X86_R10;
- break;
- case UNW_X86_64_R11:
- id = PERF_REG_X86_R11;
- break;
- case UNW_X86_64_R12:
- id = PERF_REG_X86_R12;
- break;
- case UNW_X86_64_R13:
- id = PERF_REG_X86_R13;
- break;
- case UNW_X86_64_R14:
- id = PERF_REG_X86_R14;
- break;
- case UNW_X86_64_R15:
- id = PERF_REG_X86_R15;
- break;
- case UNW_X86_64_RIP:
- id = PERF_REG_X86_IP;
- break;
- default:
- pr_err("unwind: invalid reg id %d\n", regnum);
- return -EINVAL;
- }
-
- return id;
-}
-#else
-int LIBUNWIND__ARCH_REG_ID(int regnum)
-{
- int id;
-
- switch (regnum) {
- case UNW_X86_EAX:
- id = PERF_REG_X86_AX;
- break;
- case UNW_X86_EDX:
- id = PERF_REG_X86_DX;
- break;
- case UNW_X86_ECX:
- id = PERF_REG_X86_CX;
- break;
- case UNW_X86_EBX:
- id = PERF_REG_X86_BX;
- break;
- case UNW_X86_ESI:
- id = PERF_REG_X86_SI;
- break;
- case UNW_X86_EDI:
- id = PERF_REG_X86_DI;
- break;
- case UNW_X86_EBP:
- id = PERF_REG_X86_BP;
- break;
- case UNW_X86_ESP:
- id = PERF_REG_X86_SP;
- break;
- case UNW_X86_EIP:
- id = PERF_REG_X86_IP;
- break;
- default:
- pr_err("unwind: invalid reg id %d\n", regnum);
- return -EINVAL;
- }
-
- return id;
-}
-#endif /* HAVE_ARCH_X86_64_SUPPORT */
diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index 6190a8f5b0fa..434dc6716a75 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -230,6 +230,7 @@ perf-util-$(CONFIG_LIBDW) += unwind-libdw.o
perf-util-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind-local.o
perf-util-$(CONFIG_LIBUNWIND) += unwind-libunwind.o
+perf-util-$(CONFIG_LIBUNWIND) += libunwind-arch/
perf-util-$(CONFIG_LIBUNWIND_X86) += libunwind/x86_32.o
perf-util-$(CONFIG_LIBUNWIND_AARCH64) += libunwind/arm64.o
diff --git a/tools/perf/util/libunwind-arch/Build b/tools/perf/util/libunwind-arch/Build
new file mode 100644
index 000000000000..87fd657a3248
--- /dev/null
+++ b/tools/perf/util/libunwind-arch/Build
@@ -0,0 +1,10 @@
+perf-util-$(CONFIG_LIBUNWIND) += libunwind-arch.o
+perf-util-$(CONFIG_LIBUNWIND) += libunwind-arm64.o
+perf-util-$(CONFIG_LIBUNWIND) += libunwind-arm.o
+perf-util-$(CONFIG_LIBUNWIND) += libunwind-loongarch.o
+perf-util-$(CONFIG_LIBUNWIND) += libunwind-mips.o
+perf-util-$(CONFIG_LIBUNWIND) += libunwind-ppc32.o
+perf-util-$(CONFIG_LIBUNWIND) += libunwind-ppc64.o
+perf-util-$(CONFIG_LIBUNWIND) += libunwind-s390.o
+perf-util-$(CONFIG_LIBUNWIND) += libunwind-i386.o
+perf-util-$(CONFIG_LIBUNWIND) += libunwind-x86_64.o
diff --git a/tools/perf/util/libunwind-arch/libunwind-arch.c b/tools/perf/util/libunwind-arch/libunwind-arch.c
new file mode 100644
index 000000000000..5439bf90d161
--- /dev/null
+++ b/tools/perf/util/libunwind-arch/libunwind-arch.c
@@ -0,0 +1,32 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "libunwind-arch.h"
+#include "../debug.h"
+#include <elf.h>
+#include <errno.h>
+
+int get_perf_regnum_for_unw_regnum(unsigned int e_machine, int unw_regnum)
+{
+ switch (e_machine) {
+ case EM_ARM:
+ return __get_perf_regnum_for_unw_regnum_arm(unw_regnum);
+ case EM_AARCH64:
+ return __get_perf_regnum_for_unw_regnum_arm64(unw_regnum);
+ case EM_LOONGARCH:
+ return __get_perf_regnum_for_unw_regnum_loongarch(unw_regnum);
+ case EM_MIPS:
+ return __get_perf_regnum_for_unw_regnum_mips(unw_regnum);
+ case EM_PPC:
+ return __get_perf_regnum_for_unw_regnum_ppc32(unw_regnum);
+ case EM_PPC64:
+ return __get_perf_regnum_for_unw_regnum_ppc64(unw_regnum);
+ case EM_S390:
+ return __get_perf_regnum_for_unw_regnum_s390(unw_regnum);
+ case EM_386:
+ return __get_perf_regnum_for_unw_regnum_i386(unw_regnum);
+ case EM_X86_64:
+ return __get_perf_regnum_for_unw_regnum_x86_64(unw_regnum);
+ default:
+ pr_err("ELF MACHINE %x is not supported.\n", e_machine);
+ return -EINVAL;
+ }
+}
diff --git a/tools/perf/util/libunwind-arch/libunwind-arch.h b/tools/perf/util/libunwind-arch/libunwind-arch.h
new file mode 100644
index 000000000000..e1009c6cb965
--- /dev/null
+++ b/tools/perf/util/libunwind-arch/libunwind-arch.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __LIBUNWIND_ARCH_H
+#define __LIBUNWIND_ARCH_H
+
+int __get_perf_regnum_for_unw_regnum_arm(int unw_regnum);
+int __get_perf_regnum_for_unw_regnum_arm64(int unw_regnum);
+int __get_perf_regnum_for_unw_regnum_loongarch(int unw_regnum);
+int __get_perf_regnum_for_unw_regnum_mips(int unw_regnum);
+int __get_perf_regnum_for_unw_regnum_ppc32(int unw_regnum);
+int __get_perf_regnum_for_unw_regnum_ppc64(int unw_regnum);
+int __get_perf_regnum_for_unw_regnum_s390(int unw_regnum);
+int __get_perf_regnum_for_unw_regnum_i386(int unw_regnum);
+int __get_perf_regnum_for_unw_regnum_x86_64(int unw_regnum);
+int get_perf_regnum_for_unw_regnum(unsigned int e_machine, int unw_regnum);
+
+#endif /* __LIBUNWIND_ARCH_H */
diff --git a/tools/perf/util/libunwind-arch/libunwind-arm.c b/tools/perf/util/libunwind-arch/libunwind-arm.c
new file mode 100644
index 000000000000..6740ee55b043
--- /dev/null
+++ b/tools/perf/util/libunwind-arch/libunwind-arm.c
@@ -0,0 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "libunwind-arch.h"
+#include "../debug.h"
+#include "../../../arch/arm/include/uapi/asm/perf_regs.h"
+#include <linux/compiler.h>
+#include <errno.h>
+
+int __get_perf_regnum_for_unw_regnum_arm(int unw_regnum)
+{
+ if (unw_regnum < 0 || unw_regnum >= PERF_REG_ARM_MAX) {
+ pr_err("unwind: invalid reg id %d\n", unw_regnum);
+ return -EINVAL;
+ }
+ return unw_regnum;
+}
diff --git a/tools/perf/util/libunwind-arch/libunwind-arm64.c b/tools/perf/util/libunwind-arch/libunwind-arm64.c
new file mode 100644
index 000000000000..53b1877dfa04
--- /dev/null
+++ b/tools/perf/util/libunwind-arch/libunwind-arm64.c
@@ -0,0 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "libunwind-arch.h"
+#include "../debug.h"
+#include "../../../arch/arm64/include/uapi/asm/perf_regs.h"
+#include <errno.h>
+
+int __get_perf_regnum_for_unw_regnum_arm64(int unw_regnum)
+{
+ if (unw_regnum < 0 || unw_regnum >= PERF_REG_ARM64_EXTENDED_MAX) {
+ pr_err("unwind: invalid reg id %d\n", unw_regnum);
+ return -EINVAL;
+ }
+ return unw_regnum;
+}
diff --git a/tools/perf/util/libunwind-arch/libunwind-i386.c b/tools/perf/util/libunwind-arch/libunwind-i386.c
new file mode 100644
index 000000000000..9eaf4ebff0c2
--- /dev/null
+++ b/tools/perf/util/libunwind-arch/libunwind-i386.c
@@ -0,0 +1,43 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "libunwind-arch.h"
+#include "../debug.h"
+#include "../../../arch/x86/include/uapi/asm/perf_regs.h"
+#include <linux/compiler.h>
+#include <linux/kernel.h>
+#include <errno.h>
+
+#ifdef HAVE_LIBUNWIND_X86_SUPPORT
+#include <libunwind-x86.h>
+#endif
+
+int __get_perf_regnum_for_unw_regnum_i386(int unw_regnum __maybe_unused)
+{
+#ifndef HAVE_LIBUNWIND_X86_SUPPORT
+ return -EINVAL;
+#else
+ static const int perf_i386_regnums[] = {
+#define REGNUM(reg) [UNW_X86_E ## reg] = PERF_REG_X86_ ## reg
+ REGNUM(AX),
+ REGNUM(DX),
+ REGNUM(CX),
+ REGNUM(BX),
+ REGNUM(SI),
+ REGNUM(DI),
+ REGNUM(BP),
+ REGNUM(SP),
+ REGNUM(IP),
+#undef REGNUM
+ };
+
+ if (unw_regnum == UNW_X86_EAX)
+ return PERF_REG_X86_AX;
+
+ if (unw_regnum < 0 || unw_regnum > (int)ARRAY_SIZE(perf_i386_regnums) ||
+ perf_i386_regnums[unw_regnum] == 0) {
+ pr_err("unwind: invalid reg id %d\n", unw_regnum);
+ return -EINVAL;
+ }
+
+ return perf_i386_regnums[unw_regnum];
+#endif // HAVE_LIBUNWIND_X86_SUPPORT
+}
diff --git a/tools/perf/util/libunwind-arch/libunwind-loongarch.c b/tools/perf/util/libunwind-arch/libunwind-loongarch.c
new file mode 100644
index 000000000000..7009410989bc
--- /dev/null
+++ b/tools/perf/util/libunwind-arch/libunwind-loongarch.c
@@ -0,0 +1,27 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "libunwind-arch.h"
+#include "../debug.h"
+#include "../../../arch/loongarch/include/uapi/asm/perf_regs.h"
+#include <linux/compiler.h>
+#include <errno.h>
+
+#ifdef HAVE_LIBUNWIND_LOONGARCH64_SUPPORT
+#include <libunwind-loongarch64.h>
+#endif
+
+int __get_perf_regnum_for_unw_regnum_loongarch(int unw_regnum __maybe_unused)
+{
+#ifndef HAVE_LIBUNWIND_LOONGARCH64_SUPPORT
+ return -EINVAL;
+#else
+ switch (unw_regnum) {
+ case UNW_LOONGARCH64_R1 ... UNW_LOONGARCH64_31:
+ return unw_regnum - UNW_LOONGARCH64_R1 + PERF_REG_LOONGARCH_R1;
+ case UNW_LOONGARCH64_PC:
+ return PERF_REG_LOONGARCH_PC;
+ default:
+ pr_err("unwind: invalid reg id %d\n", unw_regnum);
+ return -EINVAL;
+ }
+#endif // HAVE_LIBUNWIND_LOONGARCH64_SUPPORT
+}
diff --git a/tools/perf/util/libunwind-arch/libunwind-mips.c b/tools/perf/util/libunwind-arch/libunwind-mips.c
new file mode 100644
index 000000000000..01a506c8079c
--- /dev/null
+++ b/tools/perf/util/libunwind-arch/libunwind-mips.c
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "libunwind-arch.h"
+#include "../debug.h"
+#include "../../../arch/mips/include/uapi/asm/perf_regs.h"
+#include <linux/compiler.h>
+#include <errno.h>
+
+#ifdef HAVE_LIBUNWIND_MIPS_SUPPORT
+#include <libunwind-mips.h>
+#endif
+
+int __get_perf_regnum_for_unw_regnum_mips(int unw_regnum __maybe_unused)
+{
+#ifndef HAVE_LIBUNWIND_MIPS_SUPPORT
+ return -EINVAL;
+#else
+ switch (unw_regnum) {
+ case UNW_MIPS_R1 ... UNW_MIPS_R25:
+ return unw_regnum - UNW_MIPS_R1 + PERF_REG_MIPS_R1;
+ case UNW_MIPS_R28 ... UNW_MIPS_R31:
+ return unw_regnum - UNW_MIPS_R28 + PERF_REG_MIPS_R28;
+ case UNW_MIPS_PC:
+ return PERF_REG_MIPS_PC;
+ default:
+ pr_err("unwind: invalid reg id %d\n", unw_regnum);
+ return -EINVAL;
+ }
+#endif // HAVE_LIBUNWIND_MIPS_SUPPORT
+}
diff --git a/tools/perf/util/libunwind-arch/libunwind-ppc32.c b/tools/perf/util/libunwind-arch/libunwind-ppc32.c
new file mode 100644
index 000000000000..edcb0ec95dd7
--- /dev/null
+++ b/tools/perf/util/libunwind-arch/libunwind-ppc32.c
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#include "libunwind-arch.h"
+#include "../debug.h"
+#include "../../../arch/powerpc/include/uapi/asm/perf_regs.h"
+#include <linux/compiler.h>
+#include <errno.h>
+
+#ifdef HAVE_LIBUNWIND_PPC32_SUPPORT
+#include <libunwind-ppc32.h>
+#endif
+
+int __get_perf_regnum_for_unw_regnum_ppc32(int unw_regnum __maybe_unused)
+{
+#ifndef HAVE_LIBUNWIND_PPC32_SUPPORT
+ return -EINVAL;
+#else
+ switch (unw_regnum) {
+ case UNW_PPC32_R0 ... UNW_PPC32_31:
+ return unw_regnum - UNW_PPC32_R0 + PERF_REG_POWERPC_R0;
+ case UNW_PPC32_LR:
+ return PERF_REG_POWERPC_LINK;
+ case UNW_PPC32_CTR:
+ return PERF_REG_POWERPC_CTR;
+ case UNW_PPC32_XER:
+ return PERF_REG_POWERPC_XER;
+ default:
+ pr_err("unwind: invalid reg id %d\n", unw_regnum);
+ return -EINVAL;
+ }
+#endif // HAVE_LIBUNWIND_PPC32_SUPPORT
+}
diff --git a/tools/perf/util/libunwind-arch/libunwind-ppc64.c b/tools/perf/util/libunwind-arch/libunwind-ppc64.c
new file mode 100644
index 000000000000..9f57a049600b
--- /dev/null
+++ b/tools/perf/util/libunwind-arch/libunwind-ppc64.c
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#include "libunwind-arch.h"
+#include "../debug.h"
+#include "../../../arch/powerpc/include/uapi/asm/perf_regs.h"
+#include <linux/compiler.h>
+#include <errno.h>
+
+#ifdef HAVE_LIBUNWIND_PPC64_SUPPORT
+#include <libunwind-ppc64.h>
+#endif
+
+int __get_perf_regnum_for_unw_regnum_ppc64(int unw_regnum __maybe_unused)
+{
+#ifndef HAVE_LIBUNWIND_PPC64_SUPPORT
+ return -EINVAL;
+#else
+ switch (unw_regnum) {
+ case UNW_PPC64_R0 ... UNW_PPC64_31:
+ return unw_regnum - UNW_PPC64_R0 + PERF_REG_POWERPC_R0;
+ case UNW_PPC64_LR:
+ return PERF_REG_POWERPC_LINK;
+ case UNW_PPC64_CTR:
+ return PERF_REG_POWERPC_CTR;
+ case UNW_PPC64_XER:
+ return PERF_REG_POWERPC_XER;
+ case UNW_PPC64_NIP:
+ return PERF_REG_POWERPC_NIP;
+ default:
+ pr_err("unwind: invalid reg id %d\n", unw_regnum);
+ return -EINVAL;
+ }
+#endif // HAVE_LIBUNWIND_PPC64_SUPPORT
+}
diff --git a/tools/perf/util/libunwind-arch/libunwind-s390.c b/tools/perf/util/libunwind-arch/libunwind-s390.c
new file mode 100644
index 000000000000..9fcc7885ca55
--- /dev/null
+++ b/tools/perf/util/libunwind-arch/libunwind-s390.c
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "libunwind-arch.h"
+#include "../debug.h"
+#include "../../../arch/s390/include/uapi/asm/perf_regs.h"
+#include <linux/compiler.h>
+#include <errno.h>
+
+#ifdef HAVE_LIBUNWIND_S390X_SUPPORT
+#include <libunwind-s390x.h>
+#endif
+
+int __get_perf_regnum_for_unw_regnum_s390(int unw_regnum __maybe_unused)
+{
+#ifndef HAVE_LIBUNWIND_S390X_SUPPORT
+ return -EINVAL;
+#else
+ switch (unw_regnum) {
+ case UNW_S390X_R0 ... UNW_S390_R15:
+ return unw_regnum - UNW_S390_R0 + PERF_REG_S390_R0;
+ case UNW_S390X_F0 ... UNW_S390_F15:
+ return unw_regnum - UNW_S390_F0 + PERF_REG_S390_FP0;
+ case UNW_S390X_IP:
+ return PERF_REG_S390_PC;
+ default:
+ pr_err("unwind: invalid reg id %d\n", unw_regnum);
+ return -EINVAL;
+ }
+#endif // HAVE_LIBUNWIND_S390X_SUPPORT
+}
diff --git a/tools/perf/util/libunwind-arch/libunwind-x86_64.c b/tools/perf/util/libunwind-arch/libunwind-x86_64.c
new file mode 100644
index 000000000000..6072e3597e61
--- /dev/null
+++ b/tools/perf/util/libunwind-arch/libunwind-x86_64.c
@@ -0,0 +1,52 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "libunwind-arch.h"
+#include "../debug.h"
+#include "../../../arch/x86/include/uapi/asm/perf_regs.h"
+#include <linux/compiler.h>
+#include <linux/kernel.h>
+#include <errno.h>
+
+#ifdef HAVE_LIBUNWIND_X86_64_SUPPORT
+#include <libunwind-x86_64.h>
+#endif
+
+int __get_perf_regnum_for_unw_regnum_x86_64(int unw_regnum __maybe_unused)
+{
+#ifndef HAVE_LIBUNWIND_X86_64_SUPPORT
+ return -EINVAL;
+#else
+ static const int perf_x86_64_regnums[] = {
+#define REGNUM(reg) [UNW_X86_64_R ## reg] = PERF_REG_X86_ ## reg
+ REGNUM(AX),
+ REGNUM(DX),
+ REGNUM(CX),
+ REGNUM(BX),
+ REGNUM(SI),
+ REGNUM(DI),
+ REGNUM(BP),
+ REGNUM(SP),
+ REGNUM(IP),
+#undef REGNUM
+#define REGNUM(reg) [UNW_X86_64_ ## reg] = PERF_REG_X86_ ## reg
+ REGNUM(R8),
+ REGNUM(R9),
+ REGNUM(R10),
+ REGNUM(R11),
+ REGNUM(R12),
+ REGNUM(R13),
+ REGNUM(R14),
+ REGNUM(R15),
+#undef REGNUM
+ };
+
+ if (unw_regnum == UNW_X86_64_RAX)
+ return PERF_REG_X86_AX;
+
+ if (unw_regnum < 0 || unw_regnum > (int)ARRAY_SIZE(perf_x86_64_regnums) ||
+ perf_x86_64_regnums[unw_regnum] == 0) {
+ pr_err("unwind: invalid reg id %d\n", unw_regnum);
+ return -EINVAL;
+ }
+ return perf_x86_64_regnums[unw_regnum];
+#endif // HAVE_LIBUNWIND_X86_64_SUPPORT
+}
diff --git a/tools/perf/util/libunwind/arm64.c b/tools/perf/util/libunwind/arm64.c
index 37ecef0c53b9..15670a964495 100644
--- a/tools/perf/util/libunwind/arm64.c
+++ b/tools/perf/util/libunwind/arm64.c
@@ -14,11 +14,6 @@
#define REMOTE_UNWIND_LIBUNWIND
-/* Define arch specific functions & regs for libunwind, should be
- * defined before including "unwind.h"
- */
-#define LIBUNWIND__ARCH_REG_ID(regnum) libunwind__arm64_reg_id(regnum)
-
#include "unwind.h"
#include "libunwind-aarch64.h"
#define perf_event_arm_regs perf_event_arm64_regs
diff --git a/tools/perf/util/libunwind/x86_32.c b/tools/perf/util/libunwind/x86_32.c
index 1697dece1b74..1e9fb8bfec44 100644
--- a/tools/perf/util/libunwind/x86_32.c
+++ b/tools/perf/util/libunwind/x86_32.c
@@ -14,20 +14,8 @@
#define REMOTE_UNWIND_LIBUNWIND
-/* Define arch specific functions & regs for libunwind, should be
- * defined before including "unwind.h"
- */
-#define LIBUNWIND__ARCH_REG_ID(regnum) libunwind__x86_reg_id(regnum)
-
#include "unwind.h"
#include "libunwind-x86.h"
-#include <../../../../arch/x86/include/uapi/asm/perf_regs.h>
-
-/* HAVE_ARCH_X86_64_SUPPORT is used in'arch/x86/util/unwind-libunwind.c'
- * for x86_32, we undef it to compile code for x86_32 only.
- */
-#undef HAVE_ARCH_X86_64_SUPPORT
-#include "../../arch/x86/util/unwind-libunwind.c"
/* Explicitly define NO_LIBUNWIND_DEBUG_FRAME, because non-ARM has no
* dwarf_find_debug_frame() function.
diff --git a/tools/perf/util/unwind-libunwind-local.c b/tools/perf/util/unwind-libunwind-local.c
index 5b39ce21e333..3ecdb468b859 100644
--- a/tools/perf/util/unwind-libunwind-local.c
+++ b/tools/perf/util/unwind-libunwind-local.c
@@ -40,6 +40,7 @@
#include "debug.h"
#include "asm/bug.h"
#include "dso.h"
+#include "libunwind-arch/libunwind-arch.h"
extern int
UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,
@@ -96,6 +97,7 @@ struct unwind_info {
struct perf_sample *sample;
struct machine *machine;
struct thread *thread;
+ uint16_t e_machine;
bool best_effort;
};
@@ -584,9 +586,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(thread__e_machine(ui->thread,
- ui->machine,
- /*e_flags=*/NULL)));
+ perf_arch_reg_sp(ui->e_machine));
if (ret)
return ret;
@@ -634,7 +634,7 @@ static int access_reg(unw_addr_space_t __maybe_unused as,
return 0;
}
- id = LIBUNWIND__ARCH_REG_ID(regnum);
+ id = get_perf_regnum_for_unw_regnum(ui->e_machine, regnum);
if (id < 0)
return -EINVAL;
@@ -735,7 +735,6 @@ 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)
{
- uint16_t e_machine = thread__e_machine(ui->thread, ui->machine, /*e_flags=*/NULL);
u64 val;
unw_word_t ips[max_stack];
unw_addr_space_t addr_space;
@@ -743,7 +742,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(e_machine));
+ perf_arch_reg_ip(ui->e_machine));
if (ret)
return ret;
@@ -806,6 +805,7 @@ static int _unwind__get_entries(unwind_entry_cb_t cb, void *arg,
.sample = data,
.thread = thread,
.machine = maps__machine(thread__maps(thread)),
+ .e_machine = thread__e_machine(thread, /*machine=*/NULL, /*e_flags=*/NULL),
.best_effort = best_effort
};
diff --git a/tools/perf/util/unwind.h b/tools/perf/util/unwind.h
index 581d042e170a..e32d39ab41c6 100644
--- a/tools/perf/util/unwind.h
+++ b/tools/perf/util/unwind.h
@@ -53,11 +53,6 @@ int libunwind__get_entries(unwind_entry_cb_t cb, void *arg,
struct thread *thread,
struct perf_sample *data, int max_stack,
bool best_effort);
-#ifndef LIBUNWIND__ARCH_REG_ID
-#define LIBUNWIND__ARCH_REG_ID(regnum) libunwind__arch_reg_id(regnum)
-#endif
-
-int LIBUNWIND__ARCH_REG_ID(int regnum);
int unwind__prepare_access(struct maps *maps, struct map *map, bool *initialized);
void unwind__flush_access(struct maps *maps);
void unwind__finish_access(struct maps *maps);
--
2.53.0.473.g4a7958ca14-goog
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH v2 6/8] perf unwind-libunwind: Move flush/finish access out of local
2026-03-05 22:19 ` [PATCH v2 0/8] perf libunwind multiple remote support Ian Rogers
` (4 preceding siblings ...)
2026-03-05 22:19 ` [PATCH v2 5/8] perf unwind-libunwind: Make libunwind register reading cross platform Ian Rogers
@ 2026-03-05 22:19 ` Ian Rogers
2026-03-05 22:19 ` [PATCH v2 7/8] perf unwind-libunwind: Remove libunwind-local Ian Rogers
` (2 subsequent siblings)
8 siblings, 0 replies; 27+ messages in thread
From: Ian Rogers @ 2026-03-05 22:19 UTC (permalink / raw)
To: irogers
Cc: 9erthalion6, acme, adrian.hunter, alex, alexander.shishkin,
andrew.jones, aou, atrajeev, blakejones, ctshao, dapeng1.mi,
howardchu95, james.clark, john.g.garry, jolsa, leo.yan,
libunwind-devel, linux-arm-kernel, linux-kernel, linux-perf-users,
linux-riscv, mingo, namhyung, palmer, peterz, pjw, shimin.guo,
tglozar, tmricht, will, yuzhuo
Flush and finish access are relatively simple calls into libunwind,
move them out struct unwind_libunwind_ops. So that the correct version
can be called, add an e_machine variable to maps. This size regression
will go away when the unwind_libunwind_ops no longer need stashing in
the maps. To set the e_machine up pass it into unwind__prepare_access,
which no longer needs to determine the unwind operations based on a
map dso because of this. This also means the maps copying code can
call unwind__prepare_access once for the e_machine rather than once
per map.
Signed-off-by: Ian Rogers <irogers@google.com>
---
.../perf/util/libunwind-arch/libunwind-arch.c | 82 +++++++++++++++++++
.../perf/util/libunwind-arch/libunwind-arch.h | 24 ++++++
.../perf/util/libunwind-arch/libunwind-arm.c | 19 +++++
.../util/libunwind-arch/libunwind-arm64.c | 20 +++++
.../perf/util/libunwind-arch/libunwind-i386.c | 15 ++++
.../util/libunwind-arch/libunwind-loongarch.c | 15 ++++
.../perf/util/libunwind-arch/libunwind-mips.c | 15 ++++
.../util/libunwind-arch/libunwind-ppc32.c | 15 ++++
.../util/libunwind-arch/libunwind-ppc64.c | 15 ++++
.../perf/util/libunwind-arch/libunwind-s390.c | 15 ++++
.../util/libunwind-arch/libunwind-x86_64.c | 15 ++++
tools/perf/util/maps.c | 31 ++++---
tools/perf/util/maps.h | 2 +
tools/perf/util/thread.c | 29 +------
tools/perf/util/unwind-libunwind-local.c | 12 ---
tools/perf/util/unwind-libunwind.c | 61 +++++---------
tools/perf/util/unwind.h | 8 +-
17 files changed, 299 insertions(+), 94 deletions(-)
diff --git a/tools/perf/util/libunwind-arch/libunwind-arch.c b/tools/perf/util/libunwind-arch/libunwind-arch.c
index 5439bf90d161..9692e6c81492 100644
--- a/tools/perf/util/libunwind-arch/libunwind-arch.c
+++ b/tools/perf/util/libunwind-arch/libunwind-arch.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
#include "libunwind-arch.h"
#include "../debug.h"
+#include "../maps.h"
#include <elf.h>
#include <errno.h>
@@ -30,3 +31,84 @@ int get_perf_regnum_for_unw_regnum(unsigned int e_machine, int unw_regnum)
return -EINVAL;
}
}
+
+
+void libunwind_arch__flush_access(struct maps *maps)
+{
+ unsigned int e_machine = maps__e_machine(maps);
+
+ switch (e_machine) {
+ case EM_NONE:
+ break; // No libunwind info on the maps.
+ case EM_ARM:
+ __libunwind_arch__flush_access_arm(maps);
+ break;
+ case EM_AARCH64:
+ __libunwind_arch__flush_access_arm64(maps);
+ break;
+ case EM_LOONGARCH:
+ __libunwind_arch__flush_access_loongarch(maps);
+ break;
+ case EM_MIPS:
+ __libunwind_arch__flush_access_mips(maps);
+ break;
+ case EM_PPC:
+ __libunwind_arch__flush_access_ppc32(maps);
+ break;
+ case EM_PPC64:
+ __libunwind_arch__flush_access_ppc64(maps);
+ break;
+ case EM_S390:
+ __libunwind_arch__flush_access_s390(maps);
+ break;
+ case EM_386:
+ __libunwind_arch__flush_access_i386(maps);
+ break;
+ case EM_X86_64:
+ __libunwind_arch__flush_access_x86_64(maps);
+ break;
+ default:
+ pr_err("ELF MACHINE %x is not supported.\n", e_machine);
+ break;
+ }
+}
+
+void libunwind_arch__finish_access(struct maps *maps)
+{
+ unsigned int e_machine = maps__e_machine(maps);
+
+ switch (e_machine) {
+ case EM_NONE:
+ break; // No libunwind info on the maps.
+ case EM_ARM:
+ __libunwind_arch__finish_access_arm(maps);
+ break;
+ case EM_AARCH64:
+ __libunwind_arch__finish_access_arm64(maps);
+ break;
+ case EM_LOONGARCH:
+ __libunwind_arch__finish_access_loongarch(maps);
+ break;
+ case EM_MIPS:
+ __libunwind_arch__finish_access_mips(maps);
+ break;
+ case EM_PPC:
+ __libunwind_arch__finish_access_ppc32(maps);
+ break;
+ case EM_PPC64:
+ __libunwind_arch__finish_access_ppc64(maps);
+ break;
+ case EM_S390:
+ __libunwind_arch__finish_access_s390(maps);
+ break;
+ case EM_386:
+ __libunwind_arch__finish_access_i386(maps);
+ break;
+ case EM_X86_64:
+ __libunwind_arch__finish_access_x86_64(maps);
+ break;
+ default:
+ pr_err("ELF MACHINE %x is not supported.\n", e_machine);
+ break;
+ }
+}
diff --git a/tools/perf/util/libunwind-arch/libunwind-arch.h b/tools/perf/util/libunwind-arch/libunwind-arch.h
index e1009c6cb965..c00277a5e914 100644
--- a/tools/perf/util/libunwind-arch/libunwind-arch.h
+++ b/tools/perf/util/libunwind-arch/libunwind-arch.h
@@ -2,6 +2,8 @@
#ifndef __LIBUNWIND_ARCH_H
#define __LIBUNWIND_ARCH_H
+struct maps;
+
int __get_perf_regnum_for_unw_regnum_arm(int unw_regnum);
int __get_perf_regnum_for_unw_regnum_arm64(int unw_regnum);
int __get_perf_regnum_for_unw_regnum_loongarch(int unw_regnum);
@@ -13,4 +15,26 @@ int __get_perf_regnum_for_unw_regnum_i386(int unw_regnum);
int __get_perf_regnum_for_unw_regnum_x86_64(int unw_regnum);
int get_perf_regnum_for_unw_regnum(unsigned int e_machine, int unw_regnum);
+void __libunwind_arch__flush_access_arm(struct maps *maps);
+void __libunwind_arch__flush_access_arm64(struct maps *maps);
+void __libunwind_arch__flush_access_loongarch(struct maps *maps);
+void __libunwind_arch__flush_access_mips(struct maps *maps);
+void __libunwind_arch__flush_access_ppc32(struct maps *maps);
+void __libunwind_arch__flush_access_ppc64(struct maps *maps);
+void __libunwind_arch__flush_access_s390(struct maps *maps);
+void __libunwind_arch__flush_access_i386(struct maps *maps);
+void __libunwind_arch__flush_access_x86_64(struct maps *maps);
+void libunwind_arch__flush_access(struct maps *maps);
+
+void __libunwind_arch__finish_access_arm(struct maps *maps);
+void __libunwind_arch__finish_access_arm64(struct maps *maps);
+void __libunwind_arch__finish_access_loongarch(struct maps *maps);
+void __libunwind_arch__finish_access_mips(struct maps *maps);
+void __libunwind_arch__finish_access_ppc32(struct maps *maps);
+void __libunwind_arch__finish_access_ppc64(struct maps *maps);
+void __libunwind_arch__finish_access_s390(struct maps *maps);
+void __libunwind_arch__finish_access_i386(struct maps *maps);
+void __libunwind_arch__finish_access_x86_64(struct maps *maps);
+void libunwind_arch__finish_access(struct maps *maps);
+
#endif /* __LIBUNWIND_ARCH_H */
diff --git a/tools/perf/util/libunwind-arch/libunwind-arm.c b/tools/perf/util/libunwind-arch/libunwind-arm.c
index 6740ee55b043..bbaf01406c52 100644
--- a/tools/perf/util/libunwind-arch/libunwind-arm.c
+++ b/tools/perf/util/libunwind-arch/libunwind-arm.c
@@ -1,10 +1,15 @@
// SPDX-License-Identifier: GPL-2.0
#include "libunwind-arch.h"
#include "../debug.h"
+#include "../maps.h"
#include "../../../arch/arm/include/uapi/asm/perf_regs.h"
#include <linux/compiler.h>
#include <errno.h>
+#ifdef HAVE_LIBUNWIND_ARM_SUPPORT
+#include <libunwind-arm.h>
+#endif
+
int __get_perf_regnum_for_unw_regnum_arm(int unw_regnum)
{
if (unw_regnum < 0 || unw_regnum >= PERF_REG_ARM_MAX) {
@@ -13,3 +18,17 @@ int __get_perf_regnum_for_unw_regnum_arm(int unw_regnum)
}
return unw_regnum;
}
+
+void __libunwind_arch__flush_access_arm(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_ARM_SUPPORT
+ unw_flush_cache(maps__addr_space(maps), 0, 0);
+#endif
+}
+
+void __libunwind_arch__finish_access_arm(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_ARM_SUPPORT
+ unw_destroy_addr_space(maps__addr_space(maps));
+#endif
+}
diff --git a/tools/perf/util/libunwind-arch/libunwind-arm64.c b/tools/perf/util/libunwind-arch/libunwind-arm64.c
index 53b1877dfa04..8ba510089736 100644
--- a/tools/perf/util/libunwind-arch/libunwind-arm64.c
+++ b/tools/perf/util/libunwind-arch/libunwind-arm64.c
@@ -1,9 +1,15 @@
// SPDX-License-Identifier: GPL-2.0
#include "libunwind-arch.h"
#include "../debug.h"
+#include "../maps.h"
#include "../../../arch/arm64/include/uapi/asm/perf_regs.h"
+#include <linux/compiler.h>
#include <errno.h>
+#ifdef HAVE_LIBUNWIND_AARCH64_SUPPORT
+#include <libunwind-aarch64.h>
+#endif
+
int __get_perf_regnum_for_unw_regnum_arm64(int unw_regnum)
{
if (unw_regnum < 0 || unw_regnum >= PERF_REG_ARM64_EXTENDED_MAX) {
@@ -12,3 +18,17 @@ int __get_perf_regnum_for_unw_regnum_arm64(int unw_regnum)
}
return unw_regnum;
}
+
+void __libunwind_arch__flush_access_arm64(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_AARCH64_SUPPORT
+ unw_flush_cache(maps__addr_space(maps), 0, 0);
+#endif
+}
+
+void __libunwind_arch__finish_access_arm64(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_AARCH64_SUPPORT
+ unw_destroy_addr_space(maps__addr_space(maps));
+#endif
+}
diff --git a/tools/perf/util/libunwind-arch/libunwind-i386.c b/tools/perf/util/libunwind-arch/libunwind-i386.c
index 9eaf4ebff0c2..45ff30c95c1b 100644
--- a/tools/perf/util/libunwind-arch/libunwind-i386.c
+++ b/tools/perf/util/libunwind-arch/libunwind-i386.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
#include "libunwind-arch.h"
#include "../debug.h"
+#include "../maps.h"
#include "../../../arch/x86/include/uapi/asm/perf_regs.h"
#include <linux/compiler.h>
#include <linux/kernel.h>
@@ -41,3 +42,17 @@ int __get_perf_regnum_for_unw_regnum_i386(int unw_regnum __maybe_unused)
return perf_i386_regnums[unw_regnum];
#endif // HAVE_LIBUNWIND_X86_SUPPORT
}
+
+void __libunwind_arch__flush_access_i386(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_X86_SUPPORT
+ unw_flush_cache(maps__addr_space(maps), 0, 0);
+#endif
+}
+
+void __libunwind_arch__finish_access_i386(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_X86_SUPPORT
+ unw_destroy_addr_space(maps__addr_space(maps));
+#endif
+}
diff --git a/tools/perf/util/libunwind-arch/libunwind-loongarch.c b/tools/perf/util/libunwind-arch/libunwind-loongarch.c
index 7009410989bc..837aa11e2b9f 100644
--- a/tools/perf/util/libunwind-arch/libunwind-loongarch.c
+++ b/tools/perf/util/libunwind-arch/libunwind-loongarch.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
#include "libunwind-arch.h"
#include "../debug.h"
+#include "../maps.h"
#include "../../../arch/loongarch/include/uapi/asm/perf_regs.h"
#include <linux/compiler.h>
#include <errno.h>
@@ -25,3 +26,17 @@ int __get_perf_regnum_for_unw_regnum_loongarch(int unw_regnum __maybe_unused)
}
#endif // HAVE_LIBUNWIND_LOONGARCH64_SUPPORT
}
+
+void __libunwind_arch__flush_access_loongarch(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_LOONGARCH64_SUPPORT
+ unw_flush_cache(maps__addr_space(maps), 0, 0);
+#endif
+}
+
+void __libunwind_arch__finish_access_loongarch(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_LOONGARCH64_SUPPORT
+ unw_destroy_addr_space(maps__addr_space(maps));
+#endif
+}
diff --git a/tools/perf/util/libunwind-arch/libunwind-mips.c b/tools/perf/util/libunwind-arch/libunwind-mips.c
index 01a506c8079c..1fa81742ff4a 100644
--- a/tools/perf/util/libunwind-arch/libunwind-mips.c
+++ b/tools/perf/util/libunwind-arch/libunwind-mips.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
#include "libunwind-arch.h"
#include "../debug.h"
+#include "../maps.h"
#include "../../../arch/mips/include/uapi/asm/perf_regs.h"
#include <linux/compiler.h>
#include <errno.h>
@@ -27,3 +28,17 @@ int __get_perf_regnum_for_unw_regnum_mips(int unw_regnum __maybe_unused)
}
#endif // HAVE_LIBUNWIND_MIPS_SUPPORT
}
+
+void __libunwind_arch__flush_access_mips(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_MIPS_SUPPORT
+ unw_flush_cache(maps__addr_space(maps), 0, 0);
+#endif
+}
+
+void __libunwind_arch__finish_access_mips(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_MIPS_SUPPORT
+ unw_destroy_addr_space(maps__addr_space(maps));
+#endif
+}
diff --git a/tools/perf/util/libunwind-arch/libunwind-ppc32.c b/tools/perf/util/libunwind-arch/libunwind-ppc32.c
index edcb0ec95dd7..fa8471c74bf3 100644
--- a/tools/perf/util/libunwind-arch/libunwind-ppc32.c
+++ b/tools/perf/util/libunwind-arch/libunwind-ppc32.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "libunwind-arch.h"
#include "../debug.h"
+#include "../maps.h"
#include "../../../arch/powerpc/include/uapi/asm/perf_regs.h"
#include <linux/compiler.h>
#include <errno.h>
@@ -29,3 +30,17 @@ int __get_perf_regnum_for_unw_regnum_ppc32(int unw_regnum __maybe_unused)
}
#endif // HAVE_LIBUNWIND_PPC32_SUPPORT
}
+
+void __libunwind_arch__flush_access_ppc32(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_PPC32_SUPPORT
+ unw_flush_cache(maps__addr_space(maps), 0, 0);
+#endif
+}
+
+void __libunwind_arch__finish_access_ppc32(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_PPC32_SUPPORT
+ unw_destroy_addr_space(maps__addr_space(maps));
+#endif
+}
diff --git a/tools/perf/util/libunwind-arch/libunwind-ppc64.c b/tools/perf/util/libunwind-arch/libunwind-ppc64.c
index 9f57a049600b..2f746e347336 100644
--- a/tools/perf/util/libunwind-arch/libunwind-ppc64.c
+++ b/tools/perf/util/libunwind-arch/libunwind-ppc64.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "libunwind-arch.h"
#include "../debug.h"
+#include "../maps.h"
#include "../../../arch/powerpc/include/uapi/asm/perf_regs.h"
#include <linux/compiler.h>
#include <errno.h>
@@ -31,3 +32,17 @@ int __get_perf_regnum_for_unw_regnum_ppc64(int unw_regnum __maybe_unused)
}
#endif // HAVE_LIBUNWIND_PPC64_SUPPORT
}
+
+void __libunwind_arch__flush_access_ppc64(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_PPC64_SUPPORT
+ unw_flush_cache(maps__addr_space(maps), 0, 0);
+#endif
+}
+
+void __libunwind_arch__finish_access_ppc64(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_PPC64_SUPPORT
+ unw_destroy_addr_space(maps__addr_space(maps));
+#endif
+}
diff --git a/tools/perf/util/libunwind-arch/libunwind-s390.c b/tools/perf/util/libunwind-arch/libunwind-s390.c
index 9fcc7885ca55..9f68d15438b2 100644
--- a/tools/perf/util/libunwind-arch/libunwind-s390.c
+++ b/tools/perf/util/libunwind-arch/libunwind-s390.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
#include "libunwind-arch.h"
#include "../debug.h"
+#include "../maps.h"
#include "../../../arch/s390/include/uapi/asm/perf_regs.h"
#include <linux/compiler.h>
#include <errno.h>
@@ -27,3 +28,17 @@ int __get_perf_regnum_for_unw_regnum_s390(int unw_regnum __maybe_unused)
}
#endif // HAVE_LIBUNWIND_S390X_SUPPORT
}
+
+void __libunwind_arch__flush_access_s390(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_S390X_SUPPORT
+ unw_flush_cache(maps__addr_space(maps), 0, 0);
+#endif
+}
+
+void __libunwind_arch__finish_access_s390(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_S390X_SUPPORT
+ unw_destroy_addr_space(maps__addr_space(maps));
+#endif
+}
diff --git a/tools/perf/util/libunwind-arch/libunwind-x86_64.c b/tools/perf/util/libunwind-arch/libunwind-x86_64.c
index 6072e3597e61..25e326bd3e15 100644
--- a/tools/perf/util/libunwind-arch/libunwind-x86_64.c
+++ b/tools/perf/util/libunwind-arch/libunwind-x86_64.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
#include "libunwind-arch.h"
#include "../debug.h"
+#include "../maps.h"
#include "../../../arch/x86/include/uapi/asm/perf_regs.h"
#include <linux/compiler.h>
#include <linux/kernel.h>
@@ -50,3 +51,17 @@ int __get_perf_regnum_for_unw_regnum_x86_64(int unw_regnum __maybe_unused)
return perf_x86_64_regnums[unw_regnum];
#endif // HAVE_LIBUNWIND_X86_64_SUPPORT
}
+
+void __libunwind_arch__flush_access_x86_64(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_X86_64_SUPPORT
+ unw_flush_cache(maps__addr_space(maps), 0, 0);
+#endif
+}
+
+void __libunwind_arch__finish_access_x86_64(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_X86_64_SUPPORT
+ unw_destroy_addr_space(maps__addr_space(maps));
+#endif
+}
diff --git a/tools/perf/util/maps.c b/tools/perf/util/maps.c
index 4092211cff62..8c7b2a1e7642 100644
--- a/tools/perf/util/maps.c
+++ b/tools/perf/util/maps.c
@@ -40,6 +40,7 @@ DECLARE_RC_STRUCT(maps) {
#ifdef HAVE_LIBUNWIND_SUPPORT
void *addr_space;
const struct unwind_libunwind_ops *unwind_libunwind_ops;
+ uint16_t e_machine;
#endif
#ifdef HAVE_LIBDW_SUPPORT
void *libdw_addr_space_dwfl;
@@ -206,6 +207,16 @@ void maps__set_unwind_libunwind_ops(struct maps *maps, const struct unwind_libun
{
RC_CHK_ACCESS(maps)->unwind_libunwind_ops = ops;
}
+
+uint16_t maps__e_machine(const struct maps *maps)
+{
+ return RC_CHK_ACCESS(maps)->e_machine;
+}
+
+void maps__set_e_machine(struct maps *maps, uint16_t e_machine)
+{
+ RC_CHK_ACCESS(maps)->e_machine = e_machine;
+}
#endif
#ifdef HAVE_LIBDW_SUPPORT
void *maps__libdw_addr_space_dwfl(const struct maps *maps)
@@ -1038,6 +1049,9 @@ int maps__copy_from(struct maps *dest, struct maps *parent)
down_write(maps__lock(dest));
down_read(maps__lock(parent));
+#ifdef HAVE_LIBUNWIND_SUPPORT
+ unwind__prepare_access(dest, maps__e_machine(parent));
+#endif
parent_maps_by_address = maps__maps_by_address(parent);
n = maps__nr_maps(parent);
if (maps__nr_maps(dest) == 0) {
@@ -1067,14 +1081,11 @@ int maps__copy_from(struct maps *dest, struct maps *parent)
if (!new)
err = -ENOMEM;
else {
- err = unwind__prepare_access(dest, new, NULL);
- if (!err) {
- dest_maps_by_address[i] = new;
- map__set_kmap_maps(new, dest);
- if (dest_maps_by_name)
- dest_maps_by_name[i] = map__get(new);
- RC_CHK_ACCESS(dest)->nr_maps = i + 1;
- }
+ dest_maps_by_address[i] = new;
+ map__set_kmap_maps(new, dest);
+ if (dest_maps_by_name)
+ dest_maps_by_name[i] = map__get(new);
+ RC_CHK_ACCESS(dest)->nr_maps = i + 1;
}
if (err)
map__put(new);
@@ -1099,9 +1110,7 @@ int maps__copy_from(struct maps *dest, struct maps *parent)
if (!new)
err = -ENOMEM;
else {
- err = unwind__prepare_access(dest, new, NULL);
- if (!err)
- err = __maps__insert(dest, new);
+ err = __maps__insert(dest, new);
}
map__put(new);
}
diff --git a/tools/perf/util/maps.h b/tools/perf/util/maps.h
index 20c52084ba9e..6469f62c41a8 100644
--- a/tools/perf/util/maps.h
+++ b/tools/perf/util/maps.h
@@ -51,6 +51,8 @@ void *maps__addr_space(const struct maps *maps);
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);
+uint16_t maps__e_machine(const struct maps *maps);
+void maps__set_e_machine(struct maps *maps, uint16_t e_machine);
#endif
#ifdef HAVE_LIBDW_SUPPORT
void *maps__libdw_addr_space_dwfl(const struct maps *maps);
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c
index 22be77225bb0..c5df11ad329c 100644
--- a/tools/perf/util/thread.c
+++ b/tools/perf/util/thread.c
@@ -358,41 +358,20 @@ size_t thread__fprintf(struct thread *thread, FILE *fp)
int thread__insert_map(struct thread *thread, struct map *map)
{
int ret;
+ uint16_t e_machine = thread__e_machine(thread, /*machine=*/NULL, /*e_flags=*/NULL);
- ret = unwind__prepare_access(thread__maps(thread), map, NULL);
+ ret = unwind__prepare_access(thread__maps(thread), e_machine);
if (ret)
return ret;
return maps__fixup_overlap_and_insert(thread__maps(thread), map);
}
-struct thread__prepare_access_maps_cb_args {
- int err;
- struct maps *maps;
-};
-
-static int thread__prepare_access_maps_cb(struct map *map, void *data)
-{
- bool initialized = false;
- struct thread__prepare_access_maps_cb_args *args = data;
-
- args->err = unwind__prepare_access(args->maps, map, &initialized);
-
- return (args->err || initialized) ? 1 : 0;
-}
-
static int thread__prepare_access(struct thread *thread)
{
- struct thread__prepare_access_maps_cb_args args = {
- .err = 0,
- };
-
- if (dwarf_callchain_users) {
- args.maps = thread__maps(thread);
- maps__for_each_map(thread__maps(thread), thread__prepare_access_maps_cb, &args);
- }
+ uint16_t e_machine = thread__e_machine(thread, /*machine=*/NULL, /*e_flags=*/NULL);
- return args.err;
+ return unwind__prepare_access(thread__maps(thread), e_machine);
}
static int thread__clone_maps(struct thread *thread, struct thread *parent, bool do_maps_clone)
diff --git a/tools/perf/util/unwind-libunwind-local.c b/tools/perf/util/unwind-libunwind-local.c
index 3ecdb468b859..8f0128ba05a7 100644
--- a/tools/perf/util/unwind-libunwind-local.c
+++ b/tools/perf/util/unwind-libunwind-local.c
@@ -722,16 +722,6 @@ static int _unwind__prepare_access(struct maps *maps)
return 0;
}
-static void _unwind__flush_access(struct maps *maps)
-{
- unw_flush_cache(maps__addr_space(maps), 0, 0);
-}
-
-static void _unwind__finish_access(struct maps *maps)
-{
- unw_destroy_addr_space(maps__addr_space(maps));
-}
-
static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb,
void *arg, int max_stack)
{
@@ -821,8 +811,6 @@ static int _unwind__get_entries(unwind_entry_cb_t cb, void *arg,
static struct unwind_libunwind_ops
_unwind_libunwind_ops = {
.prepare_access = _unwind__prepare_access,
- .flush_access = _unwind__flush_access,
- .finish_access = _unwind__finish_access,
.get_entries = _unwind__get_entries,
};
diff --git a/tools/perf/util/unwind-libunwind.c b/tools/perf/util/unwind-libunwind.c
index a0016b897dae..eaee7b78d87c 100644
--- a/tools/perf/util/unwind-libunwind.c
+++ b/tools/perf/util/unwind-libunwind.c
@@ -7,76 +7,55 @@
#include "debug.h"
#include "env.h"
#include "callchain.h"
+#include "libunwind-arch/libunwind-arch.h"
+#include <dwarf-regs.h>
+#include <elf.h>
struct unwind_libunwind_ops __weak *local_unwind_libunwind_ops;
struct unwind_libunwind_ops __weak *x86_32_unwind_libunwind_ops;
struct unwind_libunwind_ops __weak *arm64_unwind_libunwind_ops;
-int unwind__prepare_access(struct maps *maps, struct map *map, bool *initialized)
+int unwind__prepare_access(struct maps *maps, uint16_t e_machine)
{
- const char *arch;
- enum dso_type dso_type;
struct unwind_libunwind_ops *ops = local_unwind_libunwind_ops;
- struct dso *dso = map__dso(map);
- struct machine *machine;
- int err;
if (!dwarf_callchain_users)
return 0;
if (maps__addr_space(maps)) {
- pr_debug("unwind: thread map already set, dso=%s\n", dso__name(dso));
- if (initialized)
- *initialized = true;
+ pr_debug3("unwind: thread map already set\n");
return 0;
}
- machine = maps__machine(maps);
- /* env->arch is NULL for live-mode (i.e. perf top) */
- if (!machine->env || !machine->env->arch)
- goto out_register;
-
- dso_type = dso__type(dso, machine);
- if (dso_type == DSO__TYPE_UNKNOWN)
- return 0;
-
- arch = perf_env__arch(machine->env);
-
- if (!strcmp(arch, "x86")) {
- if (dso_type != DSO__TYPE_64BIT)
+ if (e_machine != EM_HOST) {
+ /* If not live/local mode. */
+ switch (e_machine) {
+ case EM_386:
ops = x86_32_unwind_libunwind_ops;
- } else if (!strcmp(arch, "arm64") || !strcmp(arch, "arm")) {
- if (dso_type == DSO__TYPE_64BIT)
+ break;
+ case EM_AARCH64:
ops = arm64_unwind_libunwind_ops;
- }
-
- if (!ops) {
- pr_warning_once("unwind: target platform=%s is not supported\n", arch);
+ break;
+ default:
+ pr_warning_once("unwind: ELF machine type %d is not supported\n",
+ e_machine);
return 0;
+ }
}
-out_register:
maps__set_unwind_libunwind_ops(maps, ops);
+ maps__set_e_machine(maps, e_machine);
- err = maps__unwind_libunwind_ops(maps)->prepare_access(maps);
- if (initialized)
- *initialized = err ? false : true;
- return err;
+ return maps__unwind_libunwind_ops(maps)->prepare_access(maps);
}
void unwind__flush_access(struct maps *maps)
{
- const struct unwind_libunwind_ops *ops = maps__unwind_libunwind_ops(maps);
-
- if (ops)
- ops->flush_access(maps);
+ libunwind_arch__flush_access(maps);
}
void unwind__finish_access(struct maps *maps)
{
- const struct unwind_libunwind_ops *ops = maps__unwind_libunwind_ops(maps);
-
- if (ops)
- ops->finish_access(maps);
+ libunwind_arch__finish_access(maps);
}
int libunwind__get_entries(unwind_entry_cb_t cb, void *arg,
diff --git a/tools/perf/util/unwind.h b/tools/perf/util/unwind.h
index e32d39ab41c6..09fc60df262d 100644
--- a/tools/perf/util/unwind.h
+++ b/tools/perf/util/unwind.h
@@ -2,6 +2,7 @@
#ifndef __UNWIND_H
#define __UNWIND_H
+#include <stdint.h>
#include <linux/compiler.h>
#include <linux/types.h>
#include "util/map_symbol.h"
@@ -20,8 +21,6 @@ typedef int (*unwind_entry_cb_t)(struct unwind_entry *entry, void *arg);
struct unwind_libunwind_ops {
int (*prepare_access)(struct maps *maps);
- void (*flush_access)(struct maps *maps);
- void (*finish_access)(struct maps *maps);
int (*get_entries)(unwind_entry_cb_t cb, void *arg,
struct thread *thread,
struct perf_sample *data, int max_stack, bool best_effort);
@@ -53,13 +52,12 @@ int libunwind__get_entries(unwind_entry_cb_t cb, void *arg,
struct thread *thread,
struct perf_sample *data, int max_stack,
bool best_effort);
-int unwind__prepare_access(struct maps *maps, struct map *map, bool *initialized);
+int unwind__prepare_access(struct maps *maps, uint16_t e_machine);
void unwind__flush_access(struct maps *maps);
void unwind__finish_access(struct maps *maps);
#else
static inline int unwind__prepare_access(struct maps *maps __maybe_unused,
- struct map *map __maybe_unused,
- bool *initialized __maybe_unused)
+ uint16_t e_machine __maybe_unused)
{
return 0;
}
--
2.53.0.473.g4a7958ca14-goog
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH v2 7/8] perf unwind-libunwind: Remove libunwind-local
2026-03-05 22:19 ` [PATCH v2 0/8] perf libunwind multiple remote support Ian Rogers
` (5 preceding siblings ...)
2026-03-05 22:19 ` [PATCH v2 6/8] perf unwind-libunwind: Move flush/finish access out of local Ian Rogers
@ 2026-03-05 22:19 ` Ian Rogers
2026-03-05 22:19 ` [PATCH v2 8/8] perf unwind-libunwind: Add RISC-V libunwind support Ian Rogers
2026-03-19 21:39 ` [PATCH v2 0/8] perf libunwind multiple remote support Namhyung Kim
8 siblings, 0 replies; 27+ messages in thread
From: Ian Rogers @ 2026-03-05 22:19 UTC (permalink / raw)
To: irogers
Cc: 9erthalion6, acme, adrian.hunter, alex, alexander.shishkin,
andrew.jones, aou, atrajeev, blakejones, ctshao, dapeng1.mi,
howardchu95, james.clark, john.g.garry, jolsa, leo.yan,
libunwind-devel, linux-arm-kernel, linux-kernel, linux-perf-users,
linux-riscv, mingo, namhyung, palmer, peterz, pjw, shimin.guo,
tglozar, tmricht, will, yuzhuo
Local unwinding only works on the machine libunwind is built for,
rather than cross platform, the APIs for remote and local unwinding
are similar but types like unw_word_t depend on the included
header. Place the architecture specific code into the appropriate
libunwind-<arch>.c file. Put generic code in unwind-libunwind.c and
use libunwind-arch to choose the correct implementation based on the
thread's e_machine. Structuring the code this way avoids including the
unwind-libunwind-local.c for each architecture of remote
unwinding. Data is moved into the struct unwind_info to simplify the
architecture and generic code, trying to keep as much code as possible
generic.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/Build | 3 -
.../perf/util/libunwind-arch/libunwind-arch.c | 184 ++++
.../perf/util/libunwind-arch/libunwind-arch.h | 234 +++++
.../perf/util/libunwind-arch/libunwind-arm.c | 256 ++++++
.../util/libunwind-arch/libunwind-arm64.c | 255 ++++++
.../perf/util/libunwind-arch/libunwind-i386.c | 254 ++++++
.../util/libunwind-arch/libunwind-loongarch.c | 255 ++++++
.../perf/util/libunwind-arch/libunwind-mips.c | 255 ++++++
.../util/libunwind-arch/libunwind-ppc32.c | 255 ++++++
.../util/libunwind-arch/libunwind-ppc64.c | 255 ++++++
.../perf/util/libunwind-arch/libunwind-s390.c | 255 ++++++
.../util/libunwind-arch/libunwind-x86_64.c | 253 ++++++
tools/perf/util/libunwind/arm64.c | 35 -
tools/perf/util/libunwind/x86_32.c | 29 -
tools/perf/util/maps.c | 10 -
tools/perf/util/maps.h | 2 -
tools/perf/util/unwind-libunwind-local.c | 820 ------------------
tools/perf/util/unwind-libunwind.c | 654 +++++++++++++-
tools/perf/util/unwind.h | 7 -
19 files changed, 3334 insertions(+), 937 deletions(-)
delete mode 100644 tools/perf/util/libunwind/arm64.c
delete mode 100644 tools/perf/util/libunwind/x86_32.c
delete mode 100644 tools/perf/util/unwind-libunwind-local.c
diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index 434dc6716a75..be2745567d61 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -228,11 +228,8 @@ perf-util-$(CONFIG_LIBDW) += annotate-data.o
perf-util-$(CONFIG_LIBDW) += libdw.o
perf-util-$(CONFIG_LIBDW) += unwind-libdw.o
-perf-util-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind-local.o
perf-util-$(CONFIG_LIBUNWIND) += unwind-libunwind.o
perf-util-$(CONFIG_LIBUNWIND) += libunwind-arch/
-perf-util-$(CONFIG_LIBUNWIND_X86) += libunwind/x86_32.o
-perf-util-$(CONFIG_LIBUNWIND_AARCH64) += libunwind/arm64.o
ifeq ($(CONFIG_LIBTRACEEVENT),y)
perf-util-$(CONFIG_LIBBABELTRACE) += data-convert-bt.o
diff --git a/tools/perf/util/libunwind-arch/libunwind-arch.c b/tools/perf/util/libunwind-arch/libunwind-arch.c
index 9692e6c81492..8539b4233df4 100644
--- a/tools/perf/util/libunwind-arch/libunwind-arch.c
+++ b/tools/perf/util/libunwind-arch/libunwind-arch.c
@@ -112,3 +112,187 @@ void libunwind_arch__finish_access(struct maps *maps)
break;
}
}
+
+void *libunwind_arch__create_addr_space(unsigned int e_machine)
+{
+ switch (e_machine) {
+ case EM_ARM:
+ return __libunwind_arch__create_addr_space_arm();
+ case EM_AARCH64:
+ return __libunwind_arch__create_addr_space_arm64();
+ case EM_LOONGARCH:
+ return __libunwind_arch__create_addr_space_loongarch();
+ case EM_MIPS:
+ return __libunwind_arch__create_addr_space_mips();
+ case EM_PPC:
+ return __libunwind_arch__create_addr_space_ppc32();
+ case EM_PPC64:
+ return __libunwind_arch__create_addr_space_ppc64();
+ case EM_S390:
+ return __libunwind_arch__create_addr_space_s390();
+ case EM_386:
+ return __libunwind_arch__create_addr_space_i386();
+ case EM_X86_64:
+ return __libunwind_arch__create_addr_space_x86_64();
+ default:
+ pr_err("ELF MACHINE %x is not supported.\n", e_machine);
+ return NULL;
+ }
+}
+
+int libunwind_arch__dwarf_search_unwind_table(unsigned int e_machine,
+ void *as,
+ uint64_t ip,
+ struct libarch_unwind__dyn_info *di,
+ void *pi,
+ int need_unwind_info,
+ void *arg)
+{
+ switch (e_machine) {
+ case EM_ARM:
+ return __libunwind_arch__dwarf_search_unwind_table_arm(as, ip, di, pi,
+ need_unwind_info, arg);
+ case EM_AARCH64:
+ return __libunwind_arch__dwarf_search_unwind_table_arm64(as, ip, di, pi,
+ need_unwind_info, arg);
+ case EM_LOONGARCH:
+ return __libunwind_arch__dwarf_search_unwind_table_loongarch(as, ip, di, pi,
+ need_unwind_info, arg);
+ case EM_MIPS:
+ return __libunwind_arch__dwarf_search_unwind_table_mips(as, ip, di, pi,
+ need_unwind_info, arg);
+ case EM_PPC:
+ return __libunwind_arch__dwarf_search_unwind_table_ppc32(as, ip, di, pi,
+ need_unwind_info, arg);
+ case EM_PPC64:
+ return __libunwind_arch__dwarf_search_unwind_table_ppc64(as, ip, di, pi,
+ need_unwind_info, arg);
+ case EM_S390:
+ return __libunwind_arch__dwarf_search_unwind_table_s390(as, ip, di, pi,
+ need_unwind_info, arg);
+ case EM_386:
+ return __libunwind_arch__dwarf_search_unwind_table_i386(as, ip, di, pi,
+ need_unwind_info, arg);
+ case EM_X86_64:
+ return __libunwind_arch__dwarf_search_unwind_table_x86_64(as, ip, di, pi,
+ need_unwind_info, arg);
+ default:
+ pr_err("ELF MACHINE %x is not supported.\n", e_machine);
+ return -EINVAL;
+ }
+}
+
+int libunwind_arch__dwarf_find_debug_frame(unsigned int e_machine,
+ int found,
+ struct libarch_unwind__dyn_info *di_debug,
+ uint64_t ip,
+ uint64_t segbase,
+ const char *obj_name,
+ uint64_t start,
+ uint64_t end)
+{
+ switch (e_machine) {
+ case EM_ARM:
+ return __libunwind_arch__dwarf_find_debug_frame_arm(found, di_debug, ip, segbase,
+ obj_name, start, end);
+ case EM_AARCH64:
+ return __libunwind_arch__dwarf_find_debug_frame_arm64(found, di_debug, ip, segbase,
+ obj_name, start, end);
+ case EM_LOONGARCH:
+ return __libunwind_arch__dwarf_find_debug_frame_loongarch(found, di_debug, ip,
+ segbase, obj_name,
+ start, end);
+ case EM_MIPS:
+ return __libunwind_arch__dwarf_find_debug_frame_mips(found, di_debug, ip, segbase,
+ obj_name, start, end);
+ case EM_PPC:
+ return __libunwind_arch__dwarf_find_debug_frame_ppc32(found, di_debug, ip, segbase,
+ obj_name, start, end);
+ case EM_PPC64:
+ return __libunwind_arch__dwarf_find_debug_frame_ppc64(found, di_debug, ip, segbase,
+ obj_name, start, end);
+ case EM_S390:
+ return __libunwind_arch__dwarf_find_debug_frame_s390(found, di_debug, ip, segbase,
+ obj_name, start, end);
+ case EM_386:
+ return __libunwind_arch__dwarf_find_debug_frame_i386(found, di_debug, ip, segbase,
+ obj_name, start, end);
+ case EM_X86_64:
+ return __libunwind_arch__dwarf_find_debug_frame_x86_64(found, di_debug, ip, segbase,
+ obj_name, start, end);
+ default:
+ pr_err("ELF MACHINE %x is not supported.\n", e_machine);
+ return -EINVAL;
+ }
+}
+
+struct unwind_info *libunwind_arch_unwind_info__new(struct thread *thread,
+ struct perf_sample *sample, int max_stack,
+ bool best_effort, uint16_t e_machine,
+ uint64_t first_ip)
+{
+ switch (e_machine) {
+ case EM_ARM:
+ return __libunwind_arch_unwind_info__new_arm(thread, sample, max_stack,
+ best_effort, first_ip);
+ case EM_AARCH64:
+ return __libunwind_arch_unwind_info__new_arm64(thread, sample, max_stack,
+ best_effort, first_ip);
+ case EM_LOONGARCH:
+ return __libunwind_arch_unwind_info__new_loongarch(thread, sample, max_stack,
+ best_effort, first_ip);
+ case EM_MIPS:
+ return __libunwind_arch_unwind_info__new_mips(thread, sample, max_stack,
+ best_effort, first_ip);
+ case EM_PPC:
+ return __libunwind_arch_unwind_info__new_ppc32(thread, sample, max_stack,
+ best_effort, first_ip);
+ case EM_PPC64:
+ return __libunwind_arch_unwind_info__new_ppc64(thread, sample, max_stack,
+ best_effort, first_ip);
+ case EM_S390:
+ return __libunwind_arch_unwind_info__new_s390(thread, sample, max_stack,
+ best_effort, first_ip);
+ case EM_386:
+ return __libunwind_arch_unwind_info__new_i386(thread, sample, max_stack,
+ best_effort, first_ip);
+ case EM_X86_64:
+ return __libunwind_arch_unwind_info__new_x86_64(thread, sample, max_stack,
+ best_effort, first_ip);
+ default:
+ pr_err("ELF MACHINE %x is not supported.\n", e_machine);
+ return NULL;
+ }
+}
+
+void libunwind_arch_unwind_info__delete(struct unwind_info *ui)
+{
+ free(ui);
+}
+
+int libunwind_arch__unwind_step(struct unwind_info *ui)
+{
+ switch (ui->e_machine) {
+ case EM_ARM:
+ return __libunwind_arch__unwind_step_arm(ui);
+ case EM_AARCH64:
+ return __libunwind_arch__unwind_step_arm64(ui);
+ case EM_LOONGARCH:
+ return __libunwind_arch__unwind_step_loongarch(ui);
+ case EM_MIPS:
+ return __libunwind_arch__unwind_step_mips(ui);
+ case EM_PPC:
+ return __libunwind_arch__unwind_step_ppc32(ui);
+ case EM_PPC64:
+ return __libunwind_arch__unwind_step_ppc64(ui);
+ case EM_S390:
+ return __libunwind_arch__unwind_step_s390(ui);
+ case EM_386:
+ return __libunwind_arch__unwind_step_i386(ui);
+ case EM_X86_64:
+ return __libunwind_arch__unwind_step_x86_64(ui);
+ default:
+ pr_err("ELF MACHINE %x is not supported.\n", ui->e_machine);
+ return -EINVAL;
+ }
+}
diff --git a/tools/perf/util/libunwind-arch/libunwind-arch.h b/tools/perf/util/libunwind-arch/libunwind-arch.h
index c00277a5e914..2bf7fc33313b 100644
--- a/tools/perf/util/libunwind-arch/libunwind-arch.h
+++ b/tools/perf/util/libunwind-arch/libunwind-arch.h
@@ -2,7 +2,36 @@
#ifndef __LIBUNWIND_ARCH_H
#define __LIBUNWIND_ARCH_H
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+struct machine;
struct maps;
+struct perf_sample;
+struct thread;
+
+struct unwind_info {
+ struct machine *machine;
+ struct thread *thread;
+ struct perf_sample *sample;
+ void *cursor;
+ uint64_t *ips;
+ int cur_ip;
+ int max_ips;
+ unsigned int unw_word_t_size;
+ uint16_t e_machine;
+ bool best_effort;
+};
+
+struct libarch_unwind__dyn_info {
+ uint64_t start_ip;
+ uint64_t end_ip;
+ uint64_t segbase;
+ uint64_t table_data;
+ uint64_t table_len;
+};
+struct libarch_unwind__proc_info;
int __get_perf_regnum_for_unw_regnum_arm(int unw_regnum);
int __get_perf_regnum_for_unw_regnum_arm64(int unw_regnum);
@@ -37,4 +66,209 @@ void __libunwind_arch__finish_access_i386(struct maps *maps);
void __libunwind_arch__finish_access_x86_64(struct maps *maps);
void libunwind_arch__finish_access(struct maps *maps);
+void *__libunwind_arch__create_addr_space_arm(void);
+void *__libunwind_arch__create_addr_space_arm64(void);
+void *__libunwind_arch__create_addr_space_loongarch(void);
+void *__libunwind_arch__create_addr_space_mips(void);
+void *__libunwind_arch__create_addr_space_ppc32(void);
+void *__libunwind_arch__create_addr_space_ppc64(void);
+void *__libunwind_arch__create_addr_space_s390(void);
+void *__libunwind_arch__create_addr_space_i386(void);
+void *__libunwind_arch__create_addr_space_x86_64(void);
+void *libunwind_arch__create_addr_space(unsigned int e_machine);
+
+int __libunwind__find_proc_info(void *as, uint64_t ip, void *pi, int need_unwind_info, void *arg);
+int __libunwind__access_mem(void *as, uint64_t addr, void *valp_word, int __write, void *arg);
+int __libunwind__access_reg(void *as, int regnum, void *valp_word, int __write, void *arg);
+
+int __libunwind_arch__dwarf_search_unwind_table_arm(void *as, uint64_t ip,
+ struct libarch_unwind__dyn_info *di,
+ void *pi,
+ int need_unwind_info,
+ void *arg);
+int __libunwind_arch__dwarf_search_unwind_table_arm64(void *as, uint64_t ip,
+ struct libarch_unwind__dyn_info *di,
+ void *pi,
+ int need_unwind_info,
+ void *arg);
+int __libunwind_arch__dwarf_search_unwind_table_loongarch(void *as, uint64_t ip,
+ struct libarch_unwind__dyn_info *di,
+ void *pi,
+ int need_unwind_info,
+ void *arg);
+int __libunwind_arch__dwarf_search_unwind_table_mips(void *as, uint64_t ip,
+ struct libarch_unwind__dyn_info *di,
+ void *pi,
+ int need_unwind_info,
+ void *arg);
+int __libunwind_arch__dwarf_search_unwind_table_ppc32(void *as, uint64_t ip,
+ struct libarch_unwind__dyn_info *di,
+ void *pi,
+ int need_unwind_info,
+ void *arg);
+int __libunwind_arch__dwarf_search_unwind_table_ppc64(void *as, uint64_t ip,
+ struct libarch_unwind__dyn_info *di,
+ void *pi,
+ int need_unwind_info,
+ void *arg);
+int __libunwind_arch__dwarf_search_unwind_table_s390(void *as, uint64_t ip,
+ struct libarch_unwind__dyn_info *di,
+ void *pi,
+ int need_unwind_info,
+ void *arg);
+int __libunwind_arch__dwarf_search_unwind_table_i386(void *as, uint64_t ip,
+ struct libarch_unwind__dyn_info *di,
+ void *pi,
+ int need_unwind_info,
+ void *arg);
+int __libunwind_arch__dwarf_search_unwind_table_x86_64(void *as, uint64_t ip,
+ struct libarch_unwind__dyn_info *di,
+ void *pi,
+ int need_unwind_info,
+ void *arg);
+int libunwind_arch__dwarf_search_unwind_table(unsigned int e_machine,
+ void *as,
+ uint64_t ip,
+ struct libarch_unwind__dyn_info *di,
+ void *pi,
+ int need_unwind_info,
+ void *arg);
+
+int __libunwind_arch__dwarf_find_debug_frame_arm(int found,
+ struct libarch_unwind__dyn_info *di_debug,
+ uint64_t ip,
+ uint64_t segbase,
+ const char *obj_name,
+ uint64_t start,
+ uint64_t end);
+int __libunwind_arch__dwarf_find_debug_frame_arm64(int found,
+ struct libarch_unwind__dyn_info *di_debug,
+ uint64_t ip,
+ uint64_t segbase,
+ const char *obj_name,
+ uint64_t start,
+ uint64_t end);
+int __libunwind_arch__dwarf_find_debug_frame_loongarch(int found,
+ struct libarch_unwind__dyn_info *di_debug,
+ uint64_t ip,
+ uint64_t segbase,
+ const char *obj_name,
+ uint64_t start,
+ uint64_t end);
+int __libunwind_arch__dwarf_find_debug_frame_mips(int found,
+ struct libarch_unwind__dyn_info *di_debug,
+ uint64_t ip,
+ uint64_t segbase,
+ const char *obj_name,
+ uint64_t start,
+ uint64_t end);
+int __libunwind_arch__dwarf_find_debug_frame_ppc32(int found,
+ struct libarch_unwind__dyn_info *di_debug,
+ uint64_t ip,
+ uint64_t segbase,
+ const char *obj_name,
+ uint64_t start,
+ uint64_t end);
+int __libunwind_arch__dwarf_find_debug_frame_ppc64(int found,
+ struct libarch_unwind__dyn_info *di_debug,
+ uint64_t ip,
+ uint64_t segbase,
+ const char *obj_name,
+ uint64_t start,
+ uint64_t end);
+int __libunwind_arch__dwarf_find_debug_frame_s390(int found,
+ struct libarch_unwind__dyn_info *di_debug,
+ uint64_t ip,
+ uint64_t segbase,
+ const char *obj_name,
+ uint64_t start,
+ uint64_t end);
+int __libunwind_arch__dwarf_find_debug_frame_i386(int found,
+ struct libarch_unwind__dyn_info *di_debug,
+ uint64_t ip,
+ uint64_t segbase,
+ const char *obj_name,
+ uint64_t start,
+ uint64_t end);
+int __libunwind_arch__dwarf_find_debug_frame_x86_64(int found,
+ struct libarch_unwind__dyn_info *di_debug,
+ uint64_t ip,
+ uint64_t segbase,
+ const char *obj_name,
+ uint64_t start,
+ uint64_t end);
+int libunwind_arch__dwarf_find_debug_frame(unsigned int e_machine,
+ int found,
+ struct libarch_unwind__dyn_info *di_debug,
+ uint64_t ip,
+ uint64_t segbase,
+ const char *obj_name,
+ uint64_t start,
+ uint64_t end);
+
+struct unwind_info *__libunwind_arch_unwind_info__new_arm(struct thread *thread,
+ struct perf_sample *sample,
+ int max_stack,
+ bool best_effort,
+ uint64_t first_ip);
+struct unwind_info *__libunwind_arch_unwind_info__new_arm64(struct thread *thread,
+ struct perf_sample *sample,
+ int max_stack,
+ bool best_effort,
+ uint64_t first_ip);
+struct unwind_info *__libunwind_arch_unwind_info__new_loongarch(struct thread *thread,
+ struct perf_sample *sample,
+ int max_stack,
+ bool best_effort,
+ uint64_t first_ip);
+struct unwind_info *__libunwind_arch_unwind_info__new_mips(struct thread *thread,
+ struct perf_sample *sample,
+ int max_stack,
+ bool best_effort,
+ uint64_t first_ip);
+struct unwind_info *__libunwind_arch_unwind_info__new_ppc32(struct thread *thread,
+ struct perf_sample *sample,
+ int max_stack,
+ bool best_effort,
+ uint64_t first_ip);
+struct unwind_info *__libunwind_arch_unwind_info__new_ppc64(struct thread *thread,
+ struct perf_sample *sample,
+ int max_stack,
+ bool best_effort,
+ uint64_t first_ip);
+struct unwind_info *__libunwind_arch_unwind_info__new_s390(struct thread *thread,
+ struct perf_sample *sample,
+ int max_stack,
+ bool best_effort,
+ uint64_t first_ip);
+struct unwind_info *__libunwind_arch_unwind_info__new_i386(struct thread *thread,
+ struct perf_sample *sample,
+ int max_stack,
+ bool best_effort,
+ uint64_t first_ip);
+struct unwind_info *__libunwind_arch_unwind_info__new_x86_64(struct thread *thread,
+ struct perf_sample *sample,
+ int max_stack,
+ bool best_effort,
+ uint64_t first_ip);
+struct unwind_info *libunwind_arch_unwind_info__new(struct thread *thread,
+ struct perf_sample *sample,
+ int max_stack,
+ bool best_effort,
+ uint16_t e_machine,
+ uint64_t first_ip);
+
+void libunwind_arch_unwind_info__delete(struct unwind_info *ui);
+
+int __libunwind_arch__unwind_step_arm(struct unwind_info *ui);
+int __libunwind_arch__unwind_step_arm64(struct unwind_info *ui);
+int __libunwind_arch__unwind_step_loongarch(struct unwind_info *ui);
+int __libunwind_arch__unwind_step_mips(struct unwind_info *ui);
+int __libunwind_arch__unwind_step_ppc32(struct unwind_info *ui);
+int __libunwind_arch__unwind_step_ppc64(struct unwind_info *ui);
+int __libunwind_arch__unwind_step_s390(struct unwind_info *ui);
+int __libunwind_arch__unwind_step_i386(struct unwind_info *ui);
+int __libunwind_arch__unwind_step_x86_64(struct unwind_info *ui);
+int libunwind_arch__unwind_step(struct unwind_info *ui);
+
#endif /* __LIBUNWIND_ARCH_H */
diff --git a/tools/perf/util/libunwind-arch/libunwind-arm.c b/tools/perf/util/libunwind-arch/libunwind-arm.c
index bbaf01406c52..b31aeeab1663 100644
--- a/tools/perf/util/libunwind-arch/libunwind-arm.c
+++ b/tools/perf/util/libunwind-arch/libunwind-arm.c
@@ -2,8 +2,12 @@
#include "libunwind-arch.h"
#include "../debug.h"
#include "../maps.h"
+#include "../thread.h"
#include "../../../arch/arm/include/uapi/asm/perf_regs.h"
#include <linux/compiler.h>
+#include <linux/kernel.h>
+#include <linux/zalloc.h>
+#include <elf.h>
#include <errno.h>
#ifdef HAVE_LIBUNWIND_ARM_SUPPORT
@@ -32,3 +36,255 @@ void __libunwind_arch__finish_access_arm(struct maps *maps __maybe_unused)
unw_destroy_addr_space(maps__addr_space(maps));
#endif
}
+
+
+#ifdef HAVE_LIBUNWIND_ARM_SUPPORT
+static int find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
+ int need_unwind_info, void *arg)
+{
+ return __libunwind__find_proc_info(as, ip, pi, need_unwind_info, arg, sizeof(unw_word_t));
+}
+
+static void put_unwind_info(unw_addr_space_t __maybe_unused as,
+ unw_proc_info_t *pi __maybe_unused,
+ void *arg __maybe_unused)
+{
+ pr_debug("unwind: put_unwind_info called\n");
+}
+
+static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as,
+ unw_word_t __maybe_unused *dil_addr,
+ void __maybe_unused *arg)
+{
+ return -UNW_ENOINFO;
+}
+
+static int access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *valp,
+ int __write, void *arg)
+{
+ return __libunwind__access_mem(as, addr, valp, __write, arg, sizeof(unw_word_t));
+}
+
+static int access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t *valp,
+ int __write, void *arg)
+{
+ return __libunwind__access_reg(as, regnum, valp, __write, arg, sizeof(unw_word_t));
+}
+
+static int access_fpreg(unw_addr_space_t __maybe_unused as,
+ unw_regnum_t __maybe_unused num,
+ unw_fpreg_t __maybe_unused *val,
+ int __maybe_unused __write,
+ void __maybe_unused *arg)
+{
+ pr_err("unwind: access_fpreg unsupported\n");
+ return -UNW_EINVAL;
+}
+
+static int resume(unw_addr_space_t __maybe_unused as,
+ unw_cursor_t __maybe_unused *cu,
+ void __maybe_unused *arg)
+{
+ pr_err("unwind: resume unsupported\n");
+ return -UNW_EINVAL;
+}
+
+static int get_proc_name(unw_addr_space_t __maybe_unused as,
+ unw_word_t __maybe_unused addr,
+ char __maybe_unused *bufp, size_t __maybe_unused buf_len,
+ unw_word_t __maybe_unused *offp, void __maybe_unused *arg)
+{
+ pr_err("unwind: get_proc_name unsupported\n");
+ return -UNW_EINVAL;
+}
+#endif
+
+void *__libunwind_arch__create_addr_space_arm(void)
+{
+#ifdef HAVE_LIBUNWIND_ARM_SUPPORT
+ static unw_accessors_t accessors = {
+ .find_proc_info = find_proc_info,
+ .put_unwind_info = put_unwind_info,
+ .get_dyn_info_list_addr = get_dyn_info_list_addr,
+ .access_mem = access_mem,
+ .access_reg = access_reg,
+ .access_fpreg = access_fpreg,
+ .resume = resume,
+ .get_proc_name = get_proc_name,
+ };
+ unw_addr_space_t addr_space;
+
+ addr_space = unw_create_addr_space(&accessors, /*byte_order=*/0);
+ unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL);
+ return addr_space;
+#else
+ return NULL;
+#endif
+}
+
+#ifdef HAVE_LIBUNWIND_ARM_SUPPORT
+extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,
+ unw_word_t ip,
+ unw_dyn_info_t *di,
+ unw_proc_info_t *pi,
+ int need_unwind_info, void *arg);
+#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table)
+#endif
+
+int __libunwind_arch__dwarf_search_unwind_table_arm(void *as __maybe_unused,
+ uint64_t ip __maybe_unused,
+ struct libarch_unwind__dyn_info *_di __maybe_unused,
+ void *pi __maybe_unused,
+ int need_unwind_info __maybe_unused,
+ void *arg __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_ARM_SUPPORT
+ unw_dyn_info_t di = {
+ .format = UNW_INFO_FORMAT_REMOTE_TABLE,
+ .start_ip = _di->start_ip,
+ .end_ip = _di->end_ip,
+ .u = {
+ .rti = {
+ .segbase = _di->segbase,
+ .table_data = _di->table_data,
+ .table_len = _di->table_len,
+ },
+ },
+ };
+ int ret = dwarf_search_unwind_table(as, ip, &di, pi, need_unwind_info, arg);
+
+ _di->start_ip = di.start_ip;
+ _di->end_ip = di.end_ip;
+ _di->segbase = di.u.rti.segbase;
+ _di->table_data = di.u.rti.table_data;
+ _di->table_len = di.u.rti.table_len;
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
+
+#if defined(HAVE_LIBUNWIND_ARM_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_ARM)
+extern int UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug,
+ unw_word_t ip,
+ unw_word_t segbase,
+ const char *obj_name, unw_word_t start,
+ unw_word_t end);
+#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame)
+#endif
+
+int __libunwind_arch__dwarf_find_debug_frame_arm(int found __maybe_unused,
+ struct libarch_unwind__dyn_info *_di __maybe_unused,
+ uint64_t ip __maybe_unused,
+ uint64_t segbase __maybe_unused,
+ const char *obj_name __maybe_unused,
+ uint64_t start __maybe_unused,
+ uint64_t end __maybe_unused)
+{
+#if defined(HAVE_LIBUNWIND_ARM_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_ARM)
+ unw_dyn_info_t di = {
+ .format = UNW_INFO_FORMAT_REMOTE_TABLE,
+ .start_ip = _di->start_ip,
+ .end_ip = _di->end_ip,
+ .u = {
+ .rti = {
+ .segbase = _di->segbase,
+ .table_data = _di->table_data,
+ .table_len = _di->table_len,
+ },
+ },
+ };
+ int ret = dwarf_find_debug_frame(found, &di, ip, segbase, obj_name, start, end);
+
+ _di->start_ip = di.start_ip;
+ _di->end_ip = di.end_ip;
+ _di->segbase = di.u.rti.segbase;
+ _di->table_data = di.u.rti.table_data;
+ _di->table_len = di.u.rti.table_len;
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
+
+struct unwind_info *__libunwind_arch_unwind_info__new_arm(struct thread *thread __maybe_unused,
+ struct perf_sample *sample __maybe_unused,
+ int max_stack __maybe_unused,
+ bool best_effort __maybe_unused,
+ uint64_t first_ip __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_ARM_SUPPORT
+ struct arch_unwind_info {
+ struct unwind_info ui;
+ unw_cursor_t _cursor;
+ uint64_t _ips[];
+ };
+
+ struct maps *maps = thread__maps(thread);
+ void *addr_space = maps__addr_space(maps);
+ struct arch_unwind_info *ui;
+ int ret;
+
+ if (addr_space == NULL)
+ return NULL;
+
+ ui = zalloc(sizeof(*ui) + sizeof(ui->_ips[0]) * max_stack);
+ if (!ui)
+ return NULL;
+
+ ui->ui.machine = maps__machine(maps);
+ ui->ui.thread = thread;
+ ui->ui.sample = sample;
+ ui->ui.cursor = &ui->_cursor;
+ ui->ui.ips = &ui->_ips[0];
+ ui->ui.ips[0] = first_ip;
+ ui->ui.cur_ip = 1;
+ ui->ui.max_ips = max_stack;
+ ui->ui.unw_word_t_size = sizeof(unw_word_t);
+ ui->ui.e_machine = EM_ARM;
+ ui->ui.best_effort = best_effort;
+
+ ret = unw_init_remote(&ui->_cursor, addr_space, &ui->ui);
+ if (ret) {
+ if (!best_effort)
+ pr_err("libunwind: %s\n", unw_strerror(ret));
+ free(ui);
+ return NULL;
+ }
+
+ return &ui->ui;
+#else
+ return NULL;
+#endif
+}
+
+int __libunwind_arch__unwind_step_arm(struct unwind_info *ui __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_ARM_SUPPORT
+ int ret;
+
+ if (ui->cur_ip >= ui->max_ips)
+ return -1;
+
+ ret = unw_step(ui->cursor);
+ if (ret > 0) {
+ uint64_t ip;
+
+ unw_get_reg(ui->cursor, UNW_REG_IP, &ip);
+
+ if (unw_is_signal_frame(ui->cursor) <= 0) {
+ /*
+ * Decrement the IP for any non-activation frames. This
+ * is required to properly find the srcline for caller
+ * frames. See also the documentation for
+ * dwfl_frame_pc(), which this code tries to replicate.
+ */
+ --ip;
+ }
+ ui->ips[ui->cur_ip++] = ip;
+ }
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
diff --git a/tools/perf/util/libunwind-arch/libunwind-arm64.c b/tools/perf/util/libunwind-arch/libunwind-arm64.c
index 8ba510089736..ceeae2d0f8e9 100644
--- a/tools/perf/util/libunwind-arch/libunwind-arm64.c
+++ b/tools/perf/util/libunwind-arch/libunwind-arm64.c
@@ -2,8 +2,12 @@
#include "libunwind-arch.h"
#include "../debug.h"
#include "../maps.h"
+#include "../thread.h"
#include "../../../arch/arm64/include/uapi/asm/perf_regs.h"
#include <linux/compiler.h>
+#include <linux/kernel.h>
+#include <linux/zalloc.h>
+#include <elf.h>
#include <errno.h>
#ifdef HAVE_LIBUNWIND_AARCH64_SUPPORT
@@ -32,3 +36,254 @@ void __libunwind_arch__finish_access_arm64(struct maps *maps __maybe_unused)
unw_destroy_addr_space(maps__addr_space(maps));
#endif
}
+
+#ifdef HAVE_LIBUNWIND_AARCH64_SUPPORT
+static int find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
+ int need_unwind_info, void *arg)
+{
+ return __libunwind__find_proc_info(as, ip, pi, need_unwind_info, arg, sizeof(unw_word_t));
+}
+
+static void put_unwind_info(unw_addr_space_t __maybe_unused as,
+ unw_proc_info_t *pi __maybe_unused,
+ void *arg __maybe_unused)
+{
+ pr_debug("unwind: put_unwind_info called\n");
+}
+
+static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as,
+ unw_word_t __maybe_unused *dil_addr,
+ void __maybe_unused *arg)
+{
+ return -UNW_ENOINFO;
+}
+
+static int access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *valp,
+ int __write, void *arg)
+{
+ return __libunwind__access_mem(as, addr, valp, __write, arg, sizeof(unw_word_t));
+}
+
+static int access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t *valp,
+ int __write, void *arg)
+{
+ return __libunwind__access_reg(as, regnum, valp, __write, arg, sizeof(unw_word_t));
+}
+
+static int access_fpreg(unw_addr_space_t __maybe_unused as,
+ unw_regnum_t __maybe_unused num,
+ unw_fpreg_t __maybe_unused *val,
+ int __maybe_unused __write,
+ void __maybe_unused *arg)
+{
+ pr_err("unwind: access_fpreg unsupported\n");
+ return -UNW_EINVAL;
+}
+
+static int resume(unw_addr_space_t __maybe_unused as,
+ unw_cursor_t __maybe_unused *cu,
+ void __maybe_unused *arg)
+{
+ pr_err("unwind: resume unsupported\n");
+ return -UNW_EINVAL;
+}
+
+static int get_proc_name(unw_addr_space_t __maybe_unused as,
+ unw_word_t __maybe_unused addr,
+ char __maybe_unused *bufp, size_t __maybe_unused buf_len,
+ unw_word_t __maybe_unused *offp, void __maybe_unused *arg)
+{
+ pr_err("unwind: get_proc_name unsupported\n");
+ return -UNW_EINVAL;
+}
+#endif
+
+void *__libunwind_arch__create_addr_space_arm64(void)
+{
+#ifdef HAVE_LIBUNWIND_AARCH64_SUPPORT
+ static unw_accessors_t accessors = {
+ .find_proc_info = find_proc_info,
+ .put_unwind_info = put_unwind_info,
+ .get_dyn_info_list_addr = get_dyn_info_list_addr,
+ .access_mem = access_mem,
+ .access_reg = access_reg,
+ .access_fpreg = access_fpreg,
+ .resume = resume,
+ .get_proc_name = get_proc_name,
+ };
+ unw_addr_space_t addr_space;
+
+ addr_space = unw_create_addr_space(&accessors, /*byte_order=*/0);
+ unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL);
+ return addr_space;
+#else
+ return NULL;
+#endif
+}
+
+#ifdef HAVE_LIBUNWIND_ARM64_SUPPORT
+extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,
+ unw_word_t ip,
+ unw_dyn_info_t *di,
+ unw_proc_info_t *pi,
+ int need_unwind_info, void *arg);
+#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table)
+#endif
+
+int __libunwind_arch__dwarf_search_unwind_table_arm64(void *as __maybe_unused,
+ uint64_t ip __maybe_unused,
+ struct libarch_unwind__dyn_info *_di __maybe_unused,
+ void *pi __maybe_unused,
+ int need_unwind_info __maybe_unused,
+ void *arg __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_ARM64_SUPPORT
+ unw_dyn_info_t di = {
+ .format = UNW_INFO_FORMAT_REMOTE_TABLE,
+ .start_ip = _di->start_ip,
+ .end_ip = _di->end_ip,
+ .u = {
+ .rti = {
+ .segbase = _di->segbase,
+ .table_data = _di->table_data,
+ .table_len = _di->table_len,
+ },
+ },
+ };
+ int ret = dwarf_search_unwind_table(as, ip, &di, pi, need_unwind_info, arg);
+
+ _di->start_ip = di.start_ip;
+ _di->end_ip = di.end_ip;
+ _di->segbase = di.u.rti.segbase;
+ _di->table_data = di.u.rti.table_data;
+ _di->table_len = di.u.rti.table_len;
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
+
+#if defined(HAVE_LIBUNWIND_ARM64_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_ARM64)
+extern int UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug,
+ unw_word_t ip,
+ unw_word_t segbase,
+ const char *obj_name, unw_word_t start,
+ unw_word_t end);
+#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame)
+#endif
+
+int __libunwind_arch__dwarf_find_debug_frame_arm64(int found __maybe_unused,
+ struct libarch_unwind__dyn_info *_di __maybe_unused,
+ uint64_t ip __maybe_unused,
+ uint64_t segbase __maybe_unused,
+ const char *obj_name __maybe_unused,
+ uint64_t start __maybe_unused,
+ uint64_t end __maybe_unused)
+{
+#if defined(HAVE_LIBUNWIND_ARM64_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_ARM64)
+ unw_dyn_info_t di = {
+ .format = UNW_INFO_FORMAT_REMOTE_TABLE,
+ .start_ip = _di->start_ip,
+ .end_ip = _di->end_ip,
+ .u = {
+ .rti = {
+ .segbase = _di->segbase,
+ .table_data = _di->table_data,
+ .table_len = _di->table_len,
+ },
+ },
+ };
+ int ret = dwarf_find_debug_frame(found, &di, ip, segbase, obj_name, start, end);
+
+ _di->start_ip = di.start_ip;
+ _di->end_ip = di.end_ip;
+ _di->segbase = di.u.rti.segbase;
+ _di->table_data = di.u.rti.table_data;
+ _di->table_len = di.u.rti.table_len;
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
+
+struct unwind_info *__libunwind_arch_unwind_info__new_arm64(struct thread *thread __maybe_unused,
+ struct perf_sample *sample __maybe_unused,
+ int max_stack __maybe_unused,
+ bool best_effort __maybe_unused,
+ uint64_t first_ip __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_ARM64_SUPPORT
+ struct arch_unwind_info {
+ struct unwind_info ui;
+ unw_cursor_t _cursor;
+ uint64_t _ips[];
+ };
+
+ struct maps *maps = thread__maps(thread);
+ void *addr_space = maps__addr_space(maps);
+ struct arch_unwind_info *ui;
+ int ret;
+
+ if (addr_space == NULL)
+ return NULL;
+
+ ui = zalloc(sizeof(*ui) + sizeof(ui->_ips[0]) * max_stack);
+ if (!ui)
+ return NULL;
+
+ ui->ui.machine = maps__machine(maps);
+ ui->ui.thread = thread;
+ ui->ui.sample = sample;
+ ui->ui.cursor = &ui->_cursor;
+ ui->ui.ips = &ui->_ips[0];
+ ui->ui.ips[0] = first_ip;
+ ui->ui.cur_ip = 1;
+ ui->ui.max_ips = max_stack;
+ ui->ui.unw_word_t_size = sizeof(unw_word_t);
+ ui->ui.e_machine = EM_AARCH64;
+ ui->ui.best_effort = best_effort;
+
+ ret = unw_init_remote(&ui->_cursor, addr_space, &ui->ui);
+ if (ret) {
+ if (!best_effort)
+ pr_err("libunwind: %s\n", unw_strerror(ret));
+ free(ui);
+ return NULL;
+ }
+
+ return &ui->ui;
+#else
+ return NULL;
+#endif
+}
+
+int __libunwind_arch__unwind_step_arm64(struct unwind_info *ui __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_ARM64_SUPPORT
+ int ret;
+
+ if (ui->cur_ip >= ui->max_ips)
+ return -1;
+
+ ret = unw_step(ui->cursor);
+ if (ret > 0) {
+ uint64_t ip;
+
+ unw_get_reg(ui->cursor, UNW_REG_IP, &ip);
+
+ if (unw_is_signal_frame(ui->cursor) <= 0) {
+ /*
+ * Decrement the IP for any non-activation frames. This
+ * is required to properly find the srcline for caller
+ * frames. See also the documentation for
+ * dwfl_frame_pc(), which this code tries to replicate.
+ */
+ --ip;
+ }
+ ui->ips[ui->cur_ip++] = ip;
+ }
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
diff --git a/tools/perf/util/libunwind-arch/libunwind-i386.c b/tools/perf/util/libunwind-arch/libunwind-i386.c
index 45ff30c95c1b..fde40872a610 100644
--- a/tools/perf/util/libunwind-arch/libunwind-i386.c
+++ b/tools/perf/util/libunwind-arch/libunwind-i386.c
@@ -2,9 +2,12 @@
#include "libunwind-arch.h"
#include "../debug.h"
#include "../maps.h"
+#include "../thread.h"
#include "../../../arch/x86/include/uapi/asm/perf_regs.h"
#include <linux/compiler.h>
#include <linux/kernel.h>
+#include <linux/zalloc.h>
+#include <elf.h>
#include <errno.h>
#ifdef HAVE_LIBUNWIND_X86_SUPPORT
@@ -56,3 +59,254 @@ void __libunwind_arch__finish_access_i386(struct maps *maps __maybe_unused)
unw_destroy_addr_space(maps__addr_space(maps));
#endif
}
+
+#ifdef HAVE_LIBUNWIND_X86_SUPPORT
+static int find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
+ int need_unwind_info, void *arg)
+{
+ return __libunwind__find_proc_info(as, ip, pi, need_unwind_info, arg, sizeof(unw_word_t));
+}
+
+static void put_unwind_info(unw_addr_space_t __maybe_unused as,
+ unw_proc_info_t *pi __maybe_unused,
+ void *arg __maybe_unused)
+{
+ pr_debug("unwind: put_unwind_info called\n");
+}
+
+static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as,
+ unw_word_t __maybe_unused *dil_addr,
+ void __maybe_unused *arg)
+{
+ return -UNW_ENOINFO;
+}
+
+static int access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *valp,
+ int __write, void *arg)
+{
+ return __libunwind__access_mem(as, addr, valp, __write, arg, sizeof(unw_word_t));
+}
+
+static int access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t *valp,
+ int __write, void *arg)
+{
+ return __libunwind__access_reg(as, regnum, valp, __write, arg, sizeof(unw_word_t));
+}
+
+static int access_fpreg(unw_addr_space_t __maybe_unused as,
+ unw_regnum_t __maybe_unused num,
+ unw_fpreg_t __maybe_unused *val,
+ int __maybe_unused __write,
+ void __maybe_unused *arg)
+{
+ pr_err("unwind: access_fpreg unsupported\n");
+ return -UNW_EINVAL;
+}
+
+static int resume(unw_addr_space_t __maybe_unused as,
+ unw_cursor_t __maybe_unused *cu,
+ void __maybe_unused *arg)
+{
+ pr_err("unwind: resume unsupported\n");
+ return -UNW_EINVAL;
+}
+
+static int get_proc_name(unw_addr_space_t __maybe_unused as,
+ unw_word_t __maybe_unused addr,
+ char __maybe_unused *bufp, size_t __maybe_unused buf_len,
+ unw_word_t __maybe_unused *offp, void __maybe_unused *arg)
+{
+ pr_err("unwind: get_proc_name unsupported\n");
+ return -UNW_EINVAL;
+}
+#endif
+
+void *__libunwind_arch__create_addr_space_i386(void)
+{
+#ifdef HAVE_LIBUNWIND_X86_SUPPORT
+ static unw_accessors_t accessors = {
+ .find_proc_info = find_proc_info,
+ .put_unwind_info = put_unwind_info,
+ .get_dyn_info_list_addr = get_dyn_info_list_addr,
+ .access_mem = access_mem,
+ .access_reg = access_reg,
+ .access_fpreg = access_fpreg,
+ .resume = resume,
+ .get_proc_name = get_proc_name,
+ };
+ unw_addr_space_t addr_space;
+
+ addr_space = unw_create_addr_space(&accessors, /*byte_order=*/0);
+ unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL);
+ return addr_space;
+#else
+ return NULL;
+#endif
+}
+
+#ifdef HAVE_LIBUNWIND_X86_SUPPORT
+extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,
+ unw_word_t ip,
+ unw_dyn_info_t *di,
+ unw_proc_info_t *pi,
+ int need_unwind_info, void *arg);
+#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table)
+#endif
+
+int __libunwind_arch__dwarf_search_unwind_table_i386(void *as __maybe_unused,
+ uint64_t ip __maybe_unused,
+ struct libarch_unwind__dyn_info *_di __maybe_unused,
+ void *pi __maybe_unused,
+ int need_unwind_info __maybe_unused,
+ void *arg __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_X86_SUPPORT
+ unw_dyn_info_t di = {
+ .format = UNW_INFO_FORMAT_REMOTE_TABLE,
+ .start_ip = _di->start_ip,
+ .end_ip = _di->end_ip,
+ .u = {
+ .rti = {
+ .segbase = _di->segbase,
+ .table_data = _di->table_data,
+ .table_len = _di->table_len,
+ },
+ },
+ };
+ int ret = dwarf_search_unwind_table(as, ip, &di, pi, need_unwind_info, arg);
+
+ _di->start_ip = di.start_ip;
+ _di->end_ip = di.end_ip;
+ _di->segbase = di.u.rti.segbase;
+ _di->table_data = di.u.rti.table_data;
+ _di->table_len = di.u.rti.table_len;
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
+
+#if defined(HAVE_LIBUNWIND_X86_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_X86)
+extern int UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug,
+ unw_word_t ip,
+ unw_word_t segbase,
+ const char *obj_name, unw_word_t start,
+ unw_word_t end);
+#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame)
+#endif
+
+int __libunwind_arch__dwarf_find_debug_frame_i386(int found __maybe_unused,
+ struct libarch_unwind__dyn_info *_di __maybe_unused,
+ uint64_t ip __maybe_unused,
+ uint64_t segbase __maybe_unused,
+ const char *obj_name __maybe_unused,
+ uint64_t start __maybe_unused,
+ uint64_t end __maybe_unused)
+{
+#if defined(HAVE_LIBUNWIND_X86_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_X86)
+ unw_dyn_info_t di = {
+ .format = UNW_INFO_FORMAT_REMOTE_TABLE,
+ .start_ip = _di->start_ip,
+ .end_ip = _di->end_ip,
+ .u = {
+ .rti = {
+ .segbase = _di->segbase,
+ .table_data = _di->table_data,
+ .table_len = _di->table_len,
+ },
+ },
+ };
+ int ret = dwarf_find_debug_frame(found, &di, ip, segbase, obj_name, start, end);
+
+ _di->start_ip = di.start_ip;
+ _di->end_ip = di.end_ip;
+ _di->segbase = di.u.rti.segbase;
+ _di->table_data = di.u.rti.table_data;
+ _di->table_len = di.u.rti.table_len;
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
+
+struct unwind_info *__libunwind_arch_unwind_info__new_i386(struct thread *thread __maybe_unused,
+ struct perf_sample *sample __maybe_unused,
+ int max_stack __maybe_unused,
+ bool best_effort __maybe_unused,
+ uint64_t first_ip __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_X86_SUPPORT
+ struct arch_unwind_info {
+ struct unwind_info ui;
+ unw_cursor_t _cursor;
+ uint64_t _ips[];
+ };
+
+ struct maps *maps = thread__maps(thread);
+ void *addr_space = maps__addr_space(maps);
+ struct arch_unwind_info *ui;
+ int ret;
+
+ if (addr_space == NULL)
+ return NULL;
+
+ ui = zalloc(sizeof(*ui) + sizeof(ui->_ips[0]) * max_stack);
+ if (!ui)
+ return NULL;
+
+ ui->ui.machine = maps__machine(maps);
+ ui->ui.thread = thread;
+ ui->ui.sample = sample;
+ ui->ui.cursor = &ui->_cursor;
+ ui->ui.ips = &ui->_ips[0];
+ ui->ui.ips[0] = first_ip;
+ ui->ui.cur_ip = 1;
+ ui->ui.max_ips = max_stack;
+ ui->ui.unw_word_t_size = sizeof(unw_word_t);
+ ui->ui.e_machine = EM_I386;
+ ui->ui.best_effort = best_effort;
+
+ ret = unw_init_remote(&ui->_cursor, addr_space, &ui->ui);
+ if (ret) {
+ if (!best_effort)
+ pr_err("libunwind: %s\n", unw_strerror(ret));
+ free(ui);
+ return NULL;
+ }
+
+ return &ui->ui;
+#else
+ return NULL;
+#endif
+}
+
+int __libunwind_arch__unwind_step_i386(struct unwind_info *ui __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_X86_SUPPORT
+ int ret;
+
+ if (ui->cur_ip >= ui->max_ips)
+ return -1;
+
+ ret = unw_step(ui->cursor);
+ if (ret > 0) {
+ uint64_t ip;
+
+ unw_get_reg(ui->cursor, UNW_REG_IP, &ip);
+
+ if (unw_is_signal_frame(ui->cursor) <= 0) {
+ /*
+ * Decrement the IP for any non-activation frames. This
+ * is required to properly find the srcline for caller
+ * frames. See also the documentation for
+ * dwfl_frame_pc(), which this code tries to replicate.
+ */
+ --ip;
+ }
+ ui->ips[ui->cur_ip++] = ip;
+ }
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
diff --git a/tools/perf/util/libunwind-arch/libunwind-loongarch.c b/tools/perf/util/libunwind-arch/libunwind-loongarch.c
index 837aa11e2b9f..d86397598a9d 100644
--- a/tools/perf/util/libunwind-arch/libunwind-loongarch.c
+++ b/tools/perf/util/libunwind-arch/libunwind-loongarch.c
@@ -2,8 +2,12 @@
#include "libunwind-arch.h"
#include "../debug.h"
#include "../maps.h"
+#include "../thread.h"
#include "../../../arch/loongarch/include/uapi/asm/perf_regs.h"
#include <linux/compiler.h>
+#include <linux/kernel.h>
+#include <linux/zalloc.h>
+#include <elf.h>
#include <errno.h>
#ifdef HAVE_LIBUNWIND_LOONGARCH64_SUPPORT
@@ -40,3 +44,254 @@ void __libunwind_arch__finish_access_loongarch(struct maps *maps __maybe_unused)
unw_destroy_addr_space(maps__addr_space(maps));
#endif
}
+
+#ifdef HAVE_LIBUNWIND_LOONGARCH64_SUPPORT
+static int find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
+ int need_unwind_info, void *arg)
+{
+ return __libunwind__find_proc_info(as, ip, pi, need_unwind_info, arg, sizeof(unw_word_t));
+}
+
+static void put_unwind_info(unw_addr_space_t __maybe_unused as,
+ unw_proc_info_t *pi __maybe_unused,
+ void *arg __maybe_unused)
+{
+ pr_debug("unwind: put_unwind_info called\n");
+}
+
+static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as,
+ unw_word_t __maybe_unused *dil_addr,
+ void __maybe_unused *arg)
+{
+ return -UNW_ENOINFO;
+}
+
+static int access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *valp,
+ int __write, void *arg)
+{
+ return __libunwind__access_mem(as, addr, valp, __write, arg, sizeof(unw_word_t));
+}
+
+static int access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t *valp,
+ int __write, void *arg)
+{
+ return __libunwind__access_reg(as, regnum, valp, __write, arg, sizeof(unw_word_t));
+}
+
+static int access_fpreg(unw_addr_space_t __maybe_unused as,
+ unw_regnum_t __maybe_unused num,
+ unw_fpreg_t __maybe_unused *val,
+ int __maybe_unused __write,
+ void __maybe_unused *arg)
+{
+ pr_err("unwind: access_fpreg unsupported\n");
+ return -UNW_EINVAL;
+}
+
+static int resume(unw_addr_space_t __maybe_unused as,
+ unw_cursor_t __maybe_unused *cu,
+ void __maybe_unused *arg)
+{
+ pr_err("unwind: resume unsupported\n");
+ return -UNW_EINVAL;
+}
+
+static int get_proc_name(unw_addr_space_t __maybe_unused as,
+ unw_word_t __maybe_unused addr,
+ char __maybe_unused *bufp, size_t __maybe_unused buf_len,
+ unw_word_t __maybe_unused *offp, void __maybe_unused *arg)
+{
+ pr_err("unwind: get_proc_name unsupported\n");
+ return -UNW_EINVAL;
+}
+#endif
+
+void *__libunwind_arch__create_addr_space_loongarch(void)
+{
+#ifdef HAVE_LIBUNWIND_LOONGARCH64_SUPPORT
+ static unw_accessors_t accessors = {
+ .find_proc_info = find_proc_info,
+ .put_unwind_info = put_unwind_info,
+ .get_dyn_info_list_addr = get_dyn_info_list_addr,
+ .access_mem = access_mem,
+ .access_reg = access_reg,
+ .access_fpreg = access_fpreg,
+ .resume = resume,
+ .get_proc_name = get_proc_name,
+ };
+ unw_addr_space_t addr_space;
+
+ addr_space = unw_create_addr_space(&accessors, /*byte_order=*/0);
+ unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL);
+ return addr_space;
+#else
+ return NULL;
+#endif
+}
+
+#ifdef HAVE_LIBUNWIND_LOONGARCH_SUPPORT
+extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,
+ unw_word_t ip,
+ unw_dyn_info_t *di,
+ unw_proc_info_t *pi,
+ int need_unwind_info, void *arg);
+#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table)
+#endif
+
+int __libunwind_arch__dwarf_search_unwind_table_loongarch(void *as __maybe_unused,
+ uint64_t ip __maybe_unused,
+ struct libarch_unwind__dyn_info *_di __maybe_unused,
+ void *pi __maybe_unused,
+ int need_unwind_info __maybe_unused,
+ void *arg __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_LOONGARCH_SUPPORT
+ unw_dyn_info_t di = {
+ .format = UNW_INFO_FORMAT_REMOTE_TABLE,
+ .start_ip = _di->start_ip,
+ .end_ip = _di->end_ip,
+ .u = {
+ .rti = {
+ .segbase = _di->segbase,
+ .table_data = _di->table_data,
+ .table_len = _di->table_len,
+ },
+ },
+ };
+ int ret = dwarf_search_unwind_table(as, ip, &di, pi, need_unwind_info, arg);
+
+ _di->start_ip = di.start_ip;
+ _di->end_ip = di.end_ip;
+ _di->segbase = di.u.rti.segbase;
+ _di->table_data = di.u.rti.table_data;
+ _di->table_len = di.u.rti.table_len;
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
+
+#if defined(HAVE_LIBUNWIND_LOONGARCH_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_LOONGARCH)
+extern int UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug,
+ unw_word_t ip,
+ unw_word_t segbase,
+ const char *obj_name, unw_word_t start,
+ unw_word_t end);
+#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame)
+#endif
+
+int __libunwind_arch__dwarf_find_debug_frame_loongarch(int found __maybe_unused,
+ struct libarch_unwind__dyn_info *_di __maybe_unused,
+ uint64_t ip __maybe_unused,
+ uint64_t segbase __maybe_unused,
+ const char *obj_name __maybe_unused,
+ uint64_t start __maybe_unused,
+ uint64_t end __maybe_unused)
+{
+#if defined(HAVE_LIBUNWIND_LOONGARCH_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_LOONGARCH)
+ unw_dyn_info_t di = {
+ .format = UNW_INFO_FORMAT_REMOTE_TABLE,
+ .start_ip = _di->start_ip,
+ .end_ip = _di->end_ip,
+ .u = {
+ .rti = {
+ .segbase = _di->segbase,
+ .table_data = _di->table_data,
+ .table_len = _di->table_len,
+ },
+ },
+ };
+ int ret = dwarf_find_debug_frame(found, &di, ip, segbase, obj_name, start, end);
+
+ _di->start_ip = di.start_ip;
+ _di->end_ip = di.end_ip;
+ _di->segbase = di.u.rti.segbase;
+ _di->table_data = di.u.rti.table_data;
+ _di->table_len = di.u.rti.table_len;
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
+
+struct unwind_info *__libunwind_arch_unwind_info__new_loongarch(struct thread *thread __maybe_unused,
+ struct perf_sample *sample __maybe_unused,
+ int max_stack __maybe_unused,
+ bool best_effort __maybe_unused,
+ uint64_t first_ip __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_LOONGARCH_SUPPORT
+ struct arch_unwind_info {
+ struct unwind_info ui;
+ unw_cursor_t _cursor;
+ uint64_t _ips[];
+ };
+
+ struct maps *maps = thread__maps(thread);
+ void *addr_space = maps__addr_space(maps);
+ struct arch_unwind_info *ui;
+ int ret;
+
+ if (addr_space == NULL)
+ return NULL;
+
+ ui = zalloc(sizeof(*ui) + sizeof(ui->_ips[0]) * max_stack);
+ if (!ui)
+ return NULL;
+
+ ui->ui.machine = maps__machine(maps);
+ ui->ui.thread = thread;
+ ui->ui.sample = sample;
+ ui->ui.cursor = &ui->_cursor;
+ ui->ui.ips = &ui->_ips[0];
+ ui->ui.ips[0] = first_ip;
+ ui->ui.cur_ip = 1;
+ ui->ui.max_ips = max_stack;
+ ui->ui.unw_word_t_size = sizeof(unw_word_t);
+ ui->ui.e_machine = EM_LOONGARCH;
+ ui->ui.best_effort = best_effort;
+
+ ret = unw_init_remote(&ui->_cursor, addr_space, &ui->ui);
+ if (ret) {
+ if (!best_effort)
+ pr_err("libunwind: %s\n", unw_strerror(ret));
+ free(ui);
+ return NULL;
+ }
+
+ return &ui->ui;
+#else
+ return NULL;
+#endif
+}
+
+int __libunwind_arch__unwind_step_loongarch(struct unwind_info *ui __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_LOONGARCH_SUPPORT
+ int ret;
+
+ if (ui->cur_ip >= ui->max_ips)
+ return -1;
+
+ ret = unw_step(ui->cursor);
+ if (ret > 0) {
+ uint64_t ip;
+
+ unw_get_reg(ui->cursor, UNW_REG_IP, &ip);
+
+ if (unw_is_signal_frame(ui->cursor) <= 0) {
+ /*
+ * Decrement the IP for any non-activation frames. This
+ * is required to properly find the srcline for caller
+ * frames. See also the documentation for
+ * dwfl_frame_pc(), which this code tries to replicate.
+ */
+ --ip;
+ }
+ ui->ips[ui->cur_ip++] = ip;
+ }
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
diff --git a/tools/perf/util/libunwind-arch/libunwind-mips.c b/tools/perf/util/libunwind-arch/libunwind-mips.c
index 1fa81742ff4a..91ee3c27d159 100644
--- a/tools/perf/util/libunwind-arch/libunwind-mips.c
+++ b/tools/perf/util/libunwind-arch/libunwind-mips.c
@@ -2,8 +2,12 @@
#include "libunwind-arch.h"
#include "../debug.h"
#include "../maps.h"
+#include "../thread.h"
#include "../../../arch/mips/include/uapi/asm/perf_regs.h"
#include <linux/compiler.h>
+#include <linux/kernel.h>
+#include <linux/zalloc.h>
+#include <elf.h>
#include <errno.h>
#ifdef HAVE_LIBUNWIND_MIPS_SUPPORT
@@ -42,3 +46,254 @@ void __libunwind_arch__finish_access_mips(struct maps *maps __maybe_unused)
unw_destroy_addr_space(maps__addr_space(maps));
#endif
}
+
+#ifdef HAVE_LIBUNWIND_MIPS_SUPPORT
+static int find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
+ int need_unwind_info, void *arg)
+{
+ return __libunwind__find_proc_info(as, ip, pi, need_unwind_info, arg, sizeof(unw_word_t));
+}
+
+static void put_unwind_info(unw_addr_space_t __maybe_unused as,
+ unw_proc_info_t *pi __maybe_unused,
+ void *arg __maybe_unused)
+{
+ pr_debug("unwind: put_unwind_info called\n");
+}
+
+static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as,
+ unw_word_t __maybe_unused *dil_addr,
+ void __maybe_unused *arg)
+{
+ return -UNW_ENOINFO;
+}
+
+static int access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *valp,
+ int __write, void *arg)
+{
+ return __libunwind__access_mem(as, addr, valp, __write, arg, sizeof(unw_word_t));
+}
+
+static int access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t *valp,
+ int __write, void *arg)
+{
+ return __libunwind__access_reg(as, regnum, valp, __write, arg, sizeof(unw_word_t));
+}
+
+static int access_fpreg(unw_addr_space_t __maybe_unused as,
+ unw_regnum_t __maybe_unused num,
+ unw_fpreg_t __maybe_unused *val,
+ int __maybe_unused __write,
+ void __maybe_unused *arg)
+{
+ pr_err("unwind: access_fpreg unsupported\n");
+ return -UNW_EINVAL;
+}
+
+static int resume(unw_addr_space_t __maybe_unused as,
+ unw_cursor_t __maybe_unused *cu,
+ void __maybe_unused *arg)
+{
+ pr_err("unwind: resume unsupported\n");
+ return -UNW_EINVAL;
+}
+
+static int get_proc_name(unw_addr_space_t __maybe_unused as,
+ unw_word_t __maybe_unused addr,
+ char __maybe_unused *bufp, size_t __maybe_unused buf_len,
+ unw_word_t __maybe_unused *offp, void __maybe_unused *arg)
+{
+ pr_err("unwind: get_proc_name unsupported\n");
+ return -UNW_EINVAL;
+}
+#endif
+
+void *__libunwind_arch__create_addr_space_mips(void)
+{
+#ifdef HAVE_LIBUNWIND_MIPS_SUPPORT
+ static unw_accessors_t accessors = {
+ .find_proc_info = find_proc_info,
+ .put_unwind_info = put_unwind_info,
+ .get_dyn_info_list_addr = get_dyn_info_list_addr,
+ .access_mem = access_mem,
+ .access_reg = access_reg,
+ .access_fpreg = access_fpreg,
+ .resume = resume,
+ .get_proc_name = get_proc_name,
+ };
+ unw_addr_space_t addr_space;
+
+ addr_space = unw_create_addr_space(&accessors, /*byte_order=*/0);
+ unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL);
+ return addr_space;
+#else
+ return NULL;
+#endif
+}
+
+#ifdef HAVE_LIBUNWIND_MIPS_SUPPORT
+extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,
+ unw_word_t ip,
+ unw_dyn_info_t *di,
+ unw_proc_info_t *pi,
+ int need_unwind_info, void *arg);
+#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table)
+#endif
+
+int __libunwind_arch__dwarf_search_unwind_table_mips(void *as __maybe_unused,
+ uint64_t ip __maybe_unused,
+ struct libarch_unwind__dyn_info *_di __maybe_unused,
+ void *pi __maybe_unused,
+ int need_unwind_info __maybe_unused,
+ void *arg __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_MIPS_SUPPORT
+ unw_dyn_info_t di = {
+ .format = UNW_INFO_FORMAT_REMOTE_TABLE,
+ .start_ip = _di->start_ip,
+ .end_ip = _di->end_ip,
+ .u = {
+ .rti = {
+ .segbase = _di->segbase,
+ .table_data = _di->table_data,
+ .table_len = _di->table_len,
+ },
+ },
+ };
+ int ret = dwarf_search_unwind_table(as, ip, &di, pi, need_unwind_info, arg);
+
+ _di->start_ip = di.start_ip;
+ _di->end_ip = di.end_ip;
+ _di->segbase = di.u.rti.segbase;
+ _di->table_data = di.u.rti.table_data;
+ _di->table_len = di.u.rti.table_len;
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
+
+#if defined(HAVE_LIBUNWIND_MIPS_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_MIPS)
+extern int UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug,
+ unw_word_t ip,
+ unw_word_t segbase,
+ const char *obj_name, unw_word_t start,
+ unw_word_t end);
+#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame)
+#endif
+
+int __libunwind_arch__dwarf_find_debug_frame_mips(int found __maybe_unused,
+ struct libarch_unwind__dyn_info *_di __maybe_unused,
+ uint64_t ip __maybe_unused,
+ uint64_t segbase __maybe_unused,
+ const char *obj_name __maybe_unused,
+ uint64_t start __maybe_unused,
+ uint64_t end __maybe_unused)
+{
+#if defined(HAVE_LIBUNWIND_MIPS_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_MIPS)
+ unw_dyn_info_t di = {
+ .format = UNW_INFO_FORMAT_REMOTE_TABLE,
+ .start_ip = _di->start_ip,
+ .end_ip = _di->end_ip,
+ .u = {
+ .rti = {
+ .segbase = _di->segbase,
+ .table_data = _di->table_data,
+ .table_len = _di->table_len,
+ },
+ },
+ };
+ int ret = dwarf_find_debug_frame(found, &di, ip, segbase, obj_name, start, end);
+
+ _di->start_ip = di.start_ip;
+ _di->end_ip = di.end_ip;
+ _di->segbase = di.u.rti.segbase;
+ _di->table_data = di.u.rti.table_data;
+ _di->table_len = di.u.rti.table_len;
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
+
+struct unwind_info *__libunwind_arch_unwind_info__new_mips(struct thread *thread __maybe_unused,
+ struct perf_sample *sample __maybe_unused,
+ int max_stack __maybe_unused,
+ bool best_effort __maybe_unused,
+ uint64_t first_ip __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_MIPS_SUPPORT
+ struct arch_unwind_info {
+ struct unwind_info ui;
+ unw_cursor_t _cursor;
+ uint64_t _ips[];
+ };
+
+ struct maps *maps = thread__maps(thread);
+ void *addr_space = maps__addr_space(maps);
+ struct arch_unwind_info *ui;
+ int ret;
+
+ if (addr_space == NULL)
+ return NULL;
+
+ ui = zalloc(sizeof(*ui) + sizeof(ui->_ips[0]) * max_stack);
+ if (!ui)
+ return NULL;
+
+ ui->ui.machine = maps__machine(maps);
+ ui->ui.thread = thread;
+ ui->ui.sample = sample;
+ ui->ui.cursor = &ui->_cursor;
+ ui->ui.ips = &ui->_ips[0];
+ ui->ui.ips[0] = first_ip;
+ ui->ui.cur_ip = 1;
+ ui->ui.max_ips = max_stack;
+ ui->ui.unw_word_t_size = sizeof(unw_word_t);
+ ui->ui.e_machine = EM_MIPS;
+ ui->ui.best_effort = best_effort;
+
+ ret = unw_init_remote(&ui->_cursor, addr_space, &ui->ui);
+ if (ret) {
+ if (!best_effort)
+ pr_err("libunwind: %s\n", unw_strerror(ret));
+ free(ui);
+ return NULL;
+ }
+
+ return &ui->ui;
+#else
+ return NULL;
+#endif
+}
+
+int __libunwind_arch__unwind_step_mips(struct unwind_info *ui __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_MIPS_SUPPORT
+ int ret;
+
+ if (ui->cur_ip >= ui->max_ips)
+ return -1;
+
+ ret = unw_step(ui->cursor);
+ if (ret > 0) {
+ uint64_t ip;
+
+ unw_get_reg(ui->cursor, UNW_REG_IP, &ip);
+
+ if (unw_is_signal_frame(ui->cursor) <= 0) {
+ /*
+ * Decrement the IP for any non-activation frames. This
+ * is required to properly find the srcline for caller
+ * frames. See also the documentation for
+ * dwfl_frame_pc(), which this code tries to replicate.
+ */
+ --ip;
+ }
+ ui->ips[ui->cur_ip++] = ip;
+ }
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
diff --git a/tools/perf/util/libunwind-arch/libunwind-ppc32.c b/tools/perf/util/libunwind-arch/libunwind-ppc32.c
index fa8471c74bf3..b80d3184a28d 100644
--- a/tools/perf/util/libunwind-arch/libunwind-ppc32.c
+++ b/tools/perf/util/libunwind-arch/libunwind-ppc32.c
@@ -2,8 +2,12 @@
#include "libunwind-arch.h"
#include "../debug.h"
#include "../maps.h"
+#include "../thread.h"
#include "../../../arch/powerpc/include/uapi/asm/perf_regs.h"
#include <linux/compiler.h>
+#include <linux/kernel.h>
+#include <linux/zalloc.h>
+#include <elf.h>
#include <errno.h>
#ifdef HAVE_LIBUNWIND_PPC32_SUPPORT
@@ -44,3 +48,254 @@ void __libunwind_arch__finish_access_ppc32(struct maps *maps __maybe_unused)
unw_destroy_addr_space(maps__addr_space(maps));
#endif
}
+
+#ifdef HAVE_LIBUNWIND_PPC32_SUPPORT
+static int find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
+ int need_unwind_info, void *arg)
+{
+ return __libunwind__find_proc_info(as, ip, pi, need_unwind_info, arg, sizeof(unw_word_t));
+}
+
+static void put_unwind_info(unw_addr_space_t __maybe_unused as,
+ unw_proc_info_t *pi __maybe_unused,
+ void *arg __maybe_unused)
+{
+ pr_debug("unwind: put_unwind_info called\n");
+}
+
+static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as,
+ unw_word_t __maybe_unused *dil_addr,
+ void __maybe_unused *arg)
+{
+ return -UNW_ENOINFO;
+}
+
+static int access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *valp,
+ int __write, void *arg)
+{
+ return __libunwind__access_mem(as, addr, valp, __write, arg, sizeof(unw_word_t));
+}
+
+static int access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t *valp,
+ int __write, void *arg)
+{
+ return __libunwind__access_reg(as, regnum, valp, __write, arg, sizeof(unw_word_t));
+}
+
+static int access_fpreg(unw_addr_space_t __maybe_unused as,
+ unw_regnum_t __maybe_unused num,
+ unw_fpreg_t __maybe_unused *val,
+ int __maybe_unused __write,
+ void __maybe_unused *arg)
+{
+ pr_err("unwind: access_fpreg unsupported\n");
+ return -UNW_EINVAL;
+}
+
+static int resume(unw_addr_space_t __maybe_unused as,
+ unw_cursor_t __maybe_unused *cu,
+ void __maybe_unused *arg)
+{
+ pr_err("unwind: resume unsupported\n");
+ return -UNW_EINVAL;
+}
+
+static int get_proc_name(unw_addr_space_t __maybe_unused as,
+ unw_word_t __maybe_unused addr,
+ char __maybe_unused *bufp, size_t __maybe_unused buf_len,
+ unw_word_t __maybe_unused *offp, void __maybe_unused *arg)
+{
+ pr_err("unwind: get_proc_name unsupported\n");
+ return -UNW_EINVAL;
+}
+#endif
+
+void *__libunwind_arch__create_addr_space_ppc32(void)
+{
+#ifdef HAVE_LIBUNWIND_PPC32_SUPPORT
+ static unw_accessors_t accessors = {
+ .find_proc_info = find_proc_info,
+ .put_unwind_info = put_unwind_info,
+ .get_dyn_info_list_addr = get_dyn_info_list_addr,
+ .access_mem = access_mem,
+ .access_reg = access_reg,
+ .access_fpreg = access_fpreg,
+ .resume = resume,
+ .get_proc_name = get_proc_name,
+ };
+ unw_addr_space_t addr_space;
+
+ addr_space = unw_create_addr_space(&accessors, /*byte_order=*/0);
+ unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL);
+ return addr_space;
+#else
+ return NULL;
+#endif
+}
+
+#ifdef HAVE_LIBUNWIND_PPC32_SUPPORT
+extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,
+ unw_word_t ip,
+ unw_dyn_info_t *di,
+ unw_proc_info_t *pi,
+ int need_unwind_info, void *arg);
+#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table)
+#endif
+
+int __libunwind_arch__dwarf_search_unwind_table_ppc32(void *as __maybe_unused,
+ uint64_t ip __maybe_unused,
+ struct libarch_unwind__dyn_info *_di __maybe_unused,
+ void *pi __maybe_unused,
+ int need_unwind_info __maybe_unused,
+ void *arg __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_PPC32_SUPPORT
+ unw_dyn_info_t di = {
+ .format = UNW_INFO_FORMAT_REMOTE_TABLE,
+ .start_ip = _di->start_ip,
+ .end_ip = _di->end_ip,
+ .u = {
+ .rti = {
+ .segbase = _di->segbase,
+ .table_data = _di->table_data,
+ .table_len = _di->table_len,
+ },
+ },
+ };
+ int ret = dwarf_search_unwind_table(as, ip, &di, pi, need_unwind_info, arg);
+
+ _di->start_ip = di.start_ip;
+ _di->end_ip = di.end_ip;
+ _di->segbase = di.u.rti.segbase;
+ _di->table_data = di.u.rti.table_data;
+ _di->table_len = di.u.rti.table_len;
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
+
+#if defined(HAVE_LIBUNWIND_PPC32_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_PPC32)
+extern int UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug,
+ unw_word_t ip,
+ unw_word_t segbase,
+ const char *obj_name, unw_word_t start,
+ unw_word_t end);
+#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame)
+#endif
+
+int __libunwind_arch__dwarf_find_debug_frame_ppc32(int found __maybe_unused,
+ struct libarch_unwind__dyn_info *_di __maybe_unused,
+ uint64_t ip __maybe_unused,
+ uint64_t segbase __maybe_unused,
+ const char *obj_name __maybe_unused,
+ uint64_t start __maybe_unused,
+ uint64_t end __maybe_unused)
+{
+#if defined(HAVE_LIBUNWIND_PPC32_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_PPC32)
+ unw_dyn_info_t di = {
+ .format = UNW_INFO_FORMAT_REMOTE_TABLE,
+ .start_ip = _di->start_ip,
+ .end_ip = _di->end_ip,
+ .u = {
+ .rti = {
+ .segbase = _di->segbase,
+ .table_data = _di->table_data,
+ .table_len = _di->table_len,
+ },
+ },
+ };
+ int ret = dwarf_find_debug_frame(found, &di, ip, segbase, obj_name, start, end);
+
+ _di->start_ip = di.start_ip;
+ _di->end_ip = di.end_ip;
+ _di->segbase = di.u.rti.segbase;
+ _di->table_data = di.u.rti.table_data;
+ _di->table_len = di.u.rti.table_len;
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
+
+struct unwind_info *__libunwind_arch_unwind_info__new_ppc32(struct thread *thread __maybe_unused,
+ struct perf_sample *sample __maybe_unused,
+ int max_stack __maybe_unused,
+ bool best_effort __maybe_unused,
+ uint64_t first_ip __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_PPC32_SUPPORT
+ struct arch_unwind_info {
+ struct unwind_info ui;
+ unw_cursor_t _cursor;
+ uint64_t _ips[];
+ };
+
+ struct maps *maps = thread__maps(thread);
+ void *addr_space = maps__addr_space(maps);
+ struct arch_unwind_info *ui;
+ int ret;
+
+ if (addr_space == NULL)
+ return NULL;
+
+ ui = zalloc(sizeof(*ui) + sizeof(ui->_ips[0]) * max_stack);
+ if (!ui)
+ return NULL;
+
+ ui->ui.machine = maps__machine(maps);
+ ui->ui.thread = thread;
+ ui->ui.sample = sample;
+ ui->ui.cursor = &ui->_cursor;
+ ui->ui.ips = &ui->_ips[0];
+ ui->ui.ips[0] = first_ip;
+ ui->ui.cur_ip = 1;
+ ui->ui.max_ips = max_stack;
+ ui->ui.unw_word_t_size = sizeof(unw_word_t);
+ ui->ui.e_machine = EM_PPC;
+ ui->ui.best_effort = best_effort;
+
+ ret = unw_init_remote(&ui->_cursor, addr_space, &ui->ui);
+ if (ret) {
+ if (!best_effort)
+ pr_err("libunwind: %s\n", unw_strerror(ret));
+ free(ui);
+ return NULL;
+ }
+
+ return &ui->ui;
+#else
+ return NULL;
+#endif
+}
+
+int __libunwind_arch__unwind_step_ppc32(struct unwind_info *ui __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_PPC32_SUPPORT
+ int ret;
+
+ if (ui->cur_ip >= ui->max_ips)
+ return -1;
+
+ ret = unw_step(ui->cursor);
+ if (ret > 0) {
+ uint64_t ip;
+
+ unw_get_reg(ui->cursor, UNW_REG_IP, &ip);
+
+ if (unw_is_signal_frame(ui->cursor) <= 0) {
+ /*
+ * Decrement the IP for any non-activation frames. This
+ * is required to properly find the srcline for caller
+ * frames. See also the documentation for
+ * dwfl_frame_pc(), which this code tries to replicate.
+ */
+ --ip;
+ }
+ ui->ips[ui->cur_ip++] = ip;
+ }
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
diff --git a/tools/perf/util/libunwind-arch/libunwind-ppc64.c b/tools/perf/util/libunwind-arch/libunwind-ppc64.c
index 2f746e347336..cbe3b476203f 100644
--- a/tools/perf/util/libunwind-arch/libunwind-ppc64.c
+++ b/tools/perf/util/libunwind-arch/libunwind-ppc64.c
@@ -2,8 +2,12 @@
#include "libunwind-arch.h"
#include "../debug.h"
#include "../maps.h"
+#include "../thread.h"
#include "../../../arch/powerpc/include/uapi/asm/perf_regs.h"
#include <linux/compiler.h>
+#include <linux/kernel.h>
+#include <linux/zalloc.h>
+#include <elf.h>
#include <errno.h>
#ifdef HAVE_LIBUNWIND_PPC64_SUPPORT
@@ -46,3 +50,254 @@ void __libunwind_arch__finish_access_ppc64(struct maps *maps __maybe_unused)
unw_destroy_addr_space(maps__addr_space(maps));
#endif
}
+
+#ifdef HAVE_LIBUNWIND_PPC64_SUPPORT
+static int find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
+ int need_unwind_info, void *arg)
+{
+ return __libunwind__find_proc_info(as, ip, pi, need_unwind_info, arg, sizeof(unw_word_t));
+}
+
+static void put_unwind_info(unw_addr_space_t __maybe_unused as,
+ unw_proc_info_t *pi __maybe_unused,
+ void *arg __maybe_unused)
+{
+ pr_debug("unwind: put_unwind_info called\n");
+}
+
+static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as,
+ unw_word_t __maybe_unused *dil_addr,
+ void __maybe_unused *arg)
+{
+ return -UNW_ENOINFO;
+}
+
+static int access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *valp,
+ int __write, void *arg)
+{
+ return __libunwind__access_mem(as, addr, valp, __write, arg, sizeof(unw_word_t));
+}
+
+static int access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t *valp,
+ int __write, void *arg)
+{
+ return __libunwind__access_reg(as, regnum, valp, __write, arg, sizeof(unw_word_t));
+}
+
+static int access_fpreg(unw_addr_space_t __maybe_unused as,
+ unw_regnum_t __maybe_unused num,
+ unw_fpreg_t __maybe_unused *val,
+ int __maybe_unused __write,
+ void __maybe_unused *arg)
+{
+ pr_err("unwind: access_fpreg unsupported\n");
+ return -UNW_EINVAL;
+}
+
+static int resume(unw_addr_space_t __maybe_unused as,
+ unw_cursor_t __maybe_unused *cu,
+ void __maybe_unused *arg)
+{
+ pr_err("unwind: resume unsupported\n");
+ return -UNW_EINVAL;
+}
+
+static int get_proc_name(unw_addr_space_t __maybe_unused as,
+ unw_word_t __maybe_unused addr,
+ char __maybe_unused *bufp, size_t __maybe_unused buf_len,
+ unw_word_t __maybe_unused *offp, void __maybe_unused *arg)
+{
+ pr_err("unwind: get_proc_name unsupported\n");
+ return -UNW_EINVAL;
+}
+#endif
+
+void *__libunwind_arch__create_addr_space_ppc64(void)
+{
+#ifdef HAVE_LIBUNWIND_PPC64_SUPPORT
+ static unw_accessors_t accessors = {
+ .find_proc_info = find_proc_info,
+ .put_unwind_info = put_unwind_info,
+ .get_dyn_info_list_addr = get_dyn_info_list_addr,
+ .access_mem = access_mem,
+ .access_reg = access_reg,
+ .access_fpreg = access_fpreg,
+ .resume = resume,
+ .get_proc_name = get_proc_name,
+ };
+ unw_addr_space_t addr_space;
+
+ addr_space = unw_create_addr_space(&accessors, /*byte_order=*/0);
+ unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL);
+ return addr_space;
+#else
+ return NULL;
+#endif
+}
+
+#ifdef HAVE_LIBUNWIND_PPC64_SUPPORT
+extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,
+ unw_word_t ip,
+ unw_dyn_info_t *di,
+ unw_proc_info_t *pi,
+ int need_unwind_info, void *arg);
+#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table)
+#endif
+
+int __libunwind_arch__dwarf_search_unwind_table_ppc64(void *as __maybe_unused,
+ uint64_t ip __maybe_unused,
+ struct libarch_unwind__dyn_info *_di __maybe_unused,
+ void *pi __maybe_unused,
+ int need_unwind_info __maybe_unused,
+ void *arg __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_PPC64_SUPPORT
+ unw_dyn_info_t di = {
+ .format = UNW_INFO_FORMAT_REMOTE_TABLE,
+ .start_ip = _di->start_ip,
+ .end_ip = _di->end_ip,
+ .u = {
+ .rti = {
+ .segbase = _di->segbase,
+ .table_data = _di->table_data,
+ .table_len = _di->table_len,
+ },
+ },
+ };
+ int ret = dwarf_search_unwind_table(as, ip, &di, pi, need_unwind_info, arg);
+
+ _di->start_ip = di.start_ip;
+ _di->end_ip = di.end_ip;
+ _di->segbase = di.u.rti.segbase;
+ _di->table_data = di.u.rti.table_data;
+ _di->table_len = di.u.rti.table_len;
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
+
+#if defined(HAVE_LIBUNWIND_PPC64_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_PPC64)
+extern int UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug,
+ unw_word_t ip,
+ unw_word_t segbase,
+ const char *obj_name, unw_word_t start,
+ unw_word_t end);
+#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame)
+#endif
+
+int __libunwind_arch__dwarf_find_debug_frame_ppc64(int found __maybe_unused,
+ struct libarch_unwind__dyn_info *_di __maybe_unused,
+ uint64_t ip __maybe_unused,
+ uint64_t segbase __maybe_unused,
+ const char *obj_name __maybe_unused,
+ uint64_t start __maybe_unused,
+ uint64_t end __maybe_unused)
+{
+#if defined(HAVE_LIBUNWIND_PPC64_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_PPC64)
+ unw_dyn_info_t di = {
+ .format = UNW_INFO_FORMAT_REMOTE_TABLE,
+ .start_ip = _di->start_ip,
+ .end_ip = _di->end_ip,
+ .u = {
+ .rti = {
+ .segbase = _di->segbase,
+ .table_data = _di->table_data,
+ .table_len = _di->table_len,
+ },
+ },
+ };
+ int ret = dwarf_find_debug_frame(found, &di, ip, segbase, obj_name, start, end);
+
+ _di->start_ip = di.start_ip;
+ _di->end_ip = di.end_ip;
+ _di->segbase = di.u.rti.segbase;
+ _di->table_data = di.u.rti.table_data;
+ _di->table_len = di.u.rti.table_len;
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
+
+struct unwind_info *__libunwind_arch_unwind_info__new_ppc64(struct thread *thread __maybe_unused,
+ struct perf_sample *sample __maybe_unused,
+ int max_stack __maybe_unused,
+ bool best_effort __maybe_unused,
+ uint64_t first_ip __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_PPC64_SUPPORT
+ struct arch_unwind_info {
+ struct unwind_info ui;
+ unw_cursor_t _cursor;
+ uint64_t _ips[];
+ };
+
+ struct maps *maps = thread__maps(thread);
+ void *addr_space = maps__addr_space(maps);
+ struct arch_unwind_info *ui;
+ int ret;
+
+ if (addr_space == NULL)
+ return NULL;
+
+ ui = zalloc(sizeof(*ui) + sizeof(ui->_ips[0]) * max_stack);
+ if (!ui)
+ return NULL;
+
+ ui->ui.machine = maps__machine(maps);
+ ui->ui.thread = thread;
+ ui->ui.sample = sample;
+ ui->ui.cursor = &ui->_cursor;
+ ui->ui.ips = &ui->_ips[0];
+ ui->ui.ips[0] = first_ip;
+ ui->ui.cur_ip = 1;
+ ui->ui.max_ips = max_stack;
+ ui->ui.unw_word_t_size = sizeof(unw_word_t);
+ ui->ui.e_machine = EM_PPC64;
+ ui->ui.best_effort = best_effort;
+
+ ret = unw_init_remote(&ui->_cursor, addr_space, &ui->ui);
+ if (ret) {
+ if (!best_effort)
+ pr_err("libunwind: %s\n", unw_strerror(ret));
+ free(ui);
+ return NULL;
+ }
+
+ return &ui->ui;
+#else
+ return NULL;
+#endif
+}
+
+int __libunwind_arch__unwind_step_ppc64(struct unwind_info *ui __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_PPC64_SUPPORT
+ int ret;
+
+ if (ui->cur_ip >= ui->max_ips)
+ return -1;
+
+ ret = unw_step(ui->cursor);
+ if (ret > 0) {
+ uint64_t ip;
+
+ unw_get_reg(ui->cursor, UNW_REG_IP, &ip);
+
+ if (unw_is_signal_frame(ui->cursor) <= 0) {
+ /*
+ * Decrement the IP for any non-activation frames. This
+ * is required to properly find the srcline for caller
+ * frames. See also the documentation for
+ * dwfl_frame_pc(), which this code tries to replicate.
+ */
+ --ip;
+ }
+ ui->ips[ui->cur_ip++] = ip;
+ }
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
diff --git a/tools/perf/util/libunwind-arch/libunwind-s390.c b/tools/perf/util/libunwind-arch/libunwind-s390.c
index 9f68d15438b2..ce3788e86d1f 100644
--- a/tools/perf/util/libunwind-arch/libunwind-s390.c
+++ b/tools/perf/util/libunwind-arch/libunwind-s390.c
@@ -2,8 +2,12 @@
#include "libunwind-arch.h"
#include "../debug.h"
#include "../maps.h"
+#include "../thread.h"
#include "../../../arch/s390/include/uapi/asm/perf_regs.h"
#include <linux/compiler.h>
+#include <linux/kernel.h>
+#include <linux/zalloc.h>
+#include <elf.h>
#include <errno.h>
#ifdef HAVE_LIBUNWIND_S390X_SUPPORT
@@ -42,3 +46,254 @@ void __libunwind_arch__finish_access_s390(struct maps *maps __maybe_unused)
unw_destroy_addr_space(maps__addr_space(maps));
#endif
}
+
+#ifdef HAVE_LIBUNWIND_S390X_SUPPORT
+static int find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
+ int need_unwind_info, void *arg)
+{
+ return __libunwind__find_proc_info(as, ip, pi, need_unwind_info, arg, sizeof(unw_word_t));
+}
+
+static void put_unwind_info(unw_addr_space_t __maybe_unused as,
+ unw_proc_info_t *pi __maybe_unused,
+ void *arg __maybe_unused)
+{
+ pr_debug("unwind: put_unwind_info called\n");
+}
+
+static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as,
+ unw_word_t __maybe_unused *dil_addr,
+ void __maybe_unused *arg)
+{
+ return -UNW_ENOINFO;
+}
+
+static int access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *valp,
+ int __write, void *arg)
+{
+ return __libunwind__access_mem(as, addr, valp, __write, arg, sizeof(unw_word_t));
+}
+
+static int access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t *valp,
+ int __write, void *arg)
+{
+ return __libunwind__access_reg(as, regnum, valp, __write, arg, sizeof(unw_word_t));
+}
+
+static int access_fpreg(unw_addr_space_t __maybe_unused as,
+ unw_regnum_t __maybe_unused num,
+ unw_fpreg_t __maybe_unused *val,
+ int __maybe_unused __write,
+ void __maybe_unused *arg)
+{
+ pr_err("unwind: access_fpreg unsupported\n");
+ return -UNW_EINVAL;
+}
+
+static int resume(unw_addr_space_t __maybe_unused as,
+ unw_cursor_t __maybe_unused *cu,
+ void __maybe_unused *arg)
+{
+ pr_err("unwind: resume unsupported\n");
+ return -UNW_EINVAL;
+}
+
+static int get_proc_name(unw_addr_space_t __maybe_unused as,
+ unw_word_t __maybe_unused addr,
+ char __maybe_unused *bufp, size_t __maybe_unused buf_len,
+ unw_word_t __maybe_unused *offp, void __maybe_unused *arg)
+{
+ pr_err("unwind: get_proc_name unsupported\n");
+ return -UNW_EINVAL;
+}
+#endif
+
+void *__libunwind_arch__create_addr_space_s390(void)
+{
+#ifdef HAVE_LIBUNWIND_S390X_SUPPORT
+ static unw_accessors_t accessors = {
+ .find_proc_info = find_proc_info,
+ .put_unwind_info = put_unwind_info,
+ .get_dyn_info_list_addr = get_dyn_info_list_addr,
+ .access_mem = access_mem,
+ .access_reg = access_reg,
+ .access_fpreg = access_fpreg,
+ .resume = resume,
+ .get_proc_name = get_proc_name,
+ };
+ unw_addr_space_t addr_space;
+
+ addr_space = unw_create_addr_space(&accessors, /*byte_order=*/0);
+ unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL);
+ return addr_space;
+#else
+ return NULL;
+#endif
+}
+
+#ifdef HAVE_LIBUNWIND_S390X_SUPPORT
+extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,
+ unw_word_t ip,
+ unw_dyn_info_t *di,
+ unw_proc_info_t *pi,
+ int need_unwind_info, void *arg);
+#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table)
+#endif
+
+int __libunwind_arch__dwarf_search_unwind_table_s390(void *as __maybe_unused,
+ uint64_t ip __maybe_unused,
+ struct libarch_unwind__dyn_info *_di __maybe_unused,
+ void *pi __maybe_unused,
+ int need_unwind_info __maybe_unused,
+ void *arg __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_S390X_SUPPORT
+ unw_dyn_info_t di = {
+ .format = UNW_INFO_FORMAT_REMOTE_TABLE,
+ .start_ip = _di->start_ip,
+ .end_ip = _di->end_ip,
+ .u = {
+ .rti = {
+ .segbase = _di->segbase,
+ .table_data = _di->table_data,
+ .table_len = _di->table_len,
+ },
+ },
+ };
+ int ret = dwarf_search_unwind_table(as, ip, &di, pi, need_unwind_info, arg);
+
+ _di->start_ip = di.start_ip;
+ _di->end_ip = di.end_ip;
+ _di->segbase = di.u.rti.segbase;
+ _di->table_data = di.u.rti.table_data;
+ _di->table_len = di.u.rti.table_len;
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
+
+#if defined(HAVE_LIBUNWIND_S390X_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_S390X)
+extern int UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug,
+ unw_word_t ip,
+ unw_word_t segbase,
+ const char *obj_name, unw_word_t start,
+ unw_word_t end);
+#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame)
+#endif
+
+int __libunwind_arch__dwarf_find_debug_frame_s390(int found __maybe_unused,
+ struct libarch_unwind__dyn_info *_di __maybe_unused,
+ uint64_t ip __maybe_unused,
+ uint64_t segbase __maybe_unused,
+ const char *obj_name __maybe_unused,
+ uint64_t start __maybe_unused,
+ uint64_t end __maybe_unused)
+{
+#if defined(HAVE_LIBUNWIND_S390X_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_S390X)
+ unw_dyn_info_t di = {
+ .format = UNW_INFO_FORMAT_REMOTE_TABLE,
+ .start_ip = _di->start_ip,
+ .end_ip = _di->end_ip,
+ .u = {
+ .rti = {
+ .segbase = _di->segbase,
+ .table_data = _di->table_data,
+ .table_len = _di->table_len,
+ },
+ },
+ };
+ int ret = dwarf_find_debug_frame(found, &di, ip, segbase, obj_name, start, end);
+
+ _di->start_ip = di.start_ip;
+ _di->end_ip = di.end_ip;
+ _di->segbase = di.u.rti.segbase;
+ _di->table_data = di.u.rti.table_data;
+ _di->table_len = di.u.rti.table_len;
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
+
+struct unwind_info *__libunwind_arch_unwind_info__new_s390(struct thread *thread __maybe_unused,
+ struct perf_sample *sample __maybe_unused,
+ int max_stack __maybe_unused,
+ bool best_effort __maybe_unused,
+ uint64_t first_ip __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_S390X_SUPPORT
+ struct arch_unwind_info {
+ struct unwind_info ui;
+ unw_cursor_t _cursor;
+ uint64_t _ips[];
+ };
+
+ struct maps *maps = thread__maps(thread);
+ void *addr_space = maps__addr_space(maps);
+ struct arch_unwind_info *ui;
+ int ret;
+
+ if (addr_space == NULL)
+ return NULL;
+
+ ui = zalloc(sizeof(*ui) + sizeof(ui->_ips[0]) * max_stack);
+ if (!ui)
+ return NULL;
+
+ ui->ui.machine = maps__machine(maps);
+ ui->ui.thread = thread;
+ ui->ui.sample = sample;
+ ui->ui.cursor = &ui->_cursor;
+ ui->ui.ips = &ui->_ips[0];
+ ui->ui.ips[0] = first_ip;
+ ui->ui.cur_ip = 1;
+ ui->ui.max_ips = max_stack;
+ ui->ui.unw_word_t_size = sizeof(unw_word_t);
+ ui->ui.e_machine = EM_S390;
+ ui->ui.best_effort = best_effort;
+
+ ret = unw_init_remote(&ui->_cursor, addr_space, &ui->ui);
+ if (ret) {
+ if (!best_effort)
+ pr_err("libunwind: %s\n", unw_strerror(ret));
+ free(ui);
+ return NULL;
+ }
+
+ return &ui->ui;
+#else
+ return NULL;
+#endif
+}
+
+int __libunwind_arch__unwind_step_s390(struct unwind_info *ui __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_S390X_SUPPORT
+ int ret;
+
+ if (ui->cur_ip >= ui->max_ips)
+ return -1;
+
+ ret = unw_step(ui->cursor);
+ if (ret > 0) {
+ uint64_t ip;
+
+ unw_get_reg(ui->cursor, UNW_REG_IP, &ip);
+
+ if (unw_is_signal_frame(ui->cursor) <= 0) {
+ /*
+ * Decrement the IP for any non-activation frames. This
+ * is required to properly find the srcline for caller
+ * frames. See also the documentation for
+ * dwfl_frame_pc(), which this code tries to replicate.
+ */
+ --ip;
+ }
+ ui->ips[ui->cur_ip++] = ip;
+ }
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
diff --git a/tools/perf/util/libunwind-arch/libunwind-x86_64.c b/tools/perf/util/libunwind-arch/libunwind-x86_64.c
index 25e326bd3e15..8ca66d442ffc 100644
--- a/tools/perf/util/libunwind-arch/libunwind-x86_64.c
+++ b/tools/perf/util/libunwind-arch/libunwind-x86_64.c
@@ -2,9 +2,12 @@
#include "libunwind-arch.h"
#include "../debug.h"
#include "../maps.h"
+#include "../thread.h"
#include "../../../arch/x86/include/uapi/asm/perf_regs.h"
#include <linux/compiler.h>
#include <linux/kernel.h>
+#include <linux/zalloc.h>
+#include <elf.h>
#include <errno.h>
#ifdef HAVE_LIBUNWIND_X86_64_SUPPORT
@@ -65,3 +68,253 @@ void __libunwind_arch__finish_access_x86_64(struct maps *maps __maybe_unused)
unw_destroy_addr_space(maps__addr_space(maps));
#endif
}
+
+#ifdef HAVE_LIBUNWIND_X86_64_SUPPORT
+static int find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
+ int need_unwind_info, void *arg)
+{
+ return __libunwind__find_proc_info(as, ip, pi, need_unwind_info, arg);
+}
+
+static void put_unwind_info(unw_addr_space_t __maybe_unused as,
+ unw_proc_info_t *pi __maybe_unused,
+ void *arg __maybe_unused)
+{
+ pr_debug("unwind: put_unwind_info called\n");
+}
+
+static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as,
+ unw_word_t __maybe_unused *dil_addr,
+ void __maybe_unused *arg)
+{
+ return -UNW_ENOINFO;
+}
+
+static int access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *valp,
+ int __write, void *arg)
+{
+ return __libunwind__access_mem(as, addr, valp, __write, arg);
+}
+
+static int access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t *valp,
+ int __write, void *arg)
+{
+ return __libunwind__access_reg(as, regnum, valp, __write, arg);
+}
+
+static int access_fpreg(unw_addr_space_t __maybe_unused as,
+ unw_regnum_t __maybe_unused num,
+ unw_fpreg_t __maybe_unused *val,
+ int __maybe_unused __write,
+ void __maybe_unused *arg)
+{
+ pr_err("unwind: access_fpreg unsupported\n");
+ return -UNW_EINVAL;
+}
+
+static int resume(unw_addr_space_t __maybe_unused as,
+ unw_cursor_t __maybe_unused *cu,
+ void __maybe_unused *arg)
+{
+ pr_err("unwind: resume unsupported\n");
+ return -UNW_EINVAL;
+}
+
+static int get_proc_name(unw_addr_space_t __maybe_unused as,
+ unw_word_t __maybe_unused addr,
+ char __maybe_unused *bufp, size_t __maybe_unused buf_len,
+ unw_word_t __maybe_unused *offp, void __maybe_unused *arg)
+{
+ pr_err("unwind: get_proc_name unsupported\n");
+ return -UNW_EINVAL;
+}
+#endif
+
+void *__libunwind_arch__create_addr_space_x86_64(void)
+{
+#ifdef HAVE_LIBUNWIND_X86_64_SUPPORT
+ static unw_accessors_t accessors = {
+ .find_proc_info = find_proc_info,
+ .put_unwind_info = put_unwind_info,
+ .get_dyn_info_list_addr = get_dyn_info_list_addr,
+ .access_mem = access_mem,
+ .access_reg = access_reg,
+ .access_fpreg = access_fpreg,
+ .resume = resume,
+ .get_proc_name = get_proc_name,
+ };
+ unw_addr_space_t addr_space;
+
+ addr_space = unw_create_addr_space(&accessors, /*byte_order=*/0);
+ unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL);
+ return addr_space;
+#else
+ return NULL;
+#endif
+}
+
+#ifdef HAVE_LIBUNWIND_X86_64_SUPPORT
+extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,
+ unw_word_t ip,
+ unw_dyn_info_t *di,
+ unw_proc_info_t *pi,
+ int need_unwind_info, void *arg);
+#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table)
+#endif
+
+int __libunwind_arch__dwarf_search_unwind_table_x86_64(void *as __maybe_unused,
+ uint64_t ip __maybe_unused,
+ struct libarch_unwind__dyn_info *_di __maybe_unused,
+ void *pi __maybe_unused,
+ int need_unwind_info __maybe_unused,
+ void *arg __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_X86_64_SUPPORT
+ unw_dyn_info_t di = {
+ .format = UNW_INFO_FORMAT_REMOTE_TABLE,
+ .start_ip = _di->start_ip,
+ .end_ip = _di->end_ip,
+ .u = {
+ .rti = {
+ .segbase = _di->segbase,
+ .table_data = _di->table_data,
+ .table_len = _di->table_len,
+ },
+ },
+ };
+ int ret = dwarf_search_unwind_table(as, ip, &di, pi, need_unwind_info, arg);
+
+ _di->start_ip = di.start_ip;
+ _di->end_ip = di.end_ip;
+ _di->segbase = di.u.rti.segbase;
+ _di->table_data = di.u.rti.table_data;
+ _di->table_len = di.u.rti.table_len;
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
+
+#if defined(HAVE_LIBUNWIND_X86_64_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_X86_64)
+extern int UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug,
+ unw_word_t ip,
+ unw_word_t segbase,
+ const char *obj_name, unw_word_t start,
+ unw_word_t end);
+#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame)
+#endif
+
+int __libunwind_arch__dwarf_find_debug_frame_x86_64(int found __maybe_unused,
+ struct libarch_unwind__dyn_info *_di __maybe_unused,
+ uint64_t ip __maybe_unused,
+ uint64_t segbase __maybe_unused,
+ const char *obj_name __maybe_unused,
+ uint64_t start __maybe_unused,
+ uint64_t end __maybe_unused)
+{
+#if defined(HAVE_LIBUNWIND_X86_64_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_X86_64)
+ unw_dyn_info_t di = {
+ .format = UNW_INFO_FORMAT_REMOTE_TABLE,
+ .start_ip = _di->start_ip,
+ .end_ip = _di->end_ip,
+ .u = {
+ .rti = {
+ .segbase = _di->segbase,
+ .table_data = _di->table_data,
+ .table_len = _di->table_len,
+ },
+ },
+ };
+ int ret = dwarf_find_debug_frame(found, &di, ip, segbase, obj_name, start, end);
+
+ _di->start_ip = di.start_ip;
+ _di->end_ip = di.end_ip;
+ _di->segbase = di.u.rti.segbase;
+ _di->table_data = di.u.rti.table_data;
+ _di->table_len = di.u.rti.table_len;
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
+
+struct unwind_info *__libunwind_arch_unwind_info__new_x86_64(struct thread *thread __maybe_unused,
+ struct perf_sample *sample __maybe_unused,
+ int max_stack __maybe_unused,
+ bool best_effort __maybe_unused,
+ uint64_t first_ip __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_X86_64_SUPPORT
+ struct arch_unwind_info {
+ struct unwind_info ui;
+ unw_cursor_t _cursor;
+ uint64_t _ips[];
+ };
+
+ struct maps *maps = thread__maps(thread);
+ void *addr_space = maps__addr_space(maps);
+ struct arch_unwind_info *ui;
+ int ret;
+
+ if (addr_space == NULL)
+ return NULL;
+
+ ui = zalloc(sizeof(*ui) + sizeof(ui->_ips[0]) * max_stack);
+ if (!ui)
+ return NULL;
+
+ ui->ui.machine = maps__machine(maps);
+ ui->ui.thread = thread;
+ ui->ui.sample = sample;
+ ui->ui.cursor = &ui->_cursor;
+ ui->ui.ips = &ui->_ips[0];
+ ui->ui.ips[0] = first_ip;
+ ui->ui.cur_ip = 1;
+ ui->ui.max_ips = max_stack;
+ ui->ui.unw_word_t_size = sizeof(unw_word_t);
+ ui->ui.e_machine = EM_X86_64;
+ ui->ui.best_effort = best_effort;
+
+ ret = unw_init_remote(&ui->_cursor, addr_space, &ui->ui);
+ if (ret) {
+ if (!best_effort)
+ pr_err("libunwind: %s\n", unw_strerror(ret));
+ free(ui);
+ return NULL;
+ }
+ return &ui->ui;
+#else
+ return NULL;
+#endif
+}
+
+int __libunwind_arch__unwind_step_x86_64(struct unwind_info *ui __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_X86_64_SUPPORT
+ int ret;
+
+ if (ui->cur_ip >= ui->max_ips)
+ return 0;
+
+ ret = unw_step(ui->cursor);
+ if (ret > 0) {
+ uint64_t ip;
+
+ unw_get_reg(ui->cursor, UNW_REG_IP, &ip);
+
+ if (unw_is_signal_frame(ui->cursor) <= 0) {
+ /*
+ * Decrement the IP for any non-activation frames. This
+ * is required to properly find the srcline for caller
+ * frames. See also the documentation for
+ * dwfl_frame_pc(), which this code tries to replicate.
+ */
+ --ip;
+ }
+ ui->ips[ui->cur_ip++] = ip;
+ }
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
diff --git a/tools/perf/util/libunwind/arm64.c b/tools/perf/util/libunwind/arm64.c
deleted file mode 100644
index 15670a964495..000000000000
--- a/tools/perf/util/libunwind/arm64.c
+++ /dev/null
@@ -1,35 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * This file setups defines to compile arch specific binary from the
- * generic one.
- *
- * The function 'LIBUNWIND__ARCH_REG_ID' name is set according to arch
- * name and the definition of this function is included directly from
- * 'arch/arm64/util/unwind-libunwind.c', to make sure that this function
- * is defined no matter what arch the host is.
- *
- * Finally, the arch specific unwind methods are exported which will
- * be assigned to each arm64 thread.
- */
-
-#define REMOTE_UNWIND_LIBUNWIND
-
-#include "unwind.h"
-#include "libunwind-aarch64.h"
-#define perf_event_arm_regs perf_event_arm64_regs
-#include <../../../arch/arm64/include/uapi/asm/perf_regs.h>
-#undef perf_event_arm_regs
-#include "../../arch/arm64/util/unwind-libunwind.c"
-
-/* NO_LIBUNWIND_DEBUG_FRAME is a feature flag for local libunwind,
- * assign NO_LIBUNWIND_DEBUG_FRAME_AARCH64 to it for compiling arm64
- * unwind methods.
- */
-#undef NO_LIBUNWIND_DEBUG_FRAME
-#ifdef NO_LIBUNWIND_DEBUG_FRAME_AARCH64
-#define NO_LIBUNWIND_DEBUG_FRAME
-#endif
-#include "util/unwind-libunwind-local.c"
-
-struct unwind_libunwind_ops *
-arm64_unwind_libunwind_ops = &_unwind_libunwind_ops;
diff --git a/tools/perf/util/libunwind/x86_32.c b/tools/perf/util/libunwind/x86_32.c
deleted file mode 100644
index 1e9fb8bfec44..000000000000
--- a/tools/perf/util/libunwind/x86_32.c
+++ /dev/null
@@ -1,29 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * This file setups defines to compile arch specific binary from the
- * generic one.
- *
- * The function 'LIBUNWIND__ARCH_REG_ID' name is set according to arch
- * name and the definition of this function is included directly from
- * 'arch/x86/util/unwind-libunwind.c', to make sure that this function
- * is defined no matter what arch the host is.
- *
- * Finally, the arch specific unwind methods are exported which will
- * be assigned to each x86 thread.
- */
-
-#define REMOTE_UNWIND_LIBUNWIND
-
-#include "unwind.h"
-#include "libunwind-x86.h"
-
-/* Explicitly define NO_LIBUNWIND_DEBUG_FRAME, because non-ARM has no
- * dwarf_find_debug_frame() function.
- */
-#ifndef NO_LIBUNWIND_DEBUG_FRAME
-#define NO_LIBUNWIND_DEBUG_FRAME
-#endif
-#include "util/unwind-libunwind-local.c"
-
-struct unwind_libunwind_ops *
-x86_32_unwind_libunwind_ops = &_unwind_libunwind_ops;
diff --git a/tools/perf/util/maps.c b/tools/perf/util/maps.c
index 8c7b2a1e7642..7ba82024fb11 100644
--- a/tools/perf/util/maps.c
+++ b/tools/perf/util/maps.c
@@ -198,16 +198,6 @@ void maps__set_addr_space(struct maps *maps, void *addr_space)
RC_CHK_ACCESS(maps)->addr_space = addr_space;
}
-const struct unwind_libunwind_ops *maps__unwind_libunwind_ops(const struct maps *maps)
-{
- return RC_CHK_ACCESS(maps)->unwind_libunwind_ops;
-}
-
-void maps__set_unwind_libunwind_ops(struct maps *maps, const struct unwind_libunwind_ops *ops)
-{
- RC_CHK_ACCESS(maps)->unwind_libunwind_ops = ops;
-}
-
uint16_t maps__e_machine(const struct maps *maps)
{
return RC_CHK_ACCESS(maps)->e_machine;
diff --git a/tools/perf/util/maps.h b/tools/perf/util/maps.h
index 6469f62c41a8..5b80b199685e 100644
--- a/tools/perf/util/maps.h
+++ b/tools/perf/util/maps.h
@@ -49,8 +49,6 @@ refcount_t *maps__refcnt(struct maps *maps); /* Test only. */
#ifdef HAVE_LIBUNWIND_SUPPORT
void *maps__addr_space(const struct maps *maps);
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);
uint16_t maps__e_machine(const struct maps *maps);
void maps__set_e_machine(struct maps *maps, uint16_t e_machine);
#endif
diff --git a/tools/perf/util/unwind-libunwind-local.c b/tools/perf/util/unwind-libunwind-local.c
deleted file mode 100644
index 8f0128ba05a7..000000000000
--- a/tools/perf/util/unwind-libunwind-local.c
+++ /dev/null
@@ -1,820 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Post mortem Dwarf CFI based unwinding on top of regs and stack dumps.
- *
- * Lots of this code have been borrowed or heavily inspired from parts of
- * the libunwind 0.99 code which are (amongst other contributors I may have
- * forgotten):
- *
- * Copyright (C) 2002-2007 Hewlett-Packard Co
- * Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
- *
- * And the bugs have been added by:
- *
- * Copyright (C) 2010, Frederic Weisbecker <fweisbec@gmail.com>
- * Copyright (C) 2012, Jiri Olsa <jolsa@redhat.com>
- *
- */
-
-#include <elf.h>
-#include <errno.h>
-#include <gelf.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <string.h>
-#include <unistd.h>
-#include <sys/mman.h>
-#include <linux/list.h>
-#include <linux/zalloc.h>
-#ifndef REMOTE_UNWIND_LIBUNWIND
-#include <libunwind.h>
-#include <libunwind-ptrace.h>
-#endif
-#include "callchain.h"
-#include "thread.h"
-#include "session.h"
-#include "perf_regs.h"
-#include "unwind.h"
-#include "map.h"
-#include "symbol.h"
-#include "debug.h"
-#include "asm/bug.h"
-#include "dso.h"
-#include "libunwind-arch/libunwind-arch.h"
-
-extern int
-UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,
- unw_word_t ip,
- unw_dyn_info_t *di,
- unw_proc_info_t *pi,
- int need_unwind_info, void *arg);
-
-#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table)
-
-extern int
-UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug,
- unw_word_t ip,
- unw_word_t segbase,
- const char *obj_name, unw_word_t start,
- unw_word_t end);
-
-#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame)
-
-#define DW_EH_PE_FORMAT_MASK 0x0f /* format of the encoded value */
-#define DW_EH_PE_APPL_MASK 0x70 /* how the value is to be applied */
-
-/* Pointer-encoding formats: */
-#define DW_EH_PE_omit 0xff
-#define DW_EH_PE_ptr 0x00 /* pointer-sized unsigned value */
-#define DW_EH_PE_udata4 0x03 /* unsigned 32-bit value */
-#define DW_EH_PE_udata8 0x04 /* unsigned 64-bit value */
-#define DW_EH_PE_sdata4 0x0b /* signed 32-bit value */
-#define DW_EH_PE_sdata8 0x0c /* signed 64-bit value */
-
-/* Pointer-encoding application: */
-#define DW_EH_PE_absptr 0x00 /* absolute value */
-#define DW_EH_PE_pcrel 0x10 /* rel. to addr. of encoded value */
-
-/*
- * The following are not documented by LSB v1.3, yet they are used by
- * GCC, presumably they aren't documented by LSB since they aren't
- * used on Linux:
- */
-#define DW_EH_PE_funcrel 0x40 /* start-of-procedure-relative */
-#define DW_EH_PE_aligned 0x50 /* aligned pointer */
-
-/* Flags intentionally not handled, since they're not needed:
- * #define DW_EH_PE_indirect 0x80
- * #define DW_EH_PE_uleb128 0x01
- * #define DW_EH_PE_udata2 0x02
- * #define DW_EH_PE_sleb128 0x09
- * #define DW_EH_PE_sdata2 0x0a
- * #define DW_EH_PE_textrel 0x20
- * #define DW_EH_PE_datarel 0x30
- */
-
-struct unwind_info {
- struct perf_sample *sample;
- struct machine *machine;
- struct thread *thread;
- uint16_t e_machine;
- bool best_effort;
-};
-
-#define dw_read(ptr, type, end) ({ \
- type *__p = (type *) ptr; \
- type __v; \
- if ((__p + 1) > (type *) end) \
- return -EINVAL; \
- __v = *__p++; \
- ptr = (typeof(ptr)) __p; \
- __v; \
- })
-
-static int __dw_read_encoded_value(u8 **p, u8 *end, u64 *val,
- u8 encoding)
-{
- u8 *cur = *p;
- *val = 0;
-
- switch (encoding) {
- case DW_EH_PE_omit:
- *val = 0;
- goto out;
- case DW_EH_PE_ptr:
- *val = dw_read(cur, unsigned long, end);
- goto out;
- default:
- break;
- }
-
- switch (encoding & DW_EH_PE_APPL_MASK) {
- case DW_EH_PE_absptr:
- break;
- case DW_EH_PE_pcrel:
- *val = (unsigned long) cur;
- break;
- default:
- return -EINVAL;
- }
-
- if ((encoding & 0x07) == 0x00)
- encoding |= DW_EH_PE_udata4;
-
- switch (encoding & DW_EH_PE_FORMAT_MASK) {
- case DW_EH_PE_sdata4:
- *val += dw_read(cur, s32, end);
- break;
- case DW_EH_PE_udata4:
- *val += dw_read(cur, u32, end);
- break;
- case DW_EH_PE_sdata8:
- *val += dw_read(cur, s64, end);
- break;
- case DW_EH_PE_udata8:
- *val += dw_read(cur, u64, end);
- break;
- default:
- return -EINVAL;
- }
-
- out:
- *p = cur;
- return 0;
-}
-
-#define dw_read_encoded_value(ptr, end, enc) ({ \
- u64 __v; \
- if (__dw_read_encoded_value(&ptr, end, &__v, enc)) { \
- return -EINVAL; \
- } \
- __v; \
- })
-
-static int elf_section_address_and_offset(int fd, const char *name, u64 *address, u64 *offset)
-{
- Elf *elf;
- GElf_Ehdr ehdr;
- GElf_Shdr shdr;
- int ret = -1;
-
- elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
- if (elf == NULL)
- return -1;
-
- if (gelf_getehdr(elf, &ehdr) == NULL)
- goto out_err;
-
- if (!elf_section_by_name(elf, &ehdr, &shdr, name, NULL))
- goto out_err;
-
- *address = shdr.sh_addr;
- *offset = shdr.sh_offset;
- ret = 0;
-out_err:
- elf_end(elf);
- return ret;
-}
-
-#ifndef NO_LIBUNWIND_DEBUG_FRAME
-static u64 elf_section_offset(int fd, const char *name)
-{
- u64 address, offset = 0;
-
- if (elf_section_address_and_offset(fd, name, &address, &offset))
- return 0;
-
- return offset;
-}
-#endif
-
-static u64 elf_base_address(int fd)
-{
- Elf *elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
- GElf_Phdr phdr;
- u64 retval = 0;
- size_t i, phdrnum = 0;
-
- if (elf == NULL)
- return 0;
- (void)elf_getphdrnum(elf, &phdrnum);
- /* PT_LOAD segments are sorted by p_vaddr, so the first has the minimum p_vaddr. */
- for (i = 0; i < phdrnum; i++) {
- if (gelf_getphdr(elf, i, &phdr) && phdr.p_type == PT_LOAD) {
- retval = phdr.p_vaddr & -getpagesize();
- break;
- }
- }
-
- elf_end(elf);
- return retval;
-}
-
-#ifndef NO_LIBUNWIND_DEBUG_FRAME
-static int elf_is_exec(int fd, const char *name)
-{
- Elf *elf;
- GElf_Ehdr ehdr;
- int retval = 0;
-
- elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
- if (elf == NULL)
- return 0;
- if (gelf_getehdr(elf, &ehdr) == NULL)
- goto out;
-
- retval = (ehdr.e_type == ET_EXEC);
-
-out:
- elf_end(elf);
- pr_debug("unwind: elf_is_exec(%s): %d\n", name, retval);
- return retval;
-}
-#endif
-
-struct table_entry {
- u32 start_ip_offset;
- u32 fde_offset;
-};
-
-struct eh_frame_hdr {
- unsigned char version;
- unsigned char eh_frame_ptr_enc;
- unsigned char fde_count_enc;
- unsigned char table_enc;
-
- /*
- * The rest of the header is variable-length and consists of the
- * following members:
- *
- * encoded_t eh_frame_ptr;
- * encoded_t fde_count;
- */
-
- /* A single encoded pointer should not be more than 8 bytes. */
- u64 enc[2];
-
- /*
- * struct {
- * encoded_t start_ip;
- * encoded_t fde_addr;
- * } binary_search_table[fde_count];
- */
- char data[];
-} __packed;
-
-static int unwind_spec_ehframe(struct dso *dso, struct machine *machine,
- u64 offset, u64 *table_data_offset, u64 *fde_count)
-{
- struct eh_frame_hdr hdr;
- u8 *enc = (u8 *) &hdr.enc;
- u8 *end = (u8 *) &hdr.data;
- ssize_t r;
-
- r = dso__data_read_offset(dso, machine, offset,
- (u8 *) &hdr, sizeof(hdr));
- if (r != sizeof(hdr))
- return -EINVAL;
-
- /* We dont need eh_frame_ptr, just skip it. */
- dw_read_encoded_value(enc, end, hdr.eh_frame_ptr_enc);
-
- *fde_count = dw_read_encoded_value(enc, end, hdr.fde_count_enc);
- *table_data_offset = enc - (u8 *) &hdr;
- return 0;
-}
-
-struct read_unwind_spec_eh_frame_maps_cb_args {
- struct dso *dso;
- u64 base_addr;
-};
-
-static int read_unwind_spec_eh_frame_maps_cb(struct map *map, void *data)
-{
-
- struct read_unwind_spec_eh_frame_maps_cb_args *args = data;
-
- if (map__dso(map) == args->dso && map__start(map) - map__pgoff(map) < args->base_addr)
- args->base_addr = map__start(map) - map__pgoff(map);
-
- return 0;
-}
-
-
-static int read_unwind_spec_eh_frame(struct dso *dso, struct unwind_info *ui,
- u64 *table_data, u64 *segbase,
- u64 *fde_count)
-{
- struct read_unwind_spec_eh_frame_maps_cb_args args = {
- .dso = dso,
- .base_addr = UINT64_MAX,
- };
- int ret, fd;
-
- if (dso__data(dso)->eh_frame_hdr_offset == 0) {
- if (!dso__data_get_fd(dso, ui->machine, &fd))
- return -EINVAL;
-
- /* Check the .eh_frame section for unwinding info */
- ret = elf_section_address_and_offset(fd, ".eh_frame_hdr",
- &dso__data(dso)->eh_frame_hdr_addr,
- &dso__data(dso)->eh_frame_hdr_offset);
- dso__data(dso)->elf_base_addr = elf_base_address(fd);
- dso__data_put_fd(dso);
- if (ret || dso__data(dso)->eh_frame_hdr_offset == 0)
- return -EINVAL;
- }
-
- maps__for_each_map(thread__maps(ui->thread), read_unwind_spec_eh_frame_maps_cb, &args);
-
- args.base_addr -= dso__data(dso)->elf_base_addr;
- /* Address of .eh_frame_hdr */
- *segbase = args.base_addr + dso__data(dso)->eh_frame_hdr_addr;
- ret = unwind_spec_ehframe(dso, ui->machine, dso__data(dso)->eh_frame_hdr_offset,
- table_data, fde_count);
- if (ret)
- return ret;
- /* binary_search_table offset plus .eh_frame_hdr address */
- *table_data += *segbase;
- return 0;
-}
-
-#ifndef NO_LIBUNWIND_DEBUG_FRAME
-static int read_unwind_spec_debug_frame(struct dso *dso,
- struct machine *machine, u64 *offset)
-{
- int fd;
- u64 ofs = dso__data(dso)->debug_frame_offset;
-
- /* debug_frame can reside in:
- * - dso
- * - debug pointed by symsrc_filename
- * - gnu_debuglink, which doesn't necessary
- * has to be pointed by symsrc_filename
- */
- if (ofs == 0) {
- if (dso__data_get_fd(dso, machine, &fd)) {
- ofs = elf_section_offset(fd, ".debug_frame");
- dso__data_put_fd(dso);
- }
-
- if (ofs <= 0) {
- fd = open(dso__symsrc_filename(dso), O_RDONLY);
- if (fd >= 0) {
- ofs = elf_section_offset(fd, ".debug_frame");
- close(fd);
- }
- }
-
- if (ofs <= 0) {
- char *debuglink = malloc(PATH_MAX);
- int ret = 0;
-
- if (debuglink == NULL) {
- pr_err("unwind: Can't read unwind spec debug frame.\n");
- return -ENOMEM;
- }
-
- ret = dso__read_binary_type_filename(
- dso, DSO_BINARY_TYPE__DEBUGLINK,
- machine->root_dir, debuglink, PATH_MAX);
- if (!ret) {
- fd = open(debuglink, O_RDONLY);
- if (fd >= 0) {
- ofs = elf_section_offset(fd,
- ".debug_frame");
- close(fd);
- }
- }
- if (ofs > 0) {
- if (dso__symsrc_filename(dso) != NULL) {
- pr_warning(
- "%s: overwrite symsrc(%s,%s)\n",
- __func__,
- dso__symsrc_filename(dso),
- debuglink);
- dso__free_symsrc_filename(dso);
- }
- dso__set_symsrc_filename(dso, debuglink);
- } else {
- free(debuglink);
- }
- }
-
- dso__data(dso)->debug_frame_offset = ofs;
- }
-
- *offset = ofs;
- if (*offset)
- return 0;
-
- return -EINVAL;
-}
-#endif
-
-static struct map *find_map(unw_word_t ip, struct unwind_info *ui)
-{
- struct addr_location al;
- struct map *ret;
-
- addr_location__init(&al);
- thread__find_map(ui->thread, PERF_RECORD_MISC_USER, ip, &al);
- ret = map__get(al.map);
- addr_location__exit(&al);
- return ret;
-}
-
-static int
-find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
- int need_unwind_info, void *arg)
-{
- struct unwind_info *ui = arg;
- struct map *map;
- struct dso *dso;
- unw_dyn_info_t di;
- u64 table_data, segbase, fde_count;
- int ret = -EINVAL;
-
- map = find_map(ip, ui);
- if (!map)
- return -EINVAL;
-
- dso = map__dso(map);
- if (!dso) {
- map__put(map);
- return -EINVAL;
- }
-
- pr_debug("unwind: find_proc_info dso %s\n", dso__name(dso));
-
- /* Check the .eh_frame section for unwinding info */
- if (!read_unwind_spec_eh_frame(dso, ui, &table_data, &segbase, &fde_count)) {
- memset(&di, 0, sizeof(di));
- di.format = UNW_INFO_FORMAT_REMOTE_TABLE;
- di.start_ip = map__start(map);
- di.end_ip = map__end(map);
- di.u.rti.segbase = segbase;
- di.u.rti.table_data = table_data;
- di.u.rti.table_len = fde_count * sizeof(struct table_entry)
- / sizeof(unw_word_t);
- ret = dwarf_search_unwind_table(as, ip, &di, pi,
- need_unwind_info, arg);
- }
-
-#ifndef NO_LIBUNWIND_DEBUG_FRAME
- /* Check the .debug_frame section for unwinding info */
- if (ret < 0 &&
- !read_unwind_spec_debug_frame(dso, ui->machine, &segbase)) {
- int fd;
- u64 start = map__start(map);
- unw_word_t base = start;
- const char *symfile;
-
- if (dso__data_get_fd(dso, ui->machine, &fd)) {
- if (elf_is_exec(fd, dso__name(dso)))
- base = 0;
- dso__data_put_fd(dso);
- }
-
- symfile = dso__symsrc_filename(dso) ?: dso__name(dso);
-
- memset(&di, 0, sizeof(di));
- if (dwarf_find_debug_frame(0, &di, ip, base, symfile, start, map__end(map)))
- ret = dwarf_search_unwind_table(as, ip, &di, pi,
- need_unwind_info, arg);
- }
-#endif
- map__put(map);
- return ret;
-}
-
-static int access_fpreg(unw_addr_space_t __maybe_unused as,
- unw_regnum_t __maybe_unused num,
- unw_fpreg_t __maybe_unused *val,
- int __maybe_unused __write,
- void __maybe_unused *arg)
-{
- pr_err("unwind: access_fpreg unsupported\n");
- return -UNW_EINVAL;
-}
-
-static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as,
- unw_word_t __maybe_unused *dil_addr,
- void __maybe_unused *arg)
-{
- return -UNW_ENOINFO;
-}
-
-static int resume(unw_addr_space_t __maybe_unused as,
- unw_cursor_t __maybe_unused *cu,
- void __maybe_unused *arg)
-{
- pr_err("unwind: resume unsupported\n");
- return -UNW_EINVAL;
-}
-
-static int
-get_proc_name(unw_addr_space_t __maybe_unused as,
- unw_word_t __maybe_unused addr,
- char __maybe_unused *bufp, size_t __maybe_unused buf_len,
- unw_word_t __maybe_unused *offp, void __maybe_unused *arg)
-{
- pr_err("unwind: get_proc_name unsupported\n");
- return -UNW_EINVAL;
-}
-
-static int access_dso_mem(struct unwind_info *ui, unw_word_t addr,
- unw_word_t *data)
-{
- struct map *map;
- struct dso *dso;
- ssize_t size;
-
- map = find_map(addr, ui);
- if (!map) {
- pr_debug("unwind: no map for %lx\n", (unsigned long)addr);
- return -1;
- }
-
- dso = map__dso(map);
-
- if (!dso) {
- map__put(map);
- return -1;
- }
-
- size = dso__data_read_addr(dso, map, ui->machine,
- addr, (u8 *) data, sizeof(*data));
- map__put(map);
- return !(size == sizeof(*data));
-}
-
-static int access_mem(unw_addr_space_t __maybe_unused as,
- unw_word_t addr, unw_word_t *valp,
- int __write, void *arg)
-{
- struct unwind_info *ui = arg;
- struct stack_dump *stack = &ui->sample->user_stack;
- u64 start, end;
- int offset;
- int ret;
-
- /* Don't support write, probably not needed. */
- if (__write || !stack || !ui->sample->user_regs || !ui->sample->user_regs->regs) {
- *valp = 0;
- return 0;
- }
-
- ret = perf_reg_value(&start, perf_sample__user_regs(ui->sample),
- perf_arch_reg_sp(ui->e_machine));
- if (ret)
- return ret;
-
- end = start + stack->size;
-
- /* Check overflow. */
- if (addr + sizeof(unw_word_t) < addr)
- return -EINVAL;
-
- if (addr < start || addr + sizeof(unw_word_t) >= end) {
- ret = access_dso_mem(ui, addr, valp);
- if (ret) {
- pr_debug("unwind: access_mem %p not inside range"
- " 0x%" PRIx64 "-0x%" PRIx64 "\n",
- (void *) (uintptr_t) addr, start, end);
- *valp = 0;
- return ret;
- }
- return 0;
- }
-
- offset = addr - start;
- *valp = *(unw_word_t *)&stack->data[offset];
- pr_debug("unwind: access_mem addr %p val %lx, offset %d\n",
- (void *) (uintptr_t) addr, (unsigned long)*valp, offset);
- return 0;
-}
-
-static int access_reg(unw_addr_space_t __maybe_unused as,
- unw_regnum_t regnum, unw_word_t *valp,
- int __write, void *arg)
-{
- struct unwind_info *ui = arg;
- int id, ret;
- u64 val;
-
- /* Don't support write, I suspect we don't need it. */
- if (__write) {
- pr_err("unwind: access_reg w %d\n", regnum);
- return 0;
- }
-
- if (!ui->sample->user_regs || !ui->sample->user_regs->regs) {
- *valp = 0;
- return 0;
- }
-
- id = get_perf_regnum_for_unw_regnum(ui->e_machine, regnum);
- if (id < 0)
- return -EINVAL;
-
- ret = perf_reg_value(&val, perf_sample__user_regs(ui->sample), id);
- if (ret) {
- if (!ui->best_effort)
- pr_err("unwind: can't read reg %d\n", regnum);
- return ret;
- }
-
- *valp = (unw_word_t) val;
- pr_debug("unwind: reg %d, val %lx\n", regnum, (unsigned long)*valp);
- return 0;
-}
-
-static void put_unwind_info(unw_addr_space_t __maybe_unused as,
- unw_proc_info_t *pi __maybe_unused,
- void *arg __maybe_unused)
-{
- pr_debug("unwind: put_unwind_info called\n");
-}
-
-static int entry(u64 ip, struct thread *thread,
- unwind_entry_cb_t cb, void *arg)
-{
- struct unwind_entry e;
- struct addr_location al;
- int ret;
-
- addr_location__init(&al);
- e.ms.sym = thread__find_symbol(thread, PERF_RECORD_MISC_USER, ip, &al);
- e.ip = ip;
- e.ms.map = al.map;
- e.ms.thread = thread__get(al.thread);
-
- pr_debug("unwind: %s:ip = 0x%" PRIx64 " (0x%" PRIx64 ")\n",
- al.sym ? al.sym->name : "''",
- ip,
- al.map ? map__map_ip(al.map, ip) : (u64) 0);
-
- ret = cb(&e, arg);
- addr_location__exit(&al);
- return ret;
-}
-
-static void display_error(int err)
-{
- switch (err) {
- case UNW_EINVAL:
- pr_err("unwind: Only supports local.\n");
- break;
- case UNW_EUNSPEC:
- pr_err("unwind: Unspecified error.\n");
- break;
- case UNW_EBADREG:
- pr_err("unwind: Register unavailable.\n");
- break;
- default:
- break;
- }
-}
-
-static unw_accessors_t accessors = {
- .find_proc_info = find_proc_info,
- .put_unwind_info = put_unwind_info,
- .get_dyn_info_list_addr = get_dyn_info_list_addr,
- .access_mem = access_mem,
- .access_reg = access_reg,
- .access_fpreg = access_fpreg,
- .resume = resume,
- .get_proc_name = get_proc_name,
-};
-
-static int _unwind__prepare_access(struct maps *maps)
-{
- void *addr_space = unw_create_addr_space(&accessors, 0);
-
- maps__set_addr_space(maps, addr_space);
- if (!addr_space) {
- pr_err("unwind: Can't create unwind address space.\n");
- return -ENOMEM;
- }
-
- unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL);
- return 0;
-}
-
-static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb,
- void *arg, int max_stack)
-{
- u64 val;
- unw_word_t ips[max_stack];
- unw_addr_space_t addr_space;
- unw_cursor_t c;
- int ret, i = 0;
-
- ret = perf_reg_value(&val, perf_sample__user_regs(ui->sample),
- perf_arch_reg_ip(ui->e_machine));
- if (ret)
- return ret;
-
- ips[i++] = (unw_word_t) val;
-
- /*
- * If we need more than one entry, do the DWARF
- * unwind itself.
- */
- if (max_stack - 1 > 0) {
- WARN_ONCE(!ui->thread, "WARNING: ui->thread is NULL");
- addr_space = maps__addr_space(thread__maps(ui->thread));
-
- if (addr_space == NULL)
- return -1;
-
- ret = unw_init_remote(&c, addr_space, ui);
- if (ret && !ui->best_effort)
- display_error(ret);
-
- while (!ret && (unw_step(&c) > 0) && i < max_stack) {
- unw_get_reg(&c, UNW_REG_IP, &ips[i]);
-
- /*
- * Decrement the IP for any non-activation frames.
- * this is required to properly find the srcline
- * for caller frames.
- * See also the documentation for dwfl_frame_pc(),
- * which this code tries to replicate.
- */
- if (unw_is_signal_frame(&c) <= 0)
- --ips[i];
-
- ++i;
- }
-
- max_stack = i;
- }
-
- /*
- * Display what we got based on the order setup.
- */
- for (i = 0; i < max_stack && !ret; i++) {
- int j = i;
-
- if (callchain_param.order == ORDER_CALLER)
- j = max_stack - i - 1;
- ret = ips[j] ? entry(ips[j], ui->thread, cb, arg) : 0;
- }
-
- return ret;
-}
-
-static int _unwind__get_entries(unwind_entry_cb_t cb, void *arg,
- struct thread *thread,
- struct perf_sample *data, int max_stack,
- bool best_effort)
-{
- struct unwind_info ui = {
- .sample = data,
- .thread = thread,
- .machine = maps__machine(thread__maps(thread)),
- .e_machine = thread__e_machine(thread, /*machine=*/NULL, /*e_flags=*/NULL),
- .best_effort = best_effort
- };
-
- if (!data->user_regs || !data->user_regs->regs)
- return -EINVAL;
-
- if (max_stack <= 0)
- return -EINVAL;
-
- return get_entries(&ui, cb, arg, max_stack);
-}
-
-static struct unwind_libunwind_ops
-_unwind_libunwind_ops = {
- .prepare_access = _unwind__prepare_access,
- .get_entries = _unwind__get_entries,
-};
-
-#ifndef REMOTE_UNWIND_LIBUNWIND
-struct unwind_libunwind_ops *
-local_unwind_libunwind_ops = &_unwind_libunwind_ops;
-#endif
diff --git a/tools/perf/util/unwind-libunwind.c b/tools/perf/util/unwind-libunwind.c
index eaee7b78d87c..66e7b7a26aad 100644
--- a/tools/perf/util/unwind-libunwind.c
+++ b/tools/perf/util/unwind-libunwind.c
@@ -1,23 +1,557 @@
// SPDX-License-Identifier: GPL-2.0
-#include "unwind.h"
+#include "callchain.h"
+#include "debug.h"
#include "dso.h"
+#include "env.h"
#include "map.h"
-#include "thread.h"
+#include "perf_regs.h"
#include "session.h"
-#include "debug.h"
-#include "env.h"
-#include "callchain.h"
+#include "symbol.h"
+#include "thread.h"
+#include "unwind.h"
#include "libunwind-arch/libunwind-arch.h"
#include <dwarf-regs.h>
#include <elf.h>
+#include <fcntl.h>
+#include <gelf.h>
+#include <inttypes.h>
+
+#define DW_EH_PE_FORMAT_MASK 0x0f /* format of the encoded value */
+#define DW_EH_PE_APPL_MASK 0x70 /* how the value is to be applied */
+
+/* Pointer-encoding formats: */
+#define DW_EH_PE_omit 0xff
+#define DW_EH_PE_ptr 0x00 /* pointer-sized unsigned value */
+#define DW_EH_PE_udata4 0x03 /* unsigned 32-bit value */
+#define DW_EH_PE_udata8 0x04 /* unsigned 64-bit value */
+#define DW_EH_PE_sdata4 0x0b /* signed 32-bit value */
+#define DW_EH_PE_sdata8 0x0c /* signed 64-bit value */
+
+/* Pointer-encoding application: */
+#define DW_EH_PE_absptr 0x00 /* absolute value */
+#define DW_EH_PE_pcrel 0x10 /* rel. to addr. of encoded value */
+
+/*
+ * The following are not documented by LSB v1.3, yet they are used by
+ * GCC, presumably they aren't documented by LSB since they aren't
+ * used on Linux:
+ */
+#define DW_EH_PE_funcrel 0x40 /* start-of-procedure-relative */
+#define DW_EH_PE_aligned 0x50 /* aligned pointer */
+
+/* Flags intentionally not handled, since they're not needed:
+ * #define DW_EH_PE_indirect 0x80
+ * #define DW_EH_PE_uleb128 0x01
+ * #define DW_EH_PE_udata2 0x02
+ * #define DW_EH_PE_sleb128 0x09
+ * #define DW_EH_PE_sdata2 0x0a
+ * #define DW_EH_PE_textrel 0x20
+ * #define DW_EH_PE_datarel 0x30
+ */
+
+#define dw_read(ptr, type, end) ({ \
+ type *__p = (type *) ptr; \
+ type __v; \
+ if ((__p + 1) > (type *) end) \
+ return -EINVAL; \
+ __v = *__p++; \
+ ptr = (typeof(ptr)) __p; \
+ __v; \
+ })
+
+static int __dw_read_encoded_value(u8 **p, u8 *end, u64 *val,
+ u8 encoding)
+{
+ u8 *cur = *p;
+ *val = 0;
+
+ switch (encoding) {
+ case DW_EH_PE_omit:
+ *val = 0;
+ goto out;
+ case DW_EH_PE_ptr:
+ *val = dw_read(cur, unsigned long, end);
+ goto out;
+ default:
+ break;
+ }
+
+ switch (encoding & DW_EH_PE_APPL_MASK) {
+ case DW_EH_PE_absptr:
+ break;
+ case DW_EH_PE_pcrel:
+ *val = (unsigned long) cur;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if ((encoding & 0x07) == 0x00)
+ encoding |= DW_EH_PE_udata4;
+
+ switch (encoding & DW_EH_PE_FORMAT_MASK) {
+ case DW_EH_PE_sdata4:
+ *val += dw_read(cur, s32, end);
+ break;
+ case DW_EH_PE_udata4:
+ *val += dw_read(cur, u32, end);
+ break;
+ case DW_EH_PE_sdata8:
+ *val += dw_read(cur, s64, end);
+ break;
+ case DW_EH_PE_udata8:
+ *val += dw_read(cur, u64, end);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ out:
+ *p = cur;
+ return 0;
+}
+
+#define dw_read_encoded_value(ptr, end, enc) ({ \
+ u64 __v; \
+ if (__dw_read_encoded_value(&ptr, end, &__v, enc)) { \
+ return -EINVAL; \
+ } \
+ __v; \
+ })
+
+static u64 elf_base_address(int fd)
+{
+ Elf *elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
+ GElf_Phdr phdr;
+ u64 retval = 0;
+ size_t i, phdrnum = 0;
+
+ if (elf == NULL)
+ return 0;
+ (void)elf_getphdrnum(elf, &phdrnum);
+ /* PT_LOAD segments are sorted by p_vaddr, so the first has the minimum p_vaddr. */
+ for (i = 0; i < phdrnum; i++) {
+ if (gelf_getphdr(elf, i, &phdr) && phdr.p_type == PT_LOAD) {
+ retval = phdr.p_vaddr & -getpagesize();
+ break;
+ }
+ }
+
+ elf_end(elf);
+ return retval;
+}
+
+static int unwind_spec_ehframe(struct dso *dso, struct machine *machine,
+ u64 offset, u64 *table_data_offset, u64 *fde_count)
+{
+ struct eh_frame_hdr {
+ unsigned char version;
+ unsigned char eh_frame_ptr_enc;
+ unsigned char fde_count_enc;
+ unsigned char table_enc;
+
+ /*
+ * The rest of the header is variable-length and consists of the
+ * following members:
+ *
+ * encoded_t eh_frame_ptr;
+ * encoded_t fde_count;
+ */
+
+ /* A single encoded pointer should not be more than 8 bytes. */
+ u64 enc[2];
+
+ /*
+ * struct {
+ * encoded_t start_ip;
+ * encoded_t fde_addr;
+ * } binary_search_table[fde_count];
+ */
+ char data[];
+ } __packed hdr;
+ u8 *enc = (u8 *) &hdr.enc;
+ u8 *end = (u8 *) &hdr.data;
+ ssize_t r;
+
+ r = dso__data_read_offset(dso, machine, offset, (u8 *) &hdr, sizeof(hdr));
+ if (r != sizeof(hdr))
+ return -EINVAL;
+
+ /* We dont need eh_frame_ptr, just skip it. */
+ dw_read_encoded_value(enc, end, hdr.eh_frame_ptr_enc);
+
+ *fde_count = dw_read_encoded_value(enc, end, hdr.fde_count_enc);
+ *table_data_offset = enc - (u8 *) &hdr;
+ return 0;
+}
+
+struct read_unwind_spec_eh_frame_maps_cb_args {
+ struct dso *dso;
+ u64 base_addr;
+};
+
+static int read_unwind_spec_eh_frame_maps_cb(struct map *map, void *data)
+{
+
+ struct read_unwind_spec_eh_frame_maps_cb_args *args = data;
+
+ if (map__dso(map) == args->dso && map__start(map) - map__pgoff(map) < args->base_addr)
+ args->base_addr = map__start(map) - map__pgoff(map);
+
+ return 0;
+}
+
+static int elf_section_address_and_offset(int fd, const char *name, u64 *address, u64 *offset)
+{
+ Elf *elf;
+ GElf_Ehdr ehdr;
+ GElf_Shdr shdr;
+ int ret = -1;
+
+ elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
+ if (elf == NULL)
+ return -1;
-struct unwind_libunwind_ops __weak *local_unwind_libunwind_ops;
-struct unwind_libunwind_ops __weak *x86_32_unwind_libunwind_ops;
-struct unwind_libunwind_ops __weak *arm64_unwind_libunwind_ops;
+ if (gelf_getehdr(elf, &ehdr) == NULL)
+ goto out_err;
+
+ if (!elf_section_by_name(elf, &ehdr, &shdr, name, NULL))
+ goto out_err;
+
+ *address = shdr.sh_addr;
+ *offset = shdr.sh_offset;
+ ret = 0;
+out_err:
+ elf_end(elf);
+ return ret;
+}
+
+static int read_unwind_spec_eh_frame(struct dso *dso, struct unwind_info *ui,
+ u64 *table_data, u64 *segbase,
+ u64 *fde_count)
+{
+ struct read_unwind_spec_eh_frame_maps_cb_args args = {
+ .dso = dso,
+ .base_addr = UINT64_MAX,
+ };
+ int ret, fd;
+
+ if (dso__data(dso)->eh_frame_hdr_offset == 0) {
+ if (!dso__data_get_fd(dso, ui->machine, &fd))
+ return -EINVAL;
+
+ /* Check the .eh_frame section for unwinding info */
+ ret = elf_section_address_and_offset(fd, ".eh_frame_hdr",
+ &dso__data(dso)->eh_frame_hdr_addr,
+ &dso__data(dso)->eh_frame_hdr_offset);
+ dso__data(dso)->elf_base_addr = elf_base_address(fd);
+ dso__data_put_fd(dso);
+ if (ret || dso__data(dso)->eh_frame_hdr_offset == 0)
+ return -EINVAL;
+ }
+
+ maps__for_each_map(thread__maps(ui->thread), read_unwind_spec_eh_frame_maps_cb, &args);
+
+ args.base_addr -= dso__data(dso)->elf_base_addr;
+ /* Address of .eh_frame_hdr */
+ *segbase = args.base_addr + dso__data(dso)->eh_frame_hdr_addr;
+ ret = unwind_spec_ehframe(dso, ui->machine, dso__data(dso)->eh_frame_hdr_offset,
+ table_data, fde_count);
+ if (ret)
+ return ret;
+ /* binary_search_table offset plus .eh_frame_hdr address */
+ *table_data += *segbase;
+ return 0;
+}
+
+static u64 elf_section_offset(int fd, const char *name)
+{
+ u64 address, offset = 0;
+
+ if (elf_section_address_and_offset(fd, name, &address, &offset))
+ return 0;
+
+ return offset;
+}
+
+static int read_unwind_spec_debug_frame(struct dso *dso,
+ struct machine *machine, u64 *offset)
+{
+ int fd;
+ u64 ofs = dso__data(dso)->debug_frame_offset;
+
+ /* debug_frame can reside in:
+ * - dso
+ * - debug pointed by symsrc_filename
+ * - gnu_debuglink, which doesn't necessary
+ * has to be pointed by symsrc_filename
+ */
+ if (ofs == 0) {
+ if (dso__data_get_fd(dso, machine, &fd)) {
+ ofs = elf_section_offset(fd, ".debug_frame");
+ dso__data_put_fd(dso);
+ }
+
+ if (ofs <= 0) {
+ fd = open(dso__symsrc_filename(dso), O_RDONLY);
+ if (fd >= 0) {
+ ofs = elf_section_offset(fd, ".debug_frame");
+ close(fd);
+ }
+ }
+
+ if (ofs <= 0) {
+ char *debuglink = malloc(PATH_MAX);
+ int ret = 0;
+
+ if (debuglink == NULL) {
+ pr_err("unwind: Can't read unwind spec debug frame.\n");
+ return -ENOMEM;
+ }
+
+ ret = dso__read_binary_type_filename(
+ dso, DSO_BINARY_TYPE__DEBUGLINK,
+ machine->root_dir, debuglink, PATH_MAX);
+ if (!ret) {
+ fd = open(debuglink, O_RDONLY);
+ if (fd >= 0) {
+ ofs = elf_section_offset(fd,
+ ".debug_frame");
+ close(fd);
+ }
+ }
+ if (ofs > 0) {
+ if (dso__symsrc_filename(dso) != NULL) {
+ pr_warning(
+ "%s: overwrite symsrc(%s,%s)\n",
+ __func__,
+ dso__symsrc_filename(dso),
+ debuglink);
+ dso__free_symsrc_filename(dso);
+ }
+ dso__set_symsrc_filename(dso, debuglink);
+ } else {
+ free(debuglink);
+ }
+ }
+
+ dso__data(dso)->debug_frame_offset = ofs;
+ }
+
+ *offset = ofs;
+ if (*offset)
+ return 0;
+
+ return -EINVAL;
+}
+
+static struct map *find_map(uint64_t ip, struct unwind_info *ui)
+{
+ struct addr_location al;
+ struct map *ret;
+
+ addr_location__init(&al);
+ thread__find_map(ui->thread, PERF_RECORD_MISC_USER, ip, &al);
+ ret = map__get(al.map);
+ addr_location__exit(&al);
+ return ret;
+}
+
+static int elf_is_exec(int fd, const char *name)
+{
+ Elf *elf;
+ GElf_Ehdr ehdr;
+ int retval = 0;
+
+ elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
+ if (elf == NULL)
+ return 0;
+ if (gelf_getehdr(elf, &ehdr) == NULL)
+ goto out;
+
+ retval = (ehdr.e_type == ET_EXEC);
+
+out:
+ elf_end(elf);
+ pr_debug3("unwind: elf_is_exec(%s): %d\n", name, retval);
+ return retval;
+}
+
+int __libunwind__find_proc_info(void *as, uint64_t ip, void *pi, int need_unwind_info, void *arg)
+{
+ struct unwind_info *ui = arg;
+ struct map *map;
+ struct dso *dso;
+ u64 table_data, segbase, fde_count;
+ int ret = -EINVAL;
+
+ map = find_map(ip, ui);
+ if (!map)
+ return -EINVAL;
+
+ dso = map__dso(map);
+ if (!dso) {
+ map__put(map);
+ return -EINVAL;
+ }
+
+ pr_debug3("unwind: find_proc_info dso %s\n", dso__name(dso));
+
+ /* Check the .eh_frame section for unwinding info */
+ if (!read_unwind_spec_eh_frame(dso, ui, &table_data, &segbase, &fde_count)) {
+ struct table_entry {
+ u32 start_ip_offset;
+ u32 fde_offset;
+ };
+ struct libarch_unwind__dyn_info di = {
+ .start_ip = map__start(map),
+ .end_ip = map__end(map),
+ .segbase = segbase,
+ .table_data = table_data,
+ .table_len = fde_count * sizeof(struct table_entry) / ui->unw_word_t_size,
+ };
+
+ ret = libunwind_arch__dwarf_search_unwind_table(ui->e_machine, as, ip, &di, pi,
+ need_unwind_info, arg);
+ }
+
+ /* Check the .debug_frame section for unwinding info */
+ if (ret < 0 && !read_unwind_spec_debug_frame(dso, ui->machine, &segbase)) {
+ int fd;
+ u64 start = map__start(map);
+ u64 base = start;
+ const char *symfile;
+ struct libarch_unwind__dyn_info di = {};
+
+ if (dso__data_get_fd(dso, ui->machine, &fd)) {
+ if (elf_is_exec(fd, dso__name(dso)))
+ base = 0;
+ dso__data_put_fd(dso);
+ }
+
+ symfile = dso__symsrc_filename(dso) ?: dso__name(dso);
+
+ if (libunwind_arch__dwarf_find_debug_frame(ui->e_machine, /*found=*/0, &di, ip,
+ base, symfile, start, map__end(map))) {
+ ret = libunwind_arch__dwarf_search_unwind_table(ui->e_machine, as, ip, &di, pi,
+ need_unwind_info, arg);
+ }
+ }
+ map__put(map);
+ return ret;
+}
+
+static int access_dso_mem(struct unwind_info *ui, uint64_t addr, void *data_word)
+{
+ struct map *map;
+ struct dso *dso;
+ ssize_t size;
+
+ map = find_map(addr, ui);
+ if (!map) {
+ pr_debug("unwind: no map for %lx\n", (unsigned long)addr);
+ return -1;
+ }
+
+ dso = map__dso(map);
+
+ if (!dso) {
+ map__put(map);
+ return -1;
+ }
+
+ size = dso__data_read_addr(dso, map, ui->machine,
+ addr,
+ (u8 *) data_word,
+ ui->unw_word_t_size);
+ map__put(map);
+ return !((size_t)size == ui->unw_word_t_size);
+}
+
+int __libunwind__access_mem(void *as __maybe_unused, uint64_t addr, void *valp_word,
+ int __write, void *arg)
+{
+ struct unwind_info *ui = arg;
+ struct stack_dump *stack = &ui->sample->user_stack;
+ u64 start, end;
+ int offset;
+ int ret;
+
+ /* Don't support write, probably not needed. */
+ if (__write || !stack || !ui->sample->user_regs || !ui->sample->user_regs->regs) {
+ uint64_t zero = 0;
+
+ memcpy(valp_word, &zero, ui->unw_word_t_size);
+ return 0;
+ }
+
+ ret = perf_reg_value(&start, perf_sample__user_regs(ui->sample),
+ perf_arch_reg_sp(ui->e_machine));
+ if (ret)
+ return ret;
+
+ end = start + stack->size;
+
+ /* Check overflow. */
+ if (addr + ui->unw_word_t_size < addr)
+ return -EINVAL;
+
+ if (addr < start || addr + ui->unw_word_t_size >= end) {
+ ret = access_dso_mem(ui, addr, valp_word);
+ if (ret) {
+ pr_debug3("unwind: access_mem %p not inside range"
+ " 0x%" PRIx64 "-0x%" PRIx64 "\n",
+ (void *) (uintptr_t) addr, start, end);
+ memset(valp_word, 0, ui->unw_word_t_size);
+ return ret;
+ }
+ return 0;
+ }
+
+ offset = addr - start;
+ memcpy(valp_word, &stack->data[offset], ui->unw_word_t_size);
+ pr_debug3("unwind: access_mem addr %p val %lx, offset %d\n",
+ (void *) (uintptr_t) addr, *((unsigned long *)valp_word), offset);
+ return 0;
+}
+
+int __libunwind__access_reg(void *as __maybe_unused, int regnum, void *valp_word, int __write,
+ void *arg)
+{
+ struct unwind_info *ui = arg;
+ int id, ret;
+ u64 val;
+
+ /* Don't support write, I suspect we don't need it. */
+ if (__write) {
+ pr_err("unwind: access_reg w %d\n", regnum);
+ return 0;
+ }
+
+ if (!ui->sample->user_regs || !ui->sample->user_regs->regs) {
+ memset(valp_word, 0, ui->unw_word_t_size);
+ return 0;
+ }
+
+ id = get_perf_regnum_for_unw_regnum(ui->e_machine, regnum);
+ if (id < 0)
+ return -EINVAL;
+
+ ret = perf_reg_value(&val, perf_sample__user_regs(ui->sample), id);
+ if (ret) {
+ if (!ui->best_effort)
+ pr_err("unwind: can't read reg %d\n", regnum);
+ return ret;
+ }
+
+ memcpy(valp_word, &val, ui->unw_word_t_size);
+ pr_debug3("unwind: reg %d, val %lx\n", regnum, val);
+ return 0;
+}
int unwind__prepare_access(struct maps *maps, uint16_t e_machine)
{
- struct unwind_libunwind_ops *ops = local_unwind_libunwind_ops;
+ void *addr_space;
if (!dwarf_callchain_users)
return 0;
@@ -27,25 +561,16 @@ int unwind__prepare_access(struct maps *maps, uint16_t e_machine)
return 0;
}
- if (e_machine != EM_HOST) {
- /* If not live/local mode. */
- switch (e_machine) {
- case EM_386:
- ops = x86_32_unwind_libunwind_ops;
- break;
- case EM_AARCH64:
- ops = arm64_unwind_libunwind_ops;
- break;
- default:
- pr_warning_once("unwind: ELF machine type %d is not supported\n",
- e_machine);
- return 0;
- }
- }
- maps__set_unwind_libunwind_ops(maps, ops);
maps__set_e_machine(maps, e_machine);
+ addr_space = libunwind_arch__create_addr_space(e_machine);
+
+ maps__set_addr_space(maps, addr_space);
+ if (!addr_space) {
+ pr_err("unwind: Can't create unwind address space.\n");
+ return -ENOMEM;
+ }
- return maps__unwind_libunwind_ops(maps)->prepare_access(maps);
+ return 0;
}
void unwind__flush_access(struct maps *maps)
@@ -58,14 +583,81 @@ void unwind__finish_access(struct maps *maps)
libunwind_arch__finish_access(maps);
}
+static int entry(uint64_t ip, struct thread *thread, unwind_entry_cb_t cb, void *arg)
+{
+ struct unwind_entry e;
+ struct addr_location al;
+ int ret;
+
+ addr_location__init(&al);
+ e.ms.sym = thread__find_symbol(thread, PERF_RECORD_MISC_USER, ip, &al);
+ e.ip = ip;
+ e.ms.map = al.map;
+ e.ms.thread = thread__get(al.thread);
+
+ pr_debug("unwind: %s:ip = 0x%" PRIx64 " (0x%" PRIx64 ")\n",
+ al.sym ? al.sym->name : "''",
+ ip,
+ al.map ? map__map_ip(al.map, ip) : (u64) 0);
+
+ ret = cb(&e, arg);
+ addr_location__exit(&al);
+ return ret;
+}
+
int libunwind__get_entries(unwind_entry_cb_t cb, void *arg,
struct thread *thread,
- struct perf_sample *data, int max_stack,
+ struct perf_sample *sample, int max_stack,
bool best_effort)
{
- const struct unwind_libunwind_ops *ops = maps__unwind_libunwind_ops(thread__maps(thread));
+ struct unwind_info *ui;
+ uint64_t first_ip;
+ int ret, i = 0;
+ uint16_t e_machine;
- if (ops)
- return ops->get_entries(cb, arg, thread, data, max_stack, best_effort);
- return 0;
+ if (!sample->user_regs || !sample->user_regs->regs)
+ return -EINVAL;
+
+ if (max_stack <= 0)
+ return -EINVAL;
+
+ if (!thread) {
+ pr_warning_once("WARNING: thread is NULL");
+ return -EINVAL;
+ }
+
+ e_machine = thread__e_machine(thread, /*machine=*/NULL, /*e_flags=*/NULL);
+ ret = perf_reg_value(&first_ip, perf_sample__user_regs(sample),
+ perf_arch_reg_ip(e_machine));
+ if (ret)
+ return ret;
+
+ if (max_stack == 1) {
+ /* Special case for a single entry. */
+ return entry(first_ip, thread, cb, arg);
+ }
+
+ ui = libunwind_arch_unwind_info__new(thread, sample, max_stack, best_effort, e_machine, first_ip);
+ if (!ui)
+ return -1;
+
+ do {
+ ret = libunwind_arch__unwind_step(ui);
+ if (ret < 0)
+ goto out;
+
+ } while (ret);
+
+ /*
+ * Display what we got based on the order setup.
+ */
+ for (i = 0; i < ui->cur_ip; i++) {
+ int j = callchain_param.order == ORDER_CALLEE ? i : ui->cur_ip - i - 1;
+
+ if (ui->ips[j])
+ ret = entry(ui->ips[j], thread, cb, arg);
+ }
+out:
+ libunwind_arch_unwind_info__delete(ui);
+ return ret;
}
diff --git a/tools/perf/util/unwind.h b/tools/perf/util/unwind.h
index 09fc60df262d..e06c090c6685 100644
--- a/tools/perf/util/unwind.h
+++ b/tools/perf/util/unwind.h
@@ -19,13 +19,6 @@ struct unwind_entry {
typedef int (*unwind_entry_cb_t)(struct unwind_entry *entry, void *arg);
-struct unwind_libunwind_ops {
- int (*prepare_access)(struct maps *maps);
- int (*get_entries)(unwind_entry_cb_t cb, void *arg,
- struct thread *thread,
- struct perf_sample *data, int max_stack, bool best_effort);
-};
-
int unwind__configure(const char *var, const char *value, void *cb);
int unwind__option(const struct option *opt, const char *arg, int unset);
--
2.53.0.473.g4a7958ca14-goog
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH v2 8/8] perf unwind-libunwind: Add RISC-V libunwind support
2026-03-05 22:19 ` [PATCH v2 0/8] perf libunwind multiple remote support Ian Rogers
` (6 preceding siblings ...)
2026-03-05 22:19 ` [PATCH v2 7/8] perf unwind-libunwind: Remove libunwind-local Ian Rogers
@ 2026-03-05 22:19 ` Ian Rogers
2026-03-19 21:39 ` [PATCH v2 0/8] perf libunwind multiple remote support Namhyung Kim
8 siblings, 0 replies; 27+ messages in thread
From: Ian Rogers @ 2026-03-05 22:19 UTC (permalink / raw)
To: irogers
Cc: 9erthalion6, acme, adrian.hunter, alex, alexander.shishkin,
andrew.jones, aou, atrajeev, blakejones, ctshao, dapeng1.mi,
howardchu95, james.clark, john.g.garry, jolsa, leo.yan,
libunwind-devel, linux-arm-kernel, linux-kernel, linux-perf-users,
linux-riscv, mingo, namhyung, palmer, peterz, pjw, shimin.guo,
tglozar, tmricht, will, yuzhuo
Add a RISC-V implementation for unwinding.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/libunwind-arch/Build | 1 +
.../perf/util/libunwind-arch/libunwind-arch.c | 21 ++
.../perf/util/libunwind-arch/libunwind-arch.h | 22 ++
.../util/libunwind-arch/libunwind-riscv.c | 297 ++++++++++++++++++
4 files changed, 341 insertions(+)
create mode 100644 tools/perf/util/libunwind-arch/libunwind-riscv.c
diff --git a/tools/perf/util/libunwind-arch/Build b/tools/perf/util/libunwind-arch/Build
index 87fd657a3248..80d3571918b1 100644
--- a/tools/perf/util/libunwind-arch/Build
+++ b/tools/perf/util/libunwind-arch/Build
@@ -5,6 +5,7 @@ perf-util-$(CONFIG_LIBUNWIND) += libunwind-loongarch.o
perf-util-$(CONFIG_LIBUNWIND) += libunwind-mips.o
perf-util-$(CONFIG_LIBUNWIND) += libunwind-ppc32.o
perf-util-$(CONFIG_LIBUNWIND) += libunwind-ppc64.o
+perf-util-$(CONFIG_LIBUNWIND) += libunwind-riscv.o
perf-util-$(CONFIG_LIBUNWIND) += libunwind-s390.o
perf-util-$(CONFIG_LIBUNWIND) += libunwind-i386.o
perf-util-$(CONFIG_LIBUNWIND) += libunwind-x86_64.o
diff --git a/tools/perf/util/libunwind-arch/libunwind-arch.c b/tools/perf/util/libunwind-arch/libunwind-arch.c
index 8539b4233df4..9a74cf3c8729 100644
--- a/tools/perf/util/libunwind-arch/libunwind-arch.c
+++ b/tools/perf/util/libunwind-arch/libunwind-arch.c
@@ -20,6 +20,8 @@ int get_perf_regnum_for_unw_regnum(unsigned int e_machine, int unw_regnum)
return __get_perf_regnum_for_unw_regnum_ppc32(unw_regnum);
case EM_PPC64:
return __get_perf_regnum_for_unw_regnum_ppc64(unw_regnum);
+ case EM_RISCV:
+ return __get_perf_regnum_for_unw_regnum_riscv(unw_regnum);
case EM_S390:
return __get_perf_regnum_for_unw_regnum_s390(unw_regnum);
case EM_386:
@@ -58,6 +60,9 @@ void libunwind_arch__flush_access(struct maps *maps)
case EM_PPC64:
__libunwind_arch__flush_access_ppc64(maps);
break;
+ case EM_RISCV:
+ __libunwind_arch__flush_access_riscv(maps);
+ break;
case EM_S390:
__libunwind_arch__flush_access_s390(maps);
break;
@@ -98,6 +103,9 @@ void libunwind_arch__finish_access(struct maps *maps)
case EM_PPC64:
__libunwind_arch__finish_access_ppc64(maps);
break;
+ case EM_RISCV:
+ __libunwind_arch__finish_access_riscv(maps);
+ break;
case EM_S390:
__libunwind_arch__finish_access_s390(maps);
break;
@@ -128,6 +136,8 @@ void *libunwind_arch__create_addr_space(unsigned int e_machine)
return __libunwind_arch__create_addr_space_ppc32();
case EM_PPC64:
return __libunwind_arch__create_addr_space_ppc64();
+ case EM_RISCV:
+ return __libunwind_arch__create_addr_space_riscv();
case EM_S390:
return __libunwind_arch__create_addr_space_s390();
case EM_386:
@@ -167,6 +177,9 @@ int libunwind_arch__dwarf_search_unwind_table(unsigned int e_machine,
case EM_PPC64:
return __libunwind_arch__dwarf_search_unwind_table_ppc64(as, ip, di, pi,
need_unwind_info, arg);
+ case EM_RISCV:
+ return __libunwind_arch__dwarf_search_unwind_table_riscv(as, ip, di, pi,
+ need_unwind_info, arg);
case EM_S390:
return __libunwind_arch__dwarf_search_unwind_table_s390(as, ip, di, pi,
need_unwind_info, arg);
@@ -211,6 +224,9 @@ int libunwind_arch__dwarf_find_debug_frame(unsigned int e_machine,
case EM_PPC64:
return __libunwind_arch__dwarf_find_debug_frame_ppc64(found, di_debug, ip, segbase,
obj_name, start, end);
+ case EM_RISCV:
+ return __libunwind_arch__dwarf_find_debug_frame_riscv(found, di_debug, ip, segbase,
+ obj_name, start, end);
case EM_S390:
return __libunwind_arch__dwarf_find_debug_frame_s390(found, di_debug, ip, segbase,
obj_name, start, end);
@@ -250,6 +266,9 @@ struct unwind_info *libunwind_arch_unwind_info__new(struct thread *thread,
case EM_PPC64:
return __libunwind_arch_unwind_info__new_ppc64(thread, sample, max_stack,
best_effort, first_ip);
+ case EM_RISCV:
+ return __libunwind_arch_unwind_info__new_riscv(thread, sample, max_stack,
+ best_effort, first_ip);
case EM_S390:
return __libunwind_arch_unwind_info__new_s390(thread, sample, max_stack,
best_effort, first_ip);
@@ -285,6 +304,8 @@ int libunwind_arch__unwind_step(struct unwind_info *ui)
return __libunwind_arch__unwind_step_ppc32(ui);
case EM_PPC64:
return __libunwind_arch__unwind_step_ppc64(ui);
+ case EM_RISCV:
+ return __libunwind_arch__unwind_step_riscv(ui);
case EM_S390:
return __libunwind_arch__unwind_step_s390(ui);
case EM_386:
diff --git a/tools/perf/util/libunwind-arch/libunwind-arch.h b/tools/perf/util/libunwind-arch/libunwind-arch.h
index 2bf7fc33313b..74a09cd58f38 100644
--- a/tools/perf/util/libunwind-arch/libunwind-arch.h
+++ b/tools/perf/util/libunwind-arch/libunwind-arch.h
@@ -39,6 +39,7 @@ int __get_perf_regnum_for_unw_regnum_loongarch(int unw_regnum);
int __get_perf_regnum_for_unw_regnum_mips(int unw_regnum);
int __get_perf_regnum_for_unw_regnum_ppc32(int unw_regnum);
int __get_perf_regnum_for_unw_regnum_ppc64(int unw_regnum);
+int __get_perf_regnum_for_unw_regnum_riscv(int unw_regnum);
int __get_perf_regnum_for_unw_regnum_s390(int unw_regnum);
int __get_perf_regnum_for_unw_regnum_i386(int unw_regnum);
int __get_perf_regnum_for_unw_regnum_x86_64(int unw_regnum);
@@ -50,6 +51,7 @@ void __libunwind_arch__flush_access_loongarch(struct maps *maps);
void __libunwind_arch__flush_access_mips(struct maps *maps);
void __libunwind_arch__flush_access_ppc32(struct maps *maps);
void __libunwind_arch__flush_access_ppc64(struct maps *maps);
+void __libunwind_arch__flush_access_riscv(struct maps *maps);
void __libunwind_arch__flush_access_s390(struct maps *maps);
void __libunwind_arch__flush_access_i386(struct maps *maps);
void __libunwind_arch__flush_access_x86_64(struct maps *maps);
@@ -61,6 +63,7 @@ void __libunwind_arch__finish_access_loongarch(struct maps *maps);
void __libunwind_arch__finish_access_mips(struct maps *maps);
void __libunwind_arch__finish_access_ppc32(struct maps *maps);
void __libunwind_arch__finish_access_ppc64(struct maps *maps);
+void __libunwind_arch__finish_access_riscv(struct maps *maps);
void __libunwind_arch__finish_access_s390(struct maps *maps);
void __libunwind_arch__finish_access_i386(struct maps *maps);
void __libunwind_arch__finish_access_x86_64(struct maps *maps);
@@ -72,6 +75,7 @@ void *__libunwind_arch__create_addr_space_loongarch(void);
void *__libunwind_arch__create_addr_space_mips(void);
void *__libunwind_arch__create_addr_space_ppc32(void);
void *__libunwind_arch__create_addr_space_ppc64(void);
+void *__libunwind_arch__create_addr_space_riscv(void);
void *__libunwind_arch__create_addr_space_s390(void);
void *__libunwind_arch__create_addr_space_i386(void);
void *__libunwind_arch__create_addr_space_x86_64(void);
@@ -111,6 +115,11 @@ int __libunwind_arch__dwarf_search_unwind_table_ppc64(void *as, uint64_t ip,
void *pi,
int need_unwind_info,
void *arg);
+int __libunwind_arch__dwarf_search_unwind_table_riscv(void *as, uint64_t ip,
+ struct libarch_unwind__dyn_info *di,
+ void *pi,
+ int need_unwind_info,
+ void *arg);
int __libunwind_arch__dwarf_search_unwind_table_s390(void *as, uint64_t ip,
struct libarch_unwind__dyn_info *di,
void *pi,
@@ -176,6 +185,13 @@ int __libunwind_arch__dwarf_find_debug_frame_ppc64(int found,
const char *obj_name,
uint64_t start,
uint64_t end);
+int __libunwind_arch__dwarf_find_debug_frame_riscv(int found,
+ struct libarch_unwind__dyn_info *di_debug,
+ uint64_t ip,
+ uint64_t segbase,
+ const char *obj_name,
+ uint64_t start,
+ uint64_t end);
int __libunwind_arch__dwarf_find_debug_frame_s390(int found,
struct libarch_unwind__dyn_info *di_debug,
uint64_t ip,
@@ -236,6 +252,11 @@ struct unwind_info *__libunwind_arch_unwind_info__new_ppc64(struct thread *threa
int max_stack,
bool best_effort,
uint64_t first_ip);
+struct unwind_info *__libunwind_arch_unwind_info__new_riscv(struct thread *thread,
+ struct perf_sample *sample,
+ int max_stack,
+ bool best_effort,
+ uint64_t first_ip);
struct unwind_info *__libunwind_arch_unwind_info__new_s390(struct thread *thread,
struct perf_sample *sample,
int max_stack,
@@ -266,6 +287,7 @@ int __libunwind_arch__unwind_step_loongarch(struct unwind_info *ui);
int __libunwind_arch__unwind_step_mips(struct unwind_info *ui);
int __libunwind_arch__unwind_step_ppc32(struct unwind_info *ui);
int __libunwind_arch__unwind_step_ppc64(struct unwind_info *ui);
+int __libunwind_arch__unwind_step_riscv(struct unwind_info *ui);
int __libunwind_arch__unwind_step_s390(struct unwind_info *ui);
int __libunwind_arch__unwind_step_i386(struct unwind_info *ui);
int __libunwind_arch__unwind_step_x86_64(struct unwind_info *ui);
diff --git a/tools/perf/util/libunwind-arch/libunwind-riscv.c b/tools/perf/util/libunwind-arch/libunwind-riscv.c
new file mode 100644
index 000000000000..a70a2ea96644
--- /dev/null
+++ b/tools/perf/util/libunwind-arch/libunwind-riscv.c
@@ -0,0 +1,297 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "libunwind-arch.h"
+#include "../debug.h"
+#include "../maps.h"
+#include "../thread.h"
+#include "../../../arch/riscv/include/uapi/asm/perf_regs.h"
+#include <linux/compiler.h>
+#include <linux/kernel.h>
+#include <linux/zalloc.h>
+#include <elf.h>
+#include <errno.h>
+
+#ifdef HAVE_LIBUNWIND_RISCV_SUPPORT
+#include <libunwind-riscv.h>
+#endif
+
+int __get_perf_regnum_for_unw_regnum_riscv(int unw_regnum __maybe_unused)
+{
+#ifndef HAVE_LIBUNWIND_RISCV_SUPPORT
+ return -EINVAL;
+#else
+ switch (unw_regnum) {
+ case UNW_RISCV_X1 ... UNW_RISCV_X31:
+ return unw_regnum - UNW_RISCV_X1 + PERF_REG_RISCV_RA;
+ case UNW_RISCV_PC:
+ return PERF_REG_RISCV_PC;
+ default:
+ pr_err("unwind: invalid reg id %d\n", unw_regnum);
+ return -EINVAL;
+ }
+#endif // HAVE_LIBUNWIND_RISCV_SUPPORT
+}
+
+void __libunwind_arch__flush_access_riscv(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_RISCV_SUPPORT
+ unw_flush_cache(maps__addr_space(maps), 0, 0);
+#endif
+}
+
+void __libunwind_arch__finish_access_riscv(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_RISCV_SUPPORT
+ unw_destroy_addr_space(maps__addr_space(maps));
+#endif
+}
+
+#ifdef HAVE_LIBUNWIND_RISCV_SUPPORT
+static int find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
+ int need_unwind_info, void *arg)
+{
+ return __libunwind__find_proc_info(as, ip, pi, need_unwind_info, arg, sizeof(unw_word_t));
+}
+
+static void put_unwind_info(unw_addr_space_t __maybe_unused as,
+ unw_proc_info_t *pi __maybe_unused,
+ void *arg __maybe_unused)
+{
+ pr_debug("unwind: put_unwind_info called\n");
+}
+
+static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as,
+ unw_word_t __maybe_unused *dil_addr,
+ void __maybe_unused *arg)
+{
+ return -UNW_ENOINFO;
+}
+
+static int access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *valp,
+ int __write, void *arg)
+{
+ return __libunwind__access_mem(as, addr, valp, __write, arg, sizeof(unw_word_t));
+}
+
+static int access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t *valp,
+ int __write, void *arg)
+{
+ return __libunwind__access_reg(as, regnum, valp, __write, arg, sizeof(unw_word_t));
+}
+
+static int access_fpreg(unw_addr_space_t __maybe_unused as,
+ unw_regnum_t __maybe_unused num,
+ unw_fpreg_t __maybe_unused *val,
+ int __maybe_unused __write,
+ void __maybe_unused *arg)
+{
+ pr_err("unwind: access_fpreg unsupported\n");
+ return -UNW_EINVAL;
+}
+
+static int resume(unw_addr_space_t __maybe_unused as,
+ unw_cursor_t __maybe_unused *cu,
+ void __maybe_unused *arg)
+{
+ pr_err("unwind: resume unsupported\n");
+ return -UNW_EINVAL;
+}
+
+static int get_proc_name(unw_addr_space_t __maybe_unused as,
+ unw_word_t __maybe_unused addr,
+ char __maybe_unused *bufp, size_t __maybe_unused buf_len,
+ unw_word_t __maybe_unused *offp, void __maybe_unused *arg)
+{
+ pr_err("unwind: get_proc_name unsupported\n");
+ return -UNW_EINVAL;
+}
+#endif
+
+void *__libunwind_arch__create_addr_space_riscv(void)
+{
+#ifdef HAVE_LIBUNWIND_RISCV_SUPPORT
+ static unw_accessors_t accessors = {
+ .find_proc_info = find_proc_info,
+ .put_unwind_info = put_unwind_info,
+ .get_dyn_info_list_addr = get_dyn_info_list_addr,
+ .access_mem = access_mem,
+ .access_reg = access_reg,
+ .access_fpreg = access_fpreg,
+ .resume = resume,
+ .get_proc_name = get_proc_name,
+ };
+ unw_addr_space_t addr_space;
+
+ addr_space = unw_create_addr_space(&accessors, /*byte_order=*/0);
+ unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL);
+ return addr_space;
+#else
+ return NULL;
+#endif
+}
+
+#ifdef HAVE_LIBUNWIND_RISCV_SUPPORT
+extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,
+ unw_word_t ip,
+ unw_dyn_info_t *di,
+ unw_proc_info_t *pi,
+ int need_unwind_info, void *arg);
+#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table)
+#endif
+
+int __libunwind_arch__dwarf_search_unwind_table_riscv(void *as __maybe_unused,
+ uint64_t ip __maybe_unused,
+ struct libarch_unwind__dyn_info *_di __maybe_unused,
+ void *pi __maybe_unused,
+ int need_unwind_info __maybe_unused,
+ void *arg __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_RISCV_SUPPORT
+ unw_dyn_info_t di = {
+ .format = UNW_INFO_FORMAT_REMOTE_TABLE,
+ .start_ip = _di->start_ip,
+ .end_ip = _di->end_ip,
+ .u = {
+ .rti = {
+ .segbase = _di->segbase,
+ .table_data = _di->table_data,
+ .table_len = _di->table_len,
+ },
+ },
+ };
+ int ret = dwarf_search_unwind_table(as, ip, &di, pi, need_unwind_info, arg);
+
+ _di->start_ip = di.start_ip;
+ _di->end_ip = di.end_ip;
+ _di->segbase = di.u.rti.segbase;
+ _di->table_data = di.u.rti.table_data;
+ _di->table_len = di.u.rti.table_len;
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
+
+#if defined(HAVE_LIBUNWIND_RISCV_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_RISCV)
+extern int UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug,
+ unw_word_t ip,
+ unw_word_t segbase,
+ const char *obj_name, unw_word_t start,
+ unw_word_t end);
+#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame)
+#endif
+
+int __libunwind_arch__dwarf_find_debug_frame_riscv(int found __maybe_unused,
+ struct libarch_unwind__dyn_info *_di __maybe_unused,
+ uint64_t ip __maybe_unused,
+ uint64_t segbase __maybe_unused,
+ const char *obj_name __maybe_unused,
+ uint64_t start __maybe_unused,
+ uint64_t end __maybe_unused)
+{
+#if defined(HAVE_LIBUNWIND_RISCV_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_RISCV)
+ unw_dyn_info_t di = {
+ .format = UNW_INFO_FORMAT_REMOTE_TABLE,
+ .start_ip = _di->start_ip,
+ .end_ip = _di->end_ip,
+ .u = {
+ .rti = {
+ .segbase = _di->segbase,
+ .table_data = _di->table_data,
+ .table_len = _di->table_len,
+ },
+ },
+ };
+ int ret = dwarf_find_debug_frame(found, &di, ip, segbase, obj_name, start, end);
+
+ _di->start_ip = di.start_ip;
+ _di->end_ip = di.end_ip;
+ _di->segbase = di.u.rti.segbase;
+ _di->table_data = di.u.rti.table_data;
+ _di->table_len = di.u.rti.table_len;
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
+
+struct unwind_info *__libunwind_arch_unwind_info__new_riscv(struct thread *thread __maybe_unused,
+ struct perf_sample *sample __maybe_unused,
+ int max_stack __maybe_unused,
+ bool best_effort __maybe_unused,
+ uint64_t first_ip __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_RISCV_SUPPORT
+ struct arch_unwind_info {
+ struct unwind_info ui;
+ unw_cursor_t _cursor;
+ uint64_t _ips[];
+ };
+
+ struct maps *maps = thread__maps(thread);
+ void *addr_space = maps__addr_space(maps);
+ struct arch_unwind_info *ui;
+ int ret;
+
+ if (addr_space == NULL)
+ return NULL;
+
+ ui = zalloc(sizeof(*ui) + sizeof(ui->_ips[0]) * max_stack);
+ if (!ui)
+ return NULL;
+
+ ui->ui.machine = maps__machine(maps);
+ ui->ui.thread = thread;
+ ui->ui.sample = sample;
+ ui->ui.cursor = &ui->_cursor;
+ ui->ui.ips = &ui->_ips[0];
+ ui->ui.ips[0] = first_ip;
+ ui->ui.cur_ip = 1;
+ ui->ui.max_ips = max_stack;
+ ui->ui.unw_word_t_size = sizeof(unw_word_t);
+ ui->ui.e_machine = EM_RISCV;
+ ui->ui.best_effort = best_effort;
+
+ ret = unw_init_remote(&ui->_cursor, addr_space, &ui->ui);
+ if (ret) {
+ if (!best_effort)
+ pr_err("libunwind: %s\n", unw_strerror(ret));
+ free(ui);
+ return NULL;
+ }
+
+ return &ui->ui;
+#else
+ return NULL;
+#endif
+}
+
+int __libunwind_arch__unwind_step_riscv(struct unwind_info *ui __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_RISCV_SUPPORT
+ int ret;
+
+ if (ui->cur_ip >= ui->max_ips)
+ return -1;
+
+ ret = unw_step(ui->cursor);
+ if (ret > 0) {
+ uint64_t ip;
+
+ unw_get_reg(ui->cursor, UNW_REG_IP, &ip);
+
+ if (unw_is_signal_frame(ui->cursor) <= 0) {
+ /*
+ * Decrement the IP for any non-activation frames. This
+ * is required to properly find the srcline for caller
+ * frames. See also the documentation for
+ * dwfl_frame_pc(), which this code tries to replicate.
+ */
+ --ip;
+ }
+ ui->ips[ui->cur_ip++] = ip;
+ }
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
--
2.53.0.473.g4a7958ca14-goog
^ permalink raw reply related [flat|nested] 27+ messages in thread
* Re: [PATCH v2 0/8] perf libunwind multiple remote support
2026-03-05 22:19 ` [PATCH v2 0/8] perf libunwind multiple remote support Ian Rogers
` (7 preceding siblings ...)
2026-03-05 22:19 ` [PATCH v2 8/8] perf unwind-libunwind: Add RISC-V libunwind support Ian Rogers
@ 2026-03-19 21:39 ` Namhyung Kim
2026-03-21 3:06 ` Ian Rogers
2026-03-21 23:42 ` [PATCH v1 0/2] perf build: Remove libunwind support Ian Rogers
8 siblings, 2 replies; 27+ messages in thread
From: Namhyung Kim @ 2026-03-19 21:39 UTC (permalink / raw)
To: Ian Rogers
Cc: 9erthalion6, acme, adrian.hunter, alex, alexander.shishkin,
andrew.jones, aou, atrajeev, blakejones, ctshao, dapeng1.mi,
howardchu95, james.clark, john.g.garry, jolsa, leo.yan,
libunwind-devel, linux-arm-kernel, linux-kernel, linux-perf-users,
linux-riscv, mingo, palmer, peterz, pjw, shimin.guo, tglozar,
tmricht, will, yuzhuo
Hi Ian,
On Thu, Mar 05, 2026 at 02:19:19PM -0800, Ian Rogers wrote:
> Fix the libunwind build for when libdw and libunwind are feature
> detected, currently failing with a duplicate symbol.
>
> Refactor the libunwind support so that whenever a remote target is
> available, perf functions using the ELF machine can use that remote
> target regardless of what the host/local machine is. Migrate existing
> libunwind supported architectures like powerpc, arm64 and loongarch so
> that they can work in a cross-architecture way. Add support for
> RISC-V. Make the code more regular in function names, etc. and avoid
> including a C-file. This increases the lines of code. It is similar in
> style to the unwind-libdw implementation. It is hoped that the more
> uniform nature of the code with help with refactoring the perf
> registers for SIMD/APX support.
>
> Aside from local host testing these patches are under tested, in part
> as I'm failing to see how to build libunwind with support for multiple
> remote targets. Please could I get help in testing.
It seems libunwind project is not actively maintained. Assuming libdw
unwinding is performant enough, we may want to get rid of libunwind
support.
Thanks,
Namhyung
>
> v2: Move two fixes patches to position 1 and 2 in the series. Fix
> struct naming inconsistency, Andrew Jones
> <andrew.jones@oss.qualcomm.com>. Fix other inconsistencies and
> potential non-x86 build issues.
>
> v1: https://lore.kernel.org/lkml/20260224142938.26088-1-irogers@google.com/
>
> Ian Rogers (8):
> perf unwind: Refactor get_entries to allow dynamic libdw/libunwind
> selection
> perf build loongarch: Remove reference to missing file
> tools build: Deduplicate test-libunwind for different architectures
> perf build: Be more programmatic when setting up libunwind variables
> perf unwind-libunwind: Make libunwind register reading cross platform
> perf unwind-libunwind: Move flush/finish access out of local
> perf unwind-libunwind: Remove libunwind-local
> perf unwind-libunwind: Add RISC-V libunwind support
>
> tools/build/feature/Makefile | 38 +-
> tools/build/feature/test-libunwind-aarch64.c | 27 -
> tools/build/feature/test-libunwind-arm.c | 28 -
> .../test-libunwind-debug-frame-aarch64.c | 17 -
> .../feature/test-libunwind-debug-frame-arm.c | 17 -
> .../feature/test-libunwind-debug-frame.c | 1 -
> tools/build/feature/test-libunwind-x86.c | 28 -
> tools/build/feature/test-libunwind-x86_64.c | 28 -
> tools/build/feature/test-libunwind.c | 1 -
> tools/perf/Makefile.config | 215 ++---
> tools/perf/arch/arm/util/Build | 2 -
> tools/perf/arch/arm/util/unwind-libunwind.c | 50 --
> tools/perf/arch/arm64/util/Build | 1 -
> tools/perf/arch/arm64/util/unwind-libunwind.c | 17 -
> tools/perf/arch/loongarch/util/Build | 3 -
> .../arch/loongarch/util/unwind-libunwind.c | 82 --
> tools/perf/arch/mips/Build | 1 -
> tools/perf/arch/mips/util/Build | 1 -
> tools/perf/arch/mips/util/unwind-libunwind.c | 22 -
> tools/perf/arch/powerpc/util/Build | 1 -
> .../perf/arch/powerpc/util/unwind-libunwind.c | 92 --
> tools/perf/arch/x86/util/Build | 3 -
> tools/perf/arch/x86/util/unwind-libunwind.c | 115 ---
> tools/perf/builtin-inject.c | 4 +
> tools/perf/builtin-report.c | 4 +
> tools/perf/builtin-script.c | 4 +
> tools/perf/util/Build | 5 +-
> tools/perf/util/libunwind-arch/Build | 11 +
> .../perf/util/libunwind-arch/libunwind-arch.c | 319 +++++++
> .../perf/util/libunwind-arch/libunwind-arch.h | 296 +++++++
> .../perf/util/libunwind-arch/libunwind-arm.c | 290 ++++++
> .../util/libunwind-arch/libunwind-arm64.c | 289 ++++++
> .../perf/util/libunwind-arch/libunwind-i386.c | 312 +++++++
> .../util/libunwind-arch/libunwind-loongarch.c | 297 +++++++
> .../perf/util/libunwind-arch/libunwind-mips.c | 299 +++++++
> .../util/libunwind-arch/libunwind-ppc32.c | 301 +++++++
> .../util/libunwind-arch/libunwind-ppc64.c | 303 +++++++
> .../util/libunwind-arch/libunwind-riscv.c | 297 +++++++
> .../perf/util/libunwind-arch/libunwind-s390.c | 299 +++++++
> .../util/libunwind-arch/libunwind-x86_64.c | 320 +++++++
> tools/perf/util/libunwind/arm64.c | 40 -
> tools/perf/util/libunwind/x86_32.c | 41 -
> tools/perf/util/maps.c | 29 +-
> tools/perf/util/maps.h | 4 +-
> tools/perf/util/symbol_conf.h | 15 +
> tools/perf/util/thread.c | 29 +-
> tools/perf/util/unwind-libdw.c | 2 +-
> tools/perf/util/unwind-libunwind-local.c | 832 ------------------
> tools/perf/util/unwind-libunwind.c | 679 ++++++++++++--
> tools/perf/util/unwind.c | 102 +++
> tools/perf/util/unwind.h | 56 +-
> 51 files changed, 4536 insertions(+), 1733 deletions(-)
> delete mode 100644 tools/build/feature/test-libunwind-aarch64.c
> delete mode 100644 tools/build/feature/test-libunwind-arm.c
> delete mode 100644 tools/build/feature/test-libunwind-debug-frame-aarch64.c
> delete mode 100644 tools/build/feature/test-libunwind-debug-frame-arm.c
> delete mode 100644 tools/build/feature/test-libunwind-x86.c
> delete mode 100644 tools/build/feature/test-libunwind-x86_64.c
> delete mode 100644 tools/perf/arch/arm/util/unwind-libunwind.c
> delete mode 100644 tools/perf/arch/arm64/util/unwind-libunwind.c
> delete mode 100644 tools/perf/arch/loongarch/util/unwind-libunwind.c
> delete mode 100644 tools/perf/arch/mips/Build
> delete mode 100644 tools/perf/arch/mips/util/Build
> delete mode 100644 tools/perf/arch/mips/util/unwind-libunwind.c
> delete mode 100644 tools/perf/arch/powerpc/util/unwind-libunwind.c
> delete mode 100644 tools/perf/arch/x86/util/unwind-libunwind.c
> create mode 100644 tools/perf/util/libunwind-arch/Build
> create mode 100644 tools/perf/util/libunwind-arch/libunwind-arch.c
> create mode 100644 tools/perf/util/libunwind-arch/libunwind-arch.h
> create mode 100644 tools/perf/util/libunwind-arch/libunwind-arm.c
> create mode 100644 tools/perf/util/libunwind-arch/libunwind-arm64.c
> create mode 100644 tools/perf/util/libunwind-arch/libunwind-i386.c
> create mode 100644 tools/perf/util/libunwind-arch/libunwind-loongarch.c
> create mode 100644 tools/perf/util/libunwind-arch/libunwind-mips.c
> create mode 100644 tools/perf/util/libunwind-arch/libunwind-ppc32.c
> create mode 100644 tools/perf/util/libunwind-arch/libunwind-ppc64.c
> create mode 100644 tools/perf/util/libunwind-arch/libunwind-riscv.c
> create mode 100644 tools/perf/util/libunwind-arch/libunwind-s390.c
> create mode 100644 tools/perf/util/libunwind-arch/libunwind-x86_64.c
> delete mode 100644 tools/perf/util/libunwind/arm64.c
> delete mode 100644 tools/perf/util/libunwind/x86_32.c
> delete mode 100644 tools/perf/util/unwind-libunwind-local.c
> create mode 100644 tools/perf/util/unwind.c
>
> --
> 2.53.0.473.g4a7958ca14-goog
>
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH v2 0/8] perf libunwind multiple remote support
2026-03-19 21:39 ` [PATCH v2 0/8] perf libunwind multiple remote support Namhyung Kim
@ 2026-03-21 3:06 ` Ian Rogers
2026-03-21 8:20 ` Guilherme Amadio
2026-03-21 23:42 ` [PATCH v1 0/2] perf build: Remove libunwind support Ian Rogers
1 sibling, 1 reply; 27+ messages in thread
From: Ian Rogers @ 2026-03-21 3:06 UTC (permalink / raw)
To: Namhyung Kim, Guilherme Amadio
Cc: 9erthalion6, acme, adrian.hunter, alex, alexander.shishkin,
andrew.jones, aou, atrajeev, blakejones, ctshao, dapeng1.mi,
howardchu95, james.clark, john.g.garry, jolsa, leo.yan,
libunwind-devel, linux-arm-kernel, linux-kernel, linux-perf-users,
linux-riscv, mingo, palmer, peterz, pjw, shimin.guo, tglozar,
tmricht, will, yuzhuo
On Thu, Mar 19, 2026 at 2:39 PM Namhyung Kim <namhyung@kernel.org> wrote:
>
> Hi Ian,
>
> On Thu, Mar 05, 2026 at 02:19:19PM -0800, Ian Rogers wrote:
> > Fix the libunwind build for when libdw and libunwind are feature
> > detected, currently failing with a duplicate symbol.
> >
> > Refactor the libunwind support so that whenever a remote target is
> > available, perf functions using the ELF machine can use that remote
> > target regardless of what the host/local machine is. Migrate existing
> > libunwind supported architectures like powerpc, arm64 and loongarch so
> > that they can work in a cross-architecture way. Add support for
> > RISC-V. Make the code more regular in function names, etc. and avoid
> > including a C-file. This increases the lines of code. It is similar in
> > style to the unwind-libdw implementation. It is hoped that the more
> > uniform nature of the code with help with refactoring the perf
> > registers for SIMD/APX support.
> >
> > Aside from local host testing these patches are under tested, in part
> > as I'm failing to see how to build libunwind with support for multiple
> > remote targets. Please could I get help in testing.
>
> It seems libunwind project is not actively maintained. Assuming libdw
> unwinding is performant enough, we may want to get rid of libunwind
> support.
Guilherme, is it true that libunwind is built into perf with Gentoo?
The performance fix for unwinding with libdw is in commit
6b2658b3f36a ("perf unwind-libdw: Don't discard loaded ELF/DWARF after
every unwind") that is currently only in v7.0-rc[1-4].
In commit 13e17c9ff49119aa ("perf build: Make libunwind opt-in rather
than opt-out"), perf made libunwind opt-in rather than opt-out. This
change is in v6.13.
LLVM has a libunwind project but LLVM scares me because its library
dependencies are so large.
Perhaps we should merge these changes and then delete the libunwind
support in 2 or 3 releases time. I believe this is closer to how
libbpf deprecated the pre-1.0 behaviors. Personally I don't mind if we
just delete everything now, but typically we're not that aggressive.
Thanks,
Ian
> Thanks,
> Namhyung
>
> >
> > v2: Move two fixes patches to position 1 and 2 in the series. Fix
> > struct naming inconsistency, Andrew Jones
> > <andrew.jones@oss.qualcomm.com>. Fix other inconsistencies and
> > potential non-x86 build issues.
> >
> > v1: https://lore.kernel.org/lkml/20260224142938.26088-1-irogers@google.com/
> >
> > Ian Rogers (8):
> > perf unwind: Refactor get_entries to allow dynamic libdw/libunwind
> > selection
> > perf build loongarch: Remove reference to missing file
> > tools build: Deduplicate test-libunwind for different architectures
> > perf build: Be more programmatic when setting up libunwind variables
> > perf unwind-libunwind: Make libunwind register reading cross platform
> > perf unwind-libunwind: Move flush/finish access out of local
> > perf unwind-libunwind: Remove libunwind-local
> > perf unwind-libunwind: Add RISC-V libunwind support
> >
> > tools/build/feature/Makefile | 38 +-
> > tools/build/feature/test-libunwind-aarch64.c | 27 -
> > tools/build/feature/test-libunwind-arm.c | 28 -
> > .../test-libunwind-debug-frame-aarch64.c | 17 -
> > .../feature/test-libunwind-debug-frame-arm.c | 17 -
> > .../feature/test-libunwind-debug-frame.c | 1 -
> > tools/build/feature/test-libunwind-x86.c | 28 -
> > tools/build/feature/test-libunwind-x86_64.c | 28 -
> > tools/build/feature/test-libunwind.c | 1 -
> > tools/perf/Makefile.config | 215 ++---
> > tools/perf/arch/arm/util/Build | 2 -
> > tools/perf/arch/arm/util/unwind-libunwind.c | 50 --
> > tools/perf/arch/arm64/util/Build | 1 -
> > tools/perf/arch/arm64/util/unwind-libunwind.c | 17 -
> > tools/perf/arch/loongarch/util/Build | 3 -
> > .../arch/loongarch/util/unwind-libunwind.c | 82 --
> > tools/perf/arch/mips/Build | 1 -
> > tools/perf/arch/mips/util/Build | 1 -
> > tools/perf/arch/mips/util/unwind-libunwind.c | 22 -
> > tools/perf/arch/powerpc/util/Build | 1 -
> > .../perf/arch/powerpc/util/unwind-libunwind.c | 92 --
> > tools/perf/arch/x86/util/Build | 3 -
> > tools/perf/arch/x86/util/unwind-libunwind.c | 115 ---
> > tools/perf/builtin-inject.c | 4 +
> > tools/perf/builtin-report.c | 4 +
> > tools/perf/builtin-script.c | 4 +
> > tools/perf/util/Build | 5 +-
> > tools/perf/util/libunwind-arch/Build | 11 +
> > .../perf/util/libunwind-arch/libunwind-arch.c | 319 +++++++
> > .../perf/util/libunwind-arch/libunwind-arch.h | 296 +++++++
> > .../perf/util/libunwind-arch/libunwind-arm.c | 290 ++++++
> > .../util/libunwind-arch/libunwind-arm64.c | 289 ++++++
> > .../perf/util/libunwind-arch/libunwind-i386.c | 312 +++++++
> > .../util/libunwind-arch/libunwind-loongarch.c | 297 +++++++
> > .../perf/util/libunwind-arch/libunwind-mips.c | 299 +++++++
> > .../util/libunwind-arch/libunwind-ppc32.c | 301 +++++++
> > .../util/libunwind-arch/libunwind-ppc64.c | 303 +++++++
> > .../util/libunwind-arch/libunwind-riscv.c | 297 +++++++
> > .../perf/util/libunwind-arch/libunwind-s390.c | 299 +++++++
> > .../util/libunwind-arch/libunwind-x86_64.c | 320 +++++++
> > tools/perf/util/libunwind/arm64.c | 40 -
> > tools/perf/util/libunwind/x86_32.c | 41 -
> > tools/perf/util/maps.c | 29 +-
> > tools/perf/util/maps.h | 4 +-
> > tools/perf/util/symbol_conf.h | 15 +
> > tools/perf/util/thread.c | 29 +-
> > tools/perf/util/unwind-libdw.c | 2 +-
> > tools/perf/util/unwind-libunwind-local.c | 832 ------------------
> > tools/perf/util/unwind-libunwind.c | 679 ++++++++++++--
> > tools/perf/util/unwind.c | 102 +++
> > tools/perf/util/unwind.h | 56 +-
> > 51 files changed, 4536 insertions(+), 1733 deletions(-)
> > delete mode 100644 tools/build/feature/test-libunwind-aarch64.c
> > delete mode 100644 tools/build/feature/test-libunwind-arm.c
> > delete mode 100644 tools/build/feature/test-libunwind-debug-frame-aarch64.c
> > delete mode 100644 tools/build/feature/test-libunwind-debug-frame-arm.c
> > delete mode 100644 tools/build/feature/test-libunwind-x86.c
> > delete mode 100644 tools/build/feature/test-libunwind-x86_64.c
> > delete mode 100644 tools/perf/arch/arm/util/unwind-libunwind.c
> > delete mode 100644 tools/perf/arch/arm64/util/unwind-libunwind.c
> > delete mode 100644 tools/perf/arch/loongarch/util/unwind-libunwind.c
> > delete mode 100644 tools/perf/arch/mips/Build
> > delete mode 100644 tools/perf/arch/mips/util/Build
> > delete mode 100644 tools/perf/arch/mips/util/unwind-libunwind.c
> > delete mode 100644 tools/perf/arch/powerpc/util/unwind-libunwind.c
> > delete mode 100644 tools/perf/arch/x86/util/unwind-libunwind.c
> > create mode 100644 tools/perf/util/libunwind-arch/Build
> > create mode 100644 tools/perf/util/libunwind-arch/libunwind-arch.c
> > create mode 100644 tools/perf/util/libunwind-arch/libunwind-arch.h
> > create mode 100644 tools/perf/util/libunwind-arch/libunwind-arm.c
> > create mode 100644 tools/perf/util/libunwind-arch/libunwind-arm64.c
> > create mode 100644 tools/perf/util/libunwind-arch/libunwind-i386.c
> > create mode 100644 tools/perf/util/libunwind-arch/libunwind-loongarch.c
> > create mode 100644 tools/perf/util/libunwind-arch/libunwind-mips.c
> > create mode 100644 tools/perf/util/libunwind-arch/libunwind-ppc32.c
> > create mode 100644 tools/perf/util/libunwind-arch/libunwind-ppc64.c
> > create mode 100644 tools/perf/util/libunwind-arch/libunwind-riscv.c
> > create mode 100644 tools/perf/util/libunwind-arch/libunwind-s390.c
> > create mode 100644 tools/perf/util/libunwind-arch/libunwind-x86_64.c
> > delete mode 100644 tools/perf/util/libunwind/arm64.c
> > delete mode 100644 tools/perf/util/libunwind/x86_32.c
> > delete mode 100644 tools/perf/util/unwind-libunwind-local.c
> > create mode 100644 tools/perf/util/unwind.c
> >
> > --
> > 2.53.0.473.g4a7958ca14-goog
> >
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH v2 0/8] perf libunwind multiple remote support
2026-03-21 3:06 ` Ian Rogers
@ 2026-03-21 8:20 ` Guilherme Amadio
0 siblings, 0 replies; 27+ messages in thread
From: Guilherme Amadio @ 2026-03-21 8:20 UTC (permalink / raw)
To: Ian Rogers
Cc: Namhyung Kim, 9erthalion6, acme, adrian.hunter, alex,
alexander.shishkin, andrew.jones, aou, atrajeev, blakejones,
ctshao, dapeng1.mi, howardchu95, james.clark, john.g.garry, jolsa,
leo.yan, libunwind-devel, linux-arm-kernel, linux-kernel,
linux-perf-users, linux-riscv, mingo, palmer, peterz, pjw,
shimin.guo, tglozar, tmricht, will, yuzhuo
On Fri, Mar 20, 2026 at 08:06:42PM -0700, Ian Rogers wrote:
> On Thu, Mar 19, 2026 at 2:39 PM Namhyung Kim <namhyung@kernel.org> wrote:
> >
> > Hi Ian,
> >
> > On Thu, Mar 05, 2026 at 02:19:19PM -0800, Ian Rogers wrote:
> > > Fix the libunwind build for when libdw and libunwind are feature
> > > detected, currently failing with a duplicate symbol.
> > >
> > > Refactor the libunwind support so that whenever a remote target is
> > > available, perf functions using the ELF machine can use that remote
> > > target regardless of what the host/local machine is. Migrate existing
> > > libunwind supported architectures like powerpc, arm64 and loongarch so
> > > that they can work in a cross-architecture way. Add support for
> > > RISC-V. Make the code more regular in function names, etc. and avoid
> > > including a C-file. This increases the lines of code. It is similar in
> > > style to the unwind-libdw implementation. It is hoped that the more
> > > uniform nature of the code with help with refactoring the perf
> > > registers for SIMD/APX support.
> > >
> > > Aside from local host testing these patches are under tested, in part
> > > as I'm failing to see how to build libunwind with support for multiple
> > > remote targets. Please could I get help in testing.
> >
> > It seems libunwind project is not actively maintained. Assuming libdw
> > unwinding is performant enough, we may want to get rid of libunwind
> > support.
>
> Guilherme, is it true that libunwind is built into perf with Gentoo?
Hi Ian, libunwind support is an optional feature, which is not enabled
by default. Users can turn it on via a USE flag:
https://gitweb.gentoo.org/repo/gentoo.git/tree/dev-util/perf/perf-6.19.ebuild#n88
The full list of USE flags is here:
https://gitweb.gentoo.org/repo/gentoo.git/tree/dev-util/perf/perf-6.19.ebuild#n38
the ones marked with a + in front are enabled by default.
Personally, I enable everything when installing:
~ $ perf version --build-options
perf version 6.19
aio: [ on ] # HAVE_AIO_SUPPORT
bpf: [ on ] # HAVE_LIBBPF_SUPPORT
bpf_skeletons: [ on ] # HAVE_BPF_SKEL
debuginfod: [ on ] # HAVE_DEBUGINFOD_SUPPORT
dwarf: [ on ] # HAVE_LIBDW_SUPPORT
dwarf_getlocations: [ on ] # HAVE_LIBDW_SUPPORT
dwarf-unwind: [ on ] # HAVE_DWARF_UNWIND_SUPPORT
libbfd: [ OFF ] # HAVE_LIBBFD_SUPPORT ( tip: Deprecated, license incompatibility, use BUILD_NONDISTRO=1 and install binutils-dev[el] )
libbpf-strings: [ on ] # HAVE_LIBBPF_STRINGS_SUPPORT
libcapstone: [ on ] # HAVE_LIBCAPSTONE_SUPPORT
libdw-dwarf-unwind: [ on ] # HAVE_LIBDW_SUPPORT
libelf: [ on ] # HAVE_LIBELF_SUPPORT
libLLVM: [ on ] # HAVE_LIBLLVM_SUPPORT
libnuma: [ on ] # HAVE_LIBNUMA_SUPPORT
libopencsd: [ OFF ] # HAVE_CSTRACE_SUPPORT
libperl: [ on ] # HAVE_LIBPERL_SUPPORT
libpfm4: [ on ] # HAVE_LIBPFM
libpython: [ on ] # HAVE_LIBPYTHON_SUPPORT
libslang: [ on ] # HAVE_SLANG_SUPPORT
libtraceevent: [ on ] # HAVE_LIBTRACEEVENT
libunwind: [ on ] # HAVE_LIBUNWIND_SUPPORT
lzma: [ on ] # HAVE_LZMA_SUPPORT
numa_num_possible_cpus: [ on ] # HAVE_LIBNUMA_SUPPORT
zlib: [ on ] # HAVE_ZLIB_SUPPORT
zstd: [ on ] # HAVE_ZSTD_SUPPORT
> The performance fix for unwinding with libdw is in commit
> 6b2658b3f36a ("perf unwind-libdw: Don't discard loaded ELF/DWARF after
> every unwind") that is currently only in v7.0-rc[1-4].
>
> In commit 13e17c9ff49119aa ("perf build: Make libunwind opt-in rather
> than opt-out"), perf made libunwind opt-in rather than opt-out. This
> change is in v6.13.
>
> LLVM has a libunwind project but LLVM scares me because its library
> dependencies are so large.
If you have advice on what you'd prefer us (i.e. packagers) to do, I'm
happy to follow. For example, I've removed BUILD_NONDISTRO=1 from our
ebuilds some time around 6.17. If the performance fix above applies onto
6.19 (or is included in 6.19.x for some x), then we can pick that up as
well.
> Perhaps we should merge these changes and then delete the libunwind
> support in 2 or 3 releases time. I believe this is closer to how
> libbpf deprecated the pre-1.0 behaviors. Personally I don't mind if we
> just delete everything now, but typically we're not that aggressive.
From Gentoo's point of view, it's relatively easy to adapt the ebuild to
upstream changes. As a user, I care about having reliable stack traces,
what library is used for that is not as important.
Best regards,
-Guilherme
> Thanks,
> Ian
>
> > Thanks,
> > Namhyung
> >
> > >
> > > v2: Move two fixes patches to position 1 and 2 in the series. Fix
> > > struct naming inconsistency, Andrew Jones
> > > <andrew.jones@oss.qualcomm.com>. Fix other inconsistencies and
> > > potential non-x86 build issues.
> > >
> > > v1: https://lore.kernel.org/lkml/20260224142938.26088-1-irogers@google.com/
> > >
> > > Ian Rogers (8):
> > > perf unwind: Refactor get_entries to allow dynamic libdw/libunwind
> > > selection
> > > perf build loongarch: Remove reference to missing file
> > > tools build: Deduplicate test-libunwind for different architectures
> > > perf build: Be more programmatic when setting up libunwind variables
> > > perf unwind-libunwind: Make libunwind register reading cross platform
> > > perf unwind-libunwind: Move flush/finish access out of local
> > > perf unwind-libunwind: Remove libunwind-local
> > > perf unwind-libunwind: Add RISC-V libunwind support
> > >
> > > tools/build/feature/Makefile | 38 +-
> > > tools/build/feature/test-libunwind-aarch64.c | 27 -
> > > tools/build/feature/test-libunwind-arm.c | 28 -
> > > .../test-libunwind-debug-frame-aarch64.c | 17 -
> > > .../feature/test-libunwind-debug-frame-arm.c | 17 -
> > > .../feature/test-libunwind-debug-frame.c | 1 -
> > > tools/build/feature/test-libunwind-x86.c | 28 -
> > > tools/build/feature/test-libunwind-x86_64.c | 28 -
> > > tools/build/feature/test-libunwind.c | 1 -
> > > tools/perf/Makefile.config | 215 ++---
> > > tools/perf/arch/arm/util/Build | 2 -
> > > tools/perf/arch/arm/util/unwind-libunwind.c | 50 --
> > > tools/perf/arch/arm64/util/Build | 1 -
> > > tools/perf/arch/arm64/util/unwind-libunwind.c | 17 -
> > > tools/perf/arch/loongarch/util/Build | 3 -
> > > .../arch/loongarch/util/unwind-libunwind.c | 82 --
> > > tools/perf/arch/mips/Build | 1 -
> > > tools/perf/arch/mips/util/Build | 1 -
> > > tools/perf/arch/mips/util/unwind-libunwind.c | 22 -
> > > tools/perf/arch/powerpc/util/Build | 1 -
> > > .../perf/arch/powerpc/util/unwind-libunwind.c | 92 --
> > > tools/perf/arch/x86/util/Build | 3 -
> > > tools/perf/arch/x86/util/unwind-libunwind.c | 115 ---
> > > tools/perf/builtin-inject.c | 4 +
> > > tools/perf/builtin-report.c | 4 +
> > > tools/perf/builtin-script.c | 4 +
> > > tools/perf/util/Build | 5 +-
> > > tools/perf/util/libunwind-arch/Build | 11 +
> > > .../perf/util/libunwind-arch/libunwind-arch.c | 319 +++++++
> > > .../perf/util/libunwind-arch/libunwind-arch.h | 296 +++++++
> > > .../perf/util/libunwind-arch/libunwind-arm.c | 290 ++++++
> > > .../util/libunwind-arch/libunwind-arm64.c | 289 ++++++
> > > .../perf/util/libunwind-arch/libunwind-i386.c | 312 +++++++
> > > .../util/libunwind-arch/libunwind-loongarch.c | 297 +++++++
> > > .../perf/util/libunwind-arch/libunwind-mips.c | 299 +++++++
> > > .../util/libunwind-arch/libunwind-ppc32.c | 301 +++++++
> > > .../util/libunwind-arch/libunwind-ppc64.c | 303 +++++++
> > > .../util/libunwind-arch/libunwind-riscv.c | 297 +++++++
> > > .../perf/util/libunwind-arch/libunwind-s390.c | 299 +++++++
> > > .../util/libunwind-arch/libunwind-x86_64.c | 320 +++++++
> > > tools/perf/util/libunwind/arm64.c | 40 -
> > > tools/perf/util/libunwind/x86_32.c | 41 -
> > > tools/perf/util/maps.c | 29 +-
> > > tools/perf/util/maps.h | 4 +-
> > > tools/perf/util/symbol_conf.h | 15 +
> > > tools/perf/util/thread.c | 29 +-
> > > tools/perf/util/unwind-libdw.c | 2 +-
> > > tools/perf/util/unwind-libunwind-local.c | 832 ------------------
> > > tools/perf/util/unwind-libunwind.c | 679 ++++++++++++--
> > > tools/perf/util/unwind.c | 102 +++
> > > tools/perf/util/unwind.h | 56 +-
> > > 51 files changed, 4536 insertions(+), 1733 deletions(-)
> > > delete mode 100644 tools/build/feature/test-libunwind-aarch64.c
> > > delete mode 100644 tools/build/feature/test-libunwind-arm.c
> > > delete mode 100644 tools/build/feature/test-libunwind-debug-frame-aarch64.c
> > > delete mode 100644 tools/build/feature/test-libunwind-debug-frame-arm.c
> > > delete mode 100644 tools/build/feature/test-libunwind-x86.c
> > > delete mode 100644 tools/build/feature/test-libunwind-x86_64.c
> > > delete mode 100644 tools/perf/arch/arm/util/unwind-libunwind.c
> > > delete mode 100644 tools/perf/arch/arm64/util/unwind-libunwind.c
> > > delete mode 100644 tools/perf/arch/loongarch/util/unwind-libunwind.c
> > > delete mode 100644 tools/perf/arch/mips/Build
> > > delete mode 100644 tools/perf/arch/mips/util/Build
> > > delete mode 100644 tools/perf/arch/mips/util/unwind-libunwind.c
> > > delete mode 100644 tools/perf/arch/powerpc/util/unwind-libunwind.c
> > > delete mode 100644 tools/perf/arch/x86/util/unwind-libunwind.c
> > > create mode 100644 tools/perf/util/libunwind-arch/Build
> > > create mode 100644 tools/perf/util/libunwind-arch/libunwind-arch.c
> > > create mode 100644 tools/perf/util/libunwind-arch/libunwind-arch.h
> > > create mode 100644 tools/perf/util/libunwind-arch/libunwind-arm.c
> > > create mode 100644 tools/perf/util/libunwind-arch/libunwind-arm64.c
> > > create mode 100644 tools/perf/util/libunwind-arch/libunwind-i386.c
> > > create mode 100644 tools/perf/util/libunwind-arch/libunwind-loongarch.c
> > > create mode 100644 tools/perf/util/libunwind-arch/libunwind-mips.c
> > > create mode 100644 tools/perf/util/libunwind-arch/libunwind-ppc32.c
> > > create mode 100644 tools/perf/util/libunwind-arch/libunwind-ppc64.c
> > > create mode 100644 tools/perf/util/libunwind-arch/libunwind-riscv.c
> > > create mode 100644 tools/perf/util/libunwind-arch/libunwind-s390.c
> > > create mode 100644 tools/perf/util/libunwind-arch/libunwind-x86_64.c
> > > delete mode 100644 tools/perf/util/libunwind/arm64.c
> > > delete mode 100644 tools/perf/util/libunwind/x86_32.c
> > > delete mode 100644 tools/perf/util/unwind-libunwind-local.c
> > > create mode 100644 tools/perf/util/unwind.c
> > >
> > > --
> > > 2.53.0.473.g4a7958ca14-goog
> > >
^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH v1 0/2] perf build: Remove libunwind support
2026-03-19 21:39 ` [PATCH v2 0/8] perf libunwind multiple remote support Namhyung Kim
2026-03-21 3:06 ` Ian Rogers
@ 2026-03-21 23:42 ` Ian Rogers
2026-03-21 23:42 ` [PATCH v1 1/2] " Ian Rogers
` (2 more replies)
1 sibling, 3 replies; 27+ messages in thread
From: Ian Rogers @ 2026-03-21 23:42 UTC (permalink / raw)
To: namhyung
Cc: 9erthalion6, acme, adrian.hunter, alex, alexander.shishkin,
andrew.jones, aou, atrajeev, blakejones, ctshao, dapeng1.mi,
howardchu95, irogers, james.clark, john.g.garry, jolsa, leo.yan,
libunwind-devel, linux-arm-kernel, linux-kernel, linux-perf-users,
linux-riscv, mingo, palmer, peterz, pjw, shimin.guo, tglozar,
tmricht, will, amadio, yuzhuo
libunwind support exists for "--call-graph dwarf", however, libunwind
support has been opt-in rather than opt-out since Linux v6.13 as libdw
is preferred - commit 13e17c9ff49119aa ("perf build: Make libunwind
opt-in rather than opt-out"). A problem with the libdw support was
that it was slow, an issue fixed in Linux v7.0 in commit 6b2658b3f36a
("perf unwind-libdw: Don't discard loaded ELF/DWARF after every
unwind"). As such libunwind support is now unnecessary.
The patch series:
https://lore.kernel.org/lkml/20260305221927.3237145-1-irogers@google.com/
looked to make the libunwind support in perf similar to the libdw
support, allow cross-architecture unwinding, etc. This was motivated
by the perf regs conventions being altered by the addition of x86 APX
support:
https://lore.kernel.org/lkml/20260209072047.2180332-1-dapeng1.mi@linux.intel.com/
It is necessary to translate the library's notion of registers to the
perf register convention so that the stack unwinding state can be
initialized. On this series it was stated that removing libunwind
support from perf should be an option, rather than updating support:
https://lore.kernel.org/lkml/abxs-2rozL1tBEO1@google.com/
This was also what motivated making libunwind opt-in rather than
opt-out.
Given that 7 minor releases have happened with libunwind "deprecated"
by making it opt-in, let's remove the libunwind support. There doesn't
appear to be any disagreement to this on the mailing list.
Ian Rogers (2):
perf build: Remove libunwind support
tools build: Remove libunwind feature tests
tools/build/feature/Makefile | 31 -
tools/build/feature/test-libunwind-aarch64.c | 27 -
tools/build/feature/test-libunwind-arm.c | 28 -
.../test-libunwind-debug-frame-aarch64.c | 17 -
.../feature/test-libunwind-debug-frame-arm.c | 17 -
.../feature/test-libunwind-debug-frame.c | 17 -
tools/build/feature/test-libunwind-x86.c | 28 -
tools/build/feature/test-libunwind-x86_64.c | 28 -
tools/build/feature/test-libunwind.c | 28 -
tools/perf/Documentation/perf-check.txt | 1 -
tools/perf/Documentation/perf-config.txt | 4 +-
tools/perf/Documentation/perf-record.txt | 2 +-
tools/perf/Makefile.config | 163 +---
tools/perf/Makefile.perf | 3 -
tools/perf/arch/arm/util/Build | 1 -
tools/perf/arch/arm/util/unwind-libunwind.c | 50 --
tools/perf/arch/arm64/util/Build | 1 -
tools/perf/arch/arm64/util/unwind-libunwind.c | 17 -
tools/perf/arch/loongarch/util/Build | 1 -
.../arch/loongarch/util/unwind-libunwind.c | 82 --
tools/perf/arch/mips/util/Build | 1 -
tools/perf/arch/mips/util/unwind-libunwind.c | 22 -
tools/perf/arch/powerpc/util/Build | 1 -
.../perf/arch/powerpc/util/unwind-libunwind.c | 92 --
tools/perf/arch/x86/util/Build | 1 -
tools/perf/arch/x86/util/unwind-libunwind.c | 115 ---
tools/perf/builtin-check.c | 1 -
tools/perf/builtin-report.c | 4 +-
tools/perf/tests/make | 2 -
tools/perf/util/Build | 5 -
tools/perf/util/callchain.c | 2 +-
tools/perf/util/dso.h | 6 -
tools/perf/util/libunwind/arm64.c | 40 -
tools/perf/util/libunwind/x86_32.c | 41 -
tools/perf/util/maps.c | 47 +-
tools/perf/util/maps.h | 6 -
tools/perf/util/thread.c | 40 +-
tools/perf/util/thread.h | 1 -
tools/perf/util/unwind-libunwind-local.c | 832 ------------------
tools/perf/util/unwind-libunwind.c | 92 --
tools/perf/util/unwind.h | 41 +-
41 files changed, 19 insertions(+), 1919 deletions(-)
delete mode 100644 tools/build/feature/test-libunwind-aarch64.c
delete mode 100644 tools/build/feature/test-libunwind-arm.c
delete mode 100644 tools/build/feature/test-libunwind-debug-frame-aarch64.c
delete mode 100644 tools/build/feature/test-libunwind-debug-frame-arm.c
delete mode 100644 tools/build/feature/test-libunwind-debug-frame.c
delete mode 100644 tools/build/feature/test-libunwind-x86.c
delete mode 100644 tools/build/feature/test-libunwind-x86_64.c
delete mode 100644 tools/build/feature/test-libunwind.c
delete mode 100644 tools/perf/arch/arm/util/unwind-libunwind.c
delete mode 100644 tools/perf/arch/arm64/util/unwind-libunwind.c
delete mode 100644 tools/perf/arch/loongarch/util/unwind-libunwind.c
delete mode 100644 tools/perf/arch/mips/util/unwind-libunwind.c
delete mode 100644 tools/perf/arch/powerpc/util/unwind-libunwind.c
delete mode 100644 tools/perf/arch/x86/util/unwind-libunwind.c
delete mode 100644 tools/perf/util/libunwind/arm64.c
delete mode 100644 tools/perf/util/libunwind/x86_32.c
delete mode 100644 tools/perf/util/unwind-libunwind-local.c
delete mode 100644 tools/perf/util/unwind-libunwind.c
--
2.53.0.959.g497ff81fa9-goog
^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH v1 1/2] perf build: Remove libunwind support
2026-03-21 23:42 ` [PATCH v1 0/2] perf build: Remove libunwind support Ian Rogers
@ 2026-03-21 23:42 ` Ian Rogers
2026-03-21 23:42 ` [PATCH v1 2/2] tools build: Remove libunwind feature tests Ian Rogers
2026-03-26 22:51 ` [PATCH v1 0/2] perf build: Remove libunwind support Namhyung Kim
2 siblings, 0 replies; 27+ messages in thread
From: Ian Rogers @ 2026-03-21 23:42 UTC (permalink / raw)
To: namhyung
Cc: 9erthalion6, acme, adrian.hunter, alex, alexander.shishkin,
andrew.jones, aou, atrajeev, blakejones, ctshao, dapeng1.mi,
howardchu95, irogers, james.clark, john.g.garry, jolsa, leo.yan,
libunwind-devel, linux-arm-kernel, linux-kernel, linux-perf-users,
linux-riscv, mingo, palmer, peterz, pjw, shimin.guo, tglozar,
tmricht, will, amadio, yuzhuo
libunwind support is deprecated and libdw is preferred for DWARF unwinding.
Remove the libunwind configuration from Makefile.config, and delete the
associated source files and architecture-specific support code.
Update the DWARF post-unwind logic to rely solely on libdw. Clean up
the HAVE_LIBUNWIND_SUPPORT and CONFIG_LIBUNWIND references across
the codebase, including the unwind_libunwind_ops structure.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/Documentation/perf-check.txt | 1 -
tools/perf/Documentation/perf-config.txt | 4 +-
tools/perf/Documentation/perf-record.txt | 2 +-
tools/perf/Makefile.config | 163 +---
tools/perf/Makefile.perf | 3 -
tools/perf/arch/arm/util/Build | 1 -
tools/perf/arch/arm/util/unwind-libunwind.c | 50 --
tools/perf/arch/arm64/util/Build | 1 -
tools/perf/arch/arm64/util/unwind-libunwind.c | 17 -
tools/perf/arch/loongarch/util/Build | 1 -
.../arch/loongarch/util/unwind-libunwind.c | 82 --
tools/perf/arch/mips/util/Build | 1 -
tools/perf/arch/mips/util/unwind-libunwind.c | 22 -
tools/perf/arch/powerpc/util/Build | 1 -
.../perf/arch/powerpc/util/unwind-libunwind.c | 92 --
tools/perf/arch/x86/util/Build | 1 -
tools/perf/arch/x86/util/unwind-libunwind.c | 115 ---
tools/perf/builtin-check.c | 1 -
tools/perf/builtin-report.c | 4 +-
tools/perf/tests/make | 2 -
tools/perf/util/Build | 5 -
tools/perf/util/callchain.c | 2 +-
tools/perf/util/dso.h | 6 -
tools/perf/util/libunwind/arm64.c | 40 -
tools/perf/util/libunwind/x86_32.c | 41 -
tools/perf/util/maps.c | 47 +-
tools/perf/util/maps.h | 6 -
tools/perf/util/thread.c | 40 +-
tools/perf/util/thread.h | 1 -
tools/perf/util/unwind-libunwind-local.c | 832 ------------------
tools/perf/util/unwind-libunwind.c | 92 --
tools/perf/util/unwind.h | 41 +-
32 files changed, 19 insertions(+), 1698 deletions(-)
delete mode 100644 tools/perf/arch/arm/util/unwind-libunwind.c
delete mode 100644 tools/perf/arch/arm64/util/unwind-libunwind.c
delete mode 100644 tools/perf/arch/loongarch/util/unwind-libunwind.c
delete mode 100644 tools/perf/arch/mips/util/unwind-libunwind.c
delete mode 100644 tools/perf/arch/powerpc/util/unwind-libunwind.c
delete mode 100644 tools/perf/arch/x86/util/unwind-libunwind.c
delete mode 100644 tools/perf/util/libunwind/arm64.c
delete mode 100644 tools/perf/util/libunwind/x86_32.c
delete mode 100644 tools/perf/util/unwind-libunwind-local.c
delete mode 100644 tools/perf/util/unwind-libunwind.c
diff --git a/tools/perf/Documentation/perf-check.txt b/tools/perf/Documentation/perf-check.txt
index 09e1d35677f5..1ad30ba53bd6 100644
--- a/tools/perf/Documentation/perf-check.txt
+++ b/tools/perf/Documentation/perf-check.txt
@@ -63,7 +63,6 @@ feature::
libpython / HAVE_LIBPYTHON_SUPPORT
libslang / HAVE_SLANG_SUPPORT
libtraceevent / HAVE_LIBTRACEEVENT
- libunwind / HAVE_LIBUNWIND_SUPPORT
lzma / HAVE_LZMA_SUPPORT
numa_num_possible_cpus / HAVE_LIBNUMA_SUPPORT
zlib / HAVE_ZLIB_SUPPORT
diff --git a/tools/perf/Documentation/perf-config.txt b/tools/perf/Documentation/perf-config.txt
index 642d1c490d9e..38b8d5c69023 100644
--- a/tools/perf/Documentation/perf-config.txt
+++ b/tools/perf/Documentation/perf-config.txt
@@ -446,8 +446,8 @@ call-graph.*::
call-graph.record-mode::
The mode for user space can be 'fp' (frame pointer), 'dwarf'
- and 'lbr'. The value 'dwarf' is effective only if libunwind
- (or a recent version of libdw) is present on the system;
+ and 'lbr'. The value 'dwarf' is effective only if a recent
+ version of libdw is present on the system;
the value 'lbr' only works for certain cpus. The method for
kernel space is controlled not by this option but by the
kernel config (CONFIG_UNWINDER_*).
diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt
index 178f483140ed..b4daa668f70a 100644
--- a/tools/perf/Documentation/perf-record.txt
+++ b/tools/perf/Documentation/perf-record.txt
@@ -308,7 +308,7 @@ OPTIONS
In some systems, where binaries are build with gcc
--fomit-frame-pointer, using the "fp" method will produce bogus
call graphs, using "dwarf", if available (perf tools linked to
- the libunwind or libdw library) should be used instead.
+ the libdw library) should be used instead.
Using the "lbr" method doesn't require any compiler options. It
will produce call graphs from the hardware LBR registers. The
main limitation is that it is only available on new Intel
diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config
index a8dc72cfe48e..90b6df4cbeca 100644
--- a/tools/perf/Makefile.config
+++ b/tools/perf/Makefile.config
@@ -65,97 +65,19 @@ $(call detected_var,SRCARCH)
CFLAGS += -I$(OUTPUT)arch/$(SRCARCH)/include/generated
-# Additional ARCH settings for ppc
-ifeq ($(SRCARCH),powerpc)
- ifndef NO_LIBUNWIND
- LIBUNWIND_LIBS := -lunwind -lunwind-ppc64
- endif
-endif
-
# Additional ARCH settings for x86
ifeq ($(SRCARCH),x86)
$(call detected,CONFIG_X86)
ifeq (${IS_64_BIT}, 1)
CFLAGS += -DHAVE_ARCH_X86_64_SUPPORT
ARCH_INCLUDE = ../../arch/x86/lib/memcpy_64.S ../../arch/x86/lib/memset_64.S
- ifndef NO_LIBUNWIND
- LIBUNWIND_LIBS = -lunwind-x86_64 -lunwind -llzma
- endif
$(call detected,CONFIG_X86_64)
- else
- ifndef NO_LIBUNWIND
- LIBUNWIND_LIBS = -lunwind-x86 -llzma -lunwind
- endif
- endif
-endif
-
-ifeq ($(SRCARCH),arm)
- ifndef NO_LIBUNWIND
- LIBUNWIND_LIBS = -lunwind -lunwind-arm
- endif
-endif
-
-ifeq ($(SRCARCH),arm64)
- ifndef NO_LIBUNWIND
- LIBUNWIND_LIBS = -lunwind -lunwind-aarch64
- endif
-endif
-
-ifeq ($(SRCARCH),loongarch)
- ifndef NO_LIBUNWIND
- LIBUNWIND_LIBS = -lunwind -lunwind-loongarch64
endif
endif
ifeq ($(ARCH),s390)
CFLAGS += -fPIC
endif
-
-ifeq ($(ARCH),mips)
- ifndef NO_LIBUNWIND
- LIBUNWIND_LIBS = -lunwind -lunwind-mips
- endif
-endif
-
-ifneq ($(LIBUNWIND),1)
- NO_LIBUNWIND := 1
-endif
-
-ifeq ($(LIBUNWIND_LIBS),)
- NO_LIBUNWIND := 1
-endif
-#
-# For linking with debug library, run like:
-#
-# make DEBUG=1 LIBUNWIND_DIR=/opt/libunwind/
-#
-
-libunwind_arch_set_flags = $(eval $(libunwind_arch_set_flags_code))
-define libunwind_arch_set_flags_code
- FEATURE_CHECK_CFLAGS-libunwind-$(1) = -I$(LIBUNWIND_DIR)/include
- FEATURE_CHECK_LDFLAGS-libunwind-$(1) = -L$(LIBUNWIND_DIR)/lib
-endef
-
-ifdef LIBUNWIND_DIR
- LIBUNWIND_CFLAGS = -I$(LIBUNWIND_DIR)/include
- LIBUNWIND_LDFLAGS = -L$(LIBUNWIND_DIR)/lib
- LIBUNWIND_ARCHS = x86 x86_64 arm aarch64 debug-frame-arm debug-frame-aarch64 loongarch
- $(foreach libunwind_arch,$(LIBUNWIND_ARCHS),$(call libunwind_arch_set_flags,$(libunwind_arch)))
-endif
-
-ifndef NO_LIBUNWIND
- # Set per-feature check compilation flags
- FEATURE_CHECK_CFLAGS-libunwind = $(LIBUNWIND_CFLAGS)
- FEATURE_CHECK_LDFLAGS-libunwind = $(LIBUNWIND_LDFLAGS) $(LIBUNWIND_LIBS)
- FEATURE_CHECK_CFLAGS-libunwind-debug-frame = $(LIBUNWIND_CFLAGS)
- FEATURE_CHECK_LDFLAGS-libunwind-debug-frame = $(LIBUNWIND_LDFLAGS) $(LIBUNWIND_LIBS)
-
- FEATURE_CHECK_LDFLAGS-libunwind-arm += -lunwind -lunwind-arm
- FEATURE_CHECK_LDFLAGS-libunwind-aarch64 += -lunwind -lunwind-aarch64
- FEATURE_CHECK_LDFLAGS-libunwind-x86 += -lunwind -llzma -lunwind-x86
- FEATURE_CHECK_LDFLAGS-libunwind-x86_64 += -lunwind -llzma -lunwind-x86_64
-endif
-
ifdef CSINCLUDES
LIBOPENCSD_CFLAGS := -I$(CSINCLUDES)
endif
@@ -446,7 +368,6 @@ endif
ifdef NO_LIBELF
NO_LIBDW := 1
- NO_LIBUNWIND := 1
NO_LIBBPF := 1
NO_JVMTI := 1
else
@@ -637,49 +558,6 @@ ifeq ($(SRCARCH),powerpc)
endif
endif
-ifndef NO_LIBUNWIND
- have_libunwind :=
-
- $(call feature_check,libunwind)
-
- $(call feature_check,libunwind-x86)
- ifeq ($(feature-libunwind-x86), 1)
- $(call detected,CONFIG_LIBUNWIND_X86)
- CFLAGS += -DHAVE_LIBUNWIND_X86_SUPPORT
- LDFLAGS += -lunwind-x86
- EXTLIBS_LIBUNWIND += -lunwind-x86
- have_libunwind = 1
- endif
-
- $(call feature_check,libunwind-aarch64)
- ifeq ($(feature-libunwind-aarch64), 1)
- $(call detected,CONFIG_LIBUNWIND_AARCH64)
- CFLAGS += -DHAVE_LIBUNWIND_AARCH64_SUPPORT
- LDFLAGS += -lunwind-aarch64
- EXTLIBS_LIBUNWIND += -lunwind-aarch64
- have_libunwind = 1
- $(call feature_check,libunwind-debug-frame-aarch64)
- ifneq ($(feature-libunwind-debug-frame-aarch64), 1)
- $(warning No debug_frame support found in libunwind-aarch64)
- CFLAGS += -DNO_LIBUNWIND_DEBUG_FRAME_AARCH64
- endif
- endif
-
- ifneq ($(feature-libunwind), 1)
- $(warning No libunwind found. Please install libunwind-dev[el] >= 1.1 and/or set LIBUNWIND_DIR and set LIBUNWIND=1 in the make command line as it is opt-in now)
- NO_LOCAL_LIBUNWIND := 1
- else
- have_libunwind := 1
- $(call detected,CONFIG_LOCAL_LIBUNWIND)
- endif
-
- ifneq ($(have_libunwind), 1)
- NO_LIBUNWIND := 1
- endif
-else
- NO_LOCAL_LIBUNWIND := 1
-endif
-
ifndef NO_LIBBPF
ifneq ($(feature-bpf), 1)
$(warning BPF API too old. Please install recent kernel headers. BPF support in 'perf record' is disabled.)
@@ -742,17 +620,12 @@ dwarf-post-unwind := 1
dwarf-post-unwind-text := BUG
# setup DWARF post unwinder
-ifdef NO_LIBUNWIND
- ifdef NO_LIBDW
- $(warning Disabling post unwind, no support found.)
- dwarf-post-unwind := 0
- else
- dwarf-post-unwind-text := libdw
- $(call detected,CONFIG_LIBDW_DWARF_UNWIND)
- endif
+ifdef NO_LIBDW
+ $(warning Disabling post unwind, no support found.)
+ dwarf-post-unwind := 0
else
- dwarf-post-unwind-text := libunwind
- $(call detected,CONFIG_LIBUNWIND)
+ dwarf-post-unwind-text := libdw
+ $(call detected,CONFIG_LIBDW_DWARF_UNWIND)
endif
ifeq ($(dwarf-post-unwind),1)
@@ -760,31 +633,6 @@ ifeq ($(dwarf-post-unwind),1)
$(call detected,CONFIG_DWARF_UNWIND)
endif
-ifndef NO_LIBUNWIND
- ifndef NO_LOCAL_LIBUNWIND
- ifeq ($(SRCARCH),$(filter $(SRCARCH),arm arm64))
- $(call feature_check,libunwind-debug-frame)
- ifneq ($(feature-libunwind-debug-frame), 1)
- $(warning No debug_frame support found in libunwind)
- CFLAGS += -DNO_LIBUNWIND_DEBUG_FRAME
- endif
- else
- # non-ARM has no dwarf_find_debug_frame() function:
- CFLAGS += -DNO_LIBUNWIND_DEBUG_FRAME
- endif
- EXTLIBS += $(LIBUNWIND_LIBS)
- LDFLAGS += $(LIBUNWIND_LIBS)
- endif
- ifeq ($(findstring -static,${LDFLAGS}),-static)
- # gcc -static links libgcc_eh which contans piece of libunwind
- LIBUNWIND_LDFLAGS += -Wl,--allow-multiple-definition
- endif
- CFLAGS += -DHAVE_LIBUNWIND_SUPPORT
- CFLAGS += $(LIBUNWIND_CFLAGS)
- LDFLAGS += $(LIBUNWIND_LDFLAGS)
- EXTLIBS += $(EXTLIBS_LIBUNWIND)
-endif
-
ifneq ($(NO_LIBTRACEEVENT),1)
$(call detected,CONFIG_TRACE)
endif
@@ -1273,7 +1121,6 @@ ifeq ($(VF),1)
$(call print_var,bindir)
$(call print_var,libdir)
$(call print_var,sysconfdir)
- $(call print_var,LIBUNWIND_DIR)
$(call print_var,LIBDW_DIR)
$(call print_var,JDIR)
diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf
index 11b63bafdb23..4b798fc53650 100644
--- a/tools/perf/Makefile.perf
+++ b/tools/perf/Makefile.perf
@@ -55,9 +55,6 @@ include ../scripts/utilities.mak
#
# Define NO_LIBELF if you do not want libelf dependency (e.g. cross-builds)
#
-# Define LIBUNWIND if you do not want libunwind dependency for dwarf
-# backtrace post unwind.
-#
# Define NO_BACKTRACE if you do not want stack backtrace debug feature
#
# Define NO_LIBNUMA if you do not want numa perf benchmark
diff --git a/tools/perf/arch/arm/util/Build b/tools/perf/arch/arm/util/Build
index b94bf3c5279a..dbf3524773cf 100644
--- a/tools/perf/arch/arm/util/Build
+++ b/tools/perf/arch/arm/util/Build
@@ -1,3 +1,2 @@
-perf-util-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o
perf-util-y += pmu.o auxtrace.o cs-etm.o
diff --git a/tools/perf/arch/arm/util/unwind-libunwind.c b/tools/perf/arch/arm/util/unwind-libunwind.c
deleted file mode 100644
index 438906bf0014..000000000000
--- a/tools/perf/arch/arm/util/unwind-libunwind.c
+++ /dev/null
@@ -1,50 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-
-#include <errno.h>
-#include <libunwind.h>
-#include "perf_regs.h"
-#include "../../../util/unwind.h"
-#include "../../../util/debug.h"
-
-int libunwind__arch_reg_id(int regnum)
-{
- switch (regnum) {
- case UNW_ARM_R0:
- return PERF_REG_ARM_R0;
- case UNW_ARM_R1:
- return PERF_REG_ARM_R1;
- case UNW_ARM_R2:
- return PERF_REG_ARM_R2;
- case UNW_ARM_R3:
- return PERF_REG_ARM_R3;
- case UNW_ARM_R4:
- return PERF_REG_ARM_R4;
- case UNW_ARM_R5:
- return PERF_REG_ARM_R5;
- case UNW_ARM_R6:
- return PERF_REG_ARM_R6;
- case UNW_ARM_R7:
- return PERF_REG_ARM_R7;
- case UNW_ARM_R8:
- return PERF_REG_ARM_R8;
- case UNW_ARM_R9:
- return PERF_REG_ARM_R9;
- case UNW_ARM_R10:
- return PERF_REG_ARM_R10;
- case UNW_ARM_R11:
- return PERF_REG_ARM_FP;
- case UNW_ARM_R12:
- return PERF_REG_ARM_IP;
- case UNW_ARM_R13:
- return PERF_REG_ARM_SP;
- case UNW_ARM_R14:
- return PERF_REG_ARM_LR;
- case UNW_ARM_R15:
- return PERF_REG_ARM_PC;
- default:
- pr_err("unwind: invalid reg id %d\n", regnum);
- return -EINVAL;
- }
-
- return -EINVAL;
-}
diff --git a/tools/perf/arch/arm64/util/Build b/tools/perf/arch/arm64/util/Build
index 4e06a08d281a..4b70c4788c80 100644
--- a/tools/perf/arch/arm64/util/Build
+++ b/tools/perf/arch/arm64/util/Build
@@ -1,4 +1,3 @@
-perf-util-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o
perf-util-y += ../../arm/util/auxtrace.o
perf-util-y += ../../arm/util/cs-etm.o
perf-util-y += ../../arm/util/pmu.o
diff --git a/tools/perf/arch/arm64/util/unwind-libunwind.c b/tools/perf/arch/arm64/util/unwind-libunwind.c
deleted file mode 100644
index 871af5992298..000000000000
--- a/tools/perf/arch/arm64/util/unwind-libunwind.c
+++ /dev/null
@@ -1,17 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-#include <errno.h>
-
-#ifndef REMOTE_UNWIND_LIBUNWIND
-#include <libunwind.h>
-#include "perf_regs.h"
-#include "../../../util/unwind.h"
-#endif
-#include "../../../util/debug.h"
-
-int LIBUNWIND__ARCH_REG_ID(int regnum)
-{
- if (regnum < 0 || regnum >= PERF_REG_ARM64_EXTENDED_MAX)
- return -EINVAL;
-
- return regnum;
-}
diff --git a/tools/perf/arch/loongarch/util/Build b/tools/perf/arch/loongarch/util/Build
index 3ad73d0289f3..e4572b644906 100644
--- a/tools/perf/arch/loongarch/util/Build
+++ b/tools/perf/arch/loongarch/util/Build
@@ -1,4 +1,3 @@
perf-util-y += header.o
-perf-util-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o
perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
diff --git a/tools/perf/arch/loongarch/util/unwind-libunwind.c b/tools/perf/arch/loongarch/util/unwind-libunwind.c
deleted file mode 100644
index f693167b86ef..000000000000
--- a/tools/perf/arch/loongarch/util/unwind-libunwind.c
+++ /dev/null
@@ -1,82 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-
-#include <errno.h>
-#include <libunwind.h>
-#include "perf_regs.h"
-#include "../../util/unwind.h"
-#include "util/debug.h"
-
-int libunwind__arch_reg_id(int regnum)
-{
- switch (regnum) {
- case UNW_LOONGARCH64_R1:
- return PERF_REG_LOONGARCH_R1;
- case UNW_LOONGARCH64_R2:
- return PERF_REG_LOONGARCH_R2;
- case UNW_LOONGARCH64_R3:
- return PERF_REG_LOONGARCH_R3;
- case UNW_LOONGARCH64_R4:
- return PERF_REG_LOONGARCH_R4;
- case UNW_LOONGARCH64_R5:
- return PERF_REG_LOONGARCH_R5;
- case UNW_LOONGARCH64_R6:
- return PERF_REG_LOONGARCH_R6;
- case UNW_LOONGARCH64_R7:
- return PERF_REG_LOONGARCH_R7;
- case UNW_LOONGARCH64_R8:
- return PERF_REG_LOONGARCH_R8;
- case UNW_LOONGARCH64_R9:
- return PERF_REG_LOONGARCH_R9;
- case UNW_LOONGARCH64_R10:
- return PERF_REG_LOONGARCH_R10;
- case UNW_LOONGARCH64_R11:
- return PERF_REG_LOONGARCH_R11;
- case UNW_LOONGARCH64_R12:
- return PERF_REG_LOONGARCH_R12;
- case UNW_LOONGARCH64_R13:
- return PERF_REG_LOONGARCH_R13;
- case UNW_LOONGARCH64_R14:
- return PERF_REG_LOONGARCH_R14;
- case UNW_LOONGARCH64_R15:
- return PERF_REG_LOONGARCH_R15;
- case UNW_LOONGARCH64_R16:
- return PERF_REG_LOONGARCH_R16;
- case UNW_LOONGARCH64_R17:
- return PERF_REG_LOONGARCH_R17;
- case UNW_LOONGARCH64_R18:
- return PERF_REG_LOONGARCH_R18;
- case UNW_LOONGARCH64_R19:
- return PERF_REG_LOONGARCH_R19;
- case UNW_LOONGARCH64_R20:
- return PERF_REG_LOONGARCH_R20;
- case UNW_LOONGARCH64_R21:
- return PERF_REG_LOONGARCH_R21;
- case UNW_LOONGARCH64_R22:
- return PERF_REG_LOONGARCH_R22;
- case UNW_LOONGARCH64_R23:
- return PERF_REG_LOONGARCH_R23;
- case UNW_LOONGARCH64_R24:
- return PERF_REG_LOONGARCH_R24;
- case UNW_LOONGARCH64_R25:
- return PERF_REG_LOONGARCH_R25;
- case UNW_LOONGARCH64_R26:
- return PERF_REG_LOONGARCH_R26;
- case UNW_LOONGARCH64_R27:
- return PERF_REG_LOONGARCH_R27;
- case UNW_LOONGARCH64_R28:
- return PERF_REG_LOONGARCH_R28;
- case UNW_LOONGARCH64_R29:
- return PERF_REG_LOONGARCH_R29;
- case UNW_LOONGARCH64_R30:
- return PERF_REG_LOONGARCH_R30;
- case UNW_LOONGARCH64_R31:
- return PERF_REG_LOONGARCH_R31;
- case UNW_LOONGARCH64_PC:
- return PERF_REG_LOONGARCH_PC;
- default:
- pr_err("unwind: invalid reg id %d\n", regnum);
- return -EINVAL;
- }
-
- return -EINVAL;
-}
diff --git a/tools/perf/arch/mips/util/Build b/tools/perf/arch/mips/util/Build
index 818b808a8247..e69de29bb2d1 100644
--- a/tools/perf/arch/mips/util/Build
+++ b/tools/perf/arch/mips/util/Build
@@ -1 +0,0 @@
-perf-util-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o
diff --git a/tools/perf/arch/mips/util/unwind-libunwind.c b/tools/perf/arch/mips/util/unwind-libunwind.c
deleted file mode 100644
index 0d8c99c29da6..000000000000
--- a/tools/perf/arch/mips/util/unwind-libunwind.c
+++ /dev/null
@@ -1,22 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-
-#include <errno.h>
-#include <libunwind.h>
-#include "perf_regs.h"
-#include "../../util/unwind.h"
-#include "util/debug.h"
-
-int libunwind__arch_reg_id(int regnum)
-{
- switch (regnum) {
- case UNW_MIPS_R1 ... UNW_MIPS_R25:
- return regnum - UNW_MIPS_R1 + PERF_REG_MIPS_R1;
- case UNW_MIPS_R28 ... UNW_MIPS_R31:
- return regnum - UNW_MIPS_R28 + PERF_REG_MIPS_R28;
- case UNW_MIPS_PC:
- return PERF_REG_MIPS_PC;
- default:
- pr_err("unwind: invalid reg id %d\n", regnum);
- return -EINVAL;
- }
-}
diff --git a/tools/perf/arch/powerpc/util/Build b/tools/perf/arch/powerpc/util/Build
index d66574cbb9a9..ae928050e07a 100644
--- a/tools/perf/arch/powerpc/util/Build
+++ b/tools/perf/arch/powerpc/util/Build
@@ -6,5 +6,4 @@ perf-util-y += evsel.o
perf-util-$(CONFIG_LIBDW) += skip-callchain-idx.o
-perf-util-$(CONFIG_LIBUNWIND) += unwind-libunwind.o
perf-util-y += auxtrace.o
diff --git a/tools/perf/arch/powerpc/util/unwind-libunwind.c b/tools/perf/arch/powerpc/util/unwind-libunwind.c
deleted file mode 100644
index 90a6beda20de..000000000000
--- a/tools/perf/arch/powerpc/util/unwind-libunwind.c
+++ /dev/null
@@ -1,92 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Copyright 2016 Chandan Kumar, IBM Corporation.
- */
-
-#include <errno.h>
-#include <libunwind.h>
-#include <asm/perf_regs.h>
-#include "../../util/unwind.h"
-#include "../../util/debug.h"
-
-int libunwind__arch_reg_id(int regnum)
-{
- switch (regnum) {
- case UNW_PPC64_R0:
- return PERF_REG_POWERPC_R0;
- case UNW_PPC64_R1:
- return PERF_REG_POWERPC_R1;
- case UNW_PPC64_R2:
- return PERF_REG_POWERPC_R2;
- case UNW_PPC64_R3:
- return PERF_REG_POWERPC_R3;
- case UNW_PPC64_R4:
- return PERF_REG_POWERPC_R4;
- case UNW_PPC64_R5:
- return PERF_REG_POWERPC_R5;
- case UNW_PPC64_R6:
- return PERF_REG_POWERPC_R6;
- case UNW_PPC64_R7:
- return PERF_REG_POWERPC_R7;
- case UNW_PPC64_R8:
- return PERF_REG_POWERPC_R8;
- case UNW_PPC64_R9:
- return PERF_REG_POWERPC_R9;
- case UNW_PPC64_R10:
- return PERF_REG_POWERPC_R10;
- case UNW_PPC64_R11:
- return PERF_REG_POWERPC_R11;
- case UNW_PPC64_R12:
- return PERF_REG_POWERPC_R12;
- case UNW_PPC64_R13:
- return PERF_REG_POWERPC_R13;
- case UNW_PPC64_R14:
- return PERF_REG_POWERPC_R14;
- case UNW_PPC64_R15:
- return PERF_REG_POWERPC_R15;
- case UNW_PPC64_R16:
- return PERF_REG_POWERPC_R16;
- case UNW_PPC64_R17:
- return PERF_REG_POWERPC_R17;
- case UNW_PPC64_R18:
- return PERF_REG_POWERPC_R18;
- case UNW_PPC64_R19:
- return PERF_REG_POWERPC_R19;
- case UNW_PPC64_R20:
- return PERF_REG_POWERPC_R20;
- case UNW_PPC64_R21:
- return PERF_REG_POWERPC_R21;
- case UNW_PPC64_R22:
- return PERF_REG_POWERPC_R22;
- case UNW_PPC64_R23:
- return PERF_REG_POWERPC_R23;
- case UNW_PPC64_R24:
- return PERF_REG_POWERPC_R24;
- case UNW_PPC64_R25:
- return PERF_REG_POWERPC_R25;
- case UNW_PPC64_R26:
- return PERF_REG_POWERPC_R26;
- case UNW_PPC64_R27:
- return PERF_REG_POWERPC_R27;
- case UNW_PPC64_R28:
- return PERF_REG_POWERPC_R28;
- case UNW_PPC64_R29:
- return PERF_REG_POWERPC_R29;
- case UNW_PPC64_R30:
- return PERF_REG_POWERPC_R30;
- case UNW_PPC64_R31:
- return PERF_REG_POWERPC_R31;
- case UNW_PPC64_LR:
- return PERF_REG_POWERPC_LINK;
- case UNW_PPC64_CTR:
- return PERF_REG_POWERPC_CTR;
- case UNW_PPC64_XER:
- return PERF_REG_POWERPC_XER;
- case UNW_PPC64_NIP:
- return PERF_REG_POWERPC_NIP;
- default:
- pr_err("unwind: invalid reg id %d\n", regnum);
- return -EINVAL;
- }
- return -EINVAL;
-}
diff --git a/tools/perf/arch/x86/util/Build b/tools/perf/arch/x86/util/Build
index b94c91984c66..5045457ae39c 100644
--- a/tools/perf/arch/x86/util/Build
+++ b/tools/perf/arch/x86/util/Build
@@ -9,7 +9,6 @@ perf-util-y += mem-events.o
perf-util-y += evsel.o
perf-util-y += iostat.o
-perf-util-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o
perf-util-y += auxtrace.o
perf-util-y += intel-pt.o
diff --git a/tools/perf/arch/x86/util/unwind-libunwind.c b/tools/perf/arch/x86/util/unwind-libunwind.c
deleted file mode 100644
index 47357973b55b..000000000000
--- a/tools/perf/arch/x86/util/unwind-libunwind.c
+++ /dev/null
@@ -1,115 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-
-#include <errno.h>
-#include "../../util/debug.h"
-#ifndef REMOTE_UNWIND_LIBUNWIND
-#include <libunwind.h>
-#include "perf_regs.h"
-#include "../../util/unwind.h"
-#endif
-
-#ifdef HAVE_ARCH_X86_64_SUPPORT
-int LIBUNWIND__ARCH_REG_ID(int regnum)
-{
- int id;
-
- switch (regnum) {
- case UNW_X86_64_RAX:
- id = PERF_REG_X86_AX;
- break;
- case UNW_X86_64_RDX:
- id = PERF_REG_X86_DX;
- break;
- case UNW_X86_64_RCX:
- id = PERF_REG_X86_CX;
- break;
- case UNW_X86_64_RBX:
- id = PERF_REG_X86_BX;
- break;
- case UNW_X86_64_RSI:
- id = PERF_REG_X86_SI;
- break;
- case UNW_X86_64_RDI:
- id = PERF_REG_X86_DI;
- break;
- case UNW_X86_64_RBP:
- id = PERF_REG_X86_BP;
- break;
- case UNW_X86_64_RSP:
- id = PERF_REG_X86_SP;
- break;
- case UNW_X86_64_R8:
- id = PERF_REG_X86_R8;
- break;
- case UNW_X86_64_R9:
- id = PERF_REG_X86_R9;
- break;
- case UNW_X86_64_R10:
- id = PERF_REG_X86_R10;
- break;
- case UNW_X86_64_R11:
- id = PERF_REG_X86_R11;
- break;
- case UNW_X86_64_R12:
- id = PERF_REG_X86_R12;
- break;
- case UNW_X86_64_R13:
- id = PERF_REG_X86_R13;
- break;
- case UNW_X86_64_R14:
- id = PERF_REG_X86_R14;
- break;
- case UNW_X86_64_R15:
- id = PERF_REG_X86_R15;
- break;
- case UNW_X86_64_RIP:
- id = PERF_REG_X86_IP;
- break;
- default:
- pr_err("unwind: invalid reg id %d\n", regnum);
- return -EINVAL;
- }
-
- return id;
-}
-#else
-int LIBUNWIND__ARCH_REG_ID(int regnum)
-{
- int id;
-
- switch (regnum) {
- case UNW_X86_EAX:
- id = PERF_REG_X86_AX;
- break;
- case UNW_X86_EDX:
- id = PERF_REG_X86_DX;
- break;
- case UNW_X86_ECX:
- id = PERF_REG_X86_CX;
- break;
- case UNW_X86_EBX:
- id = PERF_REG_X86_BX;
- break;
- case UNW_X86_ESI:
- id = PERF_REG_X86_SI;
- break;
- case UNW_X86_EDI:
- id = PERF_REG_X86_DI;
- break;
- case UNW_X86_EBP:
- id = PERF_REG_X86_BP;
- break;
- case UNW_X86_ESP:
- id = PERF_REG_X86_SP;
- break;
- case UNW_X86_EIP:
- id = PERF_REG_X86_IP;
- break;
- default:
- pr_err("unwind: invalid reg id %d\n", regnum);
- return -EINVAL;
- }
-
- return id;
-}
-#endif /* HAVE_ARCH_X86_64_SUPPORT */
diff --git a/tools/perf/builtin-check.c b/tools/perf/builtin-check.c
index 3641d263b345..e6b4016305a6 100644
--- a/tools/perf/builtin-check.c
+++ b/tools/perf/builtin-check.c
@@ -56,7 +56,6 @@ struct feature_status supported_features[] = {
FEATURE_STATUS("libpython", HAVE_LIBPYTHON_SUPPORT),
FEATURE_STATUS("libslang", HAVE_SLANG_SUPPORT),
FEATURE_STATUS("libtraceevent", HAVE_LIBTRACEEVENT),
- FEATURE_STATUS_TIP("libunwind", HAVE_LIBUNWIND_SUPPORT, "Deprecated, use LIBUNWIND=1 and install libunwind-dev[el] to build with it"),
FEATURE_STATUS("lzma", HAVE_LZMA_SUPPORT),
FEATURE_STATUS("numa_num_possible_cpus", HAVE_LIBNUMA_SUPPORT),
FEATURE_STATUS("zlib", HAVE_ZLIB_SUPPORT),
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index 343c0ada5ea1..c5abf507cdac 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -460,9 +460,9 @@ static int report__setup_sample_type(struct report *rep)
if (!(evlist__combined_branch_type(session->evlist) & PERF_SAMPLE_BRANCH_ANY))
rep->nonany_branch_mode = true;
-#if !defined(HAVE_LIBUNWIND_SUPPORT) && !defined(HAVE_LIBDW_SUPPORT)
+#ifndef HAVE_LIBDW_SUPPORT
if (dwarf_callchain_users) {
- ui__warning("Please install libunwind or libdw "
+ ui__warning("Please install libdw "
"development packages during the perf build.\n");
}
#endif
diff --git a/tools/perf/tests/make b/tools/perf/tests/make
index 6587dc326d1b..3fd505ddd3f9 100644
--- a/tools/perf/tests/make
+++ b/tools/perf/tests/make
@@ -83,7 +83,6 @@ make_no_ui := NO_SLANG=1 NO_GTK2=1
make_no_demangle := NO_DEMANGLE=1
make_no_libelf := NO_LIBELF=1
make_no_libdw := NO_LIBDW=1
-make_libunwind := LIBUNWIND=1
make_no_backtrace := NO_BACKTRACE=1
make_no_libcapstone := NO_CAPSTONE=1
make_libcapstone_dlopen := LIBCAPSTONE_DLOPEN=1
@@ -158,7 +157,6 @@ run += make_no_ui
run += make_no_demangle
run += make_no_libelf
run += make_no_libdw
-run += make_libunwind
run += make_no_libdw_dwarf_unwind
run += make_no_backtrace
run += make_no_libcapstone
diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index 70cc91d00804..220c1951c508 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -225,11 +225,6 @@ perf-util-$(CONFIG_LIBDW) += annotate-data.o
perf-util-$(CONFIG_LIBDW) += libdw.o
perf-util-$(CONFIG_LIBDW) += unwind-libdw.o
-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
-perf-util-$(CONFIG_LIBUNWIND_AARCH64) += libunwind/arm64.o
-
ifeq ($(CONFIG_LIBTRACEEVENT),y)
perf-util-$(CONFIG_LIBBABELTRACE) += data-convert-bt.o
endif
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c
index f879b84f8ff9..18bf42c99c1d 100644
--- a/tools/perf/util/callchain.c
+++ b/tools/perf/util/callchain.c
@@ -1742,7 +1742,7 @@ void callchain_param_setup(u64 sample_type, uint16_t e_machine)
}
/*
- * It's necessary to use libunwind to reliably determine the caller of
+ * It's necessary to use libdw to reliably determine the caller of
* a leaf function on aarch64, as otherwise we cannot know whether to
* start from the LR or FP.
*
diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h
index ede691e9a249..b559daf36a4e 100644
--- a/tools/perf/util/dso.h
+++ b/tools/perf/util/dso.h
@@ -233,12 +233,6 @@ struct dso_data {
int status;
u32 status_seen;
u64 file_size;
-#ifdef HAVE_LIBUNWIND_SUPPORT
- u64 elf_base_addr;
- u64 debug_frame_offset;
- u64 eh_frame_hdr_addr;
- u64 eh_frame_hdr_offset;
-#endif
};
struct dso_bpf_prog {
diff --git a/tools/perf/util/libunwind/arm64.c b/tools/perf/util/libunwind/arm64.c
deleted file mode 100644
index 37ecef0c53b9..000000000000
--- a/tools/perf/util/libunwind/arm64.c
+++ /dev/null
@@ -1,40 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * This file setups defines to compile arch specific binary from the
- * generic one.
- *
- * The function 'LIBUNWIND__ARCH_REG_ID' name is set according to arch
- * name and the definition of this function is included directly from
- * 'arch/arm64/util/unwind-libunwind.c', to make sure that this function
- * is defined no matter what arch the host is.
- *
- * Finally, the arch specific unwind methods are exported which will
- * be assigned to each arm64 thread.
- */
-
-#define REMOTE_UNWIND_LIBUNWIND
-
-/* Define arch specific functions & regs for libunwind, should be
- * defined before including "unwind.h"
- */
-#define LIBUNWIND__ARCH_REG_ID(regnum) libunwind__arm64_reg_id(regnum)
-
-#include "unwind.h"
-#include "libunwind-aarch64.h"
-#define perf_event_arm_regs perf_event_arm64_regs
-#include <../../../arch/arm64/include/uapi/asm/perf_regs.h>
-#undef perf_event_arm_regs
-#include "../../arch/arm64/util/unwind-libunwind.c"
-
-/* NO_LIBUNWIND_DEBUG_FRAME is a feature flag for local libunwind,
- * assign NO_LIBUNWIND_DEBUG_FRAME_AARCH64 to it for compiling arm64
- * unwind methods.
- */
-#undef NO_LIBUNWIND_DEBUG_FRAME
-#ifdef NO_LIBUNWIND_DEBUG_FRAME_AARCH64
-#define NO_LIBUNWIND_DEBUG_FRAME
-#endif
-#include "util/unwind-libunwind-local.c"
-
-struct unwind_libunwind_ops *
-arm64_unwind_libunwind_ops = &_unwind_libunwind_ops;
diff --git a/tools/perf/util/libunwind/x86_32.c b/tools/perf/util/libunwind/x86_32.c
deleted file mode 100644
index 1697dece1b74..000000000000
--- a/tools/perf/util/libunwind/x86_32.c
+++ /dev/null
@@ -1,41 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * This file setups defines to compile arch specific binary from the
- * generic one.
- *
- * The function 'LIBUNWIND__ARCH_REG_ID' name is set according to arch
- * name and the definition of this function is included directly from
- * 'arch/x86/util/unwind-libunwind.c', to make sure that this function
- * is defined no matter what arch the host is.
- *
- * Finally, the arch specific unwind methods are exported which will
- * be assigned to each x86 thread.
- */
-
-#define REMOTE_UNWIND_LIBUNWIND
-
-/* Define arch specific functions & regs for libunwind, should be
- * defined before including "unwind.h"
- */
-#define LIBUNWIND__ARCH_REG_ID(regnum) libunwind__x86_reg_id(regnum)
-
-#include "unwind.h"
-#include "libunwind-x86.h"
-#include <../../../../arch/x86/include/uapi/asm/perf_regs.h>
-
-/* HAVE_ARCH_X86_64_SUPPORT is used in'arch/x86/util/unwind-libunwind.c'
- * for x86_32, we undef it to compile code for x86_32 only.
- */
-#undef HAVE_ARCH_X86_64_SUPPORT
-#include "../../arch/x86/util/unwind-libunwind.c"
-
-/* Explicitly define NO_LIBUNWIND_DEBUG_FRAME, because non-ARM has no
- * dwarf_find_debug_frame() function.
- */
-#ifndef NO_LIBUNWIND_DEBUG_FRAME
-#define NO_LIBUNWIND_DEBUG_FRAME
-#endif
-#include "util/unwind-libunwind-local.c"
-
-struct unwind_libunwind_ops *
-x86_32_unwind_libunwind_ops = &_unwind_libunwind_ops;
diff --git a/tools/perf/util/maps.c b/tools/perf/util/maps.c
index 4092211cff62..b78ca3c0e6ea 100644
--- a/tools/perf/util/maps.c
+++ b/tools/perf/util/maps.c
@@ -37,10 +37,6 @@ DECLARE_RC_STRUCT(maps) {
*/
struct map **maps_by_name;
struct machine *machine;
-#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
@@ -186,27 +182,6 @@ refcount_t *maps__refcnt(struct maps *maps)
return &RC_CHK_ACCESS(maps)->refcnt;
}
-#ifdef HAVE_LIBUNWIND_SUPPORT
-void *maps__addr_space(const struct maps *maps)
-{
- return RC_CHK_ACCESS(maps)->addr_space;
-}
-
-void maps__set_addr_space(struct maps *maps, void *addr_space)
-{
- RC_CHK_ACCESS(maps)->addr_space = addr_space;
-}
-
-const struct unwind_libunwind_ops *maps__unwind_libunwind_ops(const struct maps *maps)
-{
- return RC_CHK_ACCESS(maps)->unwind_libunwind_ops;
-}
-
-void maps__set_unwind_libunwind_ops(struct maps *maps, const struct unwind_libunwind_ops *ops)
-{
- RC_CHK_ACCESS(maps)->unwind_libunwind_ops = ops;
-}
-#endif
#ifdef HAVE_LIBDW_SUPPORT
void *maps__libdw_addr_space_dwfl(const struct maps *maps)
{
@@ -230,10 +205,6 @@ static void maps__init(struct maps *maps, struct machine *machine)
RC_CHK_ACCESS(maps)->maps_by_address = NULL;
RC_CHK_ACCESS(maps)->maps_by_name = NULL;
RC_CHK_ACCESS(maps)->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
@@ -257,7 +228,6 @@ 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
@@ -1067,14 +1037,11 @@ int maps__copy_from(struct maps *dest, struct maps *parent)
if (!new)
err = -ENOMEM;
else {
- err = unwind__prepare_access(dest, new, NULL);
- if (!err) {
- dest_maps_by_address[i] = new;
- map__set_kmap_maps(new, dest);
- if (dest_maps_by_name)
- dest_maps_by_name[i] = map__get(new);
- RC_CHK_ACCESS(dest)->nr_maps = i + 1;
- }
+ dest_maps_by_address[i] = new;
+ map__set_kmap_maps(new, dest);
+ if (dest_maps_by_name)
+ dest_maps_by_name[i] = map__get(new);
+ RC_CHK_ACCESS(dest)->nr_maps = i + 1;
}
if (err)
map__put(new);
@@ -1099,9 +1066,7 @@ int maps__copy_from(struct maps *dest, struct maps *parent)
if (!new)
err = -ENOMEM;
else {
- err = unwind__prepare_access(dest, new, NULL);
- if (!err)
- err = __maps__insert(dest, new);
+ err = __maps__insert(dest, new);
}
map__put(new);
}
diff --git a/tools/perf/util/maps.h b/tools/perf/util/maps.h
index 20c52084ba9e..7f7f56d39f25 100644
--- a/tools/perf/util/maps.h
+++ b/tools/perf/util/maps.h
@@ -46,12 +46,6 @@ struct machine *maps__machine(const struct maps *maps);
unsigned int maps__nr_maps(const struct maps *maps); /* Test only. */
refcount_t *maps__refcnt(struct maps *maps); /* Test only. */
-#ifdef HAVE_LIBUNWIND_SUPPORT
-void *maps__addr_space(const struct maps *maps);
-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);
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c
index 22be77225bb0..046cacb67129 100644
--- a/tools/perf/util/thread.c
+++ b/tools/perf/util/thread.c
@@ -263,9 +263,6 @@ static int ____thread__set_comm(struct thread *thread, const char *str,
if (!new)
return -ENOMEM;
list_add(&new->list, thread__comm_list(thread));
-
- if (exec)
- unwind__flush_access(thread__maps(thread));
}
thread__set_comm_set(thread, true);
@@ -357,49 +354,14 @@ size_t thread__fprintf(struct thread *thread, FILE *fp)
int thread__insert_map(struct thread *thread, struct map *map)
{
- int ret;
-
- ret = unwind__prepare_access(thread__maps(thread), map, NULL);
- if (ret)
- return ret;
-
return maps__fixup_overlap_and_insert(thread__maps(thread), map);
}
-struct thread__prepare_access_maps_cb_args {
- int err;
- struct maps *maps;
-};
-
-static int thread__prepare_access_maps_cb(struct map *map, void *data)
-{
- bool initialized = false;
- struct thread__prepare_access_maps_cb_args *args = data;
-
- args->err = unwind__prepare_access(args->maps, map, &initialized);
-
- return (args->err || initialized) ? 1 : 0;
-}
-
-static int thread__prepare_access(struct thread *thread)
-{
- struct thread__prepare_access_maps_cb_args args = {
- .err = 0,
- };
-
- if (dwarf_callchain_users) {
- args.maps = thread__maps(thread);
- maps__for_each_map(thread__maps(thread), thread__prepare_access_maps_cb, &args);
- }
-
- return args.err;
-}
-
static int thread__clone_maps(struct thread *thread, struct thread *parent, bool do_maps_clone)
{
/* This is new thread, we share map groups for process. */
if (thread__pid(thread) == thread__pid(parent))
- return thread__prepare_access(thread);
+ return 0;
if (maps__equal(thread__maps(thread), thread__maps(parent))) {
pr_debug("broken map groups on thread %d/%d parent %d/%d\n",
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h
index f5792d3e8a16..7bc5f41be0a5 100644
--- a/tools/perf/util/thread.h
+++ b/tools/perf/util/thread.h
@@ -19,7 +19,6 @@ struct addr_location;
struct map;
struct perf_record_namespaces;
struct thread_stack;
-struct unwind_libunwind_ops;
struct lbr_stitch {
struct list_head lists;
diff --git a/tools/perf/util/unwind-libunwind-local.c b/tools/perf/util/unwind-libunwind-local.c
deleted file mode 100644
index 5b39ce21e333..000000000000
--- a/tools/perf/util/unwind-libunwind-local.c
+++ /dev/null
@@ -1,832 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Post mortem Dwarf CFI based unwinding on top of regs and stack dumps.
- *
- * Lots of this code have been borrowed or heavily inspired from parts of
- * the libunwind 0.99 code which are (amongst other contributors I may have
- * forgotten):
- *
- * Copyright (C) 2002-2007 Hewlett-Packard Co
- * Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
- *
- * And the bugs have been added by:
- *
- * Copyright (C) 2010, Frederic Weisbecker <fweisbec@gmail.com>
- * Copyright (C) 2012, Jiri Olsa <jolsa@redhat.com>
- *
- */
-
-#include <elf.h>
-#include <errno.h>
-#include <gelf.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <string.h>
-#include <unistd.h>
-#include <sys/mman.h>
-#include <linux/list.h>
-#include <linux/zalloc.h>
-#ifndef REMOTE_UNWIND_LIBUNWIND
-#include <libunwind.h>
-#include <libunwind-ptrace.h>
-#endif
-#include "callchain.h"
-#include "thread.h"
-#include "session.h"
-#include "perf_regs.h"
-#include "unwind.h"
-#include "map.h"
-#include "symbol.h"
-#include "debug.h"
-#include "asm/bug.h"
-#include "dso.h"
-
-extern int
-UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,
- unw_word_t ip,
- unw_dyn_info_t *di,
- unw_proc_info_t *pi,
- int need_unwind_info, void *arg);
-
-#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table)
-
-extern int
-UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug,
- unw_word_t ip,
- unw_word_t segbase,
- const char *obj_name, unw_word_t start,
- unw_word_t end);
-
-#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame)
-
-#define DW_EH_PE_FORMAT_MASK 0x0f /* format of the encoded value */
-#define DW_EH_PE_APPL_MASK 0x70 /* how the value is to be applied */
-
-/* Pointer-encoding formats: */
-#define DW_EH_PE_omit 0xff
-#define DW_EH_PE_ptr 0x00 /* pointer-sized unsigned value */
-#define DW_EH_PE_udata4 0x03 /* unsigned 32-bit value */
-#define DW_EH_PE_udata8 0x04 /* unsigned 64-bit value */
-#define DW_EH_PE_sdata4 0x0b /* signed 32-bit value */
-#define DW_EH_PE_sdata8 0x0c /* signed 64-bit value */
-
-/* Pointer-encoding application: */
-#define DW_EH_PE_absptr 0x00 /* absolute value */
-#define DW_EH_PE_pcrel 0x10 /* rel. to addr. of encoded value */
-
-/*
- * The following are not documented by LSB v1.3, yet they are used by
- * GCC, presumably they aren't documented by LSB since they aren't
- * used on Linux:
- */
-#define DW_EH_PE_funcrel 0x40 /* start-of-procedure-relative */
-#define DW_EH_PE_aligned 0x50 /* aligned pointer */
-
-/* Flags intentionally not handled, since they're not needed:
- * #define DW_EH_PE_indirect 0x80
- * #define DW_EH_PE_uleb128 0x01
- * #define DW_EH_PE_udata2 0x02
- * #define DW_EH_PE_sleb128 0x09
- * #define DW_EH_PE_sdata2 0x0a
- * #define DW_EH_PE_textrel 0x20
- * #define DW_EH_PE_datarel 0x30
- */
-
-struct unwind_info {
- struct perf_sample *sample;
- struct machine *machine;
- struct thread *thread;
- bool best_effort;
-};
-
-#define dw_read(ptr, type, end) ({ \
- type *__p = (type *) ptr; \
- type __v; \
- if ((__p + 1) > (type *) end) \
- return -EINVAL; \
- __v = *__p++; \
- ptr = (typeof(ptr)) __p; \
- __v; \
- })
-
-static int __dw_read_encoded_value(u8 **p, u8 *end, u64 *val,
- u8 encoding)
-{
- u8 *cur = *p;
- *val = 0;
-
- switch (encoding) {
- case DW_EH_PE_omit:
- *val = 0;
- goto out;
- case DW_EH_PE_ptr:
- *val = dw_read(cur, unsigned long, end);
- goto out;
- default:
- break;
- }
-
- switch (encoding & DW_EH_PE_APPL_MASK) {
- case DW_EH_PE_absptr:
- break;
- case DW_EH_PE_pcrel:
- *val = (unsigned long) cur;
- break;
- default:
- return -EINVAL;
- }
-
- if ((encoding & 0x07) == 0x00)
- encoding |= DW_EH_PE_udata4;
-
- switch (encoding & DW_EH_PE_FORMAT_MASK) {
- case DW_EH_PE_sdata4:
- *val += dw_read(cur, s32, end);
- break;
- case DW_EH_PE_udata4:
- *val += dw_read(cur, u32, end);
- break;
- case DW_EH_PE_sdata8:
- *val += dw_read(cur, s64, end);
- break;
- case DW_EH_PE_udata8:
- *val += dw_read(cur, u64, end);
- break;
- default:
- return -EINVAL;
- }
-
- out:
- *p = cur;
- return 0;
-}
-
-#define dw_read_encoded_value(ptr, end, enc) ({ \
- u64 __v; \
- if (__dw_read_encoded_value(&ptr, end, &__v, enc)) { \
- return -EINVAL; \
- } \
- __v; \
- })
-
-static int elf_section_address_and_offset(int fd, const char *name, u64 *address, u64 *offset)
-{
- Elf *elf;
- GElf_Ehdr ehdr;
- GElf_Shdr shdr;
- int ret = -1;
-
- elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
- if (elf == NULL)
- return -1;
-
- if (gelf_getehdr(elf, &ehdr) == NULL)
- goto out_err;
-
- if (!elf_section_by_name(elf, &ehdr, &shdr, name, NULL))
- goto out_err;
-
- *address = shdr.sh_addr;
- *offset = shdr.sh_offset;
- ret = 0;
-out_err:
- elf_end(elf);
- return ret;
-}
-
-#ifndef NO_LIBUNWIND_DEBUG_FRAME
-static u64 elf_section_offset(int fd, const char *name)
-{
- u64 address, offset = 0;
-
- if (elf_section_address_and_offset(fd, name, &address, &offset))
- return 0;
-
- return offset;
-}
-#endif
-
-static u64 elf_base_address(int fd)
-{
- Elf *elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
- GElf_Phdr phdr;
- u64 retval = 0;
- size_t i, phdrnum = 0;
-
- if (elf == NULL)
- return 0;
- (void)elf_getphdrnum(elf, &phdrnum);
- /* PT_LOAD segments are sorted by p_vaddr, so the first has the minimum p_vaddr. */
- for (i = 0; i < phdrnum; i++) {
- if (gelf_getphdr(elf, i, &phdr) && phdr.p_type == PT_LOAD) {
- retval = phdr.p_vaddr & -getpagesize();
- break;
- }
- }
-
- elf_end(elf);
- return retval;
-}
-
-#ifndef NO_LIBUNWIND_DEBUG_FRAME
-static int elf_is_exec(int fd, const char *name)
-{
- Elf *elf;
- GElf_Ehdr ehdr;
- int retval = 0;
-
- elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
- if (elf == NULL)
- return 0;
- if (gelf_getehdr(elf, &ehdr) == NULL)
- goto out;
-
- retval = (ehdr.e_type == ET_EXEC);
-
-out:
- elf_end(elf);
- pr_debug("unwind: elf_is_exec(%s): %d\n", name, retval);
- return retval;
-}
-#endif
-
-struct table_entry {
- u32 start_ip_offset;
- u32 fde_offset;
-};
-
-struct eh_frame_hdr {
- unsigned char version;
- unsigned char eh_frame_ptr_enc;
- unsigned char fde_count_enc;
- unsigned char table_enc;
-
- /*
- * The rest of the header is variable-length and consists of the
- * following members:
- *
- * encoded_t eh_frame_ptr;
- * encoded_t fde_count;
- */
-
- /* A single encoded pointer should not be more than 8 bytes. */
- u64 enc[2];
-
- /*
- * struct {
- * encoded_t start_ip;
- * encoded_t fde_addr;
- * } binary_search_table[fde_count];
- */
- char data[];
-} __packed;
-
-static int unwind_spec_ehframe(struct dso *dso, struct machine *machine,
- u64 offset, u64 *table_data_offset, u64 *fde_count)
-{
- struct eh_frame_hdr hdr;
- u8 *enc = (u8 *) &hdr.enc;
- u8 *end = (u8 *) &hdr.data;
- ssize_t r;
-
- r = dso__data_read_offset(dso, machine, offset,
- (u8 *) &hdr, sizeof(hdr));
- if (r != sizeof(hdr))
- return -EINVAL;
-
- /* We dont need eh_frame_ptr, just skip it. */
- dw_read_encoded_value(enc, end, hdr.eh_frame_ptr_enc);
-
- *fde_count = dw_read_encoded_value(enc, end, hdr.fde_count_enc);
- *table_data_offset = enc - (u8 *) &hdr;
- return 0;
-}
-
-struct read_unwind_spec_eh_frame_maps_cb_args {
- struct dso *dso;
- u64 base_addr;
-};
-
-static int read_unwind_spec_eh_frame_maps_cb(struct map *map, void *data)
-{
-
- struct read_unwind_spec_eh_frame_maps_cb_args *args = data;
-
- if (map__dso(map) == args->dso && map__start(map) - map__pgoff(map) < args->base_addr)
- args->base_addr = map__start(map) - map__pgoff(map);
-
- return 0;
-}
-
-
-static int read_unwind_spec_eh_frame(struct dso *dso, struct unwind_info *ui,
- u64 *table_data, u64 *segbase,
- u64 *fde_count)
-{
- struct read_unwind_spec_eh_frame_maps_cb_args args = {
- .dso = dso,
- .base_addr = UINT64_MAX,
- };
- int ret, fd;
-
- if (dso__data(dso)->eh_frame_hdr_offset == 0) {
- if (!dso__data_get_fd(dso, ui->machine, &fd))
- return -EINVAL;
-
- /* Check the .eh_frame section for unwinding info */
- ret = elf_section_address_and_offset(fd, ".eh_frame_hdr",
- &dso__data(dso)->eh_frame_hdr_addr,
- &dso__data(dso)->eh_frame_hdr_offset);
- dso__data(dso)->elf_base_addr = elf_base_address(fd);
- dso__data_put_fd(dso);
- if (ret || dso__data(dso)->eh_frame_hdr_offset == 0)
- return -EINVAL;
- }
-
- maps__for_each_map(thread__maps(ui->thread), read_unwind_spec_eh_frame_maps_cb, &args);
-
- args.base_addr -= dso__data(dso)->elf_base_addr;
- /* Address of .eh_frame_hdr */
- *segbase = args.base_addr + dso__data(dso)->eh_frame_hdr_addr;
- ret = unwind_spec_ehframe(dso, ui->machine, dso__data(dso)->eh_frame_hdr_offset,
- table_data, fde_count);
- if (ret)
- return ret;
- /* binary_search_table offset plus .eh_frame_hdr address */
- *table_data += *segbase;
- return 0;
-}
-
-#ifndef NO_LIBUNWIND_DEBUG_FRAME
-static int read_unwind_spec_debug_frame(struct dso *dso,
- struct machine *machine, u64 *offset)
-{
- int fd;
- u64 ofs = dso__data(dso)->debug_frame_offset;
-
- /* debug_frame can reside in:
- * - dso
- * - debug pointed by symsrc_filename
- * - gnu_debuglink, which doesn't necessary
- * has to be pointed by symsrc_filename
- */
- if (ofs == 0) {
- if (dso__data_get_fd(dso, machine, &fd)) {
- ofs = elf_section_offset(fd, ".debug_frame");
- dso__data_put_fd(dso);
- }
-
- if (ofs <= 0) {
- fd = open(dso__symsrc_filename(dso), O_RDONLY);
- if (fd >= 0) {
- ofs = elf_section_offset(fd, ".debug_frame");
- close(fd);
- }
- }
-
- if (ofs <= 0) {
- char *debuglink = malloc(PATH_MAX);
- int ret = 0;
-
- if (debuglink == NULL) {
- pr_err("unwind: Can't read unwind spec debug frame.\n");
- return -ENOMEM;
- }
-
- ret = dso__read_binary_type_filename(
- dso, DSO_BINARY_TYPE__DEBUGLINK,
- machine->root_dir, debuglink, PATH_MAX);
- if (!ret) {
- fd = open(debuglink, O_RDONLY);
- if (fd >= 0) {
- ofs = elf_section_offset(fd,
- ".debug_frame");
- close(fd);
- }
- }
- if (ofs > 0) {
- if (dso__symsrc_filename(dso) != NULL) {
- pr_warning(
- "%s: overwrite symsrc(%s,%s)\n",
- __func__,
- dso__symsrc_filename(dso),
- debuglink);
- dso__free_symsrc_filename(dso);
- }
- dso__set_symsrc_filename(dso, debuglink);
- } else {
- free(debuglink);
- }
- }
-
- dso__data(dso)->debug_frame_offset = ofs;
- }
-
- *offset = ofs;
- if (*offset)
- return 0;
-
- return -EINVAL;
-}
-#endif
-
-static struct map *find_map(unw_word_t ip, struct unwind_info *ui)
-{
- struct addr_location al;
- struct map *ret;
-
- addr_location__init(&al);
- thread__find_map(ui->thread, PERF_RECORD_MISC_USER, ip, &al);
- ret = map__get(al.map);
- addr_location__exit(&al);
- return ret;
-}
-
-static int
-find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
- int need_unwind_info, void *arg)
-{
- struct unwind_info *ui = arg;
- struct map *map;
- struct dso *dso;
- unw_dyn_info_t di;
- u64 table_data, segbase, fde_count;
- int ret = -EINVAL;
-
- map = find_map(ip, ui);
- if (!map)
- return -EINVAL;
-
- dso = map__dso(map);
- if (!dso) {
- map__put(map);
- return -EINVAL;
- }
-
- pr_debug("unwind: find_proc_info dso %s\n", dso__name(dso));
-
- /* Check the .eh_frame section for unwinding info */
- if (!read_unwind_spec_eh_frame(dso, ui, &table_data, &segbase, &fde_count)) {
- memset(&di, 0, sizeof(di));
- di.format = UNW_INFO_FORMAT_REMOTE_TABLE;
- di.start_ip = map__start(map);
- di.end_ip = map__end(map);
- di.u.rti.segbase = segbase;
- di.u.rti.table_data = table_data;
- di.u.rti.table_len = fde_count * sizeof(struct table_entry)
- / sizeof(unw_word_t);
- ret = dwarf_search_unwind_table(as, ip, &di, pi,
- need_unwind_info, arg);
- }
-
-#ifndef NO_LIBUNWIND_DEBUG_FRAME
- /* Check the .debug_frame section for unwinding info */
- if (ret < 0 &&
- !read_unwind_spec_debug_frame(dso, ui->machine, &segbase)) {
- int fd;
- u64 start = map__start(map);
- unw_word_t base = start;
- const char *symfile;
-
- if (dso__data_get_fd(dso, ui->machine, &fd)) {
- if (elf_is_exec(fd, dso__name(dso)))
- base = 0;
- dso__data_put_fd(dso);
- }
-
- symfile = dso__symsrc_filename(dso) ?: dso__name(dso);
-
- memset(&di, 0, sizeof(di));
- if (dwarf_find_debug_frame(0, &di, ip, base, symfile, start, map__end(map)))
- ret = dwarf_search_unwind_table(as, ip, &di, pi,
- need_unwind_info, arg);
- }
-#endif
- map__put(map);
- return ret;
-}
-
-static int access_fpreg(unw_addr_space_t __maybe_unused as,
- unw_regnum_t __maybe_unused num,
- unw_fpreg_t __maybe_unused *val,
- int __maybe_unused __write,
- void __maybe_unused *arg)
-{
- pr_err("unwind: access_fpreg unsupported\n");
- return -UNW_EINVAL;
-}
-
-static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as,
- unw_word_t __maybe_unused *dil_addr,
- void __maybe_unused *arg)
-{
- return -UNW_ENOINFO;
-}
-
-static int resume(unw_addr_space_t __maybe_unused as,
- unw_cursor_t __maybe_unused *cu,
- void __maybe_unused *arg)
-{
- pr_err("unwind: resume unsupported\n");
- return -UNW_EINVAL;
-}
-
-static int
-get_proc_name(unw_addr_space_t __maybe_unused as,
- unw_word_t __maybe_unused addr,
- char __maybe_unused *bufp, size_t __maybe_unused buf_len,
- unw_word_t __maybe_unused *offp, void __maybe_unused *arg)
-{
- pr_err("unwind: get_proc_name unsupported\n");
- return -UNW_EINVAL;
-}
-
-static int access_dso_mem(struct unwind_info *ui, unw_word_t addr,
- unw_word_t *data)
-{
- struct map *map;
- struct dso *dso;
- ssize_t size;
-
- map = find_map(addr, ui);
- if (!map) {
- pr_debug("unwind: no map for %lx\n", (unsigned long)addr);
- return -1;
- }
-
- dso = map__dso(map);
-
- if (!dso) {
- map__put(map);
- return -1;
- }
-
- size = dso__data_read_addr(dso, map, ui->machine,
- addr, (u8 *) data, sizeof(*data));
- map__put(map);
- return !(size == sizeof(*data));
-}
-
-static int access_mem(unw_addr_space_t __maybe_unused as,
- unw_word_t addr, unw_word_t *valp,
- int __write, void *arg)
-{
- struct unwind_info *ui = arg;
- struct stack_dump *stack = &ui->sample->user_stack;
- u64 start, end;
- int offset;
- int ret;
-
- /* Don't support write, probably not needed. */
- if (__write || !stack || !ui->sample->user_regs || !ui->sample->user_regs->regs) {
- *valp = 0;
- return 0;
- }
-
- ret = perf_reg_value(&start, perf_sample__user_regs(ui->sample),
- perf_arch_reg_sp(thread__e_machine(ui->thread,
- ui->machine,
- /*e_flags=*/NULL)));
- if (ret)
- return ret;
-
- end = start + stack->size;
-
- /* Check overflow. */
- if (addr + sizeof(unw_word_t) < addr)
- return -EINVAL;
-
- if (addr < start || addr + sizeof(unw_word_t) >= end) {
- ret = access_dso_mem(ui, addr, valp);
- if (ret) {
- pr_debug("unwind: access_mem %p not inside range"
- " 0x%" PRIx64 "-0x%" PRIx64 "\n",
- (void *) (uintptr_t) addr, start, end);
- *valp = 0;
- return ret;
- }
- return 0;
- }
-
- offset = addr - start;
- *valp = *(unw_word_t *)&stack->data[offset];
- pr_debug("unwind: access_mem addr %p val %lx, offset %d\n",
- (void *) (uintptr_t) addr, (unsigned long)*valp, offset);
- return 0;
-}
-
-static int access_reg(unw_addr_space_t __maybe_unused as,
- unw_regnum_t regnum, unw_word_t *valp,
- int __write, void *arg)
-{
- struct unwind_info *ui = arg;
- int id, ret;
- u64 val;
-
- /* Don't support write, I suspect we don't need it. */
- if (__write) {
- pr_err("unwind: access_reg w %d\n", regnum);
- return 0;
- }
-
- if (!ui->sample->user_regs || !ui->sample->user_regs->regs) {
- *valp = 0;
- return 0;
- }
-
- id = LIBUNWIND__ARCH_REG_ID(regnum);
- if (id < 0)
- return -EINVAL;
-
- ret = perf_reg_value(&val, perf_sample__user_regs(ui->sample), id);
- if (ret) {
- if (!ui->best_effort)
- pr_err("unwind: can't read reg %d\n", regnum);
- return ret;
- }
-
- *valp = (unw_word_t) val;
- pr_debug("unwind: reg %d, val %lx\n", regnum, (unsigned long)*valp);
- return 0;
-}
-
-static void put_unwind_info(unw_addr_space_t __maybe_unused as,
- unw_proc_info_t *pi __maybe_unused,
- void *arg __maybe_unused)
-{
- pr_debug("unwind: put_unwind_info called\n");
-}
-
-static int entry(u64 ip, struct thread *thread,
- unwind_entry_cb_t cb, void *arg)
-{
- struct unwind_entry e;
- struct addr_location al;
- int ret;
-
- addr_location__init(&al);
- e.ms.sym = thread__find_symbol(thread, PERF_RECORD_MISC_USER, ip, &al);
- e.ip = ip;
- e.ms.map = al.map;
- e.ms.thread = thread__get(al.thread);
-
- pr_debug("unwind: %s:ip = 0x%" PRIx64 " (0x%" PRIx64 ")\n",
- al.sym ? al.sym->name : "''",
- ip,
- al.map ? map__map_ip(al.map, ip) : (u64) 0);
-
- ret = cb(&e, arg);
- addr_location__exit(&al);
- return ret;
-}
-
-static void display_error(int err)
-{
- switch (err) {
- case UNW_EINVAL:
- pr_err("unwind: Only supports local.\n");
- break;
- case UNW_EUNSPEC:
- pr_err("unwind: Unspecified error.\n");
- break;
- case UNW_EBADREG:
- pr_err("unwind: Register unavailable.\n");
- break;
- default:
- break;
- }
-}
-
-static unw_accessors_t accessors = {
- .find_proc_info = find_proc_info,
- .put_unwind_info = put_unwind_info,
- .get_dyn_info_list_addr = get_dyn_info_list_addr,
- .access_mem = access_mem,
- .access_reg = access_reg,
- .access_fpreg = access_fpreg,
- .resume = resume,
- .get_proc_name = get_proc_name,
-};
-
-static int _unwind__prepare_access(struct maps *maps)
-{
- void *addr_space = unw_create_addr_space(&accessors, 0);
-
- maps__set_addr_space(maps, addr_space);
- if (!addr_space) {
- pr_err("unwind: Can't create unwind address space.\n");
- return -ENOMEM;
- }
-
- unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL);
- return 0;
-}
-
-static void _unwind__flush_access(struct maps *maps)
-{
- unw_flush_cache(maps__addr_space(maps), 0, 0);
-}
-
-static void _unwind__finish_access(struct maps *maps)
-{
- unw_destroy_addr_space(maps__addr_space(maps));
-}
-
-static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb,
- void *arg, int max_stack)
-{
- uint16_t e_machine = thread__e_machine(ui->thread, ui->machine, /*e_flags=*/NULL);
- u64 val;
- unw_word_t ips[max_stack];
- unw_addr_space_t addr_space;
- unw_cursor_t c;
- int ret, i = 0;
-
- ret = perf_reg_value(&val, perf_sample__user_regs(ui->sample),
- perf_arch_reg_ip(e_machine));
- if (ret)
- return ret;
-
- ips[i++] = (unw_word_t) val;
-
- /*
- * If we need more than one entry, do the DWARF
- * unwind itself.
- */
- if (max_stack - 1 > 0) {
- WARN_ONCE(!ui->thread, "WARNING: ui->thread is NULL");
- addr_space = maps__addr_space(thread__maps(ui->thread));
-
- if (addr_space == NULL)
- return -1;
-
- ret = unw_init_remote(&c, addr_space, ui);
- if (ret && !ui->best_effort)
- display_error(ret);
-
- while (!ret && (unw_step(&c) > 0) && i < max_stack) {
- unw_get_reg(&c, UNW_REG_IP, &ips[i]);
-
- /*
- * Decrement the IP for any non-activation frames.
- * this is required to properly find the srcline
- * for caller frames.
- * See also the documentation for dwfl_frame_pc(),
- * which this code tries to replicate.
- */
- if (unw_is_signal_frame(&c) <= 0)
- --ips[i];
-
- ++i;
- }
-
- max_stack = i;
- }
-
- /*
- * Display what we got based on the order setup.
- */
- for (i = 0; i < max_stack && !ret; i++) {
- int j = i;
-
- if (callchain_param.order == ORDER_CALLER)
- j = max_stack - i - 1;
- ret = ips[j] ? entry(ips[j], ui->thread, cb, arg) : 0;
- }
-
- return ret;
-}
-
-static int _unwind__get_entries(unwind_entry_cb_t cb, void *arg,
- struct thread *thread,
- struct perf_sample *data, int max_stack,
- bool best_effort)
-{
- struct unwind_info ui = {
- .sample = data,
- .thread = thread,
- .machine = maps__machine(thread__maps(thread)),
- .best_effort = best_effort
- };
-
- if (!data->user_regs || !data->user_regs->regs)
- return -EINVAL;
-
- if (max_stack <= 0)
- return -EINVAL;
-
- return get_entries(&ui, cb, arg, max_stack);
-}
-
-static struct unwind_libunwind_ops
-_unwind_libunwind_ops = {
- .prepare_access = _unwind__prepare_access,
- .flush_access = _unwind__flush_access,
- .finish_access = _unwind__finish_access,
- .get_entries = _unwind__get_entries,
-};
-
-#ifndef REMOTE_UNWIND_LIBUNWIND
-struct unwind_libunwind_ops *
-local_unwind_libunwind_ops = &_unwind_libunwind_ops;
-#endif
diff --git a/tools/perf/util/unwind-libunwind.c b/tools/perf/util/unwind-libunwind.c
deleted file mode 100644
index cb8be6acfb6f..000000000000
--- a/tools/perf/util/unwind-libunwind.c
+++ /dev/null
@@ -1,92 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-#include "unwind.h"
-#include "dso.h"
-#include "map.h"
-#include "thread.h"
-#include "session.h"
-#include "debug.h"
-#include "env.h"
-#include "callchain.h"
-
-struct unwind_libunwind_ops __weak *local_unwind_libunwind_ops;
-struct unwind_libunwind_ops __weak *x86_32_unwind_libunwind_ops;
-struct unwind_libunwind_ops __weak *arm64_unwind_libunwind_ops;
-
-int unwind__prepare_access(struct maps *maps, struct map *map, bool *initialized)
-{
- const char *arch;
- enum dso_type dso_type;
- struct unwind_libunwind_ops *ops = local_unwind_libunwind_ops;
- struct dso *dso = map__dso(map);
- struct machine *machine;
- int err;
-
- if (!dwarf_callchain_users)
- return 0;
-
- if (maps__addr_space(maps)) {
- pr_debug("unwind: thread map already set, dso=%s\n", dso__name(dso));
- if (initialized)
- *initialized = true;
- return 0;
- }
-
- machine = maps__machine(maps);
- /* env->arch is NULL for live-mode (i.e. perf top) */
- if (!machine->env || !machine->env->arch)
- goto out_register;
-
- dso_type = dso__type(dso, machine);
- if (dso_type == DSO__TYPE_UNKNOWN)
- return 0;
-
- arch = perf_env__arch(machine->env);
-
- if (!strcmp(arch, "x86")) {
- if (dso_type != DSO__TYPE_64BIT)
- ops = x86_32_unwind_libunwind_ops;
- } else if (!strcmp(arch, "arm64") || !strcmp(arch, "arm")) {
- if (dso_type == DSO__TYPE_64BIT)
- ops = arm64_unwind_libunwind_ops;
- }
-
- if (!ops) {
- pr_warning_once("unwind: target platform=%s is not supported\n", arch);
- return 0;
- }
-out_register:
- maps__set_unwind_libunwind_ops(maps, ops);
-
- err = maps__unwind_libunwind_ops(maps)->prepare_access(maps);
- if (initialized)
- *initialized = err ? false : true;
- return err;
-}
-
-void unwind__flush_access(struct maps *maps)
-{
- const struct unwind_libunwind_ops *ops = maps__unwind_libunwind_ops(maps);
-
- if (ops)
- ops->flush_access(maps);
-}
-
-void unwind__finish_access(struct maps *maps)
-{
- const struct unwind_libunwind_ops *ops = maps__unwind_libunwind_ops(maps);
-
- if (ops)
- ops->finish_access(maps);
-}
-
-int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
- struct thread *thread,
- struct perf_sample *data, int max_stack,
- bool best_effort)
-{
- const struct unwind_libunwind_ops *ops = maps__unwind_libunwind_ops(thread__maps(thread));
-
- if (ops)
- return ops->get_entries(cb, arg, thread, data, max_stack, best_effort);
- return 0;
-}
diff --git a/tools/perf/util/unwind.h b/tools/perf/util/unwind.h
index 9f7164c6d9aa..782aaeedd923 100644
--- a/tools/perf/util/unwind.h
+++ b/tools/perf/util/unwind.h
@@ -17,15 +17,6 @@ struct unwind_entry {
typedef int (*unwind_entry_cb_t)(struct unwind_entry *entry, void *arg);
-struct unwind_libunwind_ops {
- int (*prepare_access)(struct maps *maps);
- void (*flush_access)(struct maps *maps);
- void (*finish_access)(struct maps *maps);
- int (*get_entries)(unwind_entry_cb_t cb, void *arg,
- struct thread *thread,
- struct perf_sample *data, int max_stack, bool best_effort);
-};
-
#ifdef HAVE_DWARF_UNWIND_SUPPORT
/*
* When best_effort is set, don't report errors and fail silently. This could
@@ -36,27 +27,6 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
struct thread *thread,
struct perf_sample *data, int max_stack,
bool best_effort);
-/* libunwind specific */
-#ifdef HAVE_LIBUNWIND_SUPPORT
-#ifndef LIBUNWIND__ARCH_REG_ID
-#define LIBUNWIND__ARCH_REG_ID(regnum) libunwind__arch_reg_id(regnum)
-#endif
-
-int LIBUNWIND__ARCH_REG_ID(int regnum);
-int unwind__prepare_access(struct maps *maps, struct map *map, bool *initialized);
-void unwind__flush_access(struct maps *maps);
-void unwind__finish_access(struct maps *maps);
-#else
-static inline int unwind__prepare_access(struct maps *maps __maybe_unused,
- struct map *map __maybe_unused,
- bool *initialized __maybe_unused)
-{
- return 0;
-}
-
-static inline void unwind__flush_access(struct maps *maps __maybe_unused) {}
-static inline void unwind__finish_access(struct maps *maps __maybe_unused) {}
-#endif
#else
static inline int
unwind__get_entries(unwind_entry_cb_t cb __maybe_unused,
@@ -68,15 +38,6 @@ unwind__get_entries(unwind_entry_cb_t cb __maybe_unused,
{
return 0;
}
-
-static inline int unwind__prepare_access(struct maps *maps __maybe_unused,
- struct map *map __maybe_unused,
- bool *initialized __maybe_unused)
-{
- return 0;
-}
-
-static inline void unwind__flush_access(struct maps *maps __maybe_unused) {}
-static inline void unwind__finish_access(struct maps *maps __maybe_unused) {}
#endif /* HAVE_DWARF_UNWIND_SUPPORT */
+
#endif /* __UNWIND_H */
--
2.53.0.959.g497ff81fa9-goog
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH v1 2/2] tools build: Remove libunwind feature tests
2026-03-21 23:42 ` [PATCH v1 0/2] perf build: Remove libunwind support Ian Rogers
2026-03-21 23:42 ` [PATCH v1 1/2] " Ian Rogers
@ 2026-03-21 23:42 ` Ian Rogers
2026-03-26 22:51 ` [PATCH v1 0/2] perf build: Remove libunwind support Namhyung Kim
2 siblings, 0 replies; 27+ messages in thread
From: Ian Rogers @ 2026-03-21 23:42 UTC (permalink / raw)
To: namhyung
Cc: 9erthalion6, acme, adrian.hunter, alex, alexander.shishkin,
andrew.jones, aou, atrajeev, blakejones, ctshao, dapeng1.mi,
howardchu95, irogers, james.clark, john.g.garry, jolsa, leo.yan,
libunwind-devel, linux-arm-kernel, linux-kernel, linux-perf-users,
linux-riscv, mingo, palmer, peterz, pjw, shimin.guo, tglozar,
tmricht, will, amadio, yuzhuo
Libunwind support has been removed from perf, so the feature tests
used to detect its presence are no longer needed. Remove the
test-libunwind-*.c files and their references in the feature Makefile.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/build/feature/Makefile | 31 -------------------
tools/build/feature/test-libunwind-aarch64.c | 27 ----------------
tools/build/feature/test-libunwind-arm.c | 28 -----------------
.../test-libunwind-debug-frame-aarch64.c | 17 ----------
.../feature/test-libunwind-debug-frame-arm.c | 17 ----------
.../feature/test-libunwind-debug-frame.c | 17 ----------
tools/build/feature/test-libunwind-x86.c | 28 -----------------
tools/build/feature/test-libunwind-x86_64.c | 28 -----------------
tools/build/feature/test-libunwind.c | 28 -----------------
9 files changed, 221 deletions(-)
delete mode 100644 tools/build/feature/test-libunwind-aarch64.c
delete mode 100644 tools/build/feature/test-libunwind-arm.c
delete mode 100644 tools/build/feature/test-libunwind-debug-frame-aarch64.c
delete mode 100644 tools/build/feature/test-libunwind-debug-frame-arm.c
delete mode 100644 tools/build/feature/test-libunwind-debug-frame.c
delete mode 100644 tools/build/feature/test-libunwind-x86.c
delete mode 100644 tools/build/feature/test-libunwind-x86_64.c
delete mode 100644 tools/build/feature/test-libunwind.c
diff --git a/tools/build/feature/Makefile b/tools/build/feature/Makefile
index 1fbcb3ce74d2..37b972526987 100644
--- a/tools/build/feature/Makefile
+++ b/tools/build/feature/Makefile
@@ -36,14 +36,6 @@ FILES= \
test-libtraceevent.bin \
test-libcpupower.bin \
test-libtracefs.bin \
- test-libunwind.bin \
- test-libunwind-debug-frame.bin \
- test-libunwind-x86.bin \
- test-libunwind-x86_64.bin \
- test-libunwind-arm.bin \
- test-libunwind-aarch64.bin \
- test-libunwind-debug-frame-arm.bin \
- test-libunwind-debug-frame-aarch64.bin \
test-pthread-attr-setaffinity-np.bin \
test-pthread-barrier.bin \
test-stackprotector-all.bin \
@@ -203,29 +195,6 @@ $(OUTPUT)test-libnuma.bin:
$(OUTPUT)test-numa_num_possible_cpus.bin:
$(BUILD) -lnuma
-$(OUTPUT)test-libunwind.bin:
- $(BUILD) -lelf -llzma
-
-$(OUTPUT)test-libunwind-debug-frame.bin:
- $(BUILD) -lelf -llzma
-$(OUTPUT)test-libunwind-x86.bin:
- $(BUILD) -lelf -llzma -lunwind-x86
-
-$(OUTPUT)test-libunwind-x86_64.bin:
- $(BUILD) -lelf -llzma -lunwind-x86_64
-
-$(OUTPUT)test-libunwind-arm.bin:
- $(BUILD) -lelf -llzma -lunwind-arm
-
-$(OUTPUT)test-libunwind-aarch64.bin:
- $(BUILD) -lelf -llzma -lunwind-aarch64
-
-$(OUTPUT)test-libunwind-debug-frame-arm.bin:
- $(BUILD) -lelf -llzma -lunwind-arm
-
-$(OUTPUT)test-libunwind-debug-frame-aarch64.bin:
- $(BUILD) -lelf -llzma -lunwind-aarch64
-
$(OUTPUT)test-libslang.bin:
$(BUILD) -lslang
diff --git a/tools/build/feature/test-libunwind-aarch64.c b/tools/build/feature/test-libunwind-aarch64.c
deleted file mode 100644
index 323803f49212..000000000000
--- a/tools/build/feature/test-libunwind-aarch64.c
+++ /dev/null
@@ -1,27 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-#include <libunwind-aarch64.h>
-#include <stdlib.h>
-
-extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,
- unw_word_t ip,
- unw_dyn_info_t *di,
- unw_proc_info_t *pi,
- int need_unwind_info, void *arg);
-
-#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table)
-
-static unw_accessors_t accessors;
-
-int main(void)
-{
- unw_addr_space_t addr_space;
-
- addr_space = unw_create_addr_space(&accessors, 0);
- if (addr_space)
- return 0;
-
- unw_init_remote(NULL, addr_space, NULL);
- dwarf_search_unwind_table(addr_space, 0, NULL, NULL, 0, NULL);
-
- return 0;
-}
diff --git a/tools/build/feature/test-libunwind-arm.c b/tools/build/feature/test-libunwind-arm.c
deleted file mode 100644
index cb378b7d6866..000000000000
--- a/tools/build/feature/test-libunwind-arm.c
+++ /dev/null
@@ -1,28 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-#include <libunwind-arm.h>
-#include <stdlib.h>
-
-extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,
- unw_word_t ip,
- unw_dyn_info_t *di,
- unw_proc_info_t *pi,
- int need_unwind_info, void *arg);
-
-
-#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table)
-
-static unw_accessors_t accessors;
-
-int main(void)
-{
- unw_addr_space_t addr_space;
-
- addr_space = unw_create_addr_space(&accessors, 0);
- if (addr_space)
- return 0;
-
- unw_init_remote(NULL, addr_space, NULL);
- dwarf_search_unwind_table(addr_space, 0, NULL, NULL, 0, NULL);
-
- return 0;
-}
diff --git a/tools/build/feature/test-libunwind-debug-frame-aarch64.c b/tools/build/feature/test-libunwind-debug-frame-aarch64.c
deleted file mode 100644
index 36d6646c185e..000000000000
--- a/tools/build/feature/test-libunwind-debug-frame-aarch64.c
+++ /dev/null
@@ -1,17 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-#include <libunwind-aarch64.h>
-#include <stdlib.h>
-
-extern int
-UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug,
- unw_word_t ip, unw_word_t segbase,
- const char *obj_name, unw_word_t start,
- unw_word_t end);
-
-#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame)
-
-int main(void)
-{
- dwarf_find_debug_frame(0, NULL, 0, 0, NULL, 0, 0);
- return 0;
-}
diff --git a/tools/build/feature/test-libunwind-debug-frame-arm.c b/tools/build/feature/test-libunwind-debug-frame-arm.c
deleted file mode 100644
index 8696e48e1268..000000000000
--- a/tools/build/feature/test-libunwind-debug-frame-arm.c
+++ /dev/null
@@ -1,17 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-#include <libunwind-arm.h>
-#include <stdlib.h>
-
-extern int
-UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug,
- unw_word_t ip, unw_word_t segbase,
- const char *obj_name, unw_word_t start,
- unw_word_t end);
-
-#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame)
-
-int main(void)
-{
- dwarf_find_debug_frame(0, NULL, 0, 0, NULL, 0, 0);
- return 0;
-}
diff --git a/tools/build/feature/test-libunwind-debug-frame.c b/tools/build/feature/test-libunwind-debug-frame.c
deleted file mode 100644
index efb55cdd8d01..000000000000
--- a/tools/build/feature/test-libunwind-debug-frame.c
+++ /dev/null
@@ -1,17 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-#include <libunwind.h>
-#include <stdlib.h>
-
-extern int
-UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug,
- unw_word_t ip, unw_word_t segbase,
- const char *obj_name, unw_word_t start,
- unw_word_t end);
-
-#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame)
-
-int main(void)
-{
- dwarf_find_debug_frame(0, NULL, 0, 0, NULL, 0, 0);
- return 0;
-}
diff --git a/tools/build/feature/test-libunwind-x86.c b/tools/build/feature/test-libunwind-x86.c
deleted file mode 100644
index e5e0f6c89637..000000000000
--- a/tools/build/feature/test-libunwind-x86.c
+++ /dev/null
@@ -1,28 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-#include <libunwind-x86.h>
-#include <stdlib.h>
-
-extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,
- unw_word_t ip,
- unw_dyn_info_t *di,
- unw_proc_info_t *pi,
- int need_unwind_info, void *arg);
-
-
-#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table)
-
-static unw_accessors_t accessors;
-
-int main(void)
-{
- unw_addr_space_t addr_space;
-
- addr_space = unw_create_addr_space(&accessors, 0);
- if (addr_space)
- return 0;
-
- unw_init_remote(NULL, addr_space, NULL);
- dwarf_search_unwind_table(addr_space, 0, NULL, NULL, 0, NULL);
-
- return 0;
-}
diff --git a/tools/build/feature/test-libunwind-x86_64.c b/tools/build/feature/test-libunwind-x86_64.c
deleted file mode 100644
index 62ae4db597dc..000000000000
--- a/tools/build/feature/test-libunwind-x86_64.c
+++ /dev/null
@@ -1,28 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-#include <libunwind-x86_64.h>
-#include <stdlib.h>
-
-extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,
- unw_word_t ip,
- unw_dyn_info_t *di,
- unw_proc_info_t *pi,
- int need_unwind_info, void *arg);
-
-
-#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table)
-
-static unw_accessors_t accessors;
-
-int main(void)
-{
- unw_addr_space_t addr_space;
-
- addr_space = unw_create_addr_space(&accessors, 0);
- if (addr_space)
- return 0;
-
- unw_init_remote(NULL, addr_space, NULL);
- dwarf_search_unwind_table(addr_space, 0, NULL, NULL, 0, NULL);
-
- return 0;
-}
diff --git a/tools/build/feature/test-libunwind.c b/tools/build/feature/test-libunwind.c
deleted file mode 100644
index 53fd26614ff0..000000000000
--- a/tools/build/feature/test-libunwind.c
+++ /dev/null
@@ -1,28 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-#include <libunwind.h>
-#include <stdlib.h>
-
-extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,
- unw_word_t ip,
- unw_dyn_info_t *di,
- unw_proc_info_t *pi,
- int need_unwind_info, void *arg);
-
-
-#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table)
-
-static unw_accessors_t accessors;
-
-int main(void)
-{
- unw_addr_space_t addr_space;
-
- addr_space = unw_create_addr_space(&accessors, 0);
- if (addr_space)
- return 0;
-
- unw_init_remote(NULL, addr_space, NULL);
- dwarf_search_unwind_table(addr_space, 0, NULL, NULL, 0, NULL);
-
- return 0;
-}
--
2.53.0.959.g497ff81fa9-goog
^ permalink raw reply related [flat|nested] 27+ messages in thread
* Re: [PATCH v1 0/2] perf build: Remove libunwind support
2026-03-21 23:42 ` [PATCH v1 0/2] perf build: Remove libunwind support Ian Rogers
2026-03-21 23:42 ` [PATCH v1 1/2] " Ian Rogers
2026-03-21 23:42 ` [PATCH v1 2/2] tools build: Remove libunwind feature tests Ian Rogers
@ 2026-03-26 22:51 ` Namhyung Kim
2026-03-26 23:14 ` Ian Rogers
2 siblings, 1 reply; 27+ messages in thread
From: Namhyung Kim @ 2026-03-26 22:51 UTC (permalink / raw)
To: Ian Rogers
Cc: 9erthalion6, acme, adrian.hunter, alex, alexander.shishkin,
andrew.jones, aou, atrajeev, blakejones, ctshao, dapeng1.mi,
howardchu95, james.clark, john.g.garry, jolsa, leo.yan,
libunwind-devel, linux-arm-kernel, linux-kernel, linux-perf-users,
linux-riscv, mingo, palmer, peterz, pjw, shimin.guo, tglozar,
tmricht, will, amadio, yuzhuo
On Sat, Mar 21, 2026 at 04:42:18PM -0700, Ian Rogers wrote:
> libunwind support exists for "--call-graph dwarf", however, libunwind
> support has been opt-in rather than opt-out since Linux v6.13 as libdw
> is preferred - commit 13e17c9ff49119aa ("perf build: Make libunwind
> opt-in rather than opt-out"). A problem with the libdw support was
> that it was slow, an issue fixed in Linux v7.0 in commit 6b2658b3f36a
> ("perf unwind-libdw: Don't discard loaded ELF/DWARF after every
> unwind"). As such libunwind support is now unnecessary.
>
> The patch series:
> https://lore.kernel.org/lkml/20260305221927.3237145-1-irogers@google.com/
> looked to make the libunwind support in perf similar to the libdw
> support, allow cross-architecture unwinding, etc. This was motivated
> by the perf regs conventions being altered by the addition of x86 APX
> support:
> https://lore.kernel.org/lkml/20260209072047.2180332-1-dapeng1.mi@linux.intel.com/
> It is necessary to translate the library's notion of registers to the
> perf register convention so that the stack unwinding state can be
> initialized. On this series it was stated that removing libunwind
> support from perf should be an option, rather than updating support:
> https://lore.kernel.org/lkml/abxs-2rozL1tBEO1@google.com/
> This was also what motivated making libunwind opt-in rather than
> opt-out.
>
> Given that 7 minor releases have happened with libunwind "deprecated"
> by making it opt-in, let's remove the libunwind support. There doesn't
> appear to be any disagreement to this on the mailing list.
I'm not sure if we want to remove it now. I think we need more time to
verify libdw unwinding is stable and fast enough. Also maybe we can
add build- or run-time warning when people try to use libunwind.
Thanks,
Namhyung
>
> Ian Rogers (2):
> perf build: Remove libunwind support
> tools build: Remove libunwind feature tests
>
> tools/build/feature/Makefile | 31 -
> tools/build/feature/test-libunwind-aarch64.c | 27 -
> tools/build/feature/test-libunwind-arm.c | 28 -
> .../test-libunwind-debug-frame-aarch64.c | 17 -
> .../feature/test-libunwind-debug-frame-arm.c | 17 -
> .../feature/test-libunwind-debug-frame.c | 17 -
> tools/build/feature/test-libunwind-x86.c | 28 -
> tools/build/feature/test-libunwind-x86_64.c | 28 -
> tools/build/feature/test-libunwind.c | 28 -
> tools/perf/Documentation/perf-check.txt | 1 -
> tools/perf/Documentation/perf-config.txt | 4 +-
> tools/perf/Documentation/perf-record.txt | 2 +-
> tools/perf/Makefile.config | 163 +---
> tools/perf/Makefile.perf | 3 -
> tools/perf/arch/arm/util/Build | 1 -
> tools/perf/arch/arm/util/unwind-libunwind.c | 50 --
> tools/perf/arch/arm64/util/Build | 1 -
> tools/perf/arch/arm64/util/unwind-libunwind.c | 17 -
> tools/perf/arch/loongarch/util/Build | 1 -
> .../arch/loongarch/util/unwind-libunwind.c | 82 --
> tools/perf/arch/mips/util/Build | 1 -
> tools/perf/arch/mips/util/unwind-libunwind.c | 22 -
> tools/perf/arch/powerpc/util/Build | 1 -
> .../perf/arch/powerpc/util/unwind-libunwind.c | 92 --
> tools/perf/arch/x86/util/Build | 1 -
> tools/perf/arch/x86/util/unwind-libunwind.c | 115 ---
> tools/perf/builtin-check.c | 1 -
> tools/perf/builtin-report.c | 4 +-
> tools/perf/tests/make | 2 -
> tools/perf/util/Build | 5 -
> tools/perf/util/callchain.c | 2 +-
> tools/perf/util/dso.h | 6 -
> tools/perf/util/libunwind/arm64.c | 40 -
> tools/perf/util/libunwind/x86_32.c | 41 -
> tools/perf/util/maps.c | 47 +-
> tools/perf/util/maps.h | 6 -
> tools/perf/util/thread.c | 40 +-
> tools/perf/util/thread.h | 1 -
> tools/perf/util/unwind-libunwind-local.c | 832 ------------------
> tools/perf/util/unwind-libunwind.c | 92 --
> tools/perf/util/unwind.h | 41 +-
> 41 files changed, 19 insertions(+), 1919 deletions(-)
> delete mode 100644 tools/build/feature/test-libunwind-aarch64.c
> delete mode 100644 tools/build/feature/test-libunwind-arm.c
> delete mode 100644 tools/build/feature/test-libunwind-debug-frame-aarch64.c
> delete mode 100644 tools/build/feature/test-libunwind-debug-frame-arm.c
> delete mode 100644 tools/build/feature/test-libunwind-debug-frame.c
> delete mode 100644 tools/build/feature/test-libunwind-x86.c
> delete mode 100644 tools/build/feature/test-libunwind-x86_64.c
> delete mode 100644 tools/build/feature/test-libunwind.c
> delete mode 100644 tools/perf/arch/arm/util/unwind-libunwind.c
> delete mode 100644 tools/perf/arch/arm64/util/unwind-libunwind.c
> delete mode 100644 tools/perf/arch/loongarch/util/unwind-libunwind.c
> delete mode 100644 tools/perf/arch/mips/util/unwind-libunwind.c
> delete mode 100644 tools/perf/arch/powerpc/util/unwind-libunwind.c
> delete mode 100644 tools/perf/arch/x86/util/unwind-libunwind.c
> delete mode 100644 tools/perf/util/libunwind/arm64.c
> delete mode 100644 tools/perf/util/libunwind/x86_32.c
> delete mode 100644 tools/perf/util/unwind-libunwind-local.c
> delete mode 100644 tools/perf/util/unwind-libunwind.c
>
> --
> 2.53.0.959.g497ff81fa9-goog
>
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH v1 0/2] perf build: Remove libunwind support
2026-03-26 22:51 ` [PATCH v1 0/2] perf build: Remove libunwind support Namhyung Kim
@ 2026-03-26 23:14 ` Ian Rogers
0 siblings, 0 replies; 27+ messages in thread
From: Ian Rogers @ 2026-03-26 23:14 UTC (permalink / raw)
To: Namhyung Kim
Cc: 9erthalion6, acme, adrian.hunter, alex, alexander.shishkin,
andrew.jones, aou, atrajeev, blakejones, ctshao, dapeng1.mi,
howardchu95, james.clark, john.g.garry, jolsa, leo.yan,
libunwind-devel, linux-arm-kernel, linux-kernel, linux-perf-users,
linux-riscv, mingo, palmer, peterz, pjw, shimin.guo, tglozar,
tmricht, will, amadio, yuzhuo
On Thu, Mar 26, 2026 at 3:51 PM Namhyung Kim <namhyung@kernel.org> wrote:
>
> On Sat, Mar 21, 2026 at 04:42:18PM -0700, Ian Rogers wrote:
> > libunwind support exists for "--call-graph dwarf", however, libunwind
> > support has been opt-in rather than opt-out since Linux v6.13 as libdw
> > is preferred - commit 13e17c9ff49119aa ("perf build: Make libunwind
> > opt-in rather than opt-out"). A problem with the libdw support was
> > that it was slow, an issue fixed in Linux v7.0 in commit 6b2658b3f36a
> > ("perf unwind-libdw: Don't discard loaded ELF/DWARF after every
> > unwind"). As such libunwind support is now unnecessary.
> >
> > The patch series:
> > https://lore.kernel.org/lkml/20260305221927.3237145-1-irogers@google.com/
> > looked to make the libunwind support in perf similar to the libdw
> > support, allow cross-architecture unwinding, etc. This was motivated
> > by the perf regs conventions being altered by the addition of x86 APX
> > support:
> > https://lore.kernel.org/lkml/20260209072047.2180332-1-dapeng1.mi@linux.intel.com/
> > It is necessary to translate the library's notion of registers to the
> > perf register convention so that the stack unwinding state can be
> > initialized. On this series it was stated that removing libunwind
> > support from perf should be an option, rather than updating support:
> > https://lore.kernel.org/lkml/abxs-2rozL1tBEO1@google.com/
> > This was also what motivated making libunwind opt-in rather than
> > opt-out.
> >
> > Given that 7 minor releases have happened with libunwind "deprecated"
> > by making it opt-in, let's remove the libunwind support. There doesn't
> > appear to be any disagreement to this on the mailing list.
>
> I'm not sure if we want to remove it now. I think we need more time to
> verify libdw unwinding is stable and fast enough. Also maybe we can
> add build- or run-time warning when people try to use libunwind.
I've had issues with old versions of libdw, but presumably the
distributions won't update perf without libdw. Given we're waiting
should we land the cleanup in:
https://lore.kernel.org/lkml/20260305221927.3237145-1-irogers@google.com/
We have test coverage.
Thanks,
Ian
> Thanks,
> Namhyung
>
> >
> > Ian Rogers (2):
> > perf build: Remove libunwind support
> > tools build: Remove libunwind feature tests
> >
> > tools/build/feature/Makefile | 31 -
> > tools/build/feature/test-libunwind-aarch64.c | 27 -
> > tools/build/feature/test-libunwind-arm.c | 28 -
> > .../test-libunwind-debug-frame-aarch64.c | 17 -
> > .../feature/test-libunwind-debug-frame-arm.c | 17 -
> > .../feature/test-libunwind-debug-frame.c | 17 -
> > tools/build/feature/test-libunwind-x86.c | 28 -
> > tools/build/feature/test-libunwind-x86_64.c | 28 -
> > tools/build/feature/test-libunwind.c | 28 -
> > tools/perf/Documentation/perf-check.txt | 1 -
> > tools/perf/Documentation/perf-config.txt | 4 +-
> > tools/perf/Documentation/perf-record.txt | 2 +-
> > tools/perf/Makefile.config | 163 +---
> > tools/perf/Makefile.perf | 3 -
> > tools/perf/arch/arm/util/Build | 1 -
> > tools/perf/arch/arm/util/unwind-libunwind.c | 50 --
> > tools/perf/arch/arm64/util/Build | 1 -
> > tools/perf/arch/arm64/util/unwind-libunwind.c | 17 -
> > tools/perf/arch/loongarch/util/Build | 1 -
> > .../arch/loongarch/util/unwind-libunwind.c | 82 --
> > tools/perf/arch/mips/util/Build | 1 -
> > tools/perf/arch/mips/util/unwind-libunwind.c | 22 -
> > tools/perf/arch/powerpc/util/Build | 1 -
> > .../perf/arch/powerpc/util/unwind-libunwind.c | 92 --
> > tools/perf/arch/x86/util/Build | 1 -
> > tools/perf/arch/x86/util/unwind-libunwind.c | 115 ---
> > tools/perf/builtin-check.c | 1 -
> > tools/perf/builtin-report.c | 4 +-
> > tools/perf/tests/make | 2 -
> > tools/perf/util/Build | 5 -
> > tools/perf/util/callchain.c | 2 +-
> > tools/perf/util/dso.h | 6 -
> > tools/perf/util/libunwind/arm64.c | 40 -
> > tools/perf/util/libunwind/x86_32.c | 41 -
> > tools/perf/util/maps.c | 47 +-
> > tools/perf/util/maps.h | 6 -
> > tools/perf/util/thread.c | 40 +-
> > tools/perf/util/thread.h | 1 -
> > tools/perf/util/unwind-libunwind-local.c | 832 ------------------
> > tools/perf/util/unwind-libunwind.c | 92 --
> > tools/perf/util/unwind.h | 41 +-
> > 41 files changed, 19 insertions(+), 1919 deletions(-)
> > delete mode 100644 tools/build/feature/test-libunwind-aarch64.c
> > delete mode 100644 tools/build/feature/test-libunwind-arm.c
> > delete mode 100644 tools/build/feature/test-libunwind-debug-frame-aarch64.c
> > delete mode 100644 tools/build/feature/test-libunwind-debug-frame-arm.c
> > delete mode 100644 tools/build/feature/test-libunwind-debug-frame.c
> > delete mode 100644 tools/build/feature/test-libunwind-x86.c
> > delete mode 100644 tools/build/feature/test-libunwind-x86_64.c
> > delete mode 100644 tools/build/feature/test-libunwind.c
> > delete mode 100644 tools/perf/arch/arm/util/unwind-libunwind.c
> > delete mode 100644 tools/perf/arch/arm64/util/unwind-libunwind.c
> > delete mode 100644 tools/perf/arch/loongarch/util/unwind-libunwind.c
> > delete mode 100644 tools/perf/arch/mips/util/unwind-libunwind.c
> > delete mode 100644 tools/perf/arch/powerpc/util/unwind-libunwind.c
> > delete mode 100644 tools/perf/arch/x86/util/unwind-libunwind.c
> > delete mode 100644 tools/perf/util/libunwind/arm64.c
> > delete mode 100644 tools/perf/util/libunwind/x86_32.c
> > delete mode 100644 tools/perf/util/unwind-libunwind-local.c
> > delete mode 100644 tools/perf/util/unwind-libunwind.c
> >
> > --
> > 2.53.0.959.g497ff81fa9-goog
> >
^ permalink raw reply [flat|nested] 27+ messages in thread
end of thread, other threads:[~2026-03-26 23:15 UTC | newest]
Thread overview: 27+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-02-24 14:29 [RFC PATCH v1 0/7] perf libunwind multiple remote support Ian Rogers
2026-02-24 14:29 ` [RFC PATCH v1 1/7] tools build: Deduplicate test-libunwind for different architectures Ian Rogers
2026-02-24 14:29 ` [RFC PATCH v1 2/7] perf build: Be more programmatic when setting up libunwind variables Ian Rogers
2026-02-24 14:29 ` [RFC PATCH v1 3/7] perf build loongarch: Remove reference to missing file Ian Rogers
2026-02-24 14:29 ` [RFC PATCH v1 4/7] perf unwind-libunwind: Make libunwind register reading cross platform Ian Rogers
2026-02-24 14:29 ` [RFC PATCH v1 5/7] perf unwind-libunwind: Move flush/finish access out of local Ian Rogers
2026-02-24 14:29 ` [RFC PATCH v1 6/7] perf unwind-libunwind: Remove libunwind-local Ian Rogers
2026-02-24 14:29 ` [RFC PATCH v1 7/7] perf unwind-libunwind: Add RISC-V libunwind support Ian Rogers
2026-02-25 21:08 ` Andrew Jones
2026-02-26 1:34 ` Ian Rogers
2026-03-05 22:19 ` [PATCH v2 0/8] perf libunwind multiple remote support Ian Rogers
2026-03-05 22:19 ` [PATCH v2 1/8] perf unwind: Refactor get_entries to allow dynamic libdw/libunwind selection Ian Rogers
2026-03-05 22:19 ` [PATCH v2 2/8] perf build loongarch: Remove reference to missing file Ian Rogers
2026-03-05 22:19 ` [PATCH v2 3/8] tools build: Deduplicate test-libunwind for different architectures Ian Rogers
2026-03-05 22:19 ` [PATCH v2 4/8] perf build: Be more programmatic when setting up libunwind variables Ian Rogers
2026-03-05 22:19 ` [PATCH v2 5/8] perf unwind-libunwind: Make libunwind register reading cross platform Ian Rogers
2026-03-05 22:19 ` [PATCH v2 6/8] perf unwind-libunwind: Move flush/finish access out of local Ian Rogers
2026-03-05 22:19 ` [PATCH v2 7/8] perf unwind-libunwind: Remove libunwind-local Ian Rogers
2026-03-05 22:19 ` [PATCH v2 8/8] perf unwind-libunwind: Add RISC-V libunwind support Ian Rogers
2026-03-19 21:39 ` [PATCH v2 0/8] perf libunwind multiple remote support Namhyung Kim
2026-03-21 3:06 ` Ian Rogers
2026-03-21 8:20 ` Guilherme Amadio
2026-03-21 23:42 ` [PATCH v1 0/2] perf build: Remove libunwind support Ian Rogers
2026-03-21 23:42 ` [PATCH v1 1/2] " Ian Rogers
2026-03-21 23:42 ` [PATCH v1 2/2] tools build: Remove libunwind feature tests Ian Rogers
2026-03-26 22:51 ` [PATCH v1 0/2] perf build: Remove libunwind support Namhyung Kim
2026-03-26 23:14 ` Ian Rogers
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox