[PATCH v2 2/2] PR34498 stackprof: adopt new libdwflst API for per-arch constants
Serhei Makarov
serhei@serhei.io
Wed Aug 12 18:43:54 GMT 2026
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 <serhei@serhei.io>
---
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
More information about the Elfutils-devel
mailing list