Skip to content

Commit 024e37a

Browse files
author
Victor Stinner
committed
Issue python#11393: Add the new faulthandler module
1 parent d854562 commit 024e37a

19 files changed

+1907
-5
lines changed

Doc/library/debug.rst

+2-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ allowing you to identify bottlenecks in your programs.
1010
.. toctree::
1111

1212
bdb.rst
13+
faulthandler.rst
1314
pdb.rst
1415
profile.rst
1516
timeit.rst
16-
trace.rst
17+
trace.rst

Doc/library/faulthandler.rst

+129
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
:mod:`faulthandler` --- Dump the Python traceback
2+
=================================================
3+
4+
.. module:: faulthandler
5+
:synopsis: Dump the Python traceback.
6+
7+
This module contains functions to dump the Python traceback explicitly, on a
8+
fault, after a timeout or on a user signal. Call :func:`faulthandler.enable` to
9+
install fault handlers for :const:`SIGSEGV`, :const:`SIGFPE`, :const:`SIGBUS`
10+
and :const:`SIGILL` signals. You can also enable them at startup by setting the
11+
:envvar:`PYTHONFAULTHANDLER` environment variable or by using :option:`-X`
12+
``faulthandler`` command line option.
13+
14+
The fault handler is compatible with system fault handlers like Apport or
15+
the Windows fault handler. The module uses an alternative stack for signal
16+
handlers, if the :c:func:`sigaltstack` function is available, to be able to
17+
dump the traceback even on a stack overflow.
18+
19+
The fault handler is called on catastrophic cases and so can only use
20+
signal-safe functions (e.g. it cannot allocate memory on the heap). That's why
21+
the traceback is limited: only support ASCII encoding (use the
22+
``backslashreplace`` error handler), limit each string to 100 characters, don't
23+
print the source code (only the filename, the function name and the line
24+
number), limit to 100 frames and 100 threads.
25+
26+
By default, the Python traceback is written to :data:`sys.stderr`. Start your
27+
graphical applications in a terminal and run your server in foreground to see
28+
the traceback, or specify a log file to :func:`faulthandler.enable()`.
29+
30+
The module is implemented in C to be able to dump a traceback on a crash or
31+
when Python is blocked (e.g. deadlock).
32+
33+
.. versionadded:: 3.3
34+
35+
36+
Dump the traceback
37+
------------------
38+
39+
.. function:: dump_traceback(file=sys.stderr, all_threads=False)
40+
41+
Dump the traceback of the current thread, or of all threads if *all_threads*
42+
is ``True``, into *file*.
43+
44+
45+
Fault handler state
46+
-------------------
47+
48+
.. function:: enable(file=sys.stderr, all_threads=False)
49+
50+
Enable the fault handler: install handlers for :const:`SIGSEGV`,
51+
:const:`SIGFPE`, :const:`SIGBUS` and :const:`SIGILL` signals to dump the
52+
Python traceback. It dumps the traceback of the current thread, or all
53+
threads if *all_threads* is ``True``, into *file*.
54+
55+
.. function:: disable()
56+
57+
Disable the fault handler: uninstall the signal handlers installed by
58+
:func:`enable`.
59+
60+
.. function:: is_enabled()
61+
62+
Check if the fault handler is enabled.
63+
64+
65+
Dump the tracebacks after a timeout
66+
-----------------------------------
67+
68+
.. function:: dump_tracebacks_later(timeout, repeat=False, file=sys.stderr, exit=False)
69+
70+
Dump the tracebacks of all threads, after a timeout of *timeout* seconds, or
71+
each *timeout* seconds if *repeat* is ``True``. If *exit* is True, call
72+
:cfunc:`_exit` with status=1 after dumping the tracebacks to terminate
73+
immediatly the process, which is not safe. For example, :cfunc:`_exit`
74+
doesn't flush file buffers. If the function is called twice, the new call
75+
replaces previous parameters (resets the timeout). The timer has a
76+
sub-second resolution.
77+
78+
This function is implemented using a watchdog thread, and therefore is
79+
not available if Python is compiled with threads disabled.
80+
81+
.. function:: cancel_dump_traceback_later()
82+
83+
Cancel the last call to :func:`dump_traceback_later`.
84+
85+
86+
Dump the traceback on a user signal
87+
-----------------------------------
88+
89+
.. function:: register(signum, file=sys.stderr, all_threads=False)
90+
91+
Register a user signal: install a handler for the *signum* signal to dump
92+
the traceback of the current thread, or of all threads if *all_threads* is
93+
``True``, into *file*.
94+
95+
Not available on Windows.
96+
97+
.. function:: unregister(signum)
98+
99+
Unregister a user signal: uninstall the handler of the *signum* signal
100+
installed by :func:`register`.
101+
102+
Not available on Windows.
103+
104+
105+
File descriptor issue
106+
---------------------
107+
108+
:func:`enable`, :func:`dump_traceback_later` and :func:`register` keep the
109+
file descriptor of their *file* argument. If the file is closed and its file
110+
descriptor is reused by a new file, or if :func:`os.dup2` is used to replace
111+
the file descriptor, the traceback will be written into a different file. Call
112+
these functions again each time that the file is replaced.
113+
114+
115+
Example
116+
-------
117+
118+
Example of a segmentation fault on Linux: ::
119+
120+
$ python -q -X faulthandler
121+
>>> import ctypes
122+
>>> ctypes.string_at(0)
123+
Fatal Python error: Segmentation fault
124+
125+
Traceback (most recent call first):
126+
File "/home/python/cpython/Lib/ctypes/__init__.py", line 486 in string_at
127+
File "<stdin>", line 1 in <module>
128+
Segmentation fault
129+

Doc/using/cmdline.rst

+7
Original file line numberDiff line numberDiff line change
@@ -498,6 +498,13 @@ These environment variables influence Python's behavior.
498498
separated string, it is equivalent to specifying :option:`-W` multiple
499499
times.
500500

501+
.. envvar:: PYTHONFAULTHANDLER
502+
503+
If this environment variable is set, :func:`faulthandler.enable` is called
504+
at startup: install a handler for :const:`SIGSEGV`, :const:`SIGFPE`,
505+
:const:`SIGBUS` and :const:`SIGILL` signals to dump the Python traceback.
506+
This is equivalent to :option:`-X` ``faulthandler`` option.
507+
501508

502509
Debug-mode variables
503510
~~~~~~~~~~~~~~~~~~~~

Doc/whatsnew/3.3.rst

+8
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,14 @@ New, Improved, and Deprecated Modules
6868

6969
* Stub
7070

71+
faulthandler
72+
------------
73+
74+
New module: :mod:`faulthandler`.
75+
76+
* :envvar:`PYTHONFAULTHANDLER`
77+
* :option:`-X` ``faulthandler``
78+
7179
os
7280
--
7381

Include/traceback.h

+40
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
extern "C" {
66
#endif
77

8+
#include "pystate.h"
9+
810
struct _frame;
911

1012
/* Traceback interface */
@@ -28,6 +30,44 @@ PyAPI_FUNC(int) _Py_DisplaySourceLine(PyObject *, PyObject *, int, int);
2830
PyAPI_DATA(PyTypeObject) PyTraceBack_Type;
2931
#define PyTraceBack_Check(v) (Py_TYPE(v) == &PyTraceBack_Type)
3032

33+
/* Write the Python traceback into the file 'fd'. For example:
34+
35+
Traceback (most recent call first):
36+
File "xxx", line xxx in <xxx>
37+
File "xxx", line xxx in <xxx>
38+
...
39+
File "xxx", line xxx in <xxx>
40+
41+
Return 0 on success, -1 on error.
42+
43+
This function is written for debug purpose only, to dump the traceback in
44+
the worst case: after a segmentation fault, at fatal error, etc. That's why,
45+
it is very limited. Strings are truncated to 100 characters and encoded to
46+
ASCII with backslashreplace. It doesn't write the source code, only the
47+
function name, filename and line number of each frame. Write only the first
48+
100 frames: if the traceback is truncated, write the line " ...".
49+
50+
This function is signal safe. */
51+
52+
PyAPI_DATA(int) _Py_DumpTraceback(
53+
int fd,
54+
PyThreadState *tstate);
55+
56+
/* Write the traceback of all threads into the file 'fd'. current_thread can be
57+
NULL. Return NULL on success, or an error message on error.
58+
59+
This function is written for debug purpose only. It calls
60+
_Py_DumpTraceback() for each thread, and so has the same limitations. It
61+
only write the traceback of the first 100 threads: write "..." if there are
62+
more threads.
63+
64+
This function is signal safe. */
65+
66+
PyAPI_DATA(const char*) _Py_DumpTracebackThreads(
67+
int fd, PyInterpreterState *interp,
68+
PyThreadState *current_thread);
69+
70+
3171
#ifdef __cplusplus
3272
}
3373
#endif

Lib/test/regrtest.py

+5
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@
157157
"""
158158

159159
import builtins
160+
import faulthandler
160161
import getopt
161162
import json
162163
import os
@@ -490,6 +491,7 @@ def main(tests=None, testdir=None, verbose=0, quiet=False,
490491
next_single_test = alltests[alltests.index(selected[0])+1]
491492
except IndexError:
492493
next_single_test = None
494+
selected = ['test_faulthandler']
493495
# Remove all the tests that precede start if it's set.
494496
if start:
495497
try:
@@ -1551,6 +1553,9 @@ def _make_temp_dir_for_build(TEMPDIR):
15511553
return TEMPDIR, TESTCWD
15521554

15531555
if __name__ == '__main__':
1556+
# Display the Python traceback on segfault and division by zero
1557+
faulthandler.enable()
1558+
15541559
# Remove regrtest.py's own directory from the module search path. Despite
15551560
# the elimination of implicit relative imports, this is still needed to
15561561
# ensure that submodules of the test package do not inappropriately appear

Lib/test/script_helper.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,12 @@ def assert_python_failure(*args, **env_vars):
5656
"""
5757
return _assert_python(False, *args, **env_vars)
5858

59-
def spawn_python(*args):
59+
def spawn_python(*args, **kw):
6060
cmd_line = [sys.executable, '-E']
6161
cmd_line.extend(args)
6262
return subprocess.Popen(cmd_line, stdin=subprocess.PIPE,
63-
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
63+
stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
64+
**kw)
6465

6566
def kill_python(p):
6667
p.stdin.close()

0 commit comments

Comments
 (0)