From c3688203847b66870c83c3c0b4d26c51c58e34c3 Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Thu, 24 Sep 2026 13:12:45 -0300 Subject: Check each subsystem's memory under one DebugAllocator, not two In a Debug build memory.zig put each subsystem allocator (core, frame, lsp, tree-sitter, image, PDF) under a DebugAllocator of its own, which is what reports that subsystem's leaks and makes memory.deinit panic on one. Beneath it, past the fixed buffer, was the allocator the shell passed in: init.gpa, which std.process.Init already makes a DebugAllocator in Debug. Every block that spilled out of a fixed buffer was tracked, checked and given stack traces twice, and in a test both layers sat on std.testing.allocator. A Debug build now puts the page allocator beneath the subsystem DebugAllocators, so each allocation passes through exactly one. The shells' own gpa is still std.process.Init's DebugAllocator for everything else, and a release build is unchanged: the subsystems sit on the gpa, no DebugAllocator anywhere. Co-Authored-By: Claude Opus 5.5 (1M context) --- src/memory.zig | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) (limited to 'src/memory.zig') diff --git a/src/memory.zig b/src/memory.zig index e5394b4f..a2c5547c 100644 --- a/src/memory.zig +++ b/src/memory.zig @@ -101,20 +101,26 @@ var pdf_debug: Debug = .init; // One session at a time; free its allocations before deinit. Concurrent LSP workers use the caller's allocator. pub fn init(fallback: Allocator) Allocators { - pardes_fallback.fallback_allocator = fallback; - frame_fallback.fallback_allocator = fallback; + // A Debug build puts each allocator under a DebugAllocator of its own, + // which reports that subsystem's leaks and bad frees (deinit panics on a + // leak). Beneath that goes the page allocator rather than `fallback`: in + // a Debug build `fallback` is std.process.Init's gpa, a DebugAllocator + // already, and a second one checked every block twice. + const backing = if (debug_enabled) std.heap.page_allocator else fallback; + pardes_fallback.fallback_allocator = backing; + frame_fallback.fallback_allocator = backing; frame_fallback.get_called = if (std.debug.runtime_safety) false else {}; - tree_sitter_fallback.fallback_allocator = fallback; + tree_sitter_fallback.fallback_allocator = backing; tree_sitter_fallback.get_called = if (std.debug.runtime_safety) false else {}; - image_fallback.fallback_allocator = fallback; + image_fallback.fallback_allocator = backing; image_fallback.get_called = if (std.debug.runtime_safety) false else {}; - pdf_fallback.fallback_allocator = fallback; + pdf_fallback.fallback_allocator = backing; pdf_fallback.get_called = if (std.debug.runtime_safety) false else {}; const raw: Allocators = .{ .pardes = pardes_fallback.get(), .frame = frame_fallback.get(), - .lsp = fallback, + .lsp = backing, .tree_sitter = tree_sitter_fallback.get(), .image = image_fallback.get(), .pdf = pdf_fallback.get(), -- cgit v1.3