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:
Victor Stinner 2011-04-01 15:37:12 +02:00
parent fcb88c4503
commit 44378d46f6
2 changed files with 43 additions and 24 deletions

View File

@ -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)

View File

@ -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);