diff options
Diffstat (limited to 'Python/traceback.c')
-rw-r--r-- | Python/traceback.c | 241 |
1 files changed, 238 insertions, 3 deletions
diff --git a/Python/traceback.c b/Python/traceback.c index 59bb3f0d15..e74a1474df 100644 --- a/Python/traceback.c +++ b/Python/traceback.c @@ -13,8 +13,13 @@ #define OFF(x) offsetof(PyTracebackObject, x) -/* Method from Parser/tokenizer.c */ -extern char * PyTokenizer_FindEncoding(int); +#define PUTS(fd, str) write(fd, str, strlen(str)) +#define MAX_STRING_LENGTH 100 +#define MAX_FRAME_DEPTH 100 +#define MAX_NTHREADS 100 + +/* Function from Parser/tokenizer.c */ +extern char * PyTokenizer_FindEncodingFilename(int, PyObject *); static PyObject * tb_dir(PyTracebackObject *self) @@ -246,7 +251,7 @@ _Py_DisplaySourceLine(PyObject *f, PyObject *filename, int lineno, int indent) /* use the right encoding to decode the file as unicode */ fd = PyObject_AsFileDescriptor(binary); - found_encoding = PyTokenizer_FindEncoding(fd); + found_encoding = PyTokenizer_FindEncodingFilename(fd, filename); encoding = (found_encoding != NULL) ? found_encoding : "utf-8"; lseek(fd, 0, 0); /* Reset position */ fob = PyObject_CallMethod(io, "TextIOWrapper", "Os", binary, encoding); @@ -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 void +dump_traceback(int fd, PyThreadState *tstate, int write_header) +{ + PyFrameObject *frame; + unsigned int depth; + + if (write_header) + PUTS(fd, "Traceback (most recent call first):\n"); + + frame = _PyThreadState_GetFrame(tstate); + if (frame == NULL) + return; + + 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++; + } +} + +void +_Py_DumpTraceback(int fd, PyThreadState *tstate) +{ + 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; +} + |