Index: Python/pythonrun.c =================================================================== --- Python/pythonrun.c (révision 85709) +++ 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 */ @@ -2081,10 +2086,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 = fileno(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 { @@ -2203,6 +2221,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,307 @@ +/* + * Python SIGSEGV, SIGFPE, SIGBUS and SIGILL 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 + +#ifdef MS_WINDOWS +# include "windows.h" +#else +# include +#endif + +#ifdef HAVE_SIGALTSTACK +static stack_t stack; +#endif + +static int + sigsegv_enabled = 0, sigfpe_enabled = 0 +#ifdef SIGBUS + , sigbus_enabled = 0 +#endif +#ifdef SIGILL + , sigill_enabled = 0 +#endif + ; +#ifdef HAVE_SIGACTION +typedef struct sigaction _Py_sighandler_t; +#else +typedef PyOS_sighandler_t _Py_sighandler_t; +#endif +static _Py_sighandler_t + old_sigsegv_handler, old_sigfpe_handler +#ifdef SIGBUS + , old_sigbus_handler +#endif +#ifdef SIGILL + , old_sigill_handler +#endif + ; + +#define PUTS(str, fd) write(fd, str, strlen(str)) + +static void +reverse_string(char *text, const size_t len) +{ + char tmp; + size_t i, j; + j = len-1; + for (i=0; i < len/2; i++, j--) { + tmp = text[i]; + text[i] = text[j]; + text[j] = tmp; + } +} + +/* Format an integer in range [0; 999999] to decimal, + and write it into the file f */ + +static void +dump_decimal(int value, int fd) +{ + char buffer[7]; + int len; + if (value < 0 || 999999 < value) + return; + len = 0; + do { + buffer[len] = '0' + (value % 10); + value /= 10; + len++; + } while (value); + reverse_string(buffer, len); + write(fd, buffer, len); +} + +/* Format an unsigned integer to hexdecimal of 'width' digits, + and write it into the file f */ + +static void +dump_hexadecimal(int width, unsigned int value, int fd) +{ + const char *hexdigits = "0123456789abcdef"; + char buffer[9]; + int len; + if (0xffffffffU < value) + return; + len = 0; + do { + buffer[len] = hexdigits[value & 15]; + value >>= 4; + len++; + } while (len < width || value); + reverse_string(buffer, len); + 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(2, *u, fd); + } + else +#ifdef Py_UNICODE_WIDE + if (*u < 65536) +#endif + { + PUTS("\\u", fd); + dump_hexadecimal(4, *u, fd); +#ifdef Py_UNICODE_WIDE + } + else { + PUTS("\\U", fd); + dump_hexadecimal(8, *u, fd); +#endif + } + } +} + +/* Write a frame into the file f: "File "xxx", line xxx in xxx" */ + +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); +} + +/* Write the current Python backtrace into the file 'fd': + + Traceback (most recent call first): + File "xxx", line xxx in + File "xxx", line xxx in + ... + File "xxx", line xxx in + */ + +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) { + if (!PyFrame_Check(frame)) + break; + dump_frame(frame, fd); + frame = frame->f_back; + } + return 0; +} + +static void +segfault_handler(int signum) +{ + const int fd = 2; /* should be fileno(stderr) */ + const char *name; + + if (signum == SIGFPE) + name = "Floating point exception"; +#ifdef SIGBUS + else if (signum == SIGBUS) + name = "Bus error"; +#endif +#ifdef SIGILL + else if (signum == SIGILL) + name = "Illegal instruction"; +#endif + else + name = "Segmentation fault"; + + PUTS("Fatal Python error: ", fd); + PUTS(name, fd); + PUTS("\n\n", fd); + + if (!_Py_DumpBacktrace(fd)) { + PUTS(name, fd); + write(fd, "\n", 1); + } + +#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; +#define install(signum, enabled, old_handler) \ + if (sigaction(signum, &action, &old_handler) == 0) enabled = 1; +#else +#define install(signum, enabled, old_handler) \ + old_handler = signal(signum, segfault_handler); \ + if (old_handler != SIG_ERR) enabled = 1; +#endif + + install(SIGSEGV, sigsegv_enabled, old_sigsegv_handler); + install(SIGFPE, sigfpe_enabled, old_sigfpe_handler); +#ifdef SIGBUS + install(SIGBUS, sigbus_enabled, old_sigbus_handler); +#endif +#ifdef SIGILL + install(SIGILL, sigill_enabled, old_sigill_handler); +#endif + +#undef install +} + +void _Py_FiniSegfault(void) +{ +#ifdef HAVE_SIGACTION +# define restore(signum, enabled, old_handler) \ + if (enabled) (void)sigaction(signum, &old_handler, NULL) +#else +# define restore(signum, enabled, old_handler) \ + if (enabled) (void)signal(signum, old_handler); +#endif + + restore(SIGSEGV, sigsegv_enabled, old_sigsegv_handler); + restore(SIGFPE, sigfpe_enabled, old_sigfpe_handler); +#ifdef SIGBUS + restore(SIGBUS, sigbus_enabled, old_sigbus_handler); +#endif +#ifdef SIGILL + restore(SIGILL, sigill_enabled, old_sigill_handler); +#endif + +#undef restore + +#ifdef HAVE_SIGALTSTACK + if (stack.ss_sp != NULL) + PyMem_Free(stack.ss_sp); +#endif +} + Index: configure =================================================================== --- configure (révision 85701) +++ configure (copie de travail) @@ -9228,7 +9228,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 85701) +++ PCbuild/pythoncore.vcproj (copie de travail) @@ -1875,6 +1875,10 @@ > + + Index: configure.in =================================================================== --- configure.in (révision 85701) +++ configure.in (copie de travail) @@ -2521,7 +2521,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 85701) +++ 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. + - Issue #9713, #10114: Parser functions (eg. PyParser_ASTFromFile) expects filenames encoded to the filesystem encoding with surrogateescape error handler (to support undecodable bytes), instead of UTF-8 in strict mode. 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,53 @@ +import unittest +import test.support +import subprocess +import sys +import _testcapi + +class SegfaultTest(unittest.TestCase): + def check_output(self, code, name): + expected = (b'Fatal Python error: ' + name + b'\n\n' + + b'Traceback (most recent call first):\n' + + b' File "", line 1 in \n' + + name + b'\n') + process = subprocess.Popen( + [sys.executable, '-E', '-c', code], + stdout=subprocess.PIPE, stderr=subprocess.PIPE) + stdout, stderr = process.communicate() + self.assertEqual(stderr, expected) + + def test_sigsegv(self): + self.check_output( + "import _testcapi; _testcapi.sigsegv()", + b'Segmentation fault') + + def test_sigfpe(self): + self.check_output( + "import _testcapi; _testcapi.sigfpe()", + b'Floating point exception') + + @unittest.skipUnless(hasattr(_testcapi, 'sigbus'), + "need _testcapi.sigbus()") + def test_sigbus(self): + self.check_output( + "import _testcapi; _testcapi.sigbus()", + b'Bus error') + + @unittest.skipUnless(hasattr(_testcapi, 'sigill'), + "need _testcapi.sigill()") + def test_sigill(self): + self.check_output( + "import _testcapi; _testcapi.sigill()", + b'Illegal instruction') + + def test_fatal_error(self): + self.check_output( + "import _testcapi; _testcapi.fatal_error(b'Error message')", + b'Error message') + + +def test_main(): + test.support.run_unittest(SegfaultTest) + +if __name__ == "__main__": + test_main() Index: Makefile.pre.in =================================================================== --- Makefile.pre.in (révision 85701) +++ Makefile.pre.in (copie de travail) @@ -328,6 +328,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 85701) +++ Modules/_testcapimodule.c (copie de travail) @@ -11,6 +11,7 @@ #include #include "structmember.h" #include "datetime.h" +#include #ifdef WITH_THREAD #include "pythread.h" @@ -2256,6 +2257,51 @@ } +static PyObject * +sigsegv(PyObject *self, PyObject *args) +{ + int *x = NULL; + return PyLong_FromLong(*x); + +} + +static PyObject * +sigfpe(PyObject *self, PyObject *args) +{ + int x = 1, y = 0; + return PyLong_FromLong(x / y); +} + +#if defined(SIGBUS) && defined(HAVE_KILL) +static PyObject * +sigbus(PyObject *self, PyObject *args) +{ + pid_t pid = getpid(); + kill(pid, SIGBUS); + Py_RETURN_NONE; +} +#endif + +#if defined(SIGILL) && defined(HAVE_KILL) +static PyObject * +sigill(PyObject *self, PyObject *args) +{ + pid_t pid = getpid(); + kill(pid, SIGILL); + Py_RETURN_NONE; +} +#endif + +static PyObject * +fatal_error(PyObject *self, PyObject *args) +{ + char *message; + if (!PyArg_ParseTuple(args, "y", &message)) + return NULL; + Py_FatalError(message); + return NULL; +} + static PyMethodDef TestMethods[] = { {"raise_exception", raise_exception, METH_VARARGS}, {"raise_memoryerror", (PyCFunction)raise_memoryerror, METH_NOARGS}, @@ -2338,6 +2384,15 @@ 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}, +#if defined(SIGBUS) && defined(HAVE_KILL) + {"sigbus", sigbus, METH_NOARGS}, +#endif +#if defined(SIGILL) && defined(HAVE_KILL) + {"sigill", sigill, METH_NOARGS}, +#endif + {"fatal_error", fatal_error, METH_VARARGS}, {NULL, NULL} /* sentinel */ }; Index: pyconfig.h.in =================================================================== --- pyconfig.h.in (révision 85701) +++ pyconfig.h.in (copie de travail) @@ -626,6 +626,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