LCOV - code coverage report
Current view: | top level - Python - traceback.c (source / functions) |  |  | Hit | Total | Coverage
Test: | CPython 3.12 LCOV report [commit 5e6661bce9] |  | Lines: | 37 | 625 | 5.9 %
Date: | 2023-03-20 08:15:36 |  | Functions: | 5 | 34 | 14.7 %
|  |  | Branches: | 12 | 409 | 2.9 %

               Branch data     Line data    Source code
           1                 :            :
           2                 :            : /* Traceback implementation */
           3                 :            :
           4                 :            : #include "Python.h"
           5                 :            :
           6                 :            : #include "pycore_ast.h"           // asdl_seq_*
           7                 :            : #include "pycore_call.h"          // _PyObject_CallMethodFormat()
           8                 :            : #include "pycore_compile.h"       // _PyAST_Optimize
           9                 :            : #include "pycore_fileutils.h"     // _Py_BEGIN_SUPPRESS_IPH
          10                 :            : #include "pycore_frame.h"         // _PyFrame_GetCode()
          11                 :            : #include "pycore_interp.h"        // PyInterpreterState.gc
          12                 :            : #include "pycore_parser.h"        // _PyParser_ASTFromString
          13                 :            : #include "pycore_pyarena.h"       // _PyArena_Free()
          14                 :            : #include "pycore_pyerrors.h"      // _PyErr_GetRaisedException()
          15                 :            : #include "pycore_pystate.h"       // _PyThreadState_GET()
          16                 :            : #include "pycore_traceback.h"     // EXCEPTION_TB_HEADER
          17                 :            :
          18                 :            : #include "../Parser/pegen.h"      // _PyPegen_byte_offset_to_character_offset()
          19                 :            : #include "frameobject.h"          // PyFrame_New()
          20                 :            : #include "structmember.h"         // PyMemberDef
          21                 :            : #include "osdefs.h"               // SEP
          22                 :            : #ifdef HAVE_FCNTL_H
          23                 :            : #  include <fcntl.h>
          24                 :            : #endif
          25                 :            :
          26                 :            : #define OFF(x) offsetof(PyTracebackObject, x)
          27                 :            :
          28                 :            : #define PUTS(fd, str) _Py_write_noraise(fd, str, (int)strlen(str))
          29                 :            : #define MAX_STRING_LENGTH 500
          30                 :            : #define MAX_FRAME_DEPTH 100
          31                 :            : #define MAX_NTHREADS 100
          32                 :            :
          33                 :            : /* Function from Parser/tokenizer.c */
          34                 :            : extern char* _PyTokenizer_FindEncodingFilename(int, PyObject *);
          35                 :            :
          36                 :            : /*[clinic input]
          37                 :            : class TracebackType "PyTracebackObject *" "&PyTraceback_Type"
          38                 :            : [clinic start generated code]*/
          39                 :            : /*[clinic end generated code: output=da39a3ee5e6b4b0d input=928fa06c10151120]*/
          40                 :            :
          41                 :            : #include "clinic/traceback.c.h"
          42                 :            :
          43                 :            : static PyObject *
          44                 :     105269 : tb_create_raw(PyTracebackObject *next, PyFrameObject *frame, int lasti,
          45                 :            :               int lineno)
          46                 :            : {
          47                 :            :     PyTracebackObject *tb;
          48   [ +  +  +  -  :     105269 :     if ((next != NULL && !PyTraceBack_Check(next)) ||
                       +  - ]
          49         [ -  + ]:     105269 :                     frame == NULL || !PyFrame_Check(frame)) {
          50                 :          0 :         PyErr_BadInternalCall();
          51                 :          0 :         return NULL;
          52                 :            :     }
          53                 :     105269 :     tb = PyObject_GC_New(PyTracebackObject, &PyTraceBack_Type);
          54         [ +  - ]:     105269 :     if (tb != NULL) {
          55                 :     105269 :         tb->tb_next = (PyTracebackObject*)Py_XNewRef(next);
          56                 :     105269 :         tb->tb_frame = (PyFrameObject*)Py_XNewRef(frame);
          57                 :     105269 :         tb->tb_lasti = lasti;
          58                 :     105269 :         tb->tb_lineno = lineno;
          59                 :     105269 :         PyObject_GC_Track(tb);
          60                 :            :     }
          61                 :     105269 :     return (PyObject *)tb;
          62                 :            : }
          63                 :            :
          64                 :            : /*[clinic input]
          65                 :            : @classmethod
          66                 :            : TracebackType.__new__ as tb_new
          67                 :            :
          68                 :            :   tb_next: object
          69                 :            :   tb_frame: object(type='PyFrameObject *', subclass_of='&PyFrame_Type')
          70                 :            :   tb_lasti: int
          71                 :            :   tb_lineno: int
          72                 :            :
          73                 :            : Create a new traceback object.
          74                 :            : [clinic start generated code]*/
          75                 :            :
          76                 :            : static PyObject *
          77                 :          0 : tb_new_impl(PyTypeObject *type, PyObject *tb_next, PyFrameObject *tb_frame,
          78                 :            :             int tb_lasti, int tb_lineno)
          79                 :            : /*[clinic end generated code: output=fa077debd72d861a input=01cbe8ec8783fca7]*/
          80                 :            : {
          81         [ #  # ]:          0 :     if (tb_next == Py_None) {
          82                 :          0 :         tb_next = NULL;
          83         [ #  # ]:          0 :     } else if (!PyTraceBack_Check(tb_next)) {
          84                 :          0 :         return PyErr_Format(PyExc_TypeError,
          85                 :            :                             "expected traceback object or None, got '%s'",
          86                 :          0 :                             Py_TYPE(tb_next)->tp_name);
          87                 :            :     }
          88                 :            :
          89                 :          0 :     return tb_create_raw((PyTracebackObject *)tb_next, tb_frame, tb_lasti,
          90                 :            :                          tb_lineno);
          91                 :            : }
          92                 :            :
          93                 :            : static PyObject *
          94                 :          0 : tb_dir(PyTracebackObject *self, PyObject *Py_UNUSED(ignored))
          95                 :            : {
          96                 :          0 :     return Py_BuildValue("[ssss]", "tb_frame", "tb_next",
          97                 :            :                                    "tb_lasti", "tb_lineno");
          98                 :            : }
          99                 :            :
         100                 :            : static PyObject *
         101                 :        248 : tb_next_get(PyTracebackObject *self, void *Py_UNUSED(_))
         102                 :            : {
         103                 :        248 :     PyObject* ret = (PyObject*)self->tb_next;
         104         [ +  + ]:        248 :     if (!ret) {
         105                 :        233 :         ret = Py_None;
         106                 :            :     }
         107                 :        248 :     return Py_NewRef(ret);
         108                 :            : }
         109                 :            :
         110                 :            : static int
         111                 :          0 : tb_next_set(PyTracebackObject *self, PyObject *new_next, void *Py_UNUSED(_))
         112                 :            : {
         113         [ #  # ]:          0 :     if (!new_next) {
         114                 :          0 :         PyErr_Format(PyExc_TypeError, "can't delete tb_next attribute");
         115                 :          0 :         return -1;
         116                 :            :     }
         117                 :            :
         118                 :            :     /* We accept None or a traceback object, and map None -> NULL (inverse of
         119                 :            :        tb_next_get) */
         120         [ #  # ]:          0 :     if (new_next == Py_None) {
         121                 :          0 :         new_next = NULL;
         122         [ #  # ]:          0 :     } else if (!PyTraceBack_Check(new_next)) {
         123                 :          0 :         PyErr_Format(PyExc_TypeError,
         124                 :            :                      "expected traceback object, got '%s'",
         125                 :          0 :                      Py_TYPE(new_next)->tp_name);
         126                 :          0 :         return -1;
         127                 :            :     }
         128                 :            :
         129                 :            :     /* Check for loops */
         130                 :          0 :     PyTracebackObject *cursor = (PyTracebackObject *)new_next;
         131         [ #  # ]:          0 :     while (cursor) {
         132         [ #  # ]:          0 :         if (cursor == self) {
         133                 :          0 :             PyErr_Format(PyExc_ValueError, "traceback loop detected");
         134                 :          0 :             return -1;
         135                 :            :         }
         136                 :          0 :         cursor = cursor->tb_next;
         137                 :            :     }
         138                 :            :
         139                 :          0 :     Py_XSETREF(self->tb_next, (PyTracebackObject *)Py_XNewRef(new_next));
         140                 :            :
         141                 :          0 :     return 0;
         142                 :            : }
         143                 :            :
         144                 :            :
         145                 :            : static PyMethodDef tb_methods[] = {
         146                 :            :    {"__dir__", _PyCFunction_CAST(tb_dir), METH_NOARGS},
         147                 :            :    {NULL, NULL, 0, NULL},
         148                 :            : };
         149                 :            :
         150                 :            : static PyMemberDef tb_memberlist[] = {
         151                 :            :     {"tb_frame",        T_OBJECT,       OFF(tb_frame),  READONLY|PY_AUDIT_READ},
         152                 :            :     {"tb_lasti",        T_INT,          OFF(tb_lasti),  READONLY},
         153                 :            :     {"tb_lineno",       T_INT,          OFF(tb_lineno), READONLY},
         154                 :            :     {NULL}      /* Sentinel */
         155                 :            : };
         156                 :            :
         157                 :            : static PyGetSetDef tb_getsetters[] = {
         158                 :            :     {"tb_next", (getter)tb_next_get, (setter)tb_next_set, NULL, NULL},
         159                 :            :     {NULL}      /* Sentinel */
         160                 :            : };
         161                 :            :
         162                 :            : static void
         163                 :     105269 : tb_dealloc(PyTracebackObject *tb)
         164                 :            : {
         165                 :     105269 :     PyObject_GC_UnTrack(tb);
         166   [ +  -  -  + ]:     105269 :     Py_TRASHCAN_BEGIN(tb, tb_dealloc)
         167                 :     105269 :     Py_XDECREF(tb->tb_next);
         168                 :     105269 :     Py_XDECREF(tb->tb_frame);
         169                 :     105269 :     PyObject_GC_Del(tb);
         170         [ +  - ]:     105269 :     Py_TRASHCAN_END
         171                 :     105269 : }
         172                 :            :
         173                 :            : static int
         174                 :          0 : tb_traverse(PyTracebackObject *tb, visitproc visit, void *arg)
         175                 :            : {
         176   [ #  #  #  # ]:          0 :     Py_VISIT(tb->tb_next);
         177   [ #  #  #  # ]:          0 :     Py_VISIT(tb->tb_frame);
         178                 :          0 :     return 0;
         179                 :            : }
         180                 :            :
         181                 :            : static int
         182                 :          0 : tb_clear(PyTracebackObject *tb)
         183                 :            : {
         184         [ #  # ]:          0 :     Py_CLEAR(tb->tb_next);
         185         [ #  # ]:          0 :     Py_CLEAR(tb->tb_frame);
         186                 :          0 :     return 0;
         187                 :            : }
         188                 :            :
         189                 :            : PyTypeObject PyTraceBack_Type = {
         190                 :            :     PyVarObject_HEAD_INIT(&PyType_Type, 0)
         191                 :            :     "traceback",
         192                 :            :     sizeof(PyTracebackObject),
         193                 :            :     0,
         194                 :            :     (destructor)tb_dealloc, /*tp_dealloc*/
         195                 :            :     0,                  /*tp_vectorcall_offset*/
         196                 :            :     0,    /*tp_getattr*/
         197                 :            :     0,                  /*tp_setattr*/
         198                 :            :     0,                  /*tp_as_async*/
         199                 :            :     0,                  /*tp_repr*/
         200                 :            :     0,                  /*tp_as_number*/
         201                 :            :     0,                  /*tp_as_sequence*/
         202                 :            :     0,                  /*tp_as_mapping*/
         203                 :            :     0,                  /* tp_hash */
         204                 :            :     0,                  /* tp_call */
         205                 :            :     0,                  /* tp_str */
         206                 :            :     PyObject_GenericGetAttr,                    /* tp_getattro */
         207                 :            :     0,                  /* tp_setattro */
         208                 :            :     0,                                          /* tp_as_buffer */
         209                 :            :     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */
         210                 :            :     tb_new__doc__,                              /* tp_doc */
         211                 :            :     (traverseproc)tb_traverse,                  /* tp_traverse */
         212                 :            :     (inquiry)tb_clear,                          /* tp_clear */
         213                 :            :     0,                                          /* tp_richcompare */
         214                 :            :     0,                                          /* tp_weaklistoffset */
         215                 :            :     0,                                          /* tp_iter */
         216                 :            :     0,                                          /* tp_iternext */
         217                 :            :     tb_methods,         /* tp_methods */
         218                 :            :     tb_memberlist,      /* tp_members */
         219                 :            :     tb_getsetters,                              /* tp_getset */
         220                 :            :     0,                                          /* tp_base */
         221                 :            :     0,                                          /* tp_dict */
         222                 :            :     0,                                          /* tp_descr_get */
         223                 :            :     0,                                          /* tp_descr_set */
         224                 :            :     0,                                          /* tp_dictoffset */
         225                 :            :     0,                                          /* tp_init */
         226                 :            :     0,                                          /* tp_alloc */
         227                 :            :     tb_new,                                     /* tp_new */
         228                 :            : };
         229                 :            :
         230                 :            :
         231                 :            : PyObject*
         232                 :     105269 : _PyTraceBack_FromFrame(PyObject *tb_next, PyFrameObject *frame)
         233                 :            : {
         234                 :            :     assert(tb_next == NULL || PyTraceBack_Check(tb_next));
         235                 :            :     assert(frame != NULL);
         236                 :     105269 :     int addr = _PyInterpreterFrame_LASTI(frame->f_frame) * sizeof(_Py_CODEUNIT);
         237                 :     105269 :     return tb_create_raw((PyTracebackObject *)tb_next, frame, addr,
         238                 :            :                          PyFrame_GetLineNumber(frame));
         239                 :            : }
         240                 :            :
         241                 :            :
         242                 :            : int
         243                 :     105269 : PyTraceBack_Here(PyFrameObject *frame)
         244                 :            : {
         245                 :     105269 :     PyObject *exc = PyErr_GetRaisedException();
         246                 :            :     assert(PyExceptionInstance_Check(exc));
         247                 :     105269 :     PyObject *tb = PyException_GetTraceback(exc);
         248                 :     105269 :     PyObject *newtb = _PyTraceBack_FromFrame(tb, frame);
         249                 :     105269 :     Py_XDECREF(tb);
         250         [ -  + ]:     105269 :     if (newtb == NULL) {
         251                 :          0 :         _PyErr_ChainExceptions1(exc);
         252                 :          0 :         return -1;
         253                 :            :     }
         254                 :     105269 :     PyException_SetTraceback(exc, newtb);
         255                 :     105269 :     Py_XDECREF(newtb);
         256                 :     105269 :     PyErr_SetRaisedException(exc);
         257                 :     105269 :     return 0;
         258                 :            : }
         259                 :            :
         260                 :            : /* Insert a frame into the traceback for (funcname, filename, lineno). */
         261                 :          0 : void _PyTraceback_Add(const char *funcname, const char *filename, int lineno)
         262                 :            : {
         263                 :            :     PyObject *globals;
         264                 :            :     PyCodeObject *code;
         265                 :            :     PyFrameObject *frame;
         266                 :          0 :     PyThreadState *tstate = _PyThreadState_GET();
         267                 :            :
         268                 :            :     /* Save and clear the current exception. Python functions must not be
         269                 :            :        called with an exception set. Calling Python functions happens when
         270                 :            :        the codec of the filesystem encoding is implemented in pure Python. */
         271                 :          0 :     PyObject *exc = _PyErr_GetRaisedException(tstate);
         272                 :            :
         273                 :          0 :     globals = PyDict_New();
         274         [ #  # ]:          0 :     if (!globals)
         275                 :          0 :         goto error;
         276                 :          0 :     code = PyCode_NewEmpty(filename, funcname, lineno);
         277         [ #  # ]:          0 :     if (!code) {
         278                 :          0 :         Py_DECREF(globals);
         279                 :          0 :         goto error;
         280                 :            :     }
         281                 :          0 :     frame = PyFrame_New(tstate, code, globals, NULL);
         282                 :          0 :     Py_DECREF(globals);
         283                 :          0 :     Py_DECREF(code);
         284         [ #  # ]:          0 :     if (!frame)
         285                 :          0 :         goto error;
         286                 :          0 :     frame->f_lineno = lineno;
         287                 :            :
         288                 :          0 :     _PyErr_SetRaisedException(tstate, exc);
         289                 :          0 :     PyTraceBack_Here(frame);
         290                 :          0 :     Py_DECREF(frame);
         291                 :          0 :     return;
         292                 :            :
         293                 :          0 : error:
         294                 :          0 :     _PyErr_ChainExceptions1(exc);
         295                 :            : }
         296                 :            :
         297                 :            : static PyObject *
         298                 :          0 : _Py_FindSourceFile(PyObject *filename, char* namebuf, size_t namelen, PyObject *io)
         299                 :            : {
         300                 :            :     Py_ssize_t i;
         301                 :            :     PyObject *binary;
         302                 :            :     PyObject *v;
         303                 :            :     Py_ssize_t npath;
         304                 :            :     size_t taillen;
         305                 :            :     PyObject *syspath;
         306                 :            :     PyObject *path;
         307                 :            :     const char* tail;
         308                 :            :     PyObject *filebytes;
         309                 :            :     const char* filepath;
         310                 :            :     Py_ssize_t len;
         311                 :            :     PyObject* result;
         312                 :          0 :     PyObject *open = NULL;
         313                 :            :
         314                 :          0 :     filebytes = PyUnicode_EncodeFSDefault(filename);
         315         [ #  # ]:          0 :     if (filebytes == NULL) {
         316                 :          0 :         PyErr_Clear();
         317                 :          0 :         return NULL;
         318                 :            :     }
         319                 :          0 :     filepath = PyBytes_AS_STRING(filebytes);
         320                 :            :
         321                 :            :     /* Search tail of filename in sys.path before giving up */
         322                 :          0 :     tail = strrchr(filepath, SEP);
         323         [ #  # ]:          0 :     if (tail == NULL)
         324                 :          0 :         tail = filepath;
         325                 :            :     else
         326                 :          0 :         tail++;
         327                 :          0 :     taillen = strlen(tail);
         328                 :            :
         329                 :          0 :     PyThreadState *tstate = _PyThreadState_GET();
         330                 :          0 :     syspath = _PySys_GetAttr(tstate, &_Py_ID(path));
         331   [ #  #  #  # ]:          0 :     if (syspath == NULL || !PyList_Check(syspath))
         332                 :          0 :         goto error;
         333                 :          0 :     npath = PyList_Size(syspath);
         334                 :            :
         335                 :          0 :     open = PyObject_GetAttr(io, &_Py_ID(open));
         336         [ #  # ]:          0 :     for (i = 0; i < npath; i++) {
         337                 :          0 :         v = PyList_GetItem(syspath, i);
         338         [ #  # ]:          0 :         if (v == NULL) {
         339                 :          0 :             PyErr_Clear();
         340                 :          0 :             break;
         341                 :            :         }
         342         [ #  # ]:          0 :         if (!PyUnicode_Check(v))
         343                 :          0 :             continue;
         344                 :          0 :         path = PyUnicode_EncodeFSDefault(v);
         345         [ #  # ]:          0 :         if (path == NULL) {
         346                 :          0 :             PyErr_Clear();
         347                 :          0 :             continue;
         348                 :            :         }
         349                 :          0 :         len = PyBytes_GET_SIZE(path);
         350         [ #  # ]:          0 :         if (len + 1 + (Py_ssize_t)taillen >= (Py_ssize_t)namelen - 1) {
         351                 :          0 :             Py_DECREF(path);
         352                 :          0 :             continue; /* Too long */
         353                 :            :         }
         354                 :          0 :         strcpy(namebuf, PyBytes_AS_STRING(path));
         355                 :          0 :         Py_DECREF(path);
         356         [ #  # ]:          0 :         if (strlen(namebuf) != (size_t)len)
         357                 :          0 :             continue; /* v contains '\0' */
         358   [ #  #  #  # ]:          0 :         if (len > 0 && namebuf[len-1] != SEP)
         359                 :          0 :             namebuf[len++] = SEP;
         360                 :          0 :         strcpy(namebuf+len, tail);
         361                 :            :
         362                 :          0 :         binary = _PyObject_CallMethodFormat(tstate, open, "ss", namebuf, "rb");
         363         [ #  # ]:          0 :         if (binary != NULL) {
         364                 :          0 :             result = binary;
         365                 :          0 :             goto finally;
         366                 :            :         }
         367                 :          0 :         PyErr_Clear();
         368                 :            :     }
         369                 :          0 :     goto error;
         370                 :            :
         371                 :          0 : error:
         372                 :          0 :     result = NULL;
         373                 :          0 : finally:
         374                 :          0 :     Py_XDECREF(open);
         375                 :          0 :     Py_DECREF(filebytes);
         376                 :          0 :     return result;
         377                 :            : }
         378                 :            :
         379                 :            : /* Writes indent spaces. Returns 0 on success and non-zero on failure.
         380                 :            :  */
         381                 :            : int
         382                 :          0 : _Py_WriteIndent(int indent, PyObject *f)
         383                 :            : {
         384                 :          0 :     char buf[11] = "          ";
         385                 :            :     assert(strlen(buf) == 10);
         386         [ #  # ]:          0 :     while (indent > 0) {
         387         [ #  # ]:          0 :         if (indent < 10) {
         388                 :          0 :             buf[indent] = '\0';
         389                 :            :         }
         390         [ #  # ]:          0 :         if (PyFile_WriteString(buf, f) < 0) {
         391                 :          0 :             return -1;
         392                 :            :         }
         393                 :          0 :         indent -= 10;
         394                 :            :     }
         395                 :          0 :     return 0;
         396                 :            : }
         397                 :            :
         398                 :            : /* Writes indent spaces, followed by the margin if it is not `\0`.
         399                 :            :    Returns 0 on success and non-zero on failure.
         400                 :            :  */
         401                 :            : int
         402                 :          0 : _Py_WriteIndentedMargin(int indent, const char *margin, PyObject *f)
         403                 :            : {
         404         [ #  # ]:          0 :     if (_Py_WriteIndent(indent, f) < 0) {
         405                 :          0 :         return -1;
         406                 :            :     }
         407         [ #  # ]:          0 :     if (margin) {
         408         [ #  # ]:          0 :         if (PyFile_WriteString(margin, f) < 0) {
         409                 :          0 :             return -1;
         410                 :            :         }
         411                 :            :     }
         412                 :          0 :     return 0;
         413                 :            : }
         414                 :            :
         415                 :            : static int
         416                 :          0 : display_source_line_with_margin(PyObject *f, PyObject *filename, int lineno, int indent,
         417                 :            :                                 int margin_indent, const char *margin,
         418                 :            :                                 int *truncation, PyObject **line)
         419                 :            : {
         420                 :            :     int fd;
         421                 :            :     int i;
         422                 :            :     char *found_encoding;
         423                 :            :     const char *encoding;
         424                 :            :     PyObject *io;
         425                 :            :     PyObject *binary;
         426                 :          0 :     PyObject *fob = NULL;
         427                 :          0 :     PyObject *lineobj = NULL;
         428                 :            :     PyObject *res;
         429                 :            :     char buf[MAXPATHLEN+1];
         430                 :            :     int kind;
         431                 :            :     const void *data;
         432                 :            :
         433                 :            :     /* open the file */
         434         [ #  # ]:          0 :     if (filename == NULL)
         435                 :          0 :         return 0;
         436                 :            :
         437                 :            :     /* Do not attempt to open things like <string> or <stdin> */
         438                 :            :     assert(PyUnicode_Check(filename));
         439         [ #  # ]:          0 :     if (PyUnicode_READ_CHAR(filename, 0) == '<') {
         440                 :          0 :         Py_ssize_t len = PyUnicode_GET_LENGTH(filename);
         441   [ #  #  #  # ]:          0 :         if (len > 0 && PyUnicode_READ_CHAR(filename, len - 1) == '>') {
         442                 :          0 :             return 0;
         443                 :            :         }
         444                 :            :     }
         445                 :            :
         446                 :          0 :     io = PyImport_ImportModule("io");
         447         [ #  # ]:          0 :     if (io == NULL) {
         448                 :          0 :         return -1;
         449                 :            :     }
         450                 :            :
         451                 :          0 :     binary = _PyObject_CallMethod(io, &_Py_ID(open), "Os", filename, "rb");
         452         [ #  # ]:          0 :     if (binary == NULL) {
         453                 :          0 :         PyErr_Clear();
         454                 :            :
         455                 :          0 :         binary = _Py_FindSourceFile(filename, buf, sizeof(buf), io);
         456         [ #  # ]:          0 :         if (binary == NULL) {
         457                 :          0 :             Py_DECREF(io);
         458                 :          0 :             return -1;
         459                 :            :         }
         460                 :            :     }
         461                 :            :
         462                 :            :     /* use the right encoding to decode the file as unicode */
         463                 :          0 :     fd = PyObject_AsFileDescriptor(binary);
         464         [ #  # ]:          0 :     if (fd < 0) {
         465                 :          0 :         Py_DECREF(io);
         466                 :          0 :         Py_DECREF(binary);
         467                 :          0 :         return 0;
         468                 :            :     }
         469                 :          0 :     found_encoding = _PyTokenizer_FindEncodingFilename(fd, filename);
         470         [ #  # ]:          0 :     if (found_encoding == NULL)
         471                 :          0 :         PyErr_Clear();
         472         [ #  # ]:          0 :     encoding = (found_encoding != NULL) ? found_encoding : "utf-8";
         473                 :            :     /* Reset position */
         474         [ #  # ]:          0 :     if (lseek(fd, 0, SEEK_SET) == (off_t)-1) {
         475                 :          0 :         Py_DECREF(io);
         476                 :          0 :         Py_DECREF(binary);
         477                 :          0 :         PyMem_Free(found_encoding);
         478                 :          0 :         return 0;
         479                 :            :     }
         480                 :          0 :     fob = _PyObject_CallMethod(io, &_Py_ID(TextIOWrapper),
         481                 :            :                                "Os", binary, encoding);
         482                 :          0 :     Py_DECREF(io);
         483                 :          0 :     PyMem_Free(found_encoding);
         484                 :            :
         485         [ #  # ]:          0 :     if (fob == NULL) {
         486                 :          0 :         PyErr_Clear();
         487                 :            :
         488                 :          0 :         res = PyObject_CallMethodNoArgs(binary, &_Py_ID(close));
         489                 :          0 :         Py_DECREF(binary);
         490         [ #  # ]:          0 :         if (res)
         491                 :          0 :             Py_DECREF(res);
         492                 :            :         else
         493                 :          0 :             PyErr_Clear();
         494                 :          0 :         return 0;
         495                 :            :     }
         496                 :          0 :     Py_DECREF(binary);
         497                 :            :
         498                 :            :     /* get the line number lineno */
         499         [ #  # ]:          0 :     for (i = 0; i < lineno; i++) {
         500                 :          0 :         Py_XDECREF(lineobj);
         501                 :          0 :         lineobj = PyFile_GetLine(fob, -1);
         502         [ #  # ]:          0 :         if (!lineobj) {
         503                 :          0 :             PyErr_Clear();
         504                 :          0 :             break;
         505                 :            :         }
         506                 :            :     }
         507                 :          0 :     res = PyObject_CallMethodNoArgs(fob, &_Py_ID(close));
         508         [ #  # ]:          0 :     if (res) {
         509                 :          0 :         Py_DECREF(res);
         510                 :            :     }
         511                 :            :     else {
         512                 :          0 :         PyErr_Clear();
         513                 :            :     }
         514                 :          0 :     Py_DECREF(fob);
         515   [ #  #  #  # ]:          0 :     if (!lineobj || !PyUnicode_Check(lineobj)) {
         516                 :          0 :         Py_XDECREF(lineobj);
         517                 :          0 :         return -1;
         518                 :            :     }
         519                 :            :
         520         [ #  # ]:          0 :     if (line) {
         521                 :          0 :         *line = Py_NewRef(lineobj);
         522                 :            :     }
         523                 :            :
         524                 :            :     /* remove the indentation of the line */
         525                 :          0 :     kind = PyUnicode_KIND(lineobj);
         526                 :          0 :     data = PyUnicode_DATA(lineobj);
         527         [ #  # ]:          0 :     for (i=0; i < PyUnicode_GET_LENGTH(lineobj); i++) {
         528                 :          0 :         Py_UCS4 ch = PyUnicode_READ(kind, data, i);
         529   [ #  #  #  #  :          0 :         if (ch != ' ' && ch != '\t' && ch != '\014')
                       #  # ]
         530                 :          0 :             break;
         531                 :            :     }
         532         [ #  # ]:          0 :     if (i) {
         533                 :            :         PyObject *truncated;
         534                 :          0 :         truncated = PyUnicode_Substring(lineobj, i, PyUnicode_GET_LENGTH(lineobj));
         535         [ #  # ]:          0 :         if (truncated) {
         536                 :          0 :             Py_SETREF(lineobj, truncated);
         537                 :            :         } else {
         538                 :          0 :             PyErr_Clear();
         539                 :            :         }
         540                 :            :     }
         541                 :            :
         542         [ #  # ]:          0 :     if (truncation != NULL) {
         543                 :          0 :         *truncation = i - indent;
         544                 :            :     }
         545                 :            :
         546         [ #  # ]:          0 :     if (_Py_WriteIndentedMargin(margin_indent, margin, f) < 0) {
         547                 :          0 :         goto error;
         548                 :            :     }
         549                 :            :
         550                 :            :     /* Write some spaces before the line */
         551         [ #  # ]:          0 :     if (_Py_WriteIndent(indent, f) < 0) {
         552                 :          0 :         goto error;
         553                 :            :     }
         554                 :            :
         555                 :            :     /* finally display the line */
         556         [ #  # ]:          0 :     if (PyFile_WriteObject(lineobj, f, Py_PRINT_RAW) < 0) {
         557                 :          0 :         goto error;
         558                 :            :     }
         559                 :            :
         560         [ #  # ]:          0 :     if (PyFile_WriteString("\n", f) < 0) {
         561                 :          0 :         goto error;
         562                 :            :     }
         563                 :            :
         564                 :          0 :     Py_DECREF(lineobj);
         565                 :          0 :     return 0;
         566                 :          0 : error:
         567                 :          0 :     Py_DECREF(lineobj);
         568                 :          0 :     return -1;
         569                 :            : }
         570                 :            :
         571                 :            : int
         572                 :          0 : _Py_DisplaySourceLine(PyObject *f, PyObject *filename, int lineno, int indent,
         573                 :            :                       int *truncation, PyObject **line)
         574                 :            : {
         575                 :          0 :     return display_source_line_with_margin(f, filename, lineno, indent, 0,
         576                 :            :                                            NULL, truncation, line);
         577                 :            : }
         578                 :            :
         579                 :            : /* AST based Traceback Specialization
         580                 :            :  *
         581                 :            :  * When displaying a new traceback line, for certain syntactical constructs
         582                 :            :  * (e.g a subscript, an arithmetic operation) we try to create a representation
         583                 :            :  * that separates the primary source of error from the rest.
         584                 :            :  *
         585                 :            :  * Example specialization of BinOp nodes:
         586                 :            :  *  Traceback (most recent call last):
         587                 :            :  *    File "/home/isidentical/cpython/cpython/t.py", line 10, in <module>
         588                 :            :  *      add_values(1, 2, 'x', 3, 4)
         589                 :            :  *    File "/home/isidentical/cpython/cpython/t.py", line 2, in add_values
         590                 :            :  *      return a + b + c + d + e
         591                 :            :  *             ~~~~~~^~~
         592                 :            :  *  TypeError: 'NoneType' object is not subscriptable
         593                 :            :  */
         594                 :            :
         595                 :            : #define IS_WHITESPACE(c) (((c) == ' ') || ((c) == '\t') || ((c) == '\f'))
         596                 :            :
         597                 :            : static int
         598                 :          0 : extract_anchors_from_expr(const char *segment_str, expr_ty expr, Py_ssize_t *left_anchor, Py_ssize_t *right_anchor,
         599                 :            :                           char** primary_error_char, char** secondary_error_char)
         600                 :            : {
         601      [ #  #  # ]:          0 :     switch (expr->kind) {
         602                 :          0 :         case BinOp_kind: {
         603                 :          0 :             expr_ty left = expr->v.BinOp.left;
         604                 :          0 :             expr_ty right = expr->v.BinOp.right;
         605         [ #  # ]:          0 :             for (int i = left->end_col_offset; i < right->col_offset; i++) {
         606   [ #  #  #  #  :          0 :                 if (IS_WHITESPACE(segment_str[i])) {
                       #  # ]
         607                 :          0 :                     continue;
         608                 :            :                 }
         609                 :            :
         610                 :          0 :                 *left_anchor = i;
         611                 :          0 :                 *right_anchor = i + 1;
         612                 :            :
         613                 :            :                 // Check whether if this a two-character operator (e.g //)
         614   [ #  #  #  #  :          0 :                 if (i + 1 < right->col_offset && !IS_WHITESPACE(segment_str[i + 1])) {
                 #  #  #  # ]
         615                 :          0 :                     ++*right_anchor;
         616                 :            :                 }
         617                 :            :
         618                 :            :                 // Set the error characters
         619                 :          0 :                 *primary_error_char = "~";
         620                 :          0 :                 *secondary_error_char = "^";
         621                 :          0 :                 break;
         622                 :            :             }
         623                 :          0 :             return 1;
         624                 :            :         }
         625                 :          0 :         case Subscript_kind: {
         626                 :          0 :             *left_anchor = expr->v.Subscript.value->end_col_offset;
         627                 :          0 :             *right_anchor = expr->v.Subscript.slice->end_col_offset + 1;
         628                 :            :
         629                 :            :             // Set the error characters
         630                 :          0 :             *primary_error_char = "~";
         631                 :          0 :             *secondary_error_char = "^";
         632                 :          0 :             return 1;
         633                 :            :         }
         634                 :          0 :         default:
         635                 :          0 :             return 0;
         636                 :            :     }
         637                 :            : }
         638                 :            :
         639                 :            : static int
         640                 :          0 : extract_anchors_from_stmt(const char *segment_str, stmt_ty statement, Py_ssize_t *left_anchor, Py_ssize_t *right_anchor,
         641                 :            :                           char** primary_error_char, char** secondary_error_char)
         642                 :            : {
         643         [ #  # ]:          0 :     switch (statement->kind) {
         644                 :          0 :         case Expr_kind: {
         645                 :          0 :             return extract_anchors_from_expr(segment_str, statement->v.Expr.value, left_anchor, right_anchor,
         646                 :            :                                              primary_error_char, secondary_error_char);
         647                 :            :         }
         648                 :          0 :         default:
         649                 :          0 :             return 0;
         650                 :            :     }
         651                 :            : }
         652                 :            :
         653                 :            : static int
         654                 :          0 : extract_anchors_from_line(PyObject *filename, PyObject *line,
         655                 :            :                           Py_ssize_t start_offset, Py_ssize_t end_offset,
         656                 :            :                           Py_ssize_t *left_anchor, Py_ssize_t *right_anchor,
         657                 :            :                           char** primary_error_char, char** secondary_error_char)
         658                 :            : {
         659                 :          0 :     int res = -1;
         660                 :          0 :     PyArena *arena = NULL;
         661                 :          0 :     PyObject *segment = PyUnicode_Substring(line, start_offset, end_offset);
         662         [ #  # ]:          0 :     if (!segment) {
         663                 :          0 :         goto done;
         664                 :            :     }
         665                 :            :
         666                 :          0 :     const char *segment_str = PyUnicode_AsUTF8(segment);
         667         [ #  # ]:          0 :     if (!segment_str) {
         668                 :          0 :         goto done;
         669                 :            :     }
         670                 :            :
         671                 :          0 :     arena = _PyArena_New();
         672         [ #  # ]:          0 :     if (!arena) {
         673                 :          0 :         goto done;
         674                 :            :     }
         675                 :            :
         676                 :          0 :     PyCompilerFlags flags = _PyCompilerFlags_INIT;
         677                 :            :
         678                 :            :     _PyASTOptimizeState state;
         679                 :          0 :     state.optimize = _Py_GetConfig()->optimization_level;
         680                 :          0 :     state.ff_features = 0;
         681                 :            :
         682                 :          0 :     mod_ty module = _PyParser_ASTFromString(segment_str, filename, Py_file_input,
         683                 :            :                                             &flags, arena);
         684         [ #  # ]:          0 :     if (!module) {
         685                 :          0 :         goto done;
         686                 :            :     }
         687         [ #  # ]:          0 :     if (!_PyAST_Optimize(module, arena, &state)) {
         688                 :          0 :         goto done;
         689                 :            :     }
         690                 :            :
         691                 :            :     assert(module->kind == Module_kind);
         692   [ #  #  #  # ]:          0 :     if (asdl_seq_LEN(module->v.Module.body) == 1) {
         693                 :          0 :         stmt_ty statement = asdl_seq_GET(module->v.Module.body, 0);
         694                 :          0 :         res = extract_anchors_from_stmt(segment_str, statement, left_anchor, right_anchor,
         695                 :            :                                         primary_error_char, secondary_error_char);
         696                 :            :     } else {
         697                 :          0 :         res = 0;
         698                 :            :     }
         699                 :            :
         700                 :          0 : done:
         701         [ #  # ]:          0 :     if (res > 0) {
         702                 :            :         // Normalize the AST offsets to byte offsets and adjust them with the
         703                 :            :         // start of the actual line (instead of the source code segment).
         704                 :            :         assert(segment != NULL);
         705                 :            :         assert(*left_anchor >= 0);
         706                 :            :         assert(*right_anchor >= 0);
         707                 :          0 :         *left_anchor = _PyPegen_byte_offset_to_character_offset(segment, *left_anchor) + start_offset;
         708                 :          0 :         *right_anchor = _PyPegen_byte_offset_to_character_offset(segment, *right_anchor) + start_offset;
         709                 :            :     }
         710                 :          0 :     Py_XDECREF(segment);
         711         [ #  # ]:          0 :     if (arena) {
         712                 :          0 :         _PyArena_Free(arena);
         713                 :            :     }
         714                 :          0 :     return res;
         715                 :            : }
         716                 :            :
         717                 :            : #define _TRACEBACK_SOURCE_LINE_INDENT 4
         718                 :            :
         719                 :            : static inline int
         720                 :          0 : ignore_source_errors(void) {
         721         [ #  # ]:          0 :     if (PyErr_Occurred()) {
         722         [ #  # ]:          0 :         if (PyErr_ExceptionMatches(PyExc_KeyboardInterrupt)) {
         723                 :          0 :             return -1;
         724                 :            :         }
         725                 :          0 :         PyErr_Clear();
         726                 :            :     }
         727                 :          0 :     return 0;
         728                 :            : }
         729                 :            :
         730                 :            : static inline int
         731                 :          0 : print_error_location_carets(PyObject *f, int offset, Py_ssize_t start_offset, Py_ssize_t end_offset,
         732                 :            :                             Py_ssize_t right_start_offset, Py_ssize_t left_end_offset,
         733                 :            :                             const char *primary, const char *secondary) {
         734   [ #  #  #  # ]:          0 :     int special_chars = (left_end_offset != -1 || right_start_offset != -1);
         735                 :            :     const char *str;
         736         [ #  # ]:          0 :     while (++offset <= end_offset) {
         737         [ #  # ]:          0 :         if (offset <= start_offset) {
         738                 :          0 :             str = " ";
         739   [ #  #  #  #  :          0 :         } else if (special_chars && left_end_offset < offset && offset <= right_start_offset) {
                       #  # ]
         740                 :          0 :             str = secondary;
         741                 :            :         } else {
         742                 :          0 :             str = primary;
         743                 :            :         }
         744         [ #  # ]:          0 :         if (PyFile_WriteString(str, f) < 0) {
         745                 :          0 :             return -1;
         746                 :            :         }
         747                 :            :     }
         748         [ #  # ]:          0 :     if (PyFile_WriteString("\n", f) < 0) {
         749                 :          0 :         return -1;
         750                 :            :     }
         751                 :          0 :     return 0;
         752                 :            : }
         753                 :            :
         754                 :            : static int
         755                 :          0 : tb_displayline(PyTracebackObject* tb, PyObject *f, PyObject *filename, int lineno,
         756                 :            :                PyFrameObject *frame, PyObject *name, int margin_indent, const char *margin)
         757                 :            : {
         758   [ #  #  #  # ]:          0 :     if (filename == NULL || name == NULL) {
         759                 :          0 :         return -1;
         760                 :            :     }
         761                 :            :
         762         [ #  # ]:          0 :     if (_Py_WriteIndentedMargin(margin_indent, margin, f) < 0) {
         763                 :          0 :         return -1;
         764                 :            :     }
         765                 :            :
         766                 :          0 :     PyObject *line = PyUnicode_FromFormat("  File \"%U\", line %d, in %U\n",
         767                 :            :                                           filename, lineno, name);
         768         [ #  # ]:          0 :     if (line == NULL) {
         769                 :          0 :         return -1;
         770                 :            :     }
         771                 :            :
         772                 :          0 :     int res = PyFile_WriteObject(line, f, Py_PRINT_RAW);
         773                 :          0 :     Py_DECREF(line);
         774         [ #  # ]:          0 :     if (res < 0) {
         775                 :          0 :         return -1;
         776                 :            :     }
         777                 :            :
         778                 :          0 :     int err = 0;
         779                 :            :
         780                 :          0 :     int truncation = _TRACEBACK_SOURCE_LINE_INDENT;
         781                 :          0 :     PyObject* source_line = NULL;
         782                 :          0 :     int rc = display_source_line_with_margin(
         783                 :            :             f, filename, lineno, _TRACEBACK_SOURCE_LINE_INDENT,
         784                 :            :             margin_indent, margin, &truncation, &source_line);
         785   [ #  #  #  # ]:          0 :     if (rc != 0 || !source_line) {
         786                 :            :         /* ignore errors since we can't report them, can we? */
         787                 :          0 :         err = ignore_source_errors();
         788                 :          0 :         goto done;
         789                 :            :     }
         790                 :            :
         791                 :          0 :     int code_offset = tb->tb_lasti;
         792                 :          0 :     PyCodeObject* code = frame->f_frame->f_code;
         793                 :          0 :     const Py_ssize_t source_line_len = PyUnicode_GET_LENGTH(source_line);
         794                 :            :
         795                 :            :     int start_line;
         796                 :            :     int end_line;
         797                 :            :     int start_col_byte_offset;
         798                 :            :     int end_col_byte_offset;
         799         [ #  # ]:          0 :     if (!PyCode_Addr2Location(code, code_offset, &start_line, &start_col_byte_offset,
         800                 :            :                               &end_line, &end_col_byte_offset)) {
         801                 :          0 :         goto done;
         802                 :            :     }
         803                 :            :
         804   [ #  #  #  # ]:          0 :     if (start_line < 0 || end_line < 0
         805         [ #  # ]:          0 :         || start_col_byte_offset < 0
         806         [ #  # ]:          0 :         || end_col_byte_offset < 0)
         807                 :            :     {
         808                 :          0 :         goto done;
         809                 :            :     }
         810                 :            :
         811                 :            :     // When displaying errors, we will use the following generic structure:
         812                 :            :     //
         813                 :            :     //  ERROR LINE ERROR LINE ERROR LINE ERROR LINE ERROR LINE ERROR LINE ERROR LINE
         814                 :            :     //        ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^~~~~~~~~~~~~~~~~~~~
         815                 :            :     //        |              |-> left_end_offset     |                  |-> end_offset
         816                 :            :     //        |-> start_offset                       |-> right_start_offset
         817                 :            :     //
         818                 :            :     // In general we will only have (start_offset, end_offset) but we can gather more information
         819                 :            :     // by analyzing the AST of the text between *start_offset* and *end_offset*. If this succeeds
         820                 :            :     // we could get *left_end_offset* and *right_start_offset* and some selection of characters for
         821                 :            :     // the different ranges (primary_error_char and secondary_error_char). If we cannot obtain the
         822                 :            :     // AST information or we cannot identify special ranges within it, then left_end_offset and
         823                 :            :     // right_end_offset will be set to -1.
         824                 :            :     //
         825                 :            :     // To keep the column indicators pertinent, they are not shown when the primary character
         826                 :            :     // spans the whole line.
         827                 :            :
         828                 :            :     // Convert the utf-8 byte offset to the actual character offset so we print the right number of carets.
         829                 :            :     assert(source_line);
         830                 :          0 :     Py_ssize_t start_offset = _PyPegen_byte_offset_to_character_offset(source_line, start_col_byte_offset);
         831         [ #  # ]:          0 :     if (start_offset < 0) {
         832                 :          0 :         err = ignore_source_errors() < 0;
         833                 :          0 :         goto done;
         834                 :            :     }
         835                 :            :
         836                 :          0 :     Py_ssize_t end_offset = _PyPegen_byte_offset_to_character_offset(source_line, end_col_byte_offset);
         837         [ #  # ]:          0 :     if (end_offset < 0) {
         838                 :          0 :         err = ignore_source_errors() < 0;
         839                 :          0 :         goto done;
         840                 :            :     }
         841                 :            :
         842                 :          0 :     Py_ssize_t left_end_offset = -1;
         843                 :          0 :     Py_ssize_t right_start_offset = -1;
         844                 :            :
         845                 :          0 :     char *primary_error_char = "^";
         846                 :          0 :     char *secondary_error_char = primary_error_char;
         847                 :            :
         848         [ #  # ]:          0 :     if (start_line == end_line) {
         849                 :          0 :         int res = extract_anchors_from_line(filename, source_line, start_offset, end_offset,
         850                 :            :                                             &left_end_offset, &right_start_offset,
         851                 :            :                                             &primary_error_char, &secondary_error_char);
         852   [ #  #  #  # ]:          0 :         if (res < 0 && ignore_source_errors() < 0) {
         853                 :          0 :             goto done;
         854                 :            :         }
         855                 :            :     }
         856                 :            :     else {
         857                 :            :         // If this is a multi-line expression, then we will highlight until
         858                 :            :         // the last non-whitespace character.
         859                 :          0 :         const char *source_line_str = PyUnicode_AsUTF8(source_line);
         860         [ #  # ]:          0 :         if (!source_line_str) {
         861                 :          0 :             goto done;
         862                 :            :         }
         863                 :            :
         864                 :          0 :         Py_ssize_t i = source_line_len;
         865         [ #  # ]:          0 :         while (--i >= 0) {
         866   [ #  #  #  #  :          0 :             if (!IS_WHITESPACE(source_line_str[i])) {
                       #  # ]
         867                 :          0 :                 break;
         868                 :            :             }
         869                 :            :         }
         870                 :            :
         871                 :          0 :         end_offset = i + 1;
         872                 :            :     }
         873                 :            :
         874                 :            :     // Elide indicators if primary char spans the frame line
         875                 :          0 :     Py_ssize_t stripped_line_len = source_line_len - truncation - _TRACEBACK_SOURCE_LINE_INDENT;
         876   [ #  #  #  # ]:          0 :     bool has_secondary_ranges = (left_end_offset != -1 || right_start_offset != -1);
         877   [ #  #  #  # ]:          0 :     if (end_offset - start_offset == stripped_line_len && !has_secondary_ranges) {
         878                 :          0 :         goto done;
         879                 :            :     }
         880                 :            :
         881         [ #  # ]:          0 :     if (_Py_WriteIndentedMargin(margin_indent, margin, f) < 0) {
         882                 :          0 :         err = -1;
         883                 :          0 :         goto done;
         884                 :            :     }
         885                 :            :
         886         [ #  # ]:          0 :     if (print_error_location_carets(f, truncation, start_offset, end_offset,
         887                 :            :                                     right_start_offset, left_end_offset,
         888                 :            :                                     primary_error_char, secondary_error_char) < 0) {
         889                 :          0 :         err = -1;
         890                 :          0 :         goto done;
         891                 :            :     }
         892                 :            :
         893                 :          0 : done:
         894                 :          0 :     Py_XDECREF(source_line);
         895                 :          0 :     return err;
         896                 :            : }
         897                 :            :
         898                 :            : static const int TB_RECURSIVE_CUTOFF = 3; // Also hardcoded in traceback.py.
         899                 :            :
         900                 :            : static int
         901                 :          0 : tb_print_line_repeated(PyObject *f, long cnt)
         902                 :            : {
         903                 :          0 :     cnt -= TB_RECURSIVE_CUTOFF;
         904         [ #  # ]:          0 :     PyObject *line = PyUnicode_FromFormat(
         905                 :            :         (cnt > 1)
         906                 :            :           ? "  [Previous line repeated %ld more times]\n"
         907                 :            :           : "  [Previous line repeated %ld more time]\n",
         908                 :            :         cnt);
         909         [ #  # ]:          0 :     if (line == NULL) {
         910                 :          0 :         return -1;
         911                 :            :     }
         912                 :          0 :     int err = PyFile_WriteObject(line, f, Py_PRINT_RAW);
         913                 :          0 :     Py_DECREF(line);
         914                 :          0 :     return err;
         915                 :            : }
         916                 :            :
         917                 :            : static int
         918                 :          0 : tb_printinternal(PyTracebackObject *tb, PyObject *f, long limit,
         919                 :            :                  int indent, const char *margin)
         920                 :            : {
         921                 :          0 :     PyCodeObject *code = NULL;
         922                 :          0 :     Py_ssize_t depth = 0;
         923                 :          0 :     PyObject *last_file = NULL;
         924                 :          0 :     int last_line = -1;
         925                 :          0 :     PyObject *last_name = NULL;
         926                 :          0 :     long cnt = 0;
         927                 :          0 :     PyTracebackObject *tb1 = tb;
         928         [ #  # ]:          0 :     while (tb1 != NULL) {
         929                 :          0 :         depth++;
         930                 :          0 :         tb1 = tb1->tb_next;
         931                 :            :     }
         932   [ #  #  #  # ]:          0 :     while (tb != NULL && depth > limit) {
         933                 :          0 :         depth--;
         934                 :          0 :         tb = tb->tb_next;
         935                 :            :     }
         936         [ #  # ]:          0 :     while (tb != NULL) {
         937                 :          0 :         code = PyFrame_GetCode(tb->tb_frame);
         938         [ #  # ]:          0 :         if (last_file == NULL ||
         939   [ #  #  #  # ]:          0 :             code->co_filename != last_file ||
         940   [ #  #  #  # ]:          0 :             last_line == -1 || tb->tb_lineno != last_line ||
         941         [ #  # ]:          0 :             last_name == NULL || code->co_name != last_name) {
         942         [ #  # ]:          0 :             if (cnt > TB_RECURSIVE_CUTOFF) {
         943         [ #  # ]:          0 :                 if (tb_print_line_repeated(f, cnt) < 0) {
         944                 :          0 :                     goto error;
         945                 :            :                 }
         946                 :            :             }
         947                 :          0 :             last_file = code->co_filename;
         948                 :          0 :             last_line = tb->tb_lineno;
         949                 :          0 :             last_name = code->co_name;
         950                 :          0 :             cnt = 0;
         951                 :            :         }
         952                 :          0 :         cnt++;
         953         [ #  # ]:          0 :         if (cnt <= TB_RECURSIVE_CUTOFF) {
         954         [ #  # ]:          0 :             if (tb_displayline(tb, f, code->co_filename, tb->tb_lineno,
         955                 :          0 :                                tb->tb_frame, code->co_name, indent, margin) < 0) {
         956                 :          0 :                 goto error;
         957                 :            :             }
         958                 :            :
         959         [ #  # ]:          0 :             if (PyErr_CheckSignals() < 0) {
         960                 :          0 :                 goto error;
         961                 :            :             }
         962                 :            :         }
         963         [ #  # ]:          0 :         Py_CLEAR(code);
         964                 :          0 :         tb = tb->tb_next;
         965                 :            :     }
         966         [ #  # ]:          0 :     if (cnt > TB_RECURSIVE_CUTOFF) {
         967         [ #  # ]:          0 :         if (tb_print_line_repeated(f, cnt) < 0) {
         968                 :          0 :             goto error;
         969                 :            :         }
         970                 :            :     }
         971                 :          0 :     return 0;
         972                 :          0 : error:
         973                 :          0 :     Py_XDECREF(code);
         974                 :          0 :     return -1;
         975                 :            : }
         976                 :            :
         977                 :            : #define PyTraceBack_LIMIT 1000
         978                 :            :
         979                 :            : int
         980                 :          0 : _PyTraceBack_Print_Indented(PyObject *v, int indent, const char *margin,
         981                 :            :                             const char *header_margin, const char *header, PyObject *f)
         982                 :            : {
         983                 :            :     PyObject *limitv;
         984                 :          0 :     long limit = PyTraceBack_LIMIT;
         985                 :            :
         986         [ #  # ]:          0 :     if (v == NULL) {
         987                 :          0 :         return 0;
         988                 :            :     }
         989         [ #  # ]:          0 :     if (!PyTraceBack_Check(v)) {
         990                 :          0 :         PyErr_BadInternalCall();
         991                 :          0 :         return -1;
         992                 :            :     }
         993                 :          0 :     limitv = PySys_GetObject("tracebacklimit");
         994   [ #  #  #  # ]:          0 :     if (limitv && PyLong_Check(limitv)) {
         995                 :            :         int overflow;
         996                 :          0 :         limit = PyLong_AsLongAndOverflow(limitv, &overflow);
         997         [ #  # ]:          0 :         if (overflow > 0) {
         998                 :          0 :             limit = LONG_MAX;
         999                 :            :         }
        1000         [ #  # ]:          0 :         else if (limit <= 0) {
        1001                 :          0 :             return 0;
        1002                 :            :         }
        1003                 :            :     }
        1004         [ #  # ]:          0 :     if (_Py_WriteIndentedMargin(indent, header_margin, f) < 0) {
        1005                 :          0 :         return -1;
        1006                 :            :     }
        1007                 :            :
        1008         [ #  # ]:          0 :     if (PyFile_WriteString(header, f) < 0) {
        1009                 :          0 :         return -1;
        1010                 :            :     }
        1011                 :            :
        1012         [ #  # ]:          0 :     if (tb_printinternal((PyTracebackObject *)v, f, limit, indent, margin) < 0) {
        1013                 :          0 :         return -1;
        1014                 :            :     }
        1015                 :            :
        1016                 :          0 :     return 0;
        1017                 :            : }
        1018                 :            :
        1019                 :            : int
        1020                 :          0 : PyTraceBack_Print(PyObject *v, PyObject *f)
        1021                 :            : {
        1022                 :          0 :     int indent = 0;
        1023                 :          0 :     const char *margin = NULL;
        1024                 :          0 :     const char *header_margin = NULL;
        1025                 :          0 :     const char *header = EXCEPTION_TB_HEADER;
        1026                 :            :
        1027                 :          0 :     return _PyTraceBack_Print_Indented(v, indent, margin, header_margin, header, f);
        1028                 :            : }
        1029                 :            :
        1030                 :            : /* Format an integer in range [0; 0xffffffff] to decimal and write it
        1031                 :            :    into the file fd.
        1032                 :            :
        1033                 :            :    This function is signal safe. */
        1034                 :            :
        1035                 :            : void
        1036                 :          0 : _Py_DumpDecimal(int fd, size_t value)
        1037                 :            : {
        1038                 :            :     /* maximum number of characters required for output of %lld or %p.
        1039                 :            :        We need at most ceil(log10(256)*SIZEOF_LONG_LONG) digits,
        1040                 :            :        plus 1 for the null byte.  53/22 is an upper bound for log10(256). */
        1041                 :            :     char buffer[1 + (sizeof(size_t)*53-1) / 22 + 1];
        1042                 :            :     char *ptr, *end;
        1043                 :            :
        1044                 :          0 :     end = &buffer[Py_ARRAY_LENGTH(buffer) - 1];
        1045                 :          0 :     ptr = end;
        1046                 :          0 :     *ptr = '\0';
        1047                 :            :     do {
        1048                 :          0 :         --ptr;
        1049                 :            :         assert(ptr >= buffer);
        1050                 :          0 :         *ptr = '0' + (value % 10);
        1051                 :          0 :         value /= 10;
        1052         [ #  # ]:          0 :     } while (value);
        1053                 :            :
        1054                 :          0 :     _Py_write_noraise(fd, ptr, end - ptr);
        1055                 :          0 : }
        1056                 :            :
        1057                 :            : /* Format an integer as hexadecimal with width digits into fd file descriptor.
        1058                 :            :    The function is signal safe. */
        1059                 :            : void
        1060                 :          0 : _Py_DumpHexadecimal(int fd, uintptr_t value, Py_ssize_t width)
        1061                 :            : {
        1062                 :            :     char buffer[sizeof(uintptr_t) * 2 + 1], *ptr, *end;
        1063                 :          0 :     const Py_ssize_t size = Py_ARRAY_LENGTH(buffer) - 1;
        1064                 :            :
        1065         [ #  # ]:          0 :     if (width > size)
        1066                 :          0 :         width = size;
        1067                 :            :     /* it's ok if width is negative */
        1068                 :            :
        1069                 :          0 :     end = &buffer[size];
        1070                 :          0 :     ptr = end;
        1071                 :          0 :     *ptr = '\0';
        1072                 :            :     do {
        1073                 :          0 :         --ptr;
        1074                 :            :         assert(ptr >= buffer);
        1075                 :          0 :         *ptr = Py_hexdigits[value & 15];
        1076                 :          0 :         value >>= 4;
        1077   [ #  #  #  # ]:          0 :     } while ((end - ptr) < width || value);
        1078                 :            :
        1079                 :          0 :     _Py_write_noraise(fd, ptr, end - ptr);
        1080                 :          0 : }
        1081                 :            :
        1082                 :            : void
        1083                 :          0 : _Py_DumpASCII(int fd, PyObject *text)
        1084                 :            : {
        1085                 :          0 :     PyASCIIObject *ascii = _PyASCIIObject_CAST(text);
        1086                 :            :     Py_ssize_t i, size;
        1087                 :            :     int truncated;
        1088                 :            :     int kind;
        1089                 :          0 :     void *data = NULL;
        1090                 :            :     Py_UCS4 ch;
        1091                 :            :
        1092         [ #  # ]:          0 :     if (!PyUnicode_Check(text))
        1093                 :          0 :         return;
        1094                 :            :
        1095                 :          0 :     size = ascii->length;
        1096                 :          0 :     kind = ascii->state.kind;
        1097         [ #  # ]:          0 :     if (ascii->state.compact) {
        1098         [ #  # ]:          0 :         if (ascii->state.ascii)
        1099                 :          0 :             data = ascii + 1;
        1100                 :            :         else
        1101                 :          0 :             data = _PyCompactUnicodeObject_CAST(text) + 1;
        1102                 :            :     }
        1103                 :            :     else {
        1104                 :          0 :         data = _PyUnicodeObject_CAST(text)->data.any;
        1105         [ #  # ]:          0 :         if (data == NULL)
        1106                 :          0 :             return;
        1107                 :            :     }
        1108                 :            :
        1109         [ #  # ]:          0 :     if (MAX_STRING_LENGTH < size) {
        1110                 :          0 :         size = MAX_STRING_LENGTH;
        1111                 :          0 :         truncated = 1;
        1112                 :            :     }
        1113                 :            :     else {
        1114                 :          0 :         truncated = 0;
        1115                 :            :     }
        1116                 :            :
        1117                 :            :     // Is an ASCII string?
        1118         [ #  # ]:          0 :     if (ascii->state.ascii) {
        1119                 :            :         assert(kind == PyUnicode_1BYTE_KIND);
        1120                 :          0 :         char *str = data;
        1121                 :            :
        1122                 :          0 :         int need_escape = 0;
        1123         [ #  # ]:          0 :         for (i=0; i < size; i++) {
        1124                 :          0 :             ch = str[i];
        1125   [ #  #  #  # ]:          0 :             if (!(' ' <= ch && ch <= 126)) {
        1126                 :          0 :                 need_escape = 1;
        1127                 :          0 :                 break;
        1128                 :            :             }
        1129                 :            :         }
        1130         [ #  # ]:          0 :         if (!need_escape) {
        1131                 :            :             // The string can be written with a single write() syscall
        1132                 :          0 :             _Py_write_noraise(fd, str, size);
        1133                 :          0 :             goto done;
        1134                 :            :         }
        1135                 :            :     }
        1136                 :            :
        1137         [ #  # ]:          0 :     for (i=0; i < size; i++) {
        1138                 :          0 :         ch = PyUnicode_READ(kind, data, i);
        1139   [ #  #  #  # ]:          0 :         if (' ' <= ch && ch <= 126) {
        1140                 :            :             /* printable ASCII character */
        1141                 :          0 :             char c = (char)ch;
        1142                 :          0 :             _Py_write_noraise(fd, &c, 1);
        1143                 :            :         }
        1144         [ #  # ]:          0 :         else if (ch <= 0xff) {
        1145                 :          0 :             PUTS(fd, "\\x");
        1146                 :          0 :             _Py_DumpHexadecimal(fd, ch, 2);
        1147                 :            :         }
        1148         [ #  # ]:          0 :         else if (ch <= 0xffff) {
        1149                 :          0 :             PUTS(fd, "\\u");
        1150                 :          0 :             _Py_DumpHexadecimal(fd, ch, 4);
        1151                 :            :         }
        1152                 :            :         else {
        1153                 :          0 :             PUTS(fd, "\\U");
        1154                 :          0 :             _Py_DumpHexadecimal(fd, ch, 8);
        1155                 :            :         }
        1156                 :            :     }
        1157                 :            :
        1158                 :          0 : done:
        1159         [ #  # ]:          0 :     if (truncated) {
        1160                 :          0 :         PUTS(fd, "...");
        1161                 :            :     }
        1162                 :            : }
        1163                 :            :
        1164                 :            : /* Write a frame into the file fd: "File "xxx", line xxx in xxx".
        1165                 :            :
        1166                 :            :    This function is signal safe. */
        1167                 :            :
        1168                 :            : static void
        1169                 :          0 : dump_frame(int fd, _PyInterpreterFrame *frame)
        1170                 :            : {
        1171                 :          0 :     PyCodeObject *code = frame->f_code;
        1172                 :          0 :     PUTS(fd, "  File ");
        1173         [ #  # ]:          0 :     if (code->co_filename != NULL
        1174         [ #  # ]:          0 :         && PyUnicode_Check(code->co_filename))
        1175                 :            :     {
        1176                 :          0 :         PUTS(fd, "\"");
        1177                 :          0 :         _Py_DumpASCII(fd, code->co_filename);
        1178                 :          0 :         PUTS(fd, "\"");
        1179                 :            :     } else {
        1180                 :          0 :         PUTS(fd, "???");
        1181                 :            :     }
        1182                 :            :
        1183                 :          0 :     int lineno = _PyInterpreterFrame_GetLine(frame);
        1184                 :          0 :     PUTS(fd, ", line ");
        1185         [ #  # ]:          0 :     if (lineno >= 0) {
        1186                 :          0 :         _Py_DumpDecimal(fd, (size_t)lineno);
        1187                 :            :     }
        1188                 :            :     else {
        1189                 :          0 :         PUTS(fd, "???");
        1190                 :            :     }
        1191                 :          0 :     PUTS(fd, " in ");
        1192                 :            :
        1193         [ #  # ]:          0 :     if (code->co_name != NULL
        1194         [ #  # ]:          0 :        && PyUnicode_Check(code->co_name)) {
        1195                 :          0 :         _Py_DumpASCII(fd, code->co_name);
        1196                 :            :     }
        1197                 :            :     else {
        1198                 :          0 :         PUTS(fd, "???");
        1199                 :            :     }
        1200                 :            :
        1201                 :          0 :     PUTS(fd, "\n");
        1202                 :          0 : }
        1203                 :            :
        1204                 :            : static void
        1205                 :          0 : dump_traceback(int fd, PyThreadState *tstate, int write_header)
        1206                 :            : {
        1207                 :            :     _PyInterpreterFrame *frame;
        1208                 :            :     unsigned int depth;
        1209                 :            :
        1210         [ #  # ]:          0 :     if (write_header) {
        1211                 :          0 :         PUTS(fd, "Stack (most recent call first):\n");
        1212                 :            :     }
        1213                 :            :
        1214                 :          0 :     frame = tstate->cframe->current_frame;
        1215         [ #  # ]:          0 :     if (frame == NULL) {
        1216                 :          0 :         PUTS(fd, "  <no Python frame>\n");
        1217                 :          0 :         return;
        1218                 :            :     }
        1219                 :            :
        1220                 :          0 :     depth = 0;
        1221                 :            :     while (1) {
        1222         [ #  # ]:          0 :         if (MAX_FRAME_DEPTH <= depth) {
        1223                 :          0 :             PUTS(fd, "  ...\n");
        1224                 :          0 :             break;
        1225                 :            :         }
        1226                 :          0 :         dump_frame(fd, frame);
        1227                 :          0 :         frame = frame->previous;
        1228         [ #  # ]:          0 :         if (frame == NULL) {
        1229                 :          0 :             break;
        1230                 :            :         }
        1231         [ #  # ]:          0 :         if (frame->owner == FRAME_OWNED_BY_CSTACK) {
        1232                 :            :             /* Trampoline frame */
        1233                 :          0 :             frame = frame->previous;
        1234                 :            :         }
        1235         [ #  # ]:          0 :         if (frame == NULL) {
        1236                 :          0 :             break;
        1237                 :            :         }
        1238                 :            :         /* Can't have more than one shim frame in a row */
        1239                 :            :         assert(frame->owner != FRAME_OWNED_BY_CSTACK);
        1240                 :          0 :         depth++;
        1241                 :            :     }
        1242                 :            : }
        1243                 :            :
        1244                 :            : /* Dump the traceback of a Python thread into fd. Use write() to write the
        1245                 :            :    traceback and retry if write() is interrupted by a signal (failed with
        1246                 :            :    EINTR), but don't call the Python signal handler.
        1247                 :            :
        1248                 :            :    The caller is responsible to call PyErr_CheckSignals() to call Python signal
        1249                 :            :    handlers if signals were received. */
        1250                 :            : void
        1251                 :          0 : _Py_DumpTraceback(int fd, PyThreadState *tstate)
        1252                 :            : {
        1253                 :          0 :     dump_traceback(fd, tstate, 1);
        1254                 :          0 : }
        1255                 :            :
        1256                 :            : /* Write the thread identifier into the file 'fd': "Current thread 0xHHHH:\" if
        1257                 :            :    is_current is true, "Thread 0xHHHH:\n" otherwise.
        1258                 :            :
        1259                 :            :    This function is signal safe. */
        1260                 :            :
        1261                 :            : static void
        1262                 :          0 : write_thread_id(int fd, PyThreadState *tstate, int is_current)
        1263                 :            : {
        1264         [ #  # ]:          0 :     if (is_current)
        1265                 :          0 :         PUTS(fd, "Current thread 0x");
        1266                 :            :     else
        1267                 :          0 :         PUTS(fd, "Thread 0x");
        1268                 :          0 :     _Py_DumpHexadecimal(fd,
        1269                 :            :                         tstate->thread_id,
        1270                 :            :                         sizeof(unsigned long) * 2);
        1271                 :          0 :     PUTS(fd, " (most recent call first):\n");
        1272                 :          0 : }
        1273                 :            :
        1274                 :            : /* Dump the traceback of all Python threads into fd. Use write() to write the
        1275                 :            :    traceback and retry if write() is interrupted by a signal (failed with
        1276                 :            :    EINTR), but don't call the Python signal handler.
        1277                 :            :
        1278                 :            :    The caller is responsible to call PyErr_CheckSignals() to call Python signal
        1279                 :            :    handlers if signals were received. */
        1280                 :            : const char*
        1281                 :          0 : _Py_DumpTracebackThreads(int fd, PyInterpreterState *interp,
        1282                 :            :                          PyThreadState *current_tstate)
        1283                 :            : {
        1284                 :            :     PyThreadState *tstate;
        1285                 :            :     unsigned int nthreads;
        1286                 :            :
        1287         [ #  # ]:          0 :     if (current_tstate == NULL) {
        1288                 :            :         /* _Py_DumpTracebackThreads() is called from signal handlers by
        1289                 :            :            faulthandler.
        1290                 :            :
        1291                 :            :            SIGSEGV, SIGFPE, SIGABRT, SIGBUS and SIGILL are synchronous signals
        1292                 :            :            and are thus delivered to the thread that caused the fault. Get the
        1293                 :            :            Python thread state of the current thread.
        1294                 :            :
        1295                 :            :            PyThreadState_Get() doesn't give the state of the thread that caused
        1296                 :            :            the fault if the thread released the GIL, and so
        1297                 :            :            _PyThreadState_GET() cannot be used. Read the thread specific
        1298                 :            :            storage (TSS) instead: call PyGILState_GetThisThreadState(). */
        1299                 :          0 :         current_tstate = PyGILState_GetThisThreadState();
        1300                 :            :     }
        1301                 :            :
        1302         [ #  # ]:          0 :     if (interp == NULL) {
        1303         [ #  # ]:          0 :         if (current_tstate == NULL) {
        1304                 :          0 :             interp = _PyGILState_GetInterpreterStateUnsafe();
        1305         [ #  # ]:          0 :             if (interp == NULL) {
        1306                 :            :                 /* We need the interpreter state to get Python threads */
        1307                 :          0 :                 return "unable to get the interpreter state";
        1308                 :            :             }
        1309                 :            :         }
        1310                 :            :         else {
        1311                 :          0 :             interp = current_tstate->interp;
        1312                 :            :         }
        1313                 :            :     }
        1314                 :            :     assert(interp != NULL);
        1315                 :            :
        1316                 :            :     /* Get the current interpreter from the current thread */
        1317                 :          0 :     tstate = PyInterpreterState_ThreadHead(interp);
        1318         [ #  # ]:          0 :     if (tstate == NULL)
        1319                 :          0 :         return "unable to get the thread head state";
        1320                 :            :
        1321                 :            :     /* Dump the traceback of each thread */
        1322                 :          0 :     tstate = PyInterpreterState_ThreadHead(interp);
        1323                 :          0 :     nthreads = 0;
        1324                 :            :     _Py_BEGIN_SUPPRESS_IPH
        1325                 :            :     do
        1326                 :            :     {
        1327         [ #  # ]:          0 :         if (nthreads != 0)
        1328                 :          0 :             PUTS(fd, "\n");
        1329         [ #  # ]:          0 :         if (nthreads >= MAX_NTHREADS) {
        1330                 :          0 :             PUTS(fd, "...\n");
        1331                 :          0 :             break;
        1332                 :            :         }
        1333                 :          0 :         write_thread_id(fd, tstate, tstate == current_tstate);
        1334   [ #  #  #  # ]:          0 :         if (tstate == current_tstate && tstate->interp->gc.collecting) {
        1335                 :          0 :             PUTS(fd, "  Garbage-collecting\n");
        1336                 :            :         }
        1337                 :          0 :         dump_traceback(fd, tstate, 0);
        1338                 :          0 :         tstate = PyThreadState_Next(tstate);
        1339                 :          0 :         nthreads++;
        1340         [ #  # ]:          0 :     } while (tstate != NULL);
        1341                 :            :     _Py_END_SUPPRESS_IPH
        1342                 :            :
        1343                 :          0 :     return NULL;
        1344                 :            : }
        1345                 :            :

Generated by: LCOV version 1.14