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