Issue #11393: Add the new faulthandler module

This commit is contained in:
Victor Stinner 2011-03-31 01:31:06 +02:00
parent d85456279f
commit 024e37adcc
19 changed files with 1907 additions and 5 deletions

View file

@ -13,6 +13,11 @@
#define OFF(x) offsetof(PyTracebackObject, x)
#define PUTS(fd, str) write(fd, str, strlen(str))
#define MAX_STRING_LENGTH 100
#define MAX_FRAME_DEPTH 100
#define MAX_NTHREADS 100
/* Method from Parser/tokenizer.c */
extern char * PyTokenizer_FindEncoding(int);
@ -402,3 +407,233 @@ PyTraceBack_Print(PyObject *v, PyObject *f)
err = tb_printinternal((PyTracebackObject *)v, f, limit);
return err;
}
/* Reverse a string. For example, "abcd" becomes "dcba".
This function is signal safe. */
static void
reverse_string(char *text, const size_t len)
{
char tmp;
size_t i, j;
if (len == 0)
return;
for (i=0, j=len-1; i < j; i++, j--) {
tmp = text[i];
text[i] = text[j];
text[j] = tmp;
}
}
/* Format an integer in range [0; 999999] to decimal,
and write it into the file fd.
This function is signal safe. */
static void
dump_decimal(int fd, int value)
{
char buffer[7];
int len;
if (value < 0 || 999999 < value)
return;
len = 0;
do {
buffer[len] = '0' + (value % 10);
value /= 10;
len++;
} while (value);
reverse_string(buffer, len);
write(fd, buffer, len);
}
/* Format an integer in range [0; 0xffffffff] to hexdecimal of 'width' digits,
and write it into the file fd.
This function is signal safe. */
static void
dump_hexadecimal(int width, unsigned long value, int fd)
{
const char *hexdigits = "0123456789abcdef";
int len;
char buffer[sizeof(unsigned long) * 2 + 1];
len = 0;
do {
buffer[len] = hexdigits[value & 15];
value >>= 4;
len++;
} while (len < width || value);
reverse_string(buffer, len);
write(fd, buffer, len);
}
/* Write an unicode object into the file fd using ascii+backslashreplace.
This function is signal safe. */
static void
dump_ascii(int fd, PyObject *text)
{
Py_ssize_t i, size;
int truncated;
Py_UNICODE *u;
char c;
size = PyUnicode_GET_SIZE(text);
u = PyUnicode_AS_UNICODE(text);
if (MAX_STRING_LENGTH < size) {
size = MAX_STRING_LENGTH;
truncated = 1;
}
else
truncated = 0;
for (i=0; i < size; i++, u++) {
if (*u < 128) {
c = (char)*u;
write(fd, &c, 1);
}
else if (*u < 256) {
PUTS(fd, "\\x");
dump_hexadecimal(2, *u, fd);
}
else
#ifdef Py_UNICODE_WIDE
if (*u < 65536)
#endif
{
PUTS(fd, "\\u");
dump_hexadecimal(4, *u, fd);
#ifdef Py_UNICODE_WIDE
}
else {
PUTS(fd, "\\U");
dump_hexadecimal(8, *u, fd);
#endif
}
}
if (truncated)
PUTS(fd, "...");
}
/* Write a frame into the file fd: "File "xxx", line xxx in xxx".
This function is signal safe. */
static void
dump_frame(int fd, PyFrameObject *frame)
{
PyCodeObject *code;
int lineno;
code = frame->f_code;
PUTS(fd, " File ");
if (code != NULL && code->co_filename != NULL
&& PyUnicode_Check(code->co_filename))
{
write(fd, "\"", 1);
dump_ascii(fd, code->co_filename);
write(fd, "\"", 1);
} else {
PUTS(fd, "???");
}
/* PyFrame_GetLineNumber() was introduced in Python 2.7.0 and 3.2.0 */
lineno = PyCode_Addr2Line(frame->f_code, frame->f_lasti);
PUTS(fd, ", line ");
dump_decimal(fd, lineno);
PUTS(fd, " in ");
if (code != NULL && code->co_name != NULL
&& PyUnicode_Check(code->co_name))
dump_ascii(fd, code->co_name);
else
PUTS(fd, "???");
write(fd, "\n", 1);
}
static int
dump_traceback(int fd, PyThreadState *tstate, int write_header)
{
PyFrameObject *frame;
unsigned int depth;
frame = _PyThreadState_GetFrame(tstate);
if (frame == NULL)
return -1;
if (write_header)
PUTS(fd, "Traceback (most recent call first):\n");
depth = 0;
while (frame != NULL) {
if (MAX_FRAME_DEPTH <= depth) {
PUTS(fd, " ...\n");
break;
}
if (!PyFrame_Check(frame))
break;
dump_frame(fd, frame);
frame = frame->f_back;
depth++;
}
return 0;
}
int
_Py_DumpTraceback(int fd, PyThreadState *tstate)
{
return dump_traceback(fd, tstate, 1);
}
/* Write the thread identifier into the file 'fd': "Current thread 0xHHHH:\" if
is_current is true, "Thread 0xHHHH:\n" otherwise.
This function is signal safe. */
static void
write_thread_id(int fd, PyThreadState *tstate, int is_current)
{
if (is_current)
PUTS(fd, "Current thread 0x");
else
PUTS(fd, "Thread 0x");
dump_hexadecimal(sizeof(long)*2, (unsigned long)tstate->thread_id, fd);
PUTS(fd, ":\n");
}
const char*
_Py_DumpTracebackThreads(int fd, PyInterpreterState *interp,
PyThreadState *current_thread)
{
PyThreadState *tstate;
unsigned int nthreads;
/* Get the current interpreter from the current thread */
tstate = PyInterpreterState_ThreadHead(interp);
if (tstate == NULL)
return "unable to get the thread head state";
/* Dump the traceback of each thread */
tstate = PyInterpreterState_ThreadHead(interp);
nthreads = 0;
do
{
if (nthreads != 0)
write(fd, "\n", 1);
if (nthreads >= MAX_NTHREADS) {
PUTS(fd, "...\n");
break;
}
write_thread_id(fd, tstate, tstate == current_thread);
dump_traceback(fd, tstate, 0);
tstate = PyThreadState_Next(tstate);
nthreads++;
} while (tstate != NULL);
return NULL;
}