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