summaryrefslogtreecommitdiffstats
path: root/Python/traceback.c
diff options
context:
space:
mode:
authorPeter Bierma <zintensitydev@gmail.com>2025-04-21 19:48:02 (GMT)
committerGitHub <noreply@github.com>2025-04-21 19:48:02 (GMT)
commit8dfa840773d1d6bae1bf6e0dfa07618ea10c9d71 (patch)
tree2fd17e8c78b9f50302f335f6e848855cf236d0a7 /Python/traceback.c
parentea8ec95cfadbf58a11ef8e41341254d982a1a479 (diff)
downloadcpython-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.c108
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