Line data Source code
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 2154 : tb_dealloc(PyTracebackObject *tb)
24 : {
25 2154 : PyObject_GC_UnTrack(tb);
26 2154 : Py_TRASHCAN_SAFE_BEGIN(tb)
27 2154 : Py_XDECREF(tb->tb_next);
28 2154 : Py_XDECREF(tb->tb_frame);
29 2154 : PyObject_GC_Del(tb);
30 2154 : Py_TRASHCAN_SAFE_END(tb)
31 2154 : }
32 :
33 : static int
34 28 : tb_traverse(PyTracebackObject *tb, visitproc visit, void *arg)
35 : {
36 28 : Py_VISIT(tb->tb_next);
37 28 : Py_VISIT(tb->tb_frame);
38 28 : return 0;
39 : }
40 :
41 : static void
42 0 : tb_clear(PyTracebackObject *tb)
43 : {
44 0 : Py_CLEAR(tb->tb_next);
45 0 : Py_CLEAR(tb->tb_frame);
46 0 : }
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 2154 : newtracebackobject(PyTracebackObject *next, PyFrameObject *frame)
85 : {
86 : PyTracebackObject *tb;
87 2154 : if ((next != NULL && !PyTraceBack_Check(next)) ||
88 2154 : frame == NULL || !PyFrame_Check(frame)) {
89 0 : PyErr_BadInternalCall();
90 0 : return NULL;
91 : }
92 2154 : tb = PyObject_GC_New(PyTracebackObject, &PyTraceBack_Type);
93 2154 : if (tb != NULL) {
94 2154 : Py_XINCREF(next);
95 2154 : tb->tb_next = next;
96 2154 : Py_XINCREF(frame);
97 2154 : tb->tb_frame = frame;
98 2154 : tb->tb_lasti = frame->f_lasti;
99 2154 : tb->tb_lineno = PyFrame_GetLineNumber(frame);
100 2154 : PyObject_GC_Track(tb);
101 : }
102 2154 : return tb;
103 : }
104 :
105 : int
106 2154 : PyTraceBack_Here(PyFrameObject *frame)
107 : {
108 2154 : PyThreadState *tstate = PyThreadState_GET();
109 2154 : PyTracebackObject *oldtb = (PyTracebackObject *) tstate->curexc_traceback;
110 2154 : PyTracebackObject *tb = newtracebackobject(oldtb, frame);
111 2154 : if (tb == NULL)
112 0 : return -1;
113 2154 : tstate->curexc_traceback = (PyObject *)tb;
114 2154 : Py_XDECREF(oldtb);
115 2154 : return 0;
116 : }
117 :
118 : int
119 0 : _Py_DisplaySourceLine(PyObject *f, const char *filename, int lineno, int indent)
120 : {
121 0 : int err = 0;
122 0 : FILE *xfp = NULL;
123 : char linebuf[2000];
124 : int i;
125 : char namebuf[MAXPATHLEN+1];
126 :
127 0 : if (filename == NULL)
128 0 : return -1;
129 : /* This is needed by Emacs' compile command */
130 : #define FMT " File \"%.500s\", line %d, in %.500s\n"
131 0 : xfp = fopen(filename, "r" PY_STDIOTEXTMODE);
132 0 : if (xfp == NULL) {
133 : /* Search tail of filename in sys.path before giving up */
134 : PyObject *path;
135 0 : const char *tail = strrchr(filename, SEP);
136 0 : if (tail == NULL)
137 0 : tail = filename;
138 : else
139 0 : tail++;
140 0 : path = PySys_GetObject("path");
141 0 : if (path != NULL && PyList_Check(path)) {
142 0 : Py_ssize_t _npath = PyList_Size(path);
143 0 : int npath = Py_SAFE_DOWNCAST(_npath, Py_ssize_t, int);
144 0 : size_t taillen = strlen(tail);
145 0 : for (i = 0; i < npath; i++) {
146 0 : PyObject *v = PyList_GetItem(path, i);
147 0 : if (v == NULL) {
148 0 : PyErr_Clear();
149 0 : break;
150 : }
151 0 : if (PyString_Check(v)) {
152 : size_t len;
153 0 : len = PyString_GET_SIZE(v);
154 0 : if (len + 1 + taillen >= MAXPATHLEN)
155 0 : continue; /* Too long */
156 0 : strcpy(namebuf, PyString_AsString(v));
157 0 : if (strlen(namebuf) != len)
158 0 : continue; /* v contains '\0' */
159 0 : if (len > 0 && namebuf[len-1] != SEP)
160 0 : namebuf[len++] = SEP;
161 0 : strcpy(namebuf+len, tail);
162 0 : xfp = fopen(namebuf, "r" PY_STDIOTEXTMODE);
163 0 : if (xfp != NULL) {
164 0 : break;
165 : }
166 : }
167 : }
168 : }
169 : }
170 :
171 0 : if (xfp == NULL)
172 0 : return err;
173 :
174 0 : for (i = 0; i < lineno; i++) {
175 0 : char* pLastChar = &linebuf[sizeof(linebuf)-2];
176 : do {
177 0 : *pLastChar = '\0';
178 0 : if (Py_UniversalNewlineFgets(linebuf, sizeof linebuf, xfp, NULL) == NULL)
179 0 : 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 0 : } while (*pLastChar != '\0' && *pLastChar != '\n');
186 : }
187 0 : if (i == lineno) {
188 : char buf[11];
189 0 : char *p = linebuf;
190 0 : while (*p == ' ' || *p == '\t' || *p == '\014')
191 0 : p++;
192 :
193 : /* Write some spaces before the line */
194 0 : strcpy(buf, " ");
195 : assert (strlen(buf) == 10);
196 0 : while (indent > 0) {
197 0 : if(indent < 10)
198 0 : buf[indent] = '\0';
199 0 : err = PyFile_WriteString(buf, f);
200 0 : if (err != 0)
201 0 : break;
202 0 : indent -= 10;
203 : }
204 :
205 0 : if (err == 0)
206 0 : err = PyFile_WriteString(p, f);
207 0 : if (err == 0 && strchr(p, '\n') == NULL)
208 0 : err = PyFile_WriteString("\n", f);
209 : }
210 0 : fclose(xfp);
211 0 : return err;
212 : }
213 :
214 : static int
215 0 : tb_displayline(PyObject *f, const char *filename, int lineno, const char *name)
216 : {
217 0 : int err = 0;
218 : char linebuf[2000];
219 :
220 0 : if (filename == NULL || name == NULL)
221 0 : return -1;
222 : /* This is needed by Emacs' compile command */
223 : #define FMT " File \"%.500s\", line %d, in %.500s\n"
224 0 : PyOS_snprintf(linebuf, sizeof(linebuf), FMT, filename, lineno, name);
225 0 : err = PyFile_WriteString(linebuf, f);
226 0 : if (err != 0)
227 0 : return err;
228 0 : return _Py_DisplaySourceLine(f, filename, lineno, 4);
229 : }
230 :
231 : static int
232 0 : tb_printinternal(PyTracebackObject *tb, PyObject *f, long limit)
233 : {
234 0 : int err = 0;
235 0 : long depth = 0;
236 0 : PyTracebackObject *tb1 = tb;
237 0 : while (tb1 != NULL) {
238 0 : depth++;
239 0 : tb1 = tb1->tb_next;
240 : }
241 0 : while (tb != NULL && err == 0) {
242 0 : if (depth <= limit) {
243 0 : err = tb_displayline(f,
244 0 : PyString_AsString(
245 0 : tb->tb_frame->f_code->co_filename),
246 : tb->tb_lineno,
247 0 : PyString_AsString(tb->tb_frame->f_code->co_name));
248 : }
249 0 : depth--;
250 0 : tb = tb->tb_next;
251 0 : if (err == 0)
252 0 : err = PyErr_CheckSignals();
253 : }
254 0 : return err;
255 : }
256 :
257 : int
258 0 : PyTraceBack_Print(PyObject *v, PyObject *f)
259 : {
260 : int err;
261 : PyObject *limitv;
262 0 : long limit = 1000;
263 0 : if (v == NULL)
264 0 : return 0;
265 0 : if (!PyTraceBack_Check(v)) {
266 0 : PyErr_BadInternalCall();
267 0 : return -1;
268 : }
269 0 : limitv = PySys_GetObject("tracebacklimit");
270 0 : if (limitv && PyInt_Check(limitv)) {
271 0 : limit = PyInt_AsLong(limitv);
272 0 : if (limit <= 0)
273 0 : return 0;
274 : }
275 0 : err = PyFile_WriteString("Traceback (most recent call last):\n", f);
276 0 : if (!err)
277 0 : err = tb_printinternal((PyTracebackObject *)v, f, limit);
278 0 : return err;
279 : }
|