1aca928a5SMartin Matuska /* 2aca928a5SMartin Matuska * CDDL HEADER START 3aca928a5SMartin Matuska * 4aca928a5SMartin Matuska * The contents of this file are subject to the terms of the 5aca928a5SMartin Matuska * Common Development and Distribution License (the "License"). 6aca928a5SMartin Matuska * You may not use this file except in compliance with the License. 7aca928a5SMartin Matuska * 8aca928a5SMartin Matuska * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9aca928a5SMartin Matuska * or https://opensource.org/licenses/CDDL-1.0. 10aca928a5SMartin Matuska * See the License for the specific language governing permissions 11aca928a5SMartin Matuska * and limitations under the License. 12aca928a5SMartin Matuska * 13aca928a5SMartin Matuska * When distributing Covered Code, include this CDDL HEADER in each 14aca928a5SMartin Matuska * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15aca928a5SMartin Matuska * If applicable, add the following below this CDDL HEADER, with the 16aca928a5SMartin Matuska * fields enclosed by brackets "[]" replaced with your own identifying 17aca928a5SMartin Matuska * information: Portions Copyright [yyyy] [name of copyright owner] 18aca928a5SMartin Matuska * 19aca928a5SMartin Matuska * CDDL HEADER END 20aca928a5SMartin Matuska */ 21aca928a5SMartin Matuska /* 22aca928a5SMartin Matuska * Copyright (c) 2024, Rob Norris <robn@despairlabs.com> 23aca928a5SMartin Matuska * Copyright (c) 2024, Klara Inc. 24aca928a5SMartin Matuska */ 25aca928a5SMartin Matuska 26aca928a5SMartin Matuska #include <sys/backtrace.h> 27aca928a5SMartin Matuska #include <sys/types.h> 2887bf66d4SMartin Matuska #include <sys/debug.h> 29aca928a5SMartin Matuska #include <unistd.h> 30aca928a5SMartin Matuska 31aca928a5SMartin Matuska /* 3287bf66d4SMartin Matuska * Output helpers. libspl_backtrace() must not block, must be thread-safe and 3387bf66d4SMartin Matuska * must be safe to call from a signal handler. At least, that means not having 3487bf66d4SMartin Matuska * printf, so we end up having to call write() directly on the fd. That's 3587bf66d4SMartin Matuska * awkward, as we always have to pass through a length, and some systems will 3687bf66d4SMartin Matuska * complain if we don't consume the return. So we have some macros to make 3787bf66d4SMartin Matuska * things a little more palatable. 38aca928a5SMartin Matuska */ 3987bf66d4SMartin Matuska #define spl_bt_write_n(fd, s, n) \ 4087bf66d4SMartin Matuska do { ssize_t r __maybe_unused = write(fd, s, n); } while (0) 41*dd215568SMartin Matuska #define spl_bt_write(fd, s) spl_bt_write_n(fd, s, sizeof (s)-1) 42aca928a5SMartin Matuska 43aca928a5SMartin Matuska #if defined(HAVE_LIBUNWIND) 44aca928a5SMartin Matuska #define UNW_LOCAL_ONLY 45aca928a5SMartin Matuska #include <libunwind.h> 46aca928a5SMartin Matuska 4787bf66d4SMartin Matuska /* 4887bf66d4SMartin Matuska * Convert `v` to ASCII hex characters. The bottom `n` nybbles (4-bits ie one 4987bf66d4SMartin Matuska * hex digit) will be written, up to `buflen`. The buffer will not be 5087bf66d4SMartin Matuska * null-terminated. Returns the number of digits written. 5187bf66d4SMartin Matuska */ 52aca928a5SMartin Matuska static size_t 5387bf66d4SMartin Matuska spl_bt_u64_to_hex_str(uint64_t v, size_t n, char *buf, size_t buflen) 54aca928a5SMartin Matuska { 55aca928a5SMartin Matuska static const char hexdigits[] = { 56aca928a5SMartin Matuska '0', '1', '2', '3', '4', '5', '6', '7', 57aca928a5SMartin Matuska '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' 58aca928a5SMartin Matuska }; 59aca928a5SMartin Matuska 60aca928a5SMartin Matuska size_t pos = 0; 6187bf66d4SMartin Matuska boolean_t want = (n == 0); 62aca928a5SMartin Matuska for (int i = 15; i >= 0; i--) { 63aca928a5SMartin Matuska const uint64_t d = v >> (i * 4) & 0xf; 6487bf66d4SMartin Matuska if (!want && (d != 0 || n > i)) 65aca928a5SMartin Matuska want = B_TRUE; 66aca928a5SMartin Matuska if (want) { 67aca928a5SMartin Matuska buf[pos++] = hexdigits[d]; 68aca928a5SMartin Matuska if (pos == buflen) 69aca928a5SMartin Matuska break; 70aca928a5SMartin Matuska } 71aca928a5SMartin Matuska } 72aca928a5SMartin Matuska return (pos); 73aca928a5SMartin Matuska } 74aca928a5SMartin Matuska 75aca928a5SMartin Matuska void 76aca928a5SMartin Matuska libspl_backtrace(int fd) 77aca928a5SMartin Matuska { 78aca928a5SMartin Matuska unw_context_t uc; 79aca928a5SMartin Matuska unw_cursor_t cp; 8087bf66d4SMartin Matuska unw_word_t v; 81aca928a5SMartin Matuska char buf[128]; 82aca928a5SMartin Matuska size_t n; 8387bf66d4SMartin Matuska int err; 84aca928a5SMartin Matuska 8587bf66d4SMartin Matuska /* Snapshot the current frame and state. */ 86aca928a5SMartin Matuska unw_getcontext(&uc); 8787bf66d4SMartin Matuska 8887bf66d4SMartin Matuska /* 8987bf66d4SMartin Matuska * TODO: walk back to the frame that tripped the assertion / the place 9087bf66d4SMartin Matuska * where the signal was recieved. 9187bf66d4SMartin Matuska */ 9287bf66d4SMartin Matuska 9387bf66d4SMartin Matuska /* 9487bf66d4SMartin Matuska * Register dump. We're going to loop over all the registers in the 9587bf66d4SMartin Matuska * top frame, and show them, with names, in a nice three-column 9687bf66d4SMartin Matuska * layout, which keeps us within 80 columns. 9787bf66d4SMartin Matuska */ 9887bf66d4SMartin Matuska spl_bt_write(fd, "Registers:\n"); 9987bf66d4SMartin Matuska 10087bf66d4SMartin Matuska /* Initialise a frame cursor, starting at the current frame */ 101aca928a5SMartin Matuska unw_init_local(&cp, &uc); 10287bf66d4SMartin Matuska 10387bf66d4SMartin Matuska /* 10487bf66d4SMartin Matuska * libunwind's list of possible registers for this architecture is an 10587bf66d4SMartin Matuska * enum, unw_regnum_t. UNW_TDEP_LAST_REG is the highest-numbered 10687bf66d4SMartin Matuska * register in that list, however, not all register numbers in this 10787bf66d4SMartin Matuska * range are defined by the architecture, and not all defined registers 10887bf66d4SMartin Matuska * will be present on every implementation of that architecture. 10987bf66d4SMartin Matuska * Moreover, libunwind provides nice names for most, but not all 11087bf66d4SMartin Matuska * registers, but these are hardcoded; a name being available does not 11187bf66d4SMartin Matuska * mean that register is available. 11287bf66d4SMartin Matuska * 11387bf66d4SMartin Matuska * So, we have to pull this all together here. We try to get the value 11487bf66d4SMartin Matuska * of every possible register. If we get a value for it, then the 11587bf66d4SMartin Matuska * register must exist, and so we get its name. If libunwind has no 11687bf66d4SMartin Matuska * name for it, we synthesize something. These cases should be rare, 11787bf66d4SMartin Matuska * and they're usually for uninteresting or niche registers, so it 11887bf66d4SMartin Matuska * shouldn't really matter. We can see the value, and that's the main 11987bf66d4SMartin Matuska * thing. 12087bf66d4SMartin Matuska */ 12187bf66d4SMartin Matuska uint_t cols = 0; 12287bf66d4SMartin Matuska for (uint_t regnum = 0; regnum <= UNW_TDEP_LAST_REG; regnum++) { 12387bf66d4SMartin Matuska /* 12487bf66d4SMartin Matuska * Get the value. Any error probably means the register 12587bf66d4SMartin Matuska * doesn't exist, and we skip it. 12687bf66d4SMartin Matuska */ 12787bf66d4SMartin Matuska if (unw_get_reg(&cp, regnum, &v) < 0) 12887bf66d4SMartin Matuska continue; 12987bf66d4SMartin Matuska 13087bf66d4SMartin Matuska /* 13187bf66d4SMartin Matuska * Register name. If libunwind doesn't have a name for it, 13287bf66d4SMartin Matuska * it will return "???". As a shortcut, we just treat '?' 13387bf66d4SMartin Matuska * is an alternate end-of-string character. 13487bf66d4SMartin Matuska */ 13587bf66d4SMartin Matuska const char *name = unw_regname(regnum); 13687bf66d4SMartin Matuska for (n = 0; name[n] != '\0' && name[n] != '?'; n++) {} 13787bf66d4SMartin Matuska if (n == 0) { 13887bf66d4SMartin Matuska /* 13987bf66d4SMartin Matuska * No valid name, so make one of the form "?xx", where 14087bf66d4SMartin Matuska * "xx" is the two-char hex of libunwind's register 14187bf66d4SMartin Matuska * number. 14287bf66d4SMartin Matuska */ 14387bf66d4SMartin Matuska buf[0] = '?'; 14487bf66d4SMartin Matuska n = spl_bt_u64_to_hex_str(regnum, 2, 14587bf66d4SMartin Matuska &buf[1], sizeof (buf)-1) + 1; 14687bf66d4SMartin Matuska name = buf; 147aca928a5SMartin Matuska } 14887bf66d4SMartin Matuska 14987bf66d4SMartin Matuska /* 15087bf66d4SMartin Matuska * Two spaces of padding before each column, plus extra 15187bf66d4SMartin Matuska * spaces to align register names shorter than three chars. 15287bf66d4SMartin Matuska */ 15387bf66d4SMartin Matuska spl_bt_write_n(fd, " ", 5-MIN(n, 3)); 15487bf66d4SMartin Matuska 15587bf66d4SMartin Matuska /* Register name and column punctuation */ 15687bf66d4SMartin Matuska spl_bt_write_n(fd, name, n); 15787bf66d4SMartin Matuska spl_bt_write(fd, ": 0x"); 15887bf66d4SMartin Matuska 15987bf66d4SMartin Matuska /* 16087bf66d4SMartin Matuska * Convert register value (from unw_get_reg()) to hex. We're 16187bf66d4SMartin Matuska * assuming that all registers are 64-bits wide, which is 16287bf66d4SMartin Matuska * probably fine for any general-purpose registers on any 16387bf66d4SMartin Matuska * machine currently in use. A more generic way would be to 16487bf66d4SMartin Matuska * look at the width of unw_word_t, but that would also 16587bf66d4SMartin Matuska * complicate the column code a bit. This is fine. 16687bf66d4SMartin Matuska */ 16787bf66d4SMartin Matuska n = spl_bt_u64_to_hex_str(v, 16, buf, sizeof (buf)); 16887bf66d4SMartin Matuska spl_bt_write_n(fd, buf, n); 16987bf66d4SMartin Matuska 17087bf66d4SMartin Matuska /* Every third column, emit a newline */ 17187bf66d4SMartin Matuska if (!(++cols % 3)) 17287bf66d4SMartin Matuska spl_bt_write(fd, "\n"); 17387bf66d4SMartin Matuska } 17487bf66d4SMartin Matuska 17587bf66d4SMartin Matuska /* If we finished before the third column, emit a newline. */ 17687bf66d4SMartin Matuska if (cols % 3) 17787bf66d4SMartin Matuska spl_bt_write(fd, "\n"); 17887bf66d4SMartin Matuska 17987bf66d4SMartin Matuska /* Now the main event, the backtrace. */ 18087bf66d4SMartin Matuska spl_bt_write(fd, "Call trace:\n"); 18187bf66d4SMartin Matuska 18287bf66d4SMartin Matuska /* Reset the cursor to the top again. */ 18387bf66d4SMartin Matuska unw_init_local(&cp, &uc); 18487bf66d4SMartin Matuska 18587bf66d4SMartin Matuska do { 18687bf66d4SMartin Matuska /* 18787bf66d4SMartin Matuska * Getting the IP should never fail; libunwind handles it 18887bf66d4SMartin Matuska * specially, because its used a lot internally. Still, no 18987bf66d4SMartin Matuska * point being silly about it, as the last thing we want is 19087bf66d4SMartin Matuska * our crash handler to crash. So if it ever does fail, we'll 19187bf66d4SMartin Matuska * show an error line, but keep going to the next frame. 19287bf66d4SMartin Matuska */ 19387bf66d4SMartin Matuska if (unw_get_reg(&cp, UNW_REG_IP, &v) < 0) { 19487bf66d4SMartin Matuska spl_bt_write(fd, " [couldn't get IP register; " 19587bf66d4SMartin Matuska "corrupt frame?]"); 19687bf66d4SMartin Matuska continue; 19787bf66d4SMartin Matuska } 19887bf66d4SMartin Matuska 19987bf66d4SMartin Matuska /* IP & punctuation */ 20087bf66d4SMartin Matuska n = spl_bt_u64_to_hex_str(v, 16, buf, sizeof (buf)); 20187bf66d4SMartin Matuska spl_bt_write(fd, " [0x"); 20287bf66d4SMartin Matuska spl_bt_write_n(fd, buf, n); 20387bf66d4SMartin Matuska spl_bt_write(fd, "] "); 20487bf66d4SMartin Matuska 20587bf66d4SMartin Matuska /* 20687bf66d4SMartin Matuska * Function ("procedure") name for the current frame. `v` 20787bf66d4SMartin Matuska * receives the offset from the named function to the IP, which 20887bf66d4SMartin Matuska * we show as a "+offset" suffix. 20987bf66d4SMartin Matuska * 21087bf66d4SMartin Matuska * If libunwind can't determine the name, we just show "???" 21187bf66d4SMartin Matuska * instead. We've already displayed the IP above; that will 21287bf66d4SMartin Matuska * have to do. 21387bf66d4SMartin Matuska * 21487bf66d4SMartin Matuska * unw_get_proc_name() will return ENOMEM if the buffer is too 21587bf66d4SMartin Matuska * small, instead truncating the name. So we treat that as a 21687bf66d4SMartin Matuska * success and use whatever is in the buffer. 21787bf66d4SMartin Matuska */ 21887bf66d4SMartin Matuska err = unw_get_proc_name(&cp, buf, sizeof (buf), &v); 21987bf66d4SMartin Matuska if (err == 0 || err == -UNW_ENOMEM) { 22087bf66d4SMartin Matuska for (n = 0; n < sizeof (buf) && buf[n] != '\0'; n++) {} 22187bf66d4SMartin Matuska spl_bt_write_n(fd, buf, n); 22287bf66d4SMartin Matuska 22387bf66d4SMartin Matuska /* Offset from proc name */ 22487bf66d4SMartin Matuska spl_bt_write(fd, "+0x"); 22587bf66d4SMartin Matuska n = spl_bt_u64_to_hex_str(v, 2, buf, sizeof (buf)); 22687bf66d4SMartin Matuska spl_bt_write_n(fd, buf, n); 22787bf66d4SMartin Matuska } else 22887bf66d4SMartin Matuska spl_bt_write(fd, "???"); 22987bf66d4SMartin Matuska 23087bf66d4SMartin Matuska #ifdef HAVE_LIBUNWIND_ELF 23187bf66d4SMartin Matuska /* 23287bf66d4SMartin Matuska * Newer libunwind has unw_get_elf_filename(), which gets 23387bf66d4SMartin Matuska * the name of the ELF object that the frame was executing in. 23487bf66d4SMartin Matuska * Like `unw_get_proc_name()`, `v` recieves the offset within 23587bf66d4SMartin Matuska * the file, and UNW_ENOMEM indicates that a truncate filename 23687bf66d4SMartin Matuska * was left in the buffer. 23787bf66d4SMartin Matuska */ 23887bf66d4SMartin Matuska err = unw_get_elf_filename(&cp, buf, sizeof (buf), &v); 23987bf66d4SMartin Matuska if (err == 0 || err == -UNW_ENOMEM) { 24087bf66d4SMartin Matuska for (n = 0; n < sizeof (buf) && buf[n] != '\0'; n++) {} 24187bf66d4SMartin Matuska spl_bt_write(fd, " (in "); 24287bf66d4SMartin Matuska spl_bt_write_n(fd, buf, n); 24387bf66d4SMartin Matuska 24487bf66d4SMartin Matuska /* Offset within file */ 24587bf66d4SMartin Matuska spl_bt_write(fd, " +0x"); 24687bf66d4SMartin Matuska n = spl_bt_u64_to_hex_str(v, 2, buf, sizeof (buf)); 24787bf66d4SMartin Matuska spl_bt_write_n(fd, buf, n); 24887bf66d4SMartin Matuska spl_bt_write(fd, ")"); 24987bf66d4SMartin Matuska } 25087bf66d4SMartin Matuska #endif 25187bf66d4SMartin Matuska spl_bt_write(fd, "\n"); 25287bf66d4SMartin Matuska } while (unw_step(&cp) > 0); 253aca928a5SMartin Matuska } 254aca928a5SMartin Matuska #elif defined(HAVE_BACKTRACE) 255aca928a5SMartin Matuska #include <execinfo.h> 256aca928a5SMartin Matuska 257aca928a5SMartin Matuska void 258aca928a5SMartin Matuska libspl_backtrace(int fd) 259aca928a5SMartin Matuska { 260aca928a5SMartin Matuska void *btptrs[64]; 261aca928a5SMartin Matuska size_t nptrs = backtrace(btptrs, 64); 26287bf66d4SMartin Matuska spl_bt_write(fd, "Call trace:\n"); 263aca928a5SMartin Matuska backtrace_symbols_fd(btptrs, nptrs, fd); 264aca928a5SMartin Matuska } 265aca928a5SMartin Matuska #else 266aca928a5SMartin Matuska void 267aca928a5SMartin Matuska libspl_backtrace(int fd __maybe_unused) 268aca928a5SMartin Matuska { 269aca928a5SMartin Matuska } 270aca928a5SMartin Matuska #endif 271