[Python-checkins] cpython: Issue #12550: Add chain optional argument to faulthandler.register()

victor.stinner python-checkins at python.org
Wed Jul 13 23:40:23 CEST 2011


http://hg.python.org/cpython/rev/39873557ff4f
changeset:   71314:39873557ff4f
parent:      71312:637210b9d054
user:        Victor Stinner <victor.stinner at haypocalc.com>
date:        Wed Jul 13 23:39:53 2011 +0200
summary:
  Issue #12550: Add chain optional argument to faulthandler.register()

Call the previous signal handler if chain is True.

files:
  Doc/library/faulthandler.rst  |   4 +-
  Lib/test/test_faulthandler.py |  31 +++++++-
  Modules/faulthandler.c        |  92 +++++++++++++++--------
  3 files changed, 90 insertions(+), 37 deletions(-)


diff --git a/Doc/library/faulthandler.rst b/Doc/library/faulthandler.rst
--- a/Doc/library/faulthandler.rst
+++ b/Doc/library/faulthandler.rst
@@ -92,11 +92,11 @@
 Dump the traceback on a user signal
 -----------------------------------
 
-.. function:: register(signum, file=sys.stderr, all_threads=True)
+.. function:: register(signum, file=sys.stderr, all_threads=True, chain=False)
 
    Register a user signal: install a handler for the *signum* signal to dump
    the traceback of all threads, or of the current thread if *all_threads* is
-   ``False``, into *file*.
+   ``False``, into *file*. Call the previous handler if chain is ``True``.
 
    Not available on Windows.
 
diff --git a/Lib/test/test_faulthandler.py b/Lib/test/test_faulthandler.py
--- a/Lib/test/test_faulthandler.py
+++ b/Lib/test/test_faulthandler.py
@@ -452,11 +452,13 @@
     @unittest.skipIf(not hasattr(faulthandler, "register"),
                      "need faulthandler.register")
     def check_register(self, filename=False, all_threads=False,
-                       unregister=False):
+                       unregister=False, chain=False):
         """
         Register a handler displaying the traceback on a user signal. Raise the
         signal and check the written traceback.
 
+        If chain is True, check that the previous signal handler is called.
+
         Raise an error if the output doesn't match the expected format.
         """
         signum = signal.SIGUSR1
@@ -464,22 +466,41 @@
 import faulthandler
 import os
 import signal
+import sys
 
 def func(signum):
     os.kill(os.getpid(), signum)
 
+def handler(signum, frame):
+    handler.called = True
+handler.called = False
+
+exitcode = 0
 signum = {signum}
 unregister = {unregister}
+chain = {chain}
+
 if {has_filename}:
     file = open({filename}, "wb")
 else:
     file = None
-faulthandler.register(signum, file=file, all_threads={all_threads})
+if chain:
+    signal.signal(signum, handler)
+faulthandler.register(signum, file=file,
+                      all_threads={all_threads}, chain={chain})
 if unregister:
     faulthandler.unregister(signum)
 func(signum)
+if chain and not handler.called:
+    if file is not None:
+        output = file
+    else:
+        output = sys.stderr
+    print("Error: signal handler not called!", file=output)
+    exitcode = 1
 if file is not None:
     file.close()
+sys.exit(exitcode)
 """.strip()
         code = code.format(
             filename=repr(filename),
@@ -487,6 +508,7 @@
             all_threads=all_threads,
             signum=signum,
             unregister=unregister,
+            chain=chain,
         )
         trace, exitcode = self.get_output(code, filename)
         trace = '\n'.join(trace)
@@ -495,7 +517,7 @@
                 regex = 'Current thread XXX:\n'
             else:
                 regex = 'Traceback \(most recent call first\):\n'
-            regex = expected_traceback(6, 17, regex)
+            regex = expected_traceback(7, 28, regex)
             self.assertRegex(trace, regex)
         else:
             self.assertEqual(trace, '')
@@ -517,6 +539,9 @@
     def test_register_threads(self):
         self.check_register(all_threads=True)
 
+    def test_register_chain(self):
+        self.check_register(chain=True)
+
 
 def test_main():
     support.run_unittest(FaultHandlerTests)
diff --git a/Modules/faulthandler.c b/Modules/faulthandler.c
--- a/Modules/faulthandler.c
+++ b/Modules/faulthandler.c
@@ -8,7 +8,6 @@
 #include <pthread.h>
 #endif
 
-
 /* Allocate at maximum 100 MB of the stack to raise the stack overflow */
 #define STACK_OVERFLOW_MAX_SIZE (100*1024*1024)
 
@@ -72,6 +71,7 @@
     PyObject *file;
     int fd;
     int all_threads;
+    int chain;
     _Py_sighandler_t previous;
     PyInterpreterState *interp;
 } user_signal_t;
@@ -94,6 +94,7 @@
 # endif
 #endif
 
+static void faulthandler_user(int signum);
 #endif /* FAULTHANDLER_USER */
 
 
@@ -259,9 +260,9 @@
 
     /* restore the previous handler */
 #ifdef HAVE_SIGACTION
-    (void)sigaction(handler->signum, &handler->previous, NULL);
+    (void)sigaction(signum, &handler->previous, NULL);
 #else
-    (void)signal(handler->signum, handler->previous);
+    (void)signal(signum, handler->previous);
 #endif
     handler->enabled = 0;
 
@@ -587,6 +588,39 @@
 #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
@@ -621,6 +655,19 @@
             return;
         _Py_DumpTraceback(user->fd, tstate);
     }
+#ifdef HAVE_SIGACTION
+    if (user->chain) {
+        (void)sigaction(signum, &user->previous, NULL);
+        /* call the previous signal handler */
+        raise(signum);
+        (void)faulthandler_register(signum, user->chain, NULL);
+    }
+#else
+    if (user->chain) {
+        /* call the previous signal handler */
+        user->previous(signum);
+    }
+#endif
     errno = save_errno;
 }
 
@@ -646,25 +693,23 @@
 }
 
 static PyObject*
-faulthandler_register(PyObject *self,
-                      PyObject *args, PyObject *kwargs)
+faulthandler_register_py(PyObject *self,
+                         PyObject *args, PyObject *kwargs)
 {
-    static char *kwlist[] = {"signum", "file", "all_threads", NULL};
+    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;
-#ifdef HAVE_SIGACTION
-    struct sigaction action;
-#endif
     PyThreadState *tstate;
     int err;
 
     if (!PyArg_ParseTupleAndKeywords(args, kwargs,
-        "i|Oi:register", kwlist,
-        &signum, &file, &all_threads))
+        "i|Oii:register", kwlist,
+        &signum, &file, &all_threads, &chain))
         return NULL;
 
     if (!check_signum(signum))
@@ -686,25 +731,7 @@
     user = &user_signals[signum];
 
     if (!user->enabled) {
-#ifdef HAVE_SIGACTION
-        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;
-#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(signum, &action, &previous);
-#else
-        previous = signal(signum, faulthandler_user);
-        err = (previous == SIG_ERR);
-#endif
+        err = faulthandler_register(signum, chain, &previous);
         if (err) {
             PyErr_SetFromErrno(PyExc_OSError);
             return NULL;
@@ -716,6 +743,7 @@
     user->file = file;
     user->fd = fd;
     user->all_threads = all_threads;
+    user->chain = chain;
     user->previous = previous;
     user->interp = tstate->interp;
     user->enabled = 1;
@@ -947,8 +975,8 @@
 
 #ifdef FAULTHANDLER_USER
     {"register",
-     (PyCFunction)faulthandler_register, METH_VARARGS|METH_KEYWORDS,
-     PyDoc_STR("register(signum, file=sys.stderr, all_threads=True): "
+     (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")},

-- 
Repository URL: http://hg.python.org/cpython


More information about the Python-checkins mailing list