mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2025-05-23 15:04:59 +03:00
Support using llvm-libunwind
This commit adds support for using llvm-libunwind for kernels built using llvm and clang. The two differences are that the largest register index is given by _LIBUNWIND_HIGHEST_DWARF_REGISTER, we need to check whether the register is a floating point register and the prototype for unw_regname takes the unwind cursor as the first argument. Reviewed-by: Rob Norris <robn@despairlabs.com> Reviewed-by: Tony Hutter <hutter2@llnl.gov> Signed-off-by: Sebastian Pauka <me@spauka.se> Closes #17230
This commit is contained in:
parent
7031a48c70
commit
1b4826b9a2
@ -34,6 +34,22 @@ AC_DEFUN([ZFS_AC_CONFIG_USER_LIBUNWIND], [
|
||||
], [
|
||||
AC_MSG_RESULT([no])
|
||||
])
|
||||
dnl LLVM includes it's own libunwind library, which
|
||||
dnl defines the highest numbered register in a different
|
||||
dnl way, and has an incompatible unw_resname function.
|
||||
AC_MSG_CHECKING([whether libunwind is llvm libunwind])
|
||||
AC_COMPILE_IFELSE([
|
||||
AC_LANG_PROGRAM([
|
||||
#include <libunwind.h>
|
||||
#if !defined(_LIBUNWIND_HIGHEST_DWARF_REGISTER)
|
||||
#error "_LIBUNWIND_HIGHEST_DWARF_REGISTER is not defined"
|
||||
#endif
|
||||
], [])], [
|
||||
AC_MSG_RESULT([yes])
|
||||
AC_DEFINE(IS_LIBUNWIND_LLVM, 1, [libunwind is llvm libunwind])
|
||||
], [
|
||||
AC_MSG_RESULT([no])
|
||||
])
|
||||
AX_RESTORE_FLAGS
|
||||
], [
|
||||
AS_IF([test "x$with_libunwind" = "xyes"], [
|
||||
|
@ -41,9 +41,26 @@
|
||||
do { ssize_t r __maybe_unused = write(fd, s, n); } while (0)
|
||||
#define spl_bt_write(fd, s) spl_bt_write_n(fd, s, sizeof (s)-1)
|
||||
|
||||
#if defined(HAVE_LIBUNWIND)
|
||||
#ifdef HAVE_LIBUNWIND
|
||||
/*
|
||||
* libunwind-gcc and libunwind-llvm both list registers using an enum,
|
||||
* unw_regnum_t, however they indicate the highest numbered register for
|
||||
* a given architecture in different ways. We can check which one is defined
|
||||
* and mark which libunwind is in use
|
||||
*/
|
||||
#ifdef IS_LIBUNWIND_LLVM
|
||||
#include <libunwind.h>
|
||||
#define LAST_REG_INDEX _LIBUNWIND_HIGHEST_DWARF_REGISTER
|
||||
#else
|
||||
/*
|
||||
* Need to define UNW_LOCAL_ONLY before importing libunwind.h
|
||||
* if using libgcc libunwind.
|
||||
*/
|
||||
#define UNW_LOCAL_ONLY
|
||||
#include <libunwind.h>
|
||||
#define LAST_REG_INDEX UNW_TDEP_LAST_REG
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Convert `v` to ASCII hex characters. The bottom `n` nybbles (4-bits ie one
|
||||
@ -102,14 +119,13 @@ libspl_backtrace(int fd)
|
||||
unw_init_local(&cp, &uc);
|
||||
|
||||
/*
|
||||
* libunwind's list of possible registers for this architecture is an
|
||||
* enum, unw_regnum_t. UNW_TDEP_LAST_REG is the highest-numbered
|
||||
* register in that list, however, not all register numbers in this
|
||||
* range are defined by the architecture, and not all defined registers
|
||||
* will be present on every implementation of that architecture.
|
||||
* Moreover, libunwind provides nice names for most, but not all
|
||||
* registers, but these are hardcoded; a name being available does not
|
||||
* mean that register is available.
|
||||
* Iterate over all registers for the architecture. We've figured
|
||||
* out the highest number above, however, not all register numbers in
|
||||
* this range are defined by the architecture, and not all defined
|
||||
* registers will be present on every implementation of that
|
||||
* architecture. Moreover, libunwind provides nice names for most, but
|
||||
* not all registers, but these are hardcoded; a name being available
|
||||
* does not mean that register is available.
|
||||
*
|
||||
* So, we have to pull this all together here. We try to get the value
|
||||
* of every possible register. If we get a value for it, then the
|
||||
@ -120,26 +136,42 @@ libspl_backtrace(int fd)
|
||||
* thing.
|
||||
*/
|
||||
uint_t cols = 0;
|
||||
for (uint_t regnum = 0; regnum <= UNW_TDEP_LAST_REG; regnum++) {
|
||||
for (uint_t regnum = 0; regnum <= LAST_REG_INDEX; regnum++) {
|
||||
/*
|
||||
* Get the value. Any error probably means the register
|
||||
* doesn't exist, and we skip it.
|
||||
* doesn't exist, and we skip it. LLVM libunwind iterates over
|
||||
* fp registers in the same list, however they have to be
|
||||
* accessed using unw_get_fpreg instead. Here, we just ignore
|
||||
* them.
|
||||
*/
|
||||
#ifdef IS_LIBUNWIND_LLVM
|
||||
if (unw_is_fpreg(&cp, regnum) ||
|
||||
unw_get_reg(&cp, regnum, &v) < 0)
|
||||
continue;
|
||||
#else
|
||||
if (unw_get_reg(&cp, regnum, &v) < 0)
|
||||
continue;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Register name. If libunwind doesn't have a name for it,
|
||||
* Register name. If GCC libunwind doesn't have a name for it,
|
||||
* it will return "???". As a shortcut, we just treat '?'
|
||||
* is an alternate end-of-string character.
|
||||
* is an alternate end-of-string character. LLVM libunwind will
|
||||
* return the string 'unknown register', which we detect by
|
||||
* checking if the register name is longer than 5 characters.
|
||||
*/
|
||||
#ifdef IS_LIBUNWIND_LLVM
|
||||
const char *name = unw_regname(&cp, regnum);
|
||||
#else
|
||||
const char *name = unw_regname(regnum);
|
||||
#endif
|
||||
for (n = 0; name[n] != '\0' && name[n] != '?'; n++) {}
|
||||
if (n == 0) {
|
||||
if (n == 0 || n > 5) {
|
||||
/*
|
||||
* No valid name, so make one of the form "?xx", where
|
||||
* "xx" is the two-char hex of libunwind's register
|
||||
* number.
|
||||
* No valid name, or likely llvm_libunwind returned
|
||||
* unknown_register, so make one of the form "?xx",
|
||||
* where "xx" is the two-char hex of libunwind's
|
||||
* register number.
|
||||
*/
|
||||
buf[0] = '?';
|
||||
n = spl_bt_u64_to_hex_str(regnum, 2,
|
||||
|
Loading…
Reference in New Issue
Block a user