Skip to content

Commit 957b2cc

Browse files
raminfpvstinner
andauthored
gh-145846: Fix memory leak in _lsprof clearEntries() context chain (#145847)
clearEntries() only freed the top currentProfilerContext but did not walk the previous linked list. When clear() is called during active profiling with nested calls, all contexts except the top one were leaked. Fix by iterating the entire linked list, matching the existing freelistProfilerContext cleanup pattern. Co-authored-by: Victor Stinner <vstinner@python.org>
1 parent 03d712e commit 957b2cc

File tree

3 files changed

+29
-3
lines changed

3 files changed

+29
-3
lines changed

Lib/test/test_profiling/test_tracing_profiler.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,28 @@ def gen():
142142

143143
self.assertTrue(any("throw" in func[2] for func in pr.stats.keys())),
144144

145+
def test_clear_with_nested_calls(self):
146+
# Calling clear() during nested profiled calls should not leak
147+
# ProfilerContexts. clearEntries() must walk the entire linked list,
148+
# not just free the top context.
149+
import _lsprof
150+
151+
def level3(profiler):
152+
profiler.clear()
153+
154+
def level2(profiler):
155+
level3(profiler)
156+
157+
def level1(profiler):
158+
level2(profiler)
159+
160+
profiler = _lsprof.Profiler()
161+
profiler.enable()
162+
for _ in range(100):
163+
level1(profiler)
164+
profiler.disable()
165+
profiler.clear()
166+
145167
def test_bad_descriptor(self):
146168
# gh-132250
147169
# cProfile should not crash when the profiler callback fails to locate
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix memory leak in ``_lsprof`` when ``clear()`` is called during active
2+
profiling with nested calls. ``clearEntries()`` now walks the entire
3+
``currentProfilerContext`` linked list instead of only freeing the top context.

Modules/_lsprof.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -292,9 +292,10 @@ static void clearEntries(ProfilerObject *pObj)
292292
RotatingTree_Enum(pObj->profilerEntries, freeEntry, NULL);
293293
pObj->profilerEntries = EMPTY_ROTATING_TREE;
294294
/* release the memory hold by the ProfilerContexts */
295-
if (pObj->currentProfilerContext) {
296-
PyMem_Free(pObj->currentProfilerContext);
297-
pObj->currentProfilerContext = NULL;
295+
while (pObj->currentProfilerContext) {
296+
ProfilerContext *pContext = pObj->currentProfilerContext;
297+
pObj->currentProfilerContext = pContext->previous;
298+
PyMem_Free(pContext);
298299
}
299300
while (pObj->freelistProfilerContext) {
300301
ProfilerContext *c = pObj->freelistProfilerContext;

0 commit comments

Comments
 (0)