Add Leak Detection to Ona Heap Allocator #15

Merged
kayomn merged 12 commits from ona-allocator-safety-tracker into main 2023-06-04 16:07:47 +02:00
8 changed files with 163 additions and 45 deletions
Showing only changes of commit 306e085d1b - Show all commits

View File

@ -49,7 +49,7 @@ pub const Stacking = struct {
} }
fn allocate_page(self: *Stacking, page_size: usize) io.AllocationError!*Page { fn allocate_page(self: *Stacking, page_size: usize) io.AllocationError!*Page {
var buffer = try io.allocate_many(u8, page_size, self.base_allocator); var buffer = try io.allocate_many(self.base_allocator, page_size, u8);
errdefer io.deallocate(self.base_allocator, buffer); errdefer io.deallocate(self.base_allocator, buffer);

View File

@ -176,7 +176,7 @@ pub const GrowingBuffer = struct {
pub const Writer = Generator(?usize, []const u8); pub const Writer = Generator(?usize, []const u8);
pub fn allocate_many(comptime Type: type, amount: usize, allocator: Allocator) AllocationError![]Type { pub fn allocate_many(allocator: Allocator, amount: usize, comptime Type: type) AllocationError![]Type {
if (@sizeOf(Type) == 0) { if (@sizeOf(Type) == 0) {
@compileError("Cannot allocate memory for 0-byte type " ++ @typeName(Type)); @compileError("Cannot allocate memory for 0-byte type " ++ @typeName(Type));
} }
@ -232,14 +232,12 @@ pub fn copy(target: []u8, source: []const u8) void {
} }
pub fn deallocate(allocator: Allocator, allocation: anytype) void { pub fn deallocate(allocator: Allocator, allocation: anytype) void {
const Allocation = @TypeOf(allocation); switch (@typeInfo(@TypeOf(allocation))) {
.Pointer => |pointer| {
switch (@typeInfo(Allocation)) {
.Pointer => |allocation_pointer| {
_ = allocator.invoke(.{ _ = allocator.invoke(.{
.allocation = switch (allocation_pointer.size) { .allocation = switch (pointer.size) {
.One => @ptrCast([*]u8, allocation)[0 .. @sizeOf(Allocation)], .One => @ptrCast([*]u8, allocation)[0 .. @sizeOf(pointer.child)],
.Slice => @ptrCast([*]u8, allocation.ptr)[0 .. (@sizeOf(Allocation) * allocation.len)], .Slice => @ptrCast([*]u8, allocation.ptr)[0 .. (@sizeOf(pointer.child) * allocation.len)],
.Many, .C => @compileError("length of allocation must be known to deallocate"), .Many, .C => @compileError("length of allocation must be known to deallocate"),
}, },

View File

@ -87,7 +87,7 @@ pub fn Stack(comptime Value: type) type {
/// ///
pub fn grow(self: *Self, allocator: io.Allocator, growth_amount: usize) io.AllocationError!void { pub fn grow(self: *Self, allocator: io.Allocator, growth_amount: usize) io.AllocationError!void {
const grown_capacity = self.capacity + growth_amount; const grown_capacity = self.capacity + growth_amount;
const values = (try io.allocate_many(Value, grown_capacity, allocator))[0 .. self.values.len]; const values = (try io.allocate_many(allocator, grown_capacity, Value))[0 .. self.values.len];
errdefer io.deallocate(allocator, values); errdefer io.deallocate(allocator, values);
@ -96,7 +96,7 @@ pub fn Stack(comptime Value: type) type {
values[index] = self.values[index]; values[index] = self.values[index];
} }
io.deallocate(allocator, self.values); io.deallocate(allocator, self.values.ptr[0 .. self.capacity]);
} }
self.values = values; self.values = values;

View File

@ -95,7 +95,7 @@ pub fn Map(comptime index_int: std.builtin.Type.Int, comptime Value: type) type
/// ///
pub fn grow(self: *Self, allocator: io.Allocator, growth_amount: usize) io.AllocationError!void { pub fn grow(self: *Self, allocator: io.Allocator, growth_amount: usize) io.AllocationError!void {
const grown_capacity = self.table.len + growth_amount; const grown_capacity = self.table.len + growth_amount;
const entries = try io.allocate_many(Entry, grown_capacity, allocator); const entries = try io.allocate_many(allocator, grown_capacity, Entry);
errdefer io.deallocate(allocator, entries); errdefer io.deallocate(allocator, entries);

View File

@ -228,7 +228,7 @@ pub fn Hashed(comptime Key: type, comptime Value: type, comptime keyer: Keyer(Ke
pub fn rehash(self: *Self, allocator: io.Allocator, requested_range: usize) io.AllocationError!void { pub fn rehash(self: *Self, allocator: io.Allocator, requested_range: usize) io.AllocationError!void {
const old_table = self.table; const old_table = self.table;
self.table = try io.allocate_many(?Entry, math.max(requested_range, self.count), allocator); self.table = try io.allocate_many(allocator, math.max(requested_range, self.count), ?Entry);
errdefer { errdefer {
io.deallocate(allocator, self.table); io.deallocate(allocator, self.table);

View File

@ -2,53 +2,171 @@ const coral = @import("coral");
const ext = @import("./ext.zig"); const ext = @import("./ext.zig");
const std = @import("std");
///
///
///
const stack_trace_frame_size = if (std.debug.sys_can_stack_trace) 4 else 0;
///
///
///
const Context = struct { const Context = struct {
live_allocations: usize, allocation_info_head: ?*AllocationInfo = null,
const Self = @This(); ///
///
///
const AllocationInfo = struct {
stack_frames: [stack_trace_frame_size]usize,
stack_trace: std.builtin.StackTrace,
next_info: ?*AllocationInfo,
size: usize,
};
const empty_allocation = [0]u8{}; ///
///
///
fn allocate(self: *Context, size: 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));
fn reallocate(self: *Self, options: coral.io.AllocationOptions) ?[]u8 { allocation_info.* = .{
if (options.size == 0) { .size = size,
if (options.allocation) |allocation| { .next_info = self.allocation_info_head,
if (allocation.ptr != &empty_allocation) { .stack_frames = [_]usize{0} ** stack_trace_frame_size,
ext.SDL_free(allocation.ptr);
.stack_trace = .{
.instruction_addresses = &allocation_info.stack_frames,
.index = 0,
},
};
std.debug.captureStackTrace(null, &allocation_info.stack_trace);
self.allocation_info_head = allocation_info;
return @ptrCast([*]u8, allocation)[allocation_info_size .. total_allocation_size];
}
///
///
///
fn deallocate(self: *Context, allocation: []u8) void {
const target_allocation_info = @intToPtr(*AllocationInfo, @ptrToInt(allocation.ptr) - @sizeOf(AllocationInfo));
if (target_allocation_info.size != allocation.len) {
@panic("incorrect allocation length");
}
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;
} }
self.live_allocations -= 1; previous_allocation_info = allocation_info;
current_allocation_info = allocation_info.next_info;
return null;
}
self.live_allocations += 1;
return &empty_allocation;
}
if (options.allocation) |allocation| {
if (ext.SDL_realloc(allocation.ptr, options.size)) |reallocation| {
self.live_allocations += 1;
return @ptrCast([*]u8, reallocation)[0 .. options.size];
} }
} }
if (ext.SDL_malloc(options.size)) |allocation| { @panic("double-free detected");
self.live_allocations += 1; }
return @ptrCast([*]u8, allocation)[0 .. options.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);
if (target_allocation_info.size != allocation.len) {
@panic("incorrect allocation length");
} }
return null; 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("use-after-free detected");
} }
}; };
var context = Context{ ///
.live_allocations = 0, ///
}; ///
var context = Context{};
/// ///
/// Heap allocator. /// Heap allocator.
/// ///
pub const allocator = coral.io.Allocator.bind(Context, &context, Context.reallocate); pub const allocator = coral.io.Allocator.bind(Context, &context, struct {
fn reallocate(self: *Context, options: coral.io.AllocationOptions) ?[]u8 {
if (options.size == 0) {
if (options.allocation) |allocation| {
self.deallocate(allocation);
return null;
}
return self.allocate(0);
}
if (options.allocation) |allocation| {
return self.reallocate(allocation, options.size);
}
return self.allocate(options.size);
}
}.reallocate);
///
///
///
pub fn trace_allocations() void {
var current_allocation_info = context.allocation_info_head;
while (current_allocation_info) |allocation_info| : (current_allocation_info = allocation_info) {
std.debug.dumpStackTrace(allocation_info.stack_trace);
}
}

View File

@ -361,7 +361,7 @@ pub fn new_array(self: *Self) coral.io.AllocationError!types.Val {
} }
pub fn new_object(self: *Self, userdata: []const u8, info: ObjectInfo) coral.io.AllocationError!types.Val { pub fn new_object(self: *Self, userdata: []const u8, info: ObjectInfo) coral.io.AllocationError!types.Val {
const allocation = try coral.io.allocate_many(u8, userdata.len, self.allocator); const allocation = try coral.io.allocate_many(self.allocator, userdata.len, u8);
errdefer coral.io.deallocate(self.allocator, allocation); errdefer coral.io.deallocate(self.allocator, allocation);

View File

@ -135,4 +135,6 @@ pub fn run_app(base_file_system: file.System) void {
ext.SDL_RenderPresent(renderer); ext.SDL_RenderPresent(renderer);
} }
} }
heap.trace_allocations();
} }