-
- Notifications
You must be signed in to change notification settings - Fork 33.5k
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 to PyInterpreterState (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() | <original malloc() or calloc()> | <--- | |
- | tracemalloc_realloc() | <original realloc() or free()> | <--- | |
- | tracemalloc_raw_alloc() | tracemalloc_alloc() * | <original malloc() or calloc()> | |
- | tracemalloc_alloc_gil() | tracemalloc_alloc() | <original malloc() or calloc()> | |
| raw | ||||
tracemalloc_raw_malloc() | tracemalloc_alloc() | <original malloc()> | tracemalloc_raw_alloc() | |
tracemalloc_raw_calloc() | tracemalloc_alloc() | <original calloc()> | tracemalloc_raw_alloc() | |
tracemalloc_raw_realloc() | tracemalloc_realloc() * | <original realloc()> | ||
tracemalloc_free() | <original free()> | <--- | ||
| mem | ||||
| obj | ||||
tracemalloc_malloc_gil() | tracemalloc_alloc_gil() | <--- | ||
tracemalloc_calloc_gil() | tracemalloc_alloc_gil() | <--- | ||
tracemalloc_realloc_gil() | tracemalloc_realloc() | <original realloc()> | ||
tracemalloc_free() | <original free()> | <--- |
* Note that tracemalloc_raw_alloc() wraps the tracemalloc_alloc() call
with PyGILState_Ensure()/PyGILState_Release().
Likewise for tracemalloc_raw_realloc() where it calls tracemalloc_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 in 0675b8f
| struct | |
| #ifdef __GNUC__ | |
| __attribute__((packed)) | |
| #endif | |
| tracemalloc_frame { | |
| /* filename cannot be NULL: "<unknown>" is used if the Python frame | |
| filename is NULL */ | |
| PyObject *filename; | |
| unsigned int lineno; | |
| }; | |
| #ifdef _MSC_VER | |
| #pragma pack(pop) | |
| #endif |
cpython/Include/internal/pycore_tracemalloc.h
Lines 57 to 64 in 0675b8f
| struct tracemalloc_traceback { | |
| Py_uhash_t hash; | |
| /* Number of frames stored */ | |
| uint16_t nframe; | |
| /* Total number of frames the traceback had */ | |
| uint16_t total_nframe; | |
| struct tracemalloc_frame frames[1]; | |
| }; |
cpython/Include/internal/pycore_tracemalloc.h
Lines 57 to 107 in 0675b8f
| struct tracemalloc_traceback { | |
| Py_uhash_t hash; | |
| /* Number of frames stored */ | |
| uint16_t nframe; | |
| /* Total number of frames the traceback had */ | |
| uint16_t total_nframe; | |
| struct tracemalloc_frame frames[1]; | |
| }; | |
| struct _tracemalloc_runtime_state { | |
| struct _PyTraceMalloc_Config config; | |
| /* Protected by the GIL */ | |
| struct { | |
| PyMemAllocatorEx mem; | |
| PyMemAllocatorEx raw; | |
| PyMemAllocatorEx obj; | |
| } allocators; | |
| #if defined(TRACE_RAW_MALLOC) | |
| PyThread_type_lock tables_lock; | |
| #endif | |
| /* Size in bytes of currently traced memory. | |
| Protected by TABLES_LOCK(). */ | |
| size_t traced_memory; | |
| /* Peak size in bytes of traced memory. | |
| Protected by TABLES_LOCK(). */ | |
| size_t peak_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. */ | |
| struct tracemalloc_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; | |
| struct tracemalloc_traceback empty_traceback; | |
| Py_tss_t reentrant_key; | |
| }; |
cpython/Modules/_tracemalloc.c
Lines 54 to 55 in 0675b8f
| typedef struct tracemalloc_frame frame_t; | |
| typedef struct tracemalloc_traceback traceback_t; |
cpython/Modules/_tracemalloc.c
Lines 69 to 76 in 0675b8f
| /* Trace of a memory block */ | |
| typedef struct { | |
| /* Size of the memory block in bytes */ | |
| size_t size; | |
| /* 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 a set of objects | |
traceback | struct tracemalloc_traceback * | GIL | a temporary buffer | |
tracebacks | _Py_hashtable_t * | GIL | interned; effectively a set of traceback_t | |
traces | _Py_hashtable_t * | tables_lock | void-ptr -> trace_t | |
domains | _Py_hashtable_t * | tables_lock | domain -> _Py_hashtable_t * (per-domain traces) | |
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