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