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