From 08785b5b5473b85dadbff36819448aac0e6d8c97 Mon Sep 17 00:00:00 2001 From: kayomn Date: Sun, 4 Jun 2023 02:23:07 +0000 Subject: [PATCH] Improve clarity of stack trace allocation origin --- source/coral/io.zig | 16 ++++++++----- source/ona/heap.zig | 55 ++++++++++++++++++++++++++++----------------- source/ona/ona.zig | 12 +++++----- 3 files changed, 50 insertions(+), 33 deletions(-) diff --git a/source/coral/io.zig b/source/coral/io.zig index e8a70e2..ad5e1ad 100755 --- a/source/coral/io.zig +++ b/source/coral/io.zig @@ -7,6 +7,7 @@ pub const AllocationError = error { }; pub const AllocationOptions = struct { + return_address: usize, allocation: ?[]u8 = null, size: usize, }; @@ -181,9 +182,10 @@ pub fn allocate_many(allocator: Allocator, amount: usize, comptime Type: type) A @compileError("Cannot allocate memory for 0-byte type " ++ @typeName(Type)); } - return @ptrCast([*]Type, @alignCast(@alignOf(Type), allocator.invoke(.{.size = @sizeOf(Type) * amount}) orelse { - return error.OutOfMemory; - }))[0 .. amount]; + return @ptrCast([*]Type, @alignCast(@alignOf(Type), allocator.invoke(.{ + .size = @sizeOf(Type) * amount, + .return_address = @returnAddress(), + }) orelse return error.OutOfMemory))[0 .. amount]; } pub fn allocate_one(allocator: Allocator, value: anytype) AllocationError!*@TypeOf(value) { @@ -193,9 +195,10 @@ pub fn allocate_one(allocator: Allocator, value: anytype) AllocationError!*@Type @compileError("Cannot allocate memory for 0-byte type " ++ @typeName(Type)); } - const allocation = @ptrCast(*Type, @alignCast(@alignOf(Type), allocator.invoke(.{.size = @sizeOf(Type)}) orelse { - return error.OutOfMemory; - })); + const allocation = @ptrCast(*Type, @alignCast(@alignOf(Type), allocator.invoke(.{ + .size = @sizeOf(Type), + .return_address = @returnAddress(), + }) orelse return error.OutOfMemory)); allocation.* = value; @@ -241,6 +244,7 @@ pub fn deallocate(allocator: Allocator, allocation: anytype) void { .Many, .C => @compileError("length of allocation must be known to deallocate"), }, + .return_address = @returnAddress(), .size = 0, }); }, diff --git a/source/ona/heap.zig b/source/ona/heap.zig index 755b0b0..9c9a5eb 100644 --- a/source/ona/heap.zig +++ b/source/ona/heap.zig @@ -4,17 +4,11 @@ const ext = @import("./ext.zig"); const std = @import("std"); -/// -/// -/// -const stack_trace_frame_size = if (std.debug.sys_can_stack_trace) 16 else 0; - /// /// /// const AllocationInfo = struct { - stack_frames: [stack_trace_frame_size]usize, - stack_trace: std.builtin.StackTrace, + trace: std.debug.Trace, next_info: ?*AllocationInfo, size: usize, }; @@ -26,9 +20,16 @@ const Context = struct { allocation_info_head: ?*AllocationInfo = null, /// + /// Attempts to allocate a buffer of `size` length from `self`, with `return_address` as the location of the + /// allocation request origin. /// + /// A reference to the allocated buffer is returned via a slice if the allocation was successful, otherwise `null` + /// is returned. /// - fn allocate(self: *Context, size: usize) ?[]u8 { + /// *Note* the returned buffer must be deallocated with [deallocate] before program exit or it will cause a memory + /// leak. + /// + 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; @@ -37,15 +38,10 @@ const Context = struct { allocation_info.* = .{ .size = size, .next_info = self.allocation_info_head, - .stack_frames = [_]usize{0} ** stack_trace_frame_size, - - .stack_trace = .{ - .instruction_addresses = &allocation_info.stack_frames, - .index = 0, - }, + .trace = .{}, }; - std.debug.captureStackTrace(null, &allocation_info.stack_trace); + allocation_info.trace.addAddr(return_address, ""); self.allocation_info_head = allocation_info; @@ -53,7 +49,10 @@ const Context = struct { } /// + /// Deallocates a the allocation buffer referenced by `allocation`. /// + /// *Note* the pointer and length of `allocation` must match valid values known to `allocator` otherwise safety- + /// checked behavior will occur. /// fn deallocate(self: *Context, allocation: []u8) void { const target_allocation_info = @intToPtr(*AllocationInfo, @ptrToInt(allocation.ptr) - @sizeOf(AllocationInfo)); @@ -92,7 +91,19 @@ const Context = struct { } /// + /// Attempts to reallocate the buffer referenced by `allocation` to be `size` length from `self`. /// + /// A reference to the reallocated buffer is returned via a slice if the reallocation was successful, otherwise + /// `null` is returned. + /// + /// *Note* the returned buffer must be deallocated with [deallocate] before program exit or it will cause a memory + /// leak. + /// + /// *Note* the pointer and length of `allocation` must match valid values known to `allocator` otherwise safety- + /// checked behavior will occur. + /// + /// *Note* the allocation referenced by `allocation` should be considered invalid once the function returns, + /// discarding it in favor of the return value. /// fn reallocate(self: *Context, allocation: []u8, size: usize) ?[]u8 { const allocation_info_size = @sizeOf(AllocationInfo); @@ -133,7 +144,7 @@ const Context = struct { }; /// -/// +/// Heap context. /// var context = Context{}; @@ -149,28 +160,30 @@ pub const allocator = coral.io.Allocator.bind(Context, &context, struct { return null; } - return self.allocate(0); + return self.allocate(0, options.return_address); } if (options.allocation) |allocation| { return self.reallocate(allocation, options.size); } - return self.allocate(options.size); + return self.allocate(options.size, options.return_address); } }.reallocate); /// +/// Checks for any allocations belonging to the process heap allocated through the [allocator] interface that are still +/// alive and reports the stack traces of any detected allocations to stderr along with the allocation address and +/// length. /// -/// -pub fn trace_allocations() void { +pub fn trace_leaks() void { 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.stack_trace + allocation_info.trace }); } } diff --git a/source/ona/ona.zig b/source/ona/ona.zig index 72ecb56..33e5fb2 100755 --- a/source/ona/ona.zig +++ b/source/ona/ona.zig @@ -55,8 +55,8 @@ const AppManifest = struct { } }; -pub fn run_app(_: file.System) void { - defer heap.trace_allocations(); +pub fn run_app(base_file_system: file.System) void { + defer heap.trace_leaks(); const Logger = struct { const Self = @This(); @@ -76,12 +76,12 @@ pub fn run_app(_: file.System) void { defer script_environment.deinit(); - // const app_file_name = "app.ona"; + const app_file_name = "app.ona"; var app_manifest = AppManifest{}; - // app_manifest.load_script(&script_environment, base_file_system, app_file_name) catch { - // return ext.SDL_LogError(ext.SDL_LOG_CATEGORY_APPLICATION, "failed to load %s\n", app_file_name); - // }; + app_manifest.load_script(&script_environment, base_file_system, app_file_name) catch { + return ext.SDL_LogError(ext.SDL_LOG_CATEGORY_APPLICATION, "failed to load %s\n", app_file_name); + }; if (ext.SDL_Init(ext.SDL_INIT_EVERYTHING) != 0) { return ext.SDL_LogError(ext.SDL_LOG_CATEGORY_APPLICATION, "%s\n", ext.SDL_GetError());