*/
PyAPI_FUNC(PyThreadState *) PyGILState_GetThisThreadState(void);
-/* Helper/diagnostic function - return 1 if the current thread
- * currently holds the GIL, 0 otherwise
- */
#ifndef Py_LIMITED_API
/* Issue #26558: Flag to disable PyGILState_Check().
- If set, PyGILState_Check() always return 1. */
+ If set to non-zero, PyGILState_Check() always return 1. */
PyAPI_DATA(int) _PyGILState_check_enabled;
+/* Helper/diagnostic function - return 1 if the current thread
+ currently holds the GIL, 0 otherwise.
+
+ The function returns 1 if _PyGILState_check_enabled is non-zero. */
PyAPI_FUNC(int) PyGILState_Check(void);
+
+/* Unsafe function to get the single PyInterpreterState used by this process'
+ GILState implementation.
+
+ Return NULL before _PyGILState_Init() is called and after _PyGILState_Fini()
+ is called. */
+PyAPI_FUNC(PyInterpreterState *) _PyGILState_GetInterpreterStateUnsafe(void);
#endif
#endif /* #ifdef WITH_THREAD */
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.
+ 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 these informations.
+
This function is signal safe. */
PyAPI_DATA(const char*) _Py_DumpTracebackThreads(
- int fd, PyInterpreterState *interp,
- PyThreadState *current_thread);
-
+ int fd,
+ PyInterpreterState *interp,
+ PyThreadState *current_tstate);
#ifndef Py_LIMITED_API
pass_fds.append(fd)
with support.SuppressCrashReport():
process = script_helper.spawn_python('-c', code, pass_fds=pass_fds)
- stdout, stderr = process.communicate()
- exitcode = process.wait()
+ with process:
+ stdout, stderr = process.communicate()
+ exitcode = process.wait()
output = support.strip_python_stderr(stdout)
output = output.decode('ascii', 'backslashreplace')
if filename:
with open(fd, "rb", closefd=False) as fp:
output = fp.read()
output = output.decode('ascii', 'backslashreplace')
- output = re.sub('Current thread 0x[0-9a-f]+',
- 'Current thread XXX',
- output)
return output.splitlines(), exitcode
def check_fatal_error(self, code, line_number, name_regex,
filename=None, all_threads=True, other_regex=None,
- fd=None):
+ fd=None, know_current_thread=True):
"""
Check that the fault handler for fatal errors is enabled and check the
traceback from the child process output.
Raise an error if the output doesn't match the expected format.
"""
if all_threads:
- header = 'Current thread XXX (most recent call first)'
+ if know_current_thread:
+ header = 'Current thread 0x[0-9a-f]+'
+ else:
+ header = 'Thread 0x[0-9a-f]+'
else:
- header = 'Stack (most recent call first)'
+ header = 'Stack'
regex = """
^Fatal Python error: {name}
- {header}:
+ {header} \(most recent call first\):
File "<string>", line {lineno} in <module>
"""
regex = dedent(regex.format(
lineno=line_number,
name=name_regex,
- header=re.escape(header))).strip()
+ header=header)).strip()
if other_regex:
regex += '|' + other_regex
output, exitcode = self.get_output(code, filename=filename, fd=fd)
3,
'Segmentation fault')
+ @unittest.skipIf(not HAVE_THREADS, 'need threads')
+ def test_fatal_error_c_thread(self):
+ self.check_fatal_error("""
+ import faulthandler
+ faulthandler.enable()
+ faulthandler._fatal_error_c_thread()
+ """,
+ 3,
+ 'in new thread',
+ know_current_thread=False)
+
def test_sigabrt(self):
self.check_fatal_error("""
import faulthandler
File ".*threading.py", line [0-9]+ in _bootstrap_inner
File ".*threading.py", line [0-9]+ in _bootstrap
- Current thread XXX \(most recent call first\):
+ Current thread 0x[0-9a-f]+ \(most recent call first\):
File "<string>", line {lineno} in dump
File "<string>", line 28 in <module>$
"""
trace = '\n'.join(trace)
if not unregister:
if all_threads:
- regex = 'Current thread XXX \(most recent call first\):\n'
+ regex = 'Current thread 0x[0-9a-f]+ \(most recent call first\):\n'
else:
regex = 'Stack \(most recent call first\):\n'
regex = expected_traceback(14, 32, regex)
static PyThreadState*
get_thread_state(void)
{
- PyThreadState *tstate = PyThreadState_Get();
+ PyThreadState *tstate = _PyThreadState_UncheckedGet();
if (tstate == NULL) {
+ /* just in case but very unlikely... */
PyErr_SetString(PyExc_RuntimeError,
"unable to get the current thread state");
return NULL;
PyGILState_GetThisThreadState(). */
tstate = PyGILState_GetThisThreadState();
#else
- tstate = PyThreadState_Get();
+ tstate = _PyThreadState_UncheckedGet();
#endif
- if (all_threads)
- _Py_DumpTracebackThreads(fd, interp, tstate);
+ if (all_threads) {
+ (void)_Py_DumpTracebackThreads(fd, NULL, tstate);
+ }
else {
if (tstate != NULL)
_Py_DumpTraceback(fd, tstate);
return NULL;
if (all_threads) {
- errmsg = _Py_DumpTracebackThreads(fd, tstate->interp, tstate);
+ errmsg = _Py_DumpTracebackThreads(fd, NULL, tstate);
if (errmsg != NULL) {
PyErr_SetString(PyExc_RuntimeError, errmsg);
return NULL;
{
PyLockStatus st;
const char* errmsg;
- PyThreadState *current;
int ok;
#if defined(HAVE_PTHREAD_SIGMASK) && !defined(HAVE_BROKEN_PTHREAD_SIGMASK)
sigset_t set;
/* Timeout => dump traceback */
assert(st == PY_LOCK_FAILURE);
- /* get the thread holding the GIL, NULL if no thread hold the GIL */
- current = _PyThreadState_UncheckedGet();
-
_Py_write_noraise(thread.fd, thread.header, (int)thread.header_len);
- errmsg = _Py_DumpTracebackThreads(thread.fd, thread.interp, current);
+ errmsg = _Py_DumpTracebackThreads(thread.fd, thread.interp, NULL);
ok = (errmsg == NULL);
if (thread.exit)
faulthandler_sigsegv(PyObject *self, PyObject *args)
{
int release_gil = 0;
- if (!PyArg_ParseTuple(args, "|i:_read_null", &release_gil))
+ if (!PyArg_ParseTuple(args, "|i:_sigsegv", &release_gil))
return NULL;
if (release_gil) {
Py_RETURN_NONE;
}
+#ifdef WITH_THREAD
+static void
+faulthandler_fatal_error_thread(void *plock)
+{
+ PyThread_type_lock *lock = (PyThread_type_lock *)plock;
+
+ Py_FatalError("in new thread");
+
+ /* notify the caller that we are done */
+ PyThread_release_lock(lock);
+}
+
+static PyObject *
+faulthandler_fatal_error_c_thread(PyObject *self, PyObject *args)
+{
+ long thread;
+ PyThread_type_lock lock;
+
+ faulthandler_suppress_crash_report();
+
+ lock = PyThread_allocate_lock();
+ if (lock == NULL)
+ return PyErr_NoMemory();
+
+ PyThread_acquire_lock(lock, WAIT_LOCK);
+
+ thread = PyThread_start_new_thread(faulthandler_fatal_error_thread, lock);
+ if (thread == -1) {
+ PyThread_free_lock(lock);
+ PyErr_SetString(PyExc_RuntimeError, "unable to start the thread");
+ return NULL;
+ }
+
+ /* wait until the thread completes: it will never occur, since Py_FatalError()
+ exits the process immedialty. */
+ PyThread_acquire_lock(lock, WAIT_LOCK);
+ PyThread_release_lock(lock);
+ PyThread_free_lock(lock);
+
+ Py_RETURN_NONE;
+}
+#endif
+
static PyObject *
faulthandler_sigfpe(PyObject *self, PyObject *args)
{
"a SIGSEGV or SIGBUS signal depending on the platform")},
{"_sigsegv", faulthandler_sigsegv, METH_VARARGS,
PyDoc_STR("_sigsegv(release_gil=False): raise a SIGSEGV signal")},
+#ifdef WITH_THREAD
+ {"_fatal_error_c_thread", faulthandler_fatal_error_c_thread, METH_NOARGS,
+ PyDoc_STR("fatal_error_c_thread(): "
+ "call Py_FatalError() in a new C thread.")},
+#endif
{"_sigabrt", faulthandler_sigabrt, METH_NOARGS,
PyDoc_STR("_sigabrt(): raise a SIGABRT signal")},
{"_sigfpe", (PyCFunction)faulthandler_sigfpe, METH_NOARGS,
static void
_Py_FatalError_DumpTracebacks(int fd)
{
- PyThreadState *tstate;
-
-#ifdef WITH_THREAD
- /* PyGILState_GetThisThreadState() works even if the GIL was released */
- tstate = PyGILState_GetThisThreadState();
-#else
- tstate = PyThreadState_GET();
-#endif
- if (tstate == NULL) {
- /* _Py_DumpTracebackThreads() requires the thread state to display
- * frames */
- return;
- }
-
fputc('\n', stderr);
fflush(stderr);
/* display the current Python stack */
- _Py_DumpTracebackThreads(fd, tstate->interp, tstate);
+ _Py_DumpTracebackThreads(fd, NULL, NULL);
}
/* Print the current exception (if an exception is set) with its traceback,
_PyGILState_NoteThreadState(t);
}
+PyInterpreterState *
+_PyGILState_GetInterpreterStateUnsafe(void)
+{
+ return autoInterpreterState;
+}
+
void
_PyGILState_Fini(void)
{
handlers if signals were received. */
const char*
_Py_DumpTracebackThreads(int fd, PyInterpreterState *interp,
- PyThreadState *current_thread)
+ PyThreadState *current_tstate)
{
PyThreadState *tstate;
unsigned int nthreads;
+#ifdef WITH_THREAD
+ if (current_tstate == NULL) {
+ /* _Py_DumpTracebackThreads() is called from signal handlers by
+ faulthandler.
+
+ SIGSEGV, SIGFPE, SIGABRT, SIGBUS and SIGILL are synchronous signals
+ and are thus delivered to the thread that caused the fault. Get the
+ Python thread state of the current thread.
+
+ PyThreadState_Get() doesn't give the state of the thread that caused
+ the fault if the thread released the GIL, and so this function
+ cannot be used. Read the thread local storage (TLS) instead: call
+ PyGILState_GetThisThreadState(). */
+ current_tstate = PyGILState_GetThisThreadState();
+ }
+
+ if (interp == NULL) {
+ if (current_tstate == NULL) {
+ interp = _PyGILState_GetInterpreterStateUnsafe();
+ if (interp == NULL) {
+ /* We need the interpreter state to get Python threads */
+ return "unable to get the interpreter state";
+ }
+ }
+ else {
+ interp = current_tstate->interp;
+ }
+ }
+#else
+ if (current_tstate == NULL) {
+ /* Call _PyThreadState_UncheckedGet() instead of PyThreadState_Get()
+ to not fail with a fatal error if the thread state is NULL. */
+ current_thread = _PyThreadState_UncheckedGet();
+ }
+
+ if (interp == NULL) {
+ if (current_tstate == NULL) {
+ /* We need the interpreter state to get Python threads */
+ return "unable to get the interpreter state";
+ }
+ interp = current_tstate->interp;
+ }
+#endif
+ assert(interp != NULL);
+
/* Get the current interpreter from the current thread */
tstate = PyInterpreterState_ThreadHead(interp);
if (tstate == NULL)
PUTS(fd, "...\n");
break;
}
- write_thread_id(fd, tstate, tstate == current_thread);
+ write_thread_id(fd, tstate, tstate == current_tstate);
dump_traceback(fd, tstate, 0);
tstate = PyThreadState_Next(tstate);
nthreads++;