[pypy-commit] pypy py3k: hg merge py3k-faulthandler
arigo
pypy.commits at gmail.com
Sat Oct 1 06:21:10 EDT 2016
Author: Armin Rigo <arigo at tunes.org>
Branch: py3k
Changeset: r87497:0863ca6a99cc
Date: 2016-10-01 12:20 +0200
http://bitbucket.org/pypy/pypy/changeset/0863ca6a99cc/
Log: hg merge py3k-faulthandler
Merge the py3k version of faulthandler.
diff --git a/pypy/config/pypyoption.py b/pypy/config/pypyoption.py
--- a/pypy/config/pypyoption.py
+++ b/pypy/config/pypyoption.py
@@ -40,13 +40,14 @@
"binascii", "_multiprocessing", '_warnings', "_collections",
"_multibytecodec", "_continuation", "_cffi_backend",
"_csv", "_pypyjson", "_posixsubprocess", # "cppyy", "micronumpy"
- "faulthandler", "_jitlog",
+ "_jitlog",
])
from rpython.jit.backend import detect_cpu
try:
if detect_cpu.autodetect().startswith('x86'):
working_modules.add('_vmprof')
+ working_modules.add('faulthandler')
except detect_cpu.ProcessorAutodetectError:
pass
@@ -93,6 +94,7 @@
('objspace.usemodules.thread', True)],
'cpyext': [('objspace.usemodules.array', True)],
'cppyy': [('objspace.usemodules.cpyext', True)],
+ 'faulthandler': [('objspace.usemodules._vmprof', True)],
}
module_suggests = {
# the reason you want _rawffi is for ctypes, which
@@ -118,7 +120,8 @@
"_hashlib" : ["pypy.module._ssl.interp_ssl"],
"_minimal_curses": ["pypy.module._minimal_curses.fficurses"],
"_continuation": ["rpython.rlib.rstacklet"],
- "_vmprof" : ["pypy.module._vmprof.interp_vmprof"],
+ "_vmprof" : ["pypy.module._vmprof.interp_vmprof"],
+ "faulthandler" : ["pypy.module._vmprof.interp_vmprof"],
"_lzma" : ["pypy.module._lzma.interp_lzma"],
}
diff --git a/pypy/interpreter/app_main.py b/pypy/interpreter/app_main.py
--- a/pypy/interpreter/app_main.py
+++ b/pypy/interpreter/app_main.py
@@ -31,6 +31,7 @@
arg ...: arguments passed to program in sys.argv[1:]
PyPy options and arguments:
--info : print translation information about this PyPy executable
+-X faulthandler: attempt to display tracebacks when PyPy crashes
"""
# Missing vs CPython: PYTHONHOME
USAGE2 = """
@@ -518,6 +519,14 @@
sys._xoptions = dict(x.split('=', 1) if '=' in x else (x, True)
for x in options['_xoptions'])
+ if 'faulthandler' in sys.builtin_module_names:
+ if 'faulthandler' in sys._xoptions or os.getenv('PYTHONFAULTHANDLER'):
+ import faulthandler
+ try:
+ faulthandler.enable(2) # manually set to stderr
+ except ValueError:
+ pass # ignore "2 is not a valid file descriptor"
+
## if not we_are_translated():
## for key in sorted(options):
## print '%40s: %s' % (key, options[key])
diff --git a/pypy/module/faulthandler/__init__.py b/pypy/module/faulthandler/__init__.py
--- a/pypy/module/faulthandler/__init__.py
+++ b/pypy/module/faulthandler/__init__.py
@@ -1,22 +1,38 @@
+import sys
from pypy.interpreter.mixedmodule import MixedModule
+
class Module(MixedModule):
appleveldefs = {
}
interpleveldefs = {
- 'enable': 'interp_faulthandler.enable',
- 'disable': 'interp_faulthandler.disable',
- 'is_enabled': 'interp_faulthandler.is_enabled',
- 'register': 'interp_faulthandler.register',
+ 'enable': 'handler.enable',
+ 'disable': 'handler.disable',
+ 'is_enabled': 'handler.is_enabled',
+# 'register': 'interp_faulthandler.register',
+#
+ 'dump_traceback': 'handler.dump_traceback',
+#
+ '_read_null': 'handler.read_null',
+ '_sigsegv': 'handler.sigsegv',
+ '_sigfpe': 'handler.sigfpe',
+ '_sigabrt': 'handler.sigabrt',
+ '_stack_overflow': 'handler.stack_overflow',
+ }
- 'dump_traceback': 'interp_faulthandler.dump_traceback',
+ def setup_after_space_initialization(self):
+ """NOT_RPYTHON"""
+ if self.space.config.translation.thread:
+ self.extra_interpdef('dump_traceback_later',
+ 'handler.dump_traceback_later')
+ self.extra_interpdef('cancel_dump_traceback_later',
+ 'handler.cancel_dump_traceback_later')
+ if sys.platform != 'win32':
+ self.extra_interpdef('register', 'handler.register')
+ self.extra_interpdef('unregister', 'handler.unregister')
- '_read_null': 'interp_faulthandler.read_null',
- '_sigsegv': 'interp_faulthandler.sigsegv',
- '_sigfpe': 'interp_faulthandler.sigfpe',
- '_sigabrt': 'interp_faulthandler.sigabrt',
- #'_sigbus': 'interp_faulthandler.sigbus',
- #'_sigill': 'interp_faulthandler.sigill',
- '_fatal_error': 'interp_faulthandler.fatal_error',
- }
+ def shutdown(self, space):
+ from pypy.module.faulthandler import handler
+ handler.finish(space)
+ MixedModule.shutdown(self, space)
diff --git a/pypy/module/faulthandler/cintf.py b/pypy/module/faulthandler/cintf.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/faulthandler/cintf.py
@@ -0,0 +1,99 @@
+import py
+from rpython.rtyper.lltypesystem import lltype, llmemory, rffi, rstr
+from rpython.translator import cdir
+from rpython.translator.tool.cbuild import ExternalCompilationInfo
+
+
+cwd = py.path.local(__file__).dirpath()
+eci = ExternalCompilationInfo(
+ includes=[cwd.join('faulthandler.h')],
+ include_dirs=[str(cwd), cdir],
+ separate_module_files=[cwd.join('faulthandler.c')])
+
+eci_later = eci.merge(ExternalCompilationInfo(
+ pre_include_bits=['#define PYPY_FAULTHANDLER_LATER\n']))
+eci_user = eci.merge(ExternalCompilationInfo(
+ pre_include_bits=['#define PYPY_FAULTHANDLER_USER\n']))
+
+def direct_llexternal(*args, **kwargs):
+ kwargs.setdefault('_nowrapper', True)
+ kwargs.setdefault('compilation_info', eci)
+ return rffi.llexternal(*args, **kwargs)
+
+
+DUMP_CALLBACK = lltype.Ptr(lltype.FuncType(
+ [rffi.INT, rffi.SIGNEDP, lltype.Signed], lltype.Void))
+
+pypy_faulthandler_setup = direct_llexternal(
+ 'pypy_faulthandler_setup', [DUMP_CALLBACK], rffi.CCHARP)
+
+pypy_faulthandler_teardown = direct_llexternal(
+ 'pypy_faulthandler_teardown', [], lltype.Void)
+
+pypy_faulthandler_enable = direct_llexternal(
+ 'pypy_faulthandler_enable', [rffi.INT, rffi.INT], rffi.CCHARP)
+
+pypy_faulthandler_disable = direct_llexternal(
+ 'pypy_faulthandler_disable', [], lltype.Void)
+
+pypy_faulthandler_is_enabled = direct_llexternal(
+ 'pypy_faulthandler_is_enabled', [], rffi.INT)
+
+pypy_faulthandler_write = direct_llexternal(
+ 'pypy_faulthandler_write', [rffi.INT, rffi.CCHARP], lltype.Void)
+
+pypy_faulthandler_write_int = direct_llexternal(
+ 'pypy_faulthandler_write_int', [rffi.INT, lltype.Signed], lltype.Void)
+
+pypy_faulthandler_dump_traceback = direct_llexternal(
+ 'pypy_faulthandler_dump_traceback',
+ [rffi.INT, rffi.INT, llmemory.Address], lltype.Void)
+
+pypy_faulthandler_dump_traceback_later = direct_llexternal(
+ 'pypy_faulthandler_dump_traceback_later',
+ [rffi.LONGLONG, rffi.INT, rffi.INT, rffi.INT], rffi.CCHARP,
+ compilation_info=eci_later)
+
+pypy_faulthandler_cancel_dump_traceback_later = direct_llexternal(
+ 'pypy_faulthandler_cancel_dump_traceback_later', [], lltype.Void)
+
+pypy_faulthandler_check_signum = direct_llexternal(
+ 'pypy_faulthandler_check_signum',
+ [rffi.LONG], rffi.INT,
+ compilation_info=eci_user)
+
+pypy_faulthandler_register = direct_llexternal(
+ 'pypy_faulthandler_register',
+ [rffi.INT, rffi.INT, rffi.INT, rffi.INT], rffi.CCHARP,
+ compilation_info=eci_user)
+
+pypy_faulthandler_unregister = direct_llexternal(
+ 'pypy_faulthandler_unregister',
+ [rffi.INT], rffi.INT,
+ compilation_info=eci_user)
+
+
+# for tests...
+
+pypy_faulthandler_read_null = direct_llexternal(
+ 'pypy_faulthandler_read_null', [], lltype.Void)
+
+pypy_faulthandler_read_null_releasegil = direct_llexternal(
+ 'pypy_faulthandler_read_null', [], lltype.Void,
+ _nowrapper=False, releasegil=True)
+
+pypy_faulthandler_sigsegv = direct_llexternal(
+ 'pypy_faulthandler_sigsegv', [], lltype.Void)
+
+pypy_faulthandler_sigsegv_releasegil = direct_llexternal(
+ 'pypy_faulthandler_sigsegv', [], lltype.Void,
+ _nowrapper=False, releasegil=True)
+
+pypy_faulthandler_sigfpe = direct_llexternal(
+ 'pypy_faulthandler_sigfpe', [], lltype.Void)
+
+pypy_faulthandler_sigabrt = direct_llexternal(
+ 'pypy_faulthandler_sigabrt', [], lltype.Void)
+
+pypy_faulthandler_stackoverflow = direct_llexternal(
+ 'pypy_faulthandler_stackoverflow', [lltype.Float], lltype.Float)
diff --git a/pypy/module/faulthandler/dumper.py b/pypy/module/faulthandler/dumper.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/faulthandler/dumper.py
@@ -0,0 +1,54 @@
+from rpython.rtyper.lltypesystem import lltype, rffi
+from rpython.rlib import rgc
+from rpython.rlib.rvmprof import traceback
+
+from pypy.interpreter.pycode import PyCode
+from pypy.module.faulthandler.cintf import pypy_faulthandler_write
+from pypy.module.faulthandler.cintf import pypy_faulthandler_write_int
+
+
+MAX_STRING_LENGTH = 500
+
+global_buf = lltype.malloc(rffi.CCHARP.TO, MAX_STRING_LENGTH, flavor='raw',
+ immortal=True, zero=True)
+
+def _dump(fd, s):
+ assert isinstance(s, str)
+ l = len(s)
+ if l >= MAX_STRING_LENGTH:
+ l = MAX_STRING_LENGTH - 1
+ i = 0
+ while i < l:
+ global_buf[i] = s[i]
+ i += 1
+ global_buf[l] = '\x00'
+ pypy_faulthandler_write(fd, global_buf)
+
+def _dump_int(fd, i):
+ pypy_faulthandler_write_int(fd, i)
+
+
+def dump_code(pycode, loc, fd):
+ if pycode is None:
+ _dump(fd, " File ???")
+ else:
+ _dump(fd, ' File "')
+ _dump(fd, pycode.co_filename)
+ _dump(fd, '" in ')
+ _dump(fd, pycode.co_name)
+ _dump(fd, ", from line ")
+ _dump_int(fd, pycode.co_firstlineno)
+ if loc == traceback.LOC_JITTED:
+ _dump(fd, " [jitted]")
+ elif loc == traceback.LOC_JITTED_INLINED:
+ _dump(fd, " [jit inlined]")
+ _dump(fd, "\n")
+
+
+ at rgc.no_collect
+def _dump_callback(fd, array_p, array_length):
+ """We are as careful as we can reasonably be here (i.e. not 100%,
+ but hopefully close enough). In particular, this is written as
+ RPython but shouldn't allocate anything.
+ """
+ traceback.walk_traceback(PyCode, dump_code, fd, array_p, array_length)
diff --git a/pypy/module/faulthandler/faulthandler.c b/pypy/module/faulthandler/faulthandler.c
--- a/pypy/module/faulthandler/faulthandler.c
+++ b/pypy/module/faulthandler/faulthandler.c
@@ -1,20 +1,625 @@
+#include "faulthandler.h"
#include <stdlib.h>
-#include "faulthandler.h"
+#include <stdio.h>
+#include <signal.h>
+#include <assert.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/resource.h>
+#include <math.h>
-int
-pypy_faulthandler_read_null(void)
+#ifdef RPYTHON_LL2CTYPES
+# include "../../../rpython/rlib/rvmprof/src/rvmprof.h"
+#else
+# include "common_header.h"
+# include "structdef.h"
+# include "rvmprof.h"
+#endif
+#include "src/threadlocal.h"
+
+#define MAX_FRAME_DEPTH 100
+#define FRAME_DEPTH_N RVMPROF_TRACEBACK_ESTIMATE_N(MAX_FRAME_DEPTH)
+
+
+typedef struct sigaction _Py_sighandler_t;
+
+typedef struct {
+ const int signum;
+ volatile int enabled;
+ const char* name;
+ _Py_sighandler_t previous;
+} fault_handler_t;
+
+static struct {
+ int initialized;
+ int enabled;
+ volatile int fd, all_threads;
+ volatile pypy_faulthandler_cb_t dump_traceback;
+} fatal_error;
+
+static stack_t stack;
+
+
+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 int faulthandler_nsignals =
+ sizeof(faulthandler_handlers) / sizeof(fault_handler_t);
+
+RPY_EXTERN
+void pypy_faulthandler_write(int fd, const char *str)
{
- volatile int *x;
- volatile int y;
-
- x = NULL;
- y = *x;
- return y;
+ (void)write(fd, str, strlen(str));
}
-void
-pypy_faulthandler_sigsegv(void)
+RPY_EXTERN
+void pypy_faulthandler_write_int(int fd, long value)
{
+ char buf[48];
+ sprintf(buf, "%ld", value);
+ pypy_faulthandler_write(fd, buf);
+}
+
+
+RPY_EXTERN
+void pypy_faulthandler_dump_traceback(int fd, int all_threads,
+ void *ucontext)
+{
+ pypy_faulthandler_cb_t fn;
+ intptr_t array_p[FRAME_DEPTH_N], array_length;
+
+ fn = fatal_error.dump_traceback;
+ if (!fn)
+ return;
+
+#ifndef RPYTHON_LL2CTYPES
+ if (all_threads && _RPython_ThreadLocals_AcquireTimeout(10000) == 0) {
+ /* This is known not to be perfectly safe against segfaults if we
+ don't hold the GIL ourselves. Too bad. I suspect that CPython
+ has issues there too.
+ */
+ struct pypy_threadlocal_s *my, *p;
+ int blankline = 0;
+ char buf[40];
+
+ my = (struct pypy_threadlocal_s *)_RPy_ThreadLocals_Get();
+ p = _RPython_ThreadLocals_Head();
+ p = _RPython_ThreadLocals_Enum(p);
+ while (p != NULL) {
+ if (blankline)
+ pypy_faulthandler_write(fd, "\n");
+ blankline = 1;
+
+ pypy_faulthandler_write(fd, my == p ? "Current thread" : "Thread");
+ sprintf(buf, " 0x%lx", (unsigned long)p->thread_ident);
+ pypy_faulthandler_write(fd, buf);
+ pypy_faulthandler_write(fd, " (most recent call first):\n");
+
+ array_length = vmprof_get_traceback(p->vmprof_tl_stack,
+ my == p ? ucontext : NULL,
+ array_p, FRAME_DEPTH_N);
+ fn(fd, array_p, array_length);
+
+ p = _RPython_ThreadLocals_Enum(p);
+ }
+ _RPython_ThreadLocals_Release();
+ }
+ else {
+ pypy_faulthandler_write(fd, "Stack (most recent call first):\n");
+ array_length = vmprof_get_traceback(NULL, ucontext,
+ array_p, FRAME_DEPTH_N);
+ fn(fd, array_p, array_length);
+ }
+#else
+ pypy_faulthandler_write(fd, "(no traceback when untranslated)\n");
+#endif
+}
+
+static void
+faulthandler_dump_traceback(int fd, int all_threads, void *ucontext)
+{
+ static volatile int reentrant = 0;
+
+ if (reentrant)
+ return;
+ reentrant = 1;
+ pypy_faulthandler_dump_traceback(fd, all_threads, ucontext);
+ reentrant = 0;
+}
+
+
+/************************************************************/
+
+
+#ifdef PYPY_FAULTHANDLER_LATER
+#include "src/thread.h"
+static struct {
+ int fd;
+ long long microseconds;
+ int repeat, exit;
+ /* The main thread always holds this lock. It is only released when
+ faulthandler_thread() is interrupted before this thread exits, or at
+ Python exit. */
+ struct RPyOpaque_ThreadLock cancel_event;
+ /* released by child thread when joined */
+ struct RPyOpaque_ThreadLock running;
+} thread_later;
+
+static void faulthandler_thread(void)
+{
+#ifndef _WIN32
+ /* we don't want to receive any signal */
+ sigset_t set;
+ sigfillset(&set);
+ pthread_sigmask(SIG_SETMASK, &set, NULL);
+#endif
+
+ RPyLockStatus st;
+ char buf[64];
+ unsigned long hour, minutes, seconds, fraction;
+ long long t;
+
+ do {
+ st = RPyThreadAcquireLockTimed(&thread_later.cancel_event,
+ thread_later.microseconds, 0);
+ if (st == RPY_LOCK_ACQUIRED) {
+ RPyThreadReleaseLock(&thread_later.cancel_event);
+ break;
+ }
+ /* Timeout => dump traceback */
+ assert(st == RPY_LOCK_FAILURE);
+
+ /* getting to know which thread holds the GIL is not as simple
+ * as in CPython, so for now we don't */
+
+ t = thread_later.microseconds;
+ fraction = (unsigned long)(t % 1000000);
+ t /= 1000000;
+ seconds = (unsigned long)(t % 60);
+ t /= 60;
+ minutes = (unsigned long)(t % 60);
+ t /= 60;
+ hour = (unsigned long)t;
+ if (fraction == 0)
+ sprintf(buf, "Timeout (%lu:%02lu:%02lu)!\n",
+ hour, minutes, seconds);
+ else
+ sprintf(buf, "Timeout (%lu:%02lu:%02lu.%06lu)!\n",
+ hour, minutes, seconds, fraction);
+
+ pypy_faulthandler_write(thread_later.fd, buf);
+ pypy_faulthandler_dump_traceback(thread_later.fd, 1, NULL);
+
+ if (thread_later.exit)
+ _exit(1);
+ } while (thread_later.repeat);
+
+ /* The only way out */
+ RPyThreadReleaseLock(&thread_later.running);
+}
+
+RPY_EXTERN
+char *pypy_faulthandler_dump_traceback_later(long long microseconds, int repeat,
+ int fd, int exit)
+{
+ pypy_faulthandler_cancel_dump_traceback_later();
+
+ thread_later.fd = fd;
+ thread_later.microseconds = microseconds;
+ thread_later.repeat = repeat;
+ thread_later.exit = exit;
+
+ RPyThreadAcquireLock(&thread_later.running, 1);
+
+ if (RPyThreadStart(&faulthandler_thread) == -1) {
+ RPyThreadReleaseLock(&thread_later.running);
+ return "unable to start watchdog thread";
+ }
+ return NULL;
+}
+#endif /* PYPY_FAULTHANDLER_LATER */
+
+RPY_EXTERN
+void pypy_faulthandler_cancel_dump_traceback_later(void)
+{
+#ifdef PYPY_FAULTHANDLER_LATER
+ /* Notify cancellation */
+ RPyThreadReleaseLock(&thread_later.cancel_event);
+
+ /* Wait for thread to join (or does nothing if no thread is running) */
+ RPyThreadAcquireLock(&thread_later.running, 1);
+ RPyThreadReleaseLock(&thread_later.running);
+
+ /* The main thread should always hold the cancel_event lock */
+ RPyThreadAcquireLock(&thread_later.cancel_event, 1);
+#endif /* PYPY_FAULTHANDLER_LATER */
+}
+
+
+/************************************************************/
+
+
+#ifdef PYPY_FAULTHANDLER_USER
+typedef struct {
+ int enabled;
+ int fd;
+ int all_threads;
+ int chain;
+ _Py_sighandler_t previous;
+} user_signal_t;
+
+static user_signal_t *user_signals;
+
+#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, siginfo_t *info, void *ucontext);
+
+static int
+faulthandler_register(int signum, int chain, _Py_sighandler_t *p_previous)
+{
+ 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 | SA_SIGINFO;
+ if (chain) {
+ /* do not prevent the signal from being received from within its
+ own signal handler */
+ action.sa_flags = SA_NODEFER;
+ }
+ if (stack.ss_sp != NULL) {
+ /* Call the signal handler on an alternate signal stack
+ provided by sigaltstack() */
+ action.sa_flags |= SA_ONSTACK;
+ }
+ return sigaction(signum, &action, p_previous);
+}
+
+static void faulthandler_user(int signum, siginfo_t *info, void *ucontext)
+{
+ int save_errno;
+ user_signal_t *user = &user_signals[signum];
+
+ if (!user->enabled)
+ return;
+
+ save_errno = errno;
+ faulthandler_dump_traceback(user->fd, user->all_threads, ucontext);
+
+ 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;
+}
+
+RPY_EXTERN
+int pypy_faulthandler_check_signum(long signum)
+{
+ unsigned int i;
+
+ for (i = 0; i < faulthandler_nsignals; i++) {
+ if (faulthandler_handlers[i].signum == signum) {
+ return -1;
+ }
+ }
+ if (signum < 1 || NSIG <= signum) {
+ return -2;
+ }
+ return 0;
+}
+
+RPY_EXTERN
+char *pypy_faulthandler_register(int signum, int fd, int all_threads, int chain)
+{
+ user_signal_t *user;
+ _Py_sighandler_t previous;
+ int err;
+
+ if (user_signals == NULL) {
+ user_signals = malloc(NSIG * sizeof(user_signal_t));
+ if (user_signals == NULL)
+ return "out of memory";
+ memset(user_signals, 0, NSIG * sizeof(user_signal_t));
+ }
+
+ user = &user_signals[signum];
+ user->fd = fd;
+ user->all_threads = all_threads;
+ user->chain = chain;
+
+ if (!user->enabled) {
+ err = faulthandler_register(signum, chain, &previous);
+ if (err)
+ return strerror(errno);
+
+ user->previous = previous;
+ user->enabled = 1;
+ }
+ return NULL;
+}
+
+RPY_EXTERN
+int pypy_faulthandler_unregister(int signum)
+{
+ user_signal_t *user;
+
+ if (user_signals == NULL)
+ return 0;
+
+ user = &user_signals[signum];
+ if (user->enabled) {
+ user->enabled = 0;
+ (void)sigaction(signum, &user->previous, NULL);
+ user->fd = -1;
+ return 1;
+ }
+ else
+ return 0;
+}
+#endif /* PYPY_FAULTHANDLER_USER */
+
+
+/************************************************************/
+
+
+/* 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, siginfo_t *info, void *ucontext)
+{
+ int fd = fatal_error.fd;
+ int i;
+ fault_handler_t *handler = NULL;
+ int save_errno = errno;
+
+ for (i = 0; i < faulthandler_nsignals; i++) {
+ handler = &faulthandler_handlers[i];
+ if (handler->signum == signum)
+ break;
+ }
+ /* If not found, we use the SIGSEGV handler (the last one in the list) */
+
+ /* restore the previous handler */
+ if (handler->enabled) {
+ (void)sigaction(signum, &handler->previous, NULL);
+ handler->enabled = 0;
+ }
+
+ pypy_faulthandler_write(fd, "Fatal Python error: ");
+ pypy_faulthandler_write(fd, handler->name);
+ pypy_faulthandler_write(fd, "\n\n");
+
+ faulthandler_dump_traceback(fd, fatal_error.all_threads, ucontext);
+
+ 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);
+}
+
+
+RPY_EXTERN
+char *pypy_faulthandler_setup(pypy_faulthandler_cb_t dump_callback)
+{
+ if (fatal_error.initialized)
+ return NULL;
+ assert(!fatal_error.enabled);
+ fatal_error.dump_traceback = dump_callback;
+
+ /* 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;
+ stack.ss_size = SIGSTKSZ;
+ stack.ss_sp = malloc(stack.ss_size);
+ if (stack.ss_sp != NULL) {
+ int err = sigaltstack(&stack, NULL);
+ if (err) {
+ free(stack.ss_sp);
+ stack.ss_sp = NULL;
+ }
+ }
+
+#ifdef PYPY_FAULTHANDLER_LATER
+ if (!RPyThreadLockInit(&thread_later.cancel_event) ||
+ !RPyThreadLockInit(&thread_later.running))
+ return "failed to initialize locks";
+ RPyThreadAcquireLock(&thread_later.cancel_event, 1);
+#endif
+
+ fatal_error.fd = -1;
+ fatal_error.initialized = 1;
+
+ return NULL;
+}
+
+RPY_EXTERN
+void pypy_faulthandler_teardown(void)
+{
+ if (fatal_error.initialized) {
+
+#ifdef PYPY_FAULTHANDLER_LATER
+ pypy_faulthandler_cancel_dump_traceback_later();
+ RPyThreadReleaseLock(&thread_later.cancel_event);
+ RPyOpaqueDealloc_ThreadLock(&thread_later.running);
+ RPyOpaqueDealloc_ThreadLock(&thread_later.cancel_event);
+#endif
+
+#ifdef PYPY_FAULTHANDLER_USER
+ int signum;
+ for (signum = 0; signum < NSIG; signum++)
+ pypy_faulthandler_unregister(signum);
+ /* don't free 'user_signals', the gain is very minor and it can
+ lead to rare crashes if another thread is still busy */
+#endif
+
+ pypy_faulthandler_disable();
+ fatal_error.initialized = 0;
+ if (stack.ss_sp) {
+ stack.ss_flags = SS_DISABLE;
+ sigaltstack(&stack, NULL);
+ free(stack.ss_sp);
+ stack.ss_sp = NULL;
+ }
+ }
+}
+
+RPY_EXTERN
+char *pypy_faulthandler_enable(int fd, int all_threads)
+{
+ /* Install the handler for fatal signals, faulthandler_fatal_error(). */
+ int i;
+ fatal_error.fd = fd;
+ fatal_error.all_threads = all_threads;
+
+ if (!fatal_error.enabled) {
+ fatal_error.enabled = 1;
+
+ for (i = 0; i < faulthandler_nsignals; i++) {
+ int err;
+ struct sigaction action;
+ fault_handler_t *handler = &faulthandler_handlers[i];
+
+ action.sa_sigaction = 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 | SA_SIGINFO;
+ if (stack.ss_sp != NULL) {
+ /* Call the signal handler on an alternate signal stack
+ provided by sigaltstack() */
+ action.sa_flags |= SA_ONSTACK;
+ }
+ err = sigaction(handler->signum, &action, &handler->previous);
+ if (err) {
+ return strerror(errno);
+ }
+ handler->enabled = 1;
+ }
+ }
+ return NULL;
+}
+
+RPY_EXTERN
+void pypy_faulthandler_disable(void)
+{
+ int i;
+ if (fatal_error.enabled) {
+ fatal_error.enabled = 0;
+ for (i = 0; i < faulthandler_nsignals; i++) {
+ fault_handler_t *handler = &faulthandler_handlers[i];
+ if (!handler->enabled)
+ continue;
+ (void)sigaction(handler->signum, &handler->previous, NULL);
+ handler->enabled = 0;
+ }
+ }
+ fatal_error.fd = -1;
+}
+
+RPY_EXTERN
+int pypy_faulthandler_is_enabled(void)
+{
+ return fatal_error.enabled;
+}
+
+
+/************************************************************/
+
+
+/* for tests... */
+
+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
+
+#ifndef MS_WINDOWS
+ 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
+}
+
+RPY_EXTERN
+int pypy_faulthandler_read_null(void)
+{
+ int *volatile x;
+
+ faulthandler_suppress_crash_report();
+ x = NULL;
+ return *x;
+}
+
+RPY_EXTERN
+void pypy_faulthandler_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
@@ -34,12 +639,13 @@
#endif
}
-int
-pypy_faulthandler_sigfpe(void)
+RPY_EXTERN
+int pypy_faulthandler_sigfpe(void)
{
/* 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. */
@@ -49,29 +655,25 @@
return z;
}
-void
-pypy_faulthandler_sigabrt()
+RPY_EXTERN
+void pypy_faulthandler_sigabrt(void)
{
-#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
+ faulthandler_suppress_crash_report();
abort();
}
-#ifdef SIGBUS
-void
-pypy_faulthandler_sigbus(void)
+static double fh_stack_overflow(double levels)
{
- raise(SIGBUS);
+ if (levels > 2.5) {
+ return (sqrt(fh_stack_overflow(levels - 1.0))
+ + fh_stack_overflow(levels * 1e-10));
+ }
+ return 1e100 + levels;
}
-#endif
-#ifdef SIGILL
-void
-pypy_faulthandler_sigill(void)
+RPY_EXTERN
+double pypy_faulthandler_stackoverflow(double levels)
{
- raise(SIGILL);
+ faulthandler_suppress_crash_report();
+ return fh_stack_overflow(levels);
}
-#endif
diff --git a/pypy/module/faulthandler/faulthandler.h b/pypy/module/faulthandler/faulthandler.h
--- a/pypy/module/faulthandler/faulthandler.h
+++ b/pypy/module/faulthandler/faulthandler.h
@@ -1,19 +1,40 @@
#ifndef PYPY_FAULTHANDLER_H
#define PYPY_FAULTHANDLER_H
-#include <signal.h>
#include "src/precommondefs.h"
+#include <stdint.h>
+
+
+typedef void (*pypy_faulthandler_cb_t)(int fd, intptr_t *array_p,
+ intptr_t length);
+
+RPY_EXTERN char *pypy_faulthandler_setup(pypy_faulthandler_cb_t dump_callback);
+RPY_EXTERN void pypy_faulthandler_teardown(void);
+
+RPY_EXTERN char *pypy_faulthandler_enable(int fd, int all_threads);
+RPY_EXTERN void pypy_faulthandler_disable(void);
+RPY_EXTERN int pypy_faulthandler_is_enabled(void);
+
+RPY_EXTERN void pypy_faulthandler_write(int fd, const char *str);
+RPY_EXTERN void pypy_faulthandler_write_int(int fd, long value);
+
+RPY_EXTERN void pypy_faulthandler_dump_traceback(int fd, int all_threads,
+ void *ucontext);
+
+RPY_EXTERN char *pypy_faulthandler_dump_traceback_later(
+ long long microseconds, int repeat, int fd, int exit);
+RPY_EXTERN void pypy_faulthandler_cancel_dump_traceback_later(void);
+
+RPY_EXTERN int pypy_faulthandler_check_signum(long signum);
+RPY_EXTERN char *pypy_faulthandler_register(int, int, int, int);
+RPY_EXTERN int pypy_faulthandler_unregister(int signum);
+
RPY_EXTERN int pypy_faulthandler_read_null(void);
RPY_EXTERN void pypy_faulthandler_sigsegv(void);
RPY_EXTERN int pypy_faulthandler_sigfpe(void);
-RPY_EXTERN void pypy_faulthandler_sigabrt();
-#ifdef SIGBUS
-RPY_EXTERN void pypy_faulthandler_sigbus(void);
-#endif
+RPY_EXTERN void pypy_faulthandler_sigabrt(void);
+RPY_EXTERN double pypy_faulthandler_stackoverflow(double);
-#ifdef SIGILL
-RPY_EXTERN void pypy_faulthandler_sigill(void);
-#endif
#endif /* PYPY_FAULTHANDLER_H */
diff --git a/pypy/module/faulthandler/handler.py b/pypy/module/faulthandler/handler.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/faulthandler/handler.py
@@ -0,0 +1,209 @@
+import os
+from rpython.rtyper.lltypesystem import lltype, llmemory, rffi
+from rpython.rlib.rposix import is_valid_fd
+from rpython.rlib.rarithmetic import widen, ovfcheck_float_to_longlong
+from rpython.rlib.objectmodel import keepalive_until_here
+from rpython.rtyper.annlowlevel import llhelper
+
+from pypy.interpreter.error import OperationError, oefmt
+from pypy.interpreter.error import exception_from_saved_errno
+from pypy.interpreter.gateway import unwrap_spec
+from pypy.module.faulthandler import cintf, dumper
+
+
+class Handler(object):
+ def __init__(self, space):
+ "NOT_RPYTHON"
+ self.space = space
+ self._cleanup_()
+
+ def _cleanup_(self):
+ self.fatal_error_w_file = None
+ self.dump_traceback_later_w_file = None
+ self.user_w_files = None
+
+ def check_err(self, p_err):
+ if p_err:
+ raise oefmt(self.space.w_RuntimeError, 'faulthandler: %8',
+ rffi.charp2str(p_err))
+
+ def get_fileno_and_file(self, w_file):
+ space = self.space
+ if space.is_none(w_file):
+ w_file = space.sys.get('stderr')
+ if space.is_none(w_file):
+ raise oefmt(space.w_RuntimeError, "sys.stderr is None")
+ elif space.isinstance_w(w_file, space.w_int):
+ fd = space.c_int_w(w_file)
+ if fd < 0 or not is_valid_fd(fd):
+ raise oefmt(space.w_ValueError,
+ "file is not a valid file descriptor")
+ return fd, None
+
+ fd = space.c_int_w(space.call_method(w_file, 'fileno'))
+ try:
+ space.call_method(w_file, 'flush')
+ except OperationError as e:
+ if e.async(space):
+ raise
+ pass # ignore flush() error
+ return fd, w_file
+
+ def setup(self):
+ dump_callback = llhelper(cintf.DUMP_CALLBACK, dumper._dump_callback)
+ self.check_err(cintf.pypy_faulthandler_setup(dump_callback))
+
+ def enable(self, w_file, all_threads):
+ fileno, w_file = self.get_fileno_and_file(w_file)
+ self.setup()
+ self.fatal_error_w_file = w_file
+ self.check_err(cintf.pypy_faulthandler_enable(
+ rffi.cast(rffi.INT, fileno),
+ rffi.cast(rffi.INT, all_threads)))
+
+ def disable(self):
+ cintf.pypy_faulthandler_disable()
+ self.fatal_error_w_file = None
+
+ def is_enabled(self):
+ return bool(widen(cintf.pypy_faulthandler_is_enabled()))
+
+ def dump_traceback(self, w_file, all_threads):
+ fileno, w_file = self.get_fileno_and_file(w_file)
+ self.setup()
+ cintf.pypy_faulthandler_dump_traceback(
+ rffi.cast(rffi.INT, fileno),
+ rffi.cast(rffi.INT, all_threads),
+ llmemory.NULL)
+ keepalive_until_here(w_file)
+
+ def dump_traceback_later(self, timeout, repeat, w_file, exit):
+ space = self.space
+ timeout *= 1e6
+ try:
+ microseconds = ovfcheck_float_to_longlong(timeout)
+ except OverflowError:
+ raise oefmt(space.w_OverflowError, "timeout value is too large")
+ if microseconds <= 0:
+ raise oefmt(space.w_ValueError, "timeout must be greater than 0")
+ fileno, w_file = self.get_fileno_and_file(w_file)
+ self.setup()
+ self.check_err(cintf.pypy_faulthandler_dump_traceback_later(
+ rffi.cast(rffi.LONGLONG, microseconds),
+ rffi.cast(rffi.INT, repeat),
+ rffi.cast(rffi.INT, fileno),
+ rffi.cast(rffi.INT, exit)))
+ self.dump_traceback_later_w_file = w_file
+
+ def cancel_dump_traceback_later(self):
+ cintf.pypy_faulthandler_cancel_dump_traceback_later()
+ self.dump_traceback_later_w_file = None
+
+ def check_signum(self, signum):
+ err = rffi.cast(lltype.Signed,
+ cintf.pypy_faulthandler_check_signum(signum))
+ if err < 0:
+ space = self.space
+ if err == -1:
+ raise oefmt(space.w_RuntimeError,
+ "signal %d cannot be registered, "
+ "use enable() instead", signum)
+ else:
+ raise oefmt(space.w_ValueError, "signal number out of range")
+
+ def register(self, signum, w_file, all_threads, chain):
+ self.check_signum(signum)
+ fileno, w_file = self.get_fileno_and_file(w_file)
+ self.setup()
+ self.check_err(cintf.pypy_faulthandler_register(
+ rffi.cast(rffi.INT, signum),
+ rffi.cast(rffi.INT, fileno),
+ rffi.cast(rffi.INT, all_threads),
+ rffi.cast(rffi.INT, chain)))
+ if self.user_w_files is None:
+ self.user_w_files = {}
+ self.user_w_files[signum] = w_file
+
+ def unregister(self, signum):
+ self.check_signum(signum)
+ change = cintf.pypy_faulthandler_unregister(
+ rffi.cast(rffi.INT, signum))
+ if self.user_w_files is not None:
+ self.user_w_files.pop(signum, None)
+ return rffi.cast(lltype.Signed, change) == 1
+
+ def finish(self):
+ cintf.pypy_faulthandler_teardown()
+ self._cleanup_()
+
+
+def finish(space):
+ "Finalize the faulthandler logic (called from shutdown())"
+ space.fromcache(Handler).finish()
+
+
+ at unwrap_spec(all_threads=int)
+def enable(space, w_file=None, all_threads=0):
+ "enable(file=sys.stderr, all_threads=True): enable the fault handler"
+ space.fromcache(Handler).enable(w_file, all_threads)
+
+def disable(space):
+ "disable(): disable the fault handler"
+ space.fromcache(Handler).disable()
+
+def is_enabled(space):
+ "is_enabled()->bool: check if the handler is enabled"
+ return space.wrap(space.fromcache(Handler).is_enabled())
+
+ at unwrap_spec(all_threads=int)
+def dump_traceback(space, w_file=None, all_threads=0):
+ """dump the traceback of the current thread into file
+ including all threads if all_threads is True"""
+ space.fromcache(Handler).dump_traceback(w_file, all_threads)
+
+ at unwrap_spec(timeout=float, repeat=int, exit=int)
+def dump_traceback_later(space, timeout, repeat=0, w_file=None, exit=0):
+ """dump the traceback of all threads in timeout seconds,
+ or each timeout seconds if repeat is True. If exit is True,
+ call _exit(1) which is not safe."""
+ space.fromcache(Handler).dump_traceback_later(timeout, repeat, w_file, exit)
+
+def cancel_dump_traceback_later(space):
+ """cancel the previous call to dump_traceback_later()."""
+ space.fromcache(Handler).cancel_dump_traceback_later()
+
+ at unwrap_spec(signum=int, all_threads=int, chain=int)
+def register(space, signum, w_file=None, all_threads=1, chain=0):
+ space.fromcache(Handler).register(signum, w_file, all_threads, chain)
+
+ at unwrap_spec(signum=int)
+def unregister(space, signum):
+ return space.wrap(space.fromcache(Handler).unregister(signum))
+
+
+# for tests...
+
+ at unwrap_spec(release_gil=int)
+def read_null(space, release_gil=0):
+ if release_gil:
+ cintf.pypy_faulthandler_read_null_releasegil()
+ else:
+ cintf.pypy_faulthandler_read_null()
+
+ at unwrap_spec(release_gil=int)
+def sigsegv(space, release_gil=0):
+ if release_gil:
+ cintf.pypy_faulthandler_sigsegv_releasegil()
+ else:
+ cintf.pypy_faulthandler_sigsegv()
+
+def sigfpe(space):
+ cintf.pypy_faulthandler_sigfpe()
+
+def sigabrt(space):
+ cintf.pypy_faulthandler_sigabrt()
+
+ at unwrap_spec(levels=int)
+def stack_overflow(space, levels=100000000):
+ levels = float(levels)
+ return space.wrap(cintf.pypy_faulthandler_stackoverflow(levels))
diff --git a/pypy/module/faulthandler/interp_faulthandler.py b/pypy/module/faulthandler/interp_faulthandler.py
deleted file mode 100644
--- a/pypy/module/faulthandler/interp_faulthandler.py
+++ /dev/null
@@ -1,127 +0,0 @@
-import os
-import py
-
-from pypy.interpreter.gateway import interp2app, unwrap_spec, WrappedDefault
-from rpython.rtyper.lltypesystem import lltype, rffi
-from rpython.translator import cdir
-from rpython.translator.tool.cbuild import ExternalCompilationInfo
-from pypy.interpreter.error import OperationError, oefmt
-
-MAX_NTHREADS = 100
-
-cwd = py.path.local(__file__).dirpath()
-eci = ExternalCompilationInfo(
- includes=[cwd.join('faulthandler.h')],
- include_dirs=[str(cwd), cdir],
- separate_module_files=[cwd.join('faulthandler.c')])
-
-def llexternal(*args, **kwargs):
- kwargs.setdefault('releasegil', False)
- kwargs.setdefault('compilation_info', eci)
- return rffi.llexternal(*args, **kwargs)
-
-pypy_faulthandler_read_null = llexternal(
- 'pypy_faulthandler_read_null', [], lltype.Void)
-pypy_faulthandler_read_null_nogil = llexternal(
- 'pypy_faulthandler_read_null', [], lltype.Void,
- releasegil=True)
-pypy_faulthandler_sigsegv = llexternal(
- 'pypy_faulthandler_sigsegv', [], lltype.Void)
-pypy_faulthandler_sigfpe = llexternal(
- 'pypy_faulthandler_sigfpe', [], lltype.Void)
-pypy_faulthandler_sigabrt = llexternal(
- 'pypy_faulthandler_sigabrt', [], lltype.Void)
-pypy_faulthandler_sigbus = llexternal(
- 'pypy_faulthandler_sigbus', [], lltype.Void)
-pypy_faulthandler_sigill = llexternal(
- 'pypy_faulthandler_sigill', [], lltype.Void)
-
-class FatalErrorState(object):
- def __init__(self, space):
- self.enabled = False
- self.all_threads = True
-
- at unwrap_spec(w_file=WrappedDefault(None),
- w_all_threads=WrappedDefault(True))
-def enable(space, w_file, w_all_threads):
- state = space.fromcache(FatalErrorState)
- state.enabled = True
- state.all_threads = bool(space.int_w(w_all_threads))
-
-def disable(space):
- state = space.fromcache(FatalErrorState)
- state.enabled = False
-
-def is_enabled(space):
- return space.wrap(space.fromcache(FatalErrorState).enabled)
-
-def register(space, __args__):
- pass
-
-
- at unwrap_spec(w_file=WrappedDefault(None),
- w_all_threads=WrappedDefault(True))
-def dump_traceback(space, w_file, w_all_threads):
- current_ec = space.getexecutioncontext()
- if space.int_w(w_all_threads):
- ecs = space.threadlocals.getallvalues()
- else:
- ecs = {0: current_ec}
-
- if space.is_none(w_file):
- w_file = space.sys.get('stderr')
- fd = space.c_filedescriptor_w(w_file)
-
- nthreads = 0
- for thread_ident, ec in ecs.items():
- if nthreads:
- os.write(fd, "\n")
- if nthreads >= MAX_NTHREADS:
- os.write(fd, "...\n")
- break
- if ec is current_ec:
- os.write(fd, "Current thread 0x%x:\n" % thread_ident)
- else:
- os.write(fd, "Thread 0x%x:\n" % thread_ident)
-
- frame = ec.gettopframe()
- while frame:
- code = frame.pycode
- lineno = frame.get_last_lineno()
- if code:
- os.write(fd, " File \"%s\", line %s in %s\n" % (
- code.co_filename, lineno, code.co_name))
- else:
- os.write(fd, " File ???, line %s in ???\n" % (
- lineno,))
-
- frame = frame.f_backref()
-
-
- at unwrap_spec(w_release_gil=WrappedDefault(False))
-def read_null(space, w_release_gil):
- if space.is_true(w_release_gil):
- pypy_faulthandler_read_null_nogil()
- else:
- pypy_faulthandler_read_null()
-
-def sigsegv():
- pypy_faulthandler_sigsegv()
-
-def sigfpe():
- pypy_faulthandler_sigfpe()
-
-def sigabrt():
- pypy_faulthandler_sigabrt()
-
-#def sigbus():
-# pypy_faulthandler_sigbus()
-
-#def sigill():
-# pypy_faulthandler_sigill()
-
- at unwrap_spec(msg=str)
-def fatal_error(space, msg):
- os.write(2, "Fatal Python error: %s\n" % msg);
- dump_traceback(space, space.wrap(None), space.wrap(True))
- raise RuntimeError(msg)
diff --git a/pypy/module/faulthandler/test/test_faulthander.py b/pypy/module/faulthandler/test/test_faulthander.py
--- a/pypy/module/faulthandler/test/test_faulthander.py
+++ b/pypy/module/faulthandler/test/test_faulthander.py
@@ -1,12 +1,7 @@
-from pypy.module.faulthandler import interp_faulthandler
-
-class TestFaultHandler:
- def test_fatal_error(self, space):
- raises(RuntimeError, interp_faulthandler.fatal_error, space, "Message")
class AppTestFaultHandler:
spaceconfig = {
- "usemodules": ["faulthandler"]
+ "usemodules": ["faulthandler", "_vmprof"]
}
def test_enable(self):
diff --git a/pypy/module/sys/__init__.py b/pypy/module/sys/__init__.py
--- a/pypy/module/sys/__init__.py
+++ b/pypy/module/sys/__init__.py
@@ -136,6 +136,7 @@
space.setitem(self.w_dict, space.wrap('thread_info'), thread_info)
def setup_after_space_initialization(self):
+ "NOT_RPYTHON"
space = self.space
if not space.config.translating:
diff --git a/rpython/rlib/rvmprof/cintf.py b/rpython/rlib/rvmprof/cintf.py
--- a/rpython/rlib/rvmprof/cintf.py
+++ b/rpython/rlib/rvmprof/cintf.py
@@ -56,6 +56,11 @@
[rffi.INT], lltype.Void,
compilation_info=eci,
_nowrapper=True)
+ vmprof_get_traceback = rffi.llexternal("vmprof_get_traceback",
+ [PVMPROFSTACK, llmemory.Address,
+ rffi.SIGNEDP, lltype.Signed],
+ lltype.Signed, compilation_info=eci,
+ _nowrapper=True)
return CInterface(locals())
@@ -154,3 +159,9 @@
def restore_rvmprof_stack(x):
vmprof_tl_stack.setraw(x)
+
+#
+# traceback support
+
+def get_rvmprof_stack():
+ return vmprof_tl_stack.get_or_make_raw()
diff --git a/rpython/rlib/rvmprof/rvmprof.py b/rpython/rlib/rvmprof/rvmprof.py
--- a/rpython/rlib/rvmprof/rvmprof.py
+++ b/rpython/rlib/rvmprof/rvmprof.py
@@ -1,6 +1,6 @@
import sys, os
from rpython.rlib.objectmodel import specialize, we_are_translated
-from rpython.rlib import jit, rposix
+from rpython.rlib import jit, rposix, rgc
from rpython.rlib.rvmprof import cintf
from rpython.rtyper.annlowlevel import cast_instance_to_gcref
from rpython.rtyper.annlowlevel import cast_base_ptr_to_instance
diff --git a/rpython/rlib/rvmprof/src/rvmprof.h b/rpython/rlib/rvmprof/src/rvmprof.h
--- a/rpython/rlib/rvmprof/src/rvmprof.h
+++ b/rpython/rlib/rvmprof/src/rvmprof.h
@@ -1,3 +1,4 @@
+#include <stdint.h>
RPY_EXTERN char *vmprof_init(int, double, char *);
RPY_EXTERN void vmprof_ignore_signals(int);
@@ -8,3 +9,6 @@
RPY_EXTERN int vmprof_stack_append(void*, long);
RPY_EXTERN long vmprof_stack_pop(void*);
RPY_EXTERN void vmprof_stack_free(void*);
+RPY_EXTERN intptr_t vmprof_get_traceback(void *, void *, intptr_t*, intptr_t);
+
+#define RVMPROF_TRACEBACK_ESTIMATE_N(num_entries) (2 * (num_entries) + 4)
diff --git a/rpython/rlib/rvmprof/src/vmprof_common.h b/rpython/rlib/rvmprof/src/vmprof_common.h
--- a/rpython/rlib/rvmprof/src/vmprof_common.h
+++ b/rpython/rlib/rvmprof/src/vmprof_common.h
@@ -128,3 +128,15 @@
return 0;
}
#endif
+
+RPY_EXTERN
+intptr_t vmprof_get_traceback(void *stack, void *ucontext,
+ intptr_t *result_p, intptr_t result_length)
+{
+ int n;
+ intptr_t pc = ucontext ? GetPC((ucontext_t *)ucontext) : 0;
+ if (stack == NULL)
+ stack = get_vmprof_stack();
+ n = get_stack_trace(stack, result_p, result_length - 2, pc);
+ return (intptr_t)n;
+}
diff --git a/rpython/rlib/rvmprof/test/test_traceback.py b/rpython/rlib/rvmprof/test/test_traceback.py
new file mode 100644
--- /dev/null
+++ b/rpython/rlib/rvmprof/test/test_traceback.py
@@ -0,0 +1,113 @@
+import re
+from rpython.rlib import rvmprof, jit
+from rpython.rlib.rvmprof import traceback
+from rpython.translator.interactive import Translation
+from rpython.rtyper.lltypesystem import lltype
+
+
+def test_direct():
+ class MyCode:
+ pass
+ def get_name(mycode):
+ raise NotImplementedError
+ rvmprof.register_code_object_class(MyCode, get_name)
+ #
+ @rvmprof.vmprof_execute_code("mycode", lambda code, level: code,
+ _hack_update_stack_untranslated=True)
+ def mainloop(code, level):
+ if level > 0:
+ mainloop(code, level - 1)
+ else:
+ p, length = traceback.traceback(20)
+ traceback.walk_traceback(MyCode, my_callback, 42, p, length)
+ lltype.free(p, flavor='raw')
+ #
+ seen = []
+ def my_callback(code, loc, arg):
+ seen.append((code, loc, arg))
+ #
+ code1 = MyCode()
+ rvmprof.register_code(code1, "foo")
+ mainloop(code1, 2)
+ #
+ assert seen == [(code1, traceback.LOC_INTERPRETED, 42),
+ (code1, traceback.LOC_INTERPRETED, 42),
+ (code1, traceback.LOC_INTERPRETED, 42)]
+
+def test_compiled():
+ class MyCode:
+ pass
+ def get_name(mycode):
+ raise NotImplementedError
+ rvmprof.register_code_object_class(MyCode, get_name)
+
+ @rvmprof.vmprof_execute_code("mycode", lambda code, level: code)
+ def mainloop(code, level):
+ if level > 0:
+ mainloop(code, level - 1)
+ else:
+ p, length = traceback.traceback(20)
+ traceback.walk_traceback(MyCode, my_callback, 42, p, length)
+ lltype.free(p, flavor='raw')
+
+ def my_callback(code, loc, arg):
+ print code, loc, arg
+ return 0
+
+ def f(argv):
+ code1 = MyCode()
+ rvmprof.register_code(code1, "foo")
+ mainloop(code1, 2)
+ return 0
+
+ t = Translation(f, None, gc="boehm")
+ t.compile_c()
+ stdout = t.driver.cbuilder.cmdexec('')
+ r = re.compile("[<]MyCode object at 0x([0-9a-f]+)[>] 0 42\n")
+ got = r.findall(stdout)
+ assert got == [got[0]] * 3
+
+def test_jitted():
+ class MyCode:
+ pass
+ def get_name(mycode):
+ raise NotImplementedError
+ rvmprof.register_code_object_class(MyCode, get_name)
+
+ jitdriver = jit.JitDriver(greens=['code'], reds='auto',
+ is_recursive=True,
+ get_unique_id=lambda code: rvmprof.get_unique_id(code))
+
+ @rvmprof.vmprof_execute_code("mycode", lambda code, level, total_i: code)
+ def mainloop(code, level, total_i):
+ i = 20
+ while i > 0:
+ jitdriver.jit_merge_point(code=code)
+ i -= 1
+ if level > 0:
+ mainloop(code, level - 1, total_i + i)
+ if level == 0 and total_i == 0:
+ p, length = traceback.traceback(20)
+ traceback.walk_traceback(MyCode, my_callback, 42, p, length)
+ lltype.free(p, flavor='raw')
+
+ def my_callback(code, loc, arg):
+ print code, loc, arg
+ return 0
+
+ def f(argv):
+ jit.set_param(jitdriver, "inlining", 0)
+ code1 = MyCode()
+ rvmprof.register_code(code1, "foo")
+ mainloop(code1, 2, 0)
+ return 0
+
+ t = Translation(f, None, gc="boehm")
+ t.rtype()
+ t.driver.pyjitpl_lltype()
+ t.compile_c()
+ stdout = t.driver.cbuilder.cmdexec('')
+ r = re.compile("[<]MyCode object at 0x([0-9a-f]+)[>] (\d) 42\n")
+ got = r.findall(stdout)
+ addr = got[0][0]
+ assert got == [(addr, '1'), (addr, '1'), (addr, '0')]
diff --git a/rpython/rlib/rvmprof/traceback.py b/rpython/rlib/rvmprof/traceback.py
new file mode 100644
--- /dev/null
+++ b/rpython/rlib/rvmprof/traceback.py
@@ -0,0 +1,65 @@
+"""
+Semi-public interface to gather and print a raw traceback, e.g.
+from the faulthandler module.
+"""
+
+from rpython.rlib.rvmprof import cintf, rvmprof
+from rpython.rlib.objectmodel import specialize
+from rpython.rtyper.lltypesystem import lltype, llmemory, rffi
+
+
+def traceback(estimate_number_of_entries):
+ """Build and return a vmprof-like traceback, as a pair (array_p,
+ array_length). The caller must free array_p. Not for signal handlers:
+ for these, call vmprof_get_traceback() from C code.
+ """
+ _cintf = rvmprof._get_vmprof().cintf
+ size = estimate_number_of_entries * 2 + 4
+ stack = cintf.get_rvmprof_stack()
+ array_p = lltype.malloc(rffi.SIGNEDP.TO, size, flavor='raw')
+ NULL = llmemory.NULL
+ array_length = _cintf.vmprof_get_traceback(stack, NULL, array_p, size)
+ return (array_p, array_length)
+
+
+LOC_INTERPRETED = 0
+LOC_JITTED = 1
+LOC_JITTED_INLINED = 2
+
+
+ at specialize.arg(0, 1)
+def _traceback_one(CodeClass, callback, arg, code_id, loc):
+ found_code = None
+ if code_id != 0:
+ all_code_wrefs = CodeClass._vmprof_weak_list.get_all_handles()
+ i = len(all_code_wrefs) - 1
+ while i >= 0:
+ code = all_code_wrefs[i]()
+ if code is not None and code._vmprof_unique_id == code_id:
+ found_code = code
+ break
+ i -= 1
+ callback(found_code, loc, arg)
+
+ at specialize.arg(0, 1)
+def walk_traceback(CodeClass, callback, arg, array_p, array_length):
+ """Invoke 'callback(code_obj, loc, arg)' for every traceback entry.
+ 'code_obj' may be None if it can't be determined. 'loc' is one
+ of the LOC_xxx constants.
+ """
+ i = 0
+ while i < array_length - 1:
+ tag = array_p[i]
+ tagged_value = array_p[i + 1]
+ if tag == rvmprof.VMPROF_CODE_TAG:
+ loc = LOC_INTERPRETED
+ _traceback_one(CodeClass, callback, arg, tagged_value, loc)
+ elif tag == rvmprof.VMPROF_JITTED_TAG:
+ if i + 2 >= array_length: # skip last entry, can't determine if
+ break # it's LOC_JITTED_INLINED or LOC_JITTED
+ if array_p[i + 2] == rvmprof.VMPROF_JITTED_TAG:
+ loc = LOC_JITTED_INLINED
+ else:
+ loc = LOC_JITTED
+ _traceback_one(CodeClass, callback, arg, tagged_value, loc)
+ i += 2
diff --git a/rpython/translator/c/src/threadlocal.c b/rpython/translator/c/src/threadlocal.c
--- a/rpython/translator/c/src/threadlocal.c
+++ b/rpython/translator/c/src/threadlocal.c
@@ -14,12 +14,22 @@
static int check_valid(void);
+int _RPython_ThreadLocals_AcquireTimeout(int max_wait_iterations) {
+ while (1) {
+ long old_value = pypy_lock_test_and_set(&pypy_threadlocal_lock, 1);
+ if (old_value == 0)
+ break;
+ /* busy loop */
+ if (max_wait_iterations == 0)
+ return -1;
+ if (max_wait_iterations > 0)
+ --max_wait_iterations;
+ }
+ assert(check_valid());
+ return 0;
+}
void _RPython_ThreadLocals_Acquire(void) {
- long old_value;
- do {
- old_value = pypy_lock_test_and_set(&pypy_threadlocal_lock, 1);
- } while (old_value != 0); /* busy loop */
- assert(check_valid());
+ _RPython_ThreadLocals_AcquireTimeout(-1);
}
void _RPython_ThreadLocals_Release(void) {
assert(check_valid());
@@ -60,11 +70,7 @@
{
/* assume that at most one pypy_threadlocal_s survived, the current one */
struct pypy_threadlocal_s *cur;
-#ifdef USE___THREAD
- cur = &pypy_threadlocal;
-#else
cur = (struct pypy_threadlocal_s *)_RPy_ThreadLocals_Get();
-#endif
if (cur && cur->ready == 42) {
cur->next = cur->prev = &linkedlist_head;
linkedlist_head.next = linkedlist_head.prev = cur;
diff --git a/rpython/translator/c/src/threadlocal.h b/rpython/translator/c/src/threadlocal.h
--- a/rpython/translator/c/src/threadlocal.h
+++ b/rpython/translator/c/src/threadlocal.h
@@ -21,6 +21,7 @@
RPY_EXTERN void _RPython_ThreadLocals_Acquire(void);
RPY_EXTERN void _RPython_ThreadLocals_Release(void);
+RPY_EXTERN int _RPython_ThreadLocals_AcquireTimeout(int max_wait_iterations);
/* Must acquire/release the thread-local lock around a series of calls
to the following function */
@@ -48,14 +49,14 @@
#define OP_THREADLOCALREF_ADDR(r) \
do { \
- r = (char *)&pypy_threadlocal; \
+ r = (void *)&pypy_threadlocal; \
if (pypy_threadlocal.ready != 42) \
r = _RPython_ThreadLocals_Build(); \
} while (0)
#define _OP_THREADLOCALREF_ADDR_SIGHANDLER(r) \
do { \
- r = (char *)&pypy_threadlocal; \
+ r = (void *)&pypy_threadlocal; \
if (pypy_threadlocal.ready != 42) \
r = NULL; \
} while (0)
@@ -66,6 +67,8 @@
#define RPY_THREADLOCALREF_GET(FIELD) pypy_threadlocal.FIELD
+#define _RPy_ThreadLocals_Get() (&pypy_threadlocal)
+
/* ------------------------------------------------------------ */
#else
@@ -89,14 +92,14 @@
#define OP_THREADLOCALREF_ADDR(r) \
do { \
- r = (char *)_RPy_ThreadLocals_Get(); \
+ r = (void *)_RPy_ThreadLocals_Get(); \
if (!r) \
r = _RPython_ThreadLocals_Build(); \
} while (0)
#define _OP_THREADLOCALREF_ADDR_SIGHANDLER(r) \
do { \
- r = (char *)_RPy_ThreadLocals_Get(); \
+ r = (void *)_RPy_ThreadLocals_Get(); \
} while (0)
#define RPY_THREADLOCALREF_ENSURE() \
More information about the pypy-commit
mailing list