Coverage Report

Created: 2025-07-04 06:49

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