[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