diff options
author | Victor Stinner <victor.stinner@gmail.com> | 2016-03-23 09:39:17 (GMT) |
---|---|---|
committer | Victor Stinner <victor.stinner@gmail.com> | 2016-03-23 09:39:17 (GMT) |
commit | 404cdc5a924a294a17c04b745df5549c32e08130 (patch) | |
tree | 4a795a02e514252d0fb796dfa09ab810275f2f33 /Modules | |
parent | bd31b7c48317eecf981215afbb1f30c81769acbf (diff) | |
download | cpython-404cdc5a924a294a17c04b745df5549c32e08130.zip cpython-404cdc5a924a294a17c04b745df5549c32e08130.tar.gz cpython-404cdc5a924a294a17c04b745df5549c32e08130.tar.bz2 |
faulthandler: add Windows exception handler
Issue #23848: On Windows, faulthandler.enable() now also installs an exception
handler to dump the traceback of all Python threads on any Windows exception,
not only on UNIX signals (SIGSEGV, SIGFPE, SIGABRT).
Diffstat (limited to 'Modules')
-rw-r--r-- | Modules/faulthandler.c | 253 |
1 files changed, 189 insertions, 64 deletions
diff --git a/Modules/faulthandler.c b/Modules/faulthandler.c index 1c247b7..2214466 100644 --- a/Modules/faulthandler.c +++ b/Modules/faulthandler.c @@ -119,7 +119,7 @@ static fault_handler_t faulthandler_handlers[] = { handler fails in faulthandler_fatal_error() */ {SIGSEGV, 0, "Segmentation fault", } }; -static const unsigned char faulthandler_nsignals = \ +static const size_t faulthandler_nsignals = \ Py_ARRAY_LENGTH(faulthandler_handlers); #ifdef HAVE_SIGALTSTACK @@ -290,6 +290,19 @@ faulthandler_dump_traceback_py(PyObject *self, Py_RETURN_NONE; } +static void +faulthandler_disable_fatal_handler(fault_handler_t *handler) +{ + if (!handler->enabled) + return; + handler->enabled = 0; +#ifdef HAVE_SIGACTION + (void)sigaction(handler->signum, &handler->previous, NULL); +#else + (void)signal(handler->signum, handler->previous); +#endif +} + /* Handler for SIGSEGV, SIGFPE, SIGABRT, SIGBUS and SIGILL signals. @@ -308,7 +321,7 @@ static void faulthandler_fatal_error(int signum) { const int fd = fatal_error.fd; - unsigned int i; + size_t i; fault_handler_t *handler = NULL; int save_errno = errno; @@ -326,12 +339,7 @@ faulthandler_fatal_error(int signum) } /* restore the previous handler */ -#ifdef HAVE_SIGACTION - (void)sigaction(signum, &handler->previous, NULL); -#else - (void)signal(signum, handler->previous); -#endif - handler->enabled = 0; + faulthandler_disable_fatal_handler(handler); PUTS(fd, "Fatal Python error: "); PUTS(fd, handler->name); @@ -353,20 +361,110 @@ faulthandler_fatal_error(int signum) raise(signum); } +#ifdef MS_WINDOWS +static LONG WINAPI +faulthandler_exc_handler(struct _EXCEPTION_POINTERS *exc_info) +{ + const int fd = fatal_error.fd; + DWORD code = exc_info->ExceptionRecord->ExceptionCode; + + PUTS(fd, "Windows exception: "); + switch (code) + { + /* only format most common errors */ + case EXCEPTION_ACCESS_VIOLATION: PUTS(fd, "access violation"); break; + case EXCEPTION_FLT_DIVIDE_BY_ZERO: PUTS(fd, "float divide by zero"); break; + case EXCEPTION_FLT_OVERFLOW: PUTS(fd, "float overflow"); break; + case EXCEPTION_INT_DIVIDE_BY_ZERO: PUTS(fd, "int divide by zero"); break; + case EXCEPTION_INT_OVERFLOW: PUTS(fd, "integer overflow"); break; + case EXCEPTION_IN_PAGE_ERROR: PUTS(fd, "page error"); break; + case EXCEPTION_STACK_OVERFLOW: PUTS(fd, "stack overflow"); break; + default: + PUTS(fd, "code 0x"); + _Py_DumpHexadecimal(fd, code, sizeof(DWORD)); + } + PUTS(fd, "\n\n"); + + if (code == EXCEPTION_ACCESS_VIOLATION) { + /* disable signal handler for SIGSEGV */ + size_t i; + for (i=0; i < faulthandler_nsignals; i++) { + fault_handler_t *handler = &faulthandler_handlers[i]; + if (handler->signum == SIGSEGV) { + faulthandler_disable_fatal_handler(handler); + break; + } + } + } + + faulthandler_dump_traceback(fd, fatal_error.all_threads, + fatal_error.interp); + + /* call the next exception handler */ + return EXCEPTION_CONTINUE_SEARCH; +} +#endif + /* Install the handler for fatal signals, faulthandler_fatal_error(). */ -static PyObject* -faulthandler_enable(PyObject *self, PyObject *args, PyObject *kwargs) +int +faulthandler_enable(void) { - static char *kwlist[] = {"file", "all_threads", NULL}; - PyObject *file = NULL; - int all_threads = 1; - unsigned int i; + size_t i; fault_handler_t *handler; #ifdef HAVE_SIGACTION struct sigaction action; #endif int err; + + if (fatal_error.enabled) { + return 0; + } + + fatal_error.enabled = 1; + + for (i=0; i < faulthandler_nsignals; i++) { + handler = &faulthandler_handlers[i]; + +#ifdef HAVE_SIGACTION + action.sa_handler = faulthandler_fatal_error; + sigemptyset(&action.sa_mask); + /* Do not prevent the signal from being received from within + its own signal handler */ + action.sa_flags = SA_NODEFER; +#ifdef HAVE_SIGALTSTACK + if (stack.ss_sp != NULL) { + /* Call the signal handler on an alternate signal stack + provided by sigaltstack() */ + action.sa_flags |= SA_ONSTACK; + } +#endif + err = sigaction(handler->signum, &action, &handler->previous); +#else + handler->previous = signal(handler->signum, + faulthandler_fatal_error); + err = (handler->previous == SIG_ERR); +#endif + if (err) { + PyErr_SetFromErrno(PyExc_RuntimeError); + return -1; + } + + handler->enabled = 1; + } + +#ifdef MS_WINDOWS + AddVectoredExceptionHandler(1, faulthandler_exc_handler); +#endif + return 0; +} + +static PyObject* +faulthandler_py_enable(PyObject *self, PyObject *args, PyObject *kwargs) +{ + static char *kwlist[] = {"file", "all_threads", NULL}; + PyObject *file = NULL; + int all_threads = 1; int fd; PyThreadState *tstate; @@ -388,37 +486,10 @@ faulthandler_enable(PyObject *self, PyObject *args, PyObject *kwargs) fatal_error.all_threads = all_threads; fatal_error.interp = tstate->interp; - if (!fatal_error.enabled) { - fatal_error.enabled = 1; - - for (i=0; i < faulthandler_nsignals; i++) { - handler = &faulthandler_handlers[i]; -#ifdef HAVE_SIGACTION - action.sa_handler = faulthandler_fatal_error; - sigemptyset(&action.sa_mask); - /* Do not prevent the signal from being received from within - its own signal handler */ - action.sa_flags = SA_NODEFER; -#ifdef HAVE_SIGALTSTACK - if (stack.ss_sp != NULL) { - /* Call the signal handler on an alternate signal stack - provided by sigaltstack() */ - action.sa_flags |= SA_ONSTACK; - } -#endif - err = sigaction(handler->signum, &action, &handler->previous); -#else - handler->previous = signal(handler->signum, - faulthandler_fatal_error); - err = (handler->previous == SIG_ERR); -#endif - if (err) { - PyErr_SetFromErrno(PyExc_RuntimeError); - return NULL; - } - handler->enabled = 1; - } + if (faulthandler_enable() < 0) { + return NULL; } + Py_RETURN_NONE; } @@ -432,14 +503,7 @@ faulthandler_disable(void) fatal_error.enabled = 0; for (i=0; i < faulthandler_nsignals; i++) { handler = &faulthandler_handlers[i]; - if (!handler->enabled) - continue; -#ifdef HAVE_SIGACTION - (void)sigaction(handler->signum, &handler->previous, NULL); -#else - (void)signal(handler->signum, handler->previous); -#endif - handler->enabled = 0; + faulthandler_disable_fatal_handler(handler); } } @@ -991,7 +1055,10 @@ faulthandler_fatal_error_py(PyObject *self, PyObject *args) Py_RETURN_NONE; } + #if defined(HAVE_SIGALTSTACK) && defined(HAVE_SIGACTION) +#define FAULTHANDLER_STACK_OVERFLOW + #ifdef __INTEL_COMPILER /* Issue #23654: Turn off ICC's tail call optimization for the * stack_overflow generator. ICC turns the recursive tail call into @@ -1005,12 +1072,21 @@ stack_overflow(Py_uintptr_t min_sp, Py_uintptr_t max_sp, size_t *depth) /* allocate 4096 bytes on the stack at each call */ unsigned char buffer[4096]; Py_uintptr_t sp = (Py_uintptr_t)&buffer; + Py_uintptr_t stop; + *depth += 1; - if (sp < min_sp || max_sp < sp) + if (sp < min_sp || max_sp < sp) { + printf("call #%lu\n", (unsigned long)*depth); return sp; - buffer[0] = 1; - buffer[4095] = 0; - return stack_overflow(min_sp, max_sp, depth); + } + + memset(buffer, (unsigned char)*depth, sizeof(buffer)); + stop = stack_overflow(min_sp, max_sp, depth) + buffer[0]; + + memset(buffer, (unsigned char)stop, sizeof(buffer)); + stop = stack_overflow(min_sp, max_sp, depth) + buffer[0]; + + return stop; } static PyObject * @@ -1018,13 +1094,19 @@ faulthandler_stack_overflow(PyObject *self) { size_t depth, size; Py_uintptr_t sp = (Py_uintptr_t)&depth; - Py_uintptr_t stop; + Py_uintptr_t min_sp, max_sp, stop; faulthandler_suppress_crash_report(); + depth = 0; - stop = stack_overflow(sp - STACK_OVERFLOW_MAX_SIZE, - sp + STACK_OVERFLOW_MAX_SIZE, - &depth); + if (sp > STACK_OVERFLOW_MAX_SIZE) + min_sp = sp - STACK_OVERFLOW_MAX_SIZE; + else + min_sp = 0; + max_sp = sp + STACK_OVERFLOW_MAX_SIZE; + + stop = stack_overflow(min_sp, max_sp, &depth); + if (sp < stop) size = stop - sp; else @@ -1035,7 +1117,7 @@ faulthandler_stack_overflow(PyObject *self) size, depth); return NULL; } -#endif +#endif /* (defined(HAVE_SIGALTSTACK) && defined(HAVE_SIGACTION)) ... */ static int @@ -1058,12 +1140,25 @@ faulthandler_traverse(PyObject *module, visitproc visit, void *arg) return 0; } +#ifdef MS_WINDOWS +static PyObject * +faulthandler_raise_exception(PyObject *self, PyObject *args) +{ + unsigned int code, flags = 0; + if (!PyArg_ParseTuple(args, "I|I:_raise_exception", &code, &flags)) + return NULL; + faulthandler_suppress_crash_report(); + RaiseException(code, flags, 0, NULL); + Py_RETURN_NONE; +} +#endif + PyDoc_STRVAR(module_doc, "faulthandler module."); static PyMethodDef module_methods[] = { {"enable", - (PyCFunction)faulthandler_enable, METH_VARARGS|METH_KEYWORDS, + (PyCFunction)faulthandler_py_enable, METH_VARARGS|METH_KEYWORDS, PyDoc_STR("enable(file=sys.stderr, all_threads=True): " "enable the fault handler")}, {"disable", (PyCFunction)faulthandler_disable_py, METH_NOARGS, @@ -1117,10 +1212,14 @@ static PyMethodDef module_methods[] = { PyDoc_STR("_sigfpe(): raise a SIGFPE signal")}, {"_fatal_error", faulthandler_fatal_error_py, METH_VARARGS, PyDoc_STR("_fatal_error(message): call Py_FatalError(message)")}, -#if defined(HAVE_SIGALTSTACK) && defined(HAVE_SIGACTION) +#ifdef FAULTHANDLER_STACK_OVERFLOW {"_stack_overflow", (PyCFunction)faulthandler_stack_overflow, METH_NOARGS, PyDoc_STR("_stack_overflow(): recursive call to raise a stack overflow")}, #endif +#ifdef MS_WINDOWS + {"_raise_exception", faulthandler_raise_exception, METH_VARARGS, + PyDoc_STR("raise_exception(code, flags=0): Call RaiseException(code, flags).")}, +#endif {NULL, NULL} /* sentinel */ }; @@ -1139,7 +1238,33 @@ static struct PyModuleDef module_def = { PyMODINIT_FUNC PyInit_faulthandler(void) { - return PyModule_Create(&module_def); + PyObject *m = PyModule_Create(&module_def); + if (m == NULL) + return NULL; + + /* Add constants for unit tests */ +#ifdef MS_WINDOWS + /* RaiseException() codes (prefixed by an underscore) */ + if (PyModule_AddIntConstant(m, "_EXCEPTION_ACCESS_VIOLATION", + EXCEPTION_ACCESS_VIOLATION)) + return NULL; + if (PyModule_AddIntConstant(m, "_EXCEPTION_INT_DIVIDE_BY_ZERO", + EXCEPTION_INT_DIVIDE_BY_ZERO)) + return NULL; + if (PyModule_AddIntConstant(m, "_EXCEPTION_STACK_OVERFLOW", + EXCEPTION_STACK_OVERFLOW)) + return NULL; + + /* RaiseException() flags (prefixed by an underscore) */ + if (PyModule_AddIntConstant(m, "_EXCEPTION_NONCONTINUABLE", + EXCEPTION_NONCONTINUABLE)) + return NULL; + if (PyModule_AddIntConstant(m, "_EXCEPTION_NONCONTINUABLE_EXCEPTION", + EXCEPTION_NONCONTINUABLE_EXCEPTION)) + return NULL; +#endif + + return m; } /* Call faulthandler.enable() if the PYTHONFAULTHANDLER environment variable |