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_Fetch()
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 : 5294461 : tb_create_raw(PyTracebackObject *next, PyFrameObject *frame, int lasti,
45 : : int lineno)
46 : : {
47 : : PyTracebackObject *tb;
48 [ + + + - : 5294461 : if ((next != NULL && !PyTraceBack_Check(next)) ||
+ - ]
49 [ - + ]: 5294461 : frame == NULL || !PyFrame_Check(frame)) {
50 : 0 : PyErr_BadInternalCall();
51 : 0 : return NULL;
52 : : }
53 : 5294461 : tb = PyObject_GC_New(PyTracebackObject, &PyTraceBack_Type);
54 [ + - ]: 5294461 : if (tb != NULL) {
55 : 5294461 : Py_XINCREF(next);
56 : 5294461 : tb->tb_next = next;
57 : 5294461 : Py_XINCREF(frame);
58 : 5294461 : tb->tb_frame = frame;
59 : 5294461 : tb->tb_lasti = lasti;
60 : 5294461 : tb->tb_lineno = lineno;
61 : 5294461 : PyObject_GC_Track(tb);
62 : : }
63 : 5294461 : return (PyObject *)tb;
64 : : }
65 : :
66 : : /*[clinic input]
67 : : @classmethod
68 : : TracebackType.__new__ as tb_new
69 : :
70 : : tb_next: object
71 : : tb_frame: object(type='PyFrameObject *', subclass_of='&PyFrame_Type')
72 : : tb_lasti: int
73 : : tb_lineno: int
74 : :
75 : : Create a new traceback object.
76 : : [clinic start generated code]*/
77 : :
78 : : static PyObject *
79 : 5 : tb_new_impl(PyTypeObject *type, PyObject *tb_next, PyFrameObject *tb_frame,
80 : : int tb_lasti, int tb_lineno)
81 : : /*[clinic end generated code: output=fa077debd72d861a input=01cbe8ec8783fca7]*/
82 : : {
83 [ + + ]: 5 : if (tb_next == Py_None) {
84 : 3 : tb_next = NULL;
85 [ + + ]: 2 : } else if (!PyTraceBack_Check(tb_next)) {
86 : 1 : return PyErr_Format(PyExc_TypeError,
87 : : "expected traceback object or None, got '%s'",
88 : 1 : Py_TYPE(tb_next)->tp_name);
89 : : }
90 : :
91 : 4 : return tb_create_raw((PyTracebackObject *)tb_next, tb_frame, tb_lasti,
92 : : tb_lineno);
93 : : }
94 : :
95 : : static PyObject *
96 : 1 : tb_dir(PyTracebackObject *self, PyObject *Py_UNUSED(ignored))
97 : : {
98 : 1 : return Py_BuildValue("[ssss]", "tb_frame", "tb_next",
99 : : "tb_lasti", "tb_lineno");
100 : : }
101 : :
102 : : static PyObject *
103 : 268300 : tb_next_get(PyTracebackObject *self, void *Py_UNUSED(_))
104 : : {
105 : 268300 : PyObject* ret = (PyObject*)self->tb_next;
106 [ + + ]: 268300 : if (!ret) {
107 : 190673 : ret = Py_None;
108 : : }
109 : 268300 : Py_INCREF(ret);
110 : 268300 : return ret;
111 : : }
112 : :
113 : : static int
114 : 157 : tb_next_set(PyTracebackObject *self, PyObject *new_next, void *Py_UNUSED(_))
115 : : {
116 [ + + ]: 157 : if (!new_next) {
117 : 1 : PyErr_Format(PyExc_TypeError, "can't delete tb_next attribute");
118 : 1 : return -1;
119 : : }
120 : :
121 : : /* We accept None or a traceback object, and map None -> NULL (inverse of
122 : : tb_next_get) */
123 [ + + ]: 156 : if (new_next == Py_None) {
124 : 152 : new_next = NULL;
125 [ + + ]: 4 : } else if (!PyTraceBack_Check(new_next)) {
126 : 1 : PyErr_Format(PyExc_TypeError,
127 : : "expected traceback object, got '%s'",
128 : 1 : Py_TYPE(new_next)->tp_name);
129 : 1 : return -1;
130 : : }
131 : :
132 : : /* Check for loops */
133 : 155 : PyTracebackObject *cursor = (PyTracebackObject *)new_next;
134 [ + + ]: 157 : while (cursor) {
135 [ + + ]: 4 : if (cursor == self) {
136 : 2 : PyErr_Format(PyExc_ValueError, "traceback loop detected");
137 : 2 : return -1;
138 : : }
139 : 2 : cursor = cursor->tb_next;
140 : : }
141 : :
142 : 153 : PyObject *old_next = (PyObject*)self->tb_next;
143 : 153 : Py_XINCREF(new_next);
144 : 153 : self->tb_next = (PyTracebackObject *)new_next;
145 : 153 : Py_XDECREF(old_next);
146 : :
147 : 153 : return 0;
148 : : }
149 : :
150 : :
151 : : static PyMethodDef tb_methods[] = {
152 : : {"__dir__", _PyCFunction_CAST(tb_dir), METH_NOARGS},
153 : : {NULL, NULL, 0, NULL},
154 : : };
155 : :
156 : : static PyMemberDef tb_memberlist[] = {
157 : : {"tb_frame", T_OBJECT, OFF(tb_frame), READONLY|PY_AUDIT_READ},
158 : : {"tb_lasti", T_INT, OFF(tb_lasti), READONLY},
159 : : {"tb_lineno", T_INT, OFF(tb_lineno), READONLY},
160 : : {NULL} /* Sentinel */
161 : : };
162 : :
163 : : static PyGetSetDef tb_getsetters[] = {
164 : : {"tb_next", (getter)tb_next_get, (setter)tb_next_set, NULL, NULL},
165 : : {NULL} /* Sentinel */
166 : : };
167 : :
168 : : static void
169 : 5295152 : tb_dealloc(PyTracebackObject *tb)
170 : : {
171 : 5295152 : PyObject_GC_UnTrack(tb);
172 [ + - + + ]: 5295152 : Py_TRASHCAN_BEGIN(tb, tb_dealloc)
173 : 5294421 : Py_XDECREF(tb->tb_next);
174 : 5294421 : Py_XDECREF(tb->tb_frame);
175 : 5294421 : PyObject_GC_Del(tb);
176 [ + - ]: 5294421 : Py_TRASHCAN_END
177 : 5295152 : }
178 : :
179 : : static int
180 : 164159 : tb_traverse(PyTracebackObject *tb, visitproc visit, void *arg)
181 : : {
182 [ + + - + ]: 164159 : Py_VISIT(tb->tb_next);
183 [ + - - + ]: 164159 : Py_VISIT(tb->tb_frame);
184 : 164159 : return 0;
185 : : }
186 : :
187 : : static int
188 : 916 : tb_clear(PyTracebackObject *tb)
189 : : {
190 [ + + ]: 916 : Py_CLEAR(tb->tb_next);
191 [ + - ]: 916 : Py_CLEAR(tb->tb_frame);
192 : 916 : return 0;
193 : : }
194 : :
195 : : PyTypeObject PyTraceBack_Type = {
196 : : PyVarObject_HEAD_INIT(&PyType_Type, 0)
197 : : "traceback",
198 : : sizeof(PyTracebackObject),
199 : : 0,
200 : : (destructor)tb_dealloc, /*tp_dealloc*/
201 : : 0, /*tp_vectorcall_offset*/
202 : : 0, /*tp_getattr*/
203 : : 0, /*tp_setattr*/
204 : : 0, /*tp_as_async*/
205 : : 0, /*tp_repr*/
206 : : 0, /*tp_as_number*/
207 : : 0, /*tp_as_sequence*/
208 : : 0, /*tp_as_mapping*/
209 : : 0, /* tp_hash */
210 : : 0, /* tp_call */
211 : : 0, /* tp_str */
212 : : PyObject_GenericGetAttr, /* tp_getattro */
213 : : 0, /* tp_setattro */
214 : : 0, /* tp_as_buffer */
215 : : Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */
216 : : tb_new__doc__, /* tp_doc */
217 : : (traverseproc)tb_traverse, /* tp_traverse */
218 : : (inquiry)tb_clear, /* tp_clear */
219 : : 0, /* tp_richcompare */
220 : : 0, /* tp_weaklistoffset */
221 : : 0, /* tp_iter */
222 : : 0, /* tp_iternext */
223 : : tb_methods, /* tp_methods */
224 : : tb_memberlist, /* tp_members */
225 : : tb_getsetters, /* tp_getset */
226 : : 0, /* tp_base */
227 : : 0, /* tp_dict */
228 : : 0, /* tp_descr_get */
229 : : 0, /* tp_descr_set */
230 : : 0, /* tp_dictoffset */
231 : : 0, /* tp_init */
232 : : 0, /* tp_alloc */
233 : : tb_new, /* tp_new */
234 : : };
235 : :
236 : :
237 : : PyObject*
238 : 5294457 : _PyTraceBack_FromFrame(PyObject *tb_next, PyFrameObject *frame)
239 : : {
240 : : assert(tb_next == NULL || PyTraceBack_Check(tb_next));
241 : : assert(frame != NULL);
242 : 5294457 : int addr = _PyInterpreterFrame_LASTI(frame->f_frame) * sizeof(_Py_CODEUNIT);
243 : 5294457 : return tb_create_raw((PyTracebackObject *)tb_next, frame, addr,
244 : : PyFrame_GetLineNumber(frame));
245 : : }
246 : :
247 : :
248 : : int
249 : 5294396 : PyTraceBack_Here(PyFrameObject *frame)
250 : : {
251 : : PyObject *exc, *val, *tb, *newtb;
252 : 5294396 : PyErr_Fetch(&exc, &val, &tb);
253 : 5294396 : newtb = _PyTraceBack_FromFrame(tb, frame);
254 [ - + ]: 5294396 : if (newtb == NULL) {
255 : 0 : _PyErr_ChainExceptions(exc, val, tb);
256 : 0 : return -1;
257 : : }
258 : 5294396 : PyErr_Restore(exc, val, newtb);
259 : 5294396 : Py_XDECREF(tb);
260 : 5294396 : return 0;
261 : : }
262 : :
263 : : /* Insert a frame into the traceback for (funcname, filename, lineno). */
264 : 30 : void _PyTraceback_Add(const char *funcname, const char *filename, int lineno)
265 : : {
266 : : PyObject *globals;
267 : : PyCodeObject *code;
268 : : PyFrameObject *frame;
269 : : PyObject *exc, *val, *tb;
270 : 30 : PyThreadState *tstate = _PyThreadState_GET();
271 : :
272 : : /* Save and clear the current exception. Python functions must not be
273 : : called with an exception set. Calling Python functions happens when
274 : : the codec of the filesystem encoding is implemented in pure Python. */
275 : 30 : _PyErr_Fetch(tstate, &exc, &val, &tb);
276 : :
277 : 30 : globals = PyDict_New();
278 [ - + ]: 30 : if (!globals)
279 : 0 : goto error;
280 : 30 : code = PyCode_NewEmpty(filename, funcname, lineno);
281 [ - + ]: 30 : if (!code) {
282 : 0 : Py_DECREF(globals);
283 : 0 : goto error;
284 : : }
285 : 30 : frame = PyFrame_New(tstate, code, globals, NULL);
286 : 30 : Py_DECREF(globals);
287 : 30 : Py_DECREF(code);
288 [ - + ]: 30 : if (!frame)
289 : 0 : goto error;
290 : 30 : frame->f_lineno = lineno;
291 : :
292 : 30 : _PyErr_Restore(tstate, exc, val, tb);
293 : 30 : PyTraceBack_Here(frame);
294 : 30 : Py_DECREF(frame);
295 : 30 : return;
296 : :
297 : 0 : error:
298 : 0 : _PyErr_ChainExceptions(exc, val, tb);
299 : : }
300 : :
301 : : static PyObject *
302 : 48 : _Py_FindSourceFile(PyObject *filename, char* namebuf, size_t namelen, PyObject *io)
303 : : {
304 : : Py_ssize_t i;
305 : : PyObject *binary;
306 : : PyObject *v;
307 : : Py_ssize_t npath;
308 : : size_t taillen;
309 : : PyObject *syspath;
310 : : PyObject *path;
311 : : const char* tail;
312 : : PyObject *filebytes;
313 : : const char* filepath;
314 : : Py_ssize_t len;
315 : : PyObject* result;
316 : 48 : PyObject *open = NULL;
317 : :
318 : 48 : filebytes = PyUnicode_EncodeFSDefault(filename);
319 [ - + ]: 48 : if (filebytes == NULL) {
320 : 0 : PyErr_Clear();
321 : 0 : return NULL;
322 : : }
323 : 48 : filepath = PyBytes_AS_STRING(filebytes);
324 : :
325 : : /* Search tail of filename in sys.path before giving up */
326 : 48 : tail = strrchr(filepath, SEP);
327 [ + + ]: 48 : if (tail == NULL)
328 : 8 : tail = filepath;
329 : : else
330 : 40 : tail++;
331 : 48 : taillen = strlen(tail);
332 : :
333 : 48 : PyThreadState *tstate = _PyThreadState_GET();
334 : 48 : syspath = _PySys_GetAttr(tstate, &_Py_ID(path));
335 [ + - - + ]: 48 : if (syspath == NULL || !PyList_Check(syspath))
336 : 0 : goto error;
337 : 48 : npath = PyList_Size(syspath);
338 : :
339 : 48 : open = PyObject_GetAttr(io, &_Py_ID(open));
340 [ + + ]: 212 : for (i = 0; i < npath; i++) {
341 : 164 : v = PyList_GetItem(syspath, i);
342 [ - + ]: 164 : if (v == NULL) {
343 : 0 : PyErr_Clear();
344 : 0 : break;
345 : : }
346 [ - + ]: 164 : if (!PyUnicode_Check(v))
347 : 0 : continue;
348 : 164 : path = PyUnicode_EncodeFSDefault(v);
349 [ - + ]: 164 : if (path == NULL) {
350 : 0 : PyErr_Clear();
351 : 0 : continue;
352 : : }
353 : 164 : len = PyBytes_GET_SIZE(path);
354 [ - + ]: 164 : if (len + 1 + (Py_ssize_t)taillen >= (Py_ssize_t)namelen - 1) {
355 : 0 : Py_DECREF(path);
356 : 0 : continue; /* Too long */
357 : : }
358 : 164 : strcpy(namebuf, PyBytes_AS_STRING(path));
359 : 164 : Py_DECREF(path);
360 [ - + ]: 164 : if (strlen(namebuf) != (size_t)len)
361 : 0 : continue; /* v contains '\0' */
362 [ + - + - ]: 164 : if (len > 0 && namebuf[len-1] != SEP)
363 : 164 : namebuf[len++] = SEP;
364 : 164 : strcpy(namebuf+len, tail);
365 : :
366 : 164 : binary = _PyObject_CallMethodFormat(tstate, open, "ss", namebuf, "rb");
367 [ - + ]: 164 : if (binary != NULL) {
368 : 0 : result = binary;
369 : 0 : goto finally;
370 : : }
371 : 164 : PyErr_Clear();
372 : : }
373 : 48 : goto error;
374 : :
375 : 48 : error:
376 : 48 : result = NULL;
377 : 48 : finally:
378 : 48 : Py_XDECREF(open);
379 : 48 : Py_DECREF(filebytes);
380 : 48 : return result;
381 : : }
382 : :
383 : : /* Writes indent spaces. Returns 0 on success and non-zero on failure.
384 : : */
385 : : int
386 : 2567 : _Py_WriteIndent(int indent, PyObject *f)
387 : : {
388 : 2567 : char buf[11] = " ";
389 : : assert(strlen(buf) == 10);
390 [ + + ]: 3323 : while (indent > 0) {
391 [ + + ]: 756 : if (indent < 10) {
392 : 681 : buf[indent] = '\0';
393 : : }
394 [ - + ]: 756 : if (PyFile_WriteString(buf, f) < 0) {
395 : 0 : return -1;
396 : : }
397 : 756 : indent -= 10;
398 : : }
399 : 2567 : return 0;
400 : : }
401 : :
402 : : /* Writes indent spaces, followed by the margin if it is not `\0`.
403 : : Returns 0 on success and non-zero on failure.
404 : : */
405 : : int
406 : 2034 : _Py_WriteIndentedMargin(int indent, const char *margin, PyObject *f)
407 : : {
408 [ - + ]: 2034 : if (_Py_WriteIndent(indent, f) < 0) {
409 : 0 : return -1;
410 : : }
411 [ + + ]: 2034 : if (margin) {
412 [ + + ]: 1925 : if (PyFile_WriteString(margin, f) < 0) {
413 : 33 : return -1;
414 : : }
415 : : }
416 : 2001 : return 0;
417 : : }
418 : :
419 : : static int
420 : 603 : display_source_line_with_margin(PyObject *f, PyObject *filename, int lineno, int indent,
421 : : int margin_indent, const char *margin,
422 : : int *truncation, PyObject **line)
423 : : {
424 : : int fd;
425 : : int i;
426 : : char *found_encoding;
427 : : const char *encoding;
428 : : PyObject *io;
429 : : PyObject *binary;
430 : 603 : PyObject *fob = NULL;
431 : 603 : PyObject *lineobj = NULL;
432 : : PyObject *res;
433 : : char buf[MAXPATHLEN+1];
434 : : int kind;
435 : : const void *data;
436 : :
437 : : /* open the file */
438 [ - + ]: 603 : if (filename == NULL)
439 : 0 : return 0;
440 : :
441 : : /* Do not attempt to open things like <string> or <stdin> */
442 : : assert(PyUnicode_Check(filename));
443 [ + + ]: 603 : if (PyUnicode_READ_CHAR(filename, 0) == '<') {
444 : 137 : Py_ssize_t len = PyUnicode_GET_LENGTH(filename);
445 [ + - + + ]: 137 : if (len > 0 && PyUnicode_READ_CHAR(filename, len - 1) == '>') {
446 : 121 : return 0;
447 : : }
448 : : }
449 : :
450 : 482 : io = PyImport_ImportModule("io");
451 [ + + ]: 482 : if (io == NULL) {
452 : 3 : return -1;
453 : : }
454 : :
455 : 479 : binary = _PyObject_CallMethod(io, &_Py_ID(open), "Os", filename, "rb");
456 [ + + ]: 479 : if (binary == NULL) {
457 : 48 : PyErr_Clear();
458 : :
459 : 48 : binary = _Py_FindSourceFile(filename, buf, sizeof(buf), io);
460 [ + - ]: 48 : if (binary == NULL) {
461 : 48 : Py_DECREF(io);
462 : 48 : return -1;
463 : : }
464 : : }
465 : :
466 : : /* use the right encoding to decode the file as unicode */
467 : 431 : fd = PyObject_AsFileDescriptor(binary);
468 [ - + ]: 431 : if (fd < 0) {
469 : 0 : Py_DECREF(io);
470 : 0 : Py_DECREF(binary);
471 : 0 : return 0;
472 : : }
473 : 431 : found_encoding = _PyTokenizer_FindEncodingFilename(fd, filename);
474 [ + + ]: 431 : if (found_encoding == NULL)
475 : 419 : PyErr_Clear();
476 [ + + ]: 431 : encoding = (found_encoding != NULL) ? found_encoding : "utf-8";
477 : : /* Reset position */
478 [ - + ]: 431 : if (lseek(fd, 0, SEEK_SET) == (off_t)-1) {
479 : 0 : Py_DECREF(io);
480 : 0 : Py_DECREF(binary);
481 : 0 : PyMem_Free(found_encoding);
482 : 0 : return 0;
483 : : }
484 : 431 : fob = _PyObject_CallMethod(io, &_Py_ID(TextIOWrapper),
485 : : "Os", binary, encoding);
486 : 431 : Py_DECREF(io);
487 : 431 : PyMem_Free(found_encoding);
488 : :
489 [ + + ]: 431 : if (fob == NULL) {
490 : 1 : PyErr_Clear();
491 : :
492 : 1 : res = PyObject_CallMethodNoArgs(binary, &_Py_ID(close));
493 : 1 : Py_DECREF(binary);
494 [ - + ]: 1 : if (res)
495 : 0 : Py_DECREF(res);
496 : : else
497 : 1 : PyErr_Clear();
498 : 1 : return 0;
499 : : }
500 : 430 : Py_DECREF(binary);
501 : :
502 : : /* get the line number lineno */
503 [ + + ]: 465289 : for (i = 0; i < lineno; i++) {
504 : 464859 : Py_XDECREF(lineobj);
505 : 464859 : lineobj = PyFile_GetLine(fob, -1);
506 [ - + ]: 464859 : if (!lineobj) {
507 : 0 : PyErr_Clear();
508 : 0 : break;
509 : : }
510 : : }
511 : 430 : res = PyObject_CallMethodNoArgs(fob, &_Py_ID(close));
512 [ + + ]: 430 : if (res) {
513 : 429 : Py_DECREF(res);
514 : : }
515 : : else {
516 : 1 : PyErr_Clear();
517 : : }
518 : 430 : Py_DECREF(fob);
519 [ + - - + ]: 430 : if (!lineobj || !PyUnicode_Check(lineobj)) {
520 : 0 : Py_XDECREF(lineobj);
521 : 0 : return -1;
522 : : }
523 : :
524 [ + + ]: 430 : if (line) {
525 : 429 : Py_INCREF(lineobj);
526 : 429 : *line = lineobj;
527 : : }
528 : :
529 : : /* remove the indentation of the line */
530 : 430 : kind = PyUnicode_KIND(lineobj);
531 : 430 : data = PyUnicode_DATA(lineobj);
532 [ + - ]: 5752 : for (i=0; i < PyUnicode_GET_LENGTH(lineobj); i++) {
533 : 5752 : Py_UCS4 ch = PyUnicode_READ(kind, data, i);
534 [ + + + - : 5752 : if (ch != ' ' && ch != '\t' && ch != '\014')
+ - ]
535 : 430 : break;
536 : : }
537 [ + + ]: 430 : if (i) {
538 : : PyObject *truncated;
539 : 411 : truncated = PyUnicode_Substring(lineobj, i, PyUnicode_GET_LENGTH(lineobj));
540 [ + - ]: 411 : if (truncated) {
541 : 411 : Py_DECREF(lineobj);
542 : 411 : lineobj = truncated;
543 : : } else {
544 : 0 : PyErr_Clear();
545 : : }
546 : : }
547 : :
548 [ + + ]: 430 : if (truncation != NULL) {
549 : 429 : *truncation = i - indent;
550 : : }
551 : :
552 [ - + ]: 430 : if (_Py_WriteIndentedMargin(margin_indent, margin, f) < 0) {
553 : 0 : goto error;
554 : : }
555 : :
556 : : /* Write some spaces before the line */
557 [ - + ]: 430 : if (_Py_WriteIndent(indent, f) < 0) {
558 : 0 : goto error;
559 : : }
560 : :
561 : : /* finally display the line */
562 [ - + ]: 430 : if (PyFile_WriteObject(lineobj, f, Py_PRINT_RAW) < 0) {
563 : 0 : goto error;
564 : : }
565 : :
566 [ - + ]: 430 : if (PyFile_WriteString("\n", f) < 0) {
567 : 0 : goto error;
568 : : }
569 : :
570 : 430 : Py_DECREF(lineobj);
571 : 430 : return 0;
572 : 0 : error:
573 : 0 : Py_DECREF(lineobj);
574 : 0 : return -1;
575 : : }
576 : :
577 : : int
578 : 5 : _Py_DisplaySourceLine(PyObject *f, PyObject *filename, int lineno, int indent,
579 : : int *truncation, PyObject **line)
580 : : {
581 : 5 : return display_source_line_with_margin(f, filename, lineno, indent, 0,
582 : : NULL, truncation, line);
583 : : }
584 : :
585 : : /* AST based Traceback Specialization
586 : : *
587 : : * When displaying a new traceback line, for certain syntactical constructs
588 : : * (e.g a subscript, an arithmetic operation) we try to create a representation
589 : : * that separates the primary source of error from the rest.
590 : : *
591 : : * Example specialization of BinOp nodes:
592 : : * Traceback (most recent call last):
593 : : * File "/home/isidentical/cpython/cpython/t.py", line 10, in <module>
594 : : * add_values(1, 2, 'x', 3, 4)
595 : : * File "/home/isidentical/cpython/cpython/t.py", line 2, in add_values
596 : : * return a + b + c + d + e
597 : : * ~~~~~~^~~
598 : : * TypeError: 'NoneType' object is not subscriptable
599 : : */
600 : :
601 : : #define IS_WHITESPACE(c) (((c) == ' ') || ((c) == '\t') || ((c) == '\f'))
602 : :
603 : : static int
604 : 304 : extract_anchors_from_expr(const char *segment_str, expr_ty expr, Py_ssize_t *left_anchor, Py_ssize_t *right_anchor,
605 : : char** primary_error_char, char** secondary_error_char)
606 : : {
607 [ + + + ]: 304 : switch (expr->kind) {
608 : 25 : case BinOp_kind: {
609 : 25 : expr_ty left = expr->v.BinOp.left;
610 : 25 : expr_ty right = expr->v.BinOp.right;
611 [ + - ]: 43 : for (int i = left->end_col_offset; i < right->col_offset; i++) {
612 [ + + + - : 43 : if (IS_WHITESPACE(segment_str[i])) {
- + ]
613 : 18 : continue;
614 : : }
615 : :
616 : 25 : *left_anchor = i;
617 : 25 : *right_anchor = i + 1;
618 : :
619 : : // Check whether if this a two-character operator (e.g //)
620 [ + + + + : 25 : if (i + 1 < right->col_offset && !IS_WHITESPACE(segment_str[i + 1])) {
+ - + - ]
621 : 7 : ++*right_anchor;
622 : : }
623 : :
624 : : // Set the error characters
625 : 25 : *primary_error_char = "~";
626 : 25 : *secondary_error_char = "^";
627 : 25 : break;
628 : : }
629 : 25 : return 1;
630 : : }
631 : 5 : case Subscript_kind: {
632 : 5 : *left_anchor = expr->v.Subscript.value->end_col_offset;
633 : 5 : *right_anchor = expr->v.Subscript.slice->end_col_offset + 1;
634 : :
635 : : // Set the error characters
636 : 5 : *primary_error_char = "~";
637 : 5 : *secondary_error_char = "^";
638 : 5 : return 1;
639 : : }
640 : 274 : default:
641 : 274 : return 0;
642 : : }
643 : : }
644 : :
645 : : static int
646 : 415 : extract_anchors_from_stmt(const char *segment_str, stmt_ty statement, Py_ssize_t *left_anchor, Py_ssize_t *right_anchor,
647 : : char** primary_error_char, char** secondary_error_char)
648 : : {
649 [ + + ]: 415 : switch (statement->kind) {
650 : 304 : case Expr_kind: {
651 : 304 : return extract_anchors_from_expr(segment_str, statement->v.Expr.value, left_anchor, right_anchor,
652 : : primary_error_char, secondary_error_char);
653 : : }
654 : 111 : default:
655 : 111 : return 0;
656 : : }
657 : : }
658 : :
659 : : static int
660 : 417 : extract_anchors_from_line(PyObject *filename, PyObject *line,
661 : : Py_ssize_t start_offset, Py_ssize_t end_offset,
662 : : Py_ssize_t *left_anchor, Py_ssize_t *right_anchor,
663 : : char** primary_error_char, char** secondary_error_char)
664 : : {
665 : 417 : int res = -1;
666 : 417 : PyArena *arena = NULL;
667 : 417 : PyObject *segment = PyUnicode_Substring(line, start_offset, end_offset);
668 [ - + ]: 417 : if (!segment) {
669 : 0 : goto done;
670 : : }
671 : :
672 : 417 : const char *segment_str = PyUnicode_AsUTF8(segment);
673 [ - + ]: 417 : if (!segment_str) {
674 : 0 : goto done;
675 : : }
676 : :
677 : 417 : arena = _PyArena_New();
678 [ - + ]: 417 : if (!arena) {
679 : 0 : goto done;
680 : : }
681 : :
682 : 417 : PyCompilerFlags flags = _PyCompilerFlags_INIT;
683 : :
684 : : _PyASTOptimizeState state;
685 : 417 : state.optimize = _Py_GetConfig()->optimization_level;
686 : 417 : state.ff_features = 0;
687 : :
688 : 417 : mod_ty module = _PyParser_ASTFromString(segment_str, filename, Py_file_input,
689 : : &flags, arena);
690 [ + + ]: 417 : if (!module) {
691 : 1 : goto done;
692 : : }
693 [ - + ]: 416 : if (!_PyAST_Optimize(module, arena, &state)) {
694 : 0 : goto done;
695 : : }
696 : :
697 : : assert(module->kind == Module_kind);
698 [ + + + - ]: 831 : if (asdl_seq_LEN(module->v.Module.body) == 1) {
699 : 415 : stmt_ty statement = asdl_seq_GET(module->v.Module.body, 0);
700 : 415 : res = extract_anchors_from_stmt(segment_str, statement, left_anchor, right_anchor,
701 : : primary_error_char, secondary_error_char);
702 : : } else {
703 : 1 : res = 0;
704 : : }
705 : :
706 : 417 : done:
707 [ + + ]: 417 : if (res > 0) {
708 : 30 : *left_anchor += start_offset;
709 : 30 : *right_anchor += start_offset;
710 : : }
711 : 417 : Py_XDECREF(segment);
712 [ + - ]: 417 : if (arena) {
713 : 417 : _PyArena_Free(arena);
714 : : }
715 : 417 : return res;
716 : : }
717 : :
718 : : #define _TRACEBACK_SOURCE_LINE_INDENT 4
719 : :
720 : : static inline int
721 : 170 : ignore_source_errors(void) {
722 [ + + ]: 170 : if (PyErr_Occurred()) {
723 [ - + ]: 1 : if (PyErr_ExceptionMatches(PyExc_KeyboardInterrupt)) {
724 : 0 : return -1;
725 : : }
726 : 1 : PyErr_Clear();
727 : : }
728 : 170 : return 0;
729 : : }
730 : :
731 : : static inline int
732 : 135 : print_error_location_carets(PyObject *f, int offset, Py_ssize_t start_offset, Py_ssize_t end_offset,
733 : : Py_ssize_t right_start_offset, Py_ssize_t left_end_offset,
734 : : const char *primary, const char *secondary) {
735 [ + + - + ]: 135 : int special_chars = (left_end_offset != -1 || right_start_offset != -1);
736 : : const char *str;
737 [ + + ]: 4551 : while (++offset <= end_offset) {
738 [ + + ]: 4416 : if (offset <= start_offset) {
739 : 1848 : str = " ";
740 [ + + + + : 2568 : } else if (special_chars && left_end_offset < offset && offset <= right_start_offset) {
+ + ]
741 : 51 : str = secondary;
742 : : } else {
743 : 2517 : str = primary;
744 : : }
745 [ - + ]: 4416 : if (PyFile_WriteString(str, f) < 0) {
746 : 0 : return -1;
747 : : }
748 : : }
749 [ - + ]: 135 : if (PyFile_WriteString("\n", f) < 0) {
750 : 0 : return -1;
751 : : }
752 : 135 : return 0;
753 : : }
754 : :
755 : : static int
756 : 598 : tb_displayline(PyTracebackObject* tb, PyObject *f, PyObject *filename, int lineno,
757 : : PyFrameObject *frame, PyObject *name, int margin_indent, const char *margin)
758 : : {
759 [ + - - + ]: 598 : if (filename == NULL || name == NULL) {
760 : 0 : return -1;
761 : : }
762 : :
763 [ - + ]: 598 : if (_Py_WriteIndentedMargin(margin_indent, margin, f) < 0) {
764 : 0 : return -1;
765 : : }
766 : :
767 : 598 : PyObject *line = PyUnicode_FromFormat(" File \"%U\", line %d, in %U\n",
768 : : filename, lineno, name);
769 [ - + ]: 598 : if (line == NULL) {
770 : 0 : return -1;
771 : : }
772 : :
773 : 598 : int res = PyFile_WriteObject(line, f, Py_PRINT_RAW);
774 : 598 : Py_DECREF(line);
775 [ - + ]: 598 : if (res < 0) {
776 : 0 : return -1;
777 : : }
778 : :
779 : 598 : int err = 0;
780 : :
781 : 598 : int truncation = _TRACEBACK_SOURCE_LINE_INDENT;
782 : 598 : PyObject* source_line = NULL;
783 : 598 : int rc = display_source_line_with_margin(
784 : : f, filename, lineno, _TRACEBACK_SOURCE_LINE_INDENT,
785 : : margin_indent, margin, &truncation, &source_line);
786 [ + + + + ]: 598 : if (rc != 0 || !source_line) {
787 : : /* ignore errors since we can't report them, can we? */
788 : 169 : err = ignore_source_errors();
789 : 169 : goto done;
790 : : }
791 : :
792 : 429 : int code_offset = tb->tb_lasti;
793 : 429 : PyCodeObject* code = frame->f_frame->f_code;
794 : 429 : const Py_ssize_t source_line_len = PyUnicode_GET_LENGTH(source_line);
795 : :
796 : : int start_line;
797 : : int end_line;
798 : : int start_col_byte_offset;
799 : : int end_col_byte_offset;
800 [ - + ]: 429 : if (!PyCode_Addr2Location(code, code_offset, &start_line, &start_col_byte_offset,
801 : : &end_line, &end_col_byte_offset)) {
802 : 0 : goto done;
803 : : }
804 : :
805 [ + - + - ]: 429 : if (start_line < 0 || end_line < 0
806 [ + + ]: 429 : || start_col_byte_offset < 0
807 [ - + ]: 428 : || end_col_byte_offset < 0)
808 : : {
809 : 1 : goto done;
810 : : }
811 : :
812 : : // When displaying errors, we will use the following generic structure:
813 : : //
814 : : // ERROR LINE ERROR LINE ERROR LINE ERROR LINE ERROR LINE ERROR LINE ERROR LINE
815 : : // ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^~~~~~~~~~~~~~~~~~~~
816 : : // | |-> left_end_offset | |-> end_offset
817 : : // |-> start_offset |-> right_start_offset
818 : : //
819 : : // In general we will only have (start_offset, end_offset) but we can gather more information
820 : : // by analyzing the AST of the text between *start_offset* and *end_offset*. If this succeeds
821 : : // we could get *left_end_offset* and *right_start_offset* and some selection of characters for
822 : : // the different ranges (primary_error_char and secondary_error_char). If we cannot obtain the
823 : : // AST information or we cannot identify special ranges within it, then left_end_offset and
824 : : // right_end_offset will be set to -1.
825 : : //
826 : : // To keep the column indicators pertinent, they are not shown when the primary character
827 : : // spans the whole line.
828 : :
829 : : // Convert the utf-8 byte offset to the actual character offset so we print the right number of carets.
830 : : assert(source_line);
831 : 428 : Py_ssize_t start_offset = _PyPegen_byte_offset_to_character_offset(source_line, start_col_byte_offset);
832 [ - + ]: 428 : if (start_offset < 0) {
833 : 0 : err = ignore_source_errors() < 0;
834 : 0 : goto done;
835 : : }
836 : :
837 : 428 : Py_ssize_t end_offset = _PyPegen_byte_offset_to_character_offset(source_line, end_col_byte_offset);
838 [ - + ]: 428 : if (end_offset < 0) {
839 : 0 : err = ignore_source_errors() < 0;
840 : 0 : goto done;
841 : : }
842 : :
843 : 428 : Py_ssize_t left_end_offset = -1;
844 : 428 : Py_ssize_t right_start_offset = -1;
845 : :
846 : 428 : char *primary_error_char = "^";
847 : 428 : char *secondary_error_char = primary_error_char;
848 : :
849 [ + + ]: 428 : if (start_line == end_line) {
850 : 417 : int res = extract_anchors_from_line(filename, source_line, start_offset, end_offset,
851 : : &left_end_offset, &right_start_offset,
852 : : &primary_error_char, &secondary_error_char);
853 [ + + - + ]: 417 : if (res < 0 && ignore_source_errors() < 0) {
854 : 0 : goto done;
855 : : }
856 : : }
857 : : else {
858 : : // If this is a multi-line expression, then we will highlight until
859 : : // the last non-whitespace character.
860 : 11 : const char *source_line_str = PyUnicode_AsUTF8(source_line);
861 [ - + ]: 11 : if (!source_line_str) {
862 : 0 : goto done;
863 : : }
864 : :
865 : 11 : Py_ssize_t i = source_line_len;
866 [ + - ]: 11 : while (--i >= 0) {
867 [ + - + - : 11 : if (!IS_WHITESPACE(source_line_str[i])) {
+ - ]
868 : 11 : break;
869 : : }
870 : : }
871 : :
872 : 11 : end_offset = i + 1;
873 : : }
874 : :
875 : : // Elide indicators if primary char spans the frame line
876 : 428 : Py_ssize_t stripped_line_len = source_line_len - truncation - _TRACEBACK_SOURCE_LINE_INDENT;
877 [ + + - + ]: 428 : bool has_secondary_ranges = (left_end_offset != -1 || right_start_offset != -1);
878 [ + + + + ]: 428 : if (end_offset - start_offset == stripped_line_len && !has_secondary_ranges) {
879 : 293 : goto done;
880 : : }
881 : :
882 [ - + ]: 135 : if (_Py_WriteIndentedMargin(margin_indent, margin, f) < 0) {
883 : 0 : err = -1;
884 : 0 : goto done;
885 : : }
886 : :
887 [ + - ]: 135 : if (print_error_location_carets(f, truncation, start_offset, end_offset,
888 : : right_start_offset, left_end_offset,
889 : : primary_error_char, secondary_error_char) < 0) {
890 : 0 : err = -1;
891 : 0 : goto done;
892 : : }
893 : :
894 : 135 : done:
895 : 598 : Py_XDECREF(source_line);
896 : 598 : return err;
897 : : }
898 : :
899 : : static const int TB_RECURSIVE_CUTOFF = 3; // Also hardcoded in traceback.py.
900 : :
901 : : static int
902 : 6 : tb_print_line_repeated(PyObject *f, long cnt)
903 : : {
904 : 6 : cnt -= TB_RECURSIVE_CUTOFF;
905 [ + + ]: 6 : PyObject *line = PyUnicode_FromFormat(
906 : : (cnt > 1)
907 : : ? " [Previous line repeated %ld more times]\n"
908 : : : " [Previous line repeated %ld more time]\n",
909 : : cnt);
910 [ - + ]: 6 : if (line == NULL) {
911 : 0 : return -1;
912 : : }
913 : 6 : int err = PyFile_WriteObject(line, f, Py_PRINT_RAW);
914 : 6 : Py_DECREF(line);
915 : 6 : return err;
916 : : }
917 : :
918 : : static int
919 : 271 : tb_printinternal(PyTracebackObject *tb, PyObject *f, long limit,
920 : : int indent, const char *margin)
921 : : {
922 : 271 : PyCodeObject *code = NULL;
923 : 271 : Py_ssize_t depth = 0;
924 : 271 : PyObject *last_file = NULL;
925 : 271 : int last_line = -1;
926 : 271 : PyObject *last_name = NULL;
927 : 271 : long cnt = 0;
928 : 271 : PyTracebackObject *tb1 = tb;
929 [ + + ]: 2843 : while (tb1 != NULL) {
930 : 2572 : depth++;
931 : 2572 : tb1 = tb1->tb_next;
932 : : }
933 [ + - + + ]: 274 : while (tb != NULL && depth > limit) {
934 : 3 : depth--;
935 : 3 : tb = tb->tb_next;
936 : : }
937 [ + + ]: 2840 : while (tb != NULL) {
938 : 2569 : code = PyFrame_GetCode(tb->tb_frame);
939 [ + + ]: 2569 : if (last_file == NULL ||
940 [ + + + - ]: 2298 : code->co_filename != last_file ||
941 [ + + + - ]: 2216 : last_line == -1 || tb->tb_lineno != last_line ||
942 [ - + ]: 1988 : last_name == NULL || code->co_name != last_name) {
943 [ + + ]: 581 : if (cnt > TB_RECURSIVE_CUTOFF) {
944 [ - + ]: 5 : if (tb_print_line_repeated(f, cnt) < 0) {
945 : 0 : goto error;
946 : : }
947 : : }
948 : 581 : last_file = code->co_filename;
949 : 581 : last_line = tb->tb_lineno;
950 : 581 : last_name = code->co_name;
951 : 581 : cnt = 0;
952 : : }
953 : 2569 : cnt++;
954 [ + + ]: 2569 : if (cnt <= TB_RECURSIVE_CUTOFF) {
955 [ - + ]: 598 : if (tb_displayline(tb, f, code->co_filename, tb->tb_lineno,
956 : : tb->tb_frame, code->co_name, indent, margin) < 0) {
957 : 0 : goto error;
958 : : }
959 : :
960 [ - + ]: 598 : if (PyErr_CheckSignals() < 0) {
961 : 0 : goto error;
962 : : }
963 : : }
964 [ + - ]: 2569 : Py_CLEAR(code);
965 : 2569 : tb = tb->tb_next;
966 : : }
967 [ + + ]: 271 : if (cnt > TB_RECURSIVE_CUTOFF) {
968 [ - + ]: 1 : if (tb_print_line_repeated(f, cnt) < 0) {
969 : 0 : goto error;
970 : : }
971 : : }
972 : 271 : return 0;
973 : 0 : error:
974 : 0 : Py_XDECREF(code);
975 : 0 : return -1;
976 : : }
977 : :
978 : : #define PyTraceBack_LIMIT 1000
979 : :
980 : : int
981 : 274 : _PyTraceBack_Print_Indented(PyObject *v, int indent, const char *margin,
982 : : const char *header_margin, const char *header, PyObject *f)
983 : : {
984 : : PyObject *limitv;
985 : 274 : long limit = PyTraceBack_LIMIT;
986 : :
987 [ - + ]: 274 : if (v == NULL) {
988 : 0 : return 0;
989 : : }
990 [ - + ]: 274 : if (!PyTraceBack_Check(v)) {
991 : 0 : PyErr_BadInternalCall();
992 : 0 : return -1;
993 : : }
994 : 274 : limitv = PySys_GetObject("tracebacklimit");
995 [ + + + + ]: 274 : if (limitv && PyLong_Check(limitv)) {
996 : : int overflow;
997 : 8 : limit = PyLong_AsLongAndOverflow(limitv, &overflow);
998 [ + + ]: 8 : if (overflow > 0) {
999 : 1 : limit = LONG_MAX;
1000 : : }
1001 [ + + ]: 7 : else if (limit <= 0) {
1002 : 3 : return 0;
1003 : : }
1004 : : }
1005 [ - + ]: 271 : if (_Py_WriteIndentedMargin(indent, header_margin, f) < 0) {
1006 : 0 : return -1;
1007 : : }
1008 : :
1009 [ - + ]: 271 : if (PyFile_WriteString(header, f) < 0) {
1010 : 0 : return -1;
1011 : : }
1012 : :
1013 [ - + ]: 271 : if (tb_printinternal((PyTracebackObject *)v, f, limit, indent, margin) < 0) {
1014 : 0 : return -1;
1015 : : }
1016 : :
1017 : 271 : return 0;
1018 : : }
1019 : :
1020 : : int
1021 : 33 : PyTraceBack_Print(PyObject *v, PyObject *f)
1022 : : {
1023 : 33 : int indent = 0;
1024 : 33 : const char *margin = NULL;
1025 : 33 : const char *header_margin = NULL;
1026 : 33 : const char *header = EXCEPTION_TB_HEADER;
1027 : :
1028 : 33 : return _PyTraceBack_Print_Indented(v, indent, margin, header_margin, header, f);
1029 : : }
1030 : :
1031 : : /* Format an integer in range [0; 0xffffffff] to decimal and write it
1032 : : into the file fd.
1033 : :
1034 : : This function is signal safe. */
1035 : :
1036 : : void
1037 : 53 : _Py_DumpDecimal(int fd, size_t value)
1038 : : {
1039 : : /* maximum number of characters required for output of %lld or %p.
1040 : : We need at most ceil(log10(256)*SIZEOF_LONG_LONG) digits,
1041 : : plus 1 for the null byte. 53/22 is an upper bound for log10(256). */
1042 : : char buffer[1 + (sizeof(size_t)*53-1) / 22 + 1];
1043 : : char *ptr, *end;
1044 : :
1045 : 53 : end = &buffer[Py_ARRAY_LENGTH(buffer) - 1];
1046 : 53 : ptr = end;
1047 : 53 : *ptr = '\0';
1048 : : do {
1049 : 112 : --ptr;
1050 : : assert(ptr >= buffer);
1051 : 112 : *ptr = '0' + (value % 10);
1052 : 112 : value /= 10;
1053 [ + + ]: 112 : } while (value);
1054 : :
1055 : 53 : _Py_write_noraise(fd, ptr, end - ptr);
1056 : 53 : }
1057 : :
1058 : : /* Format an integer as hexadecimal with width digits into fd file descriptor.
1059 : : The function is signal safe. */
1060 : : void
1061 : 16 : _Py_DumpHexadecimal(int fd, uintptr_t value, Py_ssize_t width)
1062 : : {
1063 : : char buffer[sizeof(uintptr_t) * 2 + 1], *ptr, *end;
1064 : 16 : const Py_ssize_t size = Py_ARRAY_LENGTH(buffer) - 1;
1065 : :
1066 [ - + ]: 16 : if (width > size)
1067 : 0 : width = size;
1068 : : /* it's ok if width is negative */
1069 : :
1070 : 16 : end = &buffer[size];
1071 : 16 : ptr = end;
1072 : 16 : *ptr = '\0';
1073 : : do {
1074 : 256 : --ptr;
1075 : : assert(ptr >= buffer);
1076 : 256 : *ptr = Py_hexdigits[value & 15];
1077 : 256 : value >>= 4;
1078 [ + + - + ]: 256 : } while ((end - ptr) < width || value);
1079 : :
1080 : 16 : _Py_write_noraise(fd, ptr, end - ptr);
1081 : 16 : }
1082 : :
1083 : : void
1084 : 106 : _Py_DumpASCII(int fd, PyObject *text)
1085 : : {
1086 : 106 : PyASCIIObject *ascii = _PyASCIIObject_CAST(text);
1087 : : Py_ssize_t i, size;
1088 : : int truncated;
1089 : : int kind;
1090 : 106 : void *data = NULL;
1091 : : Py_UCS4 ch;
1092 : :
1093 [ - + ]: 106 : if (!PyUnicode_Check(text))
1094 : 0 : return;
1095 : :
1096 : 106 : size = ascii->length;
1097 : 106 : kind = ascii->state.kind;
1098 [ + - ]: 106 : if (ascii->state.compact) {
1099 [ + - ]: 106 : if (ascii->state.ascii)
1100 : 106 : data = ascii + 1;
1101 : : else
1102 : 0 : data = _PyCompactUnicodeObject_CAST(text) + 1;
1103 : : }
1104 : : else {
1105 : 0 : data = _PyUnicodeObject_CAST(text)->data.any;
1106 [ # # ]: 0 : if (data == NULL)
1107 : 0 : return;
1108 : : }
1109 : :
1110 [ + + ]: 106 : if (MAX_STRING_LENGTH < size) {
1111 : 1 : size = MAX_STRING_LENGTH;
1112 : 1 : truncated = 1;
1113 : : }
1114 : : else {
1115 : 105 : truncated = 0;
1116 : : }
1117 : :
1118 : : // Is an ASCII string?
1119 [ + - ]: 106 : if (ascii->state.ascii) {
1120 : : assert(kind == PyUnicode_1BYTE_KIND);
1121 : 106 : char *str = data;
1122 : :
1123 : 106 : int need_escape = 0;
1124 [ + + ]: 1678 : for (i=0; i < size; i++) {
1125 : 1572 : ch = str[i];
1126 [ + - - + ]: 1572 : if (!(' ' <= ch && ch <= 126)) {
1127 : 0 : need_escape = 1;
1128 : 0 : break;
1129 : : }
1130 : : }
1131 [ + - ]: 106 : if (!need_escape) {
1132 : : // The string can be written with a single write() syscall
1133 : 106 : _Py_write_noraise(fd, str, size);
1134 : 106 : goto done;
1135 : : }
1136 : : }
1137 : :
1138 [ # # ]: 0 : for (i=0; i < size; i++) {
1139 : 0 : ch = PyUnicode_READ(kind, data, i);
1140 [ # # # # ]: 0 : if (' ' <= ch && ch <= 126) {
1141 : : /* printable ASCII character */
1142 : 0 : char c = (char)ch;
1143 : 0 : _Py_write_noraise(fd, &c, 1);
1144 : : }
1145 [ # # ]: 0 : else if (ch <= 0xff) {
1146 : 0 : PUTS(fd, "\\x");
1147 : 0 : _Py_DumpHexadecimal(fd, ch, 2);
1148 : : }
1149 [ # # ]: 0 : else if (ch <= 0xffff) {
1150 : 0 : PUTS(fd, "\\u");
1151 : 0 : _Py_DumpHexadecimal(fd, ch, 4);
1152 : : }
1153 : : else {
1154 : 0 : PUTS(fd, "\\U");
1155 : 0 : _Py_DumpHexadecimal(fd, ch, 8);
1156 : : }
1157 : : }
1158 : :
1159 : 0 : done:
1160 [ + + ]: 106 : if (truncated) {
1161 : 1 : PUTS(fd, "...");
1162 : : }
1163 : : }
1164 : :
1165 : : /* Write a frame into the file fd: "File "xxx", line xxx in xxx".
1166 : :
1167 : : This function is signal safe. */
1168 : :
1169 : : static void
1170 : 53 : dump_frame(int fd, _PyInterpreterFrame *frame)
1171 : : {
1172 : 53 : PyCodeObject *code = frame->f_code;
1173 : 53 : PUTS(fd, " File ");
1174 [ + - ]: 53 : if (code->co_filename != NULL
1175 [ + - ]: 53 : && PyUnicode_Check(code->co_filename))
1176 : : {
1177 : 53 : PUTS(fd, "\"");
1178 : 53 : _Py_DumpASCII(fd, code->co_filename);
1179 : 53 : PUTS(fd, "\"");
1180 : : } else {
1181 : 0 : PUTS(fd, "???");
1182 : : }
1183 : :
1184 : 53 : int lineno = _PyInterpreterFrame_GetLine(frame);
1185 : 53 : PUTS(fd, ", line ");
1186 [ + - ]: 53 : if (lineno >= 0) {
1187 : 53 : _Py_DumpDecimal(fd, (size_t)lineno);
1188 : : }
1189 : : else {
1190 : 0 : PUTS(fd, "???");
1191 : : }
1192 : 53 : PUTS(fd, " in ");
1193 : :
1194 [ + - ]: 53 : if (code->co_name != NULL
1195 [ + - ]: 53 : && PyUnicode_Check(code->co_name)) {
1196 : 53 : _Py_DumpASCII(fd, code->co_name);
1197 : : }
1198 : : else {
1199 : 0 : PUTS(fd, "???");
1200 : : }
1201 : :
1202 : 53 : PUTS(fd, "\n");
1203 : 53 : }
1204 : :
1205 : : static void
1206 : 24 : dump_traceback(int fd, PyThreadState *tstate, int write_header)
1207 : : {
1208 : : _PyInterpreterFrame *frame;
1209 : : unsigned int depth;
1210 : :
1211 [ + + ]: 24 : if (write_header) {
1212 : 8 : PUTS(fd, "Stack (most recent call first):\n");
1213 : : }
1214 : :
1215 : 24 : frame = tstate->cframe->current_frame;
1216 [ + + ]: 24 : if (frame == NULL) {
1217 : 2 : PUTS(fd, " <no Python frame>\n");
1218 : 2 : return;
1219 : : }
1220 : :
1221 : 22 : depth = 0;
1222 : : while (1) {
1223 [ - + ]: 53 : if (MAX_FRAME_DEPTH <= depth) {
1224 : 0 : PUTS(fd, " ...\n");
1225 : 0 : break;
1226 : : }
1227 : 53 : dump_frame(fd, frame);
1228 : 53 : frame = frame->previous;
1229 [ + + ]: 53 : if (frame == NULL) {
1230 : 22 : break;
1231 : : }
1232 : 31 : depth++;
1233 : : }
1234 : : }
1235 : :
1236 : : /* Dump the traceback of a Python thread into fd. Use write() to write the
1237 : : traceback and retry if write() is interrupted by a signal (failed with
1238 : : EINTR), but don't call the Python signal handler.
1239 : :
1240 : : The caller is responsible to call PyErr_CheckSignals() to call Python signal
1241 : : handlers if signals were received. */
1242 : : void
1243 : 8 : _Py_DumpTraceback(int fd, PyThreadState *tstate)
1244 : : {
1245 : 8 : dump_traceback(fd, tstate, 1);
1246 : 8 : }
1247 : :
1248 : : /* Write the thread identifier into the file 'fd': "Current thread 0xHHHH:\" if
1249 : : is_current is true, "Thread 0xHHHH:\n" otherwise.
1250 : :
1251 : : This function is signal safe. */
1252 : :
1253 : : static void
1254 : 16 : write_thread_id(int fd, PyThreadState *tstate, int is_current)
1255 : : {
1256 [ + + ]: 16 : if (is_current)
1257 : 5 : PUTS(fd, "Current thread 0x");
1258 : : else
1259 : 11 : PUTS(fd, "Thread 0x");
1260 : 16 : _Py_DumpHexadecimal(fd,
1261 : : tstate->thread_id,
1262 : : sizeof(unsigned long) * 2);
1263 : 16 : PUTS(fd, " (most recent call first):\n");
1264 : 16 : }
1265 : :
1266 : : /* Dump the traceback of all Python threads into fd. Use write() to write the
1267 : : traceback and retry if write() is interrupted by a signal (failed with
1268 : : EINTR), but don't call the Python signal handler.
1269 : :
1270 : : The caller is responsible to call PyErr_CheckSignals() to call Python signal
1271 : : handlers if signals were received. */
1272 : : const char*
1273 : 18 : _Py_DumpTracebackThreads(int fd, PyInterpreterState *interp,
1274 : : PyThreadState *current_tstate)
1275 : : {
1276 : : PyThreadState *tstate;
1277 : : unsigned int nthreads;
1278 : :
1279 [ + + ]: 18 : if (current_tstate == NULL) {
1280 : : /* _Py_DumpTracebackThreads() is called from signal handlers by
1281 : : faulthandler.
1282 : :
1283 : : SIGSEGV, SIGFPE, SIGABRT, SIGBUS and SIGILL are synchronous signals
1284 : : and are thus delivered to the thread that caused the fault. Get the
1285 : : Python thread state of the current thread.
1286 : :
1287 : : PyThreadState_Get() doesn't give the state of the thread that caused
1288 : : the fault if the thread released the GIL, and so
1289 : : _PyThreadState_GET() cannot be used. Read the thread specific
1290 : : storage (TSS) instead: call PyGILState_GetThisThreadState(). */
1291 : 13 : current_tstate = PyGILState_GetThisThreadState();
1292 : : }
1293 : :
1294 [ + + ]: 18 : if (interp == NULL) {
1295 [ + + ]: 7 : if (current_tstate == NULL) {
1296 : 4 : interp = _PyGILState_GetInterpreterStateUnsafe();
1297 [ + - ]: 4 : if (interp == NULL) {
1298 : : /* We need the interpreter state to get Python threads */
1299 : 4 : return "unable to get the interpreter state";
1300 : : }
1301 : : }
1302 : : else {
1303 : 3 : interp = current_tstate->interp;
1304 : : }
1305 : : }
1306 : : assert(interp != NULL);
1307 : :
1308 : : /* Get the current interpreter from the current thread */
1309 : 14 : tstate = PyInterpreterState_ThreadHead(interp);
1310 [ - + ]: 14 : if (tstate == NULL)
1311 : 0 : return "unable to get the thread head state";
1312 : :
1313 : : /* Dump the traceback of each thread */
1314 : 14 : tstate = PyInterpreterState_ThreadHead(interp);
1315 : 14 : nthreads = 0;
1316 : : _Py_BEGIN_SUPPRESS_IPH
1317 : : do
1318 : : {
1319 [ + + ]: 16 : if (nthreads != 0)
1320 : 2 : PUTS(fd, "\n");
1321 [ - + ]: 16 : if (nthreads >= MAX_NTHREADS) {
1322 : 0 : PUTS(fd, "...\n");
1323 : 0 : break;
1324 : : }
1325 : 16 : write_thread_id(fd, tstate, tstate == current_tstate);
1326 [ + + - + ]: 16 : if (tstate == current_tstate && tstate->interp->gc.collecting) {
1327 : 0 : PUTS(fd, " Garbage-collecting\n");
1328 : : }
1329 : 16 : dump_traceback(fd, tstate, 0);
1330 : 16 : tstate = PyThreadState_Next(tstate);
1331 : 16 : nthreads++;
1332 [ + + ]: 16 : } while (tstate != NULL);
1333 : : _Py_END_SUPPRESS_IPH
1334 : :
1335 : 14 : return NULL;
1336 : : }
1337 : :
|