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
tb_dealloc(PyTracebackObject * tb)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
tb_traverse(PyTracebackObject * tb,visitproc visit,void * arg)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
tb_clear(PyTracebackObject * tb)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 *
newtracebackobject(PyTracebackObject * next,PyFrameObject * frame)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
PyTraceBack_Here(PyFrameObject * frame)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
_Py_DisplaySourceLine(PyObject * f,const char * filename,int lineno,int indent)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 
174     for (i = 0; i < lineno; i++) {
175         char* pLastChar = &linebuf[sizeof(linebuf)-2];
176         do {
177             *pLastChar = '\0';
178             if (Py_UniversalNewlineFgets(linebuf, sizeof linebuf, xfp, NULL) == NULL)
179                 break;
180             /* fgets read *something*; if it didn't get as
181                far as pLastChar, it must have found a newline
182                or hit the end of the file;              if pLastChar is \n,
183                it obviously found a newline; else we haven't
184                yet seen a newline, so must continue */
185         } while (*pLastChar != '\0' && *pLastChar != '\n');
186     }
187     if (i == lineno) {
188         char buf[11];
189         char *p = linebuf;
190         while (*p == ' ' || *p == '\t' || *p == '\014')
191             p++;
192 
193         /* Write some spaces before the line */
194         strcpy(buf, "          ");
195         assert (strlen(buf) == 10);
196         while (indent > 0) {
197             if(indent < 10)
198                 buf[indent] = '\0';
199             err = PyFile_WriteString(buf, f);
200             if (err != 0)
201                 break;
202             indent -= 10;
203         }
204 
205         if (err == 0)
206             err = PyFile_WriteString(p, f);
207         if (err == 0 && strchr(p, '\n') == NULL)
208             err = PyFile_WriteString("\n", f);
209     }
210     fclose(xfp);
211     return err;
212 }
213 
214 static int
tb_displayline(PyObject * f,const char * filename,int lineno,const char * name)215 tb_displayline(PyObject *f, const char *filename, int lineno, const char *name)
216 {
217     int err = 0;
218     char linebuf[2000];
219 
220     if (filename == NULL || name == NULL)
221         return -1;
222     /* This is needed by Emacs' compile command */
223 #define FMT "  File \"%.500s\", line %d, in %.500s\n"
224     PyOS_snprintf(linebuf, sizeof(linebuf), FMT, filename, lineno, name);
225     err = PyFile_WriteString(linebuf, f);
226     if (err != 0)
227         return err;
228     return _Py_DisplaySourceLine(f, filename, lineno, 4);
229 }
230 
231 static int
tb_printinternal(PyTracebackObject * tb,PyObject * f,long limit)232 tb_printinternal(PyTracebackObject *tb, PyObject *f, long limit)
233 {
234     int err = 0;
235     long depth = 0;
236     PyTracebackObject *tb1 = tb;
237     while (tb1 != NULL) {
238         depth++;
239         tb1 = tb1->tb_next;
240     }
241     while (tb != NULL && err == 0) {
242         if (depth <= limit) {
243             err = tb_displayline(f,
244                 PyString_AsString(
245                     tb->tb_frame->f_code->co_filename),
246                 tb->tb_lineno,
247                 PyString_AsString(tb->tb_frame->f_code->co_name));
248         }
249         depth--;
250         tb = tb->tb_next;
251         if (err == 0)
252             err = PyErr_CheckSignals();
253     }
254     return err;
255 }
256 
257 int
PyTraceBack_Print(PyObject * v,PyObject * f)258 PyTraceBack_Print(PyObject *v, PyObject *f)
259 {
260     int err;
261     PyObject *limitv;
262     long limit = 1000;
263     if (v == NULL)
264         return 0;
265     if (!PyTraceBack_Check(v)) {
266         PyErr_BadInternalCall();
267         return -1;
268     }
269     limitv = PySys_GetObject("tracebacklimit");
270     if (limitv && PyInt_Check(limitv)) {
271         limit = PyInt_AsLong(limitv);
272         if (limit <= 0)
273             return 0;
274     }
275     err = PyFile_WriteString("Traceback (most recent call last):\n", f);
276     if (!err)
277         err = tb_printinternal((PyTracebackObject *)v, f, limit);
278     return err;
279 }
280