One need identified from implementing patches for downstream profiling tools to adopt libdwfl_stacktrace is that libebl per-architecture constants are not exposed to external projects.
TODO(REVIEW): First iteration of this patch for RFC: the APIs are part of libdwfl_stacktrace, and return error values on architectures that lack a sample_regs backend (to signal that libdwflst is not yet supported there). This is most useful for the profiler use case, but need to double-check if it makes sense to provide a more general arch constant API in libdwfl/libdw. * libdwfl_stacktrace/libdwfl_stacktrace.h (dwflst_arch_from_uname): New API, translates umachine str obtained from uname() -> struct utsname -> machine to ELF machine ID. (dwflst_arch_sp_dwarf_reg): New API, identifies index of stack ptr. (dwflst_arch_expected_frame_nregs): New API, minimal number of regs for unwinding (useful for sanity-checking incoming stack samples). * libdwfl_stacktrace/dwflst_sample_frame.c: Format in two sections. (dwflst_arch_from_uname): Implementation. (dwflst_arch_expected_frame_nregs): Implementation. (dwflst_arch_sp_dwarf_reg): Implementation. * libdw/libdw.map (ELFUTILS_0.196_EXPERIMENTAL): Add the new functions. Signed-off-by: Serhei Makarov <[email protected]> --- libdw/libdw.map | 7 +++ libdwfl_stacktrace/dwflst_sample_frame.c | 58 +++++++++++++++++++++++- libdwfl_stacktrace/libdwfl_stacktrace.h | 21 +++++++++ 3 files changed, 85 insertions(+), 1 deletion(-) diff --git a/libdw/libdw.map b/libdw/libdw.map index b45647e6..f1f8362a 100644 --- a/libdw/libdw.map +++ b/libdw/libdw.map @@ -411,3 +411,10 @@ ELFUTILS_0.194_EXPERIMENTAL { global: dwflst_sample_getframes; } ELFUTILS_0.193_EXPERIMENTAL; + +ELFUTILS_0.196_EXPERIMENTAL { + global: + dwflst_arch_from_uname; + dwflst_arch_expected_frame_nregs; + dwflst_arch_sp_dwarf_reg; +} ELFUTILS_0.194_EXPERIMENTAL; diff --git a/libdwfl_stacktrace/dwflst_sample_frame.c b/libdwfl_stacktrace/dwflst_sample_frame.c index cf33a439..0e7d9f62 100644 --- a/libdwfl_stacktrace/dwflst_sample_frame.c +++ b/libdwfl_stacktrace/dwflst_sample_frame.c @@ -32,10 +32,13 @@ #include "libdwfl_stacktraceP.h" +/* Various functions providing arch-specific info: */ + Ebl *default_ebl = NULL; GElf_Half default_ebl_machine = EM_NONE; -uint64_t dwflst_perf_sample_preferred_regs_mask (GElf_Half machine) +uint64_t +dwflst_perf_sample_preferred_regs_mask (GElf_Half machine) { /* XXX The most likely case is that this will only be called once, for the current architecture. So we keep one Ebl* around for @@ -56,6 +59,59 @@ uint64_t dwflst_perf_sample_preferred_regs_mask (GElf_Half machine) return 0; } +GElf_Half +dwflst_arch_from_uname (const char *umachine) +{ + if (strncmp(umachine, "x86_64", 6) == 0) + return EM_X86_64; + else if (strncmp(umachine, "i686", 4) == 0 + || strncmp(umachine, "i386", 4) == 0) + return EM_386; + else if (strncmp(umachine, "aarch64", 7) == 0) + return EM_AARCH64; + else if (strncmp(umachine, "armv7l", 6) == 0) + return EM_ARM; + /* XXX Other architectures not yet supported. */ + return EM_NONE; +} + +uint32_t +dwflst_arch_expected_frame_nregs (GElf_Half machine) +{ + /* For aarch64, we actually use fewer than ebl->frame_nregs to unwind: */ + if (machine == EM_AARCH64) + return 14; + if (machine == EM_ARM) + return 16; + /* On x86, expect everything except FLAGS: */ + if (machine == EM_X86_64 || machine == EM_386) + /* XXX An external user of the library can't access the Ebl, hence + can't conveniently provide it to us it here. We provide the + constant directly rather than initializing a new Ebl. */ + return machine == EM_X86_64 ? 17 : 9; + /* return ebl_frame_nregs(ebl); */ + /* XXX Other architectures are not supported yet. + In general, it's fine to be on the permissive side here. */ + return 1; +} + +int +dwflst_arch_sp_dwarf_reg (GElf_Half machine, bool is_abi32) +{ + /* Use is_abi32 to downgrade from 64-bit to 32-bit, + but not in the other direction since 64-bit samples + do not occur on a 32-bit ELF machine: */ + if (machine == EM_X86_64 || machine == EM_386) + return (is_abi32 || machine == EM_386) ? 4 : 7; + else if (machine == EM_ARM || machine == EM_AARCH64) + return (is_abi32 || machine == EM_ARM) ? 13 : 31; + else + /* XXX Other architectures are not supported yet. */ + return -1; +} + +/* Stack sample handling: */ + struct sample_info { pid_t pid; pid_t tid; diff --git a/libdwfl_stacktrace/libdwfl_stacktrace.h b/libdwfl_stacktrace/libdwfl_stacktrace.h index 84cb69a3..82689aca 100644 --- a/libdwfl_stacktrace/libdwfl_stacktrace.h +++ b/libdwfl_stacktrace/libdwfl_stacktrace.h @@ -154,6 +154,27 @@ int dwflst_perf_sample_getframes (Dwfl *dwfl, Elf *elf, pid_t pid, pid_t tid, opened later. */ uint64_t dwflst_perf_sample_preferred_regs_mask (GElf_Half machine); +/* Returns the correct ELF machine identifier for the arch identifier + string UMACHINE from struct utsname, if libdwflst handles stack + samples for MACHINE. Returns EM_NONE if stack sample handling is + not supported. */ +GElf_Half dwflst_arch_from_uname (const char *umachine); + +/* Returns the minimum required number of registers for unwinding + for MACHINE, which should be at most the number of bits set in + dwflst_perf_sample_preferred_regs_mask(MACHINE). Returns 0 + if libdwfl does not handle stack samples for MACHINE. */ +uint32_t dwflst_arch_expected_frame_nregs (GElf_Half machine); + +/* Returns the index of the stack pointer register within the + dwarf_regs ordering for MACHINE. If IS_ABI32 is true, returns the + index within the dwarf_regs ordering for the 32-bit variant of + MACHINE, to allow handling of mixed-architecture perf_events data. + (When providing data from non-perf_events sources, IS_ABI32 should + be set to false.) Returns -1 if libdwfl does not handle stack + samples for MACHINE. */ +int dwflst_arch_sp_dwarf_reg (GElf_Half machine, bool is_abi32); + #ifdef __cplusplus } #endif -- 2.54.0
