diff options
author | Peter Bierma <zintensitydev@gmail.com> | 2025-04-21 19:48:02 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-04-21 19:48:02 (GMT) |
commit | 8dfa840773d1d6bae1bf6e0dfa07618ea10c9d71 (patch) | |
tree | 2fd17e8c78b9f50302f335f6e848855cf236d0a7 /Python/traceback.c | |
parent | ea8ec95cfadbf58a11ef8e41341254d982a1a479 (diff) | |
download | cpython-8dfa840773d1d6bae1bf6e0dfa07618ea10c9d71.zip cpython-8dfa840773d1d6bae1bf6e0dfa07618ea10c9d71.tar.gz cpython-8dfa840773d1d6bae1bf6e0dfa07618ea10c9d71.tar.bz2 |
gh-127604: Add C stack dumps to `faulthandler` (#128159)
Diffstat (limited to 'Python/traceback.c')
-rw-r--r-- | Python/traceback.c | 108 |
1 files changed, 108 insertions, 0 deletions
diff --git a/Python/traceback.c b/Python/traceback.c index d30ba58..7319382 100644 --- a/Python/traceback.c +++ b/Python/traceback.c @@ -18,7 +18,25 @@ #ifdef HAVE_UNISTD_H # include <unistd.h> // lseek() #endif +#if defined(HAVE_EXECINFO_H) && defined(HAVE_DLFCN_H) && defined(HAVE_LINK_H) +# include <execinfo.h> // backtrace(), backtrace_symbols() +# include <dlfcn.h> // dladdr1() +# include <link.h> // struct DL_info +# if defined(HAVE_BACKTRACE) && defined(HAVE_BACKTRACE_SYMBOLS) && defined(HAVE_DLADDR1) +# define CAN_C_BACKTRACE +# endif +#endif +#if defined(__STDC_NO_VLA__) && (__STDC_NO_VLA__ == 1) +/* Use alloca() for VLAs. */ +# define VLA(type, name, size) type *name = alloca(size) +#elif !defined(__STDC_NO_VLA__) || (__STDC_NO_VLA__ == 0) +/* Use actual C VLAs.*/ +# define VLA(type, name, size) type name[size] +#elif defined(CAN_C_BACKTRACE) +/* VLAs are not possible. Disable C stack trace functions. */ +# undef CAN_C_BACKTRACE +#endif #define OFF(x) offsetof(PyTracebackObject, x) #define PUTS(fd, str) (void)_Py_write_noraise(fd, str, strlen(str)) @@ -1166,3 +1184,93 @@ _Py_DumpTracebackThreads(int fd, PyInterpreterState *interp, return NULL; } +#ifdef CAN_C_BACKTRACE +/* Based on glibc's implementation of backtrace_symbols(), but only uses stack memory. */ +void +_Py_backtrace_symbols_fd(int fd, void *const *array, Py_ssize_t size) +{ + VLA(Dl_info, info, size); + VLA(int, status, size); + /* Fill in the information we can get from dladdr() */ + for (Py_ssize_t i = 0; i < size; ++i) { + struct link_map *map; + status[i] = dladdr1(array[i], &info[i], (void **)&map, RTLD_DL_LINKMAP); + if (status[i] != 0 + && info[i].dli_fname != NULL + && info[i].dli_fname[0] != '\0') { + /* The load bias is more useful to the user than the load + address. The use of these addresses is to calculate an + address in the ELF file, so its prelinked bias is not + something we want to subtract out */ + info[i].dli_fbase = (void *) map->l_addr; + } + } + for (Py_ssize_t i = 0; i < size; ++i) { + if (status[i] == 0 + || info[i].dli_fname == NULL + || info[i].dli_fname[0] == '\0' + ) { + dprintf(fd, " Binary file '<unknown>' [%p]\n", array[i]); + continue; + } + + if (info[i].dli_sname == NULL) { + /* We found no symbol name to use, so describe it as + relative to the file. */ + info[i].dli_saddr = info[i].dli_fbase; + } + + if (info[i].dli_sname == NULL + && info[i].dli_saddr == 0) { + dprintf(fd, " Binary file \"%s\" [%p]\n", + info[i].dli_fname, + array[i]); + } + else { + char sign; + ptrdiff_t offset; + if (array[i] >= (void *) info[i].dli_saddr) { + sign = '+'; + offset = array[i] - info[i].dli_saddr; + } + else { + sign = '-'; + offset = info[i].dli_saddr - array[i]; + } + const char *symbol_name = info[i].dli_sname != NULL ? info[i].dli_sname : ""; + dprintf(fd, " Binary file \"%s\", at %s%c%#tx [%p]\n", + info[i].dli_fname, + symbol_name, + sign, offset, array[i]); + } + } +} + +void +_Py_DumpStack(int fd) +{ +#define BACKTRACE_SIZE 32 + PUTS(fd, "Current thread's C stack trace (most recent call first):\n"); + VLA(void *, callstack, BACKTRACE_SIZE); + int frames = backtrace(callstack, BACKTRACE_SIZE); + if (frames == 0) { + // Some systems won't return anything for the stack trace + PUTS(fd, " <system returned no stack trace>\n"); + return; + } + + _Py_backtrace_symbols_fd(fd, callstack, frames); + if (frames == BACKTRACE_SIZE) { + PUTS(fd, " <truncated rest of calls>\n"); + } + +#undef BACKTRACE_SIZE +} +#else +void +_Py_DumpStack(int fd) +{ + PUTS(fd, "Current thread's C stack trace (most recent call first):\n"); + PUTS(fd, " <cannot get C stack on this system>\n"); +} +#endif |