Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork33.7k
Description
(See#100227.)
Currently the tracemalloc module has some state in_PyRuntimeState, including objects, which is shared by all interpreters. Interpreters should be isolated from each other, for a variety of reasons (including the possibility of a per-interpreter GIL). Isolating the module will involve moving some of the state toPyInterpreterState (and, under per-interpreter GIL, guarding other state with a global lock).
Analysis:
(expand)
Allocators
The module installs a custom allocator (using the PEP 445 API
(docs).
(expand)
the allocator functions:
| domain | func | wraps | wraps (reentrant) | actually wraps |
|---|---|---|---|---|
- | tracemalloc_alloc() | <originalmalloc() orcalloc()> | <--- | |
- | tracemalloc_realloc() | <originalrealloc() orfree()> | <--- | |
- | tracemalloc_raw_alloc() | tracemalloc_alloc()* | <originalmalloc() orcalloc()> | |
- | tracemalloc_alloc_gil() | tracemalloc_alloc() | <originalmalloc() orcalloc()> | |
| raw | ||||
tracemalloc_raw_malloc() | tracemalloc_alloc() | <originalmalloc()> | tracemalloc_raw_alloc() | |
tracemalloc_raw_calloc() | tracemalloc_alloc() | <originalcalloc()> | tracemalloc_raw_alloc() | |
tracemalloc_raw_realloc() | tracemalloc_realloc()* | <originalrealloc()> | ||
tracemalloc_free() | <originalfree()> | <--- | ||
| mem | ||||
| obj | ||||
tracemalloc_malloc_gil() | tracemalloc_alloc_gil() | <--- | ||
tracemalloc_calloc_gil() | tracemalloc_alloc_gil() | <--- | ||
tracemalloc_realloc_gil() | tracemalloc_realloc() | <originalrealloc()> | ||
tracemalloc_free() | <originalfree()> | <--- |
* Note thattracemalloc_raw_alloc() wraps thetracemalloc_alloc() call
withPyGILState_Ensure()/PyGILState_Release().
Likewise fortracemalloc_raw_realloc() where it callstracemalloc_realloc().
In no other case does an allocator function use the GILState API for any calls.
State
Fields
(expand)
https://github.com/python/cpython/blob/main/Include/internal/pycore_tracemalloc.h#L57-L107
https://github.com/python/cpython/blob/main/Include/internal/pycore_runtime.h#L147
raw
cpython/Include/internal/pycore_tracemalloc.h
Lines 43 to 55 in0675b8f
| struct | |
| #ifdef__GNUC__ | |
| __attribute__((packed)) | |
| #endif | |
| tracemalloc_frame { | |
| /* filename cannot be NULL: "<unknown>" is used if the Python frame | |
| filename is NULL */ | |
| PyObject*filename; | |
| unsignedintlineno; | |
| }; | |
| #ifdef_MSC_VER | |
| #pragma pack(pop) | |
| #endif |
cpython/Include/internal/pycore_tracemalloc.h
Lines 57 to 64 in0675b8f
| structtracemalloc_traceback { | |
| Py_uhash_thash; | |
| /* Number of frames stored */ | |
| uint16_tnframe; | |
| /* Total number of frames the traceback had */ | |
| uint16_ttotal_nframe; | |
| structtracemalloc_frameframes[1]; | |
| }; |
cpython/Include/internal/pycore_tracemalloc.h
Lines 57 to 107 in0675b8f
| structtracemalloc_traceback { | |
| Py_uhash_thash; | |
| /* Number of frames stored */ | |
| uint16_tnframe; | |
| /* Total number of frames the traceback had */ | |
| uint16_ttotal_nframe; | |
| structtracemalloc_frameframes[1]; | |
| }; | |
| struct_tracemalloc_runtime_state { | |
| struct_PyTraceMalloc_Configconfig; | |
| /* Protected by the GIL */ | |
| struct { | |
| PyMemAllocatorExmem; | |
| PyMemAllocatorExraw; | |
| PyMemAllocatorExobj; | |
| }allocators; | |
| #if defined(TRACE_RAW_MALLOC) | |
| PyThread_type_locktables_lock; | |
| #endif | |
| /* Size in bytes of currently traced memory. | |
| Protected by TABLES_LOCK(). */ | |
| size_ttraced_memory; | |
| /* Peak size in bytes of traced memory. | |
| Protected by TABLES_LOCK(). */ | |
| size_tpeak_traced_memory; | |
| /* Hash table used as a set to intern filenames: | |
| PyObject* => PyObject*. | |
| Protected by the GIL */ | |
| _Py_hashtable_t*filenames; | |
| /* Buffer to store a new traceback in traceback_new(). | |
| Protected by the GIL. */ | |
| structtracemalloc_traceback*traceback; | |
| /* Hash table used as a set to intern tracebacks: | |
| traceback_t* => traceback_t* | |
| Protected by the GIL */ | |
| _Py_hashtable_t*tracebacks; | |
| /* pointer (void*) => trace (trace_t*). | |
| Protected by TABLES_LOCK(). */ | |
| _Py_hashtable_t*traces; | |
| /* domain (unsigned int) => traces (_Py_hashtable_t). | |
| Protected by TABLES_LOCK(). */ | |
| _Py_hashtable_t*domains; | |
| structtracemalloc_tracebackempty_traceback; | |
| Py_tss_treentrant_key; | |
| }; |
cpython/Modules/_tracemalloc.c
Lines 54 to 55 in0675b8f
| typedefstructtracemalloc_frameframe_t; | |
| typedefstructtracemalloc_tracebacktraceback_t; |
cpython/Modules/_tracemalloc.c
Lines 69 to 76 in0675b8f
| /* Trace of a memory block */ | |
| typedefstruct { | |
| /* Size of the memory block in bytes */ | |
| size_tsize; | |
| /* Traceback where the memory block was allocated */ | |
| traceback_t*traceback; | |
| }trace_t; |
| name | type | protected by | #ifdef | notes |
|---|---|---|---|---|
config | struct _PyTraceMalloc_Config | |||
.initialized | enum {} | GIL | ||
.tracing | bool | GIL | ||
.max_nframe | int | GIL | ||
allocators | see PEP 445 (docs) | |||
.mem | PyMemAllocatorEx | GIL | ||
.raw | PyMemAllocatorEx | GIL | ||
.obj | PyMemAllocatorEx | GIL | ||
tables_lock | PyThread_type_lock | GIL | TRACE_RAW_MALLOC | |
traced_memory | size_t | tables_lock | ||
peak_traced_memory | size_t | tables_lock | ||
filenames | _Py_hashtable_t * | GIL | interned; effectively aset of objects | |
traceback | struct tracemalloc_traceback * | GIL | a temporary buffer | |
tracebacks | _Py_hashtable_t * | GIL | interned; effectively aset oftraceback_t | |
traces | _Py_hashtable_t * | tables_lock | void-ptr ->trace_t | |
domains | _Py_hashtable_t * | tables_lock | domain ->_Py_hashtable_t * (per-domaintraces) | |
empty_traceback | struct tracemalloc_traceback | ??? | ||
reentrant_key | Py_tss_t | ??? |
notes:
- each frame in
struct tracemalloc_tracebackholds a filename object traceback_tis a typedef forstruct tracemalloc_tracebackframe_tis a typedef forstruct tracemalloc_frame
hold objects:
filenamestraceback(filename in each frame)tracebacks(filename in each frame of each traceback)traces(filename in each frame of each traceback)domains(filename in each frame of each traceback in each domain)
Usage
(expand)
simple:
| name | context | get | set |
|---|---|---|---|
config | |||
.initialized | lifecycle | tracemalloc_init()tracemalloc_deinit() | tracemalloc_init()tracemalloc_deinit() |
.tracing | module | _tracemalloc_is_tracing_impl()_tracemalloc__get_traces_impl()_tracemalloc_clear_traces_impl()_tracemalloc_get_traceback_limit_impl()_tracemalloc_get_traced_memory_impl()_tracemalloc_reset_peak_impl() | |
| C-API | PyTraceMalloc_Track()PyTraceMalloc_Untrack()_PyTraceMalloc_NewReference()_PyMem_DumpTraceback() | ||
| lifecycle | tracemalloc_start()tracemalloc_stop() | tracemalloc_start()tracemalloc_stop() | |
| internal | tracemalloc_get_traceback() | ||
.max_nframe | module | _tracemalloc_get_traceback_limit_impl() | |
| lifecycle | tracemalloc_start() | ||
| internal | traceback_get_frames() | ||
allocators | |||
.mem | lifecycle | tracemalloc_start() +tracemalloc_stop() | |
.raw | lifecycle | tracemalloc_init() +tracemalloc_start() +tracemalloc_stop() | |
| internal | raw_malloc()raw_free() | ||
.obj | lifecycle | tracemalloc_start() +tracemalloc_stop() | |
tables_lock | module | _tracemalloc__get_traces_impl() +_tracemalloc_get_tracemalloc_memory_impl() +_tracemalloc_get_traced_memory_impl() +_tracemalloc_reset_peak_impl() + | |
| C-API | PyTraceMalloc_Track() +PyTraceMalloc_Untrack() +_PyTraceMalloc_NewReference() + | ||
| lifecycle | tracemalloc_init()tracemalloc_deinit() | tracemalloc_init() *tracemalloc_deinit() * | |
| allocator | tracemalloc_alloc() +tracemalloc_realloc() +tracemalloc_free() +tracemalloc_realloc_gil() +tracemalloc_raw_realloc() + | ||
| internal | tracemalloc_get_traceback() +tracemalloc_clear_traces() + | ||
traced_memory | module | _tracemalloc_get_traced_memory_impl()_tracemalloc_reset_peak_impl() | |
| internal | tracemalloc_add_trace() | tracemalloc_add_trace()tracemalloc_remove_trace()tracemalloc_clear_traces() | |
peak_traced_memory | module | _tracemalloc_get_traced_memory_impl() | _tracemalloc_reset_peak_impl() |
| internal | tracemalloc_add_trace() | tracemalloc_add_trace()tracemalloc_clear_traces() | |
filenames | module | _tracemalloc_get_tracemalloc_memory_impl() | |
| lifecycle | tracemalloc_init() * | ||
| internal | tracemalloc_get_frame()tracemalloc_clear_traces() + | ||
traceback | lifecycle | tracemalloc_stop() | tracemalloc_start() *tracemalloc_stop() * |
| internal | traceback_new() + | ||
tracebacks | module | _tracemalloc_get_tracemalloc_memory_impl() | |
| lifecycle | tracemalloc_init() *tracemalloc_deinit() + | ||
| internal | traceback_new() +tracemalloc_clear_traces() + | ||
traces | module | _tracemalloc__get_traces_impl()_tracemalloc_get_tracemalloc_memory_impl() | |
| C-API | _PyTraceMalloc_NewReference() | ||
| lifecycle | tracemalloc_deinit() + | tracemalloc_init() * | |
| internal | tracemalloc_get_traces_table()tracemalloc_add_trace() (indirect)tracemalloc_remove_trace() (indirect)tracemalloc_get_traceback() (indirect)tracemalloc_clear_traces() + | ||
domains | module | _tracemalloc__get_traces_impl()_tracemalloc_get_tracemalloc_memory_impl() | |
| lifecycle | tracemalloc_deinit() + | tracemalloc_init() * | |
| internal | tracemalloc_get_traces_table()tracemalloc_remove_trace() (indirect)tracemalloc_get_traceback() (indirect)tracemalloc_clear_traces() +tracemalloc_add_trace() + | ||
empty_traceback | lifecycle | tracemalloc_init() + | |
| internal | traceback_new() | ||
reentrant_key | lifecycle | tracemalloc_init() +tracemalloc_deinit() + | |
| allocator | tracemalloc_alloc_gil() (indirect) +tracemalloc_realloc_gil() (indirect) +tracemalloc_raw_alloc() (indirect) +tracemalloc_raw_realloc() (indirect) + | ||
| internal | get_reentrant()set_reentrant() + |
* the function allocates/deallocates the value (see below)
+ the function mutates the value (see below)
simple (extraneous):
| name | context | allocate/deallocate | get (assert-only) |
|---|---|---|---|
config.tracing | internal | tracemalloc_add_trace()tracemalloc_remove_trace() | |
tables_lock | lifecycle | tracemalloc_init()tracemalloc_deinit() | |
traced_memory | internal | tracemalloc_remove_trace() | |
filenames | lifecycle | tracemalloc_init() | |
traceback | lifecycle | tracemalloc_start()tracemalloc_stop() | tracemalloc_start() |
tracebacks | lifecycle | tracemalloc_init() | |
traces | lifecycle | tracemalloc_init() | |
domains | lifecycle | tracemalloc_init() |
mutation of complex fields:
| name | context | initialize | finalize | clear | modify |
|---|---|---|---|---|---|
allocators.mem | lifecycle | tracemalloc_start() | |||
allocators.raw | lifecycle | tracemalloc_init()tracemalloc_start() | |||
allocators.obj | lifecycle | tracemalloc_start() | |||
tables_lock | module | _tracemalloc__get_traces_impl()_tracemalloc_get_tracemalloc_memory_impl()_tracemalloc_get_traced_memory_impl()_tracemalloc_reset_peak_impl() | |||
| C-API | PyTraceMalloc_Track()PyTraceMalloc_Untrack()_PyTraceMalloc_NewReference() | ||||
| allocator | tracemalloc_alloc() | tracemalloc_alloc()tracemalloc_realloc()tracemalloc_free()tracemalloc_realloc_gil()tracemalloc_raw_realloc() | |||
| internal | tracemalloc_clear_traces()tracemalloc_get_traceback() | ||||
filenames | internal | tracemalloc_clear_traces() | tracemalloc_get_frame()tracemalloc_clear_traces() | ||
| lifecycle | tracemalloc_deinit() | ||||
traceback | internal | traceback_new() | |||
tracebacks | lifecycle | tracemalloc_deinit() | |||
| internal | tracemalloc_clear_traces() | traceback_new() | |||
traces | lifecycle | tracemalloc_deinit() | |||
| internal | tracemalloc_clear_traces() | ||||
domains | lifecycle | tracemalloc_deinit() | |||
| internal | tracemalloc_clear_traces() | tracemalloc_add_trace() | |||
reentrant_key | lifecycle | tracemalloc_init() | tracemalloc_deinit() | ||
| internal | set_reentrant() |
indirection:
| name | context | direct | indirect |
|---|---|---|---|
allocators.raw | lifecycle | tracemalloc_start()tracemalloc_copy_trace() | raw_malloc() |
tracemalloc_stop() | raw_free() | ||
| internal | traceback_new()tracemalloc_add_trace()tracemalloc_copy_trace() | raw_malloc() | |
traceback_new()tracemalloc_add_trace()tracemalloc_remove_trace() | raw_free() | ||
traces | internal | tracemalloc_add_trace()tracemalloc_remove_trace()tracemalloc_get_traceback() | tracemalloc_get_traces_table() |
domains | internal | tracemalloc_add_trace()tracemalloc_remove_trace()tracemalloc_get_traceback() | tracemalloc_get_traces_table() |
reentrant_key | allocator | tracemalloc_alloc_gil()tracemalloc_realloc_gil()tracemalloc_raw_alloc()tracemalloc_raw_realloc() | get_reentrant() |
tracemalloc_alloc_gil()tracemalloc_realloc_gil()tracemalloc_raw_alloc()tracemalloc_raw_realloc() | set_reentrant() |
Linked PRs
Metadata
Metadata
Assignees
Labels
Projects
Status