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