Skip to content

Commit 873d86a

Browse files
committed
Fix enum value collision between AllocatorType and ObjectTrackingEvent
The ObjectTrackingEvent enum values were colliding with AllocatorType values. This caused a bug where OBJECT_DESTROYED (value 2) was incorrectly matching PYOBJECT_MALLOC in the deallocation check. This would incorrectly treat pymalloc allocations as deallocations when checking stack traces. The bug went undetected because we lacked tests for Python allocations with trace_python_allocators enabled. Fixed by changing ObjectTrackingEvent values to 10 and 20 to avoid collision. Added test coverage for pymalloc allocations with Python stack traces to prevent regressions.
1 parent 6cfb8a4 commit 873d86a

File tree

2 files changed

+48
-2
lines changed

2 files changed

+48
-2
lines changed

src/memray/_memray.pyx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,8 +129,8 @@ cpdef enum AllocatorType:
129129
MUNMAP = 15
130130

131131
cpdef enum ObjectTrackingEvent:
132-
OBJECT_CREATED = 1
133-
OBJECT_DESTROYED = 2
132+
OBJECT_CREATED = 10
133+
OBJECT_DESTROYED = 20
134134

135135
cpdef enum PythonAllocatorType:
136136
PYTHON_ALLOCATOR_PYMALLOC = 1

tests/integration/test_tracking.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1630,6 +1630,52 @@ def test_header_allocator(self, allocator, allocator_name, tmpdir):
16301630
assert metadata.python_allocator == allocator_name
16311631

16321632

1633+
def test_pymalloc_with_python_stack_traces(tmp_path):
1634+
"""Test that pymalloc allocations have Python stack traces when trace_python_allocators=True."""
1635+
# GIVEN
1636+
allocator = PymallocMemoryAllocator(PymallocDomain.PYMALLOC_OBJECT)
1637+
output = tmp_path / "test.bin"
1638+
1639+
def alloc_func3():
1640+
return allocator.malloc(ALLOC_SIZE)
1641+
1642+
def alloc_func2():
1643+
return alloc_func3()
1644+
1645+
def alloc_func1():
1646+
return alloc_func2()
1647+
1648+
# WHEN
1649+
with Tracker(output, trace_python_allocators=True):
1650+
res = alloc_func1()
1651+
if res:
1652+
allocator.free()
1653+
1654+
if not res:
1655+
pytest.skip("PymallocMemoryAllocator not supported on this platform")
1656+
1657+
# THEN
1658+
allocations = list(FileReader(output).get_allocation_records())
1659+
allocs = [
1660+
event
1661+
for event in allocations
1662+
if event.size == ALLOC_SIZE and event.allocator == AllocatorType.PYMALLOC_MALLOC
1663+
]
1664+
assert len(allocs) == 1
1665+
(alloc,) = allocs
1666+
1667+
# Check the Python stack trace
1668+
traceback = list(alloc.stack_trace())
1669+
assert len(traceback) > 0
1670+
1671+
# Verify our calling functions are in the stack
1672+
func_names = [frame[0] for frame in traceback]
1673+
assert "alloc_func3" in func_names
1674+
assert "alloc_func2" in func_names
1675+
assert "alloc_func1" in func_names
1676+
assert "test_pymalloc_with_python_stack_traces" in func_names
1677+
1678+
16331679
class TestMemorySnapshots:
16341680
@pytest.mark.valgrind
16351681
def test_memory_snapshots_are_written(self, tmp_path):

0 commit comments

Comments
 (0)