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 : :
|