changeset: 95480:cece1d77d58a tag: tip user: Victor Stinner date: Tue Apr 07 21:48:41 2015 +0200 files: Doc/library/faulthandler.rst Misc/NEWS Modules/faulthandler.c Python/traceback.c description: 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). diff -r 85a5265909cb -r cece1d77d58a Doc/library/faulthandler.rst --- a/Doc/library/faulthandler.rst Tue Apr 07 21:38:36 2015 +0200 +++ b/Doc/library/faulthandler.rst Tue Apr 07 21:48:41 2015 +0200 @@ -68,6 +68,9 @@ Fault handler state .. versionchanged:: 3.5 Added support for passing file descriptor to this function. + .. versionchanged:: 3.5 + On Windows, a handler for Windows exception is also installed. + .. function:: disable() Disable the fault handler: uninstall the signal handlers installed by diff -r 85a5265909cb -r cece1d77d58a Misc/NEWS --- a/Misc/NEWS Tue Apr 07 21:38:36 2015 +0200 +++ b/Misc/NEWS Tue Apr 07 21:48:41 2015 +0200 @@ -19,6 +19,10 @@ Core and Builtins Library ------- +- 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). + - Issue #23411: Added DefragResult, ParseResult, SplitResult, DefragResultBytes, ParseResultBytes, and SplitResultBytes to urllib.parse.__all__. Patch by Martin Panter. diff -r 85a5265909cb -r cece1d77d58a Modules/faulthandler.c --- a/Modules/faulthandler.c Tue Apr 07 21:38:36 2015 +0200 +++ b/Modules/faulthandler.c Tue Apr 07 21:48:41 2015 +0200 @@ -119,7 +119,7 @@ static fault_handler_t faulthandler_hand 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 @@ -288,6 +288,19 @@ faulthandler_dump_traceback_py(PyObject 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. @@ -306,7 +319,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; @@ -324,12 +337,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); @@ -351,6 +359,53 @@ faulthandler_fatal_error(int signum) raise(signum); } +#ifdef MS_WINDOWS +extern void _Py_dump_hexadecimal(int fd, unsigned long value, size_t bytes); + +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_dump_hexadecimal(fd, code, sizeof(DWORD)); + } + PUTS(fd, "\n\n"); + + if (code == EXCEPTION_ACCESS_VIOLATION) { + /* disable signal handler for SIGSEGV */ + fault_handler_t *handler; + size_t i; + for (i=0; i < faulthandler_nsignals; i++) { + 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* @@ -417,7 +472,12 @@ faulthandler_enable(PyObject *self, PyOb } handler->enabled = 1; } + +#ifdef MS_WINDOWS + AddVectoredExceptionHandler(1, faulthandler_exc_handler); +#endif } + Py_RETURN_NONE; } @@ -431,14 +491,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); } } @@ -938,14 +991,13 @@ static PyObject * faulthandler_fatal_error_py(PyObject *self, PyObject *args) { char *message; - if (!PyArg_ParseTuple(args, "y:fatal_error", &message)) + if (!PyArg_ParseTuple(args, "y:_fatal_error", &message)) return NULL; faulthandler_suppress_crash_report(); Py_FatalError(message); Py_RETURN_NONE; } -#if defined(HAVE_SIGALTSTACK) && defined(HAVE_SIGACTION) #ifdef __INTEL_COMPILER /* Issue #23654: Turn off ICC's tail call optimization for the * stack_overflow generator. ICC turns the recursive tail call into @@ -957,14 +1009,15 @@ Py_uintptr_t 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]; + unsigned char buffer[1024*1024]; Py_uintptr_t sp = (Py_uintptr_t)&buffer; *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)); + return stack_overflow(min_sp, max_sp, depth) + buffer[0]; } static PyObject * @@ -972,13 +1025,18 @@ faulthandler_stack_overflow(PyObject *se { size_t depth, size; Py_uintptr_t sp = (Py_uintptr_t)&depth; + Py_uintptr_t min_sp; + Py_uintptr_t max_sp; Py_uintptr_t 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 @@ -989,7 +1047,6 @@ faulthandler_stack_overflow(PyObject *se size, depth); return NULL; } -#endif static int @@ -1012,6 +1069,18 @@ faulthandler_traverse(PyObject *module, 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; + RaiseException(code, flags, 0, NULL); + Py_RETURN_NONE; +} +#endif + PyDoc_STRVAR(module_doc, "faulthandler module."); @@ -1066,9 +1135,11 @@ 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) {"_stack_overflow", (PyCFunction)faulthandler_stack_overflow, METH_NOARGS, PyDoc_STR("_stack_overflow(): recursive call to raise a stack overflow")}, +#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 */ }; @@ -1088,7 +1159,21 @@ 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; + +#ifdef MS_WINDOWS + /* RaiseException() flags */ + 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 diff -r 85a5265909cb -r cece1d77d58a Python/traceback.c --- a/Python/traceback.c Tue Apr 07 21:38:36 2015 +0200 +++ b/Python/traceback.c Tue Apr 07 21:48:41 2015 +0200 @@ -520,10 +520,11 @@ dump_decimal(int fd, int value) This function is signal safe. */ -static void -dump_hexadecimal(int fd, unsigned long value, int width) +void +_Py_dump_hexadecimal(int fd, unsigned long value, size_t bytes) { - int len; + size_t width = bytes * 2; + size_t len; char buffer[sizeof(unsigned long) * 2 + 1]; len = 0; do { @@ -589,15 +590,15 @@ dump_ascii(int fd, PyObject *text) } else if (ch <= 0xff) { PUTS(fd, "\\x"); - dump_hexadecimal(fd, ch, 2); + _Py_dump_hexadecimal(fd, ch, 1); } else if (ch <= 0xffff) { PUTS(fd, "\\u"); - dump_hexadecimal(fd, ch, 4); + _Py_dump_hexadecimal(fd, ch, 2); } else { PUTS(fd, "\\U"); - dump_hexadecimal(fd, ch, 8); + _Py_dump_hexadecimal(fd, ch, 4); } } if (truncated) @@ -692,7 +693,8 @@ write_thread_id(int fd, PyThreadState *t PUTS(fd, "Current thread 0x"); else PUTS(fd, "Thread 0x"); - dump_hexadecimal(fd, (unsigned long)tstate->thread_id, sizeof(unsigned long)*2); + _Py_dump_hexadecimal(fd, (unsigned long)tstate->thread_id, + sizeof(unsigned long)); PUTS(fd, " (most recent call first):\n"); }