Index: Python/pythonrun.c =================================================================== --- Python/pythonrun.c (révision 85426) +++ Python/pythonrun.c (copie de travail) @@ -80,6 +80,10 @@ extern void _PyGILState_Fini(void); #endif /* WITH_THREAD */ +extern void _Py_InitSegfault(void); +extern void _Py_FiniSegfault(void); +extern int _Py_DumpBacktrace(int fd); + int Py_DebugFlag; /* Needed by parser.c */ int Py_VerboseFlag; /* Needed by import.c */ int Py_InteractiveFlag; /* Needed by Py_FdIsInteractive() below */ @@ -397,6 +401,7 @@ interp = tstate->interp; /* Disable signal handling */ + _Py_FiniSegfault(); PyOS_FiniInterrupts(); /* Clear type lookup cache */ @@ -2345,10 +2350,23 @@ void Py_FatalError(const char *msg) { - fprintf(stderr, "Fatal Python error: %s\n", msg); - fflush(stderr); /* it helps in Windows debug build */ - if (PyErr_Occurred()) { + const int fd = 2; /* should be stderr */ + + fputs("Fatal Python error: ", stderr); + fputs(msg, stderr); + fputc('\n', stderr); + fflush(stderr); + + if (PyErr_Occurred()) PyErr_PrintEx(0); + else { + fputc('\n', stderr); + fflush(stderr); + if (!_Py_DumpBacktrace(fd)) { + fputs(msg, stderr); + fputc('\n', stderr); + fflush(stderr); + } } #ifdef MS_WINDOWS { @@ -2467,6 +2485,7 @@ PyOS_setsig(SIGXFSZ, SIG_IGN); #endif PyOS_InitInterrupts(); /* May imply initsignal() */ + _Py_InitSegfault(); } Index: Python/segfault.c =================================================================== --- Python/segfault.c (révision 0) +++ Python/segfault.c (révision 0) @@ -0,0 +1,228 @@ +/* + * Python SIGSEGV and SIGFPE signal handlers: display the Python backtrace and + * abort. Allocate an alternative stack for this handler, if sigaltstack() is + * available, to be able to display the backtrace on stack overflow. + */ + +#include "Python.h" +#include "frameobject.h" +#include "code.h" +#include +#include + +#ifdef MS_WINDOWS +# include "windows.h" +#else +# include +#endif + +#ifdef HAVE_SIGALTSTACK +static stack_t stack; +#endif +static int sigsegv_enable = 0, sigfpe_enable = 0; +#ifdef HAVE_SIGACTION +static struct sigaction old_sigsegv_handler, old_sigfpe_handler; +#else +static PyOS_sighandler_t old_sigsegv_handler, old_sigfpe_handler; +#endif + +#define PUTS(str, fd) write(fd, str, strlen(str)) + +static void +dump_decimal(int value, int fd) +{ + char buffer[7]; + int len; + if (value < 0 || 999999 < value) + return; + len = snprintf(buffer, sizeof(buffer), "%u", value); + if (len < 0) + return; + if (sizeof(buffer) < len) + len = sizeof(buffer); + write(fd, buffer, len); +} + +static void +dump_hexadecimal(const char *format, unsigned int value, int fd) +{ + char buffer[9]; /* largest format is "%08x" */ + int len; + len = snprintf(buffer, sizeof(buffer), format, value); + if (len < 0) + return; + if (sizeof(buffer) < len) + len = sizeof(buffer); + write(fd, buffer, len); +} + +/* Write an unicode object into the file f using ascii+backslashreplace */ + +static void +dump_ascii(PyUnicodeObject *unicode, int fd) +{ + Py_ssize_t i, size; + Py_UNICODE *u; + char c; + + size = PyUnicode_GET_SIZE(unicode); + u = PyUnicode_AS_UNICODE(unicode); + for (i=0; i < size; i++, u++) { + if (*u < 128) { + c = *u; + write(fd, &c, 1); + } + else if (*u < 256) { + PUTS("\\x", fd); + dump_hexadecimal("%02x", *u, fd); + } + else +#ifdef Py_UNICODE_WIDE + if (*u < 65536) +#endif + { + PUTS("\\u", fd); + dump_hexadecimal("%04x", *u, fd); +#ifdef Py_UNICODE_WIDE + } + else { + PUTS("\\U", fd); + dump_hexadecimal("%08x", *u, fd); +#endif + } + } +} + +/* Dump one frame */ + +static void +dump_frame(PyFrameObject *frame, int fd) +{ + PyCodeObject *code; + int lineno; + + code = frame->f_code; + PUTS(" File ", fd); + if (code != NULL && code->co_filename != NULL + && PyUnicode_Check(code->co_filename)) + { + write(fd, "\"", 1); + dump_ascii((PyUnicodeObject*)code->co_filename, fd); + write(fd, "\"", 1); + } else { + PUTS("???", fd); + } + + lineno = PyFrame_GetLineNumber(frame); + PUTS(", line ", fd); + dump_decimal(lineno, fd); + PUTS(" in ", fd); + + if (code != NULL && code->co_name != NULL + && PyUnicode_Check(code->co_name)) + dump_ascii((PyUnicodeObject*)code->co_name, fd); + else + PUTS("???", fd); + + write(fd, "\n", 1); +} + +int +_Py_DumpBacktrace(int fd) +{ + PyThreadState *tstate; + PyFrameObject *frame; + + tstate = (PyThreadState*)_Py_atomic_load_relaxed(&_PyThreadState_Current); + if (tstate == NULL) + return -1; + + frame = _PyThreadState_GetFrame(tstate); + if (frame == NULL) + return -1; + + PUTS("Traceback (most recent call first):\n", fd); + while (frame != NULL) { + dump_frame(frame, fd); + frame = frame->f_back; + } + return 0; +} + +static void +segfault_handler(int signum) +{ + const int fd = 2; /* should be stderr */ + + if (signum == SIGFPE) + PUTS("Fatal Python error: floating point exception\n\n", fd); + else + PUTS("Fatal Python error: segmentation fault\n\n", fd); + + if (!_Py_DumpBacktrace(fd)) { + if (signum == SIGFPE) + PUTS("Floating point exception\n", fd); + else + PUTS("Segmentation fault\n", fd); + } + +#if defined(MS_WINDOWS) && defined(_DEBUG) + DebugBreak(); +#endif + abort(); +} + +void _Py_InitSegfault(void) +{ +#ifdef HAVE_SIGACTION + struct sigaction action; +#endif + +#ifdef HAVE_SIGALTSTACK + stack.ss_size = SIGSTKSZ; + stack.ss_sp = PyMem_Malloc(stack.ss_size); + stack.ss_flags = SS_ONSTACK; + if (stack.ss_sp == NULL) + return; + if (sigaltstack(&stack, NULL) != 0) + return; +#endif + +#ifdef HAVE_SIGACTION + action.sa_handler = segfault_handler; + sigemptyset(&action.sa_mask); + action.sa_flags = SA_ONSTACK; + if (sigaction(SIGSEGV, &action, &old_sigsegv_handler) == 0) + sigsegv_enable = 1; + if (sigaction(SIGFPE, &action, &old_sigfpe_handler) == 0) + sigfpe_enable = 1; +#else + old_sigsegv_handler = signal(SIGSEGV, segfault_handler); + if (old_sigsegv_handler != SIG_ERR) + sigsegv_enable = 1; + old_sigfpe_handler = signal(SIGFPE, segfault_handler); + if (old_sigfpe_handler != SIG_ERR) + sigfpe_enable = 1; +#endif +} + +void _Py_FiniSegfault(void) +{ +#ifdef HAVE_SIGACTION + if (sigsegv_enable) + (void)sigaction(SIGSEGV, &old_sigsegv_handler, NULL); + if (sigfpe_enable) + (void)sigaction(SIGFPE, &old_sigfpe_handler, NULL); +#else + if (sigsegv_enable) + (void)signal(SIGSEGV, old_sigsegv_handler); + if (sigfpe_enable) + (void)signal(SIGFPE, old_sigfpe_handler); +#endif + +#ifdef HAVE_SIGALTSTACK + if (stack.ss_sp != NULL) + PyMem_Free(stack.ss_sp); +#endif +} + Index: configure =================================================================== --- configure (révision 85426) +++ configure (copie de travail) @@ -9312,7 +9312,7 @@ select sem_open sem_timedwait sem_getvalue sem_unlink setegid seteuid \ setgid \ setlocale setregid setreuid setresuid setresgid setsid setpgid setpgrp setuid setvbuf \ - sigaction siginterrupt sigrelse snprintf strftime strlcpy \ + sigaction sigaltstack siginterrupt sigrelse snprintf strftime strlcpy \ sysconf tcgetpgrp tcsetpgrp tempnam timegm times tmpfile tmpnam tmpnam_r \ truncate uname unsetenv utimes waitpid wait3 wait4 \ wcscoll wcsftime wcsxfrm _getpty Index: PCbuild/pythoncore.vcproj =================================================================== --- PCbuild/pythoncore.vcproj (révision 85426) +++ PCbuild/pythoncore.vcproj (copie de travail) @@ -1811,6 +1811,10 @@ > + + Index: configure.in =================================================================== --- configure.in (révision 85426) +++ configure.in (copie de travail) @@ -2560,7 +2560,7 @@ select sem_open sem_timedwait sem_getvalue sem_unlink setegid seteuid \ setgid \ setlocale setregid setreuid setresuid setresgid setsid setpgid setpgrp setuid setvbuf \ - sigaction siginterrupt sigrelse snprintf strftime strlcpy \ + sigaction sigaltstack siginterrupt sigrelse snprintf strftime strlcpy \ sysconf tcgetpgrp tcsetpgrp tempnam timegm times tmpfile tmpnam tmpnam_r \ truncate uname unsetenv utimes waitpid wait3 wait4 \ wcscoll wcsftime wcsxfrm _getpty) Index: Misc/NEWS =================================================================== --- Misc/NEWS (révision 85426) +++ Misc/NEWS (copie de travail) @@ -10,6 +10,9 @@ Core and Builtins ----------------- +- Issue #8863: Create SIGSEGV and SIGFPE signal handler displaying the Python + backtrace. Fatal error displays also the Python backtrace. + Library ------- Index: Lib/test/test_segfault.py =================================================================== --- Lib/test/test_segfault.py (révision 0) +++ Lib/test/test_segfault.py (révision 0) @@ -0,0 +1,40 @@ +import unittest +from test.script_helper import spawn_python +import test.support + +class SegfaultTest(unittest.TestCase): + def check_output(self, code, expected): + process = spawn_python("-c", code) + stdout, stderr = process.communicate() + self.assertEqual(stdout, expected) + + def test_sigsegv(self): + self.check_output( + "import _testcapi; _testcapi.sigsegv()", + b'Fatal Python error: segmentation fault\n\n' + b'Traceback (most recent call first):\n' + b' File "", line 1 in \n' + b'Segmentation fault\n') + + def test_sigfpe(self): + self.check_output( + "import _testcapi; _testcapi.sigfpe()", + b'Fatal Python error: floating point exception\n\n' + b'Traceback (most recent call first):\n' + b' File "", line 1 in \n' + b'Floating point exception\n') + + def test_fatal_error(self): + self.check_output( + "import _testcapi; _testcapi.fatal_error(b'xxx')", + b'Fatal Python error: xxx\n\n' + b'Traceback (most recent call first):\n' + b' File "", line 1 in \n' + b'xxx\n') + + +def test_main(): + test.support.run_unittest(SegfaultTest) + +if __name__ == "__main__": + test_main() Index: Makefile.pre.in =================================================================== --- Makefile.pre.in (révision 85426) +++ Makefile.pre.in (copie de travail) @@ -326,6 +326,7 @@ Python/dtoa.o \ Python/formatter_unicode.o \ Python/fileutils.o \ + Python/segfault.o \ Python/$(DYNLOADFILE) \ $(LIBOBJS) \ $(MACHDEP_OBJS) \ Index: Modules/_testcapimodule.c =================================================================== --- Modules/_testcapimodule.c (révision 85426) +++ Modules/_testcapimodule.c (copie de travail) @@ -2256,6 +2256,32 @@ } +static PyObject * +sigsegv(PyObject *self, PyObject *args) +{ + int *x = NULL; + return PyLong_FromLong(*x); + +} + +static PyObject * +sigfpe(PyObject *self, PyObject *args) +{ + int x, y; + x = 1; + y = 0; + return PyLong_FromLong(x / y); +} + +static PyObject * +fatal_error(PyObject *self, PyObject *args) +{ + char *message; + if (!PyArg_ParseTuple(args, "y", &message)) + return NULL; + Py_FatalError(message); +} + static PyMethodDef TestMethods[] = { {"raise_exception", raise_exception, METH_VARARGS}, {"raise_memoryerror", (PyCFunction)raise_memoryerror, METH_NOARGS}, @@ -2338,6 +2364,9 @@ METH_VARARGS | METH_KEYWORDS}, {"crash_no_current_thread", (PyCFunction)crash_no_current_thread, METH_NOARGS}, {"format_unicode", format_unicode, METH_VARARGS}, + {"sigsegv", sigsegv, METH_NOARGS}, + {"sigfpe", sigfpe, METH_NOARGS}, + {"fatal_error", fatal_error, METH_VARARGS}, {NULL, NULL} /* sentinel */ }; Index: pyconfig.h.in =================================================================== --- pyconfig.h.in (révision 85426) +++ pyconfig.h.in (copie de travail) @@ -620,6 +620,9 @@ /* Define to 1 if you have the `sigaction' function. */ #undef HAVE_SIGACTION +/* Define to 1 if you have the `sigaltstack' function. */ +#undef HAVE_SIGALTSTACK + /* Define to 1 if you have the `siginterrupt' function. */ #undef HAVE_SIGINTERRUPT