diff --git a/src/analysis.zig b/src/analysis.zig index a6880d0..afc3645 100644 --- a/src/analysis.zig +++ b/src/analysis.zig @@ -2010,6 +2010,7 @@ fn makeScopeInternal( } } + scopes.items[scope_idx].tests = tests.toOwnedSlice(); scopes.items[scope_idx].uses = uses.toOwnedSlice(); return; } diff --git a/src/debug_allocator.zig b/src/debug_allocator.zig index cb9f5b0..523784f 100644 --- a/src/debug_allocator.zig +++ b/src/debug_allocator.zig @@ -76,9 +76,12 @@ pub const AllocationInfo = struct { } }; +const stack_addresses_size = 15; + base_allocator: *std.mem.Allocator, info: AllocationInfo, max_bytes: usize, +allocation_strack_addresses: std.AutoHashMap(usize, [stack_addresses_size]usize), // Interface implementation allocator: std.mem.Allocator, @@ -88,6 +91,7 @@ pub fn init(base_allocator: *std.mem.Allocator, max_bytes: usize) DebugAllocator .base_allocator = base_allocator, .info = .{}, .max_bytes = max_bytes, + .allocation_strack_addresses = std.AutoHashMap(usize, [stack_addresses_size]usize).init(base_allocator), .allocator = .{ .allocFn = alloc, .resizeFn = resize, @@ -95,11 +99,24 @@ pub fn init(base_allocator: *std.mem.Allocator, max_bytes: usize) DebugAllocator }; } +pub fn deinit(self: DebugAllocator) void { + self.allocation_strack_addresses.deinit(); +} + fn alloc(allocator: *std.mem.Allocator, len: usize, ptr_align: u29, len_align: u29) error{OutOfMemory}![]u8 { const self = @fieldParentPtr(DebugAllocator, "allocator", allocator); + const ptr = try self.base_allocator.callAllocFn(len, ptr_align, len_align); self.info.allocation_stats.addSample(ptr.len); + var stack_addresses = std.mem.zeroes([stack_addresses_size + 2]usize); + var stack_trace = std.builtin.StackTrace{ + .instruction_addresses = &stack_addresses, + .index = 0, + }; + std.debug.captureStackTrace(@returnAddress(), &stack_trace); + try self.allocation_strack_addresses.putNoClobber(@ptrToInt(ptr.ptr), stack_addresses[2..].*); + const curr_allocs = self.info.currentlyAllocated(); if (self.max_bytes != 0 and curr_allocs >= self.max_bytes) { std.debug.print("Exceeded maximum bytes {}, exiting.\n", .{self.max_bytes}); @@ -109,6 +126,7 @@ fn alloc(allocator: *std.mem.Allocator, len: usize, ptr_align: u29, len_align: u if (curr_allocs > self.info.peak_allocated) { self.info.peak_allocated = curr_allocs; } + return ptr; } @@ -116,13 +134,21 @@ fn resize(allocator: *std.mem.Allocator, old_mem: []u8, new_size: usize, len_ali const self = @fieldParentPtr(DebugAllocator, "allocator", allocator); if (old_mem.len == 0) { - self.info.allocation_stats.addSample(new_size); - } else if (new_size == 0) { + std.log.debug(.debug_alloc, "Trying to resize empty slice\n", .{}); + std.process.exit(1); + } + + if (self.allocation_strack_addresses.get(@ptrToInt(old_mem.ptr)) == null) { + @panic("error - resize call on block not allocated by debug allocator"); + } + + if (new_size == 0) { if (self.info.allocation_stats.count == self.info.deallocation_count) { @panic("error - too many calls to free, most likely double free"); } self.info.deallocation_total += old_mem.len; self.info.deallocation_count += 1; + self.allocation_strack_addresses.removeAssertDiscard(@ptrToInt(old_mem.ptr)); } else if (new_size > old_mem.len) { self.info.reallocation_stats.addSample(new_size - old_mem.len); } else if (new_size < old_mem.len) { @@ -131,7 +157,7 @@ fn resize(allocator: *std.mem.Allocator, old_mem: []u8, new_size: usize, len_ali const curr_allocs = self.info.currentlyAllocated(); if (self.max_bytes != 0 and curr_allocs >= self.max_bytes) { - std.debug.print("Exceeded maximum bytes {}, exiting.\n", .{self.max_bytes}); + std.log.debug(.debug_alloc, "Exceeded maximum bytes {}, exiting.\n", .{self.max_bytes}); std.process.exit(1); } @@ -143,3 +169,23 @@ fn resize(allocator: *std.mem.Allocator, old_mem: []u8, new_size: usize, len_ali return e; }; } + +pub fn printRemainingStackTraces(self: DebugAllocator) void { + std.debug.print( + \\{} allocations - stack traces follow + \\------------------------------------ + , + .{self.allocation_strack_addresses.count()}); + var it = self.allocation_strack_addresses.iterator(); + var idx: usize = 1; + while (it.next()) |entry| : (idx += 1) { + std.debug.print("\nAllocation {}\n-------------\n", .{idx}); + var len: usize = 0; + while (len < stack_addresses_size and entry.value[len] != 0) : (len += 1) {} + const stack_trace = std.builtin.StackTrace{ + .instruction_addresses = &entry.value, + .index = len, + }; + std.debug.dumpStackTrace(stack_trace); + } +} diff --git a/src/main.zig b/src/main.zig index 929a943..0205327 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1070,6 +1070,7 @@ fn semanticTokensHandler(arena: *std.heap.ArenaAllocator, id: types.RequestId, r const semantic_tokens = @import("semantic_tokens.zig"); const token_array = try semantic_tokens.writeAllSemanticTokens(arena, &document_store, handle); + defer allocator.free(token_array); return try send(arena, types.Response{ .id = id, @@ -1382,6 +1383,8 @@ pub fn main() anyerror!void { defer if (debug_alloc) |dbg| { std.debug.print("Finished cleanup, last allocation info.\n", .{}); std.debug.print("\n{}\n", .{dbg.info}); + dbg.printRemainingStackTraces(); + dbg.deinit(); }; // Init global vars