Add leak detection to Ona heap allocator
This commit is contained in:
parent
9eadd30aa5
commit
306e085d1b
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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"),
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue