Added stack trace leak reporting to the debug allocator, fixed remaining two memory leaks
This commit is contained in:
		
							parent
							
								
									21e18a1d01
								
							
						
					
					
						commit
						8c154c2a60
					
				@ -2010,6 +2010,7 @@ fn makeScopeInternal(
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        scopes.items[scope_idx].tests = tests.toOwnedSlice();
 | 
			
		||||
        scopes.items[scope_idx].uses = uses.toOwnedSlice();
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user