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]
10module _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 */
18static void tracemalloc_stop(void);
19static void* raw_malloc(size_t size);
20static 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 */
30static 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(). */
41static 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. */
55typedef 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
72typedef 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 */
87static const unsigned long MAX_NFRAME = Py_MIN(UINT16_MAX, ((SIZE_MAX - sizeof(traceback_t)) / sizeof(frame_t) + 1));
88
89
90static PyObject *unknown_filename = NULL;
91static traceback_t tracemalloc_empty_traceback;
92
93/* Trace of a memory block */
94typedef 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(). */
105static size_t tracemalloc_traced_memory = 0;
106
107/* Peak size in bytes of traced memory.
108 Protected by TABLES_LOCK(). */
109static 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 */
114static _Py_hashtable_t *tracemalloc_filenames = NULL;
115
116/* Buffer to store a new traceback in traceback_new().
117 Protected by the GIL. */
118static 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 */
123static _Py_hashtable_t *tracemalloc_tracebacks = NULL;
124
125/* pointer (void*) => trace (trace_t*).
126 Protected by TABLES_LOCK(). */
127static _Py_hashtable_t *tracemalloc_traces = NULL;
128
129/* domain (unsigned int) => traces (_Py_hashtable_t).
130 Protected by TABLES_LOCK(). */
131static _Py_hashtable_t *tracemalloc_domains = NULL;
132
133
134#ifdef TRACE_DEBUG
135static void
136tracemalloc_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
152static 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
157static int
158get_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
172static void
173set_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 */
191static int tracemalloc_reentrant = 0;
192
193static int
194get_reentrant(void)
195{
196 return tracemalloc_reentrant;
197}
198
199static void
200set_reentrant(int reentrant)
201{
202 assert(reentrant != tracemalloc_reentrant);
203 tracemalloc_reentrant = reentrant;
204}
205#endif
206
207
208static Py_uhash_t
209hashtable_hash_pyobject(const void *key)
210{
211 PyObject *obj = (PyObject *)key;
212 return PyObject_Hash(obj);
213}
214
215
216static int
217hashtable_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
230static Py_uhash_t
231hashtable_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
238static _Py_hashtable_t *
239hashtable_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
251static void*
252raw_malloc(size_t size)
253{
254 return allocators.raw.malloc(allocators.raw.ctx, size);
255}
256
257static void
258raw_free(void *ptr)
259{
260 allocators.raw.free(allocators.raw.ctx, ptr);
261}
262
263
264static Py_uhash_t
265hashtable_hash_traceback(const void *key)
266{
267 const traceback_t *traceback = (const traceback_t *)key;
268 return traceback->hash;
269}
270
271
272static int
273hashtable_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
301static void
302tracemalloc_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
361static Py_uhash_t
362traceback_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
387static void
388traceback_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
416static traceback_t *
417traceback_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
466static _Py_hashtable_t*
467tracemalloc_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
475static _Py_hashtable_t*
476tracemalloc_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
485static _Py_hashtable_t*
486tracemalloc_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
497static void
498tracemalloc_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
520static int
521tracemalloc_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
580static void*
581tracemalloc_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
607static void*
608tracemalloc_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
658static void
659tracemalloc_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
677static void*
678tracemalloc_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
702static void*
703tracemalloc_malloc_gil(void *ctx, size_t size)
704{
705 return tracemalloc_alloc_gil(0, ctx, 1, size);
706}
707
708
709static void*
710tracemalloc_calloc_gil(void *ctx, size_t nelem, size_t elsize)
711{
712 return tracemalloc_alloc_gil(1, ctx, nelem, elsize);
713}
714
715
716static void*
717tracemalloc_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
750static void*
751tracemalloc_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
778static void*
779tracemalloc_raw_malloc(void *ctx, size_t size)
780{
781 return tracemalloc_raw_alloc(0, ctx, 1, size);
782}
783
784
785static void*
786tracemalloc_raw_calloc(void *ctx, size_t nelem, size_t elsize)
787{
788 return tracemalloc_raw_alloc(1, ctx, nelem, elsize);
789}
790
791
792static void*
793tracemalloc_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
827static void
828tracemalloc_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 */
836static void
837tracemalloc_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
855static int
856tracemalloc_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
924static void
925tracemalloc_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
954static int
955tracemalloc_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
1018static void
1019tracemalloc_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
1046Return True if the tracemalloc module is tracing Python memory allocations.
1047[clinic start generated code]*/
1048
1049static PyObject *
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
1060Clear traces of memory blocks allocated by Python.
1061[clinic start generated code]*/
1062
1063static PyObject *
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
1078static PyObject*
1079frame_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
1101static PyObject*
1102traceback_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
1140static PyObject*
1141trace_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
1183typedef 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
1192static int
1193tracemalloc_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
1214static _Py_hashtable_t*
1215tracemalloc_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
1233static int
1234tracemalloc_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
1255static _Py_hashtable_t*
1256tracemalloc_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
1274static int
1275tracemalloc_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
1299static int
1300tracemalloc_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
1316static void
1317tracemalloc_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
1328Get traces of all memory blocks allocated by Python.
1329
1330Return a list of (size: int, traceback: tuple) tuples.
1331traceback is a tuple of (filename: str, lineno: int) tuples.
1332
1333Return an empty list if the tracemalloc module is disabled.
1334[clinic start generated code]*/
1335
1336static PyObject *
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
1397no_memory:
1398 PyErr_NoMemory();
1399
1400error:
1401 Py_CLEAR(get_traces.list);
1402
1403finally:
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
1418static traceback_t*
1419tracemalloc_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
1451Get the traceback where the Python object obj was allocated.
1452
1453Return a tuple of (filename: str, lineno: int) tuples.
1454Return None if the tracemalloc module is disabled or did not
1455trace the allocation of the object.
1456[clinic start generated code]*/
1457
1458static PyObject *
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
1484static void
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. */
1496void
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
1529Start tracing Python memory allocations.
1530
1531Also set the maximum number of frames stored in the traceback of a
1532trace to nframe.
1533[clinic start generated code]*/
1534
1535static PyObject *
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
1549Stop tracing Python memory allocations.
1550
1551Also clear traces of memory blocks allocated by Python.
1552[clinic start generated code]*/
1553
1554static PyObject *
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
1566Get the maximum number of frames stored in the traceback of a trace.
1567
1568By default, a trace of an allocated memory block only stores
1569the most recent frame: the limit is 1.
1570[clinic start generated code]*/
1571
1572static PyObject *
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
1580static int
1581tracemalloc_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
1595Get the memory usage in bytes of the tracemalloc module.
1596
1597This memory is used internally to trace memory allocations.
1598[clinic start generated code]*/
1599
1600static PyObject *
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
1623Get the current size and peak size of memory blocks traced by tracemalloc.
1624
1625Returns a tuple: (current: int, peak: int).
1626[clinic start generated code]*/
1627
1628static PyObject *
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
1648Set the peak size of memory blocks traced by tracemalloc to the current size.
1649
1650Do nothing if the tracemalloc module is not tracing memory allocations.
1651
1652[clinic start generated code]*/
1653
1654static PyObject *
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
1670static 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
1685PyDoc_STRVAR(module_doc,
1686"Debug module to trace memory blocks allocated by Python.");
1687
1688static 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
1697PyMODINIT_FUNC
1698PyInit__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
1714int
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
1725void
1726_PyTraceMalloc_Fini(void)
1727{
1728 assert(PyGILState_Check());
1729 tracemalloc_deinit();
1730}
1731
1732int
1733PyTraceMalloc_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
1755int
1756PyTraceMalloc_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. */
1776int
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
1814PyObject*
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