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