mirror of
https://github.com/Ed94/raddebugger.git
synced 2026-10-14 04:49:41 -07:00
384 lines
11 KiB
C
384 lines
11 KiB
C
// Copyright (c) Epic Games Tools
|
|
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
|
|
|
////////////////////////////////
|
|
//~ rjf: Global Arena Table
|
|
|
|
#if ARENA_TABLE_DEBUG
|
|
typedef struct ArenaTableNode ArenaTableNode;
|
|
struct ArenaTableNode
|
|
{
|
|
ArenaTableNode *next;
|
|
Arena *arena;
|
|
};
|
|
global U64 arena_table_take_init = 0;
|
|
global U64 arena_table_inited = 0;
|
|
global U64 arena_table_lock = 0;
|
|
global ArenaTableNode *arena_table = 0;
|
|
global U64 arena_table_count = 0;
|
|
global U64 arena_table_cap = 0;
|
|
global ArenaTableNode *free_arena_table_node = 0;
|
|
#endif
|
|
|
|
////////////////////////////////
|
|
//~ rjf: Arena Functions
|
|
|
|
//- rjf: arena creation/destruction
|
|
|
|
internal Arena *
|
|
arena_alloc_(ArenaParams *params)
|
|
{
|
|
U64 reserve_size = params->reserve_size;
|
|
U64 commit_size = params->commit_size;
|
|
|
|
// rjf: reserve/commit initial block
|
|
void *base = params->optional_backing_buffer;
|
|
if(base == 0)
|
|
{
|
|
// rjf: round up reserve/commit sizes
|
|
if(params->flags & ArenaFlag_LargePages)
|
|
{
|
|
reserve_size = AlignPow2(reserve_size, get_system_info()->large_page_size);
|
|
commit_size = AlignPow2(commit_size, get_system_info()->large_page_size);
|
|
}
|
|
else
|
|
{
|
|
reserve_size = AlignPow2(reserve_size, get_system_info()->page_size);
|
|
commit_size = AlignPow2(commit_size, get_system_info()->page_size);
|
|
}
|
|
|
|
if(params->flags & ArenaFlag_LargePages)
|
|
{
|
|
base = reserve_memory_large(reserve_size);
|
|
commit_memory_large(base, commit_size);
|
|
}
|
|
else
|
|
{
|
|
base = reserve_memory(reserve_size);
|
|
commit_memory(base, commit_size);
|
|
}
|
|
AsanPoisonMemoryRegion(base, commit_size);
|
|
// TODO(rjf): we need to reintroduce this later when we have the ability to remove annotations...
|
|
// raddbg_annotate_vaddr_range(base, reserve_size, "arena %s:%i", params->allocation_site_file, params->allocation_site_line);
|
|
}
|
|
else
|
|
{
|
|
AsanPoisonMemoryRegion(base, params->reserve_size);
|
|
}
|
|
|
|
// rjf: panic on arena creation failure
|
|
#if OS_FEATURE_GRAPHICAL
|
|
if(Unlikely(base == 0))
|
|
{
|
|
wm_graphical_message(1, str8_lit("Fatal Allocation Failure"), str8_lit("Unexpected memory allocation failure."));
|
|
abort_self(1);
|
|
}
|
|
#endif
|
|
|
|
// rjf: extract arena header & fill
|
|
AsanUnpoisonMemoryRegion(base, ARENA_HEADER_SIZE);
|
|
Arena *arena = base;
|
|
arena->current = arena;
|
|
arena->flags = params->flags;
|
|
arena->cmt_size = params->commit_size;
|
|
arena->res_size = params->reserve_size;
|
|
arena->base_pos = 0;
|
|
arena->pos = ARENA_HEADER_SIZE;
|
|
arena->cmt = commit_size;
|
|
arena->res = reserve_size;
|
|
arena->allocation_site_file = params->allocation_site_file;
|
|
arena->allocation_site_line = params->allocation_site_line;
|
|
arena->name = params->name;
|
|
#if ARENA_FREE_LIST
|
|
arena->free_last = 0;
|
|
#endif
|
|
|
|
// rjf: store in global arena table
|
|
#if ARENA_TABLE_DEBUG
|
|
if(ins_atomic_u64_eval_cond_assign(&arena_table_take_init, 1, 0) == 0)
|
|
{
|
|
arena_table = reserve_memory(GB(256));
|
|
arena_table_cap = 4096;
|
|
commit_memory(arena_table, arena_table_cap * sizeof(arena_table[0]));
|
|
ins_atomic_u64_inc_eval(&arena_table_inited);
|
|
}
|
|
for(;!ins_atomic_u64_eval(&arena_table_inited);) {}
|
|
for(;;)
|
|
{
|
|
B32 got_lock = (ins_atomic_u64_eval_cond_assign(&arena_table_lock, 1, 0) == 0);
|
|
if(got_lock)
|
|
{
|
|
ArenaTableNode *node = free_arena_table_node;
|
|
if(node != 0)
|
|
{
|
|
SLLStackPop(free_arena_table_node);
|
|
}
|
|
else
|
|
{
|
|
node = &arena_table[arena_table_count];
|
|
if(arena_table_count >= arena_table_cap)
|
|
{
|
|
U64 arena_table_cap__pre_grow = arena_table_cap;
|
|
arena_table_cap *= 2;
|
|
U64 arena_table_cap__post_grow = arena_table_cap;
|
|
commit_memory(arena_table + arena_table_cap__pre_grow, sizeof(arena_table[0]) * (arena_table_cap__post_grow - arena_table_cap__pre_grow));
|
|
}
|
|
arena_table_count += 1;
|
|
}
|
|
node->arena = arena;
|
|
arena->table_node = node;
|
|
ins_atomic_u64_eval_assign(&arena_table_lock, 0);
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return arena;
|
|
}
|
|
|
|
internal void
|
|
arena_release(Arena *arena)
|
|
{
|
|
#if PROFILE_TELEMETRY
|
|
{
|
|
Arena *base_arena = arena;
|
|
while (base_arena->prev) base_arena = base_arena->prev;
|
|
tmPlot(0, TM_PLOT_UNITS_MEMORY, TM_PLOT_DRAW_LINE, 0, "%s/%p", base_arena->name, base_arena);
|
|
}
|
|
#endif
|
|
|
|
#if ARENA_TABLE_DEBUG
|
|
for(Arena *n = arena->current; n != 0; n = n->prev)
|
|
{
|
|
for(;;)
|
|
{
|
|
B32 got_lock = (ins_atomic_u64_eval_cond_assign(&arena_table_lock, 1, 0) == 0);
|
|
if(got_lock)
|
|
{
|
|
ArenaTableNode *table_node = n->table_node;
|
|
MemoryZeroStruct(table_node);
|
|
SLLStackPush(free_arena_table_node, table_node);
|
|
ins_atomic_u64_eval_assign(&arena_table_lock, 0);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
for(Arena *n = arena->current, *prev = 0; n != 0; n = prev)
|
|
{
|
|
prev = n->prev;
|
|
AsanUnpoisonMemoryRegion(n, n->cmt);
|
|
release_memory(n, n->res);
|
|
}
|
|
}
|
|
|
|
//- rjf: arena push/pop core functions
|
|
|
|
internal void *
|
|
arena_push(Arena *arena, U64 size, U64 align, B32 zero)
|
|
{
|
|
Arena *current = arena->current;
|
|
U64 pos_pre = AlignPow2(current->pos, align);
|
|
U64 pos_pst = pos_pre + size;
|
|
|
|
// TODO: Newly allocated arenas already have zeroed commited pages,
|
|
// so this unnecessarily zeroes them again.
|
|
//
|
|
// rjf: compute the size we need to zero
|
|
U64 size_to_zero = 0;
|
|
if(zero)
|
|
{
|
|
size_to_zero = Min(current->cmt, pos_pst) - pos_pre;
|
|
}
|
|
|
|
// rjf: chain, if needed
|
|
if(current->res < pos_pst && !(arena->flags & ArenaFlag_NoChain))
|
|
{
|
|
Arena *new_block = 0;
|
|
|
|
#if ARENA_FREE_LIST
|
|
{
|
|
Arena *prev_block;
|
|
for(new_block = arena->free_last, prev_block = 0; new_block != 0; prev_block = new_block, new_block = new_block->prev)
|
|
{
|
|
if(new_block->res >= AlignPow2(new_block->pos, align) + size)
|
|
{
|
|
if(prev_block)
|
|
{
|
|
prev_block->prev = new_block->prev;
|
|
}
|
|
else
|
|
{
|
|
arena->free_last = new_block->prev;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if(new_block == 0)
|
|
{
|
|
U64 res_size = current->res_size;
|
|
U64 cmt_size = current->cmt_size;
|
|
if(size + ARENA_HEADER_SIZE > res_size)
|
|
{
|
|
res_size = AlignPow2(size + ARENA_HEADER_SIZE, align);
|
|
cmt_size = AlignPow2(size + ARENA_HEADER_SIZE, align);
|
|
}
|
|
new_block = arena_alloc(.reserve_size = res_size,
|
|
.commit_size = cmt_size,
|
|
.flags = current->flags,
|
|
.allocation_site_file = current->allocation_site_file,
|
|
.allocation_site_line = current->allocation_site_line);
|
|
|
|
size_to_zero = 0;
|
|
}
|
|
else
|
|
{
|
|
size_to_zero = size;
|
|
}
|
|
|
|
new_block->base_pos = current->base_pos + current->res;
|
|
SLLStackPush_N(arena->current, new_block, prev);
|
|
|
|
current = new_block;
|
|
pos_pre = AlignPow2(current->pos, align);
|
|
pos_pst = pos_pre + size;
|
|
}
|
|
|
|
// rjf: commit new pages, if needed
|
|
if(current->cmt < pos_pst)
|
|
{
|
|
U64 cmt_pst_aligned = pos_pst + current->cmt_size-1;
|
|
cmt_pst_aligned -= cmt_pst_aligned%current->cmt_size;
|
|
U64 cmt_pst_clamped = ClampTop(cmt_pst_aligned, current->res);
|
|
U64 cmt_size = cmt_pst_clamped - current->cmt;
|
|
U8 *cmt_ptr = (U8 *)current + current->cmt;
|
|
if(current->flags & ArenaFlag_LargePages)
|
|
{
|
|
commit_memory_large(cmt_ptr, cmt_size);
|
|
}
|
|
else
|
|
{
|
|
commit_memory(cmt_ptr, cmt_size);
|
|
}
|
|
AsanPoisonMemoryRegion(cmt_ptr, cmt_size);
|
|
current->cmt = cmt_pst_clamped;
|
|
}
|
|
|
|
// rjf: push onto current block
|
|
void *result = 0;
|
|
if(current->cmt >= pos_pst)
|
|
{
|
|
result = (U8 *)current+pos_pre;
|
|
current->pos = pos_pst;
|
|
AsanUnpoisonMemoryRegion(result, size);
|
|
MemoryZero(result, size_to_zero);
|
|
}
|
|
|
|
#if PROFILE_TELEMETRY
|
|
if(size > KB(1))
|
|
{
|
|
Arena *base_arena = arena;
|
|
while (base_arena->prev) base_arena = base_arena->prev;
|
|
tmPlot(0, TM_PLOT_UNITS_MEMORY, TM_PLOT_DRAW_LINE, (double)(current->base_pos + current->pos) / 1024.0 / 1024.0, "%s/%p", base_arena->name, base_arena);
|
|
}
|
|
#endif
|
|
|
|
// rjf: panic on failure
|
|
#if OS_FEATURE_GRAPHICAL
|
|
if(Unlikely(result == 0))
|
|
{
|
|
wm_graphical_message(1, str8_lit("Fatal Allocation Failure"), str8_lit("Unexpected memory allocation failure."));
|
|
abort_self(1);
|
|
}
|
|
#endif
|
|
|
|
return result;
|
|
}
|
|
|
|
internal U64
|
|
arena_pos(Arena *arena)
|
|
{
|
|
Arena *current = arena->current;
|
|
U64 pos = current->base_pos + current->pos;
|
|
return pos;
|
|
}
|
|
|
|
internal void
|
|
arena_pop_to(Arena *arena, U64 pos)
|
|
{
|
|
U64 big_pos = ClampBot(ARENA_HEADER_SIZE, pos);
|
|
Arena *current = arena->current;
|
|
|
|
#if ARENA_FREE_LIST
|
|
for(Arena *prev = 0; current->base_pos >= big_pos; current = prev)
|
|
{
|
|
prev = current->prev;
|
|
current->pos = ARENA_HEADER_SIZE;
|
|
SLLStackPush_N(arena->free_last, current, prev);
|
|
AsanPoisonMemoryRegion((U8*)current + ARENA_HEADER_SIZE, current->res - ARENA_HEADER_SIZE);
|
|
}
|
|
#else
|
|
for(Arena *prev = 0; current->base_pos >= big_pos; current = prev)
|
|
{
|
|
prev = current->prev;
|
|
AsanUnpoisonMemoryRegion(current, current->cmt);
|
|
release_memory(current, current->res);
|
|
}
|
|
#endif
|
|
arena->current = current;
|
|
U64 new_pos = big_pos - current->base_pos;
|
|
AssertAlways(new_pos <= current->pos);
|
|
AsanPoisonMemoryRegion((U8*)current + new_pos, (current->pos - new_pos));
|
|
current->pos = new_pos;
|
|
|
|
#if PROFILE_TELEMETRY
|
|
if((pos - (new_pos + current->base_pos)) > KB(1))
|
|
{
|
|
Arena *base_arena = arena;
|
|
while (base_arena->prev) base_arena = base_arena->prev;
|
|
tmPlot(0, TM_PLOT_UNITS_MEMORY, TM_PLOT_DRAW_LINE, (double)(current->base_pos + current->pos) / 1024.0 / 1024.0, "%s/%p", base_arena->name, base_arena);
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
//- rjf: arena push/pop helpers
|
|
|
|
internal void
|
|
arena_clear(Arena *arena)
|
|
{
|
|
arena_pop_to(arena, 0);
|
|
}
|
|
|
|
internal void
|
|
arena_pop(Arena *arena, U64 amt)
|
|
{
|
|
U64 pos_old = arena_pos(arena);
|
|
U64 pos_new = pos_old;
|
|
if(amt < pos_old)
|
|
{
|
|
pos_new = pos_old - amt;
|
|
}
|
|
arena_pop_to(arena, pos_new);
|
|
}
|
|
|
|
//- rjf: temporary arena scopes
|
|
|
|
internal Temp
|
|
temp_begin(Arena *arena)
|
|
{
|
|
U64 pos = arena_pos(arena);
|
|
Temp temp = {arena, pos};
|
|
return temp;
|
|
}
|
|
|
|
internal void
|
|
temp_end(Temp temp)
|
|
{
|
|
arena_pop_to(temp.arena, temp.pos);
|
|
}
|