mirror of
https://github.com/python/cpython.git
synced 2025-12-04 00:30:19 +00:00
bpo-35053: Enhance tracemalloc to trace free lists (GH-10063)
tracemalloc now tries to update the traceback when an object is reused from a "free list" (optimization for faster object creation, used by the builtin list type for example). Changes: * Add _PyTraceMalloc_NewReference() function which tries to update the Python traceback of a Python object. * _Py_NewReference() now calls _PyTraceMalloc_NewReference(). * Add an unit test.
This commit is contained in:
parent
d7c3e5f0e8
commit
9e00e80e21
7 changed files with 157 additions and 51 deletions
|
|
@ -29,27 +29,6 @@ static struct {
|
|||
PyMemAllocatorEx obj;
|
||||
} allocators;
|
||||
|
||||
static struct {
|
||||
/* Module initialized?
|
||||
Variable protected by the GIL */
|
||||
enum {
|
||||
TRACEMALLOC_NOT_INITIALIZED,
|
||||
TRACEMALLOC_INITIALIZED,
|
||||
TRACEMALLOC_FINALIZED
|
||||
} initialized;
|
||||
|
||||
/* Is tracemalloc tracing memory allocations?
|
||||
Variable protected by the GIL */
|
||||
int tracing;
|
||||
|
||||
/* limit of the number of frames in a traceback, 1 by default.
|
||||
Variable protected by the GIL. */
|
||||
int max_nframe;
|
||||
|
||||
/* use domain in trace key?
|
||||
Variable protected by the GIL. */
|
||||
int use_domain;
|
||||
} tracemalloc_config = {TRACEMALLOC_NOT_INITIALIZED, 0, 1, 0};
|
||||
|
||||
#if defined(TRACE_RAW_MALLOC)
|
||||
/* This lock is needed because tracemalloc_free() is called without
|
||||
|
|
@ -459,7 +438,7 @@ traceback_get_frames(traceback_t *traceback)
|
|||
tracemalloc_get_frame(pyframe, &traceback->frames[traceback->nframe]);
|
||||
assert(traceback->frames[traceback->nframe].filename != NULL);
|
||||
traceback->nframe++;
|
||||
if (traceback->nframe == tracemalloc_config.max_nframe)
|
||||
if (traceback->nframe == _Py_tracemalloc_config.max_nframe)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -540,7 +519,7 @@ tracemalloc_use_domain(void)
|
|||
{
|
||||
_Py_hashtable_t *new_traces = NULL;
|
||||
|
||||
assert(!tracemalloc_config.use_domain);
|
||||
assert(!_Py_tracemalloc_config.use_domain);
|
||||
|
||||
new_traces = hashtable_new(sizeof(pointer_t),
|
||||
sizeof(trace_t),
|
||||
|
|
@ -560,7 +539,7 @@ tracemalloc_use_domain(void)
|
|||
_Py_hashtable_destroy(tracemalloc_traces);
|
||||
tracemalloc_traces = new_traces;
|
||||
|
||||
tracemalloc_config.use_domain = 1;
|
||||
_Py_tracemalloc_config.use_domain = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -572,9 +551,9 @@ tracemalloc_remove_trace(unsigned int domain, uintptr_t ptr)
|
|||
trace_t trace;
|
||||
int removed;
|
||||
|
||||
assert(tracemalloc_config.tracing);
|
||||
assert(_Py_tracemalloc_config.tracing);
|
||||
|
||||
if (tracemalloc_config.use_domain) {
|
||||
if (_Py_tracemalloc_config.use_domain) {
|
||||
pointer_t key = {ptr, domain};
|
||||
removed = _Py_HASHTABLE_POP(tracemalloc_traces, key, trace);
|
||||
}
|
||||
|
|
@ -603,14 +582,14 @@ tracemalloc_add_trace(unsigned int domain, uintptr_t ptr,
|
|||
_Py_hashtable_entry_t* entry;
|
||||
int res;
|
||||
|
||||
assert(tracemalloc_config.tracing);
|
||||
assert(_Py_tracemalloc_config.tracing);
|
||||
|
||||
traceback = traceback_new();
|
||||
if (traceback == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!tracemalloc_config.use_domain && domain != DEFAULT_DOMAIN) {
|
||||
if (!_Py_tracemalloc_config.use_domain && domain != DEFAULT_DOMAIN) {
|
||||
/* first trace using a non-zero domain whereas traces use compact
|
||||
(uintptr_t) keys: switch to pointer_t keys. */
|
||||
if (tracemalloc_use_domain() < 0) {
|
||||
|
|
@ -618,7 +597,7 @@ tracemalloc_add_trace(unsigned int domain, uintptr_t ptr,
|
|||
}
|
||||
}
|
||||
|
||||
if (tracemalloc_config.use_domain) {
|
||||
if (_Py_tracemalloc_config.use_domain) {
|
||||
entry = _Py_HASHTABLE_GET_ENTRY(tracemalloc_traces, key);
|
||||
}
|
||||
else {
|
||||
|
|
@ -639,7 +618,7 @@ tracemalloc_add_trace(unsigned int domain, uintptr_t ptr,
|
|||
trace.size = size;
|
||||
trace.traceback = traceback;
|
||||
|
||||
if (tracemalloc_config.use_domain) {
|
||||
if (_Py_tracemalloc_config.use_domain) {
|
||||
res = _Py_HASHTABLE_SET(tracemalloc_traces, key, trace);
|
||||
}
|
||||
else {
|
||||
|
|
@ -956,13 +935,13 @@ tracemalloc_clear_traces(void)
|
|||
static int
|
||||
tracemalloc_init(void)
|
||||
{
|
||||
if (tracemalloc_config.initialized == TRACEMALLOC_FINALIZED) {
|
||||
if (_Py_tracemalloc_config.initialized == TRACEMALLOC_FINALIZED) {
|
||||
PyErr_SetString(PyExc_RuntimeError,
|
||||
"the tracemalloc module has been unloaded");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (tracemalloc_config.initialized == TRACEMALLOC_INITIALIZED)
|
||||
if (_Py_tracemalloc_config.initialized == TRACEMALLOC_INITIALIZED)
|
||||
return 0;
|
||||
|
||||
PyMem_GetAllocator(PYMEM_DOMAIN_RAW, &allocators.raw);
|
||||
|
|
@ -996,7 +975,7 @@ tracemalloc_init(void)
|
|||
hashtable_hash_traceback,
|
||||
hashtable_compare_traceback);
|
||||
|
||||
if (tracemalloc_config.use_domain) {
|
||||
if (_Py_tracemalloc_config.use_domain) {
|
||||
tracemalloc_traces = hashtable_new(sizeof(pointer_t),
|
||||
sizeof(trace_t),
|
||||
hashtable_hash_pointer_t,
|
||||
|
|
@ -1026,7 +1005,7 @@ tracemalloc_init(void)
|
|||
tracemalloc_empty_traceback.frames[0].lineno = 0;
|
||||
tracemalloc_empty_traceback.hash = traceback_hash(&tracemalloc_empty_traceback);
|
||||
|
||||
tracemalloc_config.initialized = TRACEMALLOC_INITIALIZED;
|
||||
_Py_tracemalloc_config.initialized = TRACEMALLOC_INITIALIZED;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1034,9 +1013,9 @@ tracemalloc_init(void)
|
|||
static void
|
||||
tracemalloc_deinit(void)
|
||||
{
|
||||
if (tracemalloc_config.initialized != TRACEMALLOC_INITIALIZED)
|
||||
if (_Py_tracemalloc_config.initialized != TRACEMALLOC_INITIALIZED)
|
||||
return;
|
||||
tracemalloc_config.initialized = TRACEMALLOC_FINALIZED;
|
||||
_Py_tracemalloc_config.initialized = TRACEMALLOC_FINALIZED;
|
||||
|
||||
tracemalloc_stop();
|
||||
|
||||
|
|
@ -1077,13 +1056,13 @@ tracemalloc_start(int max_nframe)
|
|||
return -1;
|
||||
}
|
||||
|
||||
if (tracemalloc_config.tracing) {
|
||||
if (_Py_tracemalloc_config.tracing) {
|
||||
/* hook already installed: do nothing */
|
||||
return 0;
|
||||
}
|
||||
|
||||
assert(1 <= max_nframe && max_nframe <= MAX_NFRAME);
|
||||
tracemalloc_config.max_nframe = max_nframe;
|
||||
_Py_tracemalloc_config.max_nframe = max_nframe;
|
||||
|
||||
/* allocate a buffer to store a new traceback */
|
||||
size = TRACEBACK_SIZE(max_nframe);
|
||||
|
|
@ -1119,7 +1098,7 @@ tracemalloc_start(int max_nframe)
|
|||
PyMem_SetAllocator(PYMEM_DOMAIN_OBJ, &alloc);
|
||||
|
||||
/* everything is ready: start tracing Python memory allocations */
|
||||
tracemalloc_config.tracing = 1;
|
||||
_Py_tracemalloc_config.tracing = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1128,11 +1107,11 @@ tracemalloc_start(int max_nframe)
|
|||
static void
|
||||
tracemalloc_stop(void)
|
||||
{
|
||||
if (!tracemalloc_config.tracing)
|
||||
if (!_Py_tracemalloc_config.tracing)
|
||||
return;
|
||||
|
||||
/* stop tracing Python memory allocations */
|
||||
tracemalloc_config.tracing = 0;
|
||||
_Py_tracemalloc_config.tracing = 0;
|
||||
|
||||
/* unregister the hook on memory allocators */
|
||||
#ifdef TRACE_RAW_MALLOC
|
||||
|
|
@ -1160,7 +1139,7 @@ static PyObject *
|
|||
_tracemalloc_is_tracing_impl(PyObject *module)
|
||||
/*[clinic end generated code: output=2d763b42601cd3ef input=af104b0a00192f63]*/
|
||||
{
|
||||
return PyBool_FromLong(tracemalloc_config.tracing);
|
||||
return PyBool_FromLong(_Py_tracemalloc_config.tracing);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1174,7 +1153,7 @@ static PyObject *
|
|||
_tracemalloc_clear_traces_impl(PyObject *module)
|
||||
/*[clinic end generated code: output=a86080ee41b84197 input=0dab5b6c785183a5]*/
|
||||
{
|
||||
if (!tracemalloc_config.tracing)
|
||||
if (!_Py_tracemalloc_config.tracing)
|
||||
Py_RETURN_NONE;
|
||||
|
||||
set_reentrant(1);
|
||||
|
|
@ -1299,7 +1278,7 @@ tracemalloc_get_traces_fill(_Py_hashtable_t *traces, _Py_hashtable_entry_t *entr
|
|||
PyObject *tracemalloc_obj;
|
||||
int res;
|
||||
|
||||
if (tracemalloc_config.use_domain) {
|
||||
if (_Py_tracemalloc_config.use_domain) {
|
||||
pointer_t key;
|
||||
_Py_HASHTABLE_ENTRY_READ_KEY(traces, entry, key);
|
||||
domain = key.domain;
|
||||
|
|
@ -1359,7 +1338,7 @@ _tracemalloc__get_traces_impl(PyObject *module)
|
|||
if (get_traces.list == NULL)
|
||||
goto error;
|
||||
|
||||
if (!tracemalloc_config.tracing)
|
||||
if (!_Py_tracemalloc_config.tracing)
|
||||
return get_traces.list;
|
||||
|
||||
/* the traceback hash table is used temporarily to intern traceback tuple
|
||||
|
|
@ -1414,11 +1393,11 @@ tracemalloc_get_traceback(unsigned int domain, uintptr_t ptr)
|
|||
trace_t trace;
|
||||
int found;
|
||||
|
||||
if (!tracemalloc_config.tracing)
|
||||
if (!_Py_tracemalloc_config.tracing)
|
||||
return NULL;
|
||||
|
||||
TABLES_LOCK();
|
||||
if (tracemalloc_config.use_domain) {
|
||||
if (_Py_tracemalloc_config.use_domain) {
|
||||
pointer_t key = {ptr, domain};
|
||||
found = _Py_HASHTABLE_GET(tracemalloc_traces, key, trace);
|
||||
}
|
||||
|
|
@ -1558,7 +1537,7 @@ static PyObject *
|
|||
_tracemalloc_get_traceback_limit_impl(PyObject *module)
|
||||
/*[clinic end generated code: output=d556d9306ba95567 input=da3cd977fc68ae3b]*/
|
||||
{
|
||||
return PyLong_FromLong(tracemalloc_config.max_nframe);
|
||||
return PyLong_FromLong(_Py_tracemalloc_config.max_nframe);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1603,7 +1582,7 @@ _tracemalloc_get_traced_memory_impl(PyObject *module)
|
|||
{
|
||||
Py_ssize_t size, peak_size;
|
||||
|
||||
if (!tracemalloc_config.tracing)
|
||||
if (!_Py_tracemalloc_config.tracing)
|
||||
return Py_BuildValue("ii", 0, 0);
|
||||
|
||||
TABLES_LOCK();
|
||||
|
|
@ -1681,7 +1660,7 @@ PyTraceMalloc_Track(unsigned int domain, uintptr_t ptr,
|
|||
int res;
|
||||
PyGILState_STATE gil_state;
|
||||
|
||||
if (!tracemalloc_config.tracing) {
|
||||
if (!_Py_tracemalloc_config.tracing) {
|
||||
/* tracemalloc is not tracing: do nothing */
|
||||
return -2;
|
||||
}
|
||||
|
|
@ -1700,7 +1679,7 @@ PyTraceMalloc_Track(unsigned int domain, uintptr_t ptr,
|
|||
int
|
||||
PyTraceMalloc_Untrack(unsigned int domain, uintptr_t ptr)
|
||||
{
|
||||
if (!tracemalloc_config.tracing) {
|
||||
if (!_Py_tracemalloc_config.tracing) {
|
||||
/* tracemalloc is not tracing: do nothing */
|
||||
return -2;
|
||||
}
|
||||
|
|
@ -1713,6 +1692,60 @@ PyTraceMalloc_Untrack(unsigned int domain, uintptr_t ptr)
|
|||
}
|
||||
|
||||
|
||||
/* If the object memory block is already traced, update its trace
|
||||
with the current Python traceback.
|
||||
|
||||
Do nothing if tracemalloc is not tracing memory allocations
|
||||
or if the object memory block is not already traced. */
|
||||
int
|
||||
_PyTraceMalloc_NewReference(PyObject *op)
|
||||
{
|
||||
assert(PyGILState_Check());
|
||||
|
||||
if (!_Py_tracemalloc_config.tracing) {
|
||||
/* tracemalloc is not tracing: do nothing */
|
||||
return -1;
|
||||
}
|
||||
|
||||
uintptr_t ptr;
|
||||
PyTypeObject *type = Py_TYPE(op);
|
||||
if (PyType_IS_GC(type)) {
|
||||
ptr = (uintptr_t)((char *)op - sizeof(PyGC_Head));
|
||||
}
|
||||
else {
|
||||
ptr = (uintptr_t)op;
|
||||
}
|
||||
|
||||
_Py_hashtable_entry_t* entry;
|
||||
int res = -1;
|
||||
|
||||
TABLES_LOCK();
|
||||
if (_Py_tracemalloc_config.use_domain) {
|
||||
pointer_t key = {ptr, DEFAULT_DOMAIN};
|
||||
entry = _Py_HASHTABLE_GET_ENTRY(tracemalloc_traces, key);
|
||||
}
|
||||
else {
|
||||
entry = _Py_HASHTABLE_GET_ENTRY(tracemalloc_traces, ptr);
|
||||
}
|
||||
|
||||
if (entry != NULL) {
|
||||
/* update the traceback of the memory block */
|
||||
traceback_t *traceback = traceback_new();
|
||||
if (traceback != NULL) {
|
||||
trace_t trace;
|
||||
_Py_HASHTABLE_ENTRY_READ_DATA(tracemalloc_traces, entry, trace);
|
||||
trace.traceback = traceback;
|
||||
_Py_HASHTABLE_ENTRY_WRITE_DATA(tracemalloc_traces, entry, trace);
|
||||
res = 0;
|
||||
}
|
||||
}
|
||||
/* else: cannot track the object, its memory block size is unknown */
|
||||
TABLES_UNLOCK();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
PyObject*
|
||||
_PyTraceMalloc_GetTraceback(unsigned int domain, uintptr_t ptr)
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue