- 
          
- 
                Notifications
    You must be signed in to change notification settings 
- Fork 33.3k
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()orcalloc()> | <--- | |
| - | tracemalloc_realloc() | <original realloc()orfree()> | <--- | |
| - | tracemalloc_raw_alloc() | tracemalloc_alloc()* | <original malloc()orcalloc()> | |
| - | tracemalloc_alloc_gil() | tracemalloc_alloc() | <original malloc()orcalloc()> | |
| 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 setof objects | |
| traceback | struct tracemalloc_traceback * | GIL | a temporary buffer | |
| tracebacks | _Py_hashtable_t * | GIL | interned; effectively a setoftraceback_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 for- struct tracemalloc_traceback
- frame_tis a typedef for- struct tracemalloc_frame
hold objects:
- filenames
- traceback(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