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