Merge remote-tracking branch 'offical/bill/raddebugger-custom-section'

This commit is contained in:
ed
2025-05-25 09:25:29 -04:00
266 changed files with 20392 additions and 4511 deletions
+43 -10
View File
@@ -2,6 +2,7 @@ package mem
import "base:intrinsics"
import "base:runtime"
import "base:sanitizer"
/*
Nil allocator.
@@ -138,6 +139,7 @@ arena_init :: proc(a: ^Arena, data: []byte) {
a.offset = 0
a.peak_used = 0
a.temp_count = 0
sanitizer.address_poison(a.data)
}
/*
@@ -224,7 +226,9 @@ arena_alloc_bytes_non_zeroed :: proc(
}
a.offset += total_size
a.peak_used = max(a.peak_used, a.offset)
return byte_slice(ptr, size), nil
result := byte_slice(ptr, size)
sanitizer.address_unpoison(result)
return result, nil
}
/*
@@ -232,6 +236,7 @@ Free all memory to an arena.
*/
arena_free_all :: proc(a: ^Arena) {
a.offset = 0
sanitizer.address_poison(a.data)
}
arena_allocator_proc :: proc(
@@ -309,6 +314,7 @@ allocations *inside* the temporary memory region will be freed to the arena.
end_arena_temp_memory :: proc(tmp: Arena_Temp_Memory) {
assert(tmp.arena.offset >= tmp.prev_offset)
assert(tmp.arena.temp_count > 0)
sanitizer.address_poison(tmp.arena.data[tmp.prev_offset:tmp.arena.offset])
tmp.arena.offset = tmp.prev_offset
tmp.arena.temp_count -= 1
}
@@ -363,6 +369,7 @@ scratch_init :: proc(s: ^Scratch, size: int, backup_allocator := context.allocat
s.prev_allocation = nil
s.backup_allocator = backup_allocator
s.leaked_allocations.allocator = backup_allocator
sanitizer.address_poison(s.data)
return nil
}
@@ -377,6 +384,7 @@ scratch_destroy :: proc(s: ^Scratch) {
free_bytes(ptr, s.backup_allocator)
}
delete(s.leaked_allocations)
sanitizer.address_unpoison(s.data)
delete(s.data, s.backup_allocator)
s^ = {}
}
@@ -472,7 +480,9 @@ scratch_alloc_bytes_non_zeroed :: proc(
ptr := align_forward_uintptr(offset+start, uintptr(alignment))
s.prev_allocation = rawptr(ptr)
s.curr_offset = int(offset) + size
return byte_slice(rawptr(ptr), size), nil
result := byte_slice(rawptr(ptr), size)
sanitizer.address_unpoison(result)
return result, nil
} else {
a := s.backup_allocator
if a.procedure == nil {
@@ -516,6 +526,7 @@ scratch_free :: proc(s: ^Scratch, ptr: rawptr, loc := #caller_location) -> Alloc
old_ptr := uintptr(ptr)
if s.prev_allocation == ptr {
s.curr_offset = int(uintptr(s.prev_allocation) - start)
sanitizer.address_poison(s.data[s.curr_offset:])
s.prev_allocation = nil
return nil
}
@@ -546,6 +557,7 @@ scratch_free_all :: proc(s: ^Scratch, loc := #caller_location) {
free_bytes(ptr, s.backup_allocator, loc)
}
clear(&s.leaked_allocations)
sanitizer.address_poison(s.data)
}
/*
@@ -675,7 +687,9 @@ scratch_resize_bytes_non_zeroed :: proc(
old_ptr := uintptr(old_memory)
if begin <= old_ptr && old_ptr < end && old_ptr+uintptr(size) < end {
s.curr_offset = int(old_ptr-begin)+size
return byte_slice(old_memory, size), nil
result := byte_slice(old_memory, size)
sanitizer.address_unpoison(result)
return result, nil
}
data, err := scratch_alloc_bytes_non_zeroed(s, size, alignment, loc)
if err != nil {
@@ -776,6 +790,7 @@ stack_init :: proc(s: ^Stack, data: []byte) {
s.prev_offset = 0
s.curr_offset = 0
s.peak_used = 0
sanitizer.address_poison(data)
}
/*
@@ -861,15 +876,19 @@ stack_alloc_bytes_non_zeroed :: proc(
if s.curr_offset + padding + size > len(s.data) {
return nil, .Out_Of_Memory
}
old_offset := s.prev_offset
s.prev_offset = s.curr_offset
s.curr_offset += padding
next_addr := curr_addr + uintptr(padding)
header := (^Stack_Allocation_Header)(next_addr - size_of(Stack_Allocation_Header))
sanitizer.address_unpoison(header)
header.padding = padding
header.prev_offset = s.prev_offset
header.prev_offset = old_offset
s.curr_offset += size
s.peak_used = max(s.peak_used, s.curr_offset)
return byte_slice(rawptr(next_addr), size), nil
result := byte_slice(rawptr(next_addr), size)
sanitizer.address_unpoison(result)
return result, nil
}
/*
@@ -902,12 +921,15 @@ stack_free :: proc(
}
header := (^Stack_Allocation_Header)(curr_addr - size_of(Stack_Allocation_Header))
old_offset := int(curr_addr - uintptr(header.padding) - uintptr(raw_data(s.data)))
if old_offset != header.prev_offset {
if old_offset != s.prev_offset {
// panic("Out of order stack allocator free");
return .Invalid_Pointer
}
s.curr_offset = old_offset
s.prev_offset = header.prev_offset
sanitizer.address_poison(s.data[old_offset:s.curr_offset])
s.curr_offset = old_offset
return nil
}
@@ -917,6 +939,7 @@ Free all allocations to the stack.
stack_free_all :: proc(s: ^Stack, loc := #caller_location) {
s.prev_offset = 0
s.curr_offset = 0
sanitizer.address_poison(s.data)
}
/*
@@ -1076,7 +1099,9 @@ stack_resize_bytes_non_zeroed :: proc(
if diff > 0 {
zero(rawptr(curr_addr + uintptr(diff)), diff)
}
return byte_slice(old_memory, size), nil
result := byte_slice(old_memory, size)
sanitizer.address_unpoison(result)
return result, nil
}
stack_allocator_proc :: proc(
@@ -1144,6 +1169,7 @@ small_stack_init :: proc(s: ^Small_Stack, data: []byte) {
s.data = data
s.offset = 0
s.peak_used = 0
sanitizer.address_poison(data)
}
/*
@@ -1252,10 +1278,13 @@ small_stack_alloc_bytes_non_zeroed :: proc(
s.offset += padding
next_addr := curr_addr + uintptr(padding)
header := (^Small_Stack_Allocation_Header)(next_addr - size_of(Small_Stack_Allocation_Header))
sanitizer.address_unpoison(header)
header.padding = auto_cast padding
s.offset += size
s.peak_used = max(s.peak_used, s.offset)
return byte_slice(rawptr(next_addr), size), nil
result := byte_slice(rawptr(next_addr), size)
sanitizer.address_unpoison(result)
return result, nil
}
/*
@@ -1289,6 +1318,7 @@ small_stack_free :: proc(
}
header := (^Small_Stack_Allocation_Header)(curr_addr - size_of(Small_Stack_Allocation_Header))
old_offset := int(curr_addr - uintptr(header.padding) - uintptr(raw_data(s.data)))
sanitizer.address_poison(s.data[old_offset:s.offset])
s.offset = old_offset
return nil
}
@@ -1298,6 +1328,7 @@ Free all memory to small stack.
*/
small_stack_free_all :: proc(s: ^Small_Stack) {
s.offset = 0
sanitizer.address_poison(s.data)
}
/*
@@ -1442,7 +1473,9 @@ small_stack_resize_bytes_non_zeroed :: proc(
return nil, nil
}
if old_size == size {
return byte_slice(old_memory, size), nil
result := byte_slice(old_memory, size)
sanitizer.address_unpoison(result)
return result, nil
}
data, err := small_stack_alloc_bytes_non_zeroed(s, size, alignment, loc)
if err == nil {
+31 -19
View File
@@ -1,6 +1,7 @@
package mem
import "base:runtime"
import "base:sanitizer"
/*
Rollback stack default block size.
@@ -47,14 +48,14 @@ Rollback_Stack :: struct {
block_allocator: Allocator,
}
@(private="file", require_results)
@(private="file", require_results, no_sanitize_address)
rb_ptr_in_bounds :: proc(block: ^Rollback_Stack_Block, ptr: rawptr) -> bool {
start := raw_data(block.buffer)
end := start[block.offset:]
return start < ptr && ptr <= end
}
@(private="file", require_results)
@(private="file", require_results, no_sanitize_address)
rb_find_ptr :: proc(stack: ^Rollback_Stack, ptr: rawptr) -> (
parent: ^Rollback_Stack_Block,
block: ^Rollback_Stack_Block,
@@ -71,7 +72,7 @@ rb_find_ptr :: proc(stack: ^Rollback_Stack, ptr: rawptr) -> (
return nil, nil, nil, .Invalid_Pointer
}
@(private="file", require_results)
@(private="file", require_results, no_sanitize_address)
rb_find_last_alloc :: proc(stack: ^Rollback_Stack, ptr: rawptr) -> (
block: ^Rollback_Stack_Block,
header: ^Rollback_Stack_Header,
@@ -86,9 +87,10 @@ rb_find_last_alloc :: proc(stack: ^Rollback_Stack, ptr: rawptr) -> (
return nil, nil, false
}
@(private="file")
@(private="file", no_sanitize_address)
rb_rollback_block :: proc(block: ^Rollback_Stack_Block, header: ^Rollback_Stack_Header) {
header := header
for block.offset > 0 && header.is_free {
block.offset = header.prev_offset
block.last_alloc = raw_data(block.buffer)[header.prev_ptr:]
@@ -99,9 +101,10 @@ rb_rollback_block :: proc(block: ^Rollback_Stack_Block, header: ^Rollback_Stack_
/*
Free memory to a rollback stack allocator.
*/
@(private="file", require_results)
@(private="file", require_results, no_sanitize_address)
rb_free :: proc(stack: ^Rollback_Stack, ptr: rawptr) -> Allocator_Error {
parent, block, header := rb_find_ptr(stack, ptr) or_return
if header.is_free {
return .Invalid_Pointer
}
@@ -120,7 +123,7 @@ rb_free :: proc(stack: ^Rollback_Stack, ptr: rawptr) -> Allocator_Error {
/*
Free all memory owned by the rollback stack allocator.
*/
@(private="file")
@(private="file", no_sanitize_address)
rb_free_all :: proc(stack: ^Rollback_Stack) {
for block := stack.head.next_block; block != nil; /**/ {
next_block := block.next_block
@@ -131,12 +134,13 @@ rb_free_all :: proc(stack: ^Rollback_Stack) {
stack.head.next_block = nil
stack.head.last_alloc = nil
stack.head.offset = 0
sanitizer.address_poison(stack.head.buffer)
}
/*
Allocate memory using the rollback stack allocator.
*/
@(require_results)
@(require_results, no_sanitize_address)
rb_alloc :: proc(
stack: ^Rollback_Stack,
size: int,
@@ -153,7 +157,7 @@ rb_alloc :: proc(
/*
Allocate memory using the rollback stack allocator.
*/
@(require_results)
@(require_results, no_sanitize_address)
rb_alloc_bytes :: proc(
stack: ^Rollback_Stack,
size: int,
@@ -170,7 +174,7 @@ rb_alloc_bytes :: proc(
/*
Allocate non-initialized memory using the rollback stack allocator.
*/
@(require_results)
@(require_results, no_sanitize_address)
rb_alloc_non_zeroed :: proc(
stack: ^Rollback_Stack,
size: int,
@@ -184,7 +188,7 @@ rb_alloc_non_zeroed :: proc(
/*
Allocate non-initialized memory using the rollback stack allocator.
*/
@(require_results)
@(require_results, no_sanitize_address)
rb_alloc_bytes_non_zeroed :: proc(
stack: ^Rollback_Stack,
size: int,
@@ -194,6 +198,7 @@ rb_alloc_bytes_non_zeroed :: proc(
assert(size >= 0, "Size must be positive or zero.", loc)
assert(is_power_of_two(cast(uintptr)alignment), "Alignment must be a power of two.", loc)
parent: ^Rollback_Stack_Block
for block := stack.head; /**/; block = block.next_block {
when !ODIN_DISABLE_ASSERT {
allocated_new_block: bool
@@ -235,7 +240,9 @@ rb_alloc_bytes_non_zeroed :: proc(
// Prevent any further allocations on it.
block.offset = cast(uintptr)len(block.buffer)
}
#no_bounds_check return ptr[:size], nil
res := ptr[:size]
sanitizer.address_unpoison(res)
return res, nil
}
return nil, .Out_Of_Memory
}
@@ -243,7 +250,7 @@ rb_alloc_bytes_non_zeroed :: proc(
/*
Resize an allocation owned by rollback stack allocator.
*/
@(require_results)
@(require_results, no_sanitize_address)
rb_resize :: proc(
stack: ^Rollback_Stack,
old_ptr: rawptr,
@@ -266,7 +273,7 @@ rb_resize :: proc(
/*
Resize an allocation owned by rollback stack allocator.
*/
@(require_results)
@(require_results, no_sanitize_address)
rb_resize_bytes :: proc(
stack: ^Rollback_Stack,
old_memory: []byte,
@@ -289,7 +296,7 @@ rb_resize_bytes :: proc(
Resize an allocation owned by rollback stack allocator without explicit
zero-initialization.
*/
@(require_results)
@(require_results, no_sanitize_address)
rb_resize_non_zeroed :: proc(
stack: ^Rollback_Stack,
old_ptr: rawptr,
@@ -306,7 +313,7 @@ rb_resize_non_zeroed :: proc(
Resize an allocation owned by rollback stack allocator without explicit
zero-initialization.
*/
@(require_results)
@(require_results, no_sanitize_address)
rb_resize_bytes_non_zeroed :: proc(
stack: ^Rollback_Stack,
old_memory: []byte,
@@ -330,7 +337,9 @@ rb_resize_bytes_non_zeroed :: proc(
if len(block.buffer) <= stack.block_size {
block.offset += cast(uintptr)size - cast(uintptr)old_size
}
#no_bounds_check return (ptr)[:size], nil
res := (ptr)[:size]
sanitizer.address_unpoison(res)
#no_bounds_check return res, nil
}
}
}
@@ -340,7 +349,7 @@ rb_resize_bytes_non_zeroed :: proc(
return
}
@(private="file", require_results)
@(private="file", require_results, no_sanitize_address)
rb_make_block :: proc(size: int, allocator: Allocator) -> (block: ^Rollback_Stack_Block, err: Allocator_Error) {
buffer := runtime.mem_alloc(size_of(Rollback_Stack_Block) + size, align_of(Rollback_Stack_Block), allocator) or_return
block = cast(^Rollback_Stack_Block)raw_data(buffer)
@@ -351,6 +360,7 @@ rb_make_block :: proc(size: int, allocator: Allocator) -> (block: ^Rollback_Stac
/*
Initialize the rollback stack allocator using a fixed backing buffer.
*/
@(no_sanitize_address)
rollback_stack_init_buffered :: proc(stack: ^Rollback_Stack, buffer: []byte, location := #caller_location) {
MIN_SIZE :: size_of(Rollback_Stack_Block) + size_of(Rollback_Stack_Header) + size_of(rawptr)
assert(len(buffer) >= MIN_SIZE, "User-provided buffer to Rollback Stack Allocator is too small.", location)
@@ -365,6 +375,7 @@ rollback_stack_init_buffered :: proc(stack: ^Rollback_Stack, buffer: []byte, loc
/*
Initialize the rollback stack alocator using a backing block allocator.
*/
@(no_sanitize_address)
rollback_stack_init_dynamic :: proc(
stack: ^Rollback_Stack,
block_size : int = ROLLBACK_STACK_DEFAULT_BLOCK_SIZE,
@@ -396,6 +407,7 @@ rollback_stack_init :: proc {
/*
Destroy a rollback stack.
*/
@(no_sanitize_address)
rollback_stack_destroy :: proc(stack: ^Rollback_Stack) {
if stack.block_allocator.procedure != nil {
rb_free_all(stack)
@@ -435,7 +447,7 @@ from the last allocation backwards.
Each allocation has an overhead of 8 bytes and any extra bytes to satisfy
the requested alignment.
*/
@(require_results)
@(require_results, no_sanitize_address)
rollback_stack_allocator :: proc(stack: ^Rollback_Stack) -> Allocator {
return Allocator {
data = stack,
@@ -443,7 +455,7 @@ rollback_stack_allocator :: proc(stack: ^Rollback_Stack) -> Allocator {
}
}
@(require_results)
@(require_results, no_sanitize_address)
rollback_stack_allocator_proc :: proc(
allocator_data: rawptr,
mode: Allocator_Mode,
+55 -15
View File
@@ -10,6 +10,7 @@
// package mem_tlsf implements a Two Level Segregated Fit memory allocator.
package mem_tlsf
import "base:intrinsics"
import "base:runtime"
Error :: enum byte {
@@ -21,7 +22,6 @@ Error :: enum byte {
Backing_Allocator_Error = 5,
}
Allocator :: struct {
// Empty lists point at this block to indicate they are free.
block_null: Block_Header,
@@ -39,12 +39,13 @@ Allocator :: struct {
// statistics like how much memory is still available,
// fragmentation, etc.
pool: Pool,
// If we're expected to grow when we run out of memory,
// how much should we ask the backing allocator for?
new_pool_size: uint,
}
#assert(size_of(Allocator) % ALIGN_SIZE == 0)
@(require_results)
allocator :: proc(t: ^Allocator) -> runtime.Allocator {
return runtime.Allocator{
@@ -53,6 +54,21 @@ allocator :: proc(t: ^Allocator) -> runtime.Allocator {
}
}
// Tries to estimate a pool size sufficient for `count` allocations, each of `size` and with `alignment`.
estimate_pool_from_size_alignment :: proc(count: int, size: int, alignment: int) -> (pool_size: int) {
per_allocation := align_up(uint(size + alignment) + BLOCK_HEADER_OVERHEAD, ALIGN_SIZE)
return count * int(per_allocation) + int(INITIAL_POOL_OVERHEAD)
}
// Tries to estimate a pool size sufficient for `count` allocations of `type`.
estimate_pool_from_typeid :: proc(count: int, type: typeid) -> (pool_size: int) {
ti := type_info_of(type)
return estimate_pool_size(count, ti.size, ti.align)
}
estimate_pool_size :: proc{estimate_pool_from_size_alignment, estimate_pool_from_typeid}
@(require_results)
init_from_buffer :: proc(control: ^Allocator, buf: []byte) -> Error {
assert(control != nil)
@@ -60,21 +76,25 @@ init_from_buffer :: proc(control: ^Allocator, buf: []byte) -> Error {
return .Invalid_Alignment
}
pool_bytes := align_down(len(buf) - POOL_OVERHEAD, ALIGN_SIZE)
pool_bytes := align_down(len(buf) - INITIAL_POOL_OVERHEAD, ALIGN_SIZE)
if pool_bytes < BLOCK_SIZE_MIN {
return .Backing_Buffer_Too_Small
} else if pool_bytes > BLOCK_SIZE_MAX {
return .Backing_Buffer_Too_Large
}
clear(control)
return pool_add(control, buf[:])
control.pool = Pool{
data = buf,
allocator = {},
}
return free_all(control)
}
@(require_results)
init_from_allocator :: proc(control: ^Allocator, backing: runtime.Allocator, initial_pool_size: int, new_pool_size := 0) -> Error {
assert(control != nil)
pool_bytes := align_up(uint(initial_pool_size) + POOL_OVERHEAD, ALIGN_SIZE)
pool_bytes := uint(estimate_pool_size(1, initial_pool_size, ALIGN_SIZE))
if pool_bytes < BLOCK_SIZE_MIN {
return .Backing_Buffer_Too_Small
} else if pool_bytes > BLOCK_SIZE_MAX {
@@ -85,12 +105,15 @@ init_from_allocator :: proc(control: ^Allocator, backing: runtime.Allocator, ini
if backing_err != nil {
return .Backing_Allocator_Error
}
err := init_from_buffer(control, buf)
control.pool = Pool{
data = buf,
allocator = backing,
}
return err
control.new_pool_size = uint(new_pool_size)
return free_all(control)
}
init :: proc{init_from_buffer, init_from_allocator}
@@ -103,8 +126,6 @@ destroy :: proc(control: ^Allocator) {
// No need to call `pool_remove` or anything, as they're they're embedded in the backing memory.
// We do however need to free the `Pool` tracking entities and the backing memory itself.
// As `Allocator` is embedded in the first backing slice, the `control` pointer will be
// invalid after this call.
for p := control.pool.next; p != nil; {
next := p.next
@@ -136,9 +157,8 @@ allocator_proc :: proc(allocator_data: rawptr, mode: runtime.Allocator_Mode,
return nil, nil
case .Free_All:
// NOTE: this doesn't work right at the moment, Jeroen has it on his to-do list :)
// clear(control)
return nil, .Mode_Not_Implemented
free_all(control)
return nil, nil
case .Resize:
return resize(control, old_memory, uint(old_size), uint(size), uint(alignment))
@@ -159,3 +179,23 @@ allocator_proc :: proc(allocator_data: rawptr, mode: runtime.Allocator_Mode,
return nil, nil
}
// Exported solely to facilitate testing
@(require_results)
ffs :: proc "contextless" (word: u32) -> (bit: i32) {
return -1 if word == 0 else i32(intrinsics.count_trailing_zeros(word))
}
// Exported solely to facilitate testing
@(require_results)
fls :: proc "contextless" (word: u32) -> (bit: i32) {
N :: (size_of(u32) * 8) - 1
return i32(N - intrinsics.count_leading_zeros(word))
}
// Exported solely to facilitate testing
@(require_results)
fls_uint :: proc "contextless" (size: uint) -> (bit: i32) {
N :: (size_of(uint) * 8) - 1
return i32(N - intrinsics.count_leading_zeros(size))
}
File diff suppressed because it is too large Load Diff
+10 -1
View File
@@ -64,6 +64,7 @@ This procedure initializes the tracking allocator `t` with a backing allocator
specified with `backing_allocator`. The `internals_allocator` will used to
allocate the tracked data.
*/
@(no_sanitize_address)
tracking_allocator_init :: proc(t: ^Tracking_Allocator, backing_allocator: Allocator, internals_allocator := context.allocator) {
t.backing = backing_allocator
t.allocation_map.allocator = internals_allocator
@@ -77,6 +78,7 @@ tracking_allocator_init :: proc(t: ^Tracking_Allocator, backing_allocator: Alloc
/*
Destroy the tracking allocator.
*/
@(no_sanitize_address)
tracking_allocator_destroy :: proc(t: ^Tracking_Allocator) {
delete(t.allocation_map)
delete(t.bad_free_array)
@@ -90,6 +92,7 @@ This procedure clears the tracked data from a tracking allocator.
**Note**: This procedure clears only the current allocation data while keeping
the totals intact.
*/
@(no_sanitize_address)
tracking_allocator_clear :: proc(t: ^Tracking_Allocator) {
sync.mutex_lock(&t.mutex)
clear(&t.allocation_map)
@@ -103,6 +106,7 @@ Reset the tracking allocator.
Reset all of a Tracking Allocator's allocation data back to zero.
*/
@(no_sanitize_address)
tracking_allocator_reset :: proc(t: ^Tracking_Allocator) {
sync.mutex_lock(&t.mutex)
clear(&t.allocation_map)
@@ -124,6 +128,7 @@ Override Tracking_Allocator.bad_free_callback to have something else happen. For
example, you can use tracking_allocator_bad_free_callback_add_to_array to return
the tracking allocator to the old behavior, where the bad_free_array was used.
*/
@(no_sanitize_address)
tracking_allocator_bad_free_callback_panic :: proc(t: ^Tracking_Allocator, memory: rawptr, location: runtime.Source_Code_Location) {
runtime.print_caller_location(location)
runtime.print_string(" Tracking allocator error: Bad free of pointer ")
@@ -136,6 +141,7 @@ tracking_allocator_bad_free_callback_panic :: proc(t: ^Tracking_Allocator, memor
Alternative behavior for a bad free: Store in `bad_free_array`. If you use this,
then you must make sure to check Tracking_Allocator.bad_free_array at some point.
*/
@(no_sanitize_address)
tracking_allocator_bad_free_callback_add_to_array :: proc(t: ^Tracking_Allocator, memory: rawptr, location: runtime.Source_Code_Location) {
append(&t.bad_free_array, Tracking_Allocator_Bad_Free_Entry {
memory = memory,
@@ -175,7 +181,7 @@ Example:
}
}
*/
@(require_results)
@(require_results, no_sanitize_address)
tracking_allocator :: proc(data: ^Tracking_Allocator) -> Allocator {
return Allocator{
data = data,
@@ -183,6 +189,7 @@ tracking_allocator :: proc(data: ^Tracking_Allocator) -> Allocator {
}
}
@(no_sanitize_address)
tracking_allocator_proc :: proc(
allocator_data: rawptr,
mode: Allocator_Mode,
@@ -191,6 +198,7 @@ tracking_allocator_proc :: proc(
old_size: int,
loc := #caller_location,
) -> (result: []byte, err: Allocator_Error) {
@(no_sanitize_address)
track_alloc :: proc(data: ^Tracking_Allocator, entry: ^Tracking_Allocator_Entry) {
data.total_memory_allocated += i64(entry.size)
data.total_allocation_count += 1
@@ -200,6 +208,7 @@ tracking_allocator_proc :: proc(
}
}
@(no_sanitize_address)
track_free :: proc(data: ^Tracking_Allocator, entry: ^Tracking_Allocator_Entry) {
data.total_memory_freed += i64(entry.size)
data.total_free_count += 1
+32 -11
View File
@@ -3,6 +3,8 @@ package mem_virtual
import "core:mem"
import "core:sync"
import "base:sanitizer"
Arena_Kind :: enum uint {
Growing = 0, // Chained memory blocks (singly linked list).
Static = 1, // Fixed reservation sized.
@@ -43,7 +45,7 @@ DEFAULT_ARENA_STATIC_RESERVE_SIZE :: mem.Gigabyte when size_of(uintptr) == 8 els
// Initialization of an `Arena` to be a `.Growing` variant.
// A growing arena is a linked list of `Memory_Block`s allocated with virtual memory.
@(require_results)
@(require_results, no_sanitize_address)
arena_init_growing :: proc(arena: ^Arena, reserved: uint = DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE) -> (err: Allocator_Error) {
arena.kind = .Growing
arena.curr_block = memory_block_alloc(0, reserved, {}) or_return
@@ -53,24 +55,26 @@ arena_init_growing :: proc(arena: ^Arena, reserved: uint = DEFAULT_ARENA_GROWING
if arena.minimum_block_size == 0 {
arena.minimum_block_size = reserved
}
sanitizer.address_poison(arena.curr_block.base[:arena.curr_block.committed])
return
}
// Initialization of an `Arena` to be a `.Static` variant.
// A static arena contains a single `Memory_Block` allocated with virtual memory.
@(require_results)
@(require_results, no_sanitize_address)
arena_init_static :: proc(arena: ^Arena, reserved: uint = DEFAULT_ARENA_STATIC_RESERVE_SIZE, commit_size: uint = DEFAULT_ARENA_STATIC_COMMIT_SIZE) -> (err: Allocator_Error) {
arena.kind = .Static
arena.curr_block = memory_block_alloc(commit_size, reserved, {}) or_return
arena.total_used = 0
arena.total_reserved = arena.curr_block.reserved
sanitizer.address_poison(arena.curr_block.base[:arena.curr_block.committed])
return
}
// Initialization of an `Arena` to be a `.Buffer` variant.
// A buffer arena contains single `Memory_Block` created from a user provided []byte.
@(require_results)
@(require_results, no_sanitize_address)
arena_init_buffer :: proc(arena: ^Arena, buffer: []byte) -> (err: Allocator_Error) {
if len(buffer) < size_of(Memory_Block) {
return .Out_Of_Memory
@@ -78,7 +82,7 @@ arena_init_buffer :: proc(arena: ^Arena, buffer: []byte) -> (err: Allocator_Erro
arena.kind = .Buffer
mem.zero_slice(buffer)
sanitizer.address_poison(buffer[:])
block_base := raw_data(buffer)
block := (^Memory_Block)(block_base)
@@ -94,7 +98,7 @@ arena_init_buffer :: proc(arena: ^Arena, buffer: []byte) -> (err: Allocator_Erro
}
// Allocates memory from the provided arena.
@(require_results)
@(require_results, no_sanitize_address)
arena_alloc :: proc(arena: ^Arena, size: uint, alignment: uint, loc := #caller_location) -> (data: []byte, err: Allocator_Error) {
assert(alignment & (alignment-1) == 0, "non-power of two alignment", loc)
@@ -158,10 +162,13 @@ arena_alloc :: proc(arena: ^Arena, size: uint, alignment: uint, loc := #caller_l
data, err = alloc_from_memory_block(arena.curr_block, size, alignment, default_commit_size=0)
arena.total_used = arena.curr_block.used
}
sanitizer.address_unpoison(data)
return
}
// Resets the memory of a Static or Buffer arena to a specific `position` (offset) and zeroes the previously used memory.
@(no_sanitize_address)
arena_static_reset_to :: proc(arena: ^Arena, pos: uint, loc := #caller_location) -> bool {
sync.mutex_guard(&arena.mutex)
@@ -175,6 +182,7 @@ arena_static_reset_to :: proc(arena: ^Arena, pos: uint, loc := #caller_location)
mem.zero_slice(arena.curr_block.base[arena.curr_block.used:][:prev_pos-pos])
}
arena.total_used = arena.curr_block.used
sanitizer.address_poison(arena.curr_block.base[:arena.curr_block.committed])
return true
} else if pos == 0 {
arena.total_used = 0
@@ -184,6 +192,7 @@ arena_static_reset_to :: proc(arena: ^Arena, pos: uint, loc := #caller_location)
}
// Frees the last memory block of a Growing Arena
@(no_sanitize_address)
arena_growing_free_last_memory_block :: proc(arena: ^Arena, loc := #caller_location) {
if free_block := arena.curr_block; free_block != nil {
assert(arena.kind == .Growing, "expected a .Growing arena", loc)
@@ -191,11 +200,13 @@ arena_growing_free_last_memory_block :: proc(arena: ^Arena, loc := #caller_locat
arena.total_reserved -= free_block.reserved
arena.curr_block = free_block.prev
sanitizer.address_poison(free_block.base[:free_block.committed])
memory_block_dealloc(free_block)
}
}
// Deallocates all but the first memory block of the arena and resets the allocator's usage to 0.
@(no_sanitize_address)
arena_free_all :: proc(arena: ^Arena, loc := #caller_location) {
switch arena.kind {
case .Growing:
@@ -208,7 +219,9 @@ arena_free_all :: proc(arena: ^Arena, loc := #caller_location) {
if arena.curr_block != nil {
curr_block_used := int(arena.curr_block.used)
arena.curr_block.used = 0
sanitizer.address_unpoison(arena.curr_block.base[:curr_block_used])
mem.zero(arena.curr_block.base, curr_block_used)
sanitizer.address_poison(arena.curr_block.base[:arena.curr_block.committed])
}
arena.total_used = 0
case .Static, .Buffer:
@@ -219,6 +232,7 @@ arena_free_all :: proc(arena: ^Arena, loc := #caller_location) {
// Frees all of the memory allocated by the arena and zeros all of the values of an arena.
// A buffer based arena does not `delete` the provided `[]byte` bufffer.
@(no_sanitize_address)
arena_destroy :: proc(arena: ^Arena, loc := #caller_location) {
sync.mutex_guard(&arena.mutex)
switch arena.kind {
@@ -250,7 +264,7 @@ arena_static_bootstrap_new :: proc{
}
// Ability to bootstrap allocate a struct with an arena within the struct itself using the growing variant strategy.
@(require_results)
@(require_results, no_sanitize_address)
arena_growing_bootstrap_new_by_offset :: proc($T: typeid, offset_to_arena: uintptr, minimum_block_size: uint = DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE) -> (ptr: ^T, err: Allocator_Error) {
bootstrap: Arena
bootstrap.kind = .Growing
@@ -266,13 +280,13 @@ arena_growing_bootstrap_new_by_offset :: proc($T: typeid, offset_to_arena: uintp
}
// Ability to bootstrap allocate a struct with an arena within the struct itself using the growing variant strategy.
@(require_results)
@(require_results, no_sanitize_address)
arena_growing_bootstrap_new_by_name :: proc($T: typeid, $field_name: string, minimum_block_size: uint = DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE) -> (ptr: ^T, err: Allocator_Error) {
return arena_growing_bootstrap_new_by_offset(T, offset_of_by_string(T, field_name), minimum_block_size)
}
// Ability to bootstrap allocate a struct with an arena within the struct itself using the static variant strategy.
@(require_results)
@(require_results, no_sanitize_address)
arena_static_bootstrap_new_by_offset :: proc($T: typeid, offset_to_arena: uintptr, reserved: uint) -> (ptr: ^T, err: Allocator_Error) {
bootstrap: Arena
bootstrap.kind = .Static
@@ -288,19 +302,20 @@ arena_static_bootstrap_new_by_offset :: proc($T: typeid, offset_to_arena: uintpt
}
// Ability to bootstrap allocate a struct with an arena within the struct itself using the static variant strategy.
@(require_results)
@(require_results, no_sanitize_address)
arena_static_bootstrap_new_by_name :: proc($T: typeid, $field_name: string, reserved: uint) -> (ptr: ^T, err: Allocator_Error) {
return arena_static_bootstrap_new_by_offset(T, offset_of_by_string(T, field_name), reserved)
}
// Create an `Allocator` from the provided `Arena`
@(require_results)
@(require_results, no_sanitize_address)
arena_allocator :: proc(arena: ^Arena) -> mem.Allocator {
return mem.Allocator{arena_allocator_proc, arena}
}
// The allocator procedure used by an `Allocator` produced by `arena_allocator`
@(no_sanitize_address)
arena_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
size, alignment: int,
old_memory: rawptr, old_size: int,
@@ -334,6 +349,7 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
if size < old_size {
// shrink data in-place
data = old_data[:size]
sanitizer.address_poison(old_data[size:old_size])
return
}
@@ -347,6 +363,7 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
_ = alloc_from_memory_block(block, new_end - old_end, 1, default_commit_size=arena.default_commit_size) or_return
arena.total_used += block.used - prev_used
data = block.base[start:new_end]
sanitizer.address_unpoison(data)
return
}
}
@@ -357,6 +374,7 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
return
}
copy(new_memory, old_data[:old_size])
sanitizer.address_poison(old_data[:old_size])
return new_memory, nil
case .Query_Features:
set := (^mem.Allocator_Mode_Set)(old_memory)
@@ -382,7 +400,7 @@ Arena_Temp :: struct {
}
// Begins the section of temporary arena memory.
@(require_results)
@(require_results, no_sanitize_address)
arena_temp_begin :: proc(arena: ^Arena, loc := #caller_location) -> (temp: Arena_Temp) {
assert(arena != nil, "nil arena", loc)
sync.mutex_guard(&arena.mutex)
@@ -397,6 +415,7 @@ arena_temp_begin :: proc(arena: ^Arena, loc := #caller_location) -> (temp: Arena
}
// Ends the section of temporary arena memory by resetting the memory to the stored position.
@(no_sanitize_address)
arena_temp_end :: proc(temp: Arena_Temp, loc := #caller_location) {
assert(temp.arena != nil, "nil arena", loc)
arena := temp.arena
@@ -432,6 +451,7 @@ arena_temp_end :: proc(temp: Arena_Temp, loc := #caller_location) {
}
// Ignore the use of a `arena_temp_begin` entirely by __not__ resetting to the stored position.
@(no_sanitize_address)
arena_temp_ignore :: proc(temp: Arena_Temp, loc := #caller_location) {
assert(temp.arena != nil, "nil arena", loc)
arena := temp.arena
@@ -442,6 +462,7 @@ arena_temp_ignore :: proc(temp: Arena_Temp, loc := #caller_location) {
}
// Asserts that all uses of `Arena_Temp` has been used by an `Arena`
@(no_sanitize_address)
arena_check_temp :: proc(arena: ^Arena, loc := #caller_location) {
assert(arena.temp_count == 0, "Arena_Temp not been ended", loc)
}
+17 -8
View File
@@ -2,6 +2,7 @@ package mem_virtual
import "core:mem"
import "base:intrinsics"
import "base:sanitizer"
import "base:runtime"
_ :: runtime
@@ -14,27 +15,33 @@ platform_memory_init :: proc() {
Allocator_Error :: mem.Allocator_Error
@(require_results)
@(require_results, no_sanitize_address)
reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
return _reserve(size)
}
@(no_sanitize_address)
commit :: proc "contextless" (data: rawptr, size: uint) -> Allocator_Error {
sanitizer.address_unpoison(data, size)
return _commit(data, size)
}
@(require_results)
@(require_results, no_sanitize_address)
reserve_and_commit :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
data = reserve(size) or_return
commit(raw_data(data), size) or_return
return
}
@(no_sanitize_address)
decommit :: proc "contextless" (data: rawptr, size: uint) {
sanitizer.address_poison(data, size)
_decommit(data, size)
}
@(no_sanitize_address)
release :: proc "contextless" (data: rawptr, size: uint) {
sanitizer.address_unpoison(data, size)
_release(data, size)
}
@@ -46,13 +53,11 @@ Protect_Flag :: enum u32 {
Protect_Flags :: distinct bit_set[Protect_Flag; u32]
Protect_No_Access :: Protect_Flags{}
@(no_sanitize_address)
protect :: proc "contextless" (data: rawptr, size: uint, flags: Protect_Flags) -> bool {
return _protect(data, size, flags)
}
Memory_Block :: struct {
prev: ^Memory_Block,
base: [^]byte,
@@ -66,13 +71,13 @@ Memory_Block_Flag :: enum u32 {
Memory_Block_Flags :: distinct bit_set[Memory_Block_Flag; u32]
@(private="file", require_results)
@(private="file", require_results, no_sanitize_address)
align_formula :: #force_inline proc "contextless" (size, align: uint) -> uint {
result := size + align-1
return result - result%align
}
@(require_results)
@(require_results, no_sanitize_address)
memory_block_alloc :: proc(committed, reserved: uint, alignment: uint = 0, flags: Memory_Block_Flags = {}) -> (block: ^Memory_Block, err: Allocator_Error) {
page_size := DEFAULT_PAGE_SIZE
assert(mem.is_power_of_two(uintptr(page_size)))
@@ -116,8 +121,9 @@ memory_block_alloc :: proc(committed, reserved: uint, alignment: uint = 0, flags
return &pmblock.block, nil
}
@(require_results)
@(require_results, no_sanitize_address)
alloc_from_memory_block :: proc(block: ^Memory_Block, min_size, alignment: uint, default_commit_size: uint = 0) -> (data: []byte, err: Allocator_Error) {
@(no_sanitize_address)
calc_alignment_offset :: proc "contextless" (block: ^Memory_Block, alignment: uintptr) -> uint {
alignment_offset := uint(0)
ptr := uintptr(block.base[block.used:])
@@ -128,6 +134,7 @@ alloc_from_memory_block :: proc(block: ^Memory_Block, min_size, alignment: uint,
return alignment_offset
}
@(no_sanitize_address)
do_commit_if_necessary :: proc(block: ^Memory_Block, size: uint, default_commit_size: uint) -> (err: Allocator_Error) {
if block.committed - block.used < size {
pmblock := (^Platform_Memory_Block)(block)
@@ -172,10 +179,12 @@ alloc_from_memory_block :: proc(block: ^Memory_Block, min_size, alignment: uint,
data = block.base[block.used+alignment_offset:][:min_size]
block.used += size
sanitizer.address_unpoison(data)
return
}
@(no_sanitize_address)
memory_block_dealloc :: proc(block_to_free: ^Memory_Block) {
if block := (^Platform_Memory_Block)(block_to_free); block != nil {
platform_memory_free(block)
+3
View File
@@ -7,6 +7,7 @@ Platform_Memory_Block :: struct {
reserved: uint,
}
@(no_sanitize_address)
platform_memory_alloc :: proc "contextless" (to_commit, to_reserve: uint) -> (block: ^Platform_Memory_Block, err: Allocator_Error) {
to_commit, to_reserve := to_commit, to_reserve
to_reserve = max(to_commit, to_reserve)
@@ -26,12 +27,14 @@ platform_memory_alloc :: proc "contextless" (to_commit, to_reserve: uint) -> (bl
}
@(no_sanitize_address)
platform_memory_free :: proc "contextless" (block: ^Platform_Memory_Block) {
if block != nil {
release(block, block.reserved)
}
}
@(no_sanitize_address)
platform_memory_commit :: proc "contextless" (block: ^Platform_Memory_Block, to_commit: uint) -> (err: Allocator_Error) {
if to_commit < block.committed {
return nil
+11 -1
View File
@@ -83,6 +83,8 @@ foreign Kernel32 {
dwNumberOfBytesToMap: uint,
) -> rawptr ---
}
@(no_sanitize_address)
_reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
result := VirtualAlloc(nil, size, MEM_RESERVE, PAGE_READWRITE)
if result == nil {
@@ -93,6 +95,7 @@ _reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Err
return
}
@(no_sanitize_address)
_commit :: proc "contextless" (data: rawptr, size: uint) -> Allocator_Error {
result := VirtualAlloc(data, size, MEM_COMMIT, PAGE_READWRITE)
if result == nil {
@@ -107,12 +110,18 @@ _commit :: proc "contextless" (data: rawptr, size: uint) -> Allocator_Error {
}
return nil
}
@(no_sanitize_address)
_decommit :: proc "contextless" (data: rawptr, size: uint) {
VirtualFree(data, size, MEM_DECOMMIT)
}
@(no_sanitize_address)
_release :: proc "contextless" (data: rawptr, size: uint) {
VirtualFree(data, 0, MEM_RELEASE)
}
@(no_sanitize_address)
_protect :: proc "contextless" (data: rawptr, size: uint, flags: Protect_Flags) -> bool {
pflags: u32
pflags = PAGE_NOACCESS
@@ -136,7 +145,7 @@ _protect :: proc "contextless" (data: rawptr, size: uint, flags: Protect_Flags)
}
@(no_sanitize_address)
_platform_memory_init :: proc() {
sys_info: SYSTEM_INFO
GetSystemInfo(&sys_info)
@@ -147,6 +156,7 @@ _platform_memory_init :: proc() {
}
@(no_sanitize_address)
_map_file :: proc "contextless" (fd: uintptr, size: i64, flags: Map_File_Flags) -> (data: []byte, error: Map_File_Error) {
page_flags: u32
if flags == {.Read} {