perf unwind-libdw: Fix a cross-arch unwinding bug

The set_initial_registers field of Dwfl_Thread_Callbacks needs to be set
according to the arch of the stack samples being analyzed, not the arch
that perf itself is built for.

Currently perf fails to unwind stack samples collected from archs
different from that of the host perf is running on.

This patch moves the arch-specific implementations of set_initial_registers
from tools/perf/arch to tools/perf/utli/unwind-libdw-arch, similar to the
way the perf-regs-arch folder contains arch-specific functions related to
registers, and chooses the implementation based on the arch of the data
being processed.

Reviewed-by: Ian Rogers <irogers@google.com>
Signed-off-by: Shimin Guo <shimin.guo@skydio.com>
Acked-by: Namhyung Kim <namhyung@kernel.org>
Cc: Aditya Bodkhe <aditya.b1@linux.ibm.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Albert Ou <aou@eecs.berkeley.edu>
Cc: Alexandre Ghiti <alex@ghiti.fr>
Cc: Andi Kleen <ak@linux.intel.com>
Cc: Athira Rajeev <atrajeev@linux.ibm.com>
Cc: Chun-Tse Shao <ctshao@google.com>
Cc: Dmitriy Vyukov <dvyukov@google.com>
Cc: Dr. David Alan Gilbert <linux@treblig.org>
Cc: Guo Ren <guoren@kernel.org>
Cc: Haibo Xu <haibo1.xu@intel.com>
Cc: Howard Chu <howardchu95@gmail.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: James Clark <james.clark@linaro.org>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: John Garry <john.g.garry@oracle.com>
Cc: Krzysztof Łopatowski <krzysztof.m.lopatowski@gmail.com>
Cc: Leo Yan <leo.yan@linux.dev>
Cc: Mark Wielaard <mark@klomp.org>
Cc: Palmer Dabbelt <palmer@dabbelt.com>
Cc: Paul Walmsley <pjw@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Sergei Trofimovich <slyich@gmail.com>
Cc: Stephen Brennan <stephen.s.brennan@oracle.com>
Cc: Thomas Falcon <thomas.falcon@intel.com>
Cc: Will Deacon <will@kernel.org>
Signed-off-by: Ian Rogers <irogers@google.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
Shimin Guo
2026-01-16 21:28:30 -08:00
committed by Arnaldo Carvalho de Melo
parent 86f3801208
commit e62fae9d9e
19 changed files with 104 additions and 57 deletions
-1
View File
@@ -1,6 +1,5 @@
perf-util-y += perf_regs.o
perf-util-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o
perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
perf-util-y += pmu.o auxtrace.o cs-etm.o
-1
View File
@@ -1,4 +1,3 @@
perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
perf-util-$(CONFIG_LIBTRACEEVENT) += kvm-stat.o
perf-util-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o
perf-util-y += ../../arm/util/auxtrace.o
-2
View File
@@ -1,3 +1 @@
perf-util-y += perf_regs.o
perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
-1
View File
@@ -9,5 +9,4 @@ perf-util-y += evsel.o
perf-util-$(CONFIG_LIBDW) += skip-callchain-idx.o
perf-util-$(CONFIG_LIBUNWIND) += unwind-libunwind.o
perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
perf-util-y += auxtrace.o
-1
View File
@@ -2,4 +2,3 @@ perf-util-y += perf_regs.o
perf-util-y += header.o
perf-util-$(CONFIG_LIBTRACEEVENT) += kvm-stat.o
perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
-2
View File
@@ -2,8 +2,6 @@ perf-util-y += header.o
perf-util-$(CONFIG_LIBTRACEEVENT) += kvm-stat.o
perf-util-y += perf_regs.o
perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
perf-util-y += machine.o
perf-util-y += pmu.o
-1
View File
@@ -12,7 +12,6 @@ perf-util-y += evsel.o
perf-util-y += iostat.o
perf-util-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o
perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
perf-util-y += auxtrace.o
perf-util-y += archinsn.o
+1
View File
@@ -227,6 +227,7 @@ perf-util-$(CONFIG_LIBDW) += annotate-data.o
perf-util-$(CONFIG_LIBDW) += libdw.o
perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw-arch/
perf-util-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind-local.o
perf-util-$(CONFIG_LIBUNWIND) += unwind-libunwind.o
perf-util-$(CONFIG_LIBUNWIND_X86) += libunwind/x86_32.o
+8
View File
@@ -0,0 +1,8 @@
perf-util-y += unwind-libdw-x86.o
perf-util-y += unwind-libdw-arm.o
perf-util-y += unwind-libdw-arm64.o
perf-util-y += unwind-libdw-csky.o
perf-util-y += unwind-libdw-loongarch.o
perf-util-y += unwind-libdw-powerpc.o
perf-util-y += unwind-libdw-riscv.o
perf-util-y += unwind-libdw-s390.o
@@ -1,11 +1,11 @@
// SPDX-License-Identifier: GPL-2.0
#include <elfutils/libdwfl.h>
#include "perf_regs.h"
#include "../../../util/unwind-libdw.h"
#include "../../../util/perf_regs.h"
#include "../../../util/sample.h"
#include "../arch/arm/include/uapi/asm/perf_regs.h"
#include "util/unwind-libdw.h"
#include "util/perf_regs.h"
#include "util/sample.h"
bool libdw__arch_set_initial_registers(Dwfl_Thread *thread, void *arg)
bool libdw_set_initial_registers_arm(Dwfl_Thread *thread, void *arg)
{
struct unwind_info *ui = arg;
struct regs_dump *user_regs = perf_sample__user_regs(ui->sample);
@@ -1,11 +1,11 @@
// SPDX-License-Identifier: GPL-2.0
#include <elfutils/libdwfl.h>
#include "perf_regs.h"
#include "../../../util/unwind-libdw.h"
#include "../../../util/perf_regs.h"
#include "../../../util/sample.h"
#include "../arch/arm64/include/uapi/asm/perf_regs.h"
#include "util/unwind-libdw.h"
#include "util/perf_regs.h"
#include "util/sample.h"
bool libdw__arch_set_initial_registers(Dwfl_Thread *thread, void *arg)
bool libdw_set_initial_registers_arm64(Dwfl_Thread *thread, void *arg)
{
struct unwind_info *ui = arg;
struct regs_dump *user_regs = perf_sample__user_regs(ui->sample);
@@ -2,12 +2,12 @@
// Copyright (C) 2019 Hangzhou C-SKY Microsystems co.,ltd.
#include <elfutils/libdwfl.h>
#include "perf_regs.h"
#include "../../util/unwind-libdw.h"
#include "../../util/perf_regs.h"
#include "../../util/event.h"
#include "../arch/csky/include/uapi/asm/perf_regs.h"
#include "util/unwind-libdw.h"
#include "util/perf_regs.h"
#include "util/sample.h"
bool libdw__arch_set_initial_registers(Dwfl_Thread *thread, void *arg)
bool libdw_set_initial_registers_csky(Dwfl_Thread *thread, void *arg)
{
struct unwind_info *ui = arg;
struct regs_dump *user_regs = perf_sample__user_regs(ui->sample);
@@ -2,12 +2,12 @@
/* Copyright (C) 2020-2023 Loongson Technology Corporation Limited */
#include <elfutils/libdwfl.h>
#include "perf_regs.h"
#include "../../util/unwind-libdw.h"
#include "../../util/perf_regs.h"
#include "../../util/sample.h"
#include "../arch/loongarch/include/uapi/asm/perf_regs.h"
#include "util/unwind-libdw.h"
#include "util/perf_regs.h"
#include "util/sample.h"
bool libdw__arch_set_initial_registers(Dwfl_Thread *thread, void *arg)
bool libdw_set_initial_registers_loongarch(Dwfl_Thread *thread, void *arg)
{
struct unwind_info *ui = arg;
struct regs_dump *user_regs = perf_sample__user_regs(ui->sample);
@@ -1,10 +1,10 @@
// SPDX-License-Identifier: GPL-2.0
#include <elfutils/libdwfl.h>
#include <linux/kernel.h>
#include "perf_regs.h"
#include "../../../util/unwind-libdw.h"
#include "../../../util/perf_regs.h"
#include "../../../util/sample.h"
#include "../arch/powerpc/include/uapi/asm/perf_regs.h"
#include "util/unwind-libdw.h"
#include "util/perf_regs.h"
#include "util/sample.h"
/* See backends/ppc_initreg.c and backends/ppc_regs.c in elfutils. */
static const int special_regs[3][2] = {
@@ -13,7 +13,7 @@ static const int special_regs[3][2] = {
{ 109, PERF_REG_POWERPC_CTR },
};
bool libdw__arch_set_initial_registers(Dwfl_Thread *thread, void *arg)
bool libdw_set_initial_registers_powerpc(Dwfl_Thread *thread, void *arg)
{
struct unwind_info *ui = arg;
struct regs_dump *user_regs = perf_sample__user_regs(ui->sample);
@@ -2,12 +2,12 @@
/* Copyright (C) 2019 Hangzhou C-SKY Microsystems co.,ltd. */
#include <elfutils/libdwfl.h>
#include "perf_regs.h"
#include "../../util/unwind-libdw.h"
#include "../../util/perf_regs.h"
#include "../../util/sample.h"
#include "../arch/riscv/include/uapi/asm/perf_regs.h"
#include "util/unwind-libdw.h"
#include "util/perf_regs.h"
#include "util/sample.h"
bool libdw__arch_set_initial_registers(Dwfl_Thread *thread, void *arg)
bool libdw_set_initial_registers_riscv(Dwfl_Thread *thread, void *arg)
{
struct unwind_info *ui = arg;
struct regs_dump *user_regs = perf_sample__user_regs(ui->sample);
@@ -1,14 +1,14 @@
#include <linux/kernel.h>
#include <elfutils/libdwfl.h>
#include "../../util/unwind-libdw.h"
#include "../../util/perf_regs.h"
#include "../../util/event.h"
#include "../../util/sample.h"
#include "dwarf-regs-table.h"
#include "perf_regs.h"
#include "util/unwind-libdw.h"
#include "util/perf_regs.h"
#include "util/event.h"
#include "util/sample.h"
#include "../arch/s390/include/dwarf-regs-table.h"
#include "../arch/s390/include/uapi/asm/perf_regs.h"
bool libdw__arch_set_initial_registers(Dwfl_Thread *thread, void *arg)
bool libdw_set_initial_registers_s390(Dwfl_Thread *thread, void *arg)
{
struct unwind_info *ui = arg;
struct regs_dump *user_regs = perf_sample__user_regs(ui->sample);
@@ -1,11 +1,11 @@
// SPDX-License-Identifier: GPL-2.0
#include <elfutils/libdwfl.h>
#include "perf_regs.h"
#include "../../../util/unwind-libdw.h"
#include "../../../util/perf_regs.h"
#include "../arch/x86/include/uapi/asm/perf_regs.h"
#include "util/unwind-libdw.h"
#include "util/perf_regs.h"
#include "util/sample.h"
bool libdw__arch_set_initial_registers(Dwfl_Thread *thread, void *arg)
bool libdw_set_initial_registers_x86(Dwfl_Thread *thread, void *arg)
{
struct unwind_info *ui = arg;
struct regs_dump *user_regs = perf_sample__user_regs(ui->sample);
+45 -6
View File
@@ -225,11 +225,45 @@ static bool memory_read(Dwfl *dwfl __maybe_unused, Dwarf_Addr addr, Dwarf_Word *
return true;
}
static const Dwfl_Thread_Callbacks callbacks = {
.next_thread = next_thread,
.memory_read = memory_read,
.set_initial_registers = libdw__arch_set_initial_registers,
};
#define DEFINE_DWFL_THREAD_CALLBACKS(arch) \
static const Dwfl_Thread_Callbacks callbacks_##arch = { \
.next_thread = next_thread, \
.memory_read = memory_read, \
.set_initial_registers = libdw_set_initial_registers_##arch, \
}
DEFINE_DWFL_THREAD_CALLBACKS(x86);
DEFINE_DWFL_THREAD_CALLBACKS(arm);
DEFINE_DWFL_THREAD_CALLBACKS(arm64);
DEFINE_DWFL_THREAD_CALLBACKS(csky);
DEFINE_DWFL_THREAD_CALLBACKS(loongarch);
DEFINE_DWFL_THREAD_CALLBACKS(powerpc);
DEFINE_DWFL_THREAD_CALLBACKS(riscv);
DEFINE_DWFL_THREAD_CALLBACKS(s390);
static const Dwfl_Thread_Callbacks *get_thread_callbacks(const char *arch)
{
if (!strcmp(arch, "arm"))
return &callbacks_arm;
else if (!strcmp(arch, "arm64"))
return &callbacks_arm64;
else if (!strcmp(arch, "csky"))
return &callbacks_csky;
else if (!strcmp(arch, "loongarch"))
return &callbacks_loongarch;
else if (!strcmp(arch, "powerpc"))
return &callbacks_powerpc;
else if (!strcmp(arch, "riscv"))
return &callbacks_riscv;
else if (!strcmp(arch, "s390"))
return &callbacks_s390;
else if (!strcmp(arch, "x86"))
return &callbacks_x86;
pr_err("Fail to get thread callbacks for arch %s, returns NULL\n",
arch);
return NULL;
}
static int
frame_callback(Dwfl_Frame *state, void *arg)
@@ -278,6 +312,7 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
const char *arch = perf_env__arch(ui_buf.machine->env);
Dwarf_Word ip;
int err = -EINVAL, i;
const Dwfl_Thread_Callbacks *callbacks;
if (!data->user_regs || !data->user_regs->regs)
return -EINVAL;
@@ -300,7 +335,11 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
if (err)
goto out;
err = !dwfl_attach_state(ui->dwfl, EM_NONE, thread__tid(thread), &callbacks, ui);
callbacks = get_thread_callbacks(arch);
if (!callbacks)
goto out;
err = !dwfl_attach_state(ui->dwfl, EM_NONE, thread__tid(thread), callbacks, ui);
if (err)
goto out;
+9 -1
View File
@@ -9,7 +9,15 @@ struct machine;
struct perf_sample;
struct thread;
bool libdw__arch_set_initial_registers(Dwfl_Thread *thread, void *arg);
bool libdw_set_initial_registers_x86(Dwfl_Thread *thread, void *arg);
bool libdw_set_initial_registers_arm(Dwfl_Thread *thread, void *arg);
bool libdw_set_initial_registers_arm64(Dwfl_Thread *thread, void *arg);
bool libdw_set_initial_registers_csky(Dwfl_Thread *thread, void *arg);
bool libdw_set_initial_registers_loongarch(Dwfl_Thread *thread, void *arg);
bool libdw_set_initial_registers_mips(Dwfl_Thread *thread, void *arg);
bool libdw_set_initial_registers_powerpc(Dwfl_Thread *thread, void *arg);
bool libdw_set_initial_registers_riscv(Dwfl_Thread *thread, void *arg);
bool libdw_set_initial_registers_s390(Dwfl_Thread *thread, void *arg);
struct unwind_info {
Dwfl *dwfl;