Coverage Report

Created: 2025-07-11 06:59

/src/Python-3.8.3/Modules/_tracemalloc.c
Line
Count
Source (jump to first uncovered line)
1
#include "Python.h"
2
#include "pycore_traceback.h"
3
#include "hashtable.h"
4
#include "frameobject.h"
5
#include "pythread.h"
6
#include "osdefs.h"
7
8
#include "clinic/_tracemalloc.c.h"
9
/*[clinic input]
10
module _tracemalloc
11
[clinic start generated code]*/
12
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=708a98302fc46e5f]*/
13
14
/* Trace memory blocks allocated by PyMem_RawMalloc() */
15
#define TRACE_RAW_MALLOC
16
17
/* Forward declaration */
18
static void tracemalloc_stop(void);
19
static void* raw_malloc(size_t size);
20
static void raw_free(void *ptr);
21
22
#ifdef Py_DEBUG
23
#  define TRACE_DEBUG
24
#endif
25
26
/* Protected by the GIL */
27
static struct {
28
    PyMemAllocatorEx mem;
29
    PyMemAllocatorEx raw;
30
    PyMemAllocatorEx obj;
31
} allocators;
32
33
34
#if defined(TRACE_RAW_MALLOC)
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
static PyThread_type_lock tables_lock;
39
0
#  define TABLES_LOCK() PyThread_acquire_lock(tables_lock, 1)
40
0
#  define TABLES_UNLOCK() PyThread_release_lock(tables_lock)
41
#else
42
   /* variables are protected by the GIL */
43
#  define TABLES_LOCK()
44
#  define TABLES_UNLOCK()
45
#endif
46
47
48
0
#define DEFAULT_DOMAIN 0
49
50
/* Pack the frame_t structure to reduce the memory footprint. */
51
typedef struct
52
#ifdef __GNUC__
53
__attribute__((packed))
54
#endif
55
{
56
    uintptr_t ptr;
57
    unsigned int domain;
58
} pointer_t;
59
60
/* Pack the frame_t structure to reduce the memory footprint on 64-bit
61
   architectures: 12 bytes instead of 16. */
62
typedef struct
63
#ifdef __GNUC__
64
__attribute__((packed))
65
#elif defined(_MSC_VER)
66
#pragma pack(push, 4)
67
#endif
68
{
69
    /* filename cannot be NULL: "<unknown>" is used if the Python frame
70
       filename is NULL */
71
    PyObject *filename;
72
    unsigned int lineno;
73
} frame_t;
74
#ifdef _MSC_VER
75
#pragma pack(pop)
76
#endif
77
78
79
typedef struct {
80
    Py_uhash_t hash;
81
    int nframe;
82
    frame_t frames[1];
83
} traceback_t;
84
85
#define TRACEBACK_SIZE(NFRAME) \
86
0
        (sizeof(traceback_t) + sizeof(frame_t) * (NFRAME - 1))
87
88
#define MAX_NFRAME \
89
0
        ((INT_MAX - (int)sizeof(traceback_t)) / (int)sizeof(frame_t) + 1)
90
91
92
static PyObject *unknown_filename = NULL;
93
static traceback_t tracemalloc_empty_traceback;
94
95
/* Trace of a memory block */
96
typedef struct {
97
    /* Size of the memory block in bytes */
98
    size_t size;
99
100
    /* Traceback where the memory block was allocated */
101
    traceback_t *traceback;
102
} trace_t;
103
104
105
/* Size in bytes of currently traced memory.
106
   Protected by TABLES_LOCK(). */
107
static size_t tracemalloc_traced_memory = 0;
108
109
/* Peak size in bytes of traced memory.
110
   Protected by TABLES_LOCK(). */
111
static size_t tracemalloc_peak_traced_memory = 0;
112
113
/* Hash table used as a set to intern filenames:
114
   PyObject* => PyObject*.
115
   Protected by the GIL */
116
static _Py_hashtable_t *tracemalloc_filenames = NULL;
117
118
/* Buffer to store a new traceback in traceback_new().
119
   Protected by the GIL. */
120
static traceback_t *tracemalloc_traceback = NULL;
121
122
/* Hash table used as a set to intern tracebacks:
123
   traceback_t* => traceback_t*
124
   Protected by the GIL */
125
static _Py_hashtable_t *tracemalloc_tracebacks = NULL;
126
127
/* pointer (void*) => trace (trace_t).
128
   Protected by TABLES_LOCK(). */
129
static _Py_hashtable_t *tracemalloc_traces = NULL;
130
131
132
#ifdef TRACE_DEBUG
133
static void
134
tracemalloc_error(const char *format, ...)
135
{
136
    va_list ap;
137
    fprintf(stderr, "tracemalloc: ");
138
    va_start(ap, format);
139
    vfprintf(stderr, format, ap);
140
    va_end(ap);
141
    fprintf(stderr, "\n");
142
    fflush(stderr);
143
}
144
#endif
145
146
147
#if defined(TRACE_RAW_MALLOC)
148
#define REENTRANT_THREADLOCAL
149
150
static Py_tss_t tracemalloc_reentrant_key = Py_tss_NEEDS_INIT;
151
152
/* Any non-NULL pointer can be used */
153
0
#define REENTRANT Py_True
154
155
static int
156
get_reentrant(void)
157
0
{
158
0
    void *ptr;
159
160
0
    assert(PyThread_tss_is_created(&tracemalloc_reentrant_key));
161
0
    ptr = PyThread_tss_get(&tracemalloc_reentrant_key);
162
0
    if (ptr != NULL) {
163
0
        assert(ptr == REENTRANT);
164
0
        return 1;
165
0
    }
166
0
    else
167
0
        return 0;
168
0
}
169
170
static void
171
set_reentrant(int reentrant)
172
0
{
173
0
    assert(reentrant == 0 || reentrant == 1);
174
0
    assert(PyThread_tss_is_created(&tracemalloc_reentrant_key));
175
176
0
    if (reentrant) {
177
0
        assert(!get_reentrant());
178
0
        PyThread_tss_set(&tracemalloc_reentrant_key, REENTRANT);
179
0
    }
180
0
    else {
181
0
        assert(get_reentrant());
182
0
        PyThread_tss_set(&tracemalloc_reentrant_key, NULL);
183
0
    }
184
0
}
185
186
#else
187
188
/* TRACE_RAW_MALLOC not defined: variable protected by the GIL */
189
static int tracemalloc_reentrant = 0;
190
191
static int
192
get_reentrant(void)
193
{
194
    return tracemalloc_reentrant;
195
}
196
197
static void
198
set_reentrant(int reentrant)
199
{
200
    assert(reentrant != tracemalloc_reentrant);
201
    tracemalloc_reentrant = reentrant;
202
}
203
#endif
204
205
206
static Py_uhash_t
207
hashtable_hash_pyobject(_Py_hashtable_t *ht, const void *pkey)
208
0
{
209
0
    PyObject *obj;
210
211
0
    _Py_HASHTABLE_READ_KEY(ht, pkey, obj);
212
0
    return PyObject_Hash(obj);
213
0
}
214
215
216
static int
217
hashtable_compare_unicode(_Py_hashtable_t *ht, const void *pkey,
218
                          const _Py_hashtable_entry_t *entry)
219
0
{
220
0
    PyObject *key1, *key2;
221
222
0
    _Py_HASHTABLE_READ_KEY(ht, pkey, key1);
223
0
    _Py_HASHTABLE_ENTRY_READ_KEY(ht, entry, key2);
224
225
0
    if (key1 != NULL && key2 != NULL)
226
0
        return (PyUnicode_Compare(key1, key2) == 0);
227
0
    else
228
0
        return key1 == key2;
229
0
}
230
231
232
static Py_uhash_t
233
hashtable_hash_pointer_t(_Py_hashtable_t *ht, const void *pkey)
234
0
{
235
0
    pointer_t ptr;
236
0
    Py_uhash_t hash;
237
238
0
    _Py_HASHTABLE_READ_KEY(ht, pkey, ptr);
239
240
0
    hash = (Py_uhash_t)_Py_HashPointer((void*)ptr.ptr);
241
0
    hash ^= ptr.domain;
242
0
    return hash;
243
0
}
244
245
246
static int
247
hashtable_compare_pointer_t(_Py_hashtable_t *ht, const void *pkey,
248
                            const _Py_hashtable_entry_t *entry)
249
0
{
250
0
    pointer_t ptr1, ptr2;
251
252
0
    _Py_HASHTABLE_READ_KEY(ht, pkey, ptr1);
253
0
    _Py_HASHTABLE_ENTRY_READ_KEY(ht, entry, ptr2);
254
255
    /* compare pointer before domain, because pointer is more likely to be
256
       different */
257
0
    return (ptr1.ptr == ptr2.ptr && ptr1.domain == ptr2.domain);
258
259
0
}
260
261
262
static _Py_hashtable_t *
263
hashtable_new(size_t key_size, size_t data_size,
264
              _Py_hashtable_hash_func hash_func,
265
              _Py_hashtable_compare_func compare_func)
266
0
{
267
0
    _Py_hashtable_allocator_t hashtable_alloc = {malloc, free};
268
0
    return _Py_hashtable_new_full(key_size, data_size, 0,
269
0
                                  hash_func, compare_func,
270
0
                                  &hashtable_alloc);
271
0
}
272
273
274
static void*
275
raw_malloc(size_t size)
276
0
{
277
0
    return allocators.raw.malloc(allocators.raw.ctx, size);
278
0
}
279
280
static void
281
raw_free(void *ptr)
282
0
{
283
0
    allocators.raw.free(allocators.raw.ctx, ptr);
284
0
}
285
286
287
static Py_uhash_t
288
hashtable_hash_traceback(_Py_hashtable_t *ht, const void *pkey)
289
0
{
290
0
    traceback_t *traceback;
291
292
0
    _Py_HASHTABLE_READ_KEY(ht, pkey, traceback);
293
0
    return traceback->hash;
294
0
}
295
296
297
static int
298
hashtable_compare_traceback(_Py_hashtable_t *ht, const void *pkey,
299
                            const _Py_hashtable_entry_t *entry)
300
0
{
301
0
    traceback_t *traceback1, *traceback2;
302
0
    const frame_t *frame1, *frame2;
303
0
    int i;
304
305
0
    _Py_HASHTABLE_READ_KEY(ht, pkey, traceback1);
306
0
    _Py_HASHTABLE_ENTRY_READ_KEY(ht, entry, traceback2);
307
308
0
    if (traceback1->nframe != traceback2->nframe)
309
0
        return 0;
310
311
0
    for (i=0; i < traceback1->nframe; i++) {
312
0
        frame1 = &traceback1->frames[i];
313
0
        frame2 = &traceback2->frames[i];
314
315
0
        if (frame1->lineno != frame2->lineno)
316
0
            return 0;
317
318
0
        if (frame1->filename != frame2->filename) {
319
0
            assert(PyUnicode_Compare(frame1->filename, frame2->filename) != 0);
320
0
            return 0;
321
0
        }
322
0
    }
323
0
    return 1;
324
0
}
325
326
327
static void
328
tracemalloc_get_frame(PyFrameObject *pyframe, frame_t *frame)
329
0
{
330
0
    PyCodeObject *code;
331
0
    PyObject *filename;
332
0
    _Py_hashtable_entry_t *entry;
333
0
    int lineno;
334
335
0
    frame->filename = unknown_filename;
336
0
    lineno = PyFrame_GetLineNumber(pyframe);
337
0
    if (lineno < 0)
338
0
        lineno = 0;
339
0
    frame->lineno = (unsigned int)lineno;
340
341
0
    code = pyframe->f_code;
342
0
    if (code == NULL) {
343
#ifdef TRACE_DEBUG
344
        tracemalloc_error("failed to get the code object of the frame");
345
#endif
346
0
        return;
347
0
    }
348
349
0
    if (code->co_filename == NULL) {
350
#ifdef TRACE_DEBUG
351
        tracemalloc_error("failed to get the filename of the code object");
352
#endif
353
0
        return;
354
0
    }
355
356
0
    filename = code->co_filename;
357
0
    assert(filename != NULL);
358
0
    if (filename == NULL)
359
0
        return;
360
361
0
    if (!PyUnicode_Check(filename)) {
362
#ifdef TRACE_DEBUG
363
        tracemalloc_error("filename is not a unicode string");
364
#endif
365
0
        return;
366
0
    }
367
0
    if (!PyUnicode_IS_READY(filename)) {
368
        /* Don't make a Unicode string ready to avoid reentrant calls
369
           to tracemalloc_malloc() or tracemalloc_realloc() */
370
#ifdef TRACE_DEBUG
371
        tracemalloc_error("filename is not a ready unicode string");
372
#endif
373
0
        return;
374
0
    }
375
376
    /* intern the filename */
377
0
    entry = _Py_HASHTABLE_GET_ENTRY(tracemalloc_filenames, filename);
378
0
    if (entry != NULL) {
379
0
        _Py_HASHTABLE_ENTRY_READ_KEY(tracemalloc_filenames, entry, filename);
380
0
    }
381
0
    else {
382
        /* tracemalloc_filenames is responsible to keep a reference
383
           to the filename */
384
0
        Py_INCREF(filename);
385
0
        if (_Py_HASHTABLE_SET_NODATA(tracemalloc_filenames, filename) < 0) {
386
0
            Py_DECREF(filename);
387
#ifdef TRACE_DEBUG
388
            tracemalloc_error("failed to intern the filename");
389
#endif
390
0
            return;
391
0
        }
392
0
    }
393
394
    /* the tracemalloc_filenames table keeps a reference to the filename */
395
0
    frame->filename = filename;
396
0
}
397
398
399
static Py_uhash_t
400
traceback_hash(traceback_t *traceback)
401
0
{
402
    /* code based on tuplehash() of Objects/tupleobject.c */
403
0
    Py_uhash_t x, y;  /* Unsigned for defined overflow behavior. */
404
0
    int len = traceback->nframe;
405
0
    Py_uhash_t mult = _PyHASH_MULTIPLIER;
406
0
    frame_t *frame;
407
408
0
    x = 0x345678UL;
409
0
    frame = traceback->frames;
410
0
    while (--len >= 0) {
411
0
        y = (Py_uhash_t)PyObject_Hash(frame->filename);
412
0
        y ^= (Py_uhash_t)frame->lineno;
413
0
        frame++;
414
415
0
        x = (x ^ y) * mult;
416
        /* the cast might truncate len; that doesn't change hash stability */
417
0
        mult += (Py_uhash_t)(82520UL + len + len);
418
0
    }
419
0
    x += 97531UL;
420
0
    return x;
421
0
}
422
423
424
static void
425
traceback_get_frames(traceback_t *traceback)
426
0
{
427
0
    PyThreadState *tstate;
428
0
    PyFrameObject *pyframe;
429
430
0
    tstate = PyGILState_GetThisThreadState();
431
0
    if (tstate == NULL) {
432
#ifdef TRACE_DEBUG
433
        tracemalloc_error("failed to get the current thread state");
434
#endif
435
0
        return;
436
0
    }
437
438
0
    for (pyframe = tstate->frame; pyframe != NULL; pyframe = pyframe->f_back) {
439
0
        tracemalloc_get_frame(pyframe, &traceback->frames[traceback->nframe]);
440
0
        assert(traceback->frames[traceback->nframe].filename != NULL);
441
0
        traceback->nframe++;
442
0
        if (traceback->nframe == _Py_tracemalloc_config.max_nframe)
443
0
            break;
444
0
    }
445
0
}
446
447
448
static traceback_t *
449
traceback_new(void)
450
0
{
451
0
    traceback_t *traceback;
452
0
    _Py_hashtable_entry_t *entry;
453
454
0
    assert(PyGILState_Check());
455
456
    /* get frames */
457
0
    traceback = tracemalloc_traceback;
458
0
    traceback->nframe = 0;
459
0
    traceback_get_frames(traceback);
460
0
    if (traceback->nframe == 0)
461
0
        return &tracemalloc_empty_traceback;
462
0
    traceback->hash = traceback_hash(traceback);
463
464
    /* intern the traceback */
465
0
    entry = _Py_HASHTABLE_GET_ENTRY(tracemalloc_tracebacks, traceback);
466
0
    if (entry != NULL) {
467
0
        _Py_HASHTABLE_ENTRY_READ_KEY(tracemalloc_tracebacks, entry, traceback);
468
0
    }
469
0
    else {
470
0
        traceback_t *copy;
471
0
        size_t traceback_size;
472
473
0
        traceback_size = TRACEBACK_SIZE(traceback->nframe);
474
475
0
        copy = raw_malloc(traceback_size);
476
0
        if (copy == NULL) {
477
#ifdef TRACE_DEBUG
478
            tracemalloc_error("failed to intern the traceback: malloc failed");
479
#endif
480
0
            return NULL;
481
0
        }
482
0
        memcpy(copy, traceback, traceback_size);
483
484
0
        if (_Py_HASHTABLE_SET_NODATA(tracemalloc_tracebacks, copy) < 0) {
485
0
            raw_free(copy);
486
#ifdef TRACE_DEBUG
487
            tracemalloc_error("failed to intern the traceback: putdata failed");
488
#endif
489
0
            return NULL;
490
0
        }
491
0
        traceback = copy;
492
0
    }
493
0
    return traceback;
494
0
}
495
496
497
static int
498
tracemalloc_use_domain_cb(_Py_hashtable_t *old_traces,
499
                           _Py_hashtable_entry_t *entry, void *user_data)
500
0
{
501
0
    uintptr_t ptr;
502
0
    pointer_t key;
503
0
    _Py_hashtable_t *new_traces = (_Py_hashtable_t *)user_data;
504
0
    const void *pdata = _Py_HASHTABLE_ENTRY_PDATA(old_traces, entry);
505
506
0
    _Py_HASHTABLE_ENTRY_READ_KEY(old_traces, entry, ptr);
507
0
    key.ptr = ptr;
508
0
    key.domain = DEFAULT_DOMAIN;
509
510
0
    return _Py_hashtable_set(new_traces,
511
0
                             sizeof(key), &key,
512
0
                             old_traces->data_size, pdata);
513
0
}
514
515
516
/* Convert tracemalloc_traces from compact key (uintptr_t) to pointer_t key.
517
 * Return 0 on success, -1 on error. */
518
static int
519
tracemalloc_use_domain(void)
520
0
{
521
0
    _Py_hashtable_t *new_traces = NULL;
522
523
0
    assert(!_Py_tracemalloc_config.use_domain);
524
525
0
    new_traces = hashtable_new(sizeof(pointer_t),
526
0
                               sizeof(trace_t),
527
0
                               hashtable_hash_pointer_t,
528
0
                               hashtable_compare_pointer_t);
529
0
    if (new_traces == NULL) {
530
0
        return -1;
531
0
    }
532
533
0
    if (_Py_hashtable_foreach(tracemalloc_traces, tracemalloc_use_domain_cb,
534
0
                              new_traces) < 0)
535
0
    {
536
0
        _Py_hashtable_destroy(new_traces);
537
0
        return -1;
538
0
    }
539
540
0
    _Py_hashtable_destroy(tracemalloc_traces);
541
0
    tracemalloc_traces = new_traces;
542
543
0
    _Py_tracemalloc_config.use_domain = 1;
544
545
0
    return 0;
546
0
}
547
548
549
static void
550
tracemalloc_remove_trace(unsigned int domain, uintptr_t ptr)
551
0
{
552
0
    trace_t trace;
553
0
    int removed;
554
555
0
    assert(_Py_tracemalloc_config.tracing);
556
557
0
    if (_Py_tracemalloc_config.use_domain) {
558
0
        pointer_t key = {ptr, domain};
559
0
        removed = _Py_HASHTABLE_POP(tracemalloc_traces, key, trace);
560
0
    }
561
0
    else {
562
0
        removed = _Py_HASHTABLE_POP(tracemalloc_traces, ptr, trace);
563
0
    }
564
0
    if (!removed) {
565
0
        return;
566
0
    }
567
568
0
    assert(tracemalloc_traced_memory >= trace.size);
569
0
    tracemalloc_traced_memory -= trace.size;
570
0
}
571
572
#define REMOVE_TRACE(ptr) \
573
0
            tracemalloc_remove_trace(DEFAULT_DOMAIN, (uintptr_t)(ptr))
574
575
576
static int
577
tracemalloc_add_trace(unsigned int domain, uintptr_t ptr,
578
                      size_t size)
579
0
{
580
0
    pointer_t key = {ptr, domain};
581
0
    traceback_t *traceback;
582
0
    trace_t trace;
583
0
    _Py_hashtable_entry_t* entry;
584
0
    int res;
585
586
0
    assert(_Py_tracemalloc_config.tracing);
587
588
0
    traceback = traceback_new();
589
0
    if (traceback == NULL) {
590
0
        return -1;
591
0
    }
592
593
0
    if (!_Py_tracemalloc_config.use_domain && domain != DEFAULT_DOMAIN) {
594
        /* first trace using a non-zero domain whereas traces use compact
595
           (uintptr_t) keys: switch to pointer_t keys. */
596
0
        if (tracemalloc_use_domain() < 0) {
597
0
            return -1;
598
0
        }
599
0
    }
600
601
0
    if (_Py_tracemalloc_config.use_domain) {
602
0
        entry = _Py_HASHTABLE_GET_ENTRY(tracemalloc_traces, key);
603
0
    }
604
0
    else {
605
0
        entry = _Py_HASHTABLE_GET_ENTRY(tracemalloc_traces, ptr);
606
0
    }
607
608
0
    if (entry != NULL) {
609
        /* the memory block is already tracked */
610
0
        _Py_HASHTABLE_ENTRY_READ_DATA(tracemalloc_traces, entry, trace);
611
0
        assert(tracemalloc_traced_memory >= trace.size);
612
0
        tracemalloc_traced_memory -= trace.size;
613
614
0
        trace.size = size;
615
0
        trace.traceback = traceback;
616
0
        _Py_HASHTABLE_ENTRY_WRITE_DATA(tracemalloc_traces, entry, trace);
617
0
    }
618
0
    else {
619
0
        trace.size = size;
620
0
        trace.traceback = traceback;
621
622
0
        if (_Py_tracemalloc_config.use_domain) {
623
0
            res = _Py_HASHTABLE_SET(tracemalloc_traces, key, trace);
624
0
        }
625
0
        else {
626
0
            res = _Py_HASHTABLE_SET(tracemalloc_traces, ptr, trace);
627
0
        }
628
0
        if (res != 0) {
629
0
            return res;
630
0
        }
631
0
    }
632
633
0
    assert(tracemalloc_traced_memory <= SIZE_MAX - size);
634
0
    tracemalloc_traced_memory += size;
635
0
    if (tracemalloc_traced_memory > tracemalloc_peak_traced_memory)
636
0
        tracemalloc_peak_traced_memory = tracemalloc_traced_memory;
637
0
    return 0;
638
0
}
639
640
#define ADD_TRACE(ptr, size) \
641
0
            tracemalloc_add_trace(DEFAULT_DOMAIN, (uintptr_t)(ptr), size)
642
643
644
static void*
645
tracemalloc_alloc(int use_calloc, void *ctx, size_t nelem, size_t elsize)
646
0
{
647
0
    PyMemAllocatorEx *alloc = (PyMemAllocatorEx *)ctx;
648
0
    void *ptr;
649
650
0
    assert(elsize == 0 || nelem <= SIZE_MAX / elsize);
651
652
0
    if (use_calloc)
653
0
        ptr = alloc->calloc(alloc->ctx, nelem, elsize);
654
0
    else
655
0
        ptr = alloc->malloc(alloc->ctx, nelem * elsize);
656
0
    if (ptr == NULL)
657
0
        return NULL;
658
659
0
    TABLES_LOCK();
660
0
    if (ADD_TRACE(ptr, nelem * elsize) < 0) {
661
        /* Failed to allocate a trace for the new memory block */
662
0
        TABLES_UNLOCK();
663
0
        alloc->free(alloc->ctx, ptr);
664
0
        return NULL;
665
0
    }
666
0
    TABLES_UNLOCK();
667
0
    return ptr;
668
0
}
669
670
671
static void*
672
tracemalloc_realloc(void *ctx, void *ptr, size_t new_size)
673
0
{
674
0
    PyMemAllocatorEx *alloc = (PyMemAllocatorEx *)ctx;
675
0
    void *ptr2;
676
677
0
    ptr2 = alloc->realloc(alloc->ctx, ptr, new_size);
678
0
    if (ptr2 == NULL)
679
0
        return NULL;
680
681
0
    if (ptr != NULL) {
682
        /* an existing memory block has been resized */
683
684
0
        TABLES_LOCK();
685
686
        /* tracemalloc_add_trace() updates the trace if there is already
687
           a trace at address (domain, ptr2) */
688
0
        if (ptr2 != ptr) {
689
0
            REMOVE_TRACE(ptr);
690
0
        }
691
692
0
        if (ADD_TRACE(ptr2, new_size) < 0) {
693
            /* Memory allocation failed. The error cannot be reported to
694
               the caller, because realloc() may already have shrunk the
695
               memory block and so removed bytes.
696
697
               This case is very unlikely: a hash entry has just been
698
               released, so the hash table should have at least one free entry.
699
700
               The GIL and the table lock ensures that only one thread is
701
               allocating memory. */
702
0
            Py_UNREACHABLE();
703
0
        }
704
0
        TABLES_UNLOCK();
705
0
    }
706
0
    else {
707
        /* new allocation */
708
709
0
        TABLES_LOCK();
710
0
        if (ADD_TRACE(ptr2, new_size) < 0) {
711
            /* Failed to allocate a trace for the new memory block */
712
0
            TABLES_UNLOCK();
713
0
            alloc->free(alloc->ctx, ptr2);
714
0
            return NULL;
715
0
        }
716
0
        TABLES_UNLOCK();
717
0
    }
718
0
    return ptr2;
719
0
}
720
721
722
static void
723
tracemalloc_free(void *ctx, void *ptr)
724
0
{
725
0
    PyMemAllocatorEx *alloc = (PyMemAllocatorEx *)ctx;
726
727
0
    if (ptr == NULL)
728
0
        return;
729
730
     /* GIL cannot be locked in PyMem_RawFree() because it would introduce
731
        a deadlock in PyThreadState_DeleteCurrent(). */
732
733
0
    alloc->free(alloc->ctx, ptr);
734
735
0
    TABLES_LOCK();
736
0
    REMOVE_TRACE(ptr);
737
0
    TABLES_UNLOCK();
738
0
}
739
740
741
static void*
742
tracemalloc_alloc_gil(int use_calloc, void *ctx, size_t nelem, size_t elsize)
743
0
{
744
0
    void *ptr;
745
746
0
    if (get_reentrant()) {
747
0
        PyMemAllocatorEx *alloc = (PyMemAllocatorEx *)ctx;
748
0
        if (use_calloc)
749
0
            return alloc->calloc(alloc->ctx, nelem, elsize);
750
0
        else
751
0
            return alloc->malloc(alloc->ctx, nelem * elsize);
752
0
    }
753
754
    /* Ignore reentrant call. PyObjet_Malloc() calls PyMem_Malloc() for
755
       allocations larger than 512 bytes, don't trace the same memory
756
       allocation twice. */
757
0
    set_reentrant(1);
758
759
0
    ptr = tracemalloc_alloc(use_calloc, ctx, nelem, elsize);
760
761
0
    set_reentrant(0);
762
0
    return ptr;
763
0
}
764
765
766
static void*
767
tracemalloc_malloc_gil(void *ctx, size_t size)
768
0
{
769
0
    return tracemalloc_alloc_gil(0, ctx, 1, size);
770
0
}
771
772
773
static void*
774
tracemalloc_calloc_gil(void *ctx, size_t nelem, size_t elsize)
775
0
{
776
0
    return tracemalloc_alloc_gil(1, ctx, nelem, elsize);
777
0
}
778
779
780
static void*
781
tracemalloc_realloc_gil(void *ctx, void *ptr, size_t new_size)
782
0
{
783
0
    void *ptr2;
784
785
0
    if (get_reentrant()) {
786
        /* Reentrant call to PyMem_Realloc() and PyMem_RawRealloc().
787
           Example: PyMem_RawRealloc() is called internally by pymalloc
788
           (_PyObject_Malloc() and  _PyObject_Realloc()) to allocate a new
789
           arena (new_arena()). */
790
0
        PyMemAllocatorEx *alloc = (PyMemAllocatorEx *)ctx;
791
792
0
        ptr2 = alloc->realloc(alloc->ctx, ptr, new_size);
793
0
        if (ptr2 != NULL && ptr != NULL) {
794
0
            TABLES_LOCK();
795
0
            REMOVE_TRACE(ptr);
796
0
            TABLES_UNLOCK();
797
0
        }
798
0
        return ptr2;
799
0
    }
800
801
    /* Ignore reentrant call. PyObjet_Realloc() calls PyMem_Realloc() for
802
       allocations larger than 512 bytes. Don't trace the same memory
803
       allocation twice. */
804
0
    set_reentrant(1);
805
806
0
    ptr2 = tracemalloc_realloc(ctx, ptr, new_size);
807
808
0
    set_reentrant(0);
809
0
    return ptr2;
810
0
}
811
812
813
#ifdef TRACE_RAW_MALLOC
814
static void*
815
tracemalloc_raw_alloc(int use_calloc, void *ctx, size_t nelem, size_t elsize)
816
0
{
817
0
    PyGILState_STATE gil_state;
818
0
    void *ptr;
819
820
0
    if (get_reentrant()) {
821
0
        PyMemAllocatorEx *alloc = (PyMemAllocatorEx *)ctx;
822
0
        if (use_calloc)
823
0
            return alloc->calloc(alloc->ctx, nelem, elsize);
824
0
        else
825
0
            return alloc->malloc(alloc->ctx, nelem * elsize);
826
0
    }
827
828
    /* Ignore reentrant call. PyGILState_Ensure() may call PyMem_RawMalloc()
829
       indirectly which would call PyGILState_Ensure() if reentrant are not
830
       disabled. */
831
0
    set_reentrant(1);
832
833
0
    gil_state = PyGILState_Ensure();
834
0
    ptr = tracemalloc_alloc(use_calloc, ctx, nelem, elsize);
835
0
    PyGILState_Release(gil_state);
836
837
0
    set_reentrant(0);
838
0
    return ptr;
839
0
}
840
841
842
static void*
843
tracemalloc_raw_malloc(void *ctx, size_t size)
844
0
{
845
0
    return tracemalloc_raw_alloc(0, ctx, 1, size);
846
0
}
847
848
849
static void*
850
tracemalloc_raw_calloc(void *ctx, size_t nelem, size_t elsize)
851
0
{
852
0
    return tracemalloc_raw_alloc(1, ctx, nelem, elsize);
853
0
}
854
855
856
static void*
857
tracemalloc_raw_realloc(void *ctx, void *ptr, size_t new_size)
858
0
{
859
0
    PyGILState_STATE gil_state;
860
0
    void *ptr2;
861
862
0
    if (get_reentrant()) {
863
        /* Reentrant call to PyMem_RawRealloc(). */
864
0
        PyMemAllocatorEx *alloc = (PyMemAllocatorEx *)ctx;
865
866
0
        ptr2 = alloc->realloc(alloc->ctx, ptr, new_size);
867
868
0
        if (ptr2 != NULL && ptr != NULL) {
869
0
            TABLES_LOCK();
870
0
            REMOVE_TRACE(ptr);
871
0
            TABLES_UNLOCK();
872
0
        }
873
0
        return ptr2;
874
0
    }
875
876
    /* Ignore reentrant call. PyGILState_Ensure() may call PyMem_RawMalloc()
877
       indirectly which would call PyGILState_Ensure() if reentrant calls are
878
       not disabled. */
879
0
    set_reentrant(1);
880
881
0
    gil_state = PyGILState_Ensure();
882
0
    ptr2 = tracemalloc_realloc(ctx, ptr, new_size);
883
0
    PyGILState_Release(gil_state);
884
885
0
    set_reentrant(0);
886
0
    return ptr2;
887
0
}
888
#endif   /* TRACE_RAW_MALLOC */
889
890
891
static int
892
tracemalloc_clear_filename(_Py_hashtable_t *ht, _Py_hashtable_entry_t *entry,
893
                           void *user_data)
894
0
{
895
0
    PyObject *filename;
896
897
0
    _Py_HASHTABLE_ENTRY_READ_KEY(ht, entry, filename);
898
0
    Py_DECREF(filename);
899
0
    return 0;
900
0
}
901
902
903
static int
904
traceback_free_traceback(_Py_hashtable_t *ht, _Py_hashtable_entry_t *entry,
905
                         void *user_data)
906
0
{
907
0
    traceback_t *traceback;
908
909
0
    _Py_HASHTABLE_ENTRY_READ_KEY(ht, entry, traceback);
910
0
    raw_free(traceback);
911
0
    return 0;
912
0
}
913
914
915
/* reentrant flag must be set to call this function and GIL must be held */
916
static void
917
tracemalloc_clear_traces(void)
918
0
{
919
    /* The GIL protects variables againt concurrent access */
920
0
    assert(PyGILState_Check());
921
922
0
    TABLES_LOCK();
923
0
    _Py_hashtable_clear(tracemalloc_traces);
924
0
    tracemalloc_traced_memory = 0;
925
0
    tracemalloc_peak_traced_memory = 0;
926
0
    TABLES_UNLOCK();
927
928
0
    _Py_hashtable_foreach(tracemalloc_tracebacks, traceback_free_traceback, NULL);
929
0
    _Py_hashtable_clear(tracemalloc_tracebacks);
930
931
0
    _Py_hashtable_foreach(tracemalloc_filenames, tracemalloc_clear_filename, NULL);
932
0
    _Py_hashtable_clear(tracemalloc_filenames);
933
0
}
934
935
936
static int
937
tracemalloc_init(void)
938
0
{
939
0
    if (_Py_tracemalloc_config.initialized == TRACEMALLOC_FINALIZED) {
940
0
        PyErr_SetString(PyExc_RuntimeError,
941
0
                        "the tracemalloc module has been unloaded");
942
0
        return -1;
943
0
    }
944
945
0
    if (_Py_tracemalloc_config.initialized == TRACEMALLOC_INITIALIZED)
946
0
        return 0;
947
948
0
    PyMem_GetAllocator(PYMEM_DOMAIN_RAW, &allocators.raw);
949
950
0
#ifdef REENTRANT_THREADLOCAL
951
0
    if (PyThread_tss_create(&tracemalloc_reentrant_key) != 0) {
952
#ifdef MS_WINDOWS
953
        PyErr_SetFromWindowsErr(0);
954
#else
955
0
        PyErr_SetFromErrno(PyExc_OSError);
956
0
#endif
957
0
        return -1;
958
0
    }
959
0
#endif
960
961
0
#if defined(TRACE_RAW_MALLOC)
962
0
    if (tables_lock == NULL) {
963
0
        tables_lock = PyThread_allocate_lock();
964
0
        if (tables_lock == NULL) {
965
0
            PyErr_SetString(PyExc_RuntimeError, "cannot allocate lock");
966
0
            return -1;
967
0
        }
968
0
    }
969
0
#endif
970
971
0
    tracemalloc_filenames = hashtable_new(sizeof(PyObject *), 0,
972
0
                                          hashtable_hash_pyobject,
973
0
                                          hashtable_compare_unicode);
974
975
0
    tracemalloc_tracebacks = hashtable_new(sizeof(traceback_t *), 0,
976
0
                                           hashtable_hash_traceback,
977
0
                                           hashtable_compare_traceback);
978
979
0
    if (_Py_tracemalloc_config.use_domain) {
980
0
        tracemalloc_traces = hashtable_new(sizeof(pointer_t),
981
0
                                           sizeof(trace_t),
982
0
                                           hashtable_hash_pointer_t,
983
0
                                           hashtable_compare_pointer_t);
984
0
    }
985
0
    else {
986
0
        tracemalloc_traces = hashtable_new(sizeof(uintptr_t),
987
0
                                           sizeof(trace_t),
988
0
                                           _Py_hashtable_hash_ptr,
989
0
                                           _Py_hashtable_compare_direct);
990
0
    }
991
992
0
    if (tracemalloc_filenames == NULL || tracemalloc_tracebacks == NULL
993
0
       || tracemalloc_traces == NULL) {
994
0
        PyErr_NoMemory();
995
0
        return -1;
996
0
    }
997
998
0
    unknown_filename = PyUnicode_FromString("<unknown>");
999
0
    if (unknown_filename == NULL)
1000
0
        return -1;
1001
0
    PyUnicode_InternInPlace(&unknown_filename);
1002
1003
0
    tracemalloc_empty_traceback.nframe = 1;
1004
    /* borrowed reference */
1005
0
    tracemalloc_empty_traceback.frames[0].filename = unknown_filename;
1006
0
    tracemalloc_empty_traceback.frames[0].lineno = 0;
1007
0
    tracemalloc_empty_traceback.hash = traceback_hash(&tracemalloc_empty_traceback);
1008
1009
0
    _Py_tracemalloc_config.initialized = TRACEMALLOC_INITIALIZED;
1010
0
    return 0;
1011
0
}
1012
1013
1014
static void
1015
tracemalloc_deinit(void)
1016
0
{
1017
0
    if (_Py_tracemalloc_config.initialized != TRACEMALLOC_INITIALIZED)
1018
0
        return;
1019
0
    _Py_tracemalloc_config.initialized = TRACEMALLOC_FINALIZED;
1020
1021
0
    tracemalloc_stop();
1022
1023
    /* destroy hash tables */
1024
0
    _Py_hashtable_destroy(tracemalloc_tracebacks);
1025
0
    _Py_hashtable_destroy(tracemalloc_filenames);
1026
0
    _Py_hashtable_destroy(tracemalloc_traces);
1027
1028
0
#if defined(TRACE_RAW_MALLOC)
1029
0
    if (tables_lock != NULL) {
1030
0
        PyThread_free_lock(tables_lock);
1031
0
        tables_lock = NULL;
1032
0
    }
1033
0
#endif
1034
1035
0
#ifdef REENTRANT_THREADLOCAL
1036
0
    PyThread_tss_delete(&tracemalloc_reentrant_key);
1037
0
#endif
1038
1039
0
    Py_XDECREF(unknown_filename);
1040
0
}
1041
1042
1043
static int
1044
tracemalloc_start(int max_nframe)
1045
0
{
1046
0
    PyMemAllocatorEx alloc;
1047
0
    size_t size;
1048
1049
0
    if (max_nframe < 1 || max_nframe > MAX_NFRAME) {
1050
0
        PyErr_Format(PyExc_ValueError,
1051
0
                     "the number of frames must be in range [1; %i]",
1052
0
                     (int)MAX_NFRAME);
1053
0
        return -1;
1054
0
    }
1055
1056
0
    if (tracemalloc_init() < 0) {
1057
0
        return -1;
1058
0
    }
1059
1060
0
    if (_Py_tracemalloc_config.tracing) {
1061
        /* hook already installed: do nothing */
1062
0
        return 0;
1063
0
    }
1064
1065
0
    assert(1 <= max_nframe && max_nframe <= MAX_NFRAME);
1066
0
    _Py_tracemalloc_config.max_nframe = max_nframe;
1067
1068
    /* allocate a buffer to store a new traceback */
1069
0
    size = TRACEBACK_SIZE(max_nframe);
1070
0
    assert(tracemalloc_traceback == NULL);
1071
0
    tracemalloc_traceback = raw_malloc(size);
1072
0
    if (tracemalloc_traceback == NULL) {
1073
0
        PyErr_NoMemory();
1074
0
        return -1;
1075
0
    }
1076
1077
0
#ifdef TRACE_RAW_MALLOC
1078
0
    alloc.malloc = tracemalloc_raw_malloc;
1079
0
    alloc.calloc = tracemalloc_raw_calloc;
1080
0
    alloc.realloc = tracemalloc_raw_realloc;
1081
0
    alloc.free = tracemalloc_free;
1082
1083
0
    alloc.ctx = &allocators.raw;
1084
0
    PyMem_GetAllocator(PYMEM_DOMAIN_RAW, &allocators.raw);
1085
0
    PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &alloc);
1086
0
#endif
1087
1088
0
    alloc.malloc = tracemalloc_malloc_gil;
1089
0
    alloc.calloc = tracemalloc_calloc_gil;
1090
0
    alloc.realloc = tracemalloc_realloc_gil;
1091
0
    alloc.free = tracemalloc_free;
1092
1093
0
    alloc.ctx = &allocators.mem;
1094
0
    PyMem_GetAllocator(PYMEM_DOMAIN_MEM, &allocators.mem);
1095
0
    PyMem_SetAllocator(PYMEM_DOMAIN_MEM, &alloc);
1096
1097
0
    alloc.ctx = &allocators.obj;
1098
0
    PyMem_GetAllocator(PYMEM_DOMAIN_OBJ, &allocators.obj);
1099
0
    PyMem_SetAllocator(PYMEM_DOMAIN_OBJ, &alloc);
1100
1101
    /* everything is ready: start tracing Python memory allocations */
1102
0
    _Py_tracemalloc_config.tracing = 1;
1103
1104
0
    return 0;
1105
0
}
1106
1107
1108
static void
1109
tracemalloc_stop(void)
1110
0
{
1111
0
    if (!_Py_tracemalloc_config.tracing)
1112
0
        return;
1113
1114
    /* stop tracing Python memory allocations */
1115
0
    _Py_tracemalloc_config.tracing = 0;
1116
1117
    /* unregister the hook on memory allocators */
1118
0
#ifdef TRACE_RAW_MALLOC
1119
0
    PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &allocators.raw);
1120
0
#endif
1121
0
    PyMem_SetAllocator(PYMEM_DOMAIN_MEM, &allocators.mem);
1122
0
    PyMem_SetAllocator(PYMEM_DOMAIN_OBJ, &allocators.obj);
1123
1124
0
    tracemalloc_clear_traces();
1125
1126
    /* release memory */
1127
0
    raw_free(tracemalloc_traceback);
1128
0
    tracemalloc_traceback = NULL;
1129
0
}
1130
1131
1132
1133
/*[clinic input]
1134
_tracemalloc.is_tracing
1135
1136
Return True if the tracemalloc module is tracing Python memory allocations.
1137
[clinic start generated code]*/
1138
1139
static PyObject *
1140
_tracemalloc_is_tracing_impl(PyObject *module)
1141
/*[clinic end generated code: output=2d763b42601cd3ef input=af104b0a00192f63]*/
1142
0
{
1143
0
    return PyBool_FromLong(_Py_tracemalloc_config.tracing);
1144
0
}
1145
1146
1147
/*[clinic input]
1148
_tracemalloc.clear_traces
1149
1150
Clear traces of memory blocks allocated by Python.
1151
[clinic start generated code]*/
1152
1153
static PyObject *
1154
_tracemalloc_clear_traces_impl(PyObject *module)
1155
/*[clinic end generated code: output=a86080ee41b84197 input=0dab5b6c785183a5]*/
1156
0
{
1157
0
    if (!_Py_tracemalloc_config.tracing)
1158
0
        Py_RETURN_NONE;
1159
1160
0
    set_reentrant(1);
1161
0
    tracemalloc_clear_traces();
1162
0
    set_reentrant(0);
1163
1164
0
    Py_RETURN_NONE;
1165
0
}
1166
1167
1168
static PyObject*
1169
frame_to_pyobject(frame_t *frame)
1170
0
{
1171
0
    PyObject *frame_obj, *lineno_obj;
1172
1173
0
    frame_obj = PyTuple_New(2);
1174
0
    if (frame_obj == NULL)
1175
0
        return NULL;
1176
1177
0
    Py_INCREF(frame->filename);
1178
0
    PyTuple_SET_ITEM(frame_obj, 0, frame->filename);
1179
1180
0
    lineno_obj = PyLong_FromUnsignedLong(frame->lineno);
1181
0
    if (lineno_obj == NULL) {
1182
0
        Py_DECREF(frame_obj);
1183
0
        return NULL;
1184
0
    }
1185
0
    PyTuple_SET_ITEM(frame_obj, 1, lineno_obj);
1186
1187
0
    return frame_obj;
1188
0
}
1189
1190
1191
static PyObject*
1192
traceback_to_pyobject(traceback_t *traceback, _Py_hashtable_t *intern_table)
1193
0
{
1194
0
    int i;
1195
0
    PyObject *frames, *frame;
1196
1197
0
    if (intern_table != NULL) {
1198
0
        if (_Py_HASHTABLE_GET(intern_table, traceback, frames)) {
1199
0
            Py_INCREF(frames);
1200
0
            return frames;
1201
0
        }
1202
0
    }
1203
1204
0
    frames = PyTuple_New(traceback->nframe);
1205
0
    if (frames == NULL)
1206
0
        return NULL;
1207
1208
0
    for (i=0; i < traceback->nframe; i++) {
1209
0
        frame = frame_to_pyobject(&traceback->frames[i]);
1210
0
        if (frame == NULL) {
1211
0
            Py_DECREF(frames);
1212
0
            return NULL;
1213
0
        }
1214
0
        PyTuple_SET_ITEM(frames, i, frame);
1215
0
    }
1216
1217
0
    if (intern_table != NULL) {
1218
0
        if (_Py_HASHTABLE_SET(intern_table, traceback, frames) < 0) {
1219
0
            Py_DECREF(frames);
1220
0
            PyErr_NoMemory();
1221
0
            return NULL;
1222
0
        }
1223
        /* intern_table keeps a new reference to frames */
1224
0
        Py_INCREF(frames);
1225
0
    }
1226
0
    return frames;
1227
0
}
1228
1229
1230
static PyObject*
1231
trace_to_pyobject(unsigned int domain, trace_t *trace,
1232
                  _Py_hashtable_t *intern_tracebacks)
1233
0
{
1234
0
    PyObject *trace_obj = NULL;
1235
0
    PyObject *obj;
1236
1237
0
    trace_obj = PyTuple_New(3);
1238
0
    if (trace_obj == NULL)
1239
0
        return NULL;
1240
1241
0
    obj = PyLong_FromSize_t(domain);
1242
0
    if (obj == NULL) {
1243
0
        Py_DECREF(trace_obj);
1244
0
        return NULL;
1245
0
    }
1246
0
    PyTuple_SET_ITEM(trace_obj, 0, obj);
1247
1248
0
    obj = PyLong_FromSize_t(trace->size);
1249
0
    if (obj == NULL) {
1250
0
        Py_DECREF(trace_obj);
1251
0
        return NULL;
1252
0
    }
1253
0
    PyTuple_SET_ITEM(trace_obj, 1, obj);
1254
1255
0
    obj = traceback_to_pyobject(trace->traceback, intern_tracebacks);
1256
0
    if (obj == NULL) {
1257
0
        Py_DECREF(trace_obj);
1258
0
        return NULL;
1259
0
    }
1260
0
    PyTuple_SET_ITEM(trace_obj, 2, obj);
1261
1262
0
    return trace_obj;
1263
0
}
1264
1265
1266
typedef struct {
1267
    _Py_hashtable_t *traces;
1268
    _Py_hashtable_t *tracebacks;
1269
    PyObject *list;
1270
} get_traces_t;
1271
1272
static int
1273
tracemalloc_get_traces_fill(_Py_hashtable_t *traces, _Py_hashtable_entry_t *entry,
1274
                            void *user_data)
1275
0
{
1276
0
    get_traces_t *get_traces = user_data;
1277
0
    unsigned int domain;
1278
0
    trace_t trace;
1279
0
    PyObject *tracemalloc_obj;
1280
0
    int res;
1281
1282
0
    if (_Py_tracemalloc_config.use_domain) {
1283
0
        pointer_t key;
1284
0
        _Py_HASHTABLE_ENTRY_READ_KEY(traces, entry, key);
1285
0
        domain = key.domain;
1286
0
    }
1287
0
    else {
1288
0
        domain = DEFAULT_DOMAIN;
1289
0
    }
1290
0
    _Py_HASHTABLE_ENTRY_READ_DATA(traces, entry, trace);
1291
1292
0
    tracemalloc_obj = trace_to_pyobject(domain, &trace, get_traces->tracebacks);
1293
0
    if (tracemalloc_obj == NULL)
1294
0
        return 1;
1295
1296
0
    res = PyList_Append(get_traces->list, tracemalloc_obj);
1297
0
    Py_DECREF(tracemalloc_obj);
1298
0
    if (res < 0)
1299
0
        return 1;
1300
1301
0
    return 0;
1302
0
}
1303
1304
1305
static int
1306
tracemalloc_pyobject_decref_cb(_Py_hashtable_t *tracebacks,
1307
                               _Py_hashtable_entry_t *entry,
1308
                               void *user_data)
1309
0
{
1310
0
    PyObject *obj;
1311
0
    _Py_HASHTABLE_ENTRY_READ_DATA(tracebacks, entry, obj);
1312
0
    Py_DECREF(obj);
1313
0
    return 0;
1314
0
}
1315
1316
1317
1318
/*[clinic input]
1319
_tracemalloc._get_traces
1320
1321
Get traces of all memory blocks allocated by Python.
1322
1323
Return a list of (size: int, traceback: tuple) tuples.
1324
traceback is a tuple of (filename: str, lineno: int) tuples.
1325
1326
Return an empty list if the tracemalloc module is disabled.
1327
[clinic start generated code]*/
1328
1329
static PyObject *
1330
_tracemalloc__get_traces_impl(PyObject *module)
1331
/*[clinic end generated code: output=e9929876ced4b5cc input=6c7d2230b24255aa]*/
1332
0
{
1333
0
    get_traces_t get_traces;
1334
0
    int err;
1335
1336
0
    get_traces.traces = NULL;
1337
0
    get_traces.tracebacks = NULL;
1338
0
    get_traces.list = PyList_New(0);
1339
0
    if (get_traces.list == NULL)
1340
0
        goto error;
1341
1342
0
    if (!_Py_tracemalloc_config.tracing)
1343
0
        return get_traces.list;
1344
1345
    /* the traceback hash table is used temporarily to intern traceback tuple
1346
       of (filename, lineno) tuples */
1347
0
    get_traces.tracebacks = hashtable_new(sizeof(traceback_t *),
1348
0
                                          sizeof(PyObject *),
1349
0
                                          _Py_hashtable_hash_ptr,
1350
0
                                          _Py_hashtable_compare_direct);
1351
0
    if (get_traces.tracebacks == NULL) {
1352
0
        PyErr_NoMemory();
1353
0
        goto error;
1354
0
    }
1355
1356
0
    TABLES_LOCK();
1357
0
    get_traces.traces = _Py_hashtable_copy(tracemalloc_traces);
1358
0
    TABLES_UNLOCK();
1359
1360
0
    if (get_traces.traces == NULL) {
1361
0
        PyErr_NoMemory();
1362
0
        goto error;
1363
0
    }
1364
1365
0
    set_reentrant(1);
1366
0
    err = _Py_hashtable_foreach(get_traces.traces,
1367
0
                                tracemalloc_get_traces_fill, &get_traces);
1368
0
    set_reentrant(0);
1369
0
    if (err)
1370
0
        goto error;
1371
1372
0
    goto finally;
1373
1374
0
error:
1375
0
    Py_CLEAR(get_traces.list);
1376
1377
0
finally:
1378
0
    if (get_traces.tracebacks != NULL) {
1379
0
        _Py_hashtable_foreach(get_traces.tracebacks,
1380
0
                              tracemalloc_pyobject_decref_cb, NULL);
1381
0
        _Py_hashtable_destroy(get_traces.tracebacks);
1382
0
    }
1383
0
    if (get_traces.traces != NULL) {
1384
0
        _Py_hashtable_destroy(get_traces.traces);
1385
0
    }
1386
1387
0
    return get_traces.list;
1388
0
}
1389
1390
1391
static traceback_t*
1392
tracemalloc_get_traceback(unsigned int domain, uintptr_t ptr)
1393
0
{
1394
0
    trace_t trace;
1395
0
    int found;
1396
1397
0
    if (!_Py_tracemalloc_config.tracing)
1398
0
        return NULL;
1399
1400
0
    TABLES_LOCK();
1401
0
    if (_Py_tracemalloc_config.use_domain) {
1402
0
        pointer_t key = {ptr, domain};
1403
0
        found = _Py_HASHTABLE_GET(tracemalloc_traces, key, trace);
1404
0
    }
1405
0
    else {
1406
0
        found = _Py_HASHTABLE_GET(tracemalloc_traces, ptr, trace);
1407
0
    }
1408
0
    TABLES_UNLOCK();
1409
1410
0
    if (!found)
1411
0
        return NULL;
1412
1413
0
    return trace.traceback;
1414
0
}
1415
1416
1417
1418
/*[clinic input]
1419
_tracemalloc._get_object_traceback
1420
1421
    obj: object
1422
    /
1423
1424
Get the traceback where the Python object obj was allocated.
1425
1426
Return a tuple of (filename: str, lineno: int) tuples.
1427
Return None if the tracemalloc module is disabled or did not
1428
trace the allocation of the object.
1429
[clinic start generated code]*/
1430
1431
static PyObject *
1432
_tracemalloc__get_object_traceback(PyObject *module, PyObject *obj)
1433
/*[clinic end generated code: output=41ee0553a658b0aa input=29495f1b21c53212]*/
1434
0
{
1435
0
    PyTypeObject *type;
1436
0
    void *ptr;
1437
0
    traceback_t *traceback;
1438
1439
0
    type = Py_TYPE(obj);
1440
0
    if (PyType_IS_GC(type)) {
1441
0
        ptr = (void *)((char *)obj - sizeof(PyGC_Head));
1442
0
    }
1443
0
    else {
1444
0
        ptr = (void *)obj;
1445
0
    }
1446
1447
0
    traceback = tracemalloc_get_traceback(DEFAULT_DOMAIN, (uintptr_t)ptr);
1448
0
    if (traceback == NULL)
1449
0
        Py_RETURN_NONE;
1450
1451
0
    return traceback_to_pyobject(traceback, NULL);
1452
0
}
1453
1454
1455
0
#define PUTS(fd, str) _Py_write_noraise(fd, str, (int)strlen(str))
1456
1457
static void
1458
_PyMem_DumpFrame(int fd, frame_t * frame)
1459
0
{
1460
0
    PUTS(fd, "  File \"");
1461
0
    _Py_DumpASCII(fd, frame->filename);
1462
0
    PUTS(fd, "\", line ");
1463
0
    _Py_DumpDecimal(fd, frame->lineno);
1464
0
    PUTS(fd, "\n");
1465
0
}
1466
1467
/* Dump the traceback where a memory block was allocated into file descriptor
1468
   fd. The function may block on TABLES_LOCK() but it is unlikely. */
1469
void
1470
_PyMem_DumpTraceback(int fd, const void *ptr)
1471
0
{
1472
0
    traceback_t *traceback;
1473
0
    int i;
1474
1475
0
    if (!_Py_tracemalloc_config.tracing) {
1476
0
        PUTS(fd, "Enable tracemalloc to get the memory block "
1477
0
                 "allocation traceback\n\n");
1478
0
        return;
1479
0
    }
1480
1481
0
    traceback = tracemalloc_get_traceback(DEFAULT_DOMAIN, (uintptr_t)ptr);
1482
0
    if (traceback == NULL)
1483
0
        return;
1484
1485
0
    PUTS(fd, "Memory block allocated at (most recent call first):\n");
1486
0
    for (i=0; i < traceback->nframe; i++) {
1487
0
        _PyMem_DumpFrame(fd, &traceback->frames[i]);
1488
0
    }
1489
0
    PUTS(fd, "\n");
1490
0
}
1491
1492
#undef PUTS
1493
1494
1495
1496
/*[clinic input]
1497
_tracemalloc.start
1498
1499
    nframe: int = 1
1500
    /
1501
1502
Start tracing Python memory allocations.
1503
1504
Also set the maximum number of frames stored in the traceback of a
1505
trace to nframe.
1506
[clinic start generated code]*/
1507
1508
static PyObject *
1509
_tracemalloc_start_impl(PyObject *module, int nframe)
1510
/*[clinic end generated code: output=caae05c23c159d3c input=40d849b5b29d1933]*/
1511
0
{
1512
0
    if (tracemalloc_start(nframe) < 0) {
1513
0
        return NULL;
1514
0
    }
1515
0
    Py_RETURN_NONE;
1516
0
}
1517
1518
1519
/*[clinic input]
1520
_tracemalloc.stop
1521
1522
Stop tracing Python memory allocations.
1523
1524
Also clear traces of memory blocks allocated by Python.
1525
[clinic start generated code]*/
1526
1527
static PyObject *
1528
_tracemalloc_stop_impl(PyObject *module)
1529
/*[clinic end generated code: output=c3c42ae03e3955cd input=7478f075e51dae18]*/
1530
0
{
1531
0
    tracemalloc_stop();
1532
0
    Py_RETURN_NONE;
1533
0
}
1534
1535
1536
/*[clinic input]
1537
_tracemalloc.get_traceback_limit
1538
1539
Get the maximum number of frames stored in the traceback of a trace.
1540
1541
By default, a trace of an allocated memory block only stores
1542
the most recent frame: the limit is 1.
1543
[clinic start generated code]*/
1544
1545
static PyObject *
1546
_tracemalloc_get_traceback_limit_impl(PyObject *module)
1547
/*[clinic end generated code: output=d556d9306ba95567 input=da3cd977fc68ae3b]*/
1548
0
{
1549
0
    return PyLong_FromLong(_Py_tracemalloc_config.max_nframe);
1550
0
}
1551
1552
1553
1554
/*[clinic input]
1555
_tracemalloc.get_tracemalloc_memory
1556
1557
Get the memory usage in bytes of the tracemalloc module.
1558
1559
This memory is used internally to trace memory allocations.
1560
[clinic start generated code]*/
1561
1562
static PyObject *
1563
_tracemalloc_get_tracemalloc_memory_impl(PyObject *module)
1564
/*[clinic end generated code: output=e3f14e280a55f5aa input=5d919c0f4d5132ad]*/
1565
0
{
1566
0
    size_t size;
1567
1568
0
    size = _Py_hashtable_size(tracemalloc_tracebacks);
1569
0
    size += _Py_hashtable_size(tracemalloc_filenames);
1570
1571
0
    TABLES_LOCK();
1572
0
    size += _Py_hashtable_size(tracemalloc_traces);
1573
0
    TABLES_UNLOCK();
1574
1575
0
    return PyLong_FromSize_t(size);
1576
0
}
1577
1578
1579
1580
/*[clinic input]
1581
_tracemalloc.get_traced_memory
1582
1583
Get the current size and peak size of memory blocks traced by tracemalloc.
1584
1585
Returns a tuple: (current: int, peak: int).
1586
[clinic start generated code]*/
1587
1588
static PyObject *
1589
_tracemalloc_get_traced_memory_impl(PyObject *module)
1590
/*[clinic end generated code: output=5b167189adb9e782 input=61ddb5478400ff66]*/
1591
0
{
1592
0
    Py_ssize_t size, peak_size;
1593
1594
0
    if (!_Py_tracemalloc_config.tracing)
1595
0
        return Py_BuildValue("ii", 0, 0);
1596
1597
0
    TABLES_LOCK();
1598
0
    size = tracemalloc_traced_memory;
1599
0
    peak_size = tracemalloc_peak_traced_memory;
1600
0
    TABLES_UNLOCK();
1601
1602
0
    return Py_BuildValue("nn", size, peak_size);
1603
0
}
1604
1605
1606
static PyMethodDef module_methods[] = {
1607
    _TRACEMALLOC_IS_TRACING_METHODDEF
1608
    _TRACEMALLOC_CLEAR_TRACES_METHODDEF
1609
    _TRACEMALLOC__GET_TRACES_METHODDEF
1610
    _TRACEMALLOC__GET_OBJECT_TRACEBACK_METHODDEF
1611
    _TRACEMALLOC_START_METHODDEF
1612
    _TRACEMALLOC_STOP_METHODDEF
1613
    _TRACEMALLOC_GET_TRACEBACK_LIMIT_METHODDEF
1614
    _TRACEMALLOC_GET_TRACEMALLOC_MEMORY_METHODDEF
1615
    _TRACEMALLOC_GET_TRACED_MEMORY_METHODDEF
1616
    /* sentinel */
1617
    {NULL, NULL}
1618
};
1619
1620
PyDoc_STRVAR(module_doc,
1621
"Debug module to trace memory blocks allocated by Python.");
1622
1623
static struct PyModuleDef module_def = {
1624
    PyModuleDef_HEAD_INIT,
1625
    "_tracemalloc",
1626
    module_doc,
1627
    0, /* non-negative size to be able to unload the module */
1628
    module_methods,
1629
    NULL,
1630
};
1631
1632
PyMODINIT_FUNC
1633
PyInit__tracemalloc(void)
1634
0
{
1635
0
    PyObject *m;
1636
0
    m = PyModule_Create(&module_def);
1637
0
    if (m == NULL)
1638
0
        return NULL;
1639
1640
0
    if (tracemalloc_init() < 0) {
1641
0
        Py_DECREF(m);
1642
0
        return NULL;
1643
0
    }
1644
1645
0
    return m;
1646
0
}
1647
1648
1649
int
1650
_PyTraceMalloc_Init(int nframe)
1651
14
{
1652
14
    assert(PyGILState_Check());
1653
14
    if (nframe == 0) {
1654
14
        return 0;
1655
14
    }
1656
0
    return tracemalloc_start(nframe);
1657
14
}
1658
1659
1660
void
1661
_PyTraceMalloc_Fini(void)
1662
0
{
1663
0
    assert(PyGILState_Check());
1664
0
    tracemalloc_deinit();
1665
0
}
1666
1667
int
1668
PyTraceMalloc_Track(unsigned int domain, uintptr_t ptr,
1669
                    size_t size)
1670
0
{
1671
0
    int res;
1672
0
    PyGILState_STATE gil_state;
1673
1674
0
    if (!_Py_tracemalloc_config.tracing) {
1675
        /* tracemalloc is not tracing: do nothing */
1676
0
        return -2;
1677
0
    }
1678
1679
0
    gil_state = PyGILState_Ensure();
1680
1681
0
    TABLES_LOCK();
1682
0
    res = tracemalloc_add_trace(domain, ptr, size);
1683
0
    TABLES_UNLOCK();
1684
1685
0
    PyGILState_Release(gil_state);
1686
0
    return res;
1687
0
}
1688
1689
1690
int
1691
PyTraceMalloc_Untrack(unsigned int domain, uintptr_t ptr)
1692
0
{
1693
0
    if (!_Py_tracemalloc_config.tracing) {
1694
        /* tracemalloc is not tracing: do nothing */
1695
0
        return -2;
1696
0
    }
1697
1698
0
    TABLES_LOCK();
1699
0
    tracemalloc_remove_trace(domain, ptr);
1700
0
    TABLES_UNLOCK();
1701
1702
0
    return 0;
1703
0
}
1704
1705
1706
/* If the object memory block is already traced, update its trace
1707
   with the current Python traceback.
1708
1709
   Do nothing if tracemalloc is not tracing memory allocations
1710
   or if the object memory block is not already traced. */
1711
int
1712
_PyTraceMalloc_NewReference(PyObject *op)
1713
0
{
1714
0
    assert(PyGILState_Check());
1715
1716
0
    if (!_Py_tracemalloc_config.tracing) {
1717
        /* tracemalloc is not tracing: do nothing */
1718
0
        return -1;
1719
0
    }
1720
1721
0
    uintptr_t ptr;
1722
0
    PyTypeObject *type = Py_TYPE(op);
1723
0
    if (PyType_IS_GC(type)) {
1724
0
        ptr = (uintptr_t)((char *)op - sizeof(PyGC_Head));
1725
0
    }
1726
0
    else {
1727
0
        ptr = (uintptr_t)op;
1728
0
    }
1729
1730
0
    _Py_hashtable_entry_t* entry;
1731
0
    int res = -1;
1732
1733
0
    TABLES_LOCK();
1734
0
    if (_Py_tracemalloc_config.use_domain) {
1735
0
        pointer_t key = {ptr, DEFAULT_DOMAIN};
1736
0
        entry = _Py_HASHTABLE_GET_ENTRY(tracemalloc_traces, key);
1737
0
    }
1738
0
    else {
1739
0
        entry = _Py_HASHTABLE_GET_ENTRY(tracemalloc_traces, ptr);
1740
0
    }
1741
1742
0
    if (entry != NULL) {
1743
        /* update the traceback of the memory block */
1744
0
        traceback_t *traceback = traceback_new();
1745
0
        if (traceback != NULL) {
1746
0
            trace_t trace;
1747
0
            _Py_HASHTABLE_ENTRY_READ_DATA(tracemalloc_traces, entry, trace);
1748
0
            trace.traceback = traceback;
1749
0
            _Py_HASHTABLE_ENTRY_WRITE_DATA(tracemalloc_traces, entry, trace);
1750
0
            res = 0;
1751
0
        }
1752
0
    }
1753
    /* else: cannot track the object, its memory block size is unknown */
1754
0
    TABLES_UNLOCK();
1755
1756
0
    return res;
1757
0
}
1758
1759
1760
PyObject*
1761
_PyTraceMalloc_GetTraceback(unsigned int domain, uintptr_t ptr)
1762
0
{
1763
0
    traceback_t *traceback;
1764
1765
0
    traceback = tracemalloc_get_traceback(domain, ptr);
1766
0
    if (traceback == NULL)
1767
0
        Py_RETURN_NONE;
1768
1769
0
    return traceback_to_pyobject(traceback, NULL);
1770
0
}