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
14static 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
22static void
23tb_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
33static int
34tb_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
41static void
42tb_clear(PyTracebackObject *tb)
43{
44    Py_CLEAR(tb->tb_next);
45    Py_CLEAR(tb->tb_frame);
46}
47
48PyTypeObject 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
83static PyTracebackObject *
84newtracebackobject(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
105int
106PyTraceBack_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
118int
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
218static int
219tb_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
235static int
236tb_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
261int
262PyTraceBack_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