diff --git a/.bumpversion.cfg b/.bumpversion.cfg index d1bd9f9ba7..136749e5d4 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 1.19.0 +current_version = 1.19.1 commit = True message = Prepare for {new_version} release diff --git a/NEWS.rst b/NEWS.rst index 9f6b5117f5..d64cee4963 100644 --- a/NEWS.rst +++ b/NEWS.rst @@ -8,6 +8,12 @@ Changelog .. towncrier release notes start +memray 1.19.1 (2025-09-29) +-------------------------- + +- Fix a bug that caused Memray to refuse to produce stack traces for ``pymalloc`` allocations when ``--trace-python-allocators`` was used. (#832) + + memray 1.19.0 (2025-09-26) -------------------------- diff --git a/src/memray/_memray.pyx b/src/memray/_memray.pyx index 5a95a0a912..21dfe1ad3d 100644 --- a/src/memray/_memray.pyx +++ b/src/memray/_memray.pyx @@ -128,9 +128,11 @@ cpdef enum AllocatorType: MMAP = 14 MUNMAP = 15 +# Note: enumerator values are negative because they must be unique +# from the enumerator values of the AllocatorType enum above cpdef enum ObjectTrackingEvent: - OBJECT_CREATED = 1 - OBJECT_DESTROYED = 2 + OBJECT_CREATED = -1 + OBJECT_DESTROYED = -2 cpdef enum PythonAllocatorType: PYTHON_ALLOCATOR_PYMALLOC = 1 diff --git a/src/memray/_version.py b/src/memray/_version.py index d84d79d43f..90fa0968e4 100644 --- a/src/memray/_version.py +++ b/src/memray/_version.py @@ -1 +1 @@ -__version__ = "1.19.0" +__version__ = "1.19.1" diff --git a/tests/integration/test_tracking.py b/tests/integration/test_tracking.py index 98bd240c6e..2e61fa75db 100644 --- a/tests/integration/test_tracking.py +++ b/tests/integration/test_tracking.py @@ -1630,6 +1630,52 @@ def test_header_allocator(self, allocator, allocator_name, tmpdir): assert metadata.python_allocator == allocator_name +def test_pymalloc_with_python_stack_traces(tmp_path): + """Test that pymalloc allocations have Python stack traces""" + # GIVEN + allocator = PymallocMemoryAllocator(PymallocDomain.PYMALLOC_OBJECT) + output = tmp_path / "test.bin" + + def alloc_func3(): + return allocator.malloc(ALLOC_SIZE) + + def alloc_func2(): + return alloc_func3() + + def alloc_func1(): + return alloc_func2() + + # WHEN + with Tracker(output, trace_python_allocators=True): + res = alloc_func1() + if res: + allocator.free() + + if not res: + pytest.skip("PymallocMemoryAllocator not supported on this platform") + + # THEN + allocations = list(FileReader(output).get_allocation_records()) + allocs = [ + event + for event in allocations + if event.size == ALLOC_SIZE and event.allocator == AllocatorType.PYMALLOC_MALLOC + ] + assert len(allocs) == 1 + (alloc,) = allocs + + # Check the Python stack trace + traceback = list(alloc.stack_trace()) + assert len(traceback) > 0 + + # Verify our calling functions are in the stack + func_names = [frame[0] for frame in traceback] + assert "alloc_func3" in func_names + assert "alloc_func2" in func_names + assert "alloc_func1" in func_names + assert "test_pymalloc_with_python_stack_traces" in func_names + + class TestMemorySnapshots: @pytest.mark.valgrind def test_memory_snapshots_are_written(self, tmp_path):