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