mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2025-05-23 23:14: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])
|
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
|
AX_RESTORE_FLAGS
|
||||||
], [
|
], [
|
||||||
AS_IF([test "x$with_libunwind" = "xyes"], [
|
AS_IF([test "x$with_libunwind" = "xyes"], [
|
||||||
|
@ -41,9 +41,26 @@
|
|||||||
do { ssize_t r __maybe_unused = write(fd, s, n); } while (0)
|
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)
|
#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
|
#define UNW_LOCAL_ONLY
|
||||||
#include <libunwind.h>
|
#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
|
* 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);
|
unw_init_local(&cp, &uc);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* libunwind's list of possible registers for this architecture is an
|
* Iterate over all registers for the architecture. We've figured
|
||||||
* enum, unw_regnum_t. UNW_TDEP_LAST_REG is the highest-numbered
|
* out the highest number above, however, not all register numbers in
|
||||||
* register in that list, however, not all register numbers in this
|
* this range are defined by the architecture, and not all defined
|
||||||
* range are defined by the architecture, and not all defined registers
|
* registers will be present on every implementation of that
|
||||||
* will be present on every implementation of that architecture.
|
* architecture. Moreover, libunwind provides nice names for most, but
|
||||||
* Moreover, libunwind provides nice names for most, but not all
|
* not all registers, but these are hardcoded; a name being available
|
||||||
* registers, but these are hardcoded; a name being available does not
|
* does not mean that register is available.
|
||||||
* mean that register is available.
|
|
||||||
*
|
*
|
||||||
* So, we have to pull this all together here. We try to get the value
|
* 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
|
* of every possible register. If we get a value for it, then the
|
||||||
@ -120,26 +136,42 @@ libspl_backtrace(int fd)
|
|||||||
* thing.
|
* thing.
|
||||||
*/
|
*/
|
||||||
uint_t cols = 0;
|
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
|
* 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)
|
if (unw_get_reg(&cp, regnum, &v) < 0)
|
||||||
continue;
|
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 '?'
|
* 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);
|
const char *name = unw_regname(regnum);
|
||||||
|
#endif
|
||||||
for (n = 0; name[n] != '\0' && name[n] != '?'; n++) {}
|
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
|
* No valid name, or likely llvm_libunwind returned
|
||||||
* "xx" is the two-char hex of libunwind's register
|
* unknown_register, so make one of the form "?xx",
|
||||||
* number.
|
* where "xx" is the two-char hex of libunwind's
|
||||||
|
* register number.
|
||||||
*/
|
*/
|
||||||
buf[0] = '?';
|
buf[0] = '?';
|
||||||
n = spl_bt_u64_to_hex_str(regnum, 2,
|
n = spl_bt_u64_to_hex_str(regnum, 2,
|
||||||
|
Loading…
Reference in New Issue
Block a user