mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Ian Rogers <irogers@google.com>
To: Stian Halseth <stian@itx.no>
Cc: Peter Zijlstra <peterz@infradead.org>,
	Ingo Molnar <mingo@redhat.com>,
	 Andreas Larsson <andreas@gaisler.com>,
	"David S. Miller" <davem@davemloft.net>,
	 Arnaldo Carvalho de Melo <acme@kernel.org>,
	Namhyung Kim <namhyung@kernel.org>,
	 Mark Rutland <mark.rutland@arm.com>,
	 Alexander Shishkin <alexander.shishkin@linux.intel.com>,
	Jiri Olsa <jolsa@kernel.org>,
	 Adrian Hunter <adrian.hunter@intel.com>,
	James Clark <james.clark@linaro.org>,
	 Jonathan Corbet <corbet@lwn.net>,
	Shuah Khan <skhan@linuxfoundation.org>,
	 Randy Dunlap <rdunlap@infradead.org>,
	Magnus Lindholm <linmag7@gmail.com>,
	 linux-perf-users@vger.kernel.org, sparclinux@vger.kernel.org,
	 linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: Re: [RFC PATCH v2 3/3] perf tools: Support sparc user register samples and dwarf unwinding
Date: Tue, 22 Sep 2026 13:31:42 -0700	[thread overview]
Message-ID: <CAP-5=fWNhJGHnRoz7FrwZO2mOZa4NCi5C3ZsFGrirAE0prj69w@mail.gmail.com> (raw)
In-Reply-To: <20260922201507.1719668-4-stian@itx.no>

On Tue, Sep 22, 2026 at 1:15 PM Stian Halseth <stian@itx.no> wrote:
>
> Add the sparc perf_regs tables and the EM_SPARC/EM_SPARCV9 dispatch for
> register names, masks, ip/sp and the perf-to-DWARF register mapping,
> plus the uapi header copy.
>
> For libdw unwinding, seed DWARF registers 16-31 (%l0-%l7, %i0-%i7) from
> the register save area at the top of the stack dump: the kernel flushes
> the sampled window there, and the CFI after `save` needs %i6 and %i7 to
> find the first caller. The dump base on sparc is the biased %sp, so
> read words from the dump with memcpy rather than an aligned
> dereference.
>
> Signed-off-by: Stian Halseth <stian@itx.no>
> ---
>  tools/arch/sparc/include/uapi/asm/perf_regs.h | 33 +++++++++
>  tools/perf/arch/sparc/include/perf_regs.h     | 18 +++++
>  tools/perf/check-headers.sh                   |  1 +
>  tools/perf/util/dwarf-regs-arch/Build         |  1 +
>  .../util/dwarf-regs-arch/dwarf-regs-sparc.c   | 12 ++++
>  tools/perf/util/dwarf-regs.c                  |  4 ++
>  tools/perf/util/include/dwarf-regs.h          |  1 +
>  tools/perf/util/perf-regs-arch/Build          |  1 +
>  .../util/perf-regs-arch/perf_regs_sparc.c     | 69 +++++++++++++++++++
>  tools/perf/util/perf_regs.c                   | 18 +++++
>  tools/perf/util/perf_regs.h                   |  5 ++
>  tools/perf/util/unwind-libdw.c                | 41 ++++++++++-
>  12 files changed, 203 insertions(+), 1 deletion(-)
>  create mode 100644 tools/arch/sparc/include/uapi/asm/perf_regs.h
>  create mode 100644 tools/perf/arch/sparc/include/perf_regs.h
>  create mode 100644 tools/perf/util/dwarf-regs-arch/dwarf-regs-sparc.c
>  create mode 100644 tools/perf/util/perf-regs-arch/perf_regs_sparc.c
>
> diff --git a/tools/arch/sparc/include/uapi/asm/perf_regs.h b/tools/arch/sparc/include/uapi/asm/perf_regs.h
> new file mode 100644
> index 000000000000..17f14335d6c0
> --- /dev/null
> +++ b/tools/arch/sparc/include/uapi/asm/perf_regs.h
> @@ -0,0 +1,33 @@
> +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
> +#ifndef _UAPI_ASM_SPARC_PERF_REGS_H
> +#define _UAPI_ASM_SPARC_PERF_REGS_H
> +
> +/*
> + * Mirrors struct pt_regs.  O6 is %sp as held in the register, i.e. biased
> + * by 2047 for a 64-bit task.
> + */
> +enum perf_event_sparc_regs {
> +       PERF_REG_SPARC_G0,
> +       PERF_REG_SPARC_G1,
> +       PERF_REG_SPARC_G2,
> +       PERF_REG_SPARC_G3,
> +       PERF_REG_SPARC_G4,
> +       PERF_REG_SPARC_G5,
> +       PERF_REG_SPARC_G6,
> +       PERF_REG_SPARC_G7,
> +       PERF_REG_SPARC_O0,
> +       PERF_REG_SPARC_O1,
> +       PERF_REG_SPARC_O2,
> +       PERF_REG_SPARC_O3,
> +       PERF_REG_SPARC_O4,
> +       PERF_REG_SPARC_O5,
> +       PERF_REG_SPARC_O6,
> +       PERF_REG_SPARC_O7,
> +       PERF_REG_SPARC_TSTATE,
> +       PERF_REG_SPARC_PC,
> +       PERF_REG_SPARC_NPC,
> +       PERF_REG_SPARC_Y,
> +       PERF_REG_SPARC_MAX
> +};
> +
> +#endif /* _UAPI_ASM_SPARC_PERF_REGS_H */
> diff --git a/tools/perf/arch/sparc/include/perf_regs.h b/tools/perf/arch/sparc/include/perf_regs.h
> new file mode 100644
> index 000000000000..493fbff9ea16
> --- /dev/null
> +++ b/tools/perf/arch/sparc/include/perf_regs.h
> @@ -0,0 +1,18 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +#ifndef ARCH_PERF_REGS_H
> +#define ARCH_PERF_REGS_H
> +
> +#include <stdlib.h>
> +#include <linux/types.h>
> +#include "../../../../arch/sparc/include/uapi/asm/perf_regs.h"
> +
> +#define PERF_REGS_MASK ((1ULL << PERF_REG_SPARC_MAX) - 1)
> +#define PERF_REGS_MAX  PERF_REG_SPARC_MAX
> +
> +#ifdef __arch64__
> +#define PERF_SAMPLE_REGS_ABI   PERF_SAMPLE_REGS_ABI_64
> +#else
> +#define PERF_SAMPLE_REGS_ABI   PERF_SAMPLE_REGS_ABI_32
> +#endif
> +
> +#endif /* ARCH_PERF_REGS_H */
> diff --git a/tools/perf/check-headers.sh b/tools/perf/check-headers.sh
> index 531c0e0e84df..f144590f5082 100755
> --- a/tools/perf/check-headers.sh
> +++ b/tools/perf/check-headers.sh
> @@ -42,6 +42,7 @@ declare -a FILES=(
>    "arch/mips/include/uapi/asm/perf_regs.h"
>    "arch/powerpc/include/uapi/asm/perf_regs.h"
>    "arch/s390/include/uapi/asm/perf_regs.h"
> +  "arch/sparc/include/uapi/asm/perf_regs.h"
>    "arch/x86/include/uapi/asm/perf_regs.h"
>    "arch/x86/include/uapi/asm/kvm.h"
>    "arch/x86/include/uapi/asm/svm.h"
> diff --git a/tools/perf/util/dwarf-regs-arch/Build b/tools/perf/util/dwarf-regs-arch/Build
> index ceb68ae86fd8..bda944029046 100644
> --- a/tools/perf/util/dwarf-regs-arch/Build
> +++ b/tools/perf/util/dwarf-regs-arch/Build
> @@ -6,4 +6,5 @@ perf-util-$(CONFIG_LIBDW) += dwarf-regs-mips.o
>  perf-util-$(CONFIG_LIBDW) += dwarf-regs-powerpc.o
>  perf-util-$(CONFIG_LIBDW) += dwarf-regs-riscv.o
>  perf-util-$(CONFIG_LIBDW) += dwarf-regs-s390.o
> +perf-util-$(CONFIG_LIBDW) += dwarf-regs-sparc.o
>  perf-util-$(CONFIG_LIBDW) += dwarf-regs-x86.o
> diff --git a/tools/perf/util/dwarf-regs-arch/dwarf-regs-sparc.c b/tools/perf/util/dwarf-regs-arch/dwarf-regs-sparc.c
> new file mode 100644
> index 000000000000..dd0c80e29764
> --- /dev/null
> +++ b/tools/perf/util/dwarf-regs-arch/dwarf-regs-sparc.c
> @@ -0,0 +1,12 @@
> +// SPDX-License-Identifier: GPL-2.0
> +#include <errno.h>
> +#include <dwarf-regs.h>
> +#include "../../../arch/sparc/include/uapi/asm/perf_regs.h"
> +
> +int __get_dwarf_regnum_for_perf_regnum_sparc(int perf_regnum)
> +{
> +       if (perf_regnum < PERF_REG_SPARC_G0 || perf_regnum > PERF_REG_SPARC_O7)
> +               return -ENOENT;
> +
> +       return perf_regnum;
> +}
> diff --git a/tools/perf/util/dwarf-regs.c b/tools/perf/util/dwarf-regs.c
> index 797f455eba0d..df9200c31aaa 100644
> --- a/tools/perf/util/dwarf-regs.c
> +++ b/tools/perf/util/dwarf-regs.c
> @@ -217,6 +217,10 @@ int get_dwarf_regnum_for_perf_regnum(int perf_regnum, unsigned int machine,
>         case EM_S390:
>                 reg = __get_dwarf_regnum_for_perf_regnum_s390(perf_regnum);
>                 break;
> +       case EM_SPARC:
> +       case EM_SPARCV9:
> +               reg = __get_dwarf_regnum_for_perf_regnum_sparc(perf_regnum);
> +               break;
>         case EM_LOONGARCH:
>                 reg = __get_dwarf_regnum_for_perf_regnum_loongarch(perf_regnum);
>                 break;
> diff --git a/tools/perf/util/include/dwarf-regs.h b/tools/perf/util/include/dwarf-regs.h
> index 46a764cf322f..4a02f30c5604 100644
> --- a/tools/perf/util/include/dwarf-regs.h
> +++ b/tools/perf/util/include/dwarf-regs.h
> @@ -113,6 +113,7 @@ int __get_dwarf_regnum_for_perf_regnum_loongarch(int perf_regnum);
>  int __get_dwarf_regnum_for_perf_regnum_powerpc(int perf_regnum);
>  int __get_dwarf_regnum_for_perf_regnum_riscv(int perf_regnum);
>  int __get_dwarf_regnum_for_perf_regnum_s390(int perf_regnum);
> +int __get_dwarf_regnum_for_perf_regnum_sparc(int perf_regnum);
>  int __get_dwarf_regnum_for_perf_regnum_mips(int perf_regnum);
>
>  /*
> diff --git a/tools/perf/util/perf-regs-arch/Build b/tools/perf/util/perf-regs-arch/Build
> index be95402aa540..47220a2f2dae 100644
> --- a/tools/perf/util/perf-regs-arch/Build
> +++ b/tools/perf/util/perf-regs-arch/Build
> @@ -6,4 +6,5 @@ perf-util-y += perf_regs_mips.o
>  perf-util-y += perf_regs_powerpc.o
>  perf-util-y += perf_regs_riscv.o
>  perf-util-y += perf_regs_s390.o
> +perf-util-y += perf_regs_sparc.o
>  perf-util-y += perf_regs_x86.o
> diff --git a/tools/perf/util/perf-regs-arch/perf_regs_sparc.c b/tools/perf/util/perf-regs-arch/perf_regs_sparc.c
> new file mode 100644
> index 000000000000..36d84ddd3a48
> --- /dev/null
> +++ b/tools/perf/util/perf-regs-arch/perf_regs_sparc.c
> @@ -0,0 +1,69 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +#include "../perf_regs.h"
> +#include "../../arch/sparc/include/perf_regs.h"
> +
> +uint64_t __perf_reg_mask_sparc(bool intr __maybe_unused)
> +{
> +       return PERF_REGS_MASK;
> +}
> +
> +const char *__perf_reg_name_sparc(int id)
> +{
> +       switch (id) {
> +       case PERF_REG_SPARC_G0:
> +               return "g0";
> +       case PERF_REG_SPARC_G1:
> +               return "g1";
> +       case PERF_REG_SPARC_G2:
> +               return "g2";
> +       case PERF_REG_SPARC_G3:
> +               return "g3";
> +       case PERF_REG_SPARC_G4:
> +               return "g4";
> +       case PERF_REG_SPARC_G5:
> +               return "g5";
> +       case PERF_REG_SPARC_G6:
> +               return "g6";
> +       case PERF_REG_SPARC_G7:
> +               return "g7";
> +       case PERF_REG_SPARC_O0:
> +               return "o0";
> +       case PERF_REG_SPARC_O1:
> +               return "o1";
> +       case PERF_REG_SPARC_O2:
> +               return "o2";
> +       case PERF_REG_SPARC_O3:
> +               return "o3";
> +       case PERF_REG_SPARC_O4:
> +               return "o4";
> +       case PERF_REG_SPARC_O5:
> +               return "o5";
> +       case PERF_REG_SPARC_O6:
> +               return "sp";
> +       case PERF_REG_SPARC_O7:
> +               return "o7";
> +       case PERF_REG_SPARC_TSTATE:
> +               return "tstate";
> +       case PERF_REG_SPARC_PC:
> +               return "pc";
> +       case PERF_REG_SPARC_NPC:
> +               return "npc";
> +       case PERF_REG_SPARC_Y:
> +               return "y";
> +       default:
> +               return NULL;
> +       }
> +
> +       return NULL;
> +}
> +
> +uint64_t __perf_reg_ip_sparc(void)
> +{
> +       return PERF_REG_SPARC_PC;
> +}
> +
> +uint64_t __perf_reg_sp_sparc(void)
> +{
> +       return PERF_REG_SPARC_O6;
> +}
> diff --git a/tools/perf/util/perf_regs.c b/tools/perf/util/perf_regs.c
> index 558c143abbab..5880b54fd7dc 100644
> --- a/tools/perf/util/perf_regs.c
> +++ b/tools/perf/util/perf_regs.c
> @@ -68,6 +68,10 @@ uint64_t perf_intr_reg_mask(uint16_t e_machine)
>         case EM_S390:
>                 mask = __perf_reg_mask_s390(/*intr=*/true);
>                 break;
> +       case EM_SPARC:
> +       case EM_SPARCV9:
> +               mask = __perf_reg_mask_sparc(/*intr=*/true);
> +               break;
>         case EM_386:
>         case EM_X86_64:
>                 mask = __perf_reg_mask_x86(/*intr=*/true);
> @@ -111,6 +115,10 @@ uint64_t perf_user_reg_mask(uint16_t e_machine)
>         case EM_S390:
>                 mask = __perf_reg_mask_s390(/*intr=*/false);
>                 break;
> +       case EM_SPARC:
> +       case EM_SPARCV9:
> +               mask = __perf_reg_mask_sparc(/*intr=*/false);
> +               break;
>         case EM_386:
>         case EM_X86_64:
>                 mask = __perf_reg_mask_x86(/*intr=*/false);
> @@ -154,6 +162,10 @@ const char *perf_reg_name(int id, uint16_t e_machine, uint32_t e_flags)
>         case EM_S390:
>                 reg_name = __perf_reg_name_s390(id);
>                 break;
> +       case EM_SPARC:
> +       case EM_SPARCV9:
> +               reg_name = __perf_reg_name_sparc(id);
> +               break;
>         case EM_386:
>         case EM_X86_64:
>                 reg_name = __perf_reg_name_x86(id);
> @@ -215,6 +227,9 @@ uint64_t perf_arch_reg_ip(uint16_t e_machine)
>                 return __perf_reg_ip_riscv();
>         case EM_S390:
>                 return __perf_reg_ip_s390();
> +       case EM_SPARC:
> +       case EM_SPARCV9:
> +               return __perf_reg_ip_sparc();
>         case EM_386:
>         case EM_X86_64:
>                 return __perf_reg_ip_x86();
> @@ -244,6 +259,9 @@ uint64_t perf_arch_reg_sp(uint16_t e_machine)
>                 return __perf_reg_sp_riscv();
>         case EM_S390:
>                 return __perf_reg_sp_s390();
> +       case EM_SPARC:
> +       case EM_SPARCV9:
> +               return __perf_reg_sp_sparc();
>         case EM_386:
>         case EM_X86_64:
>                 return __perf_reg_sp_x86();
> diff --git a/tools/perf/util/perf_regs.h b/tools/perf/util/perf_regs.h
> index 79be2b791509..3f453d910aa4 100644
> --- a/tools/perf/util/perf_regs.h
> +++ b/tools/perf/util/perf_regs.h
> @@ -65,6 +65,11 @@ uint64_t __perf_reg_ip_s390(void);
>  uint64_t __perf_reg_sp_s390(void);
>  int __perf_sdt_arg_parse_op_s390(char *old_op, char **new_op);
>
> +uint64_t __perf_reg_mask_sparc(bool intr);
> +const char *__perf_reg_name_sparc(int id);
> +uint64_t __perf_reg_ip_sparc(void);
> +uint64_t __perf_reg_sp_sparc(void);
> +
>  int __perf_sdt_arg_parse_op_x86(char *old_op, char **new_op);
>  uint64_t __perf_reg_mask_x86(bool intr);
>  const char *__perf_reg_name_x86(int id);
> diff --git a/tools/perf/util/unwind-libdw.c b/tools/perf/util/unwind-libdw.c
> index 63a5c2253174..62484bcd5cd3 100644
> --- a/tools/perf/util/unwind-libdw.c
> +++ b/tools/perf/util/unwind-libdw.c
> @@ -4,7 +4,9 @@
>  #include <elfutils/libdw.h>
>  #include <elfutils/libdwfl.h>
>  #include <inttypes.h>
> +#include <elf.h>
>  #include <errno.h>
> +#include <string.h>
>  #include "debug.h"
>  #include "dso.h"
>  #include <dwarf-regs.h>
> @@ -261,12 +263,44 @@ static bool memory_read(Dwfl *dwfl __maybe_unused, Dwarf_Addr addr, Dwarf_Word *
>         }
>
>         offset  = addr - start;
> -       *result = *(Dwarf_Word *)&stack->data[offset];
> +       memcpy(result, &stack->data[offset], sizeof(*result));
>         pr_debug("unwind: access_mem addr 0x%" PRIx64 ", val %lx, offset %d\n",
>                  addr, (unsigned long)*result, offset);
>         return true;
>  }
>
> +/*
> + * The sampled window's %l and %i registers (DWARF 16-31) are not in the
> + * sample; the kernel flushed them to the register save area at the top of
> + * the dumped stack.
> + */
> +static void libdw_set_sparc_window_registers(struct unwind_info *ui,
> +                                            struct regs_dump *user_regs,
> +                                            Dwarf_Word *dwarf_regs)
> +{
> +       struct stack_dump *stack = &ui->sample->user_stack;
> +       bool abi64 = user_regs->abi == PERF_SAMPLE_REGS_ABI_64;
> +       size_t wsize = abi64 ? sizeof(u64) : sizeof(u32);
> +       u64 sp, offset = abi64 ? 2047 : 0;
> +
> +       if (perf_reg_value(&sp, user_regs, perf_arch_reg_sp(ui->e_machine)))
> +               return;
> +       if (offset + 16 * wsize > stack->size)
> +               return;
> +
> +       for (int i = 0; i < 16; i++) {
> +               const char *p = &stack->data[offset + i * wsize];
> +               u32 w32;
> +
> +               if (abi64) {
> +                       memcpy(&dwarf_regs[16 + i], p, sizeof(u64));
> +               } else {
> +                       memcpy(&w32, p, sizeof(w32));
> +                       dwarf_regs[16 + i] = w32;
> +               }
> +       }
> +}
> +
>  static bool libdw_set_initial_registers(Dwfl_Thread *thread, void *arg)
>  {
>         struct dwfl_ui_thread_info *dwfl_ui_ti = arg;
> @@ -276,6 +310,7 @@ static bool libdw_set_initial_registers(Dwfl_Thread *thread, void *arg)
>         int max_dwarf_reg = 0;
>         bool ret;
>         uint16_t e_machine = ui->e_machine;
> +       bool is_sparc = e_machine == EM_SPARC || e_machine == EM_SPARCV9;
>         int e_flags = ui->e_flags;
>         uint64_t ip_perf_reg = perf_arch_reg_ip(e_machine);
>         Dwarf_Word val = 0;
> @@ -295,6 +330,8 @@ static bool libdw_set_initial_registers(Dwfl_Thread *thread, void *arg)
>                                 max_dwarf_reg = dwarf_reg;
>                 }
>         }
> +       if (is_sparc && max_dwarf_reg < 31)
> +               max_dwarf_reg = 31;

Thanks! I think these are great patches! At this point in the code are
you setting max_dwarf_reg so that there's space for the window
registers? I think adding a comment here for clarity would be
worthwhile as it matches up with libdw_set_sparc_window_registers.

Thanks,
Ian

>
>         dwarf_regs = calloc(max_dwarf_reg + 1, sizeof(*dwarf_regs));
>         if (!dwarf_regs)
> @@ -313,6 +350,8 @@ static bool libdw_set_initial_registers(Dwfl_Thread *thread, void *arg)
>                         }
>                 }
>         }
> +       if (is_sparc)
> +               libdw_set_sparc_window_registers(ui, user_regs, dwarf_regs);
>         if (perf_reg_value(&val, user_regs, ip_perf_reg) == 0)
>                 dwfl_thread_state_register_pc(thread, val);
>
> --
> 2.55.0
>

  reply	other threads:[~2026-09-22 20:31 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-22 20:15 [RFC PATCH v2 0/3] perf: sparc64 user regs and stack dump, with an arch hook Stian Halseth
2026-09-22 20:15 ` [RFC PATCH v2 1/3] perf/core: Let an arch prepare the user stack before it is dumped Stian Halseth
2026-09-22 20:15 ` [RFC PATCH v2 2/3] sparc64: Support PERF_SAMPLE_REGS_USER and PERF_SAMPLE_STACK_USER Stian Halseth
2026-09-22 20:15 ` [RFC PATCH v2 3/3] perf tools: Support sparc user register samples and dwarf unwinding Stian Halseth
2026-09-22 20:31   ` Ian Rogers [this message]
2026-09-22 21:09     ` Stian Halseth

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to='CAP-5=fWNhJGHnRoz7FrwZO2mOZa4NCi5C3ZsFGrirAE0prj69w@mail.gmail.com' \
    --to=irogers@google.com \
    --cc=acme@kernel.org \
    --cc=adrian.hunter@intel.com \
    --cc=alexander.shishkin@linux.intel.com \
    --cc=andreas@gaisler.com \
    --cc=corbet@lwn.net \
    --cc=davem@davemloft.net \
    --cc=james.clark@linaro.org \
    --cc=jolsa@kernel.org \
    --cc=linmag7@gmail.com \
    --cc=linux-doc@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-perf-users@vger.kernel.org \
    --cc=mark.rutland@arm.com \
    --cc=mingo@redhat.com \
    --cc=namhyung@kernel.org \
    --cc=peterz@infradead.org \
    --cc=rdunlap@infradead.org \
    --cc=skhan@linuxfoundation.org \
    --cc=sparclinux@vger.kernel.org \
    --cc=stian@itx.no \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®