DebugAllocator
Description
DebugAllocator struct. Owns its heap / meta / page backing allocators inline; no external parent / meta to pass at init. Init-by-value via DebugAllocatorInit().
Usage example (Cross-references)
Usage examples (Cross-references)
- In
AllocDebug.c:11:
// overflows, no double-frees.
bool test_debug_normal_alloc_free(void) {
DebugAllocator dbg = DebugAllocatorInit();
Allocator *adbg = ALLOCATOR_OF(&dbg);- In
AllocDebug.c:38:
// double-free counter without crashing.
bool test_debug_catches_double_free(void) {
DebugAllocator dbg = DebugAllocatorInit();
Allocator *adbg = ALLOCATOR_OF(&dbg);- In
AllocDebug.c:54:
// bump the overflow counter.
bool test_debug_catches_overflow(void) {
DebugAllocator dbg = DebugAllocatorInit();
Allocator *adbg = ALLOCATOR_OF(&dbg);- In
AllocDebug.c:72:
// we query before destroy.
bool test_debug_leak_count(void) {
DebugAllocator dbg = DebugAllocatorInit();
Allocator *adbg = ALLOCATOR_OF(&dbg);- In
AllocDebug.c:96:
DebugAllocatorConfig cfg = DEBUG_ALLOCATOR_DEFAULTS;
cfg.force_page_backing = true;
DebugAllocator dbg = DebugAllocatorInitWith(cfg);
Allocator *adbg = ALLOCATOR_OF(&dbg);- In
Debug.c:92:
// ---------------------------------------------------------------------------
static DebugAllocator *debug_validate_self(const Allocator *self) {
if (!self || self->__magic != MISRA_DEBUG_ALLOCATOR_MAGIC) {
LOG_FATAL("type-confusion: allocator passed to debug_allocator_* is not a DebugAllocator");- In
Debug.c:94:
static DebugAllocator *debug_validate_self(const Allocator *self) {
if (!self || self->__magic != MISRA_DEBUG_ALLOCATOR_MAGIC) {
LOG_FATAL("type-confusion: allocator passed to debug_allocator_* is not a DebugAllocator");
}
DebugAllocator *dbg = (DebugAllocator *)self;- In
Debug.c:96:
LOG_FATAL("type-confusion: allocator passed to debug_allocator_* is not a DebugAllocator");
}
DebugAllocator *dbg = (DebugAllocator *)self;
u64 cur_tid = debug_current_tid();
if (dbg->creator_tid != cur_tid) {- In
Debug.c:100:
if (dbg->creator_tid != cur_tid) {
LOG_FATAL(
"DebugAllocator: cross-thread use detected (created on {x}, called from {x}). "
"Each thread must use its own DebugAllocator instance.",
dbg->creator_tid,- In
Debug.c:101:
LOG_FATAL(
"DebugAllocator: cross-thread use detected (created on {x}, called from {x}). "
"Each thread must use its own DebugAllocator instance.",
dbg->creator_tid,
cur_tid- In
Debug.c:117:
void *debug_allocator_allocate(Allocator *self, size bytes, i8 zeroed) {
DebugAllocator *dbg = debug_validate_self(self);
if (!bytes)
return NULL;- In
Debug.c:147:
if (!MapInsertR(&dbg->live, user_p, rec)) {
AllocatorFree(src, user_p, padded);
LOG_ERROR("DebugAllocator: failed to record allocation in live map");
return NULL;
}- In
Debug.c:165:
}
static void debug_record_free_trace(DebugAllocator *dbg, DebugRecord *rec) {
if (dbg->config.capture_traces && dbg->config.trace_depth > 0) {
u32 depth = dbg->config.trace_depth;- In
Debug.c:176:
void debug_allocator_deallocate(Allocator *self, void *ptr, size bytes) {
DebugAllocator *dbg = debug_validate_self(self);
if (!ptr)
return;- In
Debug.c:187:
dbg->double_frees += 1;
LOG_ERROR(
"DebugAllocator: DOUBLE FREE of {x} (originally {} bytes)",
(u64)(uintptr_t)ptr,
(u64)freed_rec->requested_size- In
Debug.c:201:
}
}
LOG_ERROR("DebugAllocator: free of unknown pointer {x}", (u64)(uintptr_t)ptr);
return;
}- In
Debug.c:207:
if (bytes && bytes != live_rec->requested_size) {
LOG_ERROR(
"DebugAllocator: size mismatch on free of {x} (claimed {} bytes, tracked {} bytes)",
(u64)(uintptr_t)ptr,
(u64)bytes,- In
Debug.c:219:
dbg->overflows += 1;
LOG_ERROR(
"DebugAllocator: BUFFER OVERFLOW past {x} ({} bytes requested)",
(u64)(uintptr_t)ptr,
(u64)live_rec->requested_size- In
Debug.c:249:
size rounded = (padded + page_size - 1) & ~(page_size - 1);
if (!PageProtect(ptr, rounded, PAGE_PROT_NONE)) {
LOG_ERROR("DebugAllocator: PageProtect(PROT_NONE) failed on {x}", (u64)(uintptr_t)ptr);
}
} else {- In
Debug.c:257:
void *debug_allocator_reallocate(Allocator *self, void *ptr, size old_size, size new_size) {
DebugAllocator *dbg = debug_validate_self(self);
if (new_size == 0) {
debug_allocator_deallocate(self, ptr, old_size);- In
Debug.c:281:
// ---------------------------------------------------------------------------
void DebugAllocatorDeinit(DebugAllocator *self) {
if (!self || self->base.__magic != MISRA_DEBUG_ALLOCATOR_MAGIC)
return;- In
Debug.c:288:
if (self->creator_tid != cur_tid) {
LOG_FATAL(
"DebugAllocator: Deinit called from a different thread (created on {x}, called from {x}).",
self->creator_tid,
cur_tid- In
Debug.c:296:
// Report leaks for anything still in `live`.
if (self->live.allocator && self->live.length > 0) {
LOG_ERROR("DebugAllocator: {} live allocation(s) at deinit time:", (u64)self->live.length);
MapForeachPairPtr(&self->live, key_ptr, val_ptr) {
LOG_ERROR(" leaked {x} ({} bytes)", (u64)(uintptr_t)*key_ptr, (u64)val_ptr->requested_size);- In
Debug.c:328:
// ---------------------------------------------------------------------------
size DebugAllocatorLiveCount(const DebugAllocator *self) {
if (!self)
return 0;- In
Debug.c:334:
}
size DebugAllocatorLiveBytes(const DebugAllocator *self) {
if (!self)
return 0;- In
Debug.c:340:
}
size DebugAllocatorDoubleFrees(const DebugAllocator *self) {
if (!self)
return 0;- In
Debug.c:346:
}
size DebugAllocatorOverflows(const DebugAllocator *self) {
if (!self)
return 0;- In
Debug.c:352:
}
void DebugAllocatorReportLeaks(DebugAllocator *self, Str *out) {
if (!self || !out)
return;- In
Debug.c:358:
return;
StrWriteFmt(out, "DebugAllocator: {} live allocation(s):\n", (u64)self->live.length);
MapForeachPairPtr(&self->live, key_ptr, val_ptr) {
StrWriteFmt(out, " leak: {x} ({} bytes)\n", (u64)(uintptr_t)*key_ptr, (u64)val_ptr->requested_size);- In
Allocator.h:35:
typedef struct SlabAllocator SlabAllocator;
typedef struct BudgetAllocator BudgetAllocator;
typedef struct DebugAllocator DebugAllocator;
// `zeroed` uses `i8` (signed char) directly instead of `bool` to
- In
Allocator.h:231:
SlabAllocator *: (Allocator *)(allocator_ptr), \
BudgetAllocator *: (Allocator *)(allocator_ptr), \
DebugAllocator *: (Allocator *)(allocator_ptr) \
)- In
Debug.h:124:
u64 bytes_in_use;
u64 creator_tid;
} DebugAllocator;
// Vtable functions, exposed because the Init macro stamps them
- In
Debug.h:149:
/// `page`-managed pages) and clears the struct.
///
void DebugAllocatorDeinit(DebugAllocator *self);
/// Number of outstanding allocations (alloc minus free).
- In
Debug.h:152:
/// Number of outstanding allocations (alloc minus free).
size DebugAllocatorLiveCount(const DebugAllocator *self);
/// Total user-requested bytes still outstanding.
size DebugAllocatorLiveBytes(const DebugAllocator *self);- In
Debug.h:154:
size DebugAllocatorLiveCount(const DebugAllocator *self);
/// Total user-requested bytes still outstanding.
size DebugAllocatorLiveBytes(const DebugAllocator *self);
/// Number of double-free events caught.
size DebugAllocatorDoubleFrees(const DebugAllocator *self);- In
Debug.h:156:
size DebugAllocatorLiveBytes(const DebugAllocator *self);
/// Number of double-free events caught.
size DebugAllocatorDoubleFrees(const DebugAllocator *self);
/// Number of canary-corruption events caught.
size DebugAllocatorOverflows(const DebugAllocator *self);- In
Debug.h:158:
size DebugAllocatorDoubleFrees(const DebugAllocator *self);
/// Number of canary-corruption events caught.
size DebugAllocatorOverflows(const DebugAllocator *self);
/// Append a human-readable leak report to `out`.
void DebugAllocatorReportLeaks(DebugAllocator *self, Str *out);- In
Debug.h:160:
size DebugAllocatorOverflows(const DebugAllocator *self);
/// Append a human-readable leak report to `out`.
void DebugAllocatorReportLeaks(DebugAllocator *self, Str *out);
#ifdef __cplusplus- In
Debug.h:193:
#define DebugAllocatorInitWith(_cfg) \
((DebugAllocator) { \
.base = \
{.allocate = debug_allocator_allocate, \- In
Default.h:48:
#if MISRA_DEFAULT_ALLOC_DEBUG
typedef DebugAllocator DefaultAllocator;
#else
typedef HeapAllocator DefaultAllocator;
Last updated on