self.assertTrue(any("throw" in func[2] for func in pr.stats.keys())),
+ def test_clear_with_nested_calls(self):
+ # Calling clear() during nested profiled calls should not leak
+ # ProfilerContexts. clearEntries() must walk the entire linked list,
+ # not just free the top context.
+ import _lsprof
+
+ def level3(profiler):
+ profiler.clear()
+
+ def level2(profiler):
+ level3(profiler)
+
+ def level1(profiler):
+ level2(profiler)
+
+ profiler = _lsprof.Profiler()
+ profiler.enable()
+ for _ in range(100):
+ level1(profiler)
+ profiler.disable()
+ profiler.clear()
+
def test_bad_descriptor(self):
# gh-132250
# cProfile should not crash when the profiler callback fails to locate
--- /dev/null
+Fix memory leak in ``_lsprof`` when ``clear()`` is called during active
+profiling with nested calls. ``clearEntries()`` now walks the entire
+``currentProfilerContext`` linked list instead of only freeing the top context.
RotatingTree_Enum(pObj->profilerEntries, freeEntry, NULL);
pObj->profilerEntries = EMPTY_ROTATING_TREE;
/* release the memory hold by the ProfilerContexts */
- if (pObj->currentProfilerContext) {
- PyMem_Free(pObj->currentProfilerContext);
- pObj->currentProfilerContext = NULL;
+ while (pObj->currentProfilerContext) {
+ ProfilerContext *pContext = pObj->currentProfilerContext;
+ pObj->currentProfilerContext = pContext->previous;
+ PyMem_Free(pContext);
}
while (pObj->freelistProfilerContext) {
ProfilerContext *c = pObj->freelistProfilerContext;