Coverage Report

Created: 2025-11-30 06:38

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/cpython/Python/tracemalloc.c
Line
Count
Source
1
#include "Python.h"
2
#include "pycore_fileutils.h"     // _Py_write_noraise()
3
#include "pycore_gc.h"            // PyGC_Head
4
#include "pycore_hashtable.h"     // _Py_hashtable_t
5
#include "pycore_initconfig.h"    // _PyStatus_NO_MEMORY()
6
#include "pycore_interpframe.h"   // _PyInterpreterFrame
7
#include "pycore_lock.h"          // PyMutex_LockFlags()
8
#include "pycore_object.h"        // _PyType_PreHeaderSize()
9
#include "pycore_pymem.h"         // _Py_tracemalloc_config
10
#include "pycore_runtime.h"       // _Py_ID()
11
#include "pycore_traceback.h"     // _Py_DumpASCII()
12
13
#include <stdlib.h>               // malloc()
14
15
28
#define tracemalloc_config _PyRuntime.tracemalloc.config
16
17
_Py_DECLARE_STR(anon_unknown, "<unknown>");
18
19
/* Forward declaration */
20
static void* raw_malloc(size_t size);
21
static void raw_free(void *ptr);
22
static int _PyTraceMalloc_TraceRef(PyObject *op, PyRefTracerEvent event,
23
                                   void* Py_UNUSED(ignore));
24
25
#ifdef Py_DEBUG
26
#  define TRACE_DEBUG
27
#endif
28
29
0
#define TO_PTR(key) ((const void *)(uintptr_t)(key))
30
0
#define FROM_PTR(key) ((uintptr_t)(key))
31
32
84
#define allocators _PyRuntime.tracemalloc.allocators
33
34
35
/* This lock is needed because tracemalloc_free() is called without
36
   the GIL held from PyMem_RawFree(). It cannot acquire the lock because it
37
   would introduce a deadlock in _PyThreadState_DeleteCurrent(). */
38
0
#define tables_lock _PyRuntime.tracemalloc.tables_lock
39
0
#define TABLES_LOCK() PyMutex_Lock(&tables_lock)
40
0
#define TABLES_UNLOCK() PyMutex_Unlock(&tables_lock)
41
42
43
0
#define DEFAULT_DOMAIN 0
44
45
typedef struct tracemalloc_frame frame_t;
46
typedef struct tracemalloc_traceback traceback_t;
47
48
#define TRACEBACK_SIZE(NFRAME) \
49
28
        (sizeof(traceback_t) + sizeof(frame_t) * (NFRAME))
50
51
static const int MAX_NFRAME = UINT16_MAX;
52
53
54
224
#define tracemalloc_empty_traceback _PyRuntime.tracemalloc.empty_traceback
55
56
57
/* Trace of a memory block */
58
typedef struct {
59
    /* Size of the memory block in bytes */
60
    size_t size;
61
62
    /* Traceback where the memory block was allocated */
63
    traceback_t *traceback;
64
} trace_t;
65
66
67
0
#define tracemalloc_traced_memory _PyRuntime.tracemalloc.traced_memory
68
0
#define tracemalloc_peak_traced_memory _PyRuntime.tracemalloc.peak_traced_memory
69
56
#define tracemalloc_filenames _PyRuntime.tracemalloc.filenames
70
0
#define tracemalloc_traceback _PyRuntime.tracemalloc.traceback
71
56
#define tracemalloc_tracebacks _PyRuntime.tracemalloc.tracebacks
72
56
#define tracemalloc_traces _PyRuntime.tracemalloc.traces
73
56
#define tracemalloc_domains _PyRuntime.tracemalloc.domains
74
75
76
#ifdef TRACE_DEBUG
77
static void
78
tracemalloc_error(const char *format, ...)
79
{
80
    va_list ap;
81
    fprintf(stderr, "tracemalloc: ");
82
    va_start(ap, format);
83
    vfprintf(stderr, format, ap);
84
    va_end(ap);
85
    fprintf(stderr, "\n");
86
    fflush(stderr);
87
}
88
#endif
89
90
91
28
#define tracemalloc_reentrant_key _PyRuntime.tracemalloc.reentrant_key
92
93
/* Any non-NULL pointer can be used */
94
0
#define REENTRANT Py_True
95
96
static int
97
get_reentrant(void)
98
0
{
99
0
    assert(PyThread_tss_is_created(&tracemalloc_reentrant_key));
100
101
0
    void *ptr = PyThread_tss_get(&tracemalloc_reentrant_key);
102
0
    if (ptr != NULL) {
103
0
        assert(ptr == REENTRANT);
104
0
        return 1;
105
0
    }
106
0
    else {
107
0
        return 0;
108
0
    }
109
0
}
110
111
static void
112
set_reentrant(int reentrant)
113
0
{
114
0
    assert(reentrant == 0 || reentrant == 1);
115
0
    assert(PyThread_tss_is_created(&tracemalloc_reentrant_key));
116
117
0
    if (reentrant) {
118
0
        assert(!get_reentrant());
119
0
        PyThread_tss_set(&tracemalloc_reentrant_key, REENTRANT);
120
0
    }
121
0
    else {
122
0
        assert(get_reentrant());
123
0
        PyThread_tss_set(&tracemalloc_reentrant_key, NULL);
124
0
    }
125
0
}
126
127
128
static Py_uhash_t
129
hashtable_hash_pyobject(const void *key)
130
0
{
131
0
    PyObject *obj = (PyObject *)key;
132
0
    return PyObject_Hash(obj);
133
0
}
134
135
136
static int
137
hashtable_compare_unicode(const void *key1, const void *key2)
138
0
{
139
0
    PyObject *obj1 = (PyObject *)key1;
140
0
    PyObject *obj2 = (PyObject *)key2;
141
0
    if (obj1 != NULL && obj2 != NULL) {
142
0
        return (PyUnicode_Compare(obj1, obj2) == 0);
143
0
    }
144
0
    else {
145
0
        return obj1 == obj2;
146
0
    }
147
0
}
148
149
150
static Py_uhash_t
151
hashtable_hash_uint(const void *key_raw)
152
0
{
153
0
    unsigned int key = (unsigned int)FROM_PTR(key_raw);
154
0
    return (Py_uhash_t)key;
155
0
}
156
157
158
static _Py_hashtable_t *
159
hashtable_new(_Py_hashtable_hash_func hash_func,
160
              _Py_hashtable_compare_func compare_func,
161
              _Py_hashtable_destroy_func key_destroy_func,
162
              _Py_hashtable_destroy_func value_destroy_func)
163
112
{
164
112
    _Py_hashtable_allocator_t hashtable_alloc = {malloc, free};
165
112
    return _Py_hashtable_new_full(hash_func, compare_func,
166
112
                                  key_destroy_func, value_destroy_func,
167
112
                                  &hashtable_alloc);
168
112
}
169
170
171
static void*
172
raw_malloc(size_t size)
173
28
{
174
28
    return allocators.raw.malloc(allocators.raw.ctx, size);
175
28
}
176
177
static void
178
raw_free(void *ptr)
179
0
{
180
0
    allocators.raw.free(allocators.raw.ctx, ptr);
181
0
}
182
183
184
static Py_uhash_t
185
hashtable_hash_traceback(const void *key)
186
0
{
187
0
    const traceback_t *traceback = (const traceback_t *)key;
188
0
    return traceback->hash;
189
0
}
190
191
192
static int
193
hashtable_compare_traceback(const void *key1, const void *key2)
194
0
{
195
0
    const traceback_t *traceback1 = (const traceback_t *)key1;
196
0
    const traceback_t *traceback2 = (const traceback_t *)key2;
197
198
0
    if (traceback1->nframe != traceback2->nframe) {
199
0
        return 0;
200
0
    }
201
0
    if (traceback1->total_nframe != traceback2->total_nframe) {
202
0
        return 0;
203
0
    }
204
205
0
    for (int i=0; i < traceback1->nframe; i++) {
206
0
        const frame_t *frame1 = &traceback1->frames[i];
207
0
        const frame_t *frame2 = &traceback2->frames[i];
208
209
0
        if (frame1->lineno != frame2->lineno) {
210
0
            return 0;
211
0
        }
212
0
        if (frame1->filename != frame2->filename) {
213
0
            assert(PyUnicode_Compare(frame1->filename, frame2->filename) != 0);
214
0
            return 0;
215
0
        }
216
0
    }
217
0
    return 1;
218
0
}
219
220
221
static void
222
tracemalloc_get_frame(_PyInterpreterFrame *pyframe, frame_t *frame)
223
0
{
224
0
    assert(PyStackRef_CodeCheck(pyframe->f_executable));
225
0
    frame->filename = &_Py_STR(anon_unknown);
226
227
0
    int lineno = PyUnstable_InterpreterFrame_GetLine(pyframe);
228
0
    if (lineno < 0) {
229
0
        lineno = 0;
230
0
    }
231
0
    frame->lineno = (unsigned int)lineno;
232
233
0
    PyObject *filename = _PyFrame_GetCode(pyframe)->co_filename;
234
0
    if (filename == NULL) {
235
#ifdef TRACE_DEBUG
236
        tracemalloc_error("failed to get the filename of the code object");
237
#endif
238
0
        return;
239
0
    }
240
241
0
    if (!PyUnicode_Check(filename)) {
242
#ifdef TRACE_DEBUG
243
        tracemalloc_error("filename is not a unicode string");
244
#endif
245
0
        return;
246
0
    }
247
248
    /* intern the filename */
249
0
    _Py_hashtable_entry_t *entry;
250
0
    entry = _Py_hashtable_get_entry(tracemalloc_filenames, filename);
251
0
    if (entry != NULL) {
252
0
        filename = (PyObject *)entry->key;
253
0
    }
254
0
    else {
255
        /* tracemalloc_filenames is responsible to keep a reference
256
           to the filename */
257
0
        if (_Py_hashtable_set(tracemalloc_filenames, Py_NewRef(filename),
258
0
                              NULL) < 0) {
259
0
            Py_DECREF(filename);
260
#ifdef TRACE_DEBUG
261
            tracemalloc_error("failed to intern the filename");
262
#endif
263
0
            return;
264
0
        }
265
0
    }
266
267
    /* the tracemalloc_filenames table keeps a reference to the filename */
268
0
    frame->filename = filename;
269
0
}
270
271
272
static Py_uhash_t
273
traceback_hash(traceback_t *traceback)
274
28
{
275
    /* code based on tuple_hash() of Objects/tupleobject.c */
276
28
    Py_uhash_t x, y;  /* Unsigned for defined overflow behavior. */
277
28
    int len = traceback->nframe;
278
28
    Py_uhash_t mult = PyHASH_MULTIPLIER;
279
28
    frame_t *frame;
280
281
28
    x = 0x345678UL;
282
28
    frame = traceback->frames;
283
56
    while (--len >= 0) {
284
28
        y = (Py_uhash_t)PyObject_Hash(frame->filename);
285
28
        y ^= (Py_uhash_t)frame->lineno;
286
28
        frame++;
287
288
28
        x = (x ^ y) * mult;
289
        /* the cast might truncate len; that doesn't change hash stability */
290
28
        mult += (Py_uhash_t)(82520UL + len + len);
291
28
    }
292
28
    x ^= traceback->total_nframe;
293
28
    x += 97531UL;
294
28
    return x;
295
28
}
296
297
298
static void
299
traceback_get_frames(traceback_t *traceback)
300
0
{
301
0
    PyThreadState *tstate = _PyThreadState_GET();
302
0
    assert(tstate != NULL);
303
304
0
    _PyInterpreterFrame *pyframe = _PyThreadState_GetFrame(tstate);
305
0
    while (pyframe) {
306
0
        if (traceback->nframe < tracemalloc_config.max_nframe) {
307
0
            tracemalloc_get_frame(pyframe, &traceback->frames[traceback->nframe]);
308
0
            assert(traceback->frames[traceback->nframe].filename != NULL);
309
0
            traceback->nframe++;
310
0
        }
311
0
        if (traceback->total_nframe < UINT16_MAX) {
312
0
            traceback->total_nframe++;
313
0
        }
314
0
        pyframe = _PyFrame_GetFirstComplete(pyframe->previous);
315
0
    }
316
0
}
317
318
319
static traceback_t *
320
traceback_new(void)
321
0
{
322
0
    traceback_t *traceback;
323
0
    _Py_hashtable_entry_t *entry;
324
325
0
    _Py_AssertHoldsTstate();
326
327
    /* get frames */
328
0
    traceback = tracemalloc_traceback;
329
0
    traceback->nframe = 0;
330
0
    traceback->total_nframe = 0;
331
0
    traceback_get_frames(traceback);
332
0
    if (traceback->nframe == 0) {
333
0
        return tracemalloc_empty_traceback;
334
0
    }
335
0
    traceback->hash = traceback_hash(traceback);
336
337
    /* intern the traceback */
338
0
    entry = _Py_hashtable_get_entry(tracemalloc_tracebacks, traceback);
339
0
    if (entry != NULL) {
340
0
        traceback = (traceback_t *)entry->key;
341
0
    }
342
0
    else {
343
0
        traceback_t *copy;
344
0
        size_t traceback_size;
345
346
0
        traceback_size = TRACEBACK_SIZE(traceback->nframe);
347
348
0
        copy = raw_malloc(traceback_size);
349
0
        if (copy == NULL) {
350
#ifdef TRACE_DEBUG
351
            tracemalloc_error("failed to intern the traceback: malloc failed");
352
#endif
353
0
            return NULL;
354
0
        }
355
0
        memcpy(copy, traceback, traceback_size);
356
357
0
        if (_Py_hashtable_set(tracemalloc_tracebacks, copy, NULL) < 0) {
358
0
            raw_free(copy);
359
#ifdef TRACE_DEBUG
360
            tracemalloc_error("failed to intern the traceback: putdata failed");
361
#endif
362
0
            return NULL;
363
0
        }
364
0
        traceback = copy;
365
0
    }
366
0
    return traceback;
367
0
}
368
369
370
static _Py_hashtable_t*
371
tracemalloc_create_traces_table(void)
372
28
{
373
28
    return hashtable_new(_Py_hashtable_hash_ptr,
374
28
                         _Py_hashtable_compare_direct,
375
28
                         NULL, raw_free);
376
28
}
377
378
379
static void
380
tracemalloc_destroy_domain(void *value)
381
0
{
382
0
    _Py_hashtable_t *ht = (_Py_hashtable_t*)value;
383
0
    _Py_hashtable_destroy(ht);
384
0
}
385
386
387
static _Py_hashtable_t*
388
tracemalloc_create_domains_table(void)
389
28
{
390
28
    return hashtable_new(hashtable_hash_uint,
391
28
                         _Py_hashtable_compare_direct,
392
28
                         NULL,
393
28
                         tracemalloc_destroy_domain);
394
28
}
395
396
397
static _Py_hashtable_t*
398
tracemalloc_get_traces_table(unsigned int domain)
399
0
{
400
0
    if (domain == DEFAULT_DOMAIN) {
401
0
        return tracemalloc_traces;
402
0
    }
403
0
    else {
404
0
        return _Py_hashtable_get(tracemalloc_domains, TO_PTR(domain));
405
0
    }
406
0
}
407
408
409
static void
410
tracemalloc_remove_trace_unlocked(unsigned int domain, uintptr_t ptr)
411
0
{
412
0
    assert(tracemalloc_config.tracing);
413
414
0
    _Py_hashtable_t *traces = tracemalloc_get_traces_table(domain);
415
0
    if (!traces) {
416
0
        return;
417
0
    }
418
419
0
    trace_t *trace = _Py_hashtable_steal(traces, TO_PTR(ptr));
420
0
    if (!trace) {
421
0
        return;
422
0
    }
423
0
    assert(tracemalloc_traced_memory >= trace->size);
424
0
    tracemalloc_traced_memory -= trace->size;
425
0
    raw_free(trace);
426
0
}
427
428
#define REMOVE_TRACE(ptr) \
429
0
    tracemalloc_remove_trace_unlocked(DEFAULT_DOMAIN, (uintptr_t)(ptr))
430
431
432
static int
433
tracemalloc_add_trace_unlocked(unsigned int domain, uintptr_t ptr,
434
                               size_t size)
435
0
{
436
0
    assert(tracemalloc_config.tracing);
437
438
0
    traceback_t *traceback = traceback_new();
439
0
    if (traceback == NULL) {
440
0
        return -1;
441
0
    }
442
443
0
    _Py_hashtable_t *traces = tracemalloc_get_traces_table(domain);
444
0
    if (traces == NULL) {
445
0
        traces = tracemalloc_create_traces_table();
446
0
        if (traces == NULL) {
447
0
            return -1;
448
0
        }
449
450
0
        if (_Py_hashtable_set(tracemalloc_domains, TO_PTR(domain), traces) < 0) {
451
0
            _Py_hashtable_destroy(traces);
452
0
            return -1;
453
0
        }
454
0
    }
455
456
0
    trace_t *trace = _Py_hashtable_get(traces, TO_PTR(ptr));
457
0
    if (trace != NULL) {
458
        /* the memory block is already tracked */
459
0
        assert(tracemalloc_traced_memory >= trace->size);
460
0
        tracemalloc_traced_memory -= trace->size;
461
462
0
        trace->size = size;
463
0
        trace->traceback = traceback;
464
0
    }
465
0
    else {
466
0
        trace = raw_malloc(sizeof(trace_t));
467
0
        if (trace == NULL) {
468
0
            return -1;
469
0
        }
470
0
        trace->size = size;
471
0
        trace->traceback = traceback;
472
473
0
        int res = _Py_hashtable_set(traces, TO_PTR(ptr), trace);
474
0
        if (res != 0) {
475
0
            raw_free(trace);
476
0
            return res;
477
0
        }
478
0
    }
479
480
0
    assert(tracemalloc_traced_memory <= SIZE_MAX - size);
481
0
    tracemalloc_traced_memory += size;
482
0
    if (tracemalloc_traced_memory > tracemalloc_peak_traced_memory) {
483
0
        tracemalloc_peak_traced_memory = tracemalloc_traced_memory;
484
0
    }
485
0
    return 0;
486
0
}
487
488
#define ADD_TRACE(ptr, size) \
489
0
    tracemalloc_add_trace_unlocked(DEFAULT_DOMAIN, (uintptr_t)(ptr), size)
490
491
492
static void*
493
tracemalloc_alloc(int need_gil, int use_calloc,
494
                  void *ctx, size_t nelem, size_t elsize)
495
0
{
496
0
    assert(elsize == 0 || nelem <= SIZE_MAX / elsize);
497
498
0
    int reentrant = get_reentrant();
499
500
    // Ignore reentrant call.
501
    //
502
    // For example, PyObjet_Malloc() calls
503
    // PyMem_Malloc() for allocations larger than 512 bytes: don't trace the
504
    // same memory allocation twice.
505
    //
506
    // If reentrant calls are not ignored, PyGILState_Ensure() can call
507
    // PyMem_RawMalloc() which would call PyGILState_Ensure() again in a loop.
508
0
    if (!reentrant) {
509
0
        set_reentrant(1);
510
0
    }
511
512
0
    PyMemAllocatorEx *alloc = (PyMemAllocatorEx *)ctx;
513
0
    void *ptr;
514
0
    if (use_calloc) {
515
0
        ptr = alloc->calloc(alloc->ctx, nelem, elsize);
516
0
    }
517
0
    else {
518
0
        ptr = alloc->malloc(alloc->ctx, nelem * elsize);
519
0
    }
520
521
0
    if (ptr == NULL) {
522
0
        goto done;
523
0
    }
524
0
    if (reentrant) {
525
0
        goto done;
526
0
    }
527
528
0
    PyGILState_STATE gil_state;
529
0
    if (need_gil) {
530
0
        gil_state = PyGILState_Ensure();
531
0
    }
532
0
    TABLES_LOCK();
533
534
0
    if (tracemalloc_config.tracing) {
535
0
        if (ADD_TRACE(ptr, nelem * elsize) < 0) {
536
            // Failed to allocate a trace for the new memory block
537
0
            alloc->free(alloc->ctx, ptr);
538
0
            ptr = NULL;
539
0
        }
540
0
    }
541
    // else: gh-128679: tracemalloc.stop() was called by another thread
542
543
0
    TABLES_UNLOCK();
544
0
    if (need_gil) {
545
0
        PyGILState_Release(gil_state);
546
0
    }
547
548
0
done:
549
0
    if (!reentrant) {
550
0
        set_reentrant(0);
551
0
    }
552
0
    return ptr;
553
0
}
554
555
556
static void*
557
tracemalloc_realloc(int need_gil, void *ctx, void *ptr, size_t new_size)
558
0
{
559
0
    int reentrant = get_reentrant();
560
561
    // Ignore reentrant call. PyObjet_Realloc() calls PyMem_Realloc() for
562
    // allocations larger than 512 bytes: don't trace the same memory block
563
    // twice.
564
0
    if (!reentrant) {
565
0
        set_reentrant(1);
566
0
    }
567
568
0
    PyMemAllocatorEx *alloc = (PyMemAllocatorEx *)ctx;
569
0
    void *ptr2 = alloc->realloc(alloc->ctx, ptr, new_size);
570
571
0
    if (ptr2 == NULL) {
572
0
        goto done;
573
0
    }
574
0
    if (reentrant) {
575
0
        goto done;
576
0
    }
577
578
0
    PyGILState_STATE gil_state;
579
0
    if (need_gil) {
580
0
        gil_state = PyGILState_Ensure();
581
0
    }
582
0
    TABLES_LOCK();
583
584
0
    if (!tracemalloc_config.tracing) {
585
        // gh-128679: tracemalloc.stop() was called by another thread
586
0
        goto unlock;
587
0
    }
588
589
0
    if (ptr != NULL) {
590
        // An existing memory block has been resized
591
592
        // tracemalloc_add_trace_unlocked() updates the trace if there is
593
        // already a trace at address ptr2.
594
0
        if (ptr2 != ptr) {
595
0
            REMOVE_TRACE(ptr);
596
0
        }
597
598
0
        if (ADD_TRACE(ptr2, new_size) < 0) {
599
            // Memory allocation failed. The error cannot be reported to the
600
            // caller, because realloc() already have shrunk the memory block
601
            // and so removed bytes.
602
            //
603
            // This case is very unlikely: a hash entry has just been released,
604
            // so the hash table should have at least one free entry.
605
            //
606
            // The GIL and the table lock ensures that only one thread is
607
            // allocating memory.
608
0
            Py_FatalError("tracemalloc_realloc() failed to allocate a trace");
609
0
        }
610
0
    }
611
0
    else {
612
        // New allocation
613
614
0
        if (ADD_TRACE(ptr2, new_size) < 0) {
615
            // Failed to allocate a trace for the new memory block
616
0
            alloc->free(alloc->ctx, ptr2);
617
0
            ptr2 = NULL;
618
0
        }
619
0
    }
620
621
0
unlock:
622
0
    TABLES_UNLOCK();
623
0
    if (need_gil) {
624
0
        PyGILState_Release(gil_state);
625
0
    }
626
627
0
done:
628
0
    if (!reentrant) {
629
0
        set_reentrant(0);
630
0
    }
631
0
    return ptr2;
632
0
}
633
634
635
static void
636
tracemalloc_free(void *ctx, void *ptr)
637
0
{
638
0
    if (ptr == NULL) {
639
0
        return;
640
0
    }
641
642
0
    PyMemAllocatorEx *alloc = (PyMemAllocatorEx *)ctx;
643
0
    alloc->free(alloc->ctx, ptr);
644
645
0
    if (get_reentrant()) {
646
0
        return;
647
0
    }
648
649
0
    TABLES_LOCK();
650
651
0
    if (tracemalloc_config.tracing) {
652
0
        REMOVE_TRACE(ptr);
653
0
    }
654
    // else: gh-128679: tracemalloc.stop() was called by another thread
655
656
0
    TABLES_UNLOCK();
657
0
}
658
659
660
static void*
661
tracemalloc_malloc_gil(void *ctx, size_t size)
662
0
{
663
0
    return tracemalloc_alloc(0, 0, ctx, 1, size);
664
0
}
665
666
667
static void*
668
tracemalloc_calloc_gil(void *ctx, size_t nelem, size_t elsize)
669
0
{
670
0
    return tracemalloc_alloc(0, 1, ctx, nelem, elsize);
671
0
}
672
673
674
static void*
675
tracemalloc_realloc_gil(void *ctx, void *ptr, size_t new_size)
676
0
{
677
0
    return tracemalloc_realloc(0, ctx, ptr, new_size);
678
0
}
679
680
681
static void*
682
tracemalloc_raw_malloc(void *ctx, size_t size)
683
0
{
684
0
    return tracemalloc_alloc(1, 0, ctx, 1, size);
685
0
}
686
687
688
static void*
689
tracemalloc_raw_calloc(void *ctx, size_t nelem, size_t elsize)
690
0
{
691
0
    return tracemalloc_alloc(1, 1, ctx, nelem, elsize);
692
0
}
693
694
695
static void*
696
tracemalloc_raw_realloc(void *ctx, void *ptr, size_t new_size)
697
0
{
698
0
    return tracemalloc_realloc(1, ctx, ptr, new_size);
699
0
}
700
701
702
static void
703
tracemalloc_clear_filename(void *value)
704
0
{
705
0
    PyObject *filename = (PyObject *)value;
706
0
    Py_DECREF(filename);
707
0
}
708
709
710
static void
711
tracemalloc_clear_traces_unlocked(void)
712
0
{
713
    // Clearing tracemalloc_filenames requires the GIL to call Py_DECREF()
714
0
    _Py_AssertHoldsTstate();
715
716
0
    set_reentrant(1);
717
718
0
    _Py_hashtable_clear(tracemalloc_traces);
719
0
    _Py_hashtable_clear(tracemalloc_domains);
720
0
    _Py_hashtable_clear(tracemalloc_tracebacks);
721
0
    _Py_hashtable_clear(tracemalloc_filenames);
722
723
0
    tracemalloc_traced_memory = 0;
724
0
    tracemalloc_peak_traced_memory = 0;
725
726
0
    set_reentrant(0);
727
0
}
728
729
730
PyStatus
731
_PyTraceMalloc_Init(void)
732
28
{
733
28
    assert(tracemalloc_config.initialized == TRACEMALLOC_NOT_INITIALIZED);
734
735
28
    PyMem_GetAllocator(PYMEM_DOMAIN_RAW, &allocators.raw);
736
737
28
    if (PyThread_tss_create(&tracemalloc_reentrant_key) != 0) {
738
0
        return _PyStatus_NO_MEMORY();
739
0
    }
740
741
28
    tracemalloc_filenames = hashtable_new(hashtable_hash_pyobject,
742
28
                                          hashtable_compare_unicode,
743
28
                                          tracemalloc_clear_filename, NULL);
744
745
28
    tracemalloc_tracebacks = hashtable_new(hashtable_hash_traceback,
746
28
                                           hashtable_compare_traceback,
747
28
                                           raw_free, NULL);
748
749
28
    tracemalloc_traces = tracemalloc_create_traces_table();
750
28
    tracemalloc_domains = tracemalloc_create_domains_table();
751
752
28
    if (tracemalloc_filenames == NULL || tracemalloc_tracebacks == NULL
753
28
       || tracemalloc_traces == NULL || tracemalloc_domains == NULL)
754
0
    {
755
0
        return _PyStatus_NO_MEMORY();
756
0
    }
757
758
28
    assert(tracemalloc_empty_traceback == NULL);
759
28
    tracemalloc_empty_traceback = raw_malloc(TRACEBACK_SIZE(1));
760
28
    if (tracemalloc_empty_traceback  == NULL) {
761
0
        return _PyStatus_NO_MEMORY();
762
0
    }
763
764
28
    tracemalloc_empty_traceback->nframe = 1;
765
28
    tracemalloc_empty_traceback->total_nframe = 1;
766
    /* borrowed reference */
767
28
    tracemalloc_empty_traceback->frames[0].filename = &_Py_STR(anon_unknown);
768
28
    tracemalloc_empty_traceback->frames[0].lineno = 0;
769
28
    tracemalloc_empty_traceback->hash = traceback_hash(tracemalloc_empty_traceback);
770
771
28
    tracemalloc_config.initialized = TRACEMALLOC_INITIALIZED;
772
28
    return _PyStatus_OK();
773
28
}
774
775
776
static void
777
tracemalloc_deinit(void)
778
0
{
779
0
    if (tracemalloc_config.initialized != TRACEMALLOC_INITIALIZED)
780
0
        return;
781
0
    tracemalloc_config.initialized = TRACEMALLOC_FINALIZED;
782
783
0
    _PyTraceMalloc_Stop();
784
785
    /* destroy hash tables */
786
0
    _Py_hashtable_destroy(tracemalloc_domains);
787
0
    _Py_hashtable_destroy(tracemalloc_traces);
788
0
    _Py_hashtable_destroy(tracemalloc_tracebacks);
789
0
    _Py_hashtable_destroy(tracemalloc_filenames);
790
791
0
    PyThread_tss_delete(&tracemalloc_reentrant_key);
792
793
0
    raw_free(tracemalloc_empty_traceback);
794
0
    tracemalloc_empty_traceback = NULL;
795
0
}
796
797
798
int
799
_PyTraceMalloc_Start(int max_nframe)
800
0
{
801
0
    if (max_nframe < 1 || max_nframe > MAX_NFRAME) {
802
0
        PyErr_Format(PyExc_ValueError,
803
0
                     "the number of frames must be in range [1; %i]",
804
0
                     MAX_NFRAME);
805
0
        return -1;
806
0
    }
807
808
0
    if (_PyTraceMalloc_IsTracing()) {
809
        /* hooks already installed: do nothing */
810
0
        return 0;
811
0
    }
812
813
0
    tracemalloc_config.max_nframe = max_nframe;
814
815
    /* allocate a buffer to store a new traceback */
816
0
    size_t size = TRACEBACK_SIZE(max_nframe);
817
0
    assert(tracemalloc_traceback == NULL);
818
0
    tracemalloc_traceback = raw_malloc(size);
819
0
    if (tracemalloc_traceback == NULL) {
820
0
        PyErr_NoMemory();
821
0
        return -1;
822
0
    }
823
824
0
    PyMemAllocatorEx alloc;
825
0
    alloc.malloc = tracemalloc_raw_malloc;
826
0
    alloc.calloc = tracemalloc_raw_calloc;
827
0
    alloc.realloc = tracemalloc_raw_realloc;
828
0
    alloc.free = tracemalloc_free;
829
830
0
    alloc.ctx = &allocators.raw;
831
0
    PyMem_GetAllocator(PYMEM_DOMAIN_RAW, &allocators.raw);
832
0
    PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &alloc);
833
834
0
    alloc.malloc = tracemalloc_malloc_gil;
835
0
    alloc.calloc = tracemalloc_calloc_gil;
836
0
    alloc.realloc = tracemalloc_realloc_gil;
837
0
    alloc.free = tracemalloc_free;
838
839
0
    alloc.ctx = &allocators.mem;
840
0
    PyMem_GetAllocator(PYMEM_DOMAIN_MEM, &allocators.mem);
841
0
    PyMem_SetAllocator(PYMEM_DOMAIN_MEM, &alloc);
842
843
0
    alloc.ctx = &allocators.obj;
844
0
    PyMem_GetAllocator(PYMEM_DOMAIN_OBJ, &allocators.obj);
845
0
    PyMem_SetAllocator(PYMEM_DOMAIN_OBJ, &alloc);
846
847
0
    if (PyRefTracer_SetTracer(_PyTraceMalloc_TraceRef, NULL) < 0) {
848
0
        return -1;
849
0
    }
850
851
    /* everything is ready: start tracing Python memory allocations */
852
0
    TABLES_LOCK();
853
0
    tracemalloc_config.tracing = 1;
854
0
    TABLES_UNLOCK();
855
856
0
    return 0;
857
0
}
858
859
860
void
861
_PyTraceMalloc_Stop(void)
862
0
{
863
0
    TABLES_LOCK();
864
865
0
    if (!tracemalloc_config.tracing) {
866
0
        goto done;
867
0
    }
868
869
    /* stop tracing Python memory allocations */
870
0
    tracemalloc_config.tracing = 0;
871
872
    /* unregister the hook on memory allocators */
873
0
    PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &allocators.raw);
874
0
    PyMem_SetAllocator(PYMEM_DOMAIN_MEM, &allocators.mem);
875
0
    PyMem_SetAllocator(PYMEM_DOMAIN_OBJ, &allocators.obj);
876
877
0
    tracemalloc_clear_traces_unlocked();
878
879
    /* release memory */
880
0
    raw_free(tracemalloc_traceback);
881
0
    tracemalloc_traceback = NULL;
882
883
0
    (void)PyRefTracer_SetTracer(NULL, NULL);
884
885
0
done:
886
0
    TABLES_UNLOCK();
887
0
}
888
889
890
891
static PyObject*
892
frame_to_pyobject(frame_t *frame)
893
0
{
894
0
    assert(get_reentrant());
895
896
0
    PyObject *frame_obj = PyTuple_New(2);
897
0
    if (frame_obj == NULL) {
898
0
        return NULL;
899
0
    }
900
901
0
    PyTuple_SET_ITEM(frame_obj, 0, Py_NewRef(frame->filename));
902
903
0
    PyObject *lineno_obj = PyLong_FromUnsignedLong(frame->lineno);
904
0
    if (lineno_obj == NULL) {
905
0
        Py_DECREF(frame_obj);
906
0
        return NULL;
907
0
    }
908
0
    PyTuple_SET_ITEM(frame_obj, 1, lineno_obj);
909
910
0
    return frame_obj;
911
0
}
912
913
914
static PyObject*
915
traceback_to_pyobject(traceback_t *traceback, _Py_hashtable_t *intern_table)
916
0
{
917
0
    PyObject *frames;
918
0
    if (intern_table != NULL) {
919
0
        frames = _Py_hashtable_get(intern_table, (const void *)traceback);
920
0
        if (frames) {
921
0
            return Py_NewRef(frames);
922
0
        }
923
0
    }
924
925
0
    frames = PyTuple_New(traceback->nframe);
926
0
    if (frames == NULL) {
927
0
        return NULL;
928
0
    }
929
930
0
    for (int i=0; i < traceback->nframe; i++) {
931
0
        PyObject *frame = frame_to_pyobject(&traceback->frames[i]);
932
0
        if (frame == NULL) {
933
0
            Py_DECREF(frames);
934
0
            return NULL;
935
0
        }
936
0
        PyTuple_SET_ITEM(frames, i, frame);
937
0
    }
938
939
0
    if (intern_table != NULL) {
940
0
        if (_Py_hashtable_set(intern_table, traceback, frames) < 0) {
941
0
            Py_DECREF(frames);
942
0
            PyErr_NoMemory();
943
0
            return NULL;
944
0
        }
945
        /* intern_table keeps a new reference to frames */
946
0
        Py_INCREF(frames);
947
0
    }
948
0
    return frames;
949
0
}
950
951
952
static PyObject*
953
trace_to_pyobject(unsigned int domain, const trace_t *trace,
954
                  _Py_hashtable_t *intern_tracebacks)
955
0
{
956
0
    assert(get_reentrant());
957
958
0
    PyObject *trace_obj = PyTuple_New(4);
959
0
    if (trace_obj == NULL) {
960
0
        return NULL;
961
0
    }
962
963
0
    PyObject *obj = PyLong_FromSize_t(domain);
964
0
    if (obj == NULL) {
965
0
        Py_DECREF(trace_obj);
966
0
        return NULL;
967
0
    }
968
0
    PyTuple_SET_ITEM(trace_obj, 0, obj);
969
970
0
    obj = PyLong_FromSize_t(trace->size);
971
0
    if (obj == NULL) {
972
0
        Py_DECREF(trace_obj);
973
0
        return NULL;
974
0
    }
975
0
    PyTuple_SET_ITEM(trace_obj, 1, obj);
976
977
0
    obj = traceback_to_pyobject(trace->traceback, intern_tracebacks);
978
0
    if (obj == NULL) {
979
0
        Py_DECREF(trace_obj);
980
0
        return NULL;
981
0
    }
982
0
    PyTuple_SET_ITEM(trace_obj, 2, obj);
983
984
0
    obj = PyLong_FromUnsignedLong(trace->traceback->total_nframe);
985
0
    if (obj == NULL) {
986
0
        Py_DECREF(trace_obj);
987
0
        return NULL;
988
0
    }
989
0
    PyTuple_SET_ITEM(trace_obj, 3, obj);
990
991
0
    return trace_obj;
992
0
}
993
994
995
typedef struct {
996
    _Py_hashtable_t *traces;
997
    _Py_hashtable_t *domains;
998
    _Py_hashtable_t *tracebacks;
999
    PyObject *list;
1000
    unsigned int domain;
1001
} get_traces_t;
1002
1003
1004
static int
1005
tracemalloc_copy_trace(_Py_hashtable_t *traces,
1006
                       const void *key, const void *value,
1007
                       void *user_data)
1008
0
{
1009
0
    _Py_hashtable_t *traces2 = (_Py_hashtable_t *)user_data;
1010
0
    trace_t *trace = (trace_t *)value;
1011
1012
0
    trace_t *trace2 = raw_malloc(sizeof(trace_t));
1013
0
    if (trace2 == NULL) {
1014
0
        return -1;
1015
0
    }
1016
0
    *trace2 = *trace;
1017
0
    if (_Py_hashtable_set(traces2, key, trace2) < 0) {
1018
0
        raw_free(trace2);
1019
0
        return -1;
1020
0
    }
1021
0
    return 0;
1022
0
}
1023
1024
1025
static _Py_hashtable_t*
1026
tracemalloc_copy_traces(_Py_hashtable_t *traces)
1027
0
{
1028
0
    _Py_hashtable_t *traces2 = tracemalloc_create_traces_table();
1029
0
    if (traces2 == NULL) {
1030
0
        return NULL;
1031
0
    }
1032
1033
0
    int err = _Py_hashtable_foreach(traces,
1034
0
                                    tracemalloc_copy_trace,
1035
0
                                    traces2);
1036
0
    if (err) {
1037
0
        _Py_hashtable_destroy(traces2);
1038
0
        return NULL;
1039
0
    }
1040
0
    return traces2;
1041
0
}
1042
1043
1044
static int
1045
tracemalloc_copy_domain(_Py_hashtable_t *domains,
1046
                        const void *key, const void *value,
1047
                        void *user_data)
1048
0
{
1049
0
    _Py_hashtable_t *domains2 = (_Py_hashtable_t *)user_data;
1050
0
    unsigned int domain = (unsigned int)FROM_PTR(key);
1051
0
    _Py_hashtable_t *traces = (_Py_hashtable_t *)value;
1052
1053
0
    _Py_hashtable_t *traces2 = tracemalloc_copy_traces(traces);
1054
0
    if (traces2 == NULL) {
1055
0
        return -1;
1056
0
    }
1057
0
    if (_Py_hashtable_set(domains2, TO_PTR(domain), traces2) < 0) {
1058
0
        _Py_hashtable_destroy(traces2);
1059
0
        return -1;
1060
0
    }
1061
0
    return 0;
1062
0
}
1063
1064
1065
static _Py_hashtable_t*
1066
tracemalloc_copy_domains(_Py_hashtable_t *domains)
1067
0
{
1068
0
    _Py_hashtable_t *domains2 = tracemalloc_create_domains_table();
1069
0
    if (domains2 == NULL) {
1070
0
        return NULL;
1071
0
    }
1072
1073
0
    int err = _Py_hashtable_foreach(domains,
1074
0
                                    tracemalloc_copy_domain,
1075
0
                                    domains2);
1076
0
    if (err) {
1077
0
        _Py_hashtable_destroy(domains2);
1078
0
        return NULL;
1079
0
    }
1080
0
    return domains2;
1081
0
}
1082
1083
1084
static int
1085
tracemalloc_get_traces_fill(_Py_hashtable_t *traces,
1086
                            const void *key, const void *value,
1087
                            void *user_data)
1088
0
{
1089
0
    get_traces_t *get_traces = user_data;
1090
0
    const trace_t *trace = (const trace_t *)value;
1091
1092
0
    PyObject *tuple = trace_to_pyobject(get_traces->domain, trace,
1093
0
                                        get_traces->tracebacks);
1094
0
    if (tuple == NULL) {
1095
0
        return 1;
1096
0
    }
1097
1098
0
    int res = PyList_Append(get_traces->list, tuple);
1099
0
    Py_DECREF(tuple);
1100
0
    if (res < 0) {
1101
0
        return 1;
1102
0
    }
1103
0
    return 0;
1104
0
}
1105
1106
1107
static int
1108
tracemalloc_get_traces_domain(_Py_hashtable_t *domains,
1109
                              const void *key, const void *value,
1110
                              void *user_data)
1111
0
{
1112
0
    get_traces_t *get_traces = user_data;
1113
0
    unsigned int domain = (unsigned int)FROM_PTR(key);
1114
0
    _Py_hashtable_t *traces = (_Py_hashtable_t *)value;
1115
1116
0
    get_traces->domain = domain;
1117
0
    return _Py_hashtable_foreach(traces,
1118
0
                                 tracemalloc_get_traces_fill,
1119
0
                                 get_traces);
1120
0
}
1121
1122
1123
static void
1124
tracemalloc_pyobject_decref(void *value)
1125
0
{
1126
0
    PyObject *obj = (PyObject *)value;
1127
0
    Py_DECREF(obj);
1128
0
}
1129
1130
1131
static traceback_t*
1132
tracemalloc_get_traceback_unlocked(unsigned int domain, uintptr_t ptr)
1133
0
{
1134
0
    if (!tracemalloc_config.tracing) {
1135
0
        return NULL;
1136
0
    }
1137
1138
0
    _Py_hashtable_t *traces = tracemalloc_get_traces_table(domain);
1139
0
    if (!traces) {
1140
0
        return NULL;
1141
0
    }
1142
1143
0
    trace_t *trace = _Py_hashtable_get(traces, TO_PTR(ptr));
1144
0
    if (!trace) {
1145
0
        return NULL;
1146
0
    }
1147
0
    return trace->traceback;
1148
0
}
1149
1150
1151
0
#define PUTS(fd, str) (void)_Py_write_noraise(fd, str, (int)strlen(str))
1152
1153
static void
1154
_PyMem_DumpFrame(int fd, frame_t * frame)
1155
0
{
1156
0
    PUTS(fd, "  File \"");
1157
0
    _Py_DumpASCII(fd, frame->filename);
1158
0
    PUTS(fd, "\", line ");
1159
0
    _Py_DumpDecimal(fd, frame->lineno);
1160
0
    PUTS(fd, "\n");
1161
0
}
1162
1163
/* Dump the traceback where a memory block was allocated into file descriptor
1164
   fd. The function may block on TABLES_LOCK() but it is unlikely. */
1165
void
1166
_PyMem_DumpTraceback(int fd, const void *ptr)
1167
0
{
1168
0
    TABLES_LOCK();
1169
0
    if (!tracemalloc_config.tracing) {
1170
0
        PUTS(fd, "Enable tracemalloc to get the memory block "
1171
0
                 "allocation traceback\n\n");
1172
0
        goto done;
1173
0
    }
1174
1175
0
    traceback_t *traceback;
1176
0
    traceback = tracemalloc_get_traceback_unlocked(DEFAULT_DOMAIN,
1177
0
                                                   (uintptr_t)ptr);
1178
0
    if (traceback == NULL) {
1179
0
        goto done;
1180
0
    }
1181
1182
0
    PUTS(fd, "Memory block allocated at (most recent call first):\n");
1183
0
    for (int i=0; i < traceback->nframe; i++) {
1184
0
        _PyMem_DumpFrame(fd, &traceback->frames[i]);
1185
0
    }
1186
0
    PUTS(fd, "\n");
1187
1188
0
done:
1189
0
    TABLES_UNLOCK();
1190
0
}
1191
1192
#undef PUTS
1193
1194
1195
static int
1196
tracemalloc_get_tracemalloc_memory_cb(_Py_hashtable_t *domains,
1197
                                      const void *key, const void *value,
1198
                                      void *user_data)
1199
0
{
1200
0
    const _Py_hashtable_t *traces = value;
1201
0
    size_t *size = (size_t*)user_data;
1202
0
    *size += _Py_hashtable_size(traces);
1203
0
    return 0;
1204
0
}
1205
1206
int
1207
PyTraceMalloc_Track(unsigned int domain, uintptr_t ptr,
1208
                    size_t size)
1209
0
{
1210
0
    PyGILState_STATE gil_state = PyGILState_Ensure();
1211
0
    TABLES_LOCK();
1212
1213
0
    int result;
1214
0
    if (tracemalloc_config.tracing) {
1215
0
        result = tracemalloc_add_trace_unlocked(domain, ptr, size);
1216
0
    }
1217
0
    else {
1218
        /* tracemalloc is not tracing: do nothing */
1219
0
        result = -2;
1220
0
    }
1221
1222
0
    TABLES_UNLOCK();
1223
0
    PyGILState_Release(gil_state);
1224
0
    return result;
1225
0
}
1226
1227
1228
int
1229
PyTraceMalloc_Untrack(unsigned int domain, uintptr_t ptr)
1230
0
{
1231
0
    TABLES_LOCK();
1232
1233
0
    int result;
1234
0
    if (tracemalloc_config.tracing) {
1235
0
        tracemalloc_remove_trace_unlocked(domain, ptr);
1236
0
        result = 0;
1237
0
    }
1238
0
    else {
1239
        /* tracemalloc is not tracing: do nothing */
1240
0
        result = -2;
1241
0
    }
1242
1243
0
    TABLES_UNLOCK();
1244
0
    return result;
1245
0
}
1246
1247
1248
void
1249
_PyTraceMalloc_Fini(void)
1250
0
{
1251
0
    _Py_AssertHoldsTstate();
1252
0
    tracemalloc_deinit();
1253
0
}
1254
1255
1256
/* If the object memory block is already traced, update its trace
1257
   with the current Python traceback.
1258
1259
   Do nothing if tracemalloc is not tracing memory allocations
1260
   or if the object memory block is not already traced. */
1261
static int
1262
_PyTraceMalloc_TraceRef(PyObject *op, PyRefTracerEvent event,
1263
                        void* Py_UNUSED(ignore))
1264
0
{
1265
0
    if (event != PyRefTracer_CREATE) {
1266
0
        return 0;
1267
0
    }
1268
0
    if (get_reentrant()) {
1269
0
        return 0;
1270
0
    }
1271
1272
0
    _Py_AssertHoldsTstate();
1273
0
    TABLES_LOCK();
1274
1275
0
    if (!tracemalloc_config.tracing) {
1276
0
        goto done;
1277
0
    }
1278
1279
0
    PyTypeObject *type = Py_TYPE(op);
1280
0
    const size_t presize = _PyType_PreHeaderSize(type);
1281
0
    uintptr_t ptr = (uintptr_t)((char *)op - presize);
1282
1283
0
    trace_t *trace = _Py_hashtable_get(tracemalloc_traces, TO_PTR(ptr));
1284
0
    if (trace != NULL) {
1285
        /* update the traceback of the memory block */
1286
0
        traceback_t *traceback = traceback_new();
1287
0
        if (traceback != NULL) {
1288
0
            trace->traceback = traceback;
1289
0
        }
1290
0
    }
1291
    /* else: cannot track the object, its memory block size is unknown */
1292
1293
0
done:
1294
0
    TABLES_UNLOCK();
1295
0
    return 0;
1296
0
}
1297
1298
1299
PyObject*
1300
_PyTraceMalloc_GetTraceback(unsigned int domain, uintptr_t ptr)
1301
0
{
1302
0
    TABLES_LOCK();
1303
1304
0
    traceback_t *traceback = tracemalloc_get_traceback_unlocked(domain, ptr);
1305
0
    PyObject *result;
1306
0
    if (traceback) {
1307
0
        set_reentrant(1);
1308
0
        result = traceback_to_pyobject(traceback, NULL);
1309
0
        set_reentrant(0);
1310
0
    }
1311
0
    else {
1312
0
        result = Py_NewRef(Py_None);
1313
0
    }
1314
1315
0
    TABLES_UNLOCK();
1316
0
    return result;
1317
0
}
1318
1319
int
1320
_PyTraceMalloc_IsTracing(void)
1321
0
{
1322
0
    TABLES_LOCK();
1323
0
    int tracing = tracemalloc_config.tracing;
1324
0
    TABLES_UNLOCK();
1325
0
    return tracing;
1326
0
}
1327
1328
void
1329
_PyTraceMalloc_ClearTraces(void)
1330
0
{
1331
0
    TABLES_LOCK();
1332
0
    if (tracemalloc_config.tracing) {
1333
0
        tracemalloc_clear_traces_unlocked();
1334
0
    }
1335
0
    TABLES_UNLOCK();
1336
0
}
1337
1338
PyObject *
1339
_PyTraceMalloc_GetTraces(void)
1340
0
{
1341
0
    TABLES_LOCK();
1342
0
    set_reentrant(1);
1343
1344
0
    get_traces_t get_traces;
1345
0
    get_traces.domain = DEFAULT_DOMAIN;
1346
0
    get_traces.traces = NULL;
1347
0
    get_traces.domains = NULL;
1348
0
    get_traces.tracebacks = NULL;
1349
0
    get_traces.list = PyList_New(0);
1350
0
    if (get_traces.list == NULL) {
1351
0
        goto finally;
1352
0
    }
1353
1354
0
    if (!tracemalloc_config.tracing) {
1355
0
        goto finally;
1356
0
    }
1357
1358
    /* the traceback hash table is used temporarily to intern traceback tuple
1359
       of (filename, lineno) tuples */
1360
0
    get_traces.tracebacks = hashtable_new(_Py_hashtable_hash_ptr,
1361
0
                                          _Py_hashtable_compare_direct,
1362
0
                                          NULL, tracemalloc_pyobject_decref);
1363
0
    if (get_traces.tracebacks == NULL) {
1364
0
        goto no_memory;
1365
0
    }
1366
1367
    // Copy all traces so tracemalloc_get_traces_fill() doesn't have to disable
1368
    // temporarily tracemalloc which would impact other threads and so would
1369
    // miss allocations while get_traces() is called.
1370
0
    get_traces.traces = tracemalloc_copy_traces(tracemalloc_traces);
1371
0
    if (get_traces.traces == NULL) {
1372
0
        goto no_memory;
1373
0
    }
1374
1375
0
    get_traces.domains = tracemalloc_copy_domains(tracemalloc_domains);
1376
0
    if (get_traces.domains == NULL) {
1377
0
        goto no_memory;
1378
0
    }
1379
1380
    // Convert traces to a list of tuples
1381
0
    int err = _Py_hashtable_foreach(get_traces.traces,
1382
0
                                    tracemalloc_get_traces_fill,
1383
0
                                    &get_traces);
1384
0
    if (!err) {
1385
0
        err = _Py_hashtable_foreach(get_traces.domains,
1386
0
                                    tracemalloc_get_traces_domain,
1387
0
                                    &get_traces);
1388
0
    }
1389
1390
0
    if (err) {
1391
0
        Py_CLEAR(get_traces.list);
1392
0
        goto finally;
1393
0
    }
1394
0
    goto finally;
1395
1396
0
no_memory:
1397
0
    PyErr_NoMemory();
1398
0
    Py_CLEAR(get_traces.list);
1399
0
    goto finally;
1400
1401
0
finally:
1402
0
    set_reentrant(0);
1403
0
    TABLES_UNLOCK();
1404
1405
0
    if (get_traces.tracebacks != NULL) {
1406
0
        _Py_hashtable_destroy(get_traces.tracebacks);
1407
0
    }
1408
0
    if (get_traces.traces != NULL) {
1409
0
        _Py_hashtable_destroy(get_traces.traces);
1410
0
    }
1411
0
    if (get_traces.domains != NULL) {
1412
0
        _Py_hashtable_destroy(get_traces.domains);
1413
0
    }
1414
1415
0
    return get_traces.list;
1416
0
}
1417
1418
PyObject *
1419
_PyTraceMalloc_GetObjectTraceback(PyObject *obj)
1420
/*[clinic end generated code: output=41ee0553a658b0aa input=29495f1b21c53212]*/
1421
0
{
1422
0
    PyTypeObject *type = Py_TYPE(obj);
1423
0
    const size_t presize = _PyType_PreHeaderSize(type);
1424
0
    uintptr_t ptr = (uintptr_t)((char *)obj - presize);
1425
0
    return _PyTraceMalloc_GetTraceback(DEFAULT_DOMAIN, ptr);
1426
0
}
1427
1428
int _PyTraceMalloc_GetTracebackLimit(void)
1429
0
{
1430
0
    return tracemalloc_config.max_nframe;
1431
0
}
1432
1433
size_t
1434
_PyTraceMalloc_GetMemory(void)
1435
0
{
1436
0
    TABLES_LOCK();
1437
0
    size_t size;
1438
0
    if (tracemalloc_config.tracing) {
1439
0
        size = _Py_hashtable_size(tracemalloc_tracebacks);
1440
0
        size += _Py_hashtable_size(tracemalloc_filenames);
1441
1442
0
        size += _Py_hashtable_size(tracemalloc_traces);
1443
0
        _Py_hashtable_foreach(tracemalloc_domains,
1444
0
                              tracemalloc_get_tracemalloc_memory_cb, &size);
1445
0
    }
1446
0
    else {
1447
0
        size = 0;
1448
0
    }
1449
0
    TABLES_UNLOCK();
1450
0
    return size;
1451
0
}
1452
1453
1454
PyObject *
1455
_PyTraceMalloc_GetTracedMemory(void)
1456
0
{
1457
0
    TABLES_LOCK();
1458
0
    Py_ssize_t traced, peak;
1459
0
    if (tracemalloc_config.tracing) {
1460
0
        traced = tracemalloc_traced_memory;
1461
0
        peak = tracemalloc_peak_traced_memory;
1462
0
    }
1463
0
    else {
1464
0
        traced = 0;
1465
0
        peak = 0;
1466
0
    }
1467
0
    TABLES_UNLOCK();
1468
1469
0
    return Py_BuildValue("nn", traced, peak);
1470
0
}
1471
1472
void
1473
_PyTraceMalloc_ResetPeak(void)
1474
0
{
1475
0
    TABLES_LOCK();
1476
0
    if (tracemalloc_config.tracing) {
1477
0
        tracemalloc_peak_traced_memory = tracemalloc_traced_memory;
1478
0
    }
1479
0
    TABLES_UNLOCK();
1480
0
}