• 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 againt 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 (_Py_hashtable_set(domains2, TO_PTR(domain), traces2) < 0) {
1245         _Py_hashtable_destroy(traces2);
1246         return -1;
1247     }
1248     return 0;
1249 }
1250 
1251 
1252 static _Py_hashtable_t*
tracemalloc_copy_domains(_Py_hashtable_t * domains)1253 tracemalloc_copy_domains(_Py_hashtable_t *domains)
1254 {
1255     _Py_hashtable_t *domains2 = tracemalloc_create_domains_table();
1256     if (domains2 == NULL) {
1257         return NULL;
1258     }
1259 
1260     int err = _Py_hashtable_foreach(domains,
1261                                     tracemalloc_copy_domain,
1262                                     domains2);
1263     if (err) {
1264         _Py_hashtable_destroy(domains2);
1265         return NULL;
1266     }
1267     return domains2;
1268 }
1269 
1270 
1271 static int
tracemalloc_get_traces_fill(_Py_hashtable_t * traces,const void * key,const void * value,void * user_data)1272 tracemalloc_get_traces_fill(_Py_hashtable_t *traces,
1273                             const void *key, const void *value,
1274                             void *user_data)
1275 {
1276     get_traces_t *get_traces = user_data;
1277 
1278     const trace_t *trace = (const trace_t *)value;
1279 
1280     PyObject *tuple = trace_to_pyobject(get_traces->domain, trace,
1281                                         get_traces->tracebacks);
1282     if (tuple == NULL) {
1283         return 1;
1284     }
1285 
1286     int res = PyList_Append(get_traces->list, tuple);
1287     Py_DECREF(tuple);
1288     if (res < 0) {
1289         return 1;
1290     }
1291 
1292     return 0;
1293 }
1294 
1295 
1296 static int
tracemalloc_get_traces_domain(_Py_hashtable_t * domains,const void * key,const void * value,void * user_data)1297 tracemalloc_get_traces_domain(_Py_hashtable_t *domains,
1298                               const void *key, const void *value,
1299                               void *user_data)
1300 {
1301     get_traces_t *get_traces = user_data;
1302 
1303     unsigned int domain = (unsigned int)FROM_PTR(key);
1304     _Py_hashtable_t *traces = (_Py_hashtable_t *)value;
1305 
1306     get_traces->domain = domain;
1307     return _Py_hashtable_foreach(traces,
1308                                  tracemalloc_get_traces_fill,
1309                                  get_traces);
1310 }
1311 
1312 
1313 static void
tracemalloc_pyobject_decref(void * value)1314 tracemalloc_pyobject_decref(void *value)
1315 {
1316     PyObject *obj = (PyObject *)value;
1317     Py_DECREF(obj);
1318 }
1319 
1320 
1321 
1322 /*[clinic input]
1323 _tracemalloc._get_traces
1324 
1325 Get traces of all memory blocks allocated by Python.
1326 
1327 Return a list of (size: int, traceback: tuple) tuples.
1328 traceback is a tuple of (filename: str, lineno: int) tuples.
1329 
1330 Return an empty list if the tracemalloc module is disabled.
1331 [clinic start generated code]*/
1332 
1333 static PyObject *
_tracemalloc__get_traces_impl(PyObject * module)1334 _tracemalloc__get_traces_impl(PyObject *module)
1335 /*[clinic end generated code: output=e9929876ced4b5cc input=6c7d2230b24255aa]*/
1336 {
1337     get_traces_t get_traces;
1338     get_traces.domain = DEFAULT_DOMAIN;
1339     get_traces.traces = NULL;
1340     get_traces.domains = NULL;
1341     get_traces.tracebacks = NULL;
1342     get_traces.list = PyList_New(0);
1343     if (get_traces.list == NULL)
1344         goto error;
1345 
1346     if (!_Py_tracemalloc_config.tracing)
1347         return get_traces.list;
1348 
1349     /* the traceback hash table is used temporarily to intern traceback tuple
1350        of (filename, lineno) tuples */
1351     get_traces.tracebacks = hashtable_new(_Py_hashtable_hash_ptr,
1352                                           _Py_hashtable_compare_direct,
1353                                           NULL, tracemalloc_pyobject_decref);
1354     if (get_traces.tracebacks == NULL) {
1355         goto no_memory;
1356     }
1357 
1358     // Copy all traces so tracemalloc_get_traces_fill() doesn't have to disable
1359     // temporarily tracemalloc which would impact other threads and so would
1360     // miss allocations while get_traces() is called.
1361     TABLES_LOCK();
1362     get_traces.traces = tracemalloc_copy_traces(tracemalloc_traces);
1363     TABLES_UNLOCK();
1364 
1365     if (get_traces.traces == NULL) {
1366         goto no_memory;
1367     }
1368 
1369     TABLES_LOCK();
1370     get_traces.domains = tracemalloc_copy_domains(tracemalloc_domains);
1371     TABLES_UNLOCK();
1372 
1373     if (get_traces.domains == NULL) {
1374         goto no_memory;
1375     }
1376 
1377     // Convert traces to a list of tuples
1378     set_reentrant(1);
1379     int err = _Py_hashtable_foreach(get_traces.traces,
1380                                     tracemalloc_get_traces_fill,
1381                                     &get_traces);
1382     if (!err) {
1383         err = _Py_hashtable_foreach(get_traces.domains,
1384                                     tracemalloc_get_traces_domain,
1385                                     &get_traces);
1386     }
1387     set_reentrant(0);
1388     if (err) {
1389         goto error;
1390     }
1391 
1392     goto finally;
1393 
1394 no_memory:
1395     PyErr_NoMemory();
1396 
1397 error:
1398     Py_CLEAR(get_traces.list);
1399 
1400 finally:
1401     if (get_traces.tracebacks != NULL) {
1402         _Py_hashtable_destroy(get_traces.tracebacks);
1403     }
1404     if (get_traces.traces != NULL) {
1405         _Py_hashtable_destroy(get_traces.traces);
1406     }
1407     if (get_traces.domains != NULL) {
1408         _Py_hashtable_destroy(get_traces.domains);
1409     }
1410 
1411     return get_traces.list;
1412 }
1413 
1414 
1415 static traceback_t*
tracemalloc_get_traceback(unsigned int domain,uintptr_t ptr)1416 tracemalloc_get_traceback(unsigned int domain, uintptr_t ptr)
1417 {
1418 
1419     if (!_Py_tracemalloc_config.tracing)
1420         return NULL;
1421 
1422     trace_t *trace;
1423     TABLES_LOCK();
1424     _Py_hashtable_t *traces = tracemalloc_get_traces_table(domain);
1425     if (traces) {
1426         trace = _Py_hashtable_get(traces, TO_PTR(ptr));
1427     }
1428     else {
1429         trace = NULL;
1430     }
1431     TABLES_UNLOCK();
1432 
1433     if (!trace) {
1434         return NULL;
1435     }
1436 
1437     return trace->traceback;
1438 }
1439 
1440 
1441 
1442 /*[clinic input]
1443 _tracemalloc._get_object_traceback
1444 
1445     obj: object
1446     /
1447 
1448 Get the traceback where the Python object obj was allocated.
1449 
1450 Return a tuple of (filename: str, lineno: int) tuples.
1451 Return None if the tracemalloc module is disabled or did not
1452 trace the allocation of the object.
1453 [clinic start generated code]*/
1454 
1455 static PyObject *
_tracemalloc__get_object_traceback(PyObject * module,PyObject * obj)1456 _tracemalloc__get_object_traceback(PyObject *module, PyObject *obj)
1457 /*[clinic end generated code: output=41ee0553a658b0aa input=29495f1b21c53212]*/
1458 {
1459     PyTypeObject *type;
1460     void *ptr;
1461     traceback_t *traceback;
1462 
1463     type = Py_TYPE(obj);
1464     if (PyType_IS_GC(type)) {
1465         ptr = (void *)((char *)obj - sizeof(PyGC_Head));
1466     }
1467     else {
1468         ptr = (void *)obj;
1469     }
1470 
1471     traceback = tracemalloc_get_traceback(DEFAULT_DOMAIN, (uintptr_t)ptr);
1472     if (traceback == NULL)
1473         Py_RETURN_NONE;
1474 
1475     return traceback_to_pyobject(traceback, NULL);
1476 }
1477 
1478 
1479 #define PUTS(fd, str) _Py_write_noraise(fd, str, (int)strlen(str))
1480 
1481 static void
_PyMem_DumpFrame(int fd,frame_t * frame)1482 _PyMem_DumpFrame(int fd, frame_t * frame)
1483 {
1484     PUTS(fd, "  File \"");
1485     _Py_DumpASCII(fd, frame->filename);
1486     PUTS(fd, "\", line ");
1487     _Py_DumpDecimal(fd, frame->lineno);
1488     PUTS(fd, "\n");
1489 }
1490 
1491 /* Dump the traceback where a memory block was allocated into file descriptor
1492    fd. The function may block on TABLES_LOCK() but it is unlikely. */
1493 void
_PyMem_DumpTraceback(int fd,const void * ptr)1494 _PyMem_DumpTraceback(int fd, const void *ptr)
1495 {
1496     traceback_t *traceback;
1497     int i;
1498 
1499     if (!_Py_tracemalloc_config.tracing) {
1500         PUTS(fd, "Enable tracemalloc to get the memory block "
1501                  "allocation traceback\n\n");
1502         return;
1503     }
1504 
1505     traceback = tracemalloc_get_traceback(DEFAULT_DOMAIN, (uintptr_t)ptr);
1506     if (traceback == NULL)
1507         return;
1508 
1509     PUTS(fd, "Memory block allocated at (most recent call first):\n");
1510     for (i=0; i < traceback->nframe; i++) {
1511         _PyMem_DumpFrame(fd, &traceback->frames[i]);
1512     }
1513     PUTS(fd, "\n");
1514 }
1515 
1516 #undef PUTS
1517 
1518 
1519 
1520 /*[clinic input]
1521 _tracemalloc.start
1522 
1523     nframe: int = 1
1524     /
1525 
1526 Start tracing Python memory allocations.
1527 
1528 Also set the maximum number of frames stored in the traceback of a
1529 trace to nframe.
1530 [clinic start generated code]*/
1531 
1532 static PyObject *
_tracemalloc_start_impl(PyObject * module,int nframe)1533 _tracemalloc_start_impl(PyObject *module, int nframe)
1534 /*[clinic end generated code: output=caae05c23c159d3c input=40d849b5b29d1933]*/
1535 {
1536     if (tracemalloc_start(nframe) < 0) {
1537         return NULL;
1538     }
1539     Py_RETURN_NONE;
1540 }
1541 
1542 
1543 /*[clinic input]
1544 _tracemalloc.stop
1545 
1546 Stop tracing Python memory allocations.
1547 
1548 Also clear traces of memory blocks allocated by Python.
1549 [clinic start generated code]*/
1550 
1551 static PyObject *
_tracemalloc_stop_impl(PyObject * module)1552 _tracemalloc_stop_impl(PyObject *module)
1553 /*[clinic end generated code: output=c3c42ae03e3955cd input=7478f075e51dae18]*/
1554 {
1555     tracemalloc_stop();
1556     Py_RETURN_NONE;
1557 }
1558 
1559 
1560 /*[clinic input]
1561 _tracemalloc.get_traceback_limit
1562 
1563 Get the maximum number of frames stored in the traceback of a trace.
1564 
1565 By default, a trace of an allocated memory block only stores
1566 the most recent frame: the limit is 1.
1567 [clinic start generated code]*/
1568 
1569 static PyObject *
_tracemalloc_get_traceback_limit_impl(PyObject * module)1570 _tracemalloc_get_traceback_limit_impl(PyObject *module)
1571 /*[clinic end generated code: output=d556d9306ba95567 input=da3cd977fc68ae3b]*/
1572 {
1573     return PyLong_FromLong(_Py_tracemalloc_config.max_nframe);
1574 }
1575 
1576 
1577 static int
tracemalloc_get_tracemalloc_memory_cb(_Py_hashtable_t * domains,const void * key,const void * value,void * user_data)1578 tracemalloc_get_tracemalloc_memory_cb(_Py_hashtable_t *domains,
1579                                       const void *key, const void *value,
1580                                       void *user_data)
1581 {
1582     const _Py_hashtable_t *traces = value;
1583     size_t *size = (size_t*)user_data;
1584     *size += _Py_hashtable_size(traces);
1585     return 0;
1586 }
1587 
1588 
1589 /*[clinic input]
1590 _tracemalloc.get_tracemalloc_memory
1591 
1592 Get the memory usage in bytes of the tracemalloc module.
1593 
1594 This memory is used internally to trace memory allocations.
1595 [clinic start generated code]*/
1596 
1597 static PyObject *
_tracemalloc_get_tracemalloc_memory_impl(PyObject * module)1598 _tracemalloc_get_tracemalloc_memory_impl(PyObject *module)
1599 /*[clinic end generated code: output=e3f14e280a55f5aa input=5d919c0f4d5132ad]*/
1600 {
1601     size_t size;
1602 
1603     size = _Py_hashtable_size(tracemalloc_tracebacks);
1604     size += _Py_hashtable_size(tracemalloc_filenames);
1605 
1606     TABLES_LOCK();
1607     size += _Py_hashtable_size(tracemalloc_traces);
1608     _Py_hashtable_foreach(tracemalloc_domains,
1609                           tracemalloc_get_tracemalloc_memory_cb, &size);
1610     TABLES_UNLOCK();
1611 
1612     return PyLong_FromSize_t(size);
1613 }
1614 
1615 
1616 
1617 /*[clinic input]
1618 _tracemalloc.get_traced_memory
1619 
1620 Get the current size and peak size of memory blocks traced by tracemalloc.
1621 
1622 Returns a tuple: (current: int, peak: int).
1623 [clinic start generated code]*/
1624 
1625 static PyObject *
_tracemalloc_get_traced_memory_impl(PyObject * module)1626 _tracemalloc_get_traced_memory_impl(PyObject *module)
1627 /*[clinic end generated code: output=5b167189adb9e782 input=61ddb5478400ff66]*/
1628 {
1629     Py_ssize_t size, peak_size;
1630 
1631     if (!_Py_tracemalloc_config.tracing)
1632         return Py_BuildValue("ii", 0, 0);
1633 
1634     TABLES_LOCK();
1635     size = tracemalloc_traced_memory;
1636     peak_size = tracemalloc_peak_traced_memory;
1637     TABLES_UNLOCK();
1638 
1639     return Py_BuildValue("nn", size, peak_size);
1640 }
1641 
1642 /*[clinic input]
1643 _tracemalloc.reset_peak
1644 
1645 Set the peak size of memory blocks traced by tracemalloc to the current size.
1646 
1647 Do nothing if the tracemalloc module is not tracing memory allocations.
1648 
1649 [clinic start generated code]*/
1650 
1651 static PyObject *
_tracemalloc_reset_peak_impl(PyObject * module)1652 _tracemalloc_reset_peak_impl(PyObject *module)
1653 /*[clinic end generated code: output=140c2870f691dbb2 input=18afd0635066e9ce]*/
1654 {
1655     if (!_Py_tracemalloc_config.tracing) {
1656         Py_RETURN_NONE;
1657     }
1658 
1659     TABLES_LOCK();
1660     tracemalloc_peak_traced_memory = tracemalloc_traced_memory;
1661     TABLES_UNLOCK();
1662 
1663     Py_RETURN_NONE;
1664 }
1665 
1666 
1667 static PyMethodDef module_methods[] = {
1668     _TRACEMALLOC_IS_TRACING_METHODDEF
1669     _TRACEMALLOC_CLEAR_TRACES_METHODDEF
1670     _TRACEMALLOC__GET_TRACES_METHODDEF
1671     _TRACEMALLOC__GET_OBJECT_TRACEBACK_METHODDEF
1672     _TRACEMALLOC_START_METHODDEF
1673     _TRACEMALLOC_STOP_METHODDEF
1674     _TRACEMALLOC_GET_TRACEBACK_LIMIT_METHODDEF
1675     _TRACEMALLOC_GET_TRACEMALLOC_MEMORY_METHODDEF
1676     _TRACEMALLOC_GET_TRACED_MEMORY_METHODDEF
1677     _TRACEMALLOC_RESET_PEAK_METHODDEF
1678     /* sentinel */
1679     {NULL, NULL}
1680 };
1681 
1682 PyDoc_STRVAR(module_doc,
1683 "Debug module to trace memory blocks allocated by Python.");
1684 
1685 static struct PyModuleDef module_def = {
1686     PyModuleDef_HEAD_INIT,
1687     "_tracemalloc",
1688     module_doc,
1689     0, /* non-negative size to be able to unload the module */
1690     module_methods,
1691     NULL,
1692 };
1693 
1694 PyMODINIT_FUNC
PyInit__tracemalloc(void)1695 PyInit__tracemalloc(void)
1696 {
1697     PyObject *m;
1698     m = PyModule_Create(&module_def);
1699     if (m == NULL)
1700         return NULL;
1701 
1702     if (tracemalloc_init() < 0) {
1703         Py_DECREF(m);
1704         return NULL;
1705     }
1706 
1707     return m;
1708 }
1709 
1710 
1711 int
_PyTraceMalloc_Init(int nframe)1712 _PyTraceMalloc_Init(int nframe)
1713 {
1714     assert(PyGILState_Check());
1715     if (nframe == 0) {
1716         return 0;
1717     }
1718     return tracemalloc_start(nframe);
1719 }
1720 
1721 
1722 void
_PyTraceMalloc_Fini(void)1723 _PyTraceMalloc_Fini(void)
1724 {
1725     assert(PyGILState_Check());
1726     tracemalloc_deinit();
1727 }
1728 
1729 int
PyTraceMalloc_Track(unsigned int domain,uintptr_t ptr,size_t size)1730 PyTraceMalloc_Track(unsigned int domain, uintptr_t ptr,
1731                     size_t size)
1732 {
1733     int res;
1734     PyGILState_STATE gil_state;
1735 
1736     if (!_Py_tracemalloc_config.tracing) {
1737         /* tracemalloc is not tracing: do nothing */
1738         return -2;
1739     }
1740 
1741     gil_state = PyGILState_Ensure();
1742 
1743     TABLES_LOCK();
1744     res = tracemalloc_add_trace(domain, ptr, size);
1745     TABLES_UNLOCK();
1746 
1747     PyGILState_Release(gil_state);
1748     return res;
1749 }
1750 
1751 
1752 int
PyTraceMalloc_Untrack(unsigned int domain,uintptr_t ptr)1753 PyTraceMalloc_Untrack(unsigned int domain, uintptr_t ptr)
1754 {
1755     if (!_Py_tracemalloc_config.tracing) {
1756         /* tracemalloc is not tracing: do nothing */
1757         return -2;
1758     }
1759 
1760     TABLES_LOCK();
1761     tracemalloc_remove_trace(domain, ptr);
1762     TABLES_UNLOCK();
1763 
1764     return 0;
1765 }
1766 
1767 
1768 /* If the object memory block is already traced, update its trace
1769    with the current Python traceback.
1770 
1771    Do nothing if tracemalloc is not tracing memory allocations
1772    or if the object memory block is not already traced. */
1773 int
_PyTraceMalloc_NewReference(PyObject * op)1774 _PyTraceMalloc_NewReference(PyObject *op)
1775 {
1776     assert(PyGILState_Check());
1777 
1778     if (!_Py_tracemalloc_config.tracing) {
1779         /* tracemalloc is not tracing: do nothing */
1780         return -1;
1781     }
1782 
1783     uintptr_t ptr;
1784     PyTypeObject *type = Py_TYPE(op);
1785     if (PyType_IS_GC(type)) {
1786         ptr = (uintptr_t)((char *)op - sizeof(PyGC_Head));
1787     }
1788     else {
1789         ptr = (uintptr_t)op;
1790     }
1791 
1792     int res = -1;
1793 
1794     TABLES_LOCK();
1795     trace_t *trace = _Py_hashtable_get(tracemalloc_traces, TO_PTR(ptr));
1796     if (trace != NULL) {
1797         /* update the traceback of the memory block */
1798         traceback_t *traceback = traceback_new();
1799         if (traceback != NULL) {
1800             trace->traceback = traceback;
1801             res = 0;
1802         }
1803     }
1804     /* else: cannot track the object, its memory block size is unknown */
1805     TABLES_UNLOCK();
1806 
1807     return res;
1808 }
1809 
1810 
1811 PyObject*
_PyTraceMalloc_GetTraceback(unsigned int domain,uintptr_t ptr)1812 _PyTraceMalloc_GetTraceback(unsigned int domain, uintptr_t ptr)
1813 {
1814     traceback_t *traceback;
1815 
1816     traceback = tracemalloc_get_traceback(domain, ptr);
1817     if (traceback == NULL)
1818         Py_RETURN_NONE;
1819 
1820     return traceback_to_pyobject(traceback, NULL);
1821 }
1822