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