Use backtrace on windows. Updated backtrace API

This commit is contained in:
Christoffer Lerno
2023-11-16 23:18:43 +01:00
committed by Christoffer Lerno
parent 81c93e3488
commit ffb0021d04
24 changed files with 272 additions and 273 deletions

View File

@@ -29,7 +29,7 @@ struct ArenaAllocatorHeader @local
char[*] data;
}
fn void ArenaAllocator.release(&self, void* ptr, bool, TrackingEnv* env = null) @dynamic
fn void ArenaAllocator.release(&self, void* ptr, bool) @dynamic
{
if (!ptr) return;
assert((uptr)ptr >= (uptr)self.data.ptr, "Pointer originates from a different allocator.");
@@ -50,7 +50,7 @@ fn void ArenaAllocator.reset(&self, usz mark) @dynamic => self.used = mark;
* @require offset <= size && offset >= 0
* @require mem::aligned_offset(offset, ArenaAllocatorHeader.alignof) == offset
**/
fn void*! ArenaAllocator.acquire(&self, usz size, bool clear, usz alignment, usz offset, TrackingEnv* env = null) @dynamic
fn void*! ArenaAllocator.acquire(&self, usz size, bool clear, usz alignment, usz offset) @dynamic
{
if (!size) return null;
alignment = alignment_for_allocation(alignment);
@@ -76,7 +76,7 @@ fn void*! ArenaAllocator.acquire(&self, usz size, bool clear, usz alignment, usz
* @require offset <= size && offset >= 0
* @require mem::aligned_offset(offset, ArenaAllocatorHeader.alignof) == offset
**/
fn void*! ArenaAllocator.resize(&self, void *old_pointer, usz size, usz alignment, usz offset, TrackingEnv* env) @dynamic
fn void*! ArenaAllocator.resize(&self, void *old_pointer, usz size, usz alignment, usz offset) @dynamic
{
if (!size)
{
@@ -110,7 +110,7 @@ fn void*! ArenaAllocator.resize(&self, void *old_pointer, usz size, usz alignmen
return old_pointer;
}
// Otherwise just allocate new memory.
void* mem = self.acquire(size, false, alignment, offset, env)!;
void* mem = self.acquire(size, false, alignment, offset)!;
mem::copy(mem, old_pointer, old_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
return mem;
}

View File

@@ -60,7 +60,7 @@ struct DynamicArenaChunk @local
/**
* @require self.page `tried to free pointer on invalid allocator`
*/
fn void DynamicArenaAllocator.release(&self, void* ptr, bool, TrackingEnv* env) @dynamic
fn void DynamicArenaAllocator.release(&self, void* ptr, bool) @dynamic
{
if (!ptr) return;
DynamicArenaPage* current_page = self.page;
@@ -74,16 +74,16 @@ fn void DynamicArenaAllocator.release(&self, void* ptr, bool, TrackingEnv* env)
/**
* @require self.page `tried to realloc pointer on invalid allocator`
*/
fn void*! DynamicArenaAllocator.resize(&self, void* old_pointer, usz size, usz alignment, usz offset, TrackingEnv* env) @dynamic
fn void*! DynamicArenaAllocator.resize(&self, void* old_pointer, usz size, usz alignment, usz offset) @dynamic
{
if (!size)
{
self.release(old_pointer, alignment > 0, env);
self.release(old_pointer, alignment > 0);
return null;
}
if (!old_pointer)
{
return self.acquire(size, true, alignment, offset, env);
return self.acquire(size, true, alignment, offset);
}
DynamicArenaPage* current_page = self.page;
alignment = alignment_for_allocation(alignment);
@@ -108,7 +108,7 @@ fn void*! DynamicArenaAllocator.resize(&self, void* old_pointer, usz size, usz a
current_page.used += add_size;
return old_pointer;
}
void* new_mem = self.acquire(size, false, alignment, offset, env)!;
void* new_mem = self.acquire(size, false, alignment, offset)!;
mem::copy(new_mem, old_pointer, old_size, mem::DEFAULT_MEM_ALIGNMENT);
return new_mem;
}
@@ -163,7 +163,7 @@ fn void*! DynamicArenaAllocator._alloc_new(&self, usz size, usz alignment, usz o
/**
* @require !alignment || math::is_power_of_2(alignment)
*/
fn void*! DynamicArenaAllocator.acquire(&self, usz size, bool clear, usz alignment, usz offset, TrackingEnv* env) @dynamic
fn void*! DynamicArenaAllocator.acquire(&self, usz size, bool clear, usz alignment, usz offset) @dynamic
{
if (!size) return null;
alignment = alignment_for_allocation(alignment);

View File

@@ -21,7 +21,7 @@ fn void SimpleHeapAllocator.init(&self, MemoryAllocFn allocator)
self.free_list = null;
}
fn void*! SimpleHeapAllocator.acquire(&self, usz size, bool clear, usz alignment, usz offset, TrackingEnv* env) @dynamic
fn void*! SimpleHeapAllocator.acquire(&self, usz size, bool clear, usz alignment, usz offset) @dynamic
{
if (!size) return null;
if (clear)
@@ -31,23 +31,23 @@ fn void*! SimpleHeapAllocator.acquire(&self, usz size, bool clear, usz alignment
return alignment > 0 ? @aligned_alloc(self._alloc, size, alignment, offset) : self._alloc(size);
}
fn void*! SimpleHeapAllocator.resize(&self, void* old_pointer, usz size, usz alignment, usz offset, TrackingEnv* env) @dynamic
fn void*! SimpleHeapAllocator.resize(&self, void* old_pointer, usz size, usz alignment, usz offset) @dynamic
{
if (!size)
{
self.release(old_pointer, alignment > 0, env);
self.release(old_pointer, alignment > 0);
return null;
}
if (!old_pointer)
{
return self.acquire(size, true, alignment, offset, env);
return self.acquire(size, true, alignment, offset);
}
return alignment > 0
? @aligned_realloc(self._calloc, self._free, old_pointer, size, alignment, offset)
: self._realloc(old_pointer, size);
}
fn void SimpleHeapAllocator.release(&self, void* old_pointer, bool aligned, TrackingEnv* env) @dynamic
fn void SimpleHeapAllocator.release(&self, void* old_pointer, bool aligned) @dynamic
{
if (aligned)
{

View File

@@ -10,7 +10,7 @@ const LibcAllocator LIBC_ALLOCATOR = {};
distinct LibcAllocator (Allocator) = uptr;
fn void*! LibcAllocator.acquire(&self, usz bytes, bool clear, usz alignment, usz offset, TrackingEnv* env) @dynamic
fn void*! LibcAllocator.acquire(&self, usz bytes, bool clear, usz alignment, usz offset) @dynamic
{
assert(alignment != 0 || offset == 0);
if (clear)
@@ -29,17 +29,17 @@ fn void*! LibcAllocator.acquire(&self, usz bytes, bool clear, usz alignment, usz
}
}
fn void*! LibcAllocator.resize(&self, void* old_ptr, usz new_bytes, usz alignment, usz offset, TrackingEnv* env) @dynamic
fn void*! LibcAllocator.resize(&self, void* old_ptr, usz new_bytes, usz alignment, usz offset) @dynamic
{
assert(alignment != 0 || offset == 0);
if (!new_bytes)
{
self.release(old_ptr, alignment > 0, env);
self.release(old_ptr, alignment > 0);
return null;
}
if (!old_ptr)
{
return self.acquire(new_bytes, true, alignment, offset, env);
return self.acquire(new_bytes, true, alignment, offset);
}
if (alignment)
{
@@ -50,7 +50,7 @@ fn void*! LibcAllocator.resize(&self, void* old_ptr, usz new_bytes, usz alignmen
}
fn void LibcAllocator.release(&self, void* old_ptr, bool aligned, TrackingEnv* env) @dynamic
fn void LibcAllocator.release(&self, void* old_ptr, bool aligned) @dynamic
{
if (aligned)
{

View File

@@ -54,7 +54,7 @@ struct OnStackAllocatorHeader
char[*] data;
}
fn void OnStackAllocator.release(&self, void* old_pointer, bool aligned, TrackingEnv* env = null) @dynamic
fn void OnStackAllocator.release(&self, void* old_pointer, bool aligned) @dynamic
{
if (!old_pointer) return;
if (allocation_in_stack_mem(self, old_pointer)) return;
@@ -103,18 +103,18 @@ fn OnStackAllocatorExtraChunk* on_stack_allocator_find_chunk(OnStackAllocator* a
* @require offset <= size && offset >= 0
* @require mem::aligned_offset(offset, OnStackAllocatorExtraChunk.alignof) == offset
**/
fn void*! OnStackAllocator.resize(&self, void* old_pointer, usz size, usz alignment, usz offset, TrackingEnv* env) @dynamic
fn void*! OnStackAllocator.resize(&self, void* old_pointer, usz size, usz alignment, usz offset) @dynamic
{
if (!allocation_in_stack_mem(self, old_pointer))
{
OnStackAllocatorExtraChunk* chunk = on_stack_allocator_find_chunk(self, old_pointer);
assert(chunk, "Tried to realloc pointer not belonging to the allocator");
return chunk.data = self.backing_allocator.resize(old_pointer, size, alignment, offset, env)!;
return chunk.data = self.backing_allocator.resize(old_pointer, size, alignment, offset)!;
}
OnStackAllocatorHeader* header = old_pointer - OnStackAllocatorHeader.sizeof;
usz old_size = header.size;
void* mem = self.acquire(size, true, alignment, offset, env)!;
void* mem = self.acquire(size, true, alignment, offset)!;
mem::copy(mem, old_pointer, old_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
return mem;
}
@@ -126,7 +126,7 @@ fn void*! OnStackAllocator.resize(&self, void* old_pointer, usz size, usz alignm
* @require offset == 0 || alignment > 0
* @require mem::aligned_offset(offset, OnStackAllocatorHeader.alignof) == offset
**/
fn void*! OnStackAllocator.acquire(&self, usz size, bool clear, usz alignment, usz offset, TrackingEnv* env) @dynamic
fn void*! OnStackAllocator.acquire(&self, usz size, bool clear, usz alignment, usz offset) @dynamic
{
if (size == 0) return null;
bool aligned = alignment > 0;
@@ -144,7 +144,7 @@ fn void*! OnStackAllocator.acquire(&self, usz size, bool clear, usz alignment, u
defer catch backing_allocator.free(chunk);
defer try self.chunk = chunk;
*chunk = { .prev = self.chunk, .is_aligned = aligned };
return chunk.data = backing_allocator.acquire(size, clear, aligned ? alignment : 0, offset, env)!;
return chunk.data = backing_allocator.acquire(size, clear, aligned ? alignment : 0, offset)!;
}
self.used = end;
void *mem = aligned_pointer_to_offset - offset;

View File

@@ -47,7 +47,7 @@ fn TempAllocator*! new_temp(usz size, Allocator* allocator)
fn usz TempAllocator.mark(&self) @dynamic => self.used;
fn void TempAllocator.release(&self, void* old_pointer, bool, TrackingEnv* env) @dynamic
fn void TempAllocator.release(&self, void* old_pointer, bool) @dynamic
{
usz old_size = *(usz*)(old_pointer - DEFAULT_SIZE_PREFIX);
if (old_pointer + old_size == &self.data[self.used])
@@ -75,7 +75,7 @@ fn void! TempAllocator._free_page(&self, TempAllocatorPage* page) @inline @local
return self.backing_allocator.free(mem);
}
fn void*! TempAllocator._realloc_page(&self, TempAllocatorPage* page, usz size, usz alignment, usz offset, TrackingEnv* env) @inline @local
fn void*! TempAllocator._realloc_page(&self, TempAllocatorPage* page, usz size, usz alignment, usz offset) @inline @local
{
// Then the actual start pointer:
void* real_pointer = page.start;
@@ -90,7 +90,7 @@ fn void*! TempAllocator._realloc_page(&self, TempAllocatorPage* page, usz size,
*pointer_to_prev = page.prev_page;
usz page_size = page.pagesize();
// Clear on size > original size.
void* data = self.acquire(size, size > page_size, alignment, offset, env)!;
void* data = self.acquire(size, size > page_size, alignment, offset)!;
mem::copy(data, &page.data[0], page_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
if (page.is_aligned())
{
@@ -103,16 +103,16 @@ fn void*! TempAllocator._realloc_page(&self, TempAllocatorPage* page, usz size,
return data;
}
fn void*! TempAllocator.resize(&self, void* pointer, usz size, usz alignment, usz offset, TrackingEnv* env) @dynamic
fn void*! TempAllocator.resize(&self, void* pointer, usz size, usz alignment, usz offset) @dynamic
{
if (!size)
{
self.release(pointer, alignment > 0, env);
self.release(pointer, alignment > 0);
return null;
}
if (!pointer)
{
return self.acquire(size, true, alignment, offset, env);
return self.acquire(size, true, alignment, offset);
}
TempAllocatorChunk *chunk = pointer - TempAllocatorChunk.sizeof;
if (chunk.size == (usz)-1)
@@ -120,11 +120,11 @@ fn void*! TempAllocator.resize(&self, void* pointer, usz size, usz alignment, us
assert(self.last_page, "Realloc of non temp pointer");
// First grab the page
TempAllocatorPage *page = pointer - TempAllocatorPage.sizeof;
return self._realloc_page(page, size, alignment, offset, env);
return self._realloc_page(page, size, alignment, offset);
}
// TODO optimize last allocation
TempAllocatorChunk* data = self.acquire(size, size > chunk.size, alignment, offset, env)!;
TempAllocatorChunk* data = self.acquire(size, size > chunk.size, alignment, offset)!;
mem::copy(data, pointer, chunk.size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
return data;
@@ -134,7 +134,7 @@ fn void*! TempAllocator.resize(&self, void* pointer, usz size, usz alignment, us
* @require !alignment || math::is_power_of_2(alignment)
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
**/
fn void*! TempAllocator.acquire(&self, usz size, bool clear, usz alignment, usz offset, TrackingEnv* env) @dynamic
fn void*! TempAllocator.acquire(&self, usz size, bool clear, usz alignment, usz offset) @dynamic
{
if (!size) return null;
alignment = alignment_for_allocation(alignment);
@@ -182,7 +182,7 @@ fn void*! TempAllocator.acquire(&self, usz size, bool clear, usz alignment, usz
// Here we might need to pad
usz padded_header_size = mem::aligned_offset(TempAllocatorPage.sizeof, mem::DEFAULT_MEM_ALIGNMENT);
usz total_alloc_size = padded_header_size + size;
void* alloc = self.backing_allocator.acquire(total_alloc_size, clear, 0, 0, null)!;
void* alloc = self.backing_allocator.acquire(total_alloc_size, clear, 0, 0)!;
// Find the page.
page = alloc + padded_header_size - TempAllocatorPage.sizeof;

View File

@@ -6,11 +6,12 @@ module std::core::mem::allocator;
import std::collections::map;
import std::collections::list;
const MAX_BACKTRACE = 8;
struct Allocation
{
usz size;
TrackingEnv tracking_env;
void* ptr;
usz size;
void*[MAX_BACKTRACE] backtrace;
}
def AllocMap = HashMap(<uptr, Allocation>);
@@ -79,38 +80,42 @@ fn Allocation[] TrackingAllocator.allocations_tlist(&self, Allocator* allocator)
**/
fn usz TrackingAllocator.allocation_count(&self) => self.map.count;
fn void*! TrackingAllocator.acquire(&self, usz size, bool clear, usz alignment, usz offset, TrackingEnv* env) @dynamic
fn void*! TrackingAllocator.acquire(&self, usz size, bool clear, usz alignment, usz offset) @dynamic
{
void* data = self.inner_allocator.acquire(size, clear, alignment, offset, env)!;
void* data = self.inner_allocator.acquire(size, clear, alignment, offset)!;
self.allocs_total++;
if (data)
{
self.map.set((uptr)data, { .size = size, .ptr = data, .tracking_env = env ? *env : TrackingEnv{} });
void*[MAX_BACKTRACE] bt;
backtrace::capture_current(&bt);
self.map.set((uptr)data, { data, size, bt });
self.mem_total += size;
self.allocs_total++;
}
return data;
}
fn void*! TrackingAllocator.resize(&self, void* old_pointer, usz size, usz alignment, usz offset, TrackingEnv* env) @dynamic
fn void*! TrackingAllocator.resize(&self, void* old_pointer, usz size, usz alignment, usz offset) @dynamic
{
void* data = self.inner_allocator.resize(old_pointer, size, alignment, offset, env)!;
void* data = self.inner_allocator.resize(old_pointer, size, alignment, offset)!;
if (old_pointer)
{
self.map.remove((uptr)old_pointer);
}
if (data)
{
self.map.set((uptr)data, { .size = size, .ptr = data, .tracking_env = env ? *env : TrackingEnv{} });
void*[8] bt;
backtrace::capture_current(&bt);
self.map.set((uptr)data, { data, size, bt });
self.mem_total += size;
self.allocs_total++;
}
return data;
}
fn void TrackingAllocator.release(&self, void* old_pointer, bool is_aligned, TrackingEnv* env) @dynamic
fn void TrackingAllocator.release(&self, void* old_pointer, bool is_aligned) @dynamic
{
self.inner_allocator.release(old_pointer, is_aligned, env);
self.inner_allocator.release(old_pointer, is_aligned);
if (old_pointer) self.map.remove((uptr)old_pointer);
}
@@ -126,41 +131,84 @@ fn void! TrackingAllocator.fprint_report(&self, OutStream* out)
usz total = 0;
usz entries = 0;
bool leaks = false;
@pool()
{
Allocation[] allocs = self.map.value_tlist();
if (allocs.len)
{
$if (!env::TRACK_MEMORY):
io::fprintn(out, "======== Memory Report ========")!;
io::fprintn(out, "Size in bytes Address")!;
foreach (i, &allocation : allocs)
if (!allocs[0].backtrace[0])
{
entries++;
total += allocation.size;
io::fprintfn(out, "%13s %p", allocation.size, allocation.ptr)!;
io::fprintn(out, "======== Memory Report ========")!;
io::fprintn(out, "Size in bytes Address")!;
foreach (i, &allocation : allocs)
{
entries++;
total += allocation.size;
io::fprintfn(out, "%13s %p", allocation.size, allocation.ptr)!;
}
io::fprintn(out, "===============================")!;
}
io::fprintn(out, "===============================")!;
$else
io::fprintn(out, "================================== Memory Report ==================================")!;
io::fprintn(out, "Size in bytes Address Function File")!;
foreach (i, &allocation : allocs)
else
{
entries++;
total += allocation.size;
TrackingEnv *env = &allocation.tracking_env;
io::fprintfn(out, "%13s %p %-30s %s:%d", allocation.size, allocation.ptr, env.function, env.file, env.line)!;
io::fprintn(out, "================================== Memory Report ==================================")!;
io::fprintn(out, "Size in bytes Address Function ")!;
foreach (i, &allocation : allocs)
{
entries++;
total += allocation.size;
BacktraceList backtraces = {};
Backtrace trace = backtrace::BACKTRACE_UNKNOWN;
if (allocation.backtrace[3])
{
trace = backtrace::symbolize_backtrace(allocation.backtrace[3:1], mem::temp()).get(0) ?? backtrace::BACKTRACE_UNKNOWN;
}
if (trace.function.len) leaks = true;
io::fprintfn(out, "%13s %p %s:%d", allocation.size,
allocation.ptr, trace.function.len ? trace.function : "???",
trace.line ? trace.line : 0)!;
}
io::fprintn(out, "===================================================================================")!;
}
io::fprintn(out, "===================================================================================")!;
$endif
}
else
{
io::fprintn(out, "* NO ALLOCATIONS FOUND *")!;
}
io::fprintfn(out, "- Total currently allocated memory: %d", total)!;
io::fprintfn(out, "- Total current allocations: %d", entries)!;
io::fprintfn(out, "- Total allocations (freed and retained): %d", self.allocs_total)!;
io::fprintfn(out, "- Total allocated memory (freed and retained): %d", self.mem_total)!;
if (leaks)
{
io::fprintn(out)!;
io::fprintn(out, "Full leak report:")!;
foreach (i, &allocation : allocs)
{
if (!allocation.backtrace[3])
{
io::fprintfn(out, "Allocation %d (%d bytes) - no backtrace available.", i + 1, allocation.size)!;
continue;
}
BacktraceList backtraces = {};
usz end = MAX_BACKTRACE;
foreach (j, val : allocation.backtrace)
{
if (!val)
{
end = j;
break;
}
}
BacktraceList list = backtrace::symbolize_backtrace(allocation.backtrace[3..(end - 1)], mem::temp())!;
io::fprintfn(out, "Allocation %d (%d bytes): ", i + 1, allocation.size)!;
foreach (trace : list)
{
io::fprintfn(out, " %s", trace);
}
}
}
};
io::fprintfn(out, "- Total currently allocated memory: %d", total)!;
io::fprintfn(out, "- Total current allocations: %d", entries)!;
io::fprintfn(out, "- Total allocations (freed and retained): %d", self.allocs_total)!;
io::fprintfn(out, "- Total allocated memory (freed and retained): %d", self.mem_total)!;
}