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