From 1e6e9581c3f9381a7c6e0703038b7f9548913ec7 Mon Sep 17 00:00:00 2001 From: Ed_ Date: Mon, 6 May 2024 23:29:47 -0400 Subject: [PATCH] Bug fixes to allocators and containers Still haven't found the main bug with resizing zpl_hashmap on string interning --- .vscode/launch.json | 12 ++++ .vscode/settings.json | 6 +- code/api.odin | 17 +++-- code/env.odin | 7 +- code/grime_array.odin | 49 ++++++++----- code/grime_context.odin | 7 ++ code/grime_hashmap_zpl.odin | 12 ++-- code/grime_memory_tracker.odin | 117 +++++++++++++++++++++++++++++++ code/grime_pool_allocator.odin | 32 ++++++++- code/grime_slab_allocator.odin | 18 ++++- code/grime_string_interning.odin | 4 +- code/grime_virtual_arena.odin | 33 +++++++-- code/host/host.odin | 8 +-- code/logger.odin | 2 +- code/parser_whitespace.odin | 2 +- toolchain/Odin | 2 +- 16 files changed, 276 insertions(+), 52 deletions(-) create mode 100644 code/grime_memory_tracker.odin diff --git a/.vscode/launch.json b/.vscode/launch.json index 7506802..3f892e6 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -4,6 +4,18 @@ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ + { + "name": "Debug gen_odin_src", + "type": "cppvsdbg", + "request": "launch", + "program": "${workspaceFolder}/toolchain/odin/codegen/build/gen_src.exe", + "args": [], + "stopAtEntry": false, + "cwd": "${workspaceFolder}/toolchain/odin", + "environment": [], + "console": "externalTerminal", + "visualizerFile": "${workspaceFolder}/toolchain/odin/scripts/gencpp.natvis" + }, { "type": "lldb", "request": "launch", diff --git a/.vscode/settings.json b/.vscode/settings.json index 4cf36c7..e941d1c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -10,6 +10,8 @@ "files.associations": { "*.rmd": "markdown", "type_traits": "cpp", - "utf8proc.c": "cpp" - } + "utf8proc.c": "cpp", + "xtr1common": "cpp" + }, + "C_Cpp.intelliSenseEngineFallback": "disabled" } diff --git a/code/api.odin b/code/api.odin index 8cdd541..bddab54 100644 --- a/code/api.odin +++ b/code/api.odin @@ -15,9 +15,9 @@ import rl "vendor:raylib" Path_Assets :: "../assets/" Path_Input_Replay :: "scratch.sectr_replay" -Persistent_Slab_DBG_Name :: "Peristent Slab" -Frame_Slab_DBG_Name :: "Frame Slab" -Transient_Slab_DBG_Name :: "Transient Slab" +Persistent_Slab_DBG_Name := "Peristent Slab" +Frame_Slab_DBG_Name := "Frame Slab" +Transient_Slab_DBG_Name := "Transient Slab" ModuleAPI :: struct { lib : dynlib.Library, @@ -56,6 +56,7 @@ startup :: proc( prof : ^SpallProfiler, persistent_mem, frame_mem, transient_mem } state := new( State, persistent_allocator() ) + Memory_App.state = state using state // Setup Persistent Slab @@ -118,16 +119,14 @@ startup :: proc( prof : ^SpallProfiler, persistent_mem, frame_mem, transient_mem string_cache = str_cache_init() - context.user_ptr = state - input = & input_data[1] input_prev = & input_data[0] // Configuration Load { using config - resolution_width = 1280 - resolution_height = 900 + resolution_width = 1000 + resolution_height = 600 refresh_rate = 0 cam_min_zoom = 0.25 @@ -153,7 +152,7 @@ startup :: proc( prof : ^SpallProfiler, persistent_mem, frame_mem, transient_mem rl.SetConfigFlags( { rl.ConfigFlag.WINDOW_RESIZABLE, - rl.ConfigFlag.WINDOW_TOPMOST, + // rl.ConfigFlag.WINDOW_TOPMOST, }) window_width : i32 = cast(i32) config.resolution_width @@ -275,7 +274,7 @@ reload :: proc( prof : ^SpallProfiler, persistent_mem, frame_mem, transient_mem, context.allocator = persistent_allocator() context.temp_allocator = transient_allocator() - state := get_state(); using state + using state // Procedure Addresses are not preserved on hot-reload. They must be restored for persistent data. // The only way to alleviate this is to either do custom handles to allocators diff --git a/code/env.odin b/code/env.odin index d1e3d60..963e6a3 100644 --- a/code/env.odin +++ b/code/env.odin @@ -8,6 +8,8 @@ import "core:os" import rl "vendor:raylib" +Str_App_State := "App State" + Memory_App : Memory Memory_Base_Address_Persistent :: Terabyte * 1 @@ -40,6 +42,8 @@ Memory :: struct { transient : ^VArena, files_buffer : ^VArena, + state : ^State, + // Should only be used for small memory allocation iterations // Not for large memory env states snapshot : MemorySnapshot, @@ -199,7 +203,8 @@ State :: struct { } get_state :: proc "contextless" () -> ^ State { - return cast( ^ State ) Memory_App.persistent.reserve_start + // return cast( ^ State ) Memory_App.persistent.reserve_start + return Memory_App.state } AppWindow :: struct { diff --git a/code/grime_array.odin b/code/grime_array.odin index 8d2f621..58eaeaf 100644 --- a/code/grime_array.odin +++ b/code/grime_array.odin @@ -16,6 +16,8 @@ import "core:slice" ArrayHeader :: struct ( $ Type : typeid ) { backing : Allocator, + dbg_name : string, + fixed_cap : b32, capacity : u64, num : u64, data : [^]Type, @@ -54,7 +56,7 @@ array_init :: proc( $ Type : typeid, allocator : Allocator ) -> ( Array(Type), A } array_init_reserve :: proc -( $ Type : typeid, allocator : Allocator, capacity : u64 ) -> ( result : Array(Type), alloc_error : AllocatorError ) +( $ Type : typeid, allocator : Allocator, capacity : u64, fixed_cap : b32 = false, dbg_name : string = "" ) -> ( result : Array(Type), alloc_error : AllocatorError ) { header_size := size_of(ArrayHeader(Type)) array_size := header_size + int(capacity) * size_of(Type) @@ -66,6 +68,8 @@ array_init_reserve :: proc result.header = cast( ^ArrayHeader(Type)) raw_mem; result.backing = allocator + // result.dbg_name = dbg_name + result.fixed_cap = fixed_cap result.capacity = capacity result.data = cast( [^]Type ) (cast( [^]ArrayHeader(Type)) result.header)[ 1:] return @@ -127,14 +131,8 @@ array_append_at :: proc( using self : ^Array( $ Type ), item : Type, id : u64 ) } target := & data[id] + libc.memmove( ptr_offset(target, 1), target, uint(num - id) * size_of(Type) ) - // TODO(Ed) : VERIFY VIA DEBUG THIS COPY IS FINE. - dst = slice_ptr( ptr_offset(target) + 1, num - id - 1 ) - src = slice_ptr( target, num - id ) - copy( dst, src ) - - // Note(Ed) : Original code from gencpp - // libc.memmove( ptr_offset(target, 1), target, (num - idx) * size_of(Type) ) data[id] = item num += 1 return AllocatorError.None @@ -183,11 +181,11 @@ array_push_back :: proc( using self : Array( $ Type)) -> b32 { return true } -array_clear :: proc( using self : Array( $ Type ), zero_data : b32 ) { +array_clear :: proc "contextless" ( using self : Array( $ Type ), zero_data : b32 = false ) { if zero_data { - mem.set( raw_data( data ), 0, num ) + mem.set( data, 0, int(num * size_of(Type)) ) } - num = 0 + header.num = 0 } array_fill :: proc( using self : Array( $ Type ), begin, end : u64, value : Type ) -> b32 @@ -206,7 +204,7 @@ array_fill :: proc( using self : Array( $ Type ), begin, end : u64, value : Type } array_free :: proc( using self : Array( $ Type ) ) { - free( data, backing ) + free( self.header, backing ) self.data = nil } @@ -228,13 +226,13 @@ array_pop :: proc( using self : Array( $ Type ) ) { array_remove_at :: proc( using self : Array( $ Type ), id : u64 ) { - verify( id >= num, "Attempted to remove from an index larger than the array" ) + verify( id < header.num, "Attempted to remove from an index larger than the array" ) - left = slice_ptr( data, id ) - right = slice_ptr( ptr_offset( memory_after(left), 1), num - len(left) - 1 ) - copy( left, right ) + left := & data[id] + right := & data[id + 1] + libc.memmove( left, right, uint(num - id) * size_of(Type) ) - num -= 1 + header.num -= 1 } array_reserve :: proc( using self : ^Array( $ Type ), new_capacity : u64 ) -> AllocatorError @@ -291,3 +289,20 @@ array_set_capacity :: proc( self : ^Array( $ Type ), new_capacity : u64 ) -> All self.header.num = self.num return result_code } + +array_block_size :: proc "contextless" ( self : Array( $Type ) ) -> u64 { + header_size :: size_of(ArrayHeader(Type)) + block_size := cast(u64) (header_size + self.capacity * size_of(Type)) + return block_size +} + +array_memtracker_entry :: proc( self : Array( $Type ), name : string ) -> MemoryTrackerEntry { + header_size :: size_of(ArrayHeader(Type)) + block_size := cast(uintptr) (header_size + (cast(uintptr) self.capacity) * size_of(Type)) + + block_start := transmute(^u8) self.header + block_end := ptr_offset( block_start, block_size ) + + tracker_entry := MemoryTrackerEntry { name, block_start, block_end } + return tracker_entry +} diff --git a/code/grime_context.odin b/code/grime_context.odin index 110eea9..233bfb3 100644 --- a/code/grime_context.odin +++ b/code/grime_context.odin @@ -1,5 +1,12 @@ package sectr +// GrimeContextExt :: struct { +// dbg_name : string +// } + +// Global_Transient_Context : GrimeContextExt + context_ext :: proc( $ Type : typeid ) -> (^Type) { return cast(^Type) context.user_ptr } + diff --git a/code/grime_hashmap_zpl.odin b/code/grime_hashmap_zpl.odin index 5c4f9a2..f3ecfca 100644 --- a/code/grime_hashmap_zpl.odin +++ b/code/grime_hashmap_zpl.odin @@ -36,8 +36,8 @@ HMapZPL_Entry :: struct ( $ Type : typeid) { } HMapZPL :: struct ( $ Type : typeid ) { - hashes : Array( i64 ), - entries : Array( HMapZPL_Entry(Type) ), + hashes : Array( i64 ), + entries : Array( HMapZPL_Entry(Type) ), } zpl_hmap_init :: proc( $ Type : typeid, allocator : Allocator ) -> ( HMapZPL( Type), AllocatorError ) { @@ -45,12 +45,12 @@ zpl_hmap_init :: proc( $ Type : typeid, allocator : Allocator ) -> ( HMapZPL( Ty } zpl_hmap_init_reserve :: proc -( $ Type : typeid, allocator : Allocator, num : u64 ) -> ( HMapZPL( Type), AllocatorError ) +( $ Type : typeid, allocator : Allocator, num : u64, dbg_name : string = "" ) -> ( HMapZPL( Type), AllocatorError ) { result : HMapZPL(Type) hashes_result, entries_result : AllocatorError - result.hashes, hashes_result = array_init_reserve( i64, allocator, num ) + result.hashes, hashes_result = array_init_reserve( i64, allocator, num, dbg_name = dbg_name ) if hashes_result != AllocatorError.None { ensure( false, "Failed to allocate hashes array" ) return result, hashes_result @@ -58,7 +58,7 @@ zpl_hmap_init_reserve :: proc array_resize( & result.hashes, num ) slice.fill( slice_ptr( result.hashes.data, cast(int) result.hashes.num), -1 ) - result.entries, entries_result = array_init_reserve( HMapZPL_Entry(Type), allocator, num ) + result.entries, entries_result = array_init_reserve( HMapZPL_Entry(Type), allocator, num, dbg_name = dbg_name ) if entries_result != AllocatorError.None { ensure( false, "Failed to allocate entries array" ) return result, entries_result @@ -119,7 +119,7 @@ zpl_hmap_rehash :: proc( ht : ^ HMapZPL( $ Type ), new_num : u64 ) -> AllocatorE // ensure( false, "ZPL HMAP IS REHASHING" ) last_added_index : i64 - new_ht, init_result := zpl_hmap_init_reserve( Type, ht.hashes.backing, new_num ) + new_ht, init_result := zpl_hmap_init_reserve( Type, ht.hashes.backing, new_num, ht.hashes.dbg_name ) if init_result != AllocatorError.None { ensure( false, "New zpl_hmap failed to allocate" ) return init_result diff --git a/code/grime_memory_tracker.odin b/code/grime_memory_tracker.odin new file mode 100644 index 0000000..4de4ecc --- /dev/null +++ b/code/grime_memory_tracker.odin @@ -0,0 +1,117 @@ +package sectr + +MemoryTrackerEntry :: struct { + start, end : rawptr, + // owner : string, +} + +MemoryTracker :: struct { + name : string, + entries : Array(MemoryTrackerEntry), +} + +memtracker_clear :: proc ( tracker : MemoryTracker ) { + // logf("Clearing tracker: %v", tracker.name) + memtracker_dump_entries(tracker); + array_clear(tracker.entries) +} + +memtracker_init :: proc ( tracker : ^MemoryTracker, allocator : Allocator, num_entries : u64, name : string ) +{ + tracker.name = name + + error : AllocatorError + tracker.entries, error = array_init_reserve( MemoryTrackerEntry, allocator, num_entries, dbg_name = name ) + if error != AllocatorError.None { + fatal("Failed to allocate memory tracker's hashmap"); + } +} + +memtracker_register :: proc( tracker : ^MemoryTracker, new_entry : MemoryTrackerEntry ) +{ + if tracker.entries.num == tracker.entries.capacity { + ensure(false, "Memory tracker entries array full, can no longer register any more allocations") + return + } + + for idx in 0..< tracker.entries.num + { + entry := & tracker.entries.data[idx] + if new_entry.start > entry.start { + continue + } + + if (entry.end < new_entry.start) + { + msg := str_fmt_tmp("Memory tracker(%v) detected a collision:\nold_entry: %v\nnew_entry: %v", tracker.name, entry, new_entry) + ensure( false, msg ) + memtracker_dump_entries(tracker ^) + } + array_append_at( & tracker.entries, new_entry, idx ) + // log(str_fmt_tmp("%v : Registered: %v", tracker.name, new_entry) ) + return + } + + array_append( & tracker.entries, new_entry ) + // log(str_fmt_tmp("%v : Registered: %v", tracker.name, new_entry) ) +} + +memtracker_register_auto_name :: proc( tracker : ^MemoryTracker, start, end : rawptr ) +{ + memtracker_register( tracker, {start, end}) +} + +memtracker_register_auto_name_slice :: proc( tracker : ^MemoryTracker, slice : []byte ) +{ + start := raw_data(slice) + end := & slice[ len(slice) - 1 ] + memtracker_register( tracker, {start, end}) +} + +memtracker_unregister :: proc( tracker : MemoryTracker, to_remove : MemoryTrackerEntry ) +{ + entries := array_to_slice_num(tracker.entries) + for idx in 0..< tracker.entries.num + { + entry := & entries[idx] + if entry.start == to_remove.start { + if (entry.end == to_remove.end || to_remove.end == nil) { + // log(str_fmt_tmp("%v: Unregistered: %v", tracker.name, to_remove)); + array_remove_at(tracker.entries, idx) + return + } + + ensure(false, str_fmt_tmp("%v: Found an entry with the same start address but end address was different:\nentry : %v\nto_remove: %v", tracker.name, entry, to_remove)) + memtracker_dump_entries(tracker) + } + } + + ensure(false, str_fmt_tmp("%v: Attempted to unregister an entry that was not tracked: %v", tracker.name, to_remove)) + memtracker_dump_entries(tracker) +} + +memtracker_check_for_collisions :: proc ( tracker : MemoryTracker ) +{ + entries := array_to_slice_num(tracker.entries) + + for idx in 1 ..< tracker.entries.num { + // Check to make sure each allocations adjacent entries do not intersect + left := & entries[idx - 1] + right := & entries[idx] + + collided := left.start > right.start || left.end > right.end + if collided { + msg := str_fmt_tmp("%v: Memory tracker detected a collision:\nleft: %v\nright: %v", tracker.name, left, right) + memtracker_dump_entries(tracker) + } + } +} + +memtracker_dump_entries :: proc( tracker : MemoryTracker ) +{ + log( "Dumping Memory Tracker:") + for idx in 0 ..< tracker.entries.num { + entry := & tracker.entries.data[idx] + log( str_fmt_tmp("%v", entry) ) + } +} diff --git a/code/grime_pool_allocator.odin b/code/grime_pool_allocator.odin index 4fb7e88..715cf29 100644 --- a/code/grime_pool_allocator.odin +++ b/code/grime_pool_allocator.odin @@ -23,7 +23,9 @@ Pool :: struct { } PoolHeader :: struct { - backing : Allocator, + backing : Allocator, + dbg_name : string, + tracker : MemoryTracker, zero_bucket : b32, block_size : uint, @@ -53,7 +55,8 @@ pool_init :: proc ( bucket_capacity : uint, bucket_reserve_num : uint = 0, alignment : uint = mem.DEFAULT_ALIGNMENT, - allocator : Allocator = context.allocator + allocator : Allocator = context.allocator, + dbg_name : string, ) -> ( pool : Pool, alloc_error : AllocatorError ) { header_size := align_forward_int( size_of(PoolHeader), int(alignment) ) @@ -65,13 +68,17 @@ pool_init :: proc ( pool.header = cast( ^PoolHeader) raw_mem pool.zero_bucket = should_zero_buckets pool.backing = allocator + pool.dbg_name = dbg_name pool.block_size = align_forward_uint(block_size, alignment) pool.bucket_capacity = bucket_capacity pool.alignment = alignment + memtracker_init( & pool.tracker, allocator, Kilobyte * 96, dbg_name ) + if bucket_reserve_num > 0 { alloc_error = pool_allocate_buckets( pool, bucket_reserve_num ) } + pool.current_bucket = pool.bucket_list.first return } @@ -91,6 +98,8 @@ pool_destroy :: proc ( using self : Pool ) } free( self.header, backing ) + + memtracker_clear( self.tracker ) } pool_allocate_buckets :: proc( pool : Pool, num_buckets : uint ) -> AllocatorError @@ -175,6 +184,8 @@ pool_grab :: proc( pool : Pool, zero_memory := false ) -> ( block : []byte, allo if zero_memory { slice.zero(block) } + + memtracker_register_auto_name_slice( & pool.tracker, block) return } @@ -234,6 +245,8 @@ pool_grab :: proc( pool : Pool, zero_memory := false ) -> ( block : []byte, allo slice.zero(block) // log( str_fmt_tmp("Zeroed memory - Range(%p to %p)", block_ptr, cast(rawptr) (uintptr(block_ptr) + uintptr(pool.block_size)))) } + + memtracker_register_auto_name_slice( & pool.tracker, block) return } @@ -257,8 +270,12 @@ pool_release :: proc( self : Pool, block : []byte, loc := #caller_location ) new_free_block.next = self.free_list_head self.free_list_head = new_free_block - new_free_block = new_free_block + // new_free_block = new_free_block // log( str_fmt_tmp("Released block: %p %d", new_free_block, self.block_size)) + + start := new_free_block + end := transmute(rawptr) (uintptr(new_free_block) + uintptr(self.block_size) - 1) + memtracker_unregister( self.tracker, { start, end } ) } pool_reset :: proc( using pool : Pool ) @@ -298,6 +315,15 @@ pool_validate_ownership :: proc( using self : Pool, block : [] byte ) -> b32 block_address := uintptr(raw_data(block)) if start <= block_address && block_address < end { + misalignment := (block_address - start) % uintptr(block_size) + if misalignment != 0 { + ensure(false, "pool_validate_ownership: This data is within this pool's buckets, however its not aligned to the start of a block") + log(str_fmt_tmp("Block address: %p Misalignment: %p closest: %p", + transmute(rawptr)block_address, + transmute(rawptr)misalignment, + rawptr(block_address - misalignment))) + } + within_bucket = true break } diff --git a/code/grime_slab_allocator.odin b/code/grime_slab_allocator.odin index 7a29ca2..07f09f4 100644 --- a/code/grime_slab_allocator.odin +++ b/code/grime_slab_allocator.odin @@ -44,6 +44,7 @@ SlabPolicy :: StackFixed(SlabSizeClass, Slab_Max_Size_Classes) SlabHeader :: struct { dbg_name : string, + tracker : MemoryTracker, backing : Allocator, pools : StackFixed(Pool, Slab_Max_Size_Classes), } @@ -69,6 +70,7 @@ slab_init :: proc( policy : ^SlabPolicy, bucket_reserve_num : uint = 0, allocato slab.header = cast( ^SlabHeader) raw_mem slab.backing = allocator slab.dbg_name = dbg_name + memtracker_init( & slab.tracker, allocator, Kilobyte * 256, dbg_name ) alloc_error = slab_init_pools( slab, policy, bucket_reserve_num, should_zero_buckets ) return } @@ -76,10 +78,12 @@ slab_init :: proc( policy : ^SlabPolicy, bucket_reserve_num : uint = 0, allocato slab_init_pools :: proc ( using self : Slab, policy : ^SlabPolicy, bucket_reserve_num : uint = 0, should_zero_buckets : b32 ) -> AllocatorError { profile(#procedure) + for id in 0 ..< policy.idx { using size_class := policy.items[id] - pool, alloc_error := pool_init( should_zero_buckets, block_size, bucket_capacity, bucket_reserve_num, block_alignment, backing ) + pool_dbg_name := str_fmt_alloc("%v pool[%v]", dbg_name, block_size, allocator = backing) + pool, alloc_error := pool_init( should_zero_buckets, block_size, bucket_capacity, bucket_reserve_num, block_alignment, backing, pool_dbg_name ) if alloc_error != .None do return alloc_error push( & self.pools, pool ) @@ -105,6 +109,7 @@ slab_destroy :: proc( using self : Slab ) } free( self.header, backing ) + memtracker_clear(tracker) } slab_alloc :: proc( self : Slab, @@ -142,6 +147,8 @@ slab_alloc :: proc( self : Slab, if zero_memory { slice.zero(data) } + + memtracker_register_auto_name( & self.tracker, raw_data(block), & block[ len(block) - 1 ] ) return } @@ -153,6 +160,9 @@ slab_free :: proc( using self : Slab, data : []byte, loc := #caller_location ) { pool = pools.items[id] if pool_validate_ownership( pool, data ) { + start := raw_data(data) + end := ptr_offset(start, pool.block_size - 1) + memtracker_unregister( self.tracker, { start, end } ) pool_release( pool, data, loc ) return } @@ -235,9 +245,14 @@ slab_resize :: proc( using self : Slab, // log( str_fmt_tmp("%v: Resize via new block, copying from old data block to new block: (%p %d), (%p %d)", dbg_name, raw_data(data), len(data), raw_data(new_block), len(new_block))) copy_non_overlapping( raw_data(new_block), raw_data(data), int(old_size) ) pool_release( pool_old, data ) + + start := raw_data( data ) + end := rawptr(uintptr(start) + uintptr(pool_old.block_size) - 1) + memtracker_unregister( self.tracker, { start, end } ) } new_data = new_block[ :new_size] + memtracker_register_auto_name( & self.tracker, raw_data(new_block), & new_block[ len(new_block) - 1 ] ) return } @@ -247,6 +262,7 @@ slab_reset :: proc( slab : Slab ) pool := slab.pools.items[id] pool_reset( pool ) } + memtracker_clear(slab.tracker) } slab_validate_pools :: proc( slab : Slab ) diff --git a/code/grime_string_interning.odin b/code/grime_string_interning.odin index 69848a0..9cc757d 100644 --- a/code/grime_string_interning.odin +++ b/code/grime_string_interning.odin @@ -61,8 +61,8 @@ str_cache_init :: proc( /*allocator : Allocator*/ ) -> ( cache : StringCache ) { cache.slab, alloc_error = slab_init( & policy, allocator = persistent_allocator(), dbg_name = dbg_name ) verify(alloc_error == .None, "Failed to initialize the string cache" ) - cache.table, alloc_error = zpl_hmap_init_reserve( StringCached, persistent_slab_allocator(), 4 * Kilobyte ) - // cache.table, alloc_error = zpl_hmap_init_reserve( StringCached, persistent_slab_allocator(), 8 ) + // cache.table, alloc_error = zpl_hmap_init_reserve( StringCached, persistent_slab_allocator(), 4 * Kilobyte ) + cache.table, alloc_error = zpl_hmap_init_reserve( StringCached, persistent_slab_allocator(), 8, dbg_name ) return } diff --git a/code/grime_virtual_arena.odin b/code/grime_virtual_arena.odin index 784e4ac..9830611 100644 --- a/code/grime_virtual_arena.odin +++ b/code/grime_virtual_arena.odin @@ -25,6 +25,8 @@ VArena_GrowthPolicyProc :: #type proc( commit_used, committed, reserved, request VArena :: struct { using vmem : VirtualMemoryRegion, + dbg_name : string, + tracker : MemoryTracker, commit_used : uint, growth_policy : VArena_GrowthPolicyProc, allow_any_reize : b32, @@ -59,7 +61,7 @@ varena_allocator :: proc( arena : ^VArena ) -> ( allocator : Allocator ) { // Default growth_policy is nil varena_init :: proc( base_address : uintptr, to_reserve, to_commit : uint, - growth_policy : VArena_GrowthPolicyProc, allow_any_reize : b32 = false + growth_policy : VArena_GrowthPolicyProc, allow_any_reize : b32 = false, dbg_name : string ) -> ( arena : VArena, alloc_error : AllocatorError) { page_size := uint(virtual_get_page_size()) @@ -85,6 +87,9 @@ varena_init :: proc( base_address : uintptr, to_reserve, to_commit : uint, arena.growth_policy = growth_policy } arena.allow_any_reize = allow_any_reize + + // Setup the tracker + memtracker_init( & arena.tracker, runtime.heap_allocator(), Kilobyte * 128, dbg_name ) return } @@ -96,7 +101,6 @@ varena_alloc :: proc( using self : ^VArena, ) -> ( data : []byte, alloc_error : AllocatorError ) { verify( alignment & (alignment - 1) == 0, "Non-power of two alignment", location = location ) - context.user_ptr = self page_size := uint(virtual_get_page_size()) requested_size := size @@ -160,6 +164,9 @@ varena_alloc :: proc( using self : ^VArena, mem_zero( data_ptr, int(requested_size) ) } + when ODIN_DEBUG { + memtracker_register_auto_name( & tracker, & data[0], & data[len(data) - 1] ) + } return } @@ -167,6 +174,10 @@ varena_free_all :: proc( using self : ^VArena ) { sync.mutex_guard( & mutex ) commit_used = 0 + + when ODIN_DEBUG { + array_clear(tracker.entries) + } } varena_release :: proc( using self : ^VArena ) @@ -198,7 +209,8 @@ varena_allocator_proc :: proc( switch mode { case .Alloc, .Alloc_Non_Zeroed: - return varena_alloc( arena, size, alignment, (mode != .Alloc_Non_Zeroed), location ) + data, alloc_error = varena_alloc( arena, size, alignment, (mode != .Alloc_Non_Zeroed), location ) + return case .Free: alloc_error = .Mode_Not_Implemented @@ -209,7 +221,8 @@ varena_allocator_proc :: proc( case .Resize, .Resize_Non_Zeroed: if old_memory == nil { ensure(false, "Resizing without old_memory?") - return varena_alloc( arena, size, alignment, (mode != .Resize_Non_Zeroed), location ) + data, alloc_error = varena_alloc( arena, size, alignment, (mode != .Resize_Non_Zeroed), location ) + return } if size == old_size { @@ -249,12 +262,20 @@ varena_allocator_proc :: proc( if new_region == nil || alloc_error != .None { ensure(false, "Failed to grab new region") data = byte_slice( old_memory, old_size ) + + when ODIN_DEBUG { + memtracker_register_auto_name( & arena.tracker, & data[0], & data[len(data) - 1] ) + } return } copy_non_overlapping( raw_data(new_region), old_memory, int(old_size) ) data = new_region // log( str_fmt_tmp("varena resize (new): old: %p %v new: %p %v", old_memory, old_size, (& data[0]), size)) + + when ODIN_DEBUG { + memtracker_register_auto_name( & arena.tracker, & data[0], & data[len(data) - 1] ) + } return } @@ -268,6 +289,10 @@ varena_allocator_proc :: proc( data = byte_slice( old_memory, size ) // log( str_fmt_tmp("varena resize (expanded): old: %p %v new: %p %v", old_memory, old_size, (& data[0]), size)) + + when ODIN_DEBUG { + memtracker_register_auto_name( & arena.tracker, & data[0], & data[len(data) - 1] ) + } return case .Query_Features: diff --git a/code/host/host.odin b/code/host/host.odin index 3ab03fd..06d11d4 100644 --- a/code/host/host.odin +++ b/code/host/host.odin @@ -120,16 +120,16 @@ setup_memory :: proc( profiler : ^SpallProfiler ) -> ClientMemory // Setup the static arena for the entire application { alloc_error : AllocatorError - persistent, alloc_error = varena_init( sectr.Memory_Base_Address_Persistent, sectr.Memory_Reserve_Persistent, sectr.Memory_Commit_Initial_Persistent, nil ) + persistent, alloc_error = varena_init( sectr.Memory_Base_Address_Persistent, sectr.Memory_Reserve_Persistent, sectr.Memory_Commit_Initial_Persistent, nil, dbg_name = "persistent" ) verify( alloc_error == .None, "Failed to allocate persistent virtual arena for the sectr module") - frame, alloc_error = varena_init( sectr.Memory_Base_Address_Frame, sectr.Memory_Reserve_Frame, sectr.Memory_Commit_Initial_Frame, nil, allow_any_reize = true ) + frame, alloc_error = varena_init( sectr.Memory_Base_Address_Frame, sectr.Memory_Reserve_Frame, sectr.Memory_Commit_Initial_Frame, nil, allow_any_reize = true, dbg_name = "frame" ) verify( alloc_error == .None, "Failed to allocate frame virtual arena for the sectr module") - transient, alloc_error = varena_init( sectr.Memory_Base_Address_Transient, sectr.Memory_Reserve_Transient, sectr.Memory_Commit_Initial_Transient, nil, allow_any_reize = true ) + transient, alloc_error = varena_init( sectr.Memory_Base_Address_Transient, sectr.Memory_Reserve_Transient, sectr.Memory_Commit_Initial_Transient, nil, allow_any_reize = true, dbg_name = "transient" ) verify( alloc_error == .None, "Failed to allocate transient virtual arena for the sectr module") - files_buffer, alloc_error = varena_init( sectr.Memory_Base_Address_Files_Buffer, sectr.Memory_Reserve_FilesBuffer, sectr.Memory_Commit_Initial_Filebuffer, nil ) + files_buffer, alloc_error = varena_init( sectr.Memory_Base_Address_Files_Buffer, sectr.Memory_Reserve_FilesBuffer, sectr.Memory_Commit_Initial_Filebuffer, nil, dbg_name = "files_buffer" ) verify( alloc_error == .None, "Failed to allocate files buffer virtual arena for the sectr module") } diff --git a/code/logger.odin b/code/logger.odin index c775bd3..061889b 100644 --- a/code/logger.odin +++ b/code/logger.odin @@ -8,7 +8,7 @@ import str "core:strings" import "core:time" import core_log "core:log" -Max_Logger_Message_Width :: 120 +Max_Logger_Message_Width :: 300 LogLevel :: core_log.Level diff --git a/code/parser_whitespace.odin b/code/parser_whitespace.odin index e031338..2e03583 100644 --- a/code/parser_whitespace.odin +++ b/code/parser_whitespace.odin @@ -165,7 +165,7 @@ pws_parser_lex :: proc ( text : string, allocator : Allocator ) -> ( PWS_LexResu } alloc_error : AllocatorError - tokens, alloc_error = array_init_reserve( PWS_Token, allocator, Kilobyte * 4 ) + tokens, alloc_error = array_init_reserve( PWS_Token, allocator, Kilobyte * 4 ) if alloc_error != AllocatorError.None { ensure(false, "Failed to allocate token's array") return result, alloc_error diff --git a/toolchain/Odin b/toolchain/Odin index fa82547..647d7ed 160000 --- a/toolchain/Odin +++ b/toolchain/Odin @@ -1 +1 @@ -Subproject commit fa825477057fc060debd44027181e2c31c1976a1 +Subproject commit 647d7ed9e3e07b8b248d3b56eaa8fa60b451e1c9