mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 20:11:17 +00:00
Use backtrace on windows. Updated backtrace API
This commit is contained in:
committed by
Christoffer Lerno
parent
81c93e3488
commit
ffb0021d04
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)!;
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user