diff options
author | nkozlovskiy <nmk@ydb.tech> | 2023-10-02 18:57:38 +0300 |
---|---|---|
committer | nkozlovskiy <nmk@ydb.tech> | 2023-10-02 19:39:06 +0300 |
commit | 6295ef4d23465c11296e898b9dc4524ad9592b5d (patch) | |
tree | fc0c852877b2c52f365a1f6ed0710955844338c2 /contrib/deprecated/python/faulthandler | |
parent | de63c80b75948ecc13894854514d147840ff8430 (diff) | |
download | ydb-6295ef4d23465c11296e898b9dc4524ad9592b5d.tar.gz |
oss ydb: fix dstool building and test run
Diffstat (limited to 'contrib/deprecated/python/faulthandler')
-rw-r--r-- | contrib/deprecated/python/faulthandler/.dist-info/METADATA | 78 | ||||
-rw-r--r-- | contrib/deprecated/python/faulthandler/.dist-info/top_level.txt | 1 | ||||
-rw-r--r-- | contrib/deprecated/python/faulthandler/AUTHORS | 14 | ||||
-rw-r--r-- | contrib/deprecated/python/faulthandler/COPYING | 25 | ||||
-rw-r--r-- | contrib/deprecated/python/faulthandler/README.rst | 57 | ||||
-rw-r--r-- | contrib/deprecated/python/faulthandler/faulthandler.c | 1415 | ||||
-rw-r--r-- | contrib/deprecated/python/faulthandler/traceback.c | 326 | ||||
-rw-r--r-- | contrib/deprecated/python/faulthandler/ya.make | 30 |
8 files changed, 1946 insertions, 0 deletions
diff --git a/contrib/deprecated/python/faulthandler/.dist-info/METADATA b/contrib/deprecated/python/faulthandler/.dist-info/METADATA new file mode 100644 index 0000000000..1d2974b87f --- /dev/null +++ b/contrib/deprecated/python/faulthandler/.dist-info/METADATA @@ -0,0 +1,78 @@ +Metadata-Version: 2.1 +Name: faulthandler +Version: 3.2 +Summary: Display the Python traceback on a crash +Home-page: https://faulthandler.readthedocs.io/ +Author: Victor Stinner +Author-email: victor.stinner@gmail.com +License: BSD (2-clause) +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Natural Language :: English +Classifier: Programming Language :: C +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Topic :: Software Development :: Debuggers +Classifier: Topic :: Software Development :: Libraries :: Python Modules + ++++++++++++++ +Fault handler ++++++++++++++ + +.. image:: https://img.shields.io/pypi/v/faulthandler.svg + :alt: Latest release on the Python Cheeseshop (PyPI) + :target: https://pypi.python.org/pypi/faulthandler + +.. image:: https://travis-ci.org/vstinner/faulthandler.svg?branch=master + :alt: Build status of faulthandler on Travis CI + :target: https://travis-ci.org/vstinner/faulthandler + +.. image:: http://unmaintained.tech/badge.svg + :target: http://unmaintained.tech/ + :alt: No Maintenance Intended + +Fault handler for SIGSEGV, SIGFPE, SIGABRT, SIGBUS and SIGILL signals: display +the Python traceback and restore the previous handler. Allocate an alternate +stack for this handler, if sigaltstack() is available, to be able to allocate +memory on the stack, even on stack overflow (not available on Windows). + +Import the module and call faulthandler.enable() to enable the fault handler. + +Alternatively you can set the PYTHONFAULTHANDLER environment variable to a +non-empty value. + +The fault handler is called on catastrophic cases and so it can only use +signal-safe functions (eg. it doesn't allocate memory on the heap). That's why +the traceback is limited: it only supports ASCII encoding (use the +backslashreplace error handler for non-ASCII characters) and limits each string +to 100 characters, doesn't print the source code in the traceback (only the +filename, the function name and the line number), is limited to 100 frames and +100 threads. + +By default, the Python traceback is written to the standard error stream. Start +your graphical applications in a terminal and run your server in foreground to +see the traceback, or pass a file to faulthandler.enable(). + +faulthandler is implemented in C using signal handlers to be able to dump a +traceback on a crash or when Python is blocked (eg. deadlock). + +This module is the backport for CPython 2.7. faulthandler is part of CPython +standard library since CPython 3.3: `faulthandler +<http://docs.python.org/dev/library/faulthandler.html>`_. For PyPy, +faulthandler is builtin since PyPy 5.5: use ``pypy -X faulthandler``. + +Website: +https://faulthandler.readthedocs.io/ + +faulthandler 3.2 is the last version released by Victor Stinner. I maintained +it for 10 years in my free time for the great pleasure of Python 2 users, but +Python 2 is no longer supported upstream since 2020-01-01. Each faulthandler +release requires me to start my Windows VM, install Python 2.7 in 32-bit and +64-bit, install an old C compiler just for Python 2.7, and type manually some +commands to upload Windows binaries. Moreover, I have to fix some issues on +Travis CI and many small boring tasks. The maintenance is far from being free. +In 10 years, I got zero "thank you" (and 0€), only bug reports :-) + diff --git a/contrib/deprecated/python/faulthandler/.dist-info/top_level.txt b/contrib/deprecated/python/faulthandler/.dist-info/top_level.txt new file mode 100644 index 0000000000..c093449392 --- /dev/null +++ b/contrib/deprecated/python/faulthandler/.dist-info/top_level.txt @@ -0,0 +1 @@ +faulthandler diff --git a/contrib/deprecated/python/faulthandler/AUTHORS b/contrib/deprecated/python/faulthandler/AUTHORS new file mode 100644 index 0000000000..56ba9ac0c5 --- /dev/null +++ b/contrib/deprecated/python/faulthandler/AUTHORS @@ -0,0 +1,14 @@ +Authors +======= + +Victor Stinner <victor.stinner@gmail.com> + +Contributors +============ + +Dan Sully <daniel@electricrain.com> - minor fixes +Guido van Rossum <guido@dropbox.com> - enhance traceback output +Martin (gzlist) <gzlist@googlemail.com> - improved Windows support +Ionel Cristian Mărieș <contact@ionelmc.ro> - guilty for the .pth/environment variable activation +Wei Wu - added support for file descriptors +Giuseppe Corbelli - added support for thread name printout on Linux diff --git a/contrib/deprecated/python/faulthandler/COPYING b/contrib/deprecated/python/faulthandler/COPYING new file mode 100644 index 0000000000..b334336b5c --- /dev/null +++ b/contrib/deprecated/python/faulthandler/COPYING @@ -0,0 +1,25 @@ +Copyright 2010 Victor Stinner. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are +permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list + of conditions and the following disclaimer in the documentation and/or other materials + provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY VICTOR STINNER ``AS IS'' AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL VICTOR STINNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those of the +authors and should not be interpreted as representing official policies, either expressed +or implied, of Victor Stinner. diff --git a/contrib/deprecated/python/faulthandler/README.rst b/contrib/deprecated/python/faulthandler/README.rst new file mode 100644 index 0000000000..82a8c1ac78 --- /dev/null +++ b/contrib/deprecated/python/faulthandler/README.rst @@ -0,0 +1,57 @@ ++++++++++++++ +Fault handler ++++++++++++++ + +.. image:: https://img.shields.io/pypi/v/faulthandler.svg + :alt: Latest release on the Python Cheeseshop (PyPI) + :target: https://pypi.python.org/pypi/faulthandler + +.. image:: https://travis-ci.org/vstinner/faulthandler.svg?branch=master + :alt: Build status of faulthandler on Travis CI + :target: https://travis-ci.org/vstinner/faulthandler + +.. image:: http://unmaintained.tech/badge.svg + :target: http://unmaintained.tech/ + :alt: No Maintenance Intended + +Fault handler for SIGSEGV, SIGFPE, SIGABRT, SIGBUS and SIGILL signals: display +the Python traceback and restore the previous handler. Allocate an alternate +stack for this handler, if sigaltstack() is available, to be able to allocate +memory on the stack, even on stack overflow (not available on Windows). + +Import the module and call faulthandler.enable() to enable the fault handler. + +Alternatively you can set the PYTHONFAULTHANDLER environment variable to a +non-empty value. + +The fault handler is called on catastrophic cases and so it can only use +signal-safe functions (eg. it doesn't allocate memory on the heap). That's why +the traceback is limited: it only supports ASCII encoding (use the +backslashreplace error handler for non-ASCII characters) and limits each string +to 100 characters, doesn't print the source code in the traceback (only the +filename, the function name and the line number), is limited to 100 frames and +100 threads. + +By default, the Python traceback is written to the standard error stream. Start +your graphical applications in a terminal and run your server in foreground to +see the traceback, or pass a file to faulthandler.enable(). + +faulthandler is implemented in C using signal handlers to be able to dump a +traceback on a crash or when Python is blocked (eg. deadlock). + +This module is the backport for CPython 2.7. faulthandler is part of CPython +standard library since CPython 3.3: `faulthandler +<http://docs.python.org/dev/library/faulthandler.html>`_. For PyPy, +faulthandler is builtin since PyPy 5.5: use ``pypy -X faulthandler``. + +Website: +https://faulthandler.readthedocs.io/ + +faulthandler 3.2 is the last version released by Victor Stinner. I maintained +it for 10 years in my free time for the great pleasure of Python 2 users, but +Python 2 is no longer supported upstream since 2020-01-01. Each faulthandler +release requires me to start my Windows VM, install Python 2.7 in 32-bit and +64-bit, install an old C compiler just for Python 2.7, and type manually some +commands to upload Windows binaries. Moreover, I have to fix some issues on +Travis CI and many small boring tasks. The maintenance is far from being free. +In 10 years, I got zero "thank you" (and 0€), only bug reports :-) diff --git a/contrib/deprecated/python/faulthandler/faulthandler.c b/contrib/deprecated/python/faulthandler/faulthandler.c new file mode 100644 index 0000000000..4aaae8b636 --- /dev/null +++ b/contrib/deprecated/python/faulthandler/faulthandler.c @@ -0,0 +1,1415 @@ +/* + * faulthandler module + * + * Written by Victor Stinner. + */ + +#include "Python.h" +#include "pythread.h" +#include <signal.h> +#ifdef MS_WINDOWS +# include <windows.h> +#endif +#ifdef HAVE_SYS_RESOURCE_H +# include <sys/resource.h> +#endif + +#define VERSION 0x302 + +/* Allocate at maximum 100 MB of the stack to raise the stack overflow */ +#define STACK_OVERFLOW_MAX_SIZE (100*1024*1024) + +#ifdef SIGALRM +# define FAULTHANDLER_LATER +#endif + +#ifndef MS_WINDOWS + /* sigaltstack() is not available on Windows */ +# define HAVE_SIGALTSTACK + + /* register() is useless on Windows, because only SIGSEGV, SIGABRT and + SIGILL can be handled by the process, and these signals can only be used + with enable(), not using register() */ +# define FAULTHANDLER_USER +#endif + +#if PY_MAJOR_VERSION >= 3 +# define PYINT_CHECK PyLong_Check +# define PYINT_ASLONG PyLong_AsLong +#else +# define PYINT_CHECK PyInt_Check +# define PYINT_ASLONG PyInt_AsLong +#endif + +/* defined in traceback.c */ +extern Py_ssize_t _Py_write_noraise(int fd, const char *buf, size_t count); +extern void dump_decimal(int fd, int value); +extern void reverse_string(char *text, const size_t len); + +/* cast size_t to int because write() takes an int on Windows + (anyway, the length is smaller than 30 characters) */ +#define PUTS(fd, str) _Py_write_noraise(fd, str, (int)strlen(str)) + +#ifdef HAVE_SIGACTION +typedef struct sigaction _Py_sighandler_t; +#else +typedef PyOS_sighandler_t _Py_sighandler_t; +#endif + +typedef struct { + int signum; + int enabled; + const char* name; + _Py_sighandler_t previous; + int all_threads; +} fault_handler_t; + +static struct { + int enabled; + PyObject *file; + int fd; + int all_threads; + PyInterpreterState *interp; +} fatal_error = {0, NULL, -1, 0}; + +#ifdef FAULTHANDLER_LATER +static struct { + PyObject *file; + int fd; + int timeout; + int repeat; + PyInterpreterState *interp; + int exit; + char *header; + size_t header_len; +} fault_alarm; +#endif + +#ifdef FAULTHANDLER_USER +typedef struct { + int enabled; + PyObject *file; + int fd; + int all_threads; + int chain; + _Py_sighandler_t previous; + PyInterpreterState *interp; +} user_signal_t; + +static user_signal_t *user_signals; + +/* the following macros come from Python: Modules/signalmodule.c of Python 3.3 */ +#if defined(PYOS_OS2) && !defined(PYCC_GCC) +#define NSIG 12 +#endif +#ifndef NSIG +# if defined(_NSIG) +# define NSIG _NSIG /* For BSD/SysV */ +# elif defined(_SIGMAX) +# define NSIG (_SIGMAX + 1) /* For QNX */ +# elif defined(SIGMAX) +# define NSIG (SIGMAX + 1) /* For djgpp */ +# else +# define NSIG 64 /* Use a reasonable default value */ +# endif +#endif + +static void faulthandler_user(int signum); +#endif /* FAULTHANDLER_USER */ + +#ifndef SI_KERNEL +#define SI_KERNEL 0x80 +#endif + +#ifndef SI_TKILL +#define SI_TKILL -6 +#endif + +static fault_handler_t faulthandler_handlers[] = { +#ifdef SIGBUS + {SIGBUS, 0, "Bus error", }, +#endif +#ifdef SIGILL + {SIGILL, 0, "Illegal instruction", }, +#endif + {SIGFPE, 0, "Floating point exception", }, + {SIGABRT, 0, "Aborted", }, + /* define SIGSEGV at the end to make it the default choice if searching the + handler fails in faulthandler_fatal_error() */ + {SIGSEGV, 0, "Segmentation fault", } +}; +static const size_t faulthandler_nsignals = \ + sizeof(faulthandler_handlers) / sizeof(faulthandler_handlers[0]); + +#ifdef HAVE_SIGALTSTACK +static stack_t stack; +#endif + +/* Forward */ +static void faulthandler_unload(void); + +/* from traceback.c */ +extern void _Py_DumpTraceback(int fd, PyThreadState *tstate); +extern const char* _Py_DumpTracebackThreads( + int fd, + PyInterpreterState *interp, + PyThreadState *current_thread); + +/* Get the file descriptor of a file by calling its fileno() method and then + call its flush() method. + + If file is NULL or Py_None, use sys.stderr as the new file. + If file is an integer, it will be treated as file descriptor. + + On success, return the file descriptor and write the new file into *file_ptr. + On error, return -1. */ + +static int +faulthandler_get_fileno(PyObject **file_ptr) +{ + PyObject *result; + long fd_long; + long fd; + PyObject *file = *file_ptr; + + if (file == NULL || file == Py_None) { + file = PySys_GetObject("stderr"); + if (file == NULL) { + PyErr_SetString(PyExc_RuntimeError, "unable to get sys.stderr"); + return -1; + } + if (file == Py_None) { + PyErr_SetString(PyExc_RuntimeError, "sys.stderr is None"); + return -1; + } + } + else if (PYINT_CHECK(file)) { + fd = PYINT_ASLONG(file); + if (fd == -1 && PyErr_Occurred()) + return -1; + if (fd < 0 || fd > INT_MAX) { + PyErr_SetString(PyExc_ValueError, + "file is not a valid file descripter"); + return -1; + } + *file_ptr = NULL; + return (int)fd; + } + + result = PyObject_CallMethod(file, "fileno", ""); + if (result == NULL) + return -1; + + fd = -1; + if (PYINT_CHECK(result)) { + fd_long = PYINT_ASLONG(result); + if (0 < fd_long && fd_long < INT_MAX) + fd = (int)fd_long; + } + Py_DECREF(result); + + if (fd == -1) { + PyErr_SetString(PyExc_RuntimeError, + "file.fileno() is not a valid file descriptor"); + return -1; + } + + result = PyObject_CallMethod(file, "flush", ""); + if (result != NULL) + Py_DECREF(result); + else { + /* ignore flush() error */ + PyErr_Clear(); + } + *file_ptr = file; + return fd; +} + +/* Get the state of the current thread: only call this function if the current + thread holds the GIL. Raise an exception on error. */ +static PyThreadState* +get_thread_state(void) +{ + PyThreadState *tstate = PyThreadState_Get(); + if (tstate == NULL) { + PyErr_SetString(PyExc_RuntimeError, + "unable to get the current thread state"); + return NULL; + } + return tstate; +} + +static void +faulthandler_dump_traceback(int fd, int all_threads, + PyInterpreterState *interp) +{ + static volatile int reentrant = 0; + PyThreadState *tstate; + + if (reentrant) + return; + + reentrant = 1; + +#ifdef WITH_THREAD + /* SIGSEGV, SIGFPE, SIGABRT, SIGBUS and SIGILL are synchronous signals and + are thus delivered to the thread that caused the fault. Get the Python + thread state of the current thread. + + PyThreadState_Get() doesn't give the state of the thread that caused the + fault if the thread released the GIL, and so this function cannot be + used. Read the thread local storage (TLS) instead: call + PyGILState_GetThisThreadState(). */ + tstate = PyGILState_GetThisThreadState(); +#else + tstate = PyThreadState_Get(); +#endif + + if (all_threads) + _Py_DumpTracebackThreads(fd, interp, tstate); + else { + if (tstate != NULL) + _Py_DumpTraceback(fd, tstate); + } + + reentrant = 0; +} + +static PyObject* +faulthandler_dump_traceback_py(PyObject *self, + PyObject *args, PyObject *kwargs) +{ + static char *kwlist[] = {"file", "all_threads", NULL}; + PyObject *file = NULL; + int all_threads = 1; + PyThreadState *tstate; + const char *errmsg; + int fd; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, + "|Oi:dump_traceback", kwlist, + &file, &all_threads)) + return NULL; + + fd = faulthandler_get_fileno(&file); + if (fd < 0) + return NULL; + + tstate = get_thread_state(); + if (tstate == NULL) + return NULL; + + if (all_threads) { + errmsg = _Py_DumpTracebackThreads(fd, tstate->interp, tstate); + if (errmsg != NULL) { + PyErr_SetString(PyExc_RuntimeError, errmsg); + return NULL; + } + } + else { + _Py_DumpTraceback(fd, tstate); + } + + if (PyErr_CheckSignals()) + return NULL; + + 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. + + Display the current Python traceback, restore the previous handler and call + the previous handler. + + On Windows, don't explicitly call the previous handler, because the Windows + signal handler would not be called (for an unknown reason). The execution of + the program continues at faulthandler_fatal_error() exit, but the same + instruction will raise the same fault (signal), and so the previous handler + will be called. + + This function is signal-safe and should only call signal-safe functions. */ + +static void +faulthandler_fatal_error(int signum) +{ + const int fd = fatal_error.fd; + size_t i; + fault_handler_t *handler = NULL; + int save_errno = errno; + + if (!fatal_error.enabled) + return; + + for (i=0; i < faulthandler_nsignals; i++) { + handler = &faulthandler_handlers[i]; + if (handler->signum == signum) + break; + } + if (handler == NULL) { + /* faulthandler_nsignals == 0 (unlikely) */ + return; + } + + /* restore the previous handler */ + faulthandler_disable_fatal_handler(handler); + + PUTS(fd, "Fatal Python error: "); + PUTS(fd, handler->name); + PUTS(fd, "\n\n"); + + faulthandler_dump_traceback(fd, fatal_error.all_threads, + fatal_error.interp); + + errno = save_errno; +#ifdef MS_WINDOWS + if (signum == SIGSEGV) { + /* don't explicitly call the previous handler for SIGSEGV in this signal + handler, because the Windows signal handler would not be called */ + return; + } +#endif + /* call the previous signal handler: it is called immediately if we use + sigaction() thanks to SA_NODEFER flag, otherwise it is deferred */ + raise(signum); +} + +static size_t +uitoa(size_t val, char* ss) { + char* start = ss; + size_t len = 0; + do { + *ss = '0' + (val % 10); + val /= 10; + ss++; len++; + } while (val); + reverse_string(start, len); + return len; +} + +static void +read_proc_exe(pid_t pid, char* buff, size_t len) { + char pathname[32] = {0}; + strcpy(pathname, "/proc/"); + size_t pos = uitoa(pid, &pathname[6]) + 6; + strcpy(&pathname[pos], "/exe"); + + ssize_t l = readlink(pathname, buff, len); + if (l > 0) { + // readlink() does not append a null byte to buf + buff[l] = '\0'; + } else { + strncpy(buff, "unknown_program", len); + } +} + +#ifdef HAVE_SIGACTION +static void +faulthandler_fatal_error_siginfo(int signum, siginfo_t* siginfo, void* ctx) +{ + const int fd = fatal_error.fd; + int save_errno = errno; + + if (!fatal_error.enabled) + return; + + PUTS(fd, "\n*** Signal {si_signo="); + dump_decimal(fd, siginfo->si_signo); + + PUTS(fd, ", si_code="); + dump_decimal(fd, siginfo->si_code); + switch (siginfo->si_code) { + case SEGV_ACCERR: PUTS(fd, " SEGV_ACCERR"); break; + case SEGV_MAPERR: PUTS(fd, " SEGV_MAPERR"); break; + case SI_KERNEL: PUTS(fd, " SI_KERNEL"); break; + case SI_TIMER: PUTS(fd, " SI_TIMER"); break; + case SI_TKILL: PUTS(fd, " SI_TKILL"); break; + case SI_USER: PUTS(fd, " SI_USER"); break; + } + + if (siginfo->si_pid > 0) { + PUTS(fd, ", si_pid="); + dump_decimal(fd, siginfo->si_pid); + PUTS(fd, " "); + char buffer[PATH_MAX] = {0}; + read_proc_exe(siginfo->si_pid, &buffer[0], PATH_MAX - 1); + PUTS(fd, &buffer[0]); + } + + PUTS(fd, ", si_uid="); + dump_decimal(fd, siginfo->si_uid); + + PUTS(fd, "} received by proc {pid="); + dump_decimal(fd, getpid()); + PUTS(fd, ", uid="); + dump_decimal(fd, getuid()); + PUTS(fd, "} ***\n"); + + faulthandler_fatal_error(signum); + + errno = save_errno; +} +#endif + +#ifdef MS_WINDOWS +extern void _Py_dump_hexadecimal(int fd, unsigned long value, size_t bytes); + +static int +faulthandler_ignore_exception(DWORD code) +{ + /* bpo-30557: ignore exceptions which are not errors */ + if (!(code & 0x80000000)) { + return 1; + } + /* bpo-31701: ignore MSC and COM exceptions + E0000000 + code */ + if (code == 0xE06D7363 /* MSC exception ("Emsc") */ + || code == 0xE0434352 /* COM Callable Runtime exception ("ECCR") */) { + return 1; + } + /* Interesting exception: log it with the Python traceback */ + return 0; +} + +static LONG WINAPI +faulthandler_exc_handler(struct _EXCEPTION_POINTERS *exc_info) +{ + const int fd = fatal_error.fd; + DWORD code = exc_info->ExceptionRecord->ExceptionCode; + + if (faulthandler_ignore_exception(code)) { + /* ignore the exception: call the next exception handler */ + return EXCEPTION_CONTINUE_SEARCH; + } + 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* +faulthandler_enable(PyObject *self, PyObject *args, PyObject *kwargs) +{ + static char *kwlist[] = {"file", "all_threads", NULL}; + PyObject *file = NULL; + int all_threads = 1; + unsigned int i; + fault_handler_t *handler; +#ifdef HAVE_SIGACTION + struct sigaction action; +#endif + int err; + int fd; + PyThreadState *tstate; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, + "|Oi:enable", kwlist, &file, &all_threads)) + return NULL; + + fd = faulthandler_get_fileno(&file); + if (fd < 0) + return NULL; + + tstate = get_thread_state(); + if (tstate == NULL) + return NULL; + + Py_XDECREF(fatal_error.file); + Py_XINCREF(file); + fatal_error.file = file; + fatal_error.fd = fd; + 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_flags = 0; +#ifdef USE_SIGINFO + action.sa_handler = faulthandler_fatal_error_siginfo; + action.sa_flags |= SA_SIGINFO; +#else + action.sa_handler = faulthandler_fatal_error; +#endif + 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; + } +#ifdef MS_WINDOWS + AddVectoredExceptionHandler(1, faulthandler_exc_handler); +#endif + } + Py_RETURN_NONE; +} + +static void +faulthandler_disable(void) +{ + unsigned int i; + fault_handler_t *handler; + + if (fatal_error.enabled) { + fatal_error.enabled = 0; + for (i=0; i < faulthandler_nsignals; i++) { + handler = &faulthandler_handlers[i]; + faulthandler_disable_fatal_handler(handler); + } + } + + Py_CLEAR(fatal_error.file); +} + +static PyObject* +faulthandler_disable_py(PyObject *self) +{ + if (!fatal_error.enabled) { + Py_INCREF(Py_False); + return Py_False; + } + faulthandler_disable(); + Py_INCREF(Py_True); + return Py_True; +} + +static PyObject* +faulthandler_is_enabled(PyObject *self) +{ + return PyBool_FromLong(fatal_error.enabled); +} + +#ifdef FAULTHANDLER_LATER +/* Handler of the SIGALRM signal. + + Dump the traceback of the current thread, or of all threads if + fault_alarm.all_threads is true. On success, register itself again if + fault_alarm.repeat is true. + + This function is signal safe and should only call signal safe functions. */ + +static void +faulthandler_alarm(int signum) +{ + PyThreadState *tstate; + const char* errmsg; + int ok; + + _Py_write_noraise(fault_alarm.fd, + fault_alarm.header, fault_alarm.header_len); + + /* PyThreadState_Get() doesn't give the state of the current thread if + the thread doesn't hold the GIL. Read the thread local storage (TLS) + instead: call PyGILState_GetThisThreadState(). */ + tstate = PyGILState_GetThisThreadState(); + + errmsg = _Py_DumpTracebackThreads(fault_alarm.fd, fault_alarm.interp, tstate); + ok = (errmsg == NULL); + + if (ok && fault_alarm.repeat) + alarm(fault_alarm.timeout); + else + /* don't call Py_CLEAR() here because it may call _Py_Dealloc() which + is not signal safe */ + alarm(0); + + if (fault_alarm.exit) + _exit(1); +} + +static char* +format_timeout(double timeout) +{ + unsigned long us, sec, min, hour; + double intpart, fracpart; + char buffer[100]; + + fracpart = modf(timeout, &intpart); + sec = (unsigned long)intpart; + us = (unsigned long)(fracpart * 1e6); + min = sec / 60; + sec %= 60; + hour = min / 60; + min %= 60; + + if (us != 0) + PyOS_snprintf(buffer, sizeof(buffer), + "Timeout (%lu:%02lu:%02lu.%06lu)!\n", + hour, min, sec, us); + else + PyOS_snprintf(buffer, sizeof(buffer), + "Timeout (%lu:%02lu:%02lu)!\n", + hour, min, sec); + + return strdup(buffer); +} + +static PyObject* +faulthandler_dump_traceback_later(PyObject *self, + PyObject *args, PyObject *kwargs) +{ + static char *kwlist[] = {"timeout", "repeat", "file", "exit", NULL}; + int timeout; + PyOS_sighandler_t previous; + int repeat = 0; + PyObject *file = NULL; + int exit = 0; + PyThreadState *tstate; + int fd; + char *header; + size_t header_len; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, + "i|iOi:dump_traceback_later", kwlist, + &timeout, &repeat, &file, &exit)) + return NULL; + if (timeout <= 0) { + PyErr_SetString(PyExc_ValueError, "timeout must be greater than 0"); + return NULL; + } + + tstate = get_thread_state(); + if (tstate == NULL) + return NULL; + + fd = faulthandler_get_fileno(&file); + if (fd < 0) + return NULL; + + /* format the timeout */ + header = format_timeout(timeout); + if (header == NULL) + return PyErr_NoMemory(); + header_len = strlen(header); + + previous = signal(SIGALRM, faulthandler_alarm); + if (previous == SIG_ERR) { + PyErr_SetString(PyExc_RuntimeError, "unable to set SIGALRM handler"); + free(header); + return NULL; + } + + Py_XDECREF(fault_alarm.file); + Py_XINCREF(file); + fault_alarm.file = file; + fault_alarm.fd = fd; + fault_alarm.timeout = timeout; + fault_alarm.repeat = repeat; + fault_alarm.interp = tstate->interp; + fault_alarm.exit = exit; + fault_alarm.header = header; + fault_alarm.header_len = header_len; + + alarm(timeout); + + Py_RETURN_NONE; +} + +static PyObject* +faulthandler_cancel_dump_traceback_later_py(PyObject *self) +{ + alarm(0); + Py_CLEAR(fault_alarm.file); + free(fault_alarm.header); + fault_alarm.header = NULL; + Py_RETURN_NONE; +} +#endif /* FAULTHANDLER_LATER */ + +#ifdef FAULTHANDLER_USER +static int +faulthandler_register(int signum, int chain, _Py_sighandler_t *p_previous) +{ +#ifdef HAVE_SIGACTION + struct sigaction action; + action.sa_handler = faulthandler_user; + sigemptyset(&action.sa_mask); + /* if the signal is received while the kernel is executing a system + call, try to restart the system call instead of interrupting it and + return EINTR. */ + action.sa_flags = SA_RESTART; + if (chain) { + /* 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 + return sigaction(signum, &action, p_previous); +#else + _Py_sighandler_t previous; + previous = signal(signum, faulthandler_user); + if (p_previous != NULL) + *p_previous = previous; + return (previous == SIG_ERR); +#endif +} + +/* Handler of user signals (e.g. SIGUSR1). + + Dump the traceback of the current thread, or of all threads if + thread.all_threads is true. + + This function is signal safe and should only call signal safe functions. */ + +static void +faulthandler_user(int signum) +{ + user_signal_t *user; + int save_errno = errno; + + user = &user_signals[signum]; + if (!user->enabled) + return; + + faulthandler_dump_traceback(user->fd, user->all_threads, user->interp); + +#ifdef HAVE_SIGACTION + if (user->chain) { + (void)sigaction(signum, &user->previous, NULL); + errno = save_errno; + + /* call the previous signal handler */ + raise(signum); + + save_errno = errno; + (void)faulthandler_register(signum, user->chain, NULL); + errno = save_errno; + } +#else + if (user->chain) { + errno = save_errno; + /* call the previous signal handler */ + user->previous(signum); + } +#endif +} + +static int +check_signum(int signum) +{ + unsigned int i; + + for (i=0; i < faulthandler_nsignals; i++) { + if (faulthandler_handlers[i].signum == signum) { + PyErr_Format(PyExc_RuntimeError, + "signal %i cannot be registered, " + "use enable() instead", + signum); + return 0; + } + } + if (signum < 1 || NSIG <= signum) { + PyErr_SetString(PyExc_ValueError, "signal number out of range"); + return 0; + } + return 1; +} + +static PyObject* +faulthandler_register_py(PyObject *self, + PyObject *args, PyObject *kwargs) +{ + static char *kwlist[] = {"signum", "file", "all_threads", "chain", NULL}; + int signum; + PyObject *file = NULL; + int all_threads = 1; + int chain = 0; + int fd; + user_signal_t *user; + _Py_sighandler_t previous; + PyThreadState *tstate; + int err; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, + "i|Oii:register", kwlist, + &signum, &file, &all_threads, &chain)) + return NULL; + + if (!check_signum(signum)) + return NULL; + + tstate = get_thread_state(); + if (tstate == NULL) + return NULL; + + fd = faulthandler_get_fileno(&file); + if (fd < 0) + return NULL; + + if (user_signals == NULL) { + user_signals = PyMem_Malloc(NSIG * sizeof(user_signal_t)); + if (user_signals == NULL) + return PyErr_NoMemory(); + memset(user_signals, 0, NSIG * sizeof(user_signal_t)); + } + user = &user_signals[signum]; + + if (!user->enabled) { + err = faulthandler_register(signum, chain, &previous); + if (err) { + PyErr_SetFromErrno(PyExc_OSError); + return NULL; + } + + user->previous = previous; + } + + Py_XDECREF(user->file); + Py_XINCREF(file); + user->file = file; + user->fd = fd; + user->all_threads = all_threads; + user->chain = chain; + user->interp = tstate->interp; + user->enabled = 1; + + Py_RETURN_NONE; +} + +static int +faulthandler_unregister(user_signal_t *user, int signum) +{ + if (!user->enabled) + return 0; + user->enabled = 0; +#ifdef HAVE_SIGACTION + (void)sigaction(signum, &user->previous, NULL); +#else + (void)signal(signum, user->previous); +#endif + user->fd = -1; + return 1; +} + +static PyObject* +faulthandler_unregister_py(PyObject *self, PyObject *args) +{ + int signum; + user_signal_t *user; + int change; + + if (!PyArg_ParseTuple(args, "i:unregister", &signum)) + return NULL; + + if (!check_signum(signum)) + return NULL; + + if (user_signals == NULL) + Py_RETURN_FALSE; + + user = &user_signals[signum]; + change = faulthandler_unregister(user, signum); + Py_CLEAR(user->file); + return PyBool_FromLong(change); +} +#endif /* FAULTHANDLER_USER */ + + +static void +faulthandler_suppress_crash_report(void) +{ +#ifdef MS_WINDOWS + UINT mode; + + /* Configure Windows to not display the Windows Error Reporting dialog */ + mode = SetErrorMode(SEM_NOGPFAULTERRORBOX); + SetErrorMode(mode | SEM_NOGPFAULTERRORBOX); +#endif + +#ifdef HAVE_SYS_RESOURCE_H + struct rlimit rl; + + /* Disable creation of core dump */ + if (getrlimit(RLIMIT_CORE, &rl) != 0) { + rl.rlim_cur = 0; + setrlimit(RLIMIT_CORE, &rl); + } +#endif + +#ifdef _MSC_VER + /* Visual Studio: configure abort() to not display an error message nor + open a popup asking to report the fault. */ + _set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT); +#endif +} + +static PyObject * +faulthandler_read_null(PyObject *self, PyObject *args) +{ + volatile int *x; + volatile int y; + + faulthandler_suppress_crash_report(); + x = NULL; + y = *x; + return PyLong_FromLong(y); + +} + +static void +faulthandler_raise_sigsegv(void) +{ + faulthandler_suppress_crash_report(); +#if defined(MS_WINDOWS) + /* For SIGSEGV, faulthandler_fatal_error() restores the previous signal + handler and then gives back the execution flow to the program (without + explicitly calling the previous error handler). In a normal case, the + SIGSEGV was raised by the kernel because of a fault, and so if the + program retries to execute the same instruction, the fault will be + raised again. + + Here the fault is simulated by a fake SIGSEGV signal raised by the + application. We have to raise SIGSEGV at lease twice: once for + faulthandler_fatal_error(), and one more time for the previous signal + handler. */ + while(1) + raise(SIGSEGV); +#else + raise(SIGSEGV); +#endif +} + +static PyObject * +faulthandler_sigsegv(PyObject *self, PyObject *args) +{ + int release_gil = 0; + if (!PyArg_ParseTuple(args, "|i:_read_null", &release_gil)) + return NULL; + + if (release_gil) { + Py_BEGIN_ALLOW_THREADS + faulthandler_raise_sigsegv(); + Py_END_ALLOW_THREADS + } else { + faulthandler_raise_sigsegv(); + } + Py_RETURN_NONE; +} + +static PyObject * +faulthandler_sigfpe(PyObject *self, PyObject *args) +{ + /* Do an integer division by zero: raise a SIGFPE on Intel CPU, but not on + PowerPC. Use volatile to disable compile-time optimizations. */ + volatile int x = 1, y = 0, z; + faulthandler_suppress_crash_report(); + z = x / y; + /* If the division by zero didn't raise a SIGFPE (e.g. on PowerPC), + raise it manually. */ + raise(SIGFPE); + /* This line is never reached, but we pretend to make something with z + to silence a compiler warning. */ + return PyLong_FromLong(z); +} + +static PyObject * +faulthandler_sigabrt(PyObject *self, PyObject *args) +{ + faulthandler_suppress_crash_report(); + abort(); + Py_RETURN_NONE; +} + +static PyObject * +faulthandler_raise_signal(PyObject *self, PyObject *args) +{ + int signum, err; + + if (PyArg_ParseTuple(args, "i:raise_signal", &signum) < 0) + return NULL; + + faulthandler_suppress_crash_report(); + + err = raise(signum); + if (err) + return PyErr_SetFromErrno(PyExc_OSError); + + if (PyErr_CheckSignals() < 0) + return NULL; + + Py_RETURN_NONE; +} + +static PyObject * +faulthandler_fatal_error_py(PyObject *self, PyObject *args) +{ + char *message; +#if PY_MAJOR_VERSION >= 3 + if (!PyArg_ParseTuple(args, "y:_fatal_error", &message)) + return NULL; +#else + if (!PyArg_ParseTuple(args, "s:fatal_error", &message)) + return NULL; +#endif + faulthandler_suppress_crash_report(); + Py_FatalError(message); + Py_RETURN_NONE; +} + + +#ifdef __INTEL_COMPILER + /* Issue #23654: Turn off ICC's tail call optimization for the + * stack_overflow generator. ICC turns the recursive tail call into + * a loop. */ +# pragma intel optimization_level 0 +#endif +static +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[1024*1024]; + Py_uintptr_t sp = (Py_uintptr_t)&buffer; + *depth += 1; + if (sp < min_sp || max_sp < sp) { + return sp; + } + memset(buffer, (unsigned char)*depth, sizeof(buffer)); + return stack_overflow(min_sp, max_sp, depth) + buffer[0]; +} + +static PyObject * +faulthandler_stack_overflow(PyObject *self) +{ + 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; + 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 + size = sp - stop; + PyErr_Format(PyExc_RuntimeError, + "unable to raise a stack overflow (allocated %zu bytes " + "on the stack, %zu recursive calls)", + size, depth); + return NULL; +} + +#if PY_MAJOR_VERSION >= 3 +static int +faulthandler_traverse(PyObject *module, visitproc visit, void *arg) +{ +#ifdef FAULTHANDLER_USER + unsigned int signum; +#endif + +#ifdef FAULTHANDLER_LATER + Py_VISIT(fault_alarm.file); +#endif +#ifdef FAULTHANDLER_USER + if (user_signals != NULL) { + for (signum=0; signum < NSIG; signum++) + Py_VISIT(user_signals[signum].file); + } +#endif + Py_VISIT(fatal_error.file); + return 0; +} +#endif + +#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."); + +static PyMethodDef module_methods[] = { + {"enable", + (PyCFunction)faulthandler_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, + PyDoc_STR("disable(): disable the fault handler")}, + {"is_enabled", (PyCFunction)faulthandler_is_enabled, METH_NOARGS, + PyDoc_STR("is_enabled()->bool: check if the handler is enabled")}, + {"dump_traceback", + (PyCFunction)faulthandler_dump_traceback_py, METH_VARARGS|METH_KEYWORDS, + PyDoc_STR("dump_traceback(file=sys.stderr, all_threads=True): " + "dump the traceback of the current thread, or of all threads " + "if all_threads is True, into file")}, +#ifdef FAULTHANDLER_LATER + {"dump_traceback_later", + (PyCFunction)faulthandler_dump_traceback_later, METH_VARARGS|METH_KEYWORDS, + PyDoc_STR("dump_traceback_later(timeout, repeat=False, file=sys.stderrn, exit=False):\n" + "dump the traceback of all threads in timeout seconds,\n" + "or each timeout seconds if repeat is True. If exit is True, " + "call _exit(1) which is not safe.")}, + {"cancel_dump_traceback_later", + (PyCFunction)faulthandler_cancel_dump_traceback_later_py, METH_NOARGS, + PyDoc_STR("cancel_dump_traceback_later():\ncancel the previous call " + "to dump_traceback_later().")}, +#endif + +#ifdef FAULTHANDLER_USER + {"register", + (PyCFunction)faulthandler_register_py, METH_VARARGS|METH_KEYWORDS, + PyDoc_STR("register(signum, file=sys.stderr, all_threads=True, chain=False): " + "register an handler for the signal 'signum': dump the " + "traceback of the current thread, or of all threads if " + "all_threads is True, into file")}, + {"unregister", + faulthandler_unregister_py, METH_VARARGS|METH_KEYWORDS, + PyDoc_STR("unregister(signum): unregister the handler of the signal " + "'signum' registered by register()")}, +#endif + + {"_read_null", faulthandler_read_null, METH_NOARGS, + PyDoc_STR("_read_null(): read from NULL, raise " + "a SIGSEGV or SIGBUS signal depending on the platform")}, + {"_sigsegv", faulthandler_sigsegv, METH_VARARGS, + PyDoc_STR("_sigsegv(release_gil=False): raise a SIGSEGV signal")}, + {"_sigabrt", faulthandler_sigabrt, METH_NOARGS, + PyDoc_STR("_sigabrt(): raise a SIGABRT signal")}, + {"_sigfpe", (PyCFunction)faulthandler_sigfpe, METH_NOARGS, + PyDoc_STR("_sigfpe(): raise a SIGFPE signal")}, + {"_raise_signal", (PyCFunction)faulthandler_raise_signal, METH_VARARGS, + PyDoc_STR("raise_signal(signum): raise a signal")}, + {"_fatal_error", faulthandler_fatal_error_py, METH_VARARGS, + PyDoc_STR("_fatal_error(message): call Py_FatalError(message)")}, + {"_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 */ +}; + +#if PY_MAJOR_VERSION >= 3 +static struct PyModuleDef module_def = { + PyModuleDef_HEAD_INIT, + "faulthandler", + module_doc, + 0, /* non negative size to be able to unload the module */ + module_methods, + NULL, + faulthandler_traverse, + NULL, + NULL +}; +#endif + + +PyMODINIT_FUNC +#if PY_MAJOR_VERSION >= 3 +PyInit_faulthandler(void) +#else +initfaulthandler(void) +#endif +{ + PyObject *m, *version; +#ifdef HAVE_SIGALTSTACK + int err; +#endif + +#if PY_MAJOR_VERSION >= 3 + m = PyModule_Create(&module_def); +#else + m = Py_InitModule3("faulthandler", module_methods, module_doc); +#endif + if (m == NULL) { +#if PY_MAJOR_VERSION >= 3 + return NULL; +#else + return; +#endif + } + +#ifdef MS_WINDOWS + /* RaiseException() flags */ + if (PyModule_AddIntConstant(m, "_EXCEPTION_NONCONTINUABLE", + EXCEPTION_NONCONTINUABLE)) + goto error; + if (PyModule_AddIntConstant(m, "_EXCEPTION_NONCONTINUABLE_EXCEPTION", + EXCEPTION_NONCONTINUABLE_EXCEPTION)) + goto error; +#endif + + +#ifdef HAVE_SIGALTSTACK + /* Try to allocate an alternate stack for faulthandler() signal handler to + * be able to allocate memory on the stack, even on a stack overflow. If it + * fails, ignore the error. */ + stack.ss_flags = 0; + /* bpo-21131: allocate dedicated stack of SIGSTKSZ*2 bytes, instead of just + SIGSTKSZ bytes. Calling the previous signal handler in faulthandler + signal handler uses more than SIGSTKSZ bytes of stack memory on some + platforms. */ + stack.ss_size = SIGSTKSZ * 2; + stack.ss_sp = PyMem_Malloc(stack.ss_size); + if (stack.ss_sp != NULL) { + err = sigaltstack(&stack, NULL); + if (err) { + PyMem_Free(stack.ss_sp); + stack.ss_sp = NULL; + } + } +#endif + + (void)Py_AtExit(faulthandler_unload); + + version = Py_BuildValue("(ii)", VERSION >> 8, VERSION & 0xFF); + if (version == NULL) + goto error; + PyModule_AddObject(m, "version", version); + +#if PY_MAJOR_VERSION >= 3 + version = PyUnicode_FromFormat("%i.%i", VERSION >> 8, VERSION & 0xFF); +#else + version = PyString_FromFormat("%i.%i", VERSION >> 8, VERSION & 0xFF); +#endif + if (version == NULL) + goto error; + PyModule_AddObject(m, "__version__", version); + +#if PY_MAJOR_VERSION >= 3 + return m; +#else + return; +#endif + +error: +#if PY_MAJOR_VERSION >= 3 + Py_DECREF(m); + return NULL; +#else + return; +#endif +} + +static void +faulthandler_unload(void) +{ +#ifdef FAULTHANDLER_USER + unsigned int signum; +#endif + +#ifdef FAULTHANDLER_LATER + /* later */ + alarm(0); + if (fault_alarm.header != NULL) { + free(fault_alarm.header); + fault_alarm.header = NULL; + } + /* Don't call Py_CLEAR(fault_alarm.file): this function is called too late, + by Py_AtExit(). Destroy a Python object here raise strange errors. */ +#endif +#ifdef FAULTHANDLER_USER + /* user */ + if (user_signals != NULL) { + for (signum=0; signum < NSIG; signum++) { + faulthandler_unregister(&user_signals[signum], signum); + /* Don't call Py_CLEAR(user->file): this function is called too late, + by Py_AtExit(). Destroy a Python object here raise strange + errors. */ + } + PyMem_Free(user_signals); + user_signals = NULL; + } +#endif + + /* don't release file: faulthandler_unload_fatal_error() + is called too late */ + fatal_error.file = NULL; + faulthandler_disable(); +#ifdef HAVE_SIGALTSTACK + if (stack.ss_sp != NULL) { + PyMem_Free(stack.ss_sp); + stack.ss_sp = NULL; + } +#endif +} diff --git a/contrib/deprecated/python/faulthandler/traceback.c b/contrib/deprecated/python/faulthandler/traceback.c new file mode 100644 index 0000000000..6bf727da65 --- /dev/null +++ b/contrib/deprecated/python/faulthandler/traceback.c @@ -0,0 +1,326 @@ +#ifdef __gnu_linux__ +# include <sys/prctl.h> +#endif + +#include "Python.h" +#include <frameobject.h> + +#if PY_MAJOR_VERSION >= 3 +# define PYSTRING_CHECK PyUnicode_Check +#else +# define PYSTRING_CHECK PyString_Check +#endif + +#define PUTS(fd, str) _Py_write_noraise(fd, str, (int)strlen(str)) +#define MAX_STRING_LENGTH 500 +#define MAX_FRAME_DEPTH 100 +#define MAX_NTHREADS 100 + +/* Write count bytes of buf into fd. + * + * On success, return the number of written bytes, it can be lower than count + * including 0. On error, set errno and return -1. + * + * When interrupted by a signal (write() fails with EINTR), retry the syscall + * without calling the Python signal handler. */ +Py_ssize_t +_Py_write_noraise(int fd, const char *buf, size_t count) +{ + Py_ssize_t res; + + do { +#ifdef MS_WINDOWS + assert(count < INT_MAX); + res = write(fd, buf, (int)count); +#else + res = write(fd, buf, count); +#endif + /* retry write() if it was interrupted by a signal */ + } while (res < 0 && errno == EINTR); + + return res; +} + +/* Reverse a string. For example, "abcd" becomes "dcba". + + This function is signal safe. */ + +void +reverse_string(char *text, const size_t len) +{ + char tmp; + size_t i, j; + if (len == 0) + return; + for (i=0, j=len-1; i < j; i++, j--) { + tmp = text[i]; + text[i] = text[j]; + text[j] = tmp; + } +} + +/* Format an integer in range [0; 999999999] to decimal, + and write it into the file fd. + + This function is signal safe. */ + +void +dump_decimal(int fd, int value) +{ + char buffer[10]; + int len; + if (value < 0 || 999999999 < value) + return; + len = 0; + do { + buffer[len] = '0' + (value % 10); + value /= 10; + len++; + } while (value); + reverse_string(buffer, len); + _Py_write_noraise(fd, buffer, len); +} + +/* Format an integer in range [0; 0xffffffff] to hexadecimal of 'width' digits, + and write it into the file fd. + + This function is signal safe. */ + +void +_Py_dump_hexadecimal(int fd, unsigned long value, size_t bytes) +{ + const char *hexdigits = "0123456789abcdef"; + size_t width = bytes * 2; + size_t len; + char buffer[sizeof(unsigned long) * 2 + 1]; + len = 0; + do { + buffer[len] = hexdigits[value & 15]; + value >>= 4; + len++; + } while (len < width || value); + reverse_string(buffer, len); + _Py_write_noraise(fd, buffer, len); +} + +/* Write an unicode object into the file fd using ascii+backslashreplace. + + This function is signal safe. */ + +static void +dump_ascii(int fd, PyObject *text) +{ + Py_ssize_t i, size; + int truncated; + unsigned long ch; +#if PY_MAJOR_VERSION >= 3 + Py_UNICODE *u; + + size = PyUnicode_GET_SIZE(text); + u = PyUnicode_AS_UNICODE(text); +#else + char *s; + + size = PyString_GET_SIZE(text); + s = PyString_AS_STRING(text); +#endif + + if (MAX_STRING_LENGTH < size) { + size = MAX_STRING_LENGTH; + truncated = 1; + } + else + truncated = 0; + +#if PY_MAJOR_VERSION >= 3 + for (i=0; i < size; i++, u++) { + ch = *u; + if (' ' <= ch && ch < 0x7f) { + /* printable ASCII character */ + char c = (char)ch; + _Py_write_noraise(fd, &c, 1); + } + else if (ch <= 0xff) { + PUTS(fd, "\\x"); + _Py_dump_hexadecimal(fd, ch, 1); + } + else +#ifdef Py_UNICODE_WIDE + if (ch <= 0xffff) +#endif + { + PUTS(fd, "\\u"); + _Py_dump_hexadecimal(fd, ch, 2); +#ifdef Py_UNICODE_WIDE + } + else { + PUTS(fd, "\\U"); + _Py_dump_hexadecimal(fd, ch, 4); +#endif + } + } +#else + for (i=0; i < size; i++, s++) { + ch = *s; + if (' ' <= ch && ch <= 126) { + /* printable ASCII character */ + _Py_write_noraise(fd, s, 1); + } + else { + PUTS(fd, "\\x"); + _Py_dump_hexadecimal(fd, ch, 1); + } + } +#endif + if (truncated) + PUTS(fd, "..."); +} + +/* Write a frame into the file fd: "File "xxx", line xxx in xxx". + + This function is signal safe. */ + +static void +dump_frame(int fd, PyFrameObject *frame) +{ + PyCodeObject *code; + int lineno; + + code = frame->f_code; + PUTS(fd, " File "); + if (code != NULL && code->co_filename != NULL + && PYSTRING_CHECK(code->co_filename)) + { + PUTS(fd, "\""); + dump_ascii(fd, code->co_filename); + PUTS(fd, "\""); + } else { + PUTS(fd, "???"); + } + +#if (PY_MAJOR_VERSION <= 2 && PY_MINOR_VERSION < 7) \ +|| (PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION < 2) + /* PyFrame_GetLineNumber() was introduced in Python 2.7.0 and 3.2.0 */ + lineno = PyCode_Addr2Line(code, frame->f_lasti); +#else + lineno = PyFrame_GetLineNumber(frame); +#endif + PUTS(fd, ", line "); + dump_decimal(fd, lineno); + PUTS(fd, " in "); + + if (code != NULL && code->co_name != NULL + && PYSTRING_CHECK(code->co_name)) + dump_ascii(fd, code->co_name); + else + PUTS(fd, "???"); + + PUTS(fd, "\n"); +} + +static void +dump_traceback(int fd, PyThreadState *tstate, int write_header) +{ + PyFrameObject *frame; + unsigned int depth; + + if (write_header) + PUTS(fd, "Stack (most recent call first):\n"); + + frame = _PyThreadState_GetFrame(tstate); + if (frame == NULL) + return; + + depth = 0; + while (frame != NULL) { + if (MAX_FRAME_DEPTH <= depth) { + PUTS(fd, " ...\n"); + break; + } + if (!PyFrame_Check(frame)) + break; + dump_frame(fd, frame); + frame = frame->f_back; + depth++; + } +} + +/* Dump the traceback of a Python thread into fd. Use write() to write the + traceback and retry if write() is interrupted by a signal (failed with + EINTR), but don't call the Python signal handler. + + The caller is responsible to call PyErr_CheckSignals() to call Python signal + handlers if signals were received. */ +void +_Py_DumpTraceback(int fd, PyThreadState *tstate) +{ + dump_traceback(fd, tstate, 1); +} + +/* Write the thread identifier into the file 'fd': "Current thread 0xHHHH:\" if + is_current is true, "Thread 0xHHHH:\n" otherwise. + + This function is signal safe. */ + +static void +write_thread_id(int fd, PyThreadState *tstate, int is_current) +{ + if (is_current) + PUTS(fd, "Current thread 0x"); + else + PUTS(fd, "Thread 0x"); + _Py_dump_hexadecimal(fd, (unsigned long)tstate->thread_id, sizeof(unsigned long)); + +#ifdef __gnu_linux__ + /* Linux only, get and print thread name */ + static char thread_name[16]; + if (0 == prctl(PR_GET_NAME, (unsigned long) thread_name, 0, 0, 0)) { + if (0 != strlen(thread_name)) { + PUTS(fd, " <"); + PUTS(fd, thread_name); + PUTS(fd, ">"); + } + } +#endif + + PUTS(fd, " (most recent call first):\n"); +} + +/* Dump the traceback of all Python threads into fd. Use write() to write the + traceback and retry if write() is interrupted by a signal (failed with + EINTR), but don't call the Python signal handler. + + The caller is responsible to call PyErr_CheckSignals() to call Python signal + handlers if signals were received. */ +const char* +_Py_DumpTracebackThreads(int fd, PyInterpreterState *interp, + PyThreadState *current_thread) +{ + PyThreadState *tstate; + unsigned int nthreads; + + /* Get the current interpreter from the current thread */ + tstate = PyInterpreterState_ThreadHead(interp); + if (tstate == NULL) + return "unable to get the thread head state"; + + /* Dump the traceback of each thread */ + tstate = PyInterpreterState_ThreadHead(interp); + nthreads = 0; + do + { + if (nthreads != 0) + PUTS(fd, "\n"); + if (nthreads >= MAX_NTHREADS) { + PUTS(fd, "...\n"); + break; + } + write_thread_id(fd, tstate, tstate == current_thread); + dump_traceback(fd, tstate, 0); + tstate = PyThreadState_Next(tstate); + nthreads++; + } while (tstate != NULL); + + return NULL; +} + diff --git a/contrib/deprecated/python/faulthandler/ya.make b/contrib/deprecated/python/faulthandler/ya.make new file mode 100644 index 0000000000..b7bd4f199f --- /dev/null +++ b/contrib/deprecated/python/faulthandler/ya.make @@ -0,0 +1,30 @@ +PY2_LIBRARY() + +VERSION(3.2) + +LICENSE(BSD-2-Clause) + +NO_COMPILER_WARNINGS() + +NO_LINT() + +CFLAGS( + -DUSE_SIGINFO +) + +SRCS( + faulthandler.c + traceback.c +) + +PY_REGISTER( + faulthandler +) + +RESOURCE_FILES( + PREFIX contrib/deprecated/python/faulthandler/ + .dist-info/METADATA + .dist-info/top_level.txt +) + +END() |