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