Remove allocation checks in optimized release builds

This commit is contained in:
kayomn 2023-06-04 13:37:26 +00:00
parent 341710bfbc
commit d378939f30
1 changed files with 136 additions and 79 deletions

View File

@ -1,3 +1,5 @@
const builtin = @import("builtin");
const coral = @import("coral"); const coral = @import("coral");
const ext = @import("./ext.zig"); const ext = @import("./ext.zig");
@ -8,11 +10,19 @@ const std = @import("std");
/// ///
/// ///
const AllocationInfo = struct { const AllocationInfo = struct {
trace: std.debug.Trace, trace: AllocationTrace,
next_info: ?*AllocationInfo, next_info: ?*AllocationInfo,
size: usize, size: usize,
}; };
///
///
///
const AllocationTrace = std.debug.ConfigurableTrace(2, 4, switch (builtin.mode) {
.Debug, .ReleaseSafe => true,
.ReleaseFast, .ReleaseSmall => false,
});
/// ///
/// ///
/// ///
@ -29,7 +39,11 @@ const Context = struct {
/// *Note* the returned buffer must be deallocated with [deallocate] before program exit or it will cause a memory /// *Note* the returned buffer must be deallocated with [deallocate] before program exit or it will cause a memory
/// leak. /// leak.
/// ///
/// *Note* allocation checks are disabled in release builds optimized for speed or size.
///
fn allocate(self: *Context, size: usize, return_address: usize) ?[]u8 { fn allocate(self: *Context, size: usize, return_address: usize) ?[]u8 {
switch (builtin.mode) {
.Debug, .ReleaseSafe => {
const allocation_info_size = @sizeOf(AllocationInfo); const allocation_info_size = @sizeOf(AllocationInfo);
const total_allocation_size = allocation_info_size + size; const total_allocation_size = allocation_info_size + size;
const allocation = ext.SDL_malloc(total_allocation_size) orelse return null; const allocation = ext.SDL_malloc(total_allocation_size) orelse return null;
@ -46,6 +60,19 @@ const Context = struct {
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,8 +81,12 @@ const Context = struct {
/// *Note* the pointer and length of `allocation` must match valid values known to `allocator` otherwise safety- /// *Note* the pointer and length of `allocation` must match valid values known to `allocator` otherwise safety-
/// checked behavior will occur. /// checked behavior will occur.
/// ///
/// *Note* allocation checks are disabled in release builds optimized for speed or size.
///
fn deallocate(self: *Context, allocation: []u8) void { 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) { if (target_allocation_info.size != allocation.len) {
@panic("incorrect allocation length for deallocating"); @panic("incorrect allocation length for deallocating");
@ -88,6 +119,12 @@ const Context = struct {
} }
@panic("incorrect allocation address for deallocating"); @panic("incorrect allocation address for deallocating");
},
.ReleaseFast, .ReleaseSmall => {
ext.SDL_free(allocation.ptr);
},
}
} }
/// ///
@ -105,14 +142,19 @@ const Context = struct {
/// *Note* the allocation referenced by `allocation` should be considered invalid once the function returns, /// *Note* the allocation referenced by `allocation` should be considered invalid once the function returns,
/// discarding it in favor of the return value. /// 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 { fn reallocate(self: *Context, allocation: []u8, size: usize) ?[]u8 {
const allocation_info_size = @sizeOf(AllocationInfo); switch (builtin.mode) {
const target_allocation_info = @intToPtr(*AllocationInfo, @ptrToInt(allocation.ptr) - allocation_info_size); .Debug, .ReleaseSafe => {
const target_allocation_info = allocation_info_of(allocation.ptr);
if (target_allocation_info.size != allocation.len) { if (target_allocation_info.size != allocation.len) {
@panic("incorrect allocation length for reallocating"); @panic("incorrect allocation length for reallocating");
} }
const allocation_info_size = @sizeOf(AllocationInfo);
if (self.allocation_info_head) |allocation_info_head| { if (self.allocation_info_head) |allocation_info_head| {
if (target_allocation_info == allocation_info_head) { if (target_allocation_info == allocation_info_head) {
self.allocation_info_head = allocation_info_head.next_info; self.allocation_info_head = allocation_info_head.next_info;
@ -140,6 +182,12 @@ const Context = struct {
} }
@panic("incorrect allocation address for reallocating"); @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 /// alive and reports the stack traces of any detected allocations to stderr along with the allocation address and
/// length. /// length.
/// ///
/// *Note* this function becomes a no-op in release builds optimized for speed or size.
///
pub fn trace_leaks() void { pub fn trace_leaks() void {
switch (builtin.mode) {
.Debug, .ReleaseSafe => {
var current_allocation_info = context.allocation_info_head; var current_allocation_info = context.allocation_info_head;
while (current_allocation_info) |allocation_info| : (current_allocation_info = allocation_info.next_info) { while (current_allocation_info) |allocation_info| : (current_allocation_info = allocation_info.next_info) {
std.debug.print("{d} byte leak at 0x{x} detected: {}", .{ std.debug.print("{d} byte leak at 0x{x} detected:\n", .{
allocation_info.size, allocation_info.size,
@ptrToInt(allocation_info) + @sizeOf(AllocationInfo), @ptrToInt(allocation_info) + @sizeOf(AllocationInfo),
allocation_info.trace
}); });
allocation_info.trace.dump();
}
},
.ReleaseFast, .ReleaseSmall => {},
} }
} }