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