Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit a9a9dab

Browse files
author
Victor Stinner
committed
Issue #12550: Add chain optional argument to faulthandler.register()
Call the previous signal handler if chain is True.
1 parent d93da2b commit a9a9dab

3 files changed

Lines changed: 90 additions & 37 deletions

File tree

Doc/library/faulthandler.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,11 +92,11 @@ Dump the tracebacks after a timeout
9292
Dump the traceback on a user signal
9393
-----------------------------------
9494

95-
.. function:: register(signum, file=sys.stderr, all_threads=True)
95+
.. function:: register(signum, file=sys.stderr, all_threads=True, chain=False)
9696

9797
Register a user signal: install a handler for the *signum* signal to dump
9898
the traceback of all threads, or of the current thread if *all_threads* is
99-
``False``, into *file*.
99+
``False``, into *file*. Call the previous handler if chain is ``True``.
100100

101101
Not available on Windows.
102102

Lib/test/test_faulthandler.py

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -452,41 +452,63 @@ def test_dump_tracebacks_later_twice(self):
452452
@unittest.skipIf(not hasattr(faulthandler, "register"),
453453
"need faulthandler.register")
454454
def check_register(self, filename=False, all_threads=False,
455-
unregister=False):
455+
unregister=False, chain=False):
456456
"""
457457
Register a handler displaying the traceback on a user signal. Raise the
458458
signal and check the written traceback.
459459
460+
If chain is True, check that the previous signal handler is called.
461+
460462
Raise an error if the output doesn't match the expected format.
461463
"""
462464
signum = signal.SIGUSR1
463465
code = """
464466
import faulthandler
465467
import os
466468
import signal
469+
import sys
467470
468471
def func(signum):
469472
os.kill(os.getpid(), signum)
470473
474+
def handler(signum, frame):
475+
handler.called = True
476+
handler.called = False
477+
478+
exitcode = 0
471479
signum = {signum}
472480
unregister = {unregister}
481+
chain = {chain}
482+
473483
if {has_filename}:
474484
file = open({filename}, "wb")
475485
else:
476486
file = None
477-
faulthandler.register(signum, file=file, all_threads={all_threads})
487+
if chain:
488+
signal.signal(signum, handler)
489+
faulthandler.register(signum, file=file,
490+
all_threads={all_threads}, chain={chain})
478491
if unregister:
479492
faulthandler.unregister(signum)
480493
func(signum)
494+
if chain and not handler.called:
495+
if file is not None:
496+
output = file
497+
else:
498+
output = sys.stderr
499+
print("Error: signal handler not called!", file=output)
500+
exitcode = 1
481501
if file is not None:
482502
file.close()
503+
sys.exit(exitcode)
483504
""".strip()
484505
code = code.format(
485506
filename=repr(filename),
486507
has_filename=bool(filename),
487508
all_threads=all_threads,
488509
signum=signum,
489510
unregister=unregister,
511+
chain=chain,
490512
)
491513
trace, exitcode = self.get_output(code, filename)
492514
trace = '\n'.join(trace)
@@ -495,7 +517,7 @@ def func(signum):
495517
regex = 'Current thread XXX:\n'
496518
else:
497519
regex = 'Traceback \(most recent call first\):\n'
498-
regex = expected_traceback(6, 17, regex)
520+
regex = expected_traceback(7, 28, regex)
499521
self.assertRegex(trace, regex)
500522
else:
501523
self.assertEqual(trace, '')
@@ -517,6 +539,9 @@ def test_register_file(self):
517539
def test_register_threads(self):
518540
self.check_register(all_threads=True)
519541

542+
def test_register_chain(self):
543+
self.check_register(chain=True)
544+
520545

521546
def test_main():
522547
support.run_unittest(FaultHandlerTests)

Modules/faulthandler.c

Lines changed: 60 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
#include <pthread.h>
99
#endif
1010

11-
1211
/* Allocate at maximum 100 MB of the stack to raise the stack overflow */
1312
#define STACK_OVERFLOW_MAX_SIZE (100*1024*1024)
1413

@@ -72,6 +71,7 @@ typedef struct {
7271
PyObject *file;
7372
int fd;
7473
int all_threads;
74+
int chain;
7575
_Py_sighandler_t previous;
7676
PyInterpreterState *interp;
7777
} user_signal_t;
@@ -94,6 +94,7 @@ static user_signal_t *user_signals;
9494
# endif
9595
#endif
9696

97+
static void faulthandler_user(int signum);
9798
#endif /* FAULTHANDLER_USER */
9899

99100

@@ -259,9 +260,9 @@ faulthandler_fatal_error(int signum)
259260

260261
/* restore the previous handler */
261262
#ifdef HAVE_SIGACTION
262-
(void)sigaction(handler->signum, &handler->previous, NULL);
263+
(void)sigaction(signum, &handler->previous, NULL);
263264
#else
264-
(void)signal(handler->signum, handler->previous);
265+
(void)signal(signum, handler->previous);
265266
#endif
266267
handler->enabled = 0;
267268

@@ -587,6 +588,39 @@ faulthandler_cancel_dump_tracebacks_later_py(PyObject *self)
587588
#endif /* FAULTHANDLER_LATER */
588589

589590
#ifdef FAULTHANDLER_USER
591+
static int
592+
faulthandler_register(int signum, int chain, _Py_sighandler_t *p_previous)
593+
{
594+
#ifdef HAVE_SIGACTION
595+
struct sigaction action;
596+
action.sa_handler = faulthandler_user;
597+
sigemptyset(&action.sa_mask);
598+
/* if the signal is received while the kernel is executing a system
599+
call, try to restart the system call instead of interrupting it and
600+
return EINTR. */
601+
action.sa_flags = SA_RESTART;
602+
if (chain) {
603+
/* do not prevent the signal from being received from within its
604+
own signal handler */
605+
action.sa_flags = SA_NODEFER;
606+
}
607+
#ifdef HAVE_SIGALTSTACK
608+
if (stack.ss_sp != NULL) {
609+
/* Call the signal handler on an alternate signal stack
610+
provided by sigaltstack() */
611+
action.sa_flags |= SA_ONSTACK;
612+
}
613+
#endif
614+
return sigaction(signum, &action, p_previous);
615+
#else
616+
_Py_sighandler_t previous;
617+
previous = signal(signum, faulthandler_user);
618+
if (p_previous != NULL)
619+
*p_previous = previous;
620+
return (previous == SIG_ERR);
621+
#endif
622+
}
623+
590624
/* Handler of user signals (e.g. SIGUSR1).
591625
592626
Dump the traceback of the current thread, or of all threads if
@@ -621,6 +655,19 @@ faulthandler_user(int signum)
621655
return;
622656
_Py_DumpTraceback(user->fd, tstate);
623657
}
658+
#ifdef HAVE_SIGACTION
659+
if (user->chain) {
660+
(void)sigaction(signum, &user->previous, NULL);
661+
/* call the previous signal handler */
662+
raise(signum);
663+
(void)faulthandler_register(signum, user->chain, NULL);
664+
}
665+
#else
666+
if (user->chain) {
667+
/* call the previous signal handler */
668+
user->previous(signum);
669+
}
670+
#endif
624671
errno = save_errno;
625672
}
626673

@@ -646,25 +693,23 @@ check_signum(int signum)
646693
}
647694

648695
static PyObject*
649-
faulthandler_register(PyObject *self,
650-
PyObject *args, PyObject *kwargs)
696+
faulthandler_register_py(PyObject *self,
697+
PyObject *args, PyObject *kwargs)
651698
{
652-
static char *kwlist[] = {"signum", "file", "all_threads", NULL};
699+
static char *kwlist[] = {"signum", "file", "all_threads", "chain", NULL};
653700
int signum;
654701
PyObject *file = NULL;
655702
int all_threads = 1;
703+
int chain = 0;
656704
int fd;
657705
user_signal_t *user;
658706
_Py_sighandler_t previous;
659-
#ifdef HAVE_SIGACTION
660-
struct sigaction action;
661-
#endif
662707
PyThreadState *tstate;
663708
int err;
664709

665710
if (!PyArg_ParseTupleAndKeywords(args, kwargs,
666-
"i|Oi:register", kwlist,
667-
&signum, &file, &all_threads))
711+
"i|Oii:register", kwlist,
712+
&signum, &file, &all_threads, &chain))
668713
return NULL;
669714

670715
if (!check_signum(signum))
@@ -686,25 +731,7 @@ faulthandler_register(PyObject *self,
686731
user = &user_signals[signum];
687732

688733
if (!user->enabled) {
689-
#ifdef HAVE_SIGACTION
690-
action.sa_handler = faulthandler_user;
691-
sigemptyset(&action.sa_mask);
692-
/* if the signal is received while the kernel is executing a system
693-
call, try to restart the system call instead of interrupting it and
694-
return EINTR */
695-
action.sa_flags = SA_RESTART;
696-
#ifdef HAVE_SIGALTSTACK
697-
if (stack.ss_sp != NULL) {
698-
/* Call the signal handler on an alternate signal stack
699-
provided by sigaltstack() */
700-
action.sa_flags |= SA_ONSTACK;
701-
}
702-
#endif
703-
err = sigaction(signum, &action, &previous);
704-
#else
705-
previous = signal(signum, faulthandler_user);
706-
err = (previous == SIG_ERR);
707-
#endif
734+
err = faulthandler_register(signum, chain, &previous);
708735
if (err) {
709736
PyErr_SetFromErrno(PyExc_OSError);
710737
return NULL;
@@ -716,6 +743,7 @@ faulthandler_register(PyObject *self,
716743
user->file = file;
717744
user->fd = fd;
718745
user->all_threads = all_threads;
746+
user->chain = chain;
719747
user->previous = previous;
720748
user->interp = tstate->interp;
721749
user->enabled = 1;
@@ -947,8 +975,8 @@ static PyMethodDef module_methods[] = {
947975

948976
#ifdef FAULTHANDLER_USER
949977
{"register",
950-
(PyCFunction)faulthandler_register, METH_VARARGS|METH_KEYWORDS,
951-
PyDoc_STR("register(signum, file=sys.stderr, all_threads=True): "
978+
(PyCFunction)faulthandler_register_py, METH_VARARGS|METH_KEYWORDS,
979+
PyDoc_STR("register(signum, file=sys.stderr, all_threads=True, chain=False): "
952980
"register an handler for the signal 'signum': dump the "
953981
"traceback of the current thread, or of all threads if "
954982
"all_threads is True, into file")},

0 commit comments

Comments
 (0)