Hi Serhei,
On Wed, Aug 12, 2026 at 2:44 PM Serhei Makarov <[email protected]> wrote:
>
> This tidies up the architecture-handling code and shows how a profiler
> project outside of Elfutils codebase (no access to libebl) can use the
> new libdwflst API to accomplish what stackprof does.
>
> v2: Now incorporates a necessary fix for PR34498.
>
> * src/stackprof.cxx (class PerfConsumerUnwinder): Remove get_sp_reg.
> (PerfReader::PerfReader): Use dwflst_arch_from_uname for ELF arch ID.
> (expected_frame_nregs): Remove.
> (PerfConsumerUnwinder::find_dwfl): Additional bounds check on sp,
> use dwflst_arch_expected_frame_nregs and dwflst_arch_sp_perf_reg
> for per-architecture values.
> (PerfConsumerUnwinder::get_sp_reg): Remove.
> (PerfConsumerUnwinder::unwind_frame_cb): Additional bounds check on
> sp, use dwflst_arch_sp_dwarf_reg for per-architecture value.
>
> Signed-off-by: Serhei Makarov <[email protected]>
LGTM.
Aaron
> ---
> src/stackprof.cxx | 60 ++++++++++++++---------------------------------
> 1 file changed, 17 insertions(+), 43 deletions(-)
>
> diff --git a/src/stackprof.cxx b/src/stackprof.cxx
> index 0371c7ea..42f5c053 100644
> --- a/src/stackprof.cxx
> +++ b/src/stackprof.cxx
> @@ -308,8 +308,6 @@ class PerfConsumerUnwinder: public PerfConsumer
> Dwfl *find_dwfl(pid_t pid, const uint64_t *regs, uint32_t nregs,
> Elf **elf, bool *cached);
>
> - int get_sp_reg(bool is_abi32);
> -
> public:
> PerfConsumerUnwinder(UnwindSampleConsumer* usc, UnwindStatsTable *ust);
> PerfConsumerUnwinder(UnwindSampleConsumer* usc, UnwindStatsTable *ust,
> PerfReader *reader);
> @@ -759,17 +757,13 @@ PerfReader::PerfReader(perf_event_attr* attr,
> PerfConsumer* consumer, int pid)
>
> struct utsname u;
> uname(&u);
> - int em = EM_NONE;
> - std::string_view machine = u.machine;
> - if (machine == "x86_64") em = EM_X86_64;
> - else if (machine == "i686" || machine == "i386") em = EM_386;
> - else if (machine == "aarch64") em = EM_AARCH64;
> - else if (machine == "armv7l") em = EM_ARM;
> - else {
> + /* XXX Possibly could be a libdwfl api, but can't be libebl since it
> + must be accessible by external tools. */
> + int em = dwflst_arch_from_uname(u.machine);
> + if (em == EM_NONE) {
> cerr << format("ERROR: Unsupported architecture: {}\n", u.machine);
> exit(1);
> }
> - // TODO: replace above with libdwflst api
> this->default_ebl = ebl_openbackend_machine(em);
> this->sample_regs_user = ebl_perf_frame_regs_mask (this->default_ebl);
> this->sample_regs_count = bitset<64>(this->sample_regs_user).count();
> @@ -1311,26 +1305,11 @@ Dwfl *pcu_init_dwfl_cb (Dwflst_Process_Tracker
> *cb_tracker __attribute__ ((unuse
> return pcu->init_dwfl(pid);
> }
>
> -uint32_t expected_frame_nregs (Ebl *ebl)
> -{
> - int m = ebl_get_elfmachine(ebl);
> - /* TODO: Generalize the API via libdwflst to allow any architecture. */
> - /* For aarch64, we actually use fewer than ebl->frame_nregs to unwind. */
> - if (m == EM_AARCH64)
> - return 14;
> - if (m == EM_ARM)
> - return 16;
> - /* On x86, expect everything except FLAGS: */
> - 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;
> -}
> -
> Dwfl *PerfConsumerUnwinder::find_dwfl(pid_t pid, const uint64_t *regs,
> uint32_t nregs,
> Elf **out_elf, bool *cached)
> {
> - if (nregs < expected_frame_nregs(this->reader->ebl()))
> + int machine = ebl_get_elfmachine(this->reader->ebl());
> + if (nregs < dwflst_arch_expected_frame_nregs(machine))
> {
> if (verbose)
> cerr << format(N_("WARNING: find_dwfl: nregs={}, expected at least
> {}\n"), nregs, ebl_frame_nregs(this->reader->ebl()));
> @@ -1355,8 +1334,10 @@ Dwfl *PerfConsumerUnwinder::find_dwfl(pid_t pid, const
> uint64_t *regs, uint32_t
> }
>
> reuse:
> - /* TODO: bounds check? */
> - this->last_us.sp = regs[this->get_sp_reg(this->last_us.elfclass ==
> ELFCLASS32)];
> + bool is_abi32 = this->last_us.elfclass == ELFCLASS32;
> + int user_regs_sp = dwflst_arch_sp_perf_reg(machine,
> this->reader->regs_mask(), is_abi32);
> + /* Bounds check, unlikely to fail: */
> + this->last_us.sp = user_regs_sp >= 0 ? regs[user_regs_sp] : 0;
> this->last_us.base = this->last_us.sp;
>
> if (!*cached)
> @@ -1365,17 +1346,6 @@ Dwfl *PerfConsumerUnwinder::find_dwfl(pid_t pid, const
> uint64_t *regs, uint32_t
> return dwfl;
> }
>
> -/* TODO move above */
> -/* Index of stack pointer within dwarf_regs order: */
> -int PerfConsumerUnwinder::get_sp_reg(bool is_abi32)
> -{
> - /* TODO: Generalize the API via libdwflst to allow any architecture. */
> - int machine = ebl_get_elfmachine(this->reader->ebl());
> - if (machine == EM_X86_64 || machine == EM_386) return is_abi32 ? 4 : 7;
> - else if (machine == EM_ARM || machine == EM_AARCH64) return is_abi32 ? 13
> : 31;
> - else { assert(0); return 7; }
> -}
> -
> int PerfConsumerUnwinder::unwind_frame_cb(Dwfl_Frame *state)
> {
> Dwarf_Addr pc;
> @@ -1391,11 +1361,15 @@ int PerfConsumerUnwinder::unwind_frame_cb(Dwfl_Frame
> *state)
> Dwarf_Addr sp;
>
> int is_abi32 = (this->last_us.elfclass == ELFCLASS32);
> - int user_regs_sp = this->get_sp_reg(is_abi32);
> - int rc = dwfl_frame_reg(state, user_regs_sp, &sp);
> + int m = ebl_get_elfmachine(this->reader->ebl());
> + int user_regs_sp = dwflst_arch_sp_dwarf_reg(m, is_abi32);
> + /* Bounds check, unlikely to fail: */
> + int rc = user_regs_sp >= 0 ? dwfl_frame_reg(state, user_regs_sp, &sp) : -1;
> if (rc < 0)
> {
> - if (verbose)
> + if (verbose && user_regs_sp < 0)
> + cerr << "WARNING: dwflst_arch_sp_dwarf_reg: arch unsupported\n";
> + else if (verbose)
> cerr << format("WARNING: dwfl_frame_reg: {}\n", dwfl_errmsg(-1));
> return DWARF_CB_ABORT;
> }
> --
> 2.54.0
>