Coverage Report

Created: 2022-07-08 09:39

/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