gh-127604: Add C stack dumps to faulthandler (#128159)

This commit is contained in:
Peter Bierma 2025-04-21 15:48:02 -04:00 committed by GitHub
parent ea8ec95cfa
commit 8dfa840773
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 378 additions and 69 deletions

View file

@ -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