faulthandler: dump_tracebacks_later() displays also the timeout
This commit is contained in:
parent
941893291a
commit
c790a5346d
|
@ -1,4 +1,5 @@
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
|
import datetime
|
||||||
import faulthandler
|
import faulthandler
|
||||||
import re
|
import re
|
||||||
import signal
|
import signal
|
||||||
|
@ -360,6 +361,7 @@ Current thread XXX:
|
||||||
|
|
||||||
Raise an error if the output doesn't match the expect format.
|
Raise an error if the output doesn't match the expect format.
|
||||||
"""
|
"""
|
||||||
|
timeout_str = str(datetime.timedelta(seconds=TIMEOUT))
|
||||||
code = """
|
code = """
|
||||||
import faulthandler
|
import faulthandler
|
||||||
import time
|
import time
|
||||||
|
@ -399,7 +401,7 @@ if file is not None:
|
||||||
count = loops
|
count = loops
|
||||||
if repeat:
|
if repeat:
|
||||||
count *= 2
|
count *= 2
|
||||||
header = 'Thread 0x[0-9a-f]+:\n'
|
header = r'Timeout \(%s\)!\nThread 0x[0-9a-f]+:\n' % timeout_str
|
||||||
regex = expected_traceback(9, 20, header, count=count)
|
regex = expected_traceback(9, 20, header, count=count)
|
||||||
self.assertRegex(trace, regex)
|
self.assertRegex(trace, regex)
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -50,6 +50,8 @@ static struct {
|
||||||
int repeat;
|
int repeat;
|
||||||
PyInterpreterState *interp;
|
PyInterpreterState *interp;
|
||||||
int exit;
|
int exit;
|
||||||
|
char *header;
|
||||||
|
size_t header_len;
|
||||||
/* The main thread always hold this lock. It is only released when
|
/* The main thread always hold this lock. It is only released when
|
||||||
faulthandler_thread() is interrupted until this thread exits, or at
|
faulthandler_thread() is interrupted until this thread exits, or at
|
||||||
Python exit. */
|
Python exit. */
|
||||||
|
@ -424,6 +426,8 @@ faulthandler_thread(void *unused)
|
||||||
/* get the thread holding the GIL, NULL if no thread hold the GIL */
|
/* get the thread holding the GIL, NULL if no thread hold the GIL */
|
||||||
current = _Py_atomic_load_relaxed(&_PyThreadState_Current);
|
current = _Py_atomic_load_relaxed(&_PyThreadState_Current);
|
||||||
|
|
||||||
|
write(thread.fd, thread.header, thread.header_len);
|
||||||
|
|
||||||
errmsg = _Py_DumpTracebackThreads(thread.fd, thread.interp, current);
|
errmsg = _Py_DumpTracebackThreads(thread.fd, thread.interp, current);
|
||||||
ok = (errmsg == NULL);
|
ok = (errmsg == NULL);
|
||||||
|
|
||||||
|
@ -449,6 +453,37 @@ cancel_dump_tracebacks_later(void)
|
||||||
PyThread_acquire_lock(thread.cancel_event, 1);
|
PyThread_acquire_lock(thread.cancel_event, 1);
|
||||||
|
|
||||||
Py_CLEAR(thread.file);
|
Py_CLEAR(thread.file);
|
||||||
|
if (thread.header) {
|
||||||
|
free(thread.header);
|
||||||
|
thread.header = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static char*
|
||||||
|
format_timeout(double timeout)
|
||||||
|
{
|
||||||
|
unsigned long us, sec, min, hour;
|
||||||
|
double intpart, fracpart;
|
||||||
|
char buffer[100];
|
||||||
|
|
||||||
|
fracpart = modf(timeout, &intpart);
|
||||||
|
sec = (unsigned long)intpart;
|
||||||
|
us = (unsigned long)(fracpart * 1e6);
|
||||||
|
min = sec / 60;
|
||||||
|
sec %= 60;
|
||||||
|
hour = min / 60;
|
||||||
|
min %= 60;
|
||||||
|
|
||||||
|
if (us != 0)
|
||||||
|
PyOS_snprintf(buffer, sizeof(buffer),
|
||||||
|
"Timeout (%lu:%02lu:%02lu.%06lu)!\n",
|
||||||
|
hour, min, sec, us);
|
||||||
|
else
|
||||||
|
PyOS_snprintf(buffer, sizeof(buffer),
|
||||||
|
"Timeout (%lu:%02lu:%02lu)!\n",
|
||||||
|
hour, min, sec);
|
||||||
|
|
||||||
|
return strdup(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
|
@ -463,17 +498,18 @@ faulthandler_dump_tracebacks_later(PyObject *self,
|
||||||
int fd;
|
int fd;
|
||||||
int exit = 0;
|
int exit = 0;
|
||||||
PyThreadState *tstate;
|
PyThreadState *tstate;
|
||||||
|
char *header;
|
||||||
|
size_t header_len;
|
||||||
|
|
||||||
if (!PyArg_ParseTupleAndKeywords(args, kwargs,
|
if (!PyArg_ParseTupleAndKeywords(args, kwargs,
|
||||||
"d|iOi:dump_tracebacks_later", kwlist,
|
"d|iOi:dump_tracebacks_later", kwlist,
|
||||||
&timeout, &repeat, &file, &exit))
|
&timeout, &repeat, &file, &exit))
|
||||||
return NULL;
|
return NULL;
|
||||||
timeout *= 1e6;
|
if ((timeout * 1e6) >= (double) PY_TIMEOUT_MAX) {
|
||||||
if (timeout >= (double) PY_TIMEOUT_MAX) {
|
|
||||||
PyErr_SetString(PyExc_OverflowError, "timeout value is too large");
|
PyErr_SetString(PyExc_OverflowError, "timeout value is too large");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
timeout_us = (PY_TIMEOUT_T)timeout;
|
timeout_us = (PY_TIMEOUT_T)(timeout * 1e6);
|
||||||
if (timeout_us <= 0) {
|
if (timeout_us <= 0) {
|
||||||
PyErr_SetString(PyExc_ValueError, "timeout must be greater than 0");
|
PyErr_SetString(PyExc_ValueError, "timeout must be greater than 0");
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -490,6 +526,12 @@ faulthandler_dump_tracebacks_later(PyObject *self,
|
||||||
if (file == NULL)
|
if (file == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
/* format the timeout */
|
||||||
|
header = format_timeout(timeout);
|
||||||
|
if (header == NULL)
|
||||||
|
return PyErr_NoMemory();
|
||||||
|
header_len = strlen(header);
|
||||||
|
|
||||||
/* Cancel previous thread, if running */
|
/* Cancel previous thread, if running */
|
||||||
cancel_dump_tracebacks_later();
|
cancel_dump_tracebacks_later();
|
||||||
|
|
||||||
|
@ -501,6 +543,8 @@ faulthandler_dump_tracebacks_later(PyObject *self,
|
||||||
thread.repeat = repeat;
|
thread.repeat = repeat;
|
||||||
thread.interp = tstate->interp;
|
thread.interp = tstate->interp;
|
||||||
thread.exit = exit;
|
thread.exit = exit;
|
||||||
|
thread.header = header;
|
||||||
|
thread.header_len = header_len;
|
||||||
|
|
||||||
/* Arm these locks to serve as events when released */
|
/* Arm these locks to serve as events when released */
|
||||||
PyThread_acquire_lock(thread.running, 1);
|
PyThread_acquire_lock(thread.running, 1);
|
||||||
|
@ -508,6 +552,8 @@ faulthandler_dump_tracebacks_later(PyObject *self,
|
||||||
if (PyThread_start_new_thread(faulthandler_thread, NULL) == -1) {
|
if (PyThread_start_new_thread(faulthandler_thread, NULL) == -1) {
|
||||||
PyThread_release_lock(thread.running);
|
PyThread_release_lock(thread.running);
|
||||||
Py_CLEAR(thread.file);
|
Py_CLEAR(thread.file);
|
||||||
|
free(header);
|
||||||
|
thread.header = NULL;
|
||||||
PyErr_SetString(PyExc_RuntimeError,
|
PyErr_SetString(PyExc_RuntimeError,
|
||||||
"unable to start watchdog thread");
|
"unable to start watchdog thread");
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
Loading…
Reference in New Issue