diff options
Diffstat (limited to 'Python/traceback.c')
| -rw-r--r-- | Python/traceback.c | 225 | 
1 files changed, 152 insertions, 73 deletions
| diff --git a/Python/traceback.c b/Python/traceback.c index 9e7fe3b..9f8c568 100644 --- a/Python/traceback.c +++ b/Python/traceback.c @@ -320,7 +320,7 @@ _Py_DisplaySourceLine(PyObject *f, PyObject *filename, int lineno, int indent)      if (fob == NULL) {          PyErr_Clear(); -        res = _PyObject_CallMethodId(binary, &PyId_close, ""); +        res = _PyObject_CallMethodId(binary, &PyId_close, NULL);          Py_DECREF(binary);          if (res)              Py_DECREF(res); @@ -340,7 +340,7 @@ _Py_DisplaySourceLine(PyObject *f, PyObject *filename, int lineno, int indent)              break;          }      } -    res = _PyObject_CallMethodId(fob, &PyId_close, ""); +    res = _PyObject_CallMethodId(fob, &PyId_close, NULL);      if (res)          Py_DECREF(res);      else @@ -418,6 +418,11 @@ tb_printinternal(PyTracebackObject *tb, PyObject *f, long limit)  {      int err = 0;      long depth = 0; +    PyObject *last_file = NULL; +    int last_line = -1; +    PyObject *last_name = NULL; +    long cnt = 0; +    PyObject *line;      PyTracebackObject *tb1 = tb;      while (tb1 != NULL) {          depth++; @@ -425,16 +430,41 @@ tb_printinternal(PyTracebackObject *tb, PyObject *f, long limit)      }      while (tb != NULL && err == 0) {          if (depth <= limit) { -            err = tb_displayline(f, -                                 tb->tb_frame->f_code->co_filename, -                                 tb->tb_lineno, -                                 tb->tb_frame->f_code->co_name); +            if (last_file != NULL && +                tb->tb_frame->f_code->co_filename == last_file && +                last_line != -1 && tb->tb_lineno == last_line && +                last_name != NULL && +                tb->tb_frame->f_code->co_name == last_name) { +                    cnt++; +                } else { +                    if (cnt > 3) { +                        line = PyUnicode_FromFormat( +                        "  [Previous line repeated %d more times]\n", cnt-3); +                        err = PyFile_WriteObject(line, f, Py_PRINT_RAW); +                        Py_DECREF(line); +                    } +                    last_file = tb->tb_frame->f_code->co_filename; +                    last_line = tb->tb_lineno; +                    last_name = tb->tb_frame->f_code->co_name; +                    cnt = 0; +                } +            if (cnt < 3) +                err = tb_displayline(f, +                                     tb->tb_frame->f_code->co_filename, +                                     tb->tb_lineno, +                                     tb->tb_frame->f_code->co_name);          }          depth--;          tb = tb->tb_next;          if (err == 0)              err = PyErr_CheckSignals();      } +    if (cnt > 3) { +        line = PyUnicode_FromFormat( +        "  [Previous line repeated %d more times]\n", cnt-3); +        err = PyFile_WriteObject(line, f, Py_PRINT_RAW); +        Py_DECREF(line); +    }      return err;  } @@ -485,40 +515,26 @@ PyTraceBack_Print(PyObject *v, PyObject *f)     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) +void +_Py_DumpDecimal(int fd, unsigned long value)  { -    char buffer[7]; -    int len; -    if (value < 0 || 999999 < value) -        return; -    len = 0; +    /* maximum number of characters required for output of %lld or %p. +       We need at most ceil(log10(256)*SIZEOF_LONG_LONG) digits, +       plus 1 for the null byte.  53/22 is an upper bound for log10(256). */ +    char buffer[1 + (sizeof(unsigned long)*53-1) / 22 + 1]; +    char *ptr, *end; + +    end = &buffer[Py_ARRAY_LENGTH(buffer) - 1]; +    ptr = end; +    *ptr = '\0';      do { -        buffer[len] = '0' + (value % 10); +        --ptr; +        assert(ptr >= buffer); +        *ptr = '0' + (value % 10);          value /= 10; -        len++;      } while (value); -    reverse_string(buffer, len); -    _Py_write_noraise(fd, buffer, len); + +    _Py_write_noraise(fd, ptr, end - ptr);  }  /* Format an integer in range [0; 0xffffffff] to hexadecimal of 'width' digits, @@ -526,27 +542,31 @@ dump_decimal(int fd, int value)     This function is signal safe. */ -static void -dump_hexadecimal(int fd, unsigned long value, int width) +void +_Py_DumpHexadecimal(int fd, unsigned long value, Py_ssize_t width)  { -    int len; -    char buffer[sizeof(unsigned long) * 2 + 1]; -    len = 0; +    char buffer[sizeof(unsigned long) * 2 + 1], *ptr, *end; +    const Py_ssize_t size = Py_ARRAY_LENGTH(buffer) - 1; + +    if (width > size) +        width = size; +    /* it's ok if width is negative */ + +    end = &buffer[size]; +    ptr = end; +    *ptr = '\0';      do { -        buffer[len] = Py_hexdigits[value & 15]; +        --ptr; +        assert(ptr >= buffer); +        *ptr = Py_hexdigits[value & 15];          value >>= 4; -        len++; -    } while (len < width || value); -    reverse_string(buffer, len); -    _Py_write_noraise(fd, buffer, len); -} - -/* Write an unicode object into the file fd using ascii+backslashreplace. +    } while ((end - ptr) < width || value); -   This function is signal safe. */ +    _Py_write_noraise(fd, ptr, end - ptr); +} -static void -dump_ascii(int fd, PyObject *text) +void +_Py_DumpASCII(int fd, PyObject *text)  {      PyASCIIObject *ascii = (PyASCIIObject *)text;      Py_ssize_t i, size; @@ -556,32 +576,36 @@ dump_ascii(int fd, PyObject *text)      wchar_t *wstr = NULL;      Py_UCS4 ch; +    if (!PyUnicode_Check(text)) +        return; +      size = ascii->length;      kind = ascii->state.kind; -    if (ascii->state.compact) { +    if (kind == PyUnicode_WCHAR_KIND) { +        wstr = ((PyASCIIObject *)text)->wstr; +        if (wstr == NULL) +            return; +        size = ((PyCompactUnicodeObject *)text)->wstr_length; +    } +    else if (ascii->state.compact) {          if (ascii->state.ascii)              data = ((PyASCIIObject*)text) + 1;          else              data = ((PyCompactUnicodeObject*)text) + 1;      } -    else if (kind != PyUnicode_WCHAR_KIND) { +    else {          data = ((PyUnicodeObject *)text)->data.any;          if (data == NULL)              return;      } -    else { -        wstr = ((PyASCIIObject *)text)->wstr; -        if (wstr == NULL) -            return; -        size = ((PyCompactUnicodeObject *)text)->wstr_length; -    }      if (MAX_STRING_LENGTH < size) {          size = MAX_STRING_LENGTH;          truncated = 1;      } -    else +    else {          truncated = 0; +    }      for (i=0; i < size; i++) {          if (kind != PyUnicode_WCHAR_KIND) @@ -595,19 +619,20 @@ dump_ascii(int fd, PyObject *text)          }          else if (ch <= 0xff) {              PUTS(fd, "\\x"); -            dump_hexadecimal(fd, ch, 2); +            _Py_DumpHexadecimal(fd, ch, 2);          }          else if (ch <= 0xffff) {              PUTS(fd, "\\u"); -            dump_hexadecimal(fd, ch, 4); +            _Py_DumpHexadecimal(fd, ch, 4);          }          else {              PUTS(fd, "\\U"); -            dump_hexadecimal(fd, ch, 8); +            _Py_DumpHexadecimal(fd, ch, 8);          }      } -    if (truncated) +    if (truncated) {          PUTS(fd, "..."); +    }  }  /* Write a frame into the file fd: "File "xxx", line xxx in xxx". @@ -626,7 +651,7 @@ dump_frame(int fd, PyFrameObject *frame)          && PyUnicode_Check(code->co_filename))      {          PUTS(fd, "\""); -        dump_ascii(fd, code->co_filename); +        _Py_DumpASCII(fd, code->co_filename);          PUTS(fd, "\"");      } else {          PUTS(fd, "???"); @@ -635,14 +660,21 @@ dump_frame(int fd, PyFrameObject *frame)      /* PyFrame_GetLineNumber() was introduced in Python 2.7.0 and 3.2.0 */      lineno = PyCode_Addr2Line(code, frame->f_lasti);      PUTS(fd, ", line "); -    dump_decimal(fd, lineno); +    if (lineno >= 0) { +        _Py_DumpDecimal(fd, (unsigned long)lineno); +    } +    else { +        PUTS(fd, "???"); +    }      PUTS(fd, " in ");      if (code != NULL && code->co_name != NULL -        && PyUnicode_Check(code->co_name)) -        dump_ascii(fd, code->co_name); -    else +       && PyUnicode_Check(code->co_name)) { +        _Py_DumpASCII(fd, code->co_name); +    } +    else {          PUTS(fd, "???"); +    }      PUTS(fd, "\n");  } @@ -698,7 +730,9 @@ write_thread_id(int fd, PyThreadState *tstate, int is_current)          PUTS(fd, "Current thread 0x");      else          PUTS(fd, "Thread 0x"); -    dump_hexadecimal(fd, (unsigned long)tstate->thread_id, sizeof(unsigned long)*2); +    _Py_DumpHexadecimal(fd, +                        (unsigned long)tstate->thread_id, +                        sizeof(unsigned long) * 2);      PUTS(fd, " (most recent call first):\n");  } @@ -710,11 +744,56 @@ write_thread_id(int fd, PyThreadState *tstate, int is_current)     handlers if signals were received. */  const char*  _Py_DumpTracebackThreads(int fd, PyInterpreterState *interp, -                         PyThreadState *current_thread) +                         PyThreadState *current_tstate)  {      PyThreadState *tstate;      unsigned int nthreads; +#ifdef WITH_THREAD +    if (current_tstate == NULL) { +        /* _Py_DumpTracebackThreads() is called from signal handlers by +           faulthandler. + +           SIGSEGV, SIGFPE, SIGABRT, SIGBUS and SIGILL are synchronous signals +           and are thus delivered to the thread that caused the fault. Get the +           Python thread state of the current thread. + +           PyThreadState_Get() doesn't give the state of the thread that caused +           the fault if the thread released the GIL, and so this function +           cannot be used. Read the thread local storage (TLS) instead: call +           PyGILState_GetThisThreadState(). */ +        current_tstate = PyGILState_GetThisThreadState(); +    } + +    if (interp == NULL) { +        if (current_tstate == NULL) { +            interp = _PyGILState_GetInterpreterStateUnsafe(); +            if (interp == NULL) { +                /* We need the interpreter state to get Python threads */ +                return "unable to get the interpreter state"; +            } +        } +        else { +            interp = current_tstate->interp; +        } +    } +#else +    if (current_tstate == NULL) { +        /* Call _PyThreadState_UncheckedGet() instead of PyThreadState_Get() +           to not fail with a fatal error if the thread state is NULL. */ +        current_tstate = _PyThreadState_UncheckedGet(); +    } + +    if (interp == NULL) { +        if (current_tstate == NULL) { +            /* We need the interpreter state to get Python threads */ +            return "unable to get the interpreter state"; +        } +        interp = current_tstate->interp; +    } +#endif +    assert(interp != NULL); +      /* Get the current interpreter from the current thread */      tstate = PyInterpreterState_ThreadHead(interp);      if (tstate == NULL) @@ -732,7 +811,7 @@ _Py_DumpTracebackThreads(int fd, PyInterpreterState *interp,              PUTS(fd, "...\n");              break;          } -        write_thread_id(fd, tstate, tstate == current_thread); +        write_thread_id(fd, tstate, tstate == current_tstate);          dump_traceback(fd, tstate, 0);          tstate = PyThreadState_Next(tstate);          nthreads++; | 
