Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions Doc/c-api/exceptions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1346,3 +1346,58 @@
This function returns ``0`` on success, and returns ``-1`` with an
exception set on failure.
.. c:function:: void PyUnstable_DumpTraceback(int fd, PyThreadState *tstate)
Write a trace of the Python stack in *tstate* into the file *fd*. The format
looks like::
Traceback (most recent call first):
File "xxx", line xxx in <xxx>
File "xxx", line xxx in <xxx>
...
File "xxx", line xxx in <xxx>
This function is meant to debug situations such as segfaults, fatal errors,
.etc. The file and function names it outputs are encoded to ASCII with
backslashreplace and truncated to 500 characters. It writes only the first
100 frames, further frames are truncated with the line " ...".
This function is safe to use from signal handlers.
The caller does not need to hold an :term:`attached thread state`, nor does
*tstate* need to be attached.
.. versionadded:: next
.. c:function:: const char* PyUnstable_DumpTracebackThreads(int fd, PyInterpreterState *interp, PyThreadState *current_tstate)
Write the traces of all Python threads in *interp* into the file *fd*.
If *interp* is ``NULL`` then this function will try to identify the current
interpreter using thread-specific storage. If it cannot, it will return an
error.
If *current_tstate* is not ``NULL`` then it will be used to identify what the
current thread is in the written output. If it is ``NULL`` then this function
will identify the current thread using thread-specific storage. It is not an
error if the function is unable to get the current Python thread state.
This function will return ``NULL`` on success, or an error message on error.
This function is meant to debug debug situations such as segfaults, fatal

Check warning on line 1388 in Doc/c-api/exceptions.rst

View workflow job for this annotation

GitHub Actions / Docs / Docs

c:func reference target not found: PyUnsafe_DumpTraceback [ref.func]
errors, .etc. It calls :c:func:`PyUnsafe_DumpTraceback` for each thread. It
only writes the tracebacks of the first 100 threads, further output is
truncated with the line " ...".
This function is safe to use from signal handlers.
The caller does not need to hold an :term:`attached thread state`, nor does
*current_tstate* need to be attached.
.. warning::
On the :term:`free-threaded build`, this function is not thread-safe. If
another thread deletes its :term:`thread state` while this function is being
called, the process will likely crash.
.. versionadded:: next
7 changes: 7 additions & 0 deletions Include/cpython/traceback.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,10 @@ struct _traceback {
int tb_lasti;
int tb_lineno;
};

PyAPI_FUNC(void) PyUnstable_DumpTraceback(int fd, PyThreadState *tstate);

PyAPI_FUNC(const char*) PyUnstable_DumpTracebackThreads(
int fd,
PyInterpreterState *interp,
PyThreadState *current_tstate);
49 changes: 0 additions & 49 deletions Include/internal/pycore_traceback.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,55 +14,6 @@ PyAPI_FUNC(int) _Py_DisplaySourceLine(PyObject *, PyObject *, int, int, int *, P
// Export for 'pyexact' shared extension
PyAPI_FUNC(void) _PyTraceback_Add(const char *, const char *, int);

/* Write the Python traceback into the file 'fd'. For example:
Traceback (most recent call first):
File "xxx", line xxx in <xxx>
File "xxx", line xxx in <xxx>
...
File "xxx", line xxx in <xxx>
This function is written for debug purpose only, to dump the traceback in
the worst case: after a segmentation fault, at fatal error, etc. That's why,
it is very limited. Strings are truncated to 100 characters and encoded to
ASCII with backslashreplace. It doesn't write the source code, only the
function name, filename and line number of each frame. Write only the first
100 frames: if the traceback is truncated, write the line " ...".
This function is signal safe. */

extern void _Py_DumpTraceback(
int fd,
PyThreadState *tstate);

/* Write the traceback of all threads into the file 'fd'. current_thread can be
NULL.
Return NULL on success, or an error message on error.
This function is written for debug purpose only. It calls
_Py_DumpTraceback() for each thread, and so has the same limitations. It
only write the traceback of the first 100 threads: write "..." if there are
more threads.
If current_tstate is NULL, the function tries to get the Python thread state
of the current thread. It is not an error if the function is unable to get
the current Python thread state.
If interp is NULL, the function tries to get the interpreter state from
the current Python thread state, or from
_PyGILState_GetInterpreterStateUnsafe() in last resort.
It is better to pass NULL to interp and current_tstate, the function tries
different options to retrieve this information.
This function is signal safe. */

extern const char* _Py_DumpTracebackThreads(
int fd,
PyInterpreterState *interp,
PyThreadState *current_tstate);

/* Write a Unicode object into the file descriptor fd. Encode the string to
ASCII using the backslashreplace error handler.
Expand Down
1 change: 0 additions & 1 deletion Include/traceback.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ PyAPI_FUNC(int) PyTraceBack_Print(PyObject *, PyObject *);
PyAPI_DATA(PyTypeObject) PyTraceBack_Type;
#define PyTraceBack_Check(v) Py_IS_TYPE((v), &PyTraceBack_Type)


#ifndef Py_LIMITED_API
# define Py_CPYTHON_TRACEBACK_H
# include "cpython/traceback.h"
Expand Down
15 changes: 8 additions & 7 deletions Modules/faulthandler.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
#include "pycore_runtime.h" // _Py_ID()
#include "pycore_signal.h" // Py_NSIG
#include "pycore_time.h" // _PyTime_FromSecondsObject()
#include "pycore_traceback.h" // _Py_DumpTracebackThreads
#include "pycore_traceback.h" // _Py_DumpStack()
#ifdef HAVE_UNISTD_H
# include <unistd.h> // _exit()
#endif
Expand Down Expand Up @@ -205,14 +205,15 @@ faulthandler_dump_traceback(int fd, int all_threads,
PyThreadState *tstate = PyGILState_GetThisThreadState();

if (all_threads == 1) {
(void)_Py_DumpTracebackThreads(fd, NULL, tstate);
(void)PyUnstable_DumpTracebackThreads(fd, NULL, tstate);
}
else {
if (all_threads == FT_IGNORE_ALL_THREADS) {
PUTS(fd, "<Cannot show all threads while the GIL is disabled>\n");
}
if (tstate != NULL)
_Py_DumpTraceback(fd, tstate);
if (tstate != NULL) {
PyUnstable_DumpTraceback(fd, tstate);
}
}

reentrant = 0;
Expand Down Expand Up @@ -273,7 +274,7 @@ faulthandler_dump_traceback_py_impl(PyObject *module, PyObject *file,
/* gh-128400: Accessing other thread states while they're running
* isn't safe if those threads are running. */
_PyEval_StopTheWorld(interp);
errmsg = _Py_DumpTracebackThreads(fd, NULL, tstate);
errmsg = PyUnstable_DumpTracebackThreads(fd, NULL, tstate);
_PyEval_StartTheWorld(interp);
if (errmsg != NULL) {
PyErr_SetString(PyExc_RuntimeError, errmsg);
Expand All @@ -282,7 +283,7 @@ faulthandler_dump_traceback_py_impl(PyObject *module, PyObject *file,
}
}
else {
_Py_DumpTraceback(fd, tstate);
PyUnstable_DumpTraceback(fd, tstate);
}
Py_XDECREF(file);

Expand Down Expand Up @@ -703,7 +704,7 @@ faulthandler_thread(void *unused)

(void)_Py_write_noraise(thread.fd, thread.header, (int)thread.header_len);

errmsg = _Py_DumpTracebackThreads(thread.fd, thread.interp, NULL);
errmsg = PyUnstable_DumpTracebackThreads(thread.fd, thread.interp, NULL);
ok = (errmsg == NULL);

if (thread.exit)
Expand Down
6 changes: 3 additions & 3 deletions Python/pylifecycle.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
#include "pycore_setobject.h" // _PySet_NextEntry()
#include "pycore_stats.h" // _PyStats_InterpInit()
#include "pycore_sysmodule.h" // _PySys_ClearAttrString()
#include "pycore_traceback.h" // _Py_DumpTracebackThreads()
#include "pycore_traceback.h" // _Py_DumpTraceback_Init()
#include "pycore_typeobject.h" // _PyTypes_InitTypes()
#include "pycore_typevarobject.h" // _Py_clear_generic_types()
#include "pycore_unicodeobject.h" // _PyUnicode_InitTypes()
Expand Down Expand Up @@ -3208,9 +3208,9 @@ _Py_FatalError_DumpTracebacks(int fd, PyInterpreterState *interp,

/* display the current Python stack */
#ifndef Py_GIL_DISABLED
_Py_DumpTracebackThreads(fd, interp, tstate);
PyUnstable_DumpTracebackThreads(fd, interp, tstate);
#else
_Py_DumpTraceback(fd, tstate);
PyUnstable_DumpTraceback(fd, tstate);
#endif
}

Expand Down
8 changes: 4 additions & 4 deletions Python/traceback.c
Original file line number Diff line number Diff line change
Expand Up @@ -1168,7 +1168,7 @@ dump_traceback(int fd, PyThreadState *tstate, int write_header)
The caller is responsible to call PyErr_CheckSignals() to call Python signal
handlers if signals were received. */
void
_Py_DumpTraceback(int fd, PyThreadState *tstate)
PyUnstable_DumpTraceback(int fd, PyThreadState *tstate)
{
dump_traceback(fd, tstate, 1);
}
Expand Down Expand Up @@ -1264,11 +1264,11 @@ write_thread_id(int fd, PyThreadState *tstate, int is_current)
The caller is responsible to call PyErr_CheckSignals() to call Python signal
handlers if signals were received. */
const char* _Py_NO_SANITIZE_THREAD
_Py_DumpTracebackThreads(int fd, PyInterpreterState *interp,
PyThreadState *current_tstate)
PyUnstable_DumpTracebackThreads(int fd, PyInterpreterState *interp,
PyThreadState *current_tstate)
{
if (current_tstate == NULL) {
/* _Py_DumpTracebackThreads() is called from signal handlers by
/* PyUnstable_DumpTracebackThreads() is called from signal handlers by
faulthandler.
SIGSEGV, SIGFPE, SIGABRT, SIGBUS and SIGILL are synchronous signals
Expand Down
2 changes: 1 addition & 1 deletion Tools/wasm/emscripten/node_entry.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,6 @@ try {
// Show JavaScript exception and traceback
console.warn(e);
// Show Python exception and traceback
Module.__Py_DumpTraceback(2, Module._PyGILState_GetThisThreadState());
Module.PyUnstable_DumpTraceback(2, Module._PyGILState_GetThisThreadState());
process.exit(1);
}
2 changes: 1 addition & 1 deletion Tools/wasm/emscripten/web_example_pyrepl_jspi/src.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,6 @@ try {
// Show JavaScript exception and traceback
console.warn(e);
// Show Python exception and traceback
Module.__Py_DumpTraceback(2, Module._PyGILState_GetThisThreadState());
Module.PyUnstable_DumpTraceback(2, Module._PyGILState_GetThisThreadState());
process.exit(1);
}
2 changes: 1 addition & 1 deletion configure

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -2357,7 +2357,7 @@ AS_CASE([$ac_sys_system],
dnl Include file system support
AS_VAR_APPEND([LINKFORSHARED], [" -sFORCE_FILESYSTEM -lidbfs.js -lnodefs.js -lproxyfs.js -lworkerfs.js"])
AS_VAR_APPEND([LINKFORSHARED], [" -sEXPORTED_RUNTIME_METHODS=FS,callMain,ENV,HEAPU32,TTY"])
AS_VAR_APPEND([LINKFORSHARED], [" -sEXPORTED_FUNCTIONS=_main,_Py_Version,__PyRuntime,_PyGILState_GetThisThreadState,__Py_DumpTraceback,__PyEM_EMSCRIPTEN_TRAMPOLINE_OFFSET"])
AS_VAR_APPEND([LINKFORSHARED], [" -sEXPORTED_FUNCTIONS=_main,_Py_Version,__PyRuntime,_PyGILState_GetThisThreadState,__PyEM_EMSCRIPTEN_TRAMPOLINE_OFFSET"])
AS_VAR_APPEND([LINKFORSHARED], [" -sSTACK_SIZE=5MB"])
dnl Avoid bugs in JS fallback string decoding path
AS_VAR_APPEND([LINKFORSHARED], [" -sTEXTDECODER=2"])
Expand Down
Loading