Issue #11393: signal of user signal displays tracebacks even if tstate==NULL
* faulthandler_user() displays the tracebacks of all threads even if it is unable to get the state of the current thread * test_faulthandler: only release the GIL in test_gil_released() check * create check_signum() subfunction
This commit is contained in:
parent
fcb88c4503
commit
44378d46f6
|
@ -8,6 +8,8 @@ from test import support, script_helper
|
||||||
import tempfile
|
import tempfile
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
TIMEOUT = 0.5
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from resource import setrlimit, RLIMIT_CORE, error as resource_error
|
from resource import setrlimit, RLIMIT_CORE, error as resource_error
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
@ -189,7 +191,7 @@ faulthandler._read_null(True)
|
||||||
import faulthandler
|
import faulthandler
|
||||||
output = open({filename}, 'wb')
|
output = open({filename}, 'wb')
|
||||||
faulthandler.enable(output)
|
faulthandler.enable(output)
|
||||||
faulthandler._read_null(True)
|
faulthandler._read_null()
|
||||||
""".strip().format(filename=repr(filename)),
|
""".strip().format(filename=repr(filename)),
|
||||||
4,
|
4,
|
||||||
'(?:Segmentation fault|Bus error)',
|
'(?:Segmentation fault|Bus error)',
|
||||||
|
@ -199,7 +201,7 @@ faulthandler._read_null(True)
|
||||||
self.check_fatal_error("""
|
self.check_fatal_error("""
|
||||||
import faulthandler
|
import faulthandler
|
||||||
faulthandler.enable(all_threads=True)
|
faulthandler.enable(all_threads=True)
|
||||||
faulthandler._read_null(True)
|
faulthandler._read_null()
|
||||||
""".strip(),
|
""".strip(),
|
||||||
3,
|
3,
|
||||||
'(?:Segmentation fault|Bus error)',
|
'(?:Segmentation fault|Bus error)',
|
||||||
|
@ -376,7 +378,7 @@ def func(repeat, cancel, timeout):
|
||||||
# Check that sleep() was not interrupted
|
# Check that sleep() was not interrupted
|
||||||
assert (b - a) >= min_pause, "{{}} < {{}}".format(b - a, min_pause)
|
assert (b - a) >= min_pause, "{{}} < {{}}".format(b - a, min_pause)
|
||||||
|
|
||||||
timeout = 0.5
|
timeout = {timeout}
|
||||||
repeat = {repeat}
|
repeat = {repeat}
|
||||||
cancel = {cancel}
|
cancel = {cancel}
|
||||||
if {has_filename}:
|
if {has_filename}:
|
||||||
|
@ -394,6 +396,7 @@ if file is not None:
|
||||||
has_filename=bool(filename),
|
has_filename=bool(filename),
|
||||||
repeat=repeat,
|
repeat=repeat,
|
||||||
cancel=cancel,
|
cancel=cancel,
|
||||||
|
timeout=TIMEOUT,
|
||||||
)
|
)
|
||||||
trace, exitcode = self.get_output(code, filename)
|
trace, exitcode = self.get_output(code, filename)
|
||||||
trace = '\n'.join(trace)
|
trace = '\n'.join(trace)
|
||||||
|
|
|
@ -65,6 +65,7 @@ typedef struct {
|
||||||
int fd;
|
int fd;
|
||||||
int all_threads;
|
int all_threads;
|
||||||
_Py_sighandler_t previous;
|
_Py_sighandler_t previous;
|
||||||
|
PyInterpreterState *interp;
|
||||||
} user_signal_t;
|
} user_signal_t;
|
||||||
|
|
||||||
static user_signal_t *user_signals;
|
static user_signal_t *user_signals;
|
||||||
|
@ -529,16 +530,36 @@ faulthandler_user(int signum)
|
||||||
the thread doesn't hold the GIL. Read the thread local storage (TLS)
|
the thread doesn't hold the GIL. Read the thread local storage (TLS)
|
||||||
instead: call PyGILState_GetThisThreadState(). */
|
instead: call PyGILState_GetThisThreadState(). */
|
||||||
tstate = PyGILState_GetThisThreadState();
|
tstate = PyGILState_GetThisThreadState();
|
||||||
if (tstate == NULL) {
|
|
||||||
/* unable to get the current thread, do nothing */
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (user->all_threads)
|
if (user->all_threads)
|
||||||
_Py_DumpTracebackThreads(user->fd, tstate->interp, tstate);
|
_Py_DumpTracebackThreads(user->fd, user->interp, tstate);
|
||||||
else
|
else {
|
||||||
|
if (tstate == NULL)
|
||||||
|
return;
|
||||||
_Py_DumpTraceback(user->fd, tstate);
|
_Py_DumpTraceback(user->fd, tstate);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
check_signum(int signum)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for (i=0; i < faulthandler_nsignals; i++) {
|
||||||
|
if (faulthandler_handlers[i].signum == signum) {
|
||||||
|
PyErr_Format(PyExc_RuntimeError,
|
||||||
|
"signal %i cannot be registered, "
|
||||||
|
"use enable() instead",
|
||||||
|
signum);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (signum < 1 || NSIG <= signum) {
|
||||||
|
PyErr_SetString(PyExc_ValueError, "signal number out of range");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
faulthandler_register(PyObject *self,
|
faulthandler_register(PyObject *self,
|
||||||
|
@ -549,12 +570,12 @@ faulthandler_register(PyObject *self,
|
||||||
PyObject *file = NULL;
|
PyObject *file = NULL;
|
||||||
int all_threads = 0;
|
int all_threads = 0;
|
||||||
int fd;
|
int fd;
|
||||||
unsigned int i;
|
|
||||||
user_signal_t *user;
|
user_signal_t *user;
|
||||||
_Py_sighandler_t previous;
|
_Py_sighandler_t previous;
|
||||||
#ifdef HAVE_SIGACTION
|
#ifdef HAVE_SIGACTION
|
||||||
struct sigaction action;
|
struct sigaction action;
|
||||||
#endif
|
#endif
|
||||||
|
PyThreadState *tstate;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if (!PyArg_ParseTupleAndKeywords(args, kwargs,
|
if (!PyArg_ParseTupleAndKeywords(args, kwargs,
|
||||||
|
@ -562,20 +583,16 @@ faulthandler_register(PyObject *self,
|
||||||
&signum, &file, &all_threads))
|
&signum, &file, &all_threads))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
if (signum < 1 || NSIG <= signum) {
|
if (!check_signum(signum))
|
||||||
PyErr_SetString(PyExc_ValueError, "signal number out of range");
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
|
||||||
|
|
||||||
for (i=0; i < faulthandler_nsignals; i++) {
|
/* The caller holds the GIL and so PyThreadState_Get() can be used */
|
||||||
if (faulthandler_handlers[i].signum == signum) {
|
tstate = PyThreadState_Get();
|
||||||
PyErr_Format(PyExc_RuntimeError,
|
if (tstate == NULL) {
|
||||||
"signal %i cannot be registered by register(), "
|
PyErr_SetString(PyExc_RuntimeError,
|
||||||
"use enable() instead",
|
"unable to get the current thread state");
|
||||||
signum);
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
file = faulthandler_get_fileno(file, &fd);
|
file = faulthandler_get_fileno(file, &fd);
|
||||||
if (file == NULL)
|
if (file == NULL)
|
||||||
|
@ -620,6 +637,7 @@ faulthandler_register(PyObject *self,
|
||||||
user->fd = fd;
|
user->fd = fd;
|
||||||
user->all_threads = all_threads;
|
user->all_threads = all_threads;
|
||||||
user->previous = previous;
|
user->previous = previous;
|
||||||
|
user->interp = tstate->interp;
|
||||||
user->enabled = 1;
|
user->enabled = 1;
|
||||||
|
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
|
@ -651,10 +669,8 @@ faulthandler_unregister_py(PyObject *self, PyObject *args)
|
||||||
if (!PyArg_ParseTuple(args, "i:unregister", &signum))
|
if (!PyArg_ParseTuple(args, "i:unregister", &signum))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
if (signum < 1 || NSIG <= signum) {
|
if (!check_signum(signum))
|
||||||
PyErr_SetString(PyExc_ValueError, "signal number out of range");
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
|
||||||
|
|
||||||
user = &user_signals[signum];
|
user = &user_signals[signum];
|
||||||
change = faulthandler_unregister(user, signum);
|
change = faulthandler_unregister(user, signum);
|
||||||
|
|
Loading…
Reference in New Issue