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.

* 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_dwarf_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]>
---
 src/stackprof.cxx | 60 ++++++++++++++---------------------------------
 1 file changed, 17 insertions(+), 43 deletions(-)

diff --git a/src/stackprof.cxx b/src/stackprof.cxx
index 0371c7ea..85b41fdb 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 {
+  /* TODO(REVIEW) 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_dwarf_reg(machine, 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 << format("WARNING: dwflst_arch_sp_dwarf_reg: arch unsupported\n", 
dwfl_errmsg(-1));
+      else if (verbose)
        cerr << format("WARNING: dwfl_frame_reg: {}\n", dwfl_errmsg(-1));
       return DWARF_CB_ABORT;
     }
-- 
2.54.0

Reply via email to