From d378939f30340d1de4359b0f00c13de977cbb196 Mon Sep 17 00:00:00 2001 From: kayomn Date: Sun, 4 Jun 2023 13:37:26 +0000 Subject: [PATCH] Remove allocation checks in optimized release builds --- source/ona/heap.zig | 215 ++++++++++++++++++++++++++++---------------- 1 file changed, 136 insertions(+), 79 deletions(-) diff --git a/source/ona/heap.zig b/source/ona/heap.zig index 35fc21d..4d32118 100644 --- a/source/ona/heap.zig +++ b/source/ona/heap.zig @@ -1,3 +1,5 @@ +const builtin = @import("builtin"); + const coral = @import("coral"); const ext = @import("./ext.zig"); @@ -8,11 +10,19 @@ const std = @import("std"); /// /// const AllocationInfo = struct { - trace: std.debug.Trace, + trace: AllocationTrace, next_info: ?*AllocationInfo, size: usize, }; +/// +/// +/// +const AllocationTrace = std.debug.ConfigurableTrace(2, 4, switch (builtin.mode) { + .Debug, .ReleaseSafe => true, + .ReleaseFast, .ReleaseSmall => false, +}); + /// /// /// @@ -29,23 +39,40 @@ const Context = struct { /// *Note* the returned buffer must be deallocated with [deallocate] before program exit or it will cause a memory /// leak. /// + /// *Note* allocation checks are disabled in release builds optimized for speed or size. + /// fn allocate(self: *Context, size: usize, return_address: usize) ?[]u8 { - const allocation_info_size = @sizeOf(AllocationInfo); - const total_allocation_size = allocation_info_size + size; - const allocation = ext.SDL_malloc(total_allocation_size) orelse return null; - const allocation_info = @ptrCast(*AllocationInfo, @alignCast(@alignOf(AllocationInfo), allocation)); + switch (builtin.mode) { + .Debug, .ReleaseSafe => { + const allocation_info_size = @sizeOf(AllocationInfo); + const total_allocation_size = allocation_info_size + size; + const allocation = ext.SDL_malloc(total_allocation_size) orelse return null; + const allocation_info = @ptrCast(*AllocationInfo, @alignCast(@alignOf(AllocationInfo), allocation)); - allocation_info.* = .{ - .size = size, - .next_info = self.allocation_info_head, - .trace = .{}, - }; + allocation_info.* = .{ + .size = size, + .next_info = self.allocation_info_head, + .trace = .{}, + }; - allocation_info.trace.addAddr(return_address, ""); + allocation_info.trace.addAddr(return_address, ""); - self.allocation_info_head = allocation_info; + self.allocation_info_head = allocation_info; - return @ptrCast([*]u8, allocation)[allocation_info_size .. total_allocation_size]; + return @ptrCast([*]u8, allocation)[allocation_info_size .. total_allocation_size]; + }, + + .ReleaseFast, .ReleaseSmall => { + return @ptrCast([*]u8, ext.SDL_malloc(size) orelse return null)[0 .. size]; + }, + } + } + + /// + /// + /// + fn allocation_info_of(allocation: [*]u8) *AllocationInfo { + return @intToPtr(*AllocationInfo, @ptrToInt(allocation) - @sizeOf(AllocationInfo)); } /// @@ -54,40 +81,50 @@ const Context = struct { /// *Note* the pointer and length of `allocation` must match valid values known to `allocator` otherwise safety- /// checked behavior will occur. /// + /// *Note* allocation checks are disabled in release builds optimized for speed or size. + /// fn deallocate(self: *Context, allocation: []u8) void { - const target_allocation_info = @intToPtr(*AllocationInfo, @ptrToInt(allocation.ptr) - @sizeOf(AllocationInfo)); + switch (builtin.mode) { + .Debug, .ReleaseSafe => { + const target_allocation_info = allocation_info_of(allocation.ptr); - if (target_allocation_info.size != allocation.len) { - @panic("incorrect allocation length for deallocating"); - } - - if (self.allocation_info_head) |allocation_info_head| { - if (target_allocation_info == allocation_info_head) { - self.allocation_info_head = allocation_info_head.next_info; - - ext.SDL_free(target_allocation_info); - - return; - } - - var previous_allocation_info = allocation_info_head; - var current_allocation_info = allocation_info_head.next_info; - - while (current_allocation_info) |allocation_info| { - if (allocation_info == target_allocation_info) { - previous_allocation_info.next_info = allocation_info.next_info; - - ext.SDL_free(target_allocation_info); - - return; + if (target_allocation_info.size != allocation.len) { + @panic("incorrect allocation length for deallocating"); } - previous_allocation_info = allocation_info; - current_allocation_info = allocation_info.next_info; - } - } + if (self.allocation_info_head) |allocation_info_head| { + if (target_allocation_info == allocation_info_head) { + self.allocation_info_head = allocation_info_head.next_info; - @panic("incorrect allocation address for deallocating"); + ext.SDL_free(target_allocation_info); + + return; + } + + var previous_allocation_info = allocation_info_head; + var current_allocation_info = allocation_info_head.next_info; + + while (current_allocation_info) |allocation_info| { + if (allocation_info == target_allocation_info) { + previous_allocation_info.next_info = allocation_info.next_info; + + ext.SDL_free(target_allocation_info); + + return; + } + + previous_allocation_info = allocation_info; + current_allocation_info = allocation_info.next_info; + } + } + + @panic("incorrect allocation address for deallocating"); + }, + + .ReleaseFast, .ReleaseSmall => { + ext.SDL_free(allocation.ptr); + }, + } } /// @@ -105,41 +142,52 @@ const Context = struct { /// *Note* the allocation referenced by `allocation` should be considered invalid once the function returns, /// discarding it in favor of the return value. /// + /// *Note* allocation checks are disabled in release builds optimized for speed or size. + /// fn reallocate(self: *Context, allocation: []u8, size: usize) ?[]u8 { - const allocation_info_size = @sizeOf(AllocationInfo); - const target_allocation_info = @intToPtr(*AllocationInfo, @ptrToInt(allocation.ptr) - allocation_info_size); + switch (builtin.mode) { + .Debug, .ReleaseSafe => { + const target_allocation_info = allocation_info_of(allocation.ptr); - if (target_allocation_info.size != allocation.len) { - @panic("incorrect allocation length for reallocating"); - } - - if (self.allocation_info_head) |allocation_info_head| { - if (target_allocation_info == allocation_info_head) { - self.allocation_info_head = allocation_info_head.next_info; - - return @ptrCast([*]u8, ext.SDL_realloc(target_allocation_info, size) orelse { - return null; - })[allocation_info_size .. allocation_info_size + size]; - } - - var previous_allocation_info = allocation_info_head; - var current_allocation_info = allocation_info_head.next_info; - - while (current_allocation_info) |allocation_info| { - if (allocation_info == target_allocation_info) { - previous_allocation_info.next_info = allocation_info.next_info; - - return @ptrCast([*]u8, ext.SDL_realloc(target_allocation_info, size) orelse { - return null; - })[allocation_info_size .. allocation_info_size + size]; + if (target_allocation_info.size != allocation.len) { + @panic("incorrect allocation length for reallocating"); } - previous_allocation_info = allocation_info; - current_allocation_info = allocation_info.next_info; - } - } + const allocation_info_size = @sizeOf(AllocationInfo); - @panic("incorrect allocation address for reallocating"); + if (self.allocation_info_head) |allocation_info_head| { + if (target_allocation_info == allocation_info_head) { + self.allocation_info_head = allocation_info_head.next_info; + + return @ptrCast([*]u8, ext.SDL_realloc(target_allocation_info, size) orelse { + return null; + })[allocation_info_size .. allocation_info_size + size]; + } + + var previous_allocation_info = allocation_info_head; + var current_allocation_info = allocation_info_head.next_info; + + while (current_allocation_info) |allocation_info| { + if (allocation_info == target_allocation_info) { + previous_allocation_info.next_info = allocation_info.next_info; + + return @ptrCast([*]u8, ext.SDL_realloc(target_allocation_info, size) orelse { + return null; + })[allocation_info_size .. allocation_info_size + size]; + } + + previous_allocation_info = allocation_info; + current_allocation_info = allocation_info.next_info; + } + } + + @panic("incorrect allocation address for reallocating"); + }, + + .ReleaseFast, .ReleaseSmall => { + return @ptrCast([*]u8, ext.SDL_realloc(allocation.ptr, size) orelse return null)[0 .. size]; + }, + } } }; @@ -176,14 +224,23 @@ pub const allocator = coral.io.Allocator.bind(Context, &context, struct { /// alive and reports the stack traces of any detected allocations to stderr along with the allocation address and /// length. /// +/// *Note* this function becomes a no-op in release builds optimized for speed or size. +/// pub fn trace_leaks() void { - var current_allocation_info = context.allocation_info_head; + switch (builtin.mode) { + .Debug, .ReleaseSafe => { + var current_allocation_info = context.allocation_info_head; - while (current_allocation_info) |allocation_info| : (current_allocation_info = allocation_info.next_info) { - std.debug.print("{d} byte leak at 0x{x} detected: {}", .{ - allocation_info.size, - @ptrToInt(allocation_info) + @sizeOf(AllocationInfo), - allocation_info.trace - }); + while (current_allocation_info) |allocation_info| : (current_allocation_info = allocation_info.next_info) { + std.debug.print("{d} byte leak at 0x{x} detected:\n", .{ + allocation_info.size, + @ptrToInt(allocation_info) + @sizeOf(AllocationInfo), + }); + + allocation_info.trace.dump(); + } + }, + + .ReleaseFast, .ReleaseSmall => {}, } }