[pypy-commit] pypy py3k-faulthandler: in-progress
arigo
pypy.commits at gmail.com
Thu Sep 29 10:58:57 EDT 2016
Author: Armin Rigo <arigo at tunes.org>
Branch: py3k-faulthandler
Changeset: r87446:f17896d64f65
Date: 2016-09-29 16:58 +0200
http://bitbucket.org/pypy/pypy/changeset/f17896d64f65/
Log: in-progress
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,5 +1,7 @@
+import sys
from pypy.interpreter.mixedmodule import MixedModule
+
class Module(MixedModule):
appleveldefs = {
}
@@ -26,6 +28,9 @@
'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')
def shutdown(self, space):
from pypy.module.faulthandler import handler
diff --git a/pypy/module/faulthandler/cintf.py b/pypy/module/faulthandler/cintf.py
--- a/pypy/module/faulthandler/cintf.py
+++ b/pypy/module/faulthandler/cintf.py
@@ -12,6 +12,8 @@
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)
@@ -55,6 +57,21 @@
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...
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
@@ -138,6 +138,10 @@
reentrant = 0;
}
+
+/************************************************************/
+
+
#ifdef PYPY_FAULTHANDLER_LATER
#include "src/thread.h"
static struct {
@@ -243,6 +247,152 @@
}
+/************************************************************/
+
+
+#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
@@ -344,6 +494,14 @@
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) {
@@ -415,6 +573,9 @@
}
+/************************************************************/
+
+
/* for tests... */
static void
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
@@ -25,6 +25,10 @@
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);
diff --git a/pypy/module/faulthandler/handler.py b/pypy/module/faulthandler/handler.py
--- a/pypy/module/faulthandler/handler.py
+++ b/pypy/module/faulthandler/handler.py
@@ -1,5 +1,5 @@
import os
-from rpython.rtyper.lltypesystem import llmemory, rffi
+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
@@ -20,6 +20,7 @@
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:
@@ -98,6 +99,37 @@
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))
+ return rffi.cast(lltype.Signed, change) == 1
+
def finish(self):
cintf.pypy_faulthandler_teardown()
self._cleanup_()
@@ -138,6 +170,14 @@
"""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...
More information about the pypy-commit
mailing list