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