Hi Serhei,

On Tue, Mar 24, 2026 at 6:38 PM Serhei Makarov <[email protected]> wrote:
>
> Since we plan to replace stacktrace with stackprof (& the associated
> test case) in a subsequent patchset, this is a minimal patch to
> illustrate where the existing code using libdwfl_stacktrace needs to
> generalize.
>
> * src/stacktrace.c: Enable for ARM architectures.
>   (expected_frame_nregs): New function.
>   (sp_reg_index): New function.
>   (sysprof_find_dwfl): Replace hardcoded SP index with new functions.
>   (sysprof_unwind_frame_cb): Likewise.
>   (main): Open default_ebl backend appropriate to the current arch.
> ---
>  src/stacktrace.c | 51 +++++++++++++++++++++++++++++++++++-------------
>  1 file changed, 37 insertions(+), 14 deletions(-)
>
> diff --git a/src/stacktrace.c b/src/stacktrace.c
> index 0740a2bc..c1888b67 100644
> --- a/src/stacktrace.c
> +++ b/src/stacktrace.c
> @@ -80,13 +80,14 @@
>  #include <locale.h>
>
>  #include <system.h>
> +#include <sys/utsname.h>
>
>  #include <linux/perf_event.h>
>
> -/* TODO: Need to generalize the code beyond x86 architectures. */
> +/* TODO: Need to generalize the code beyond x86/ARM architectures. */
>  #include <asm/perf_regs.h>
> -#ifndef _ASM_X86_PERF_REGS_H
> -#error "eu-stacktrace is currently limited to x86 architectures"
> +#if !defined(_ASM_X86_PERF_REGS_H) && !defined(_ASM_ARM_PERF_REGS_H) && 
> !defined(_ASM_ARM64_PERF_REGS_H)
> +#error "eu-stacktrace is currently limited to x86/ARM architectures"

configure.ac needs to be updated to check for
_ASM_ARM[64]_PERF_REGS_H. Currently --enable-stacktrace will fail on
arm/aarch64.

>  #endif
>
>  /*************************************
> @@ -853,6 +854,26 @@ sysprof_init_dwfl_cb (Dwflst_Process_Tracker *cb_tracker,
>    return dwfl;
>  }
>
> +uint32_t expected_frame_nregs (Ebl *ebl)
> +{
> +  int m = ebl_get_elfmachine(ebl);
> +  /* For aarch64, we use fewer and ebl->frame_nregs to unwind.  */
> +  if (m == EM_ARM)

Here and elsewhere in this patch there should be hanlding for EM_AARCH64 too.

> +    return 14;
> +  if (m == EM_X86_64 || m == EM_386)
> +    return ebl_frame_nregs(ebl);
> +  /* In general, it's better to be on the permissive side.  */
> +  return 1;
> +}
> +
> +int sp_reg_index (Ebl *ebl, bool is_abi32)
> +{
> +  int m = ebl_get_elfmachine(ebl);
> +  if (m == EM_X86_64 || m == EM_386) return is_abi32 ? 4 : 7;
> +  else if (m == EM_ARM) return is_abi32 ? 13 : 31;
> +  else return 0; /* XXX unwinding will likely not continue */
> +}
> +
>  Dwfl *
>  sysprof_find_dwfl (struct sysprof_unwind_info *sui,
>                    SysprofCaptureStackUser *ev,
> @@ -860,10 +881,7 @@ sysprof_find_dwfl (struct sysprof_unwind_info *sui,
>                    Elf **out_elf)
>  {
>    pid_t pid = ev->frame.pid;
> -  /* XXX: Note that sysprof requesting the x86_64 register file from
> -     perf_events will result in an array of 17 regs even for 32-bit
> -     applications. */
> -  if (regs->n_regs < ebl_frame_nregs(default_ebl)) /* XXX expecting 
> everything except FLAGS */
> +  if (regs->n_regs < expected_frame_nregs(default_ebl))
>      {
>        if (show_failures)
>         fprintf(stderr, N_("sysprof_find_dwfl: n_regs=%d, expected %ld\n"),
> @@ -891,12 +909,11 @@ sysprof_find_dwfl (struct sysprof_unwind_info *sui,
>      }
>
>   reuse:
> -  /* TODO: Generalize to other architectures than x86_64. */
> -  sui->last_sp = regs->regs[7];
> +  bool is_abi32 = (regs->abi == PERF_SAMPLE_REGS_ABI_32);
> +  sui->last_sp = regs->regs[sp_reg_index(default_ebl, is_abi32)];
>    sui->last_base = sui->last_sp;
>
>    if (show_frames) {
> -    bool is_abi32 = (regs->abi == PERF_SAMPLE_REGS_ABI_32);
>      fprintf(stderr, "sysprof_find_dwfl pid %lld%s: size=%ld%s pc=%lx 
> sp=%lx+(%lx)\n",
>             (long long) pid, cached ? " (cached)" : "",
>             ev->size, is_abi32 ? " (32-bit)" : "",
> @@ -924,11 +941,9 @@ sysprof_unwind_frame_cb (Dwfl_Frame *state, void *arg)
>
>    Dwarf_Addr pc_adjusted = pc - (isactivation ? 0 : 1);
>    Dwarf_Addr sp;
> -  /* TODO: Need to generalize this code beyond x86 architectures. */
>    struct sysprof_unwind_info *sui = (struct sysprof_unwind_info *)arg;
>    int is_abi32 = (sui->last_abi == PERF_SAMPLE_REGS_ABI_32);
> -  /* DWARF register order cf. elfutils backends/{x86_64,i386}_initreg.c: */
> -  int user_regs_sp = is_abi32 ? 4 : 7;
> +  int user_regs_sp = sp_reg_index(default_ebl, is_abi32);
>    int rc = dwfl_frame_reg (state, user_regs_sp, &sp);
>    if (rc < 0)
>      {
> @@ -1376,7 +1391,15 @@ 
> https://sourceware.org/cgit/elfutils/tree/README.eu-stacktrace?h=users/serhei/eu
>         error (EXIT_BAD, errno, N_("Could not initialize Dwfl table"));
>
>        /* TODO: Generalize to other architectures. */
> -      default_ebl = ebl_openbackend_machine(EM_X86_64);
> +      struct utsname u;
> +      uname(&u);
> +      int em = EM_NONE;
> +      if (strcmp(u.machine, "x86_64") == 0) em = EM_X86_64;
> +      else if (strcmp(u.machine, "i686") == 0 || strcmp(u.machine, "i386") 
> == 0) em = EM_386;
> +      else if (strcmp(u.machine, "aarch64") == 0 || strcmp(u.machine, 
> "armv7l")) em = EM_ARM;

strcmp(u.machine, "armv7l") is missing == 0. Also "aarch64" should set
em = EM_AARCH64 instead of EM_ARM.

Aaron

> +      else
> +       error (EXIT_BAD, errno, N_("Unsupported architecture: %s"), 
> u.machine);
> +      default_ebl = ebl_openbackend_machine(em);
>
>        struct sysprof_unwind_info sui;
>        sui.output_fd = output_fd;
> --
> 2.53.0
>

Reply via email to