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