-
-
Notifications
You must be signed in to change notification settings - Fork 33.2k
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