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