diff options
author | Victor Stinner <vstinner@redhat.com> | 2018-10-25 11:31:16 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-10-25 11:31:16 (GMT) |
commit | 9e00e80e213ebc37eff89ce72102c1f928ebc133 (patch) | |
tree | 50b24d69615a7994aeb4d776adc8666fcec5aafd /Modules | |
parent | d7c3e5f0e89cb807093e33165815c8bbd3c00f4b (diff) | |
download | cpython-9e00e80e213ebc37eff89ce72102c1f928ebc133.zip cpython-9e00e80e213ebc37eff89ce72102c1f928ebc133.tar.gz cpython-9e00e80e213ebc37eff89ce72102c1f928ebc133.tar.bz2 |
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.
Diffstat (limited to 'Modules')
-rw-r--r-- | Modules/_tracemalloc.c | 135 |
1 files changed, 84 insertions, 51 deletions
diff --git a/Modules/_tracemalloc.c b/Modules/_tracemalloc.c index e07022c..d736b24 100644 --- a/Modules/_tracemalloc.c +++ b/Modules/_tracemalloc.c @@ -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) { |