Home | History | Annotate | Download | only in Python
      1 
      2 /* Traceback implementation */
      3 
      4 #include "Python.h"
      5 
      6 #include "code.h"
      7 #include "frameobject.h"
      8 #include "structmember.h"
      9 #include "osdefs.h"
     10 #include "traceback.h"
     11 
     12 #define OFF(x) offsetof(PyTracebackObject, x)
     13 
     14 static PyMemberDef tb_memberlist[] = {
     15     {"tb_next",         T_OBJECT,       OFF(tb_next), READONLY},
     16     {"tb_frame",        T_OBJECT,       OFF(tb_frame), READONLY},
     17     {"tb_lasti",        T_INT,          OFF(tb_lasti), READONLY},
     18     {"tb_lineno",       T_INT,          OFF(tb_lineno), READONLY},
     19     {NULL}      /* Sentinel */
     20 };
     21 
     22 static void
     23 tb_dealloc(PyTracebackObject *tb)
     24 {
     25     PyObject_GC_UnTrack(tb);
     26     Py_TRASHCAN_SAFE_BEGIN(tb)
     27     Py_XDECREF(tb->tb_next);
     28     Py_XDECREF(tb->tb_frame);
     29     PyObject_GC_Del(tb);
     30     Py_TRASHCAN_SAFE_END(tb)
     31 }
     32 
     33 static int
     34 tb_traverse(PyTracebackObject *tb, visitproc visit, void *arg)
     35 {
     36     Py_VISIT(tb->tb_next);
     37     Py_VISIT(tb->tb_frame);
     38     return 0;
     39 }
     40 
     41 static void
     42 tb_clear(PyTracebackObject *tb)
     43 {
     44     Py_CLEAR(tb->tb_next);
     45     Py_CLEAR(tb->tb_frame);
     46 }
     47 
     48 PyTypeObject PyTraceBack_Type = {
     49     PyVarObject_HEAD_INIT(&PyType_Type, 0)
     50     "traceback",
     51     sizeof(PyTracebackObject),
     52     0,
     53     (destructor)tb_dealloc, /*tp_dealloc*/
     54     0,                  /*tp_print*/
     55     0,              /*tp_getattr*/
     56     0,                  /*tp_setattr*/
     57     0,                  /*tp_compare*/
     58     0,                  /*tp_repr*/
     59     0,                  /*tp_as_number*/
     60     0,                  /*tp_as_sequence*/
     61     0,                  /*tp_as_mapping*/
     62     0,                  /* tp_hash */
     63     0,                  /* tp_call */
     64     0,                  /* tp_str */
     65     0,                  /* tp_getattro */
     66     0,                  /* tp_setattro */
     67     0,                                          /* tp_as_buffer */
     68     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */
     69     0,                                          /* tp_doc */
     70     (traverseproc)tb_traverse,                  /* tp_traverse */
     71     (inquiry)tb_clear,                          /* tp_clear */
     72     0,                                          /* tp_richcompare */
     73     0,                                          /* tp_weaklistoffset */
     74     0,                                          /* tp_iter */
     75     0,                                          /* tp_iternext */
     76     0,                                          /* tp_methods */
     77     tb_memberlist,                              /* tp_members */
     78     0,                                          /* tp_getset */
     79     0,                                          /* tp_base */
     80     0,                                          /* tp_dict */
     81 };
     82 
     83 static PyTracebackObject *
     84 newtracebackobject(PyTracebackObject *next, PyFrameObject *frame)
     85 {
     86     PyTracebackObject *tb;
     87     if ((next != NULL && !PyTraceBack_Check(next)) ||
     88                     frame == NULL || !PyFrame_Check(frame)) {
     89         PyErr_BadInternalCall();
     90         return NULL;
     91     }
     92     tb = PyObject_GC_New(PyTracebackObject, &PyTraceBack_Type);
     93     if (tb != NULL) {
     94         Py_XINCREF(next);
     95         tb->tb_next = next;
     96         Py_XINCREF(frame);
     97         tb->tb_frame = frame;
     98         tb->tb_lasti = frame->f_lasti;
     99         tb->tb_lineno = PyFrame_GetLineNumber(frame);
    100         PyObject_GC_Track(tb);
    101     }
    102     return tb;
    103 }
    104 
    105 int
    106 PyTraceBack_Here(PyFrameObject *frame)
    107 {
    108     PyThreadState *tstate = PyThreadState_GET();
    109     PyTracebackObject *oldtb = (PyTracebackObject *) tstate->curexc_traceback;
    110     PyTracebackObject *tb = newtracebackobject(oldtb, frame);
    111     if (tb == NULL)
    112         return -1;
    113     tstate->curexc_traceback = (PyObject *)tb;
    114     Py_XDECREF(oldtb);
    115     return 0;
    116 }
    117 
    118 int
    119 _Py_DisplaySourceLine(PyObject *f, const char *filename, int lineno, int indent)
    120 {
    121     int err = 0;
    122     FILE *xfp = NULL;
    123     char linebuf[2000];
    124     int i;
    125     char namebuf[MAXPATHLEN+1];
    126 
    127     if (filename == NULL)
    128         return -1;
    129     /* This is needed by Emacs' compile command */
    130 #define FMT "  File \"%.500s\", line %d, in %.500s\n"
    131     xfp = fopen(filename, "r" PY_STDIOTEXTMODE);
    132     if (xfp == NULL) {
    133         /* Search tail of filename in sys.path before giving up */
    134         PyObject *path;
    135         const char *tail = strrchr(filename, SEP);
    136         if (tail == NULL)
    137             tail = filename;
    138         else
    139             tail++;
    140         path = PySys_GetObject("path");
    141         if (path != NULL && PyList_Check(path)) {
    142             Py_ssize_t _npath = PyList_Size(path);
    143             int npath = Py_SAFE_DOWNCAST(_npath, Py_ssize_t, int);
    144             size_t taillen = strlen(tail);
    145             for (i = 0; i < npath; i++) {
    146                 PyObject *v = PyList_GetItem(path, i);
    147                 if (v == NULL) {
    148                     PyErr_Clear();
    149                     break;
    150                 }
    151                 if (PyString_Check(v)) {
    152                     size_t len;
    153                     len = PyString_GET_SIZE(v);
    154                     if (len + 1 + taillen >= MAXPATHLEN)
    155                         continue; /* Too long */
    156                     strcpy(namebuf, PyString_AsString(v));
    157                     if (strlen(namebuf) != len)
    158                         continue; /* v contains '\0' */
    159                     if (len > 0 && namebuf[len-1] != SEP)
    160                         namebuf[len++] = SEP;
    161                     strcpy(namebuf+len, tail);
    162                     xfp = fopen(namebuf, "r" PY_STDIOTEXTMODE);
    163                     if (xfp != NULL) {
    164                         break;
    165                     }
    166                 }
    167             }
    168         }
    169     }
    170 
    171     if (xfp == NULL)
    172         return err;
    173     if (err != 0) {
    174         fclose(xfp);
    175         return err;
    176     }
    177 
    178     for (i = 0; i < lineno; i++) {
    179         char* pLastChar = &linebuf[sizeof(linebuf)-2];
    180         do {
    181             *pLastChar = '\0';
    182             if (Py_UniversalNewlineFgets(linebuf, sizeof linebuf, xfp, NULL) == NULL)
    183                 break;
    184             /* fgets read *something*; if it didn't get as
    185                far as pLastChar, it must have found a newline
    186                or hit the end of the file;              if pLastChar is \n,
    187                it obviously found a newline; else we haven't
    188                yet seen a newline, so must continue */
    189         } while (*pLastChar != '\0' && *pLastChar != '\n');
    190     }
    191     if (i == lineno) {
    192         char buf[11];
    193         char *p = linebuf;
    194         while (*p == ' ' || *p == '\t' || *p == '\014')
    195             p++;
    196 
    197         /* Write some spaces before the line */
    198         strcpy(buf, "          ");
    199         assert (strlen(buf) == 10);
    200         while (indent > 0) {
    201             if(indent < 10)
    202                 buf[indent] = '\0';
    203             err = PyFile_WriteString(buf, f);
    204             if (err != 0)
    205                 break;
    206             indent -= 10;
    207         }
    208 
    209         if (err == 0)
    210             err = PyFile_WriteString(p, f);
    211         if (err == 0 && strchr(p, '\n') == NULL)
    212             err = PyFile_WriteString("\n", f);
    213     }
    214     fclose(xfp);
    215     return err;
    216 }
    217 
    218 static int
    219 tb_displayline(PyObject *f, const char *filename, int lineno, const char *name)
    220 {
    221     int err = 0;
    222     char linebuf[2000];
    223 
    224     if (filename == NULL || name == NULL)
    225         return -1;
    226     /* This is needed by Emacs' compile command */
    227 #define FMT "  File \"%.500s\", line %d, in %.500s\n"
    228     PyOS_snprintf(linebuf, sizeof(linebuf), FMT, filename, lineno, name);
    229     err = PyFile_WriteString(linebuf, f);
    230     if (err != 0)
    231         return err;
    232     return _Py_DisplaySourceLine(f, filename, lineno, 4);
    233 }
    234 
    235 static int
    236 tb_printinternal(PyTracebackObject *tb, PyObject *f, long limit)
    237 {
    238     int err = 0;
    239     long depth = 0;
    240     PyTracebackObject *tb1 = tb;
    241     while (tb1 != NULL) {
    242         depth++;
    243         tb1 = tb1->tb_next;
    244     }
    245     while (tb != NULL && err == 0) {
    246         if (depth <= limit) {
    247             err = tb_displayline(f,
    248                 PyString_AsString(
    249                     tb->tb_frame->f_code->co_filename),
    250                 tb->tb_lineno,
    251                 PyString_AsString(tb->tb_frame->f_code->co_name));
    252         }
    253         depth--;
    254         tb = tb->tb_next;
    255         if (err == 0)
    256             err = PyErr_CheckSignals();
    257     }
    258     return err;
    259 }
    260 
    261 int
    262 PyTraceBack_Print(PyObject *v, PyObject *f)
    263 {
    264     int err;
    265     PyObject *limitv;
    266     long limit = 1000;
    267     if (v == NULL)
    268         return 0;
    269     if (!PyTraceBack_Check(v)) {
    270         PyErr_BadInternalCall();
    271         return -1;
    272     }
    273     limitv = PySys_GetObject("tracebacklimit");
    274     if (limitv && PyInt_Check(limitv)) {
    275         limit = PyInt_AsLong(limitv);
    276         if (limit <= 0)
    277             return 0;
    278     }
    279     err = PyFile_WriteString("Traceback (most recent call last):\n", f);
    280     if (!err)
    281         err = tb_printinternal((PyTracebackObject *)v, f, limit);
    282     return err;
    283 }
    284