Coverage Report

Created: 2026-01-17 06:45

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/cpython3/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
22
#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
66
#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
22
        (sizeof(traceback_t) + sizeof(frame_t) * (NFRAME))
50
51
static const int MAX_NFRAME = UINT16_MAX;
52
53
54
176
#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
44
#define tracemalloc_filenames _PyRuntime.tracemalloc.filenames
70
0
#define tracemalloc_traceback _PyRuntime.tracemalloc.traceback
71
44
#define tracemalloc_tracebacks _PyRuntime.tracemalloc.tracebacks
72
44
#define tracemalloc_traces _PyRuntime.tracemalloc.traces
73
44
#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
22
#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
88
{
164
88
    _Py_hashtable_allocator_t hashtable_alloc = {malloc, free};
165
88
    return _Py_hashtable_new_full(hash_func, compare_func,
166
88
                                  key_destroy_func, value_destroy_func,
167
88
                                  &hashtable_alloc);
168
88
}
169
170
171
static void*
172
raw_malloc(size_t size)
173
22
{
174
22
    return allocators.raw.malloc(allocators.raw.ctx, size);
175
22
}
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
22
{
275
    /* code based on tuple_hash() of Objects/tupleobject.c */
276
22
    Py_uhash_t x, y;  /* Unsigned for defined overflow behavior. */
277
22
    int len = traceback->nframe;
278
22
    Py_uhash_t mult = PyHASH_MULTIPLIER;
279
22
    frame_t *frame;
280
281
22
    x = 0x345678UL;
282
22
    frame = traceback->frames;
283
44
    while (--len >= 0) {
284
22
        y = (Py_uhash_t)PyObject_Hash(frame->filename);
285
22
        y ^= (Py_uhash_t)frame->lineno;
286
22
        frame++;
287
288
22
        x = (x ^ y) * mult;
289
        /* the cast might truncate len; that doesn't change hash stability */
290
22
        mult += (Py_uhash_t)(82520UL + len + len);
291
22
    }
292
22
    x ^= traceback->total_nframe;
293
22
    x += 97531UL;
294
22
    return x;
295
22
}
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
22
{
373
22
    return hashtable_new(_Py_hashtable_hash_ptr,
374
22
                         _Py_hashtable_compare_direct,
375
22
                         NULL, raw_free);
376
22
}
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
22
{
390
22
    return hashtable_new(hashtable_hash_uint,
391
22
                         _Py_hashtable_compare_direct,
392
22
                         NULL,
393
22
                         tracemalloc_destroy_domain);
394
22
}
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
22
{
733
22
    assert(tracemalloc_config.initialized == TRACEMALLOC_NOT_INITIALIZED);
734
735
22
    PyMem_GetAllocator(PYMEM_DOMAIN_RAW, &allocators.raw);
736
737
22
    if (PyThread_tss_create(&tracemalloc_reentrant_key) != 0) {
738
0
        return _PyStatus_NO_MEMORY();
739
0
    }
740
741
22
    tracemalloc_filenames = hashtable_new(hashtable_hash_pyobject,
742
22
                                          hashtable_compare_unicode,
743
22
                                          tracemalloc_clear_filename, NULL);
744
745
22
    tracemalloc_tracebacks = hashtable_new(hashtable_hash_traceback,
746
22
                                           hashtable_compare_traceback,
747
22
                                           raw_free, NULL);
748
749
22
    tracemalloc_traces = tracemalloc_create_traces_table();
750
22
    tracemalloc_domains = tracemalloc_create_domains_table();
751
752
22
    if (tracemalloc_filenames == NULL || tracemalloc_tracebacks == NULL
753
22
       || tracemalloc_traces == NULL || tracemalloc_domains == NULL)
754
0
    {
755
0
        return _PyStatus_NO_MEMORY();
756
0
    }
757
758
22
    assert(tracemalloc_empty_traceback == NULL);
759
22
    tracemalloc_empty_traceback = raw_malloc(TRACEBACK_SIZE(1));
760
22
    if (tracemalloc_empty_traceback  == NULL) {
761
0
        return _PyStatus_NO_MEMORY();
762
0
    }
763
764
22
    tracemalloc_empty_traceback->nframe = 1;
765
22
    tracemalloc_empty_traceback->total_nframe = 1;
766
    /* borrowed reference */
767
22
    tracemalloc_empty_traceback->frames[0].filename = &_Py_STR(anon_unknown);
768
22
    tracemalloc_empty_traceback->frames[0].lineno = 0;
769
22
    tracemalloc_empty_traceback->hash = traceback_hash(tracemalloc_empty_traceback);
770
771
22
    tracemalloc_config.initialized = TRACEMALLOC_INITIALIZED;
772
22
    return _PyStatus_OK();
773
22
}
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
    _Py_atomic_store_int_relaxed(&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
    _Py_atomic_store_int_relaxed(&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
    if (_Py_atomic_load_int_relaxed(&tracemalloc_config.tracing) == 0) {
1211
        /* tracemalloc is not tracing: do nothing */
1212
0
        return -2;
1213
0
    }
1214
0
    PyGILState_STATE gil_state = PyGILState_Ensure();
1215
0
    TABLES_LOCK();
1216
1217
0
    int result;
1218
0
    if (tracemalloc_config.tracing) {
1219
0
        result = tracemalloc_add_trace_unlocked(domain, ptr, size);
1220
0
    }
1221
0
    else {
1222
        /* tracemalloc is not tracing: do nothing */
1223
0
        result = -2;
1224
0
    }
1225
1226
0
    TABLES_UNLOCK();
1227
0
    PyGILState_Release(gil_state);
1228
0
    return result;
1229
0
}
1230
1231
1232
int
1233
PyTraceMalloc_Untrack(unsigned int domain, uintptr_t ptr)
1234
0
{
1235
0
    if (_Py_atomic_load_int_relaxed(&tracemalloc_config.tracing) == 0) {
1236
        /* tracemalloc is not tracing: do nothing */
1237
0
        return -2;
1238
0
    }
1239
1240
0
    TABLES_LOCK();
1241
1242
0
    int result;
1243
0
    if (tracemalloc_config.tracing) {
1244
0
        tracemalloc_remove_trace_unlocked(domain, ptr);
1245
0
        result = 0;
1246
0
    }
1247
0
    else {
1248
        /* tracemalloc is not tracing: do nothing */
1249
0
        result = -2;
1250
0
    }
1251
1252
0
    TABLES_UNLOCK();
1253
0
    return result;
1254
0
}
1255
1256
1257
void
1258
_PyTraceMalloc_Fini(void)
1259
0
{
1260
0
    _Py_AssertHoldsTstate();
1261
0
    tracemalloc_deinit();
1262
0
}
1263
1264
1265
/* If the object memory block is already traced, update its trace
1266
   with the current Python traceback.
1267
1268
   Do nothing if tracemalloc is not tracing memory allocations
1269
   or if the object memory block is not already traced. */
1270
static int
1271
_PyTraceMalloc_TraceRef(PyObject *op, PyRefTracerEvent event,
1272
                        void* Py_UNUSED(ignore))
1273
0
{
1274
0
    if (event != PyRefTracer_CREATE) {
1275
0
        return 0;
1276
0
    }
1277
0
    if (get_reentrant()) {
1278
0
        return 0;
1279
0
    }
1280
1281
0
    _Py_AssertHoldsTstate();
1282
0
    TABLES_LOCK();
1283
1284
0
    if (!tracemalloc_config.tracing) {
1285
0
        goto done;
1286
0
    }
1287
1288
0
    PyTypeObject *type = Py_TYPE(op);
1289
0
    const size_t presize = _PyType_PreHeaderSize(type);
1290
0
    uintptr_t ptr = (uintptr_t)((char *)op - presize);
1291
1292
0
    trace_t *trace = _Py_hashtable_get(tracemalloc_traces, TO_PTR(ptr));
1293
0
    if (trace != NULL) {
1294
        /* update the traceback of the memory block */
1295
0
        traceback_t *traceback = traceback_new();
1296
0
        if (traceback != NULL) {
1297
0
            trace->traceback = traceback;
1298
0
        }
1299
0
    }
1300
    /* else: cannot track the object, its memory block size is unknown */
1301
1302
0
done:
1303
0
    TABLES_UNLOCK();
1304
0
    return 0;
1305
0
}
1306
1307
1308
PyObject*
1309
_PyTraceMalloc_GetTraceback(unsigned int domain, uintptr_t ptr)
1310
0
{
1311
0
    TABLES_LOCK();
1312
1313
0
    traceback_t *traceback = tracemalloc_get_traceback_unlocked(domain, ptr);
1314
0
    PyObject *result;
1315
0
    if (traceback) {
1316
0
        set_reentrant(1);
1317
0
        result = traceback_to_pyobject(traceback, NULL);
1318
0
        set_reentrant(0);
1319
0
    }
1320
0
    else {
1321
0
        result = Py_NewRef(Py_None);
1322
0
    }
1323
1324
0
    TABLES_UNLOCK();
1325
0
    return result;
1326
0
}
1327
1328
int
1329
_PyTraceMalloc_IsTracing(void)
1330
0
{
1331
0
    TABLES_LOCK();
1332
0
    int tracing = tracemalloc_config.tracing;
1333
0
    TABLES_UNLOCK();
1334
0
    return tracing;
1335
0
}
1336
1337
void
1338
_PyTraceMalloc_ClearTraces(void)
1339
0
{
1340
0
    TABLES_LOCK();
1341
0
    if (tracemalloc_config.tracing) {
1342
0
        tracemalloc_clear_traces_unlocked();
1343
0
    }
1344
0
    TABLES_UNLOCK();
1345
0
}
1346
1347
PyObject *
1348
_PyTraceMalloc_GetTraces(void)
1349
0
{
1350
0
    TABLES_LOCK();
1351
0
    set_reentrant(1);
1352
1353
0
    get_traces_t get_traces;
1354
0
    get_traces.domain = DEFAULT_DOMAIN;
1355
0
    get_traces.traces = NULL;
1356
0
    get_traces.domains = NULL;
1357
0
    get_traces.tracebacks = NULL;
1358
0
    get_traces.list = PyList_New(0);
1359
0
    if (get_traces.list == NULL) {
1360
0
        goto finally;
1361
0
    }
1362
1363
0
    if (!tracemalloc_config.tracing) {
1364
0
        goto finally;
1365
0
    }
1366
1367
    /* the traceback hash table is used temporarily to intern traceback tuple
1368
       of (filename, lineno) tuples */
1369
0
    get_traces.tracebacks = hashtable_new(_Py_hashtable_hash_ptr,
1370
0
                                          _Py_hashtable_compare_direct,
1371
0
                                          NULL, tracemalloc_pyobject_decref);
1372
0
    if (get_traces.tracebacks == NULL) {
1373
0
        goto no_memory;
1374
0
    }
1375
1376
    // Copy all traces so tracemalloc_get_traces_fill() doesn't have to disable
1377
    // temporarily tracemalloc which would impact other threads and so would
1378
    // miss allocations while get_traces() is called.
1379
0
    get_traces.traces = tracemalloc_copy_traces(tracemalloc_traces);
1380
0
    if (get_traces.traces == NULL) {
1381
0
        goto no_memory;
1382
0
    }
1383
1384
0
    get_traces.domains = tracemalloc_copy_domains(tracemalloc_domains);
1385
0
    if (get_traces.domains == NULL) {
1386
0
        goto no_memory;
1387
0
    }
1388
1389
    // Convert traces to a list of tuples
1390
0
    int err = _Py_hashtable_foreach(get_traces.traces,
1391
0
                                    tracemalloc_get_traces_fill,
1392
0
                                    &get_traces);
1393
0
    if (!err) {
1394
0
        err = _Py_hashtable_foreach(get_traces.domains,
1395
0
                                    tracemalloc_get_traces_domain,
1396
0
                                    &get_traces);
1397
0
    }
1398
1399
0
    if (err) {
1400
0
        Py_CLEAR(get_traces.list);
1401
0
        goto finally;
1402
0
    }
1403
0
    goto finally;
1404
1405
0
no_memory:
1406
0
    PyErr_NoMemory();
1407
0
    Py_CLEAR(get_traces.list);
1408
0
    goto finally;
1409
1410
0
finally:
1411
0
    set_reentrant(0);
1412
0
    TABLES_UNLOCK();
1413
1414
0
    if (get_traces.tracebacks != NULL) {
1415
0
        _Py_hashtable_destroy(get_traces.tracebacks);
1416
0
    }
1417
0
    if (get_traces.traces != NULL) {
1418
0
        _Py_hashtable_destroy(get_traces.traces);
1419
0
    }
1420
0
    if (get_traces.domains != NULL) {
1421
0
        _Py_hashtable_destroy(get_traces.domains);
1422
0
    }
1423
1424
0
    return get_traces.list;
1425
0
}
1426
1427
PyObject *
1428
_PyTraceMalloc_GetObjectTraceback(PyObject *obj)
1429
/*[clinic end generated code: output=41ee0553a658b0aa input=29495f1b21c53212]*/
1430
0
{
1431
0
    PyTypeObject *type = Py_TYPE(obj);
1432
0
    const size_t presize = _PyType_PreHeaderSize(type);
1433
0
    uintptr_t ptr = (uintptr_t)((char *)obj - presize);
1434
0
    return _PyTraceMalloc_GetTraceback(DEFAULT_DOMAIN, ptr);
1435
0
}
1436
1437
int _PyTraceMalloc_GetTracebackLimit(void)
1438
0
{
1439
0
    return tracemalloc_config.max_nframe;
1440
0
}
1441
1442
size_t
1443
_PyTraceMalloc_GetMemory(void)
1444
0
{
1445
0
    TABLES_LOCK();
1446
0
    size_t size;
1447
0
    if (tracemalloc_config.tracing) {
1448
0
        size = _Py_hashtable_size(tracemalloc_tracebacks);
1449
0
        size += _Py_hashtable_size(tracemalloc_filenames);
1450
1451
0
        size += _Py_hashtable_size(tracemalloc_traces);
1452
0
        _Py_hashtable_foreach(tracemalloc_domains,
1453
0
                              tracemalloc_get_tracemalloc_memory_cb, &size);
1454
0
    }
1455
0
    else {
1456
0
        size = 0;
1457
0
    }
1458
0
    TABLES_UNLOCK();
1459
0
    return size;
1460
0
}
1461
1462
1463
PyObject *
1464
_PyTraceMalloc_GetTracedMemory(void)
1465
0
{
1466
0
    TABLES_LOCK();
1467
0
    Py_ssize_t traced, peak;
1468
0
    if (tracemalloc_config.tracing) {
1469
0
        traced = tracemalloc_traced_memory;
1470
0
        peak = tracemalloc_peak_traced_memory;
1471
0
    }
1472
0
    else {
1473
0
        traced = 0;
1474
0
        peak = 0;
1475
0
    }
1476
0
    TABLES_UNLOCK();
1477
1478
0
    return Py_BuildValue("nn", traced, peak);
1479
0
}
1480
1481
void
1482
_PyTraceMalloc_ResetPeak(void)
1483
0
{
1484
0
    TABLES_LOCK();
1485
0
    if (tracemalloc_config.tracing) {
1486
0
        tracemalloc_peak_traced_memory = tracemalloc_traced_memory;
1487
0
    }
1488
0
    TABLES_UNLOCK();
1489
0
}