This function returns ``0`` on success, and returns ``-1`` with an
exception set on failure.
+
+.. c:function:: const char* 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,
+ and similar. 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 will return ``NULL`` on success, or an error message on error.
+
+ This function is intended for use in crash scenarios such as signal handlers
+ for SIGSEGV, where the interpreter may be in an inconsistent state. Given
+ that it reads interpreter data structures that may be partially modified, the
+ function might produce incomplete output or it may even crash itself.
+
+ 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, Py_ssize_t max_threads)
+
+ 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
+ errors, and similar. It calls :c:func:`PyUnstable_DumpTraceback` for each
+ thread. It only writes the tracebacks of the first *max_threads* threads,
+ further output is truncated with the line ``...``. If *max_threads* is 0, the
+ function will use a default value of 100 for the argument.
+
+ This function is intended for use in crash scenarios such as signal handlers
+ for SIGSEGV, where the interpreter may be in an inconsistent state. Given
+ that it reads interpreter data structures that may be partially modified, the
+ function might produce incomplete output or it may even crash itself.
+
+ 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
Python 3.14.
(Contributed by Victor Stinner in :gh:`142417`.)
+* Add :c:func:`PyUnstable_DumpTraceback` and
+ :c:func:`PyUnstable_DumpTracebackThreads` functions to output Python
+ stacktraces.
+ (Contributed by Alex Malyshev in :gh:`145559`.)
+
Changed C APIs
--------------
int tb_lasti;
int tb_lineno;
};
+
+PyAPI_FUNC(const char*) PyUnstable_DumpTraceback(int fd, PyThreadState *tstate);
+
+PyAPI_FUNC(const char*) PyUnstable_DumpTracebackThreads(
+ int fd,
+ PyInterpreterState *interp,
+ PyThreadState *current_tstate,
+ Py_ssize_t max_threads);
// 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,
- Py_ssize_t max_threads);
-
/* Write a Unicode object into the file descriptor fd. Encode the string to
ASCII using the backslashreplace error handler.
--- /dev/null
+Rename ``_Py_DumpTraceback`` and ``_Py_DumpTracebackThreads`` to
+:c:func:`PyUnstable_DumpTraceback` and
+:c:func:`PyUnstable_DumpTracebackThreads`.
#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
PyThreadState *tstate = PyGILState_GetThisThreadState();
if (all_threads == 1) {
- (void)_Py_DumpTracebackThreads(fd, NULL, tstate, max_threads);
+ (void)PyUnstable_DumpTracebackThreads(fd, NULL, tstate, max_threads);
}
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;
/* 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, max_threads);
+ errmsg = PyUnstable_DumpTracebackThreads(fd, NULL, tstate, max_threads);
_PyEval_StartTheWorld(interp);
- if (errmsg != NULL) {
- PyErr_SetString(PyExc_RuntimeError, errmsg);
- Py_XDECREF(file);
- return NULL;
- }
}
else {
- _Py_DumpTraceback(fd, tstate);
+ errmsg = PyUnstable_DumpTraceback(fd, tstate);
}
+ if (errmsg != NULL) {
+ PyErr_SetString(PyExc_RuntimeError, errmsg);
+ Py_XDECREF(file);
+ return NULL;
+ }
+
Py_XDECREF(file);
if (PyErr_CheckSignals())
(void)_Py_write_noraise(thread.fd, thread.header, (int)thread.header_len);
- errmsg = _Py_DumpTracebackThreads(thread.fd, thread.interp, NULL,
- thread.max_threads);
+ errmsg = PyUnstable_DumpTracebackThreads(thread.fd, thread.interp, NULL,
+ thread.max_threads);
ok = (errmsg == NULL);
if (thread.exit)
// 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);
}
// 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);
}
#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" // PyUnstable_TracebackThreads()
#include "pycore_tuple.h" // _PyTuple_FromPair
#include "pycore_typeobject.h" // _PyTypes_InitTypes()
#include "pycore_typevarobject.h" // _Py_clear_generic_types()
/* display the current Python stack */
#ifndef Py_GIL_DISABLED
- _Py_DumpTracebackThreads(fd, interp, tstate, 0);
+ PyUnstable_DumpTracebackThreads(fd, interp, tstate, 0);
#else
- _Py_DumpTraceback(fd, tstate);
+ PyUnstable_DumpTraceback(fd, tstate);
#endif
}
The caller is responsible to call PyErr_CheckSignals() to call Python signal
handlers if signals were received. */
-void
-_Py_DumpTraceback(int fd, PyThreadState *tstate)
+const char*
+PyUnstable_DumpTraceback(int fd, PyThreadState *tstate)
{
dump_traceback(fd, tstate, 1);
+ return NULL;
}
#if defined(HAVE_PTHREAD_GETNAME_NP) || defined(HAVE_PTHREAD_GET_NAME_NP)
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,
- Py_ssize_t max_threads)
+PyUnstable_DumpTracebackThreads(int fd, PyInterpreterState *interp,
+ PyThreadState *current_tstate,
+ Py_ssize_t max_threads)
{
if (max_threads == 0) {
max_threads = DEFAULT_MAX_NTHREADS;
}
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
as_fn_append LINKFORSHARED " -sFORCE_FILESYSTEM -lidbfs.js -lnodefs.js -lproxyfs.js -lworkerfs.js"
as_fn_append LINKFORSHARED " -sEXPORTED_RUNTIME_METHODS=FS,callMain,ENV,HEAPU32,TTY,ERRNO_CODES"
- as_fn_append LINKFORSHARED " -sEXPORTED_FUNCTIONS=_main,_Py_Version,__PyRuntime,_PyGILState_GetThisThreadState,__Py_DumpTraceback,__PyEM_EMSCRIPTEN_TRAMPOLINE_OFFSET"
+ as_fn_append LINKFORSHARED " -sEXPORTED_FUNCTIONS=_main,_Py_Version,__PyRuntime,_PyGILState_GetThisThreadState,__PyEM_EMSCRIPTEN_TRAMPOLINE_OFFSET"
as_fn_append LINKFORSHARED " -sSTACK_SIZE=5MB"
as_fn_append LINKFORSHARED " -sTEXTDECODER=2"
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,ERRNO_CODES"])
- 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"])