-
-
Notifications
You must be signed in to change notification settings - Fork 32k
gh-127604: Add C stack dumps to faulthandler
#128159
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 12 commits
0ebdc69
0ccf9fb
52945e5
9bdd802
66f2641
0591013
64707a2
8a5b784
4f09358
e1aa619
6b515d3
c69ee9d
f08b1dd
dbb6d25
faf1a3e
ec832aa
524f167
dd08bcb
bda3dcd
a3564a5
f3fcea1
db97dd2
c17457f
d5f7d4b
e79e661
0c84f8a
8198997
0f670f0
079f186
892a085
896abd1
cab079e
1784071
3304e2a
aa97f24
50b4964
533b1db
58b3580
f62dac8
4feaf09
c95369f
95833e7
3e2701d
56127fa
b70bd43
7a070ab
e9c3d7c
195a539
9dd6c3b
ce9c39f
c344ad7
e899792
b136f71
2c381b9
52c0748
bd47026
07a20d0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
Add support for printing the C stack trace on systems that support it via | ||
:func:`faulthandler.dump_c_stack` or via the *c_stack* argument in | ||
:func:`faulthandler.c_stack`. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,6 +10,10 @@ | |
#ifdef HAVE_UNISTD_H | ||
# include <unistd.h> // _exit() | ||
#endif | ||
#ifdef HAVE_EXECINFO_H | ||
# include <execinfo.h> // backtrace(), backtrace_symbols() | ||
#endif | ||
|
||
#include <signal.h> // sigaction() | ||
#include <stdlib.h> // abort() | ||
#if defined(HAVE_PTHREAD_SIGMASK) && !defined(HAVE_BROKEN_PTHREAD_SIGMASK) && defined(HAVE_PTHREAD_H) | ||
|
@@ -212,6 +216,81 @@ faulthandler_dump_traceback(int fd, int all_threads, | |
reentrant = 0; | ||
} | ||
|
||
#ifdef HAVE_EXECINFO_H | ||
static void | ||
faulthandler_stack_dump_impl(int fd) | ||
{ | ||
#define SIZE 128 | ||
#define ENTRY_SIZE 256 | ||
ZeroIntensity marked this conversation as resolved.
Show resolved
Hide resolved
|
||
void *callstack[SIZE]; | ||
int frames = backtrace(callstack, SIZE); | ||
char **strings = backtrace_symbols(callstack, SIZE); | ||
if (strings == NULL) | ||
{ | ||
ZeroIntensity marked this conversation as resolved.
Show resolved
Hide resolved
|
||
PUTS(fd, " <failed to get stack trace>"); | ||
} | ||
else { | ||
for (int i = 0; i < frames; ++i) | ||
{ | ||
char entry_str[ENTRY_SIZE]; | ||
ZeroIntensity marked this conversation as resolved.
Show resolved
Hide resolved
|
||
snprintf(entry_str, ENTRY_SIZE, " %s\n", strings[i]); | ||
gpshead marked this conversation as resolved.
Show resolved
Hide resolved
|
||
size_t length = strlen(entry_str) + 1; | ||
if (length == ENTRY_SIZE) | ||
{ | ||
ZeroIntensity marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/* We exceeded the size, make it look prettier */ | ||
|
||
// Add ellipsis to last 3 characters (but before the newline and null terminator) | ||
for (int x = 0; x < 3; ++x) | ||
entry_str[ENTRY_SIZE - (x + 3)] = '.'; | ||
ZeroIntensity marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
entry_str[ENTRY_SIZE - 2] = '\n'; | ||
entry_str[ENTRY_SIZE - 1] = '\0'; | ||
} | ||
_Py_write_noraise(fd, entry_str, length); | ||
} | ||
|
||
if (frames == SIZE) | ||
{ | ||
ZeroIntensity marked this conversation as resolved.
Show resolved
Hide resolved
|
||
PUTS(fd, " <truncated rest of calls>"); | ||
} | ||
} | ||
#undef ENTRY_SIZE | ||
#undef SIZE | ||
} | ||
#else | ||
static void | ||
faulthandler_stack_dump_impl(int fd) | ||
{ | ||
PUTS(fd, " <cannot get C stack on this system>\n"); | ||
} | ||
ZeroIntensity marked this conversation as resolved.
Show resolved
Hide resolved
|
||
#endif | ||
|
||
static void | ||
faulthandler_dump_c_stack_nocheck(int fd) | ||
{ | ||
PUTS(fd, "Current thread's C stack trace (most recent call first):\n"); | ||
faulthandler_stack_dump_impl(fd); | ||
} | ||
|
||
static void | ||
faulthandler_dump_c_stack(int fd) | ||
{ | ||
static volatile int reentrant = 0; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe it is better to use atomics here? AFAIU volatile have different behavior for msvc for different platforms. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could be, but regardless, that should be done in a different PR. |
||
|
||
if (reentrant) | ||
ZeroIntensity marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return; | ||
ZeroIntensity marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
reentrant = 1; | ||
|
||
if (fatal_error.c_stack) | ||
{ | ||
PUTS(fd, "\n"); | ||
ZeroIntensity marked this conversation as resolved.
Show resolved
Hide resolved
|
||
faulthandler_dump_c_stack_nocheck(fd); | ||
} | ||
|
||
reentrant = 0; | ||
} | ||
|
||
static PyObject* | ||
faulthandler_dump_traceback_py(PyObject *self, | ||
PyObject *args, PyObject *kwargs) | ||
|
@@ -253,6 +332,32 @@ faulthandler_dump_traceback_py(PyObject *self, | |
Py_RETURN_NONE; | ||
} | ||
|
||
static PyObject * | ||
faulthandler_dump_c_stack_py(PyObject *self, | ||
picnixz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
PyObject *args, PyObject *kwargs) | ||
{ | ||
static char *kwlist[] = {"file", NULL}; | ||
PyObject *file = NULL; | ||
ZeroIntensity marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
if (!PyArg_ParseTupleAndKeywords(args, kwargs, | ||
"|p:dump_c_stack", kwlist, | ||
&file)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. PEP 7: add braces There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this got brought up before. The rest of the file avoids the braces, it's probably better to just stay consistent. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I respectfully disagree, we are adding new functions so it's better to go closer to the standard that deviate more from it There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As the author of the original code, I would prefer that new code respects PEP 7 (add braces) :-) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will do. cc @picnixz if you want to bicker about styling (I'm pretty sure you were the one that originally brought this up). |
||
return NULL; | ||
ZeroIntensity marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
int fd = faulthandler_get_fileno(&file); | ||
if (fd < 0) { | ||
return NULL; | ||
} | ||
|
||
faulthandler_dump_c_stack_nocheck(fd); | ||
|
||
if (PyErr_CheckSignals()) { | ||
return NULL; | ||
} | ||
|
||
Py_RETURN_NONE; | ||
} | ||
|
||
static void | ||
faulthandler_disable_fatal_handler(fault_handler_t *handler) | ||
{ | ||
|
@@ -322,6 +427,7 @@ faulthandler_fatal_error(int signum) | |
|
||
faulthandler_dump_traceback(fd, fatal_error.all_threads, | ||
fatal_error.interp); | ||
faulthandler_dump_c_stack(fd); | ||
|
||
_Py_DumpExtensionModules(fd, fatal_error.interp); | ||
|
||
|
@@ -398,6 +504,7 @@ faulthandler_exc_handler(struct _EXCEPTION_POINTERS *exc_info) | |
|
||
faulthandler_dump_traceback(fd, fatal_error.all_threads, | ||
fatal_error.interp); | ||
faulthandler_dump_c_stack(fd); | ||
|
||
/* call the next exception handler */ | ||
return EXCEPTION_CONTINUE_SEARCH; | ||
|
@@ -492,14 +599,15 @@ faulthandler_enable(void) | |
static PyObject* | ||
faulthandler_py_enable(PyObject *self, PyObject *args, PyObject *kwargs) | ||
{ | ||
static char *kwlist[] = {"file", "all_threads", NULL}; | ||
static char *kwlist[] = {"file", "all_threads", "c_stack", NULL}; | ||
PyObject *file = NULL; | ||
int all_threads = 1; | ||
int fd; | ||
int c_stack = 1; | ||
ZeroIntensity marked this conversation as resolved.
Show resolved
Hide resolved
|
||
PyThreadState *tstate; | ||
|
||
if (!PyArg_ParseTupleAndKeywords(args, kwargs, | ||
"|Op:enable", kwlist, &file, &all_threads)) | ||
"|Opp:enable", kwlist, &file, &all_threads, &c_stack)) | ||
return NULL; | ||
|
||
fd = faulthandler_get_fileno(&file); | ||
|
@@ -515,6 +623,7 @@ faulthandler_py_enable(PyObject *self, PyObject *args, PyObject *kwargs) | |
fatal_error.fd = fd; | ||
fatal_error.all_threads = all_threads; | ||
fatal_error.interp = PyThreadState_GetInterpreter(tstate); | ||
fatal_error.c_stack = c_stack; | ||
|
||
if (faulthandler_enable() < 0) { | ||
return NULL; | ||
|
@@ -1205,6 +1314,10 @@ static PyMethodDef module_methods[] = { | |
PyDoc_STR("dump_traceback($module, /, file=sys.stderr, all_threads=True)\n--\n\n" | ||
"Dump the traceback of the current thread, or of all threads " | ||
"if all_threads is True, into file.")}, | ||
{"dump_c_stack", | ||
_PyCFunction_CAST(faulthandler_dump_c_stack_py), METH_VARARGS|METH_KEYWORDS, | ||
PyDoc_STR("dump_c_stack($module, /, file=sys.stderr)\n--\n\n" | ||
"Dump the C stack of the current thread.")}, | ||
{"dump_traceback_later", | ||
_PyCFunction_CAST(faulthandler_dump_traceback_later), METH_VARARGS|METH_KEYWORDS, | ||
PyDoc_STR("dump_traceback_later($module, /, timeout, repeat=False, file=sys.stderr, exit=False)\n--\n\n" | ||
|
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Uh oh!
There was an error while loading. Please reload this page.