mirror of
https://github.com/odin-lang/Odin.git
synced 2026-07-31 19:28:55 +00:00
Merge pull request #7147 from A1029384756/trace-tracking-allocator-improvements
[debug/trace] sync tracking allocator with core:mem
This commit is contained in:
@@ -7,8 +7,6 @@ import "core:fmt"
|
||||
import "core:mem"
|
||||
import "core:sync"
|
||||
|
||||
// TODO: should this just be added to `core:mem.Tracking_Allocator`?
|
||||
|
||||
/*
|
||||
The backtrace tracking allocator is a similar allocator as the `core:mem` tracking allocator but keeps
|
||||
backtraces for each allocation.
|
||||
@@ -39,12 +37,18 @@ Example:
|
||||
}
|
||||
*/
|
||||
Tracking_Allocator :: struct {
|
||||
backing: mem.Allocator,
|
||||
internals_allocator: mem.Allocator,
|
||||
allocation_map: map[rawptr]Tracking_Allocator_Entry,
|
||||
bad_free_array: [dynamic]Tracking_Allocator_Bad_Free_Entry,
|
||||
mutex: sync.Mutex,
|
||||
clear_on_free_all: bool,
|
||||
backing: mem.Allocator,
|
||||
allocation_map: map[rawptr]Tracking_Allocator_Entry,
|
||||
bad_free_callback: Tracking_Allocator_Bad_Free_Callback,
|
||||
bad_free_array: [dynamic]Tracking_Allocator_Bad_Free_Entry,
|
||||
mutex: sync.Mutex,
|
||||
clear_on_free_all: bool,
|
||||
total_memory_allocated: i64,
|
||||
total_allocation_count: i64,
|
||||
total_memory_freed: i64,
|
||||
total_free_count: i64,
|
||||
peak_memory_allocated: i64,
|
||||
current_memory_allocated: i64,
|
||||
}
|
||||
|
||||
Tracking_Allocator_Entry :: struct {
|
||||
@@ -63,14 +67,25 @@ Tracking_Allocator_Bad_Free_Entry :: struct {
|
||||
backtrace: Capture_Const,
|
||||
}
|
||||
|
||||
/*
|
||||
Callback type for when tracking allocator runs into a bad free.
|
||||
*/
|
||||
Tracking_Allocator_Bad_Free_Callback :: #type proc(
|
||||
t: ^Tracking_Allocator,
|
||||
memory: rawptr,
|
||||
backtrace: Capture_Const,
|
||||
location: runtime.Source_Code_Location,
|
||||
)
|
||||
|
||||
@(no_sanitize_address)
|
||||
tracking_allocator_init :: proc(
|
||||
t: ^Tracking_Allocator,
|
||||
backing_allocator: mem.Allocator,
|
||||
internals_allocator := context.allocator,
|
||||
) {
|
||||
t.backing = backing_allocator
|
||||
t.internals_allocator = internals_allocator
|
||||
t.allocation_map.allocator = internals_allocator
|
||||
t.bad_free_callback = tracking_allocator_bad_free_callback_panic
|
||||
t.bad_free_array.allocator = internals_allocator
|
||||
|
||||
if .Free_All in mem.query_features(t.backing) {
|
||||
@@ -78,23 +93,68 @@ tracking_allocator_init :: proc(
|
||||
}
|
||||
}
|
||||
|
||||
@(no_sanitize_address)
|
||||
tracking_allocator_destroy :: proc(t: ^Tracking_Allocator) {
|
||||
delete(t.allocation_map)
|
||||
delete(t.bad_free_array)
|
||||
}
|
||||
|
||||
@(no_sanitize_address)
|
||||
tracking_allocator_clear :: proc(t: ^Tracking_Allocator) {
|
||||
sync.guard(&t.mutex)
|
||||
|
||||
clear(&t.allocation_map)
|
||||
clear(&t.bad_free_array)
|
||||
t.current_memory_allocated = 0
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
@(no_sanitize_address)
|
||||
tracking_allocator_reset :: proc(t: ^Tracking_Allocator) {
|
||||
sync.guard(&t.mutex)
|
||||
clear(&t.allocation_map)
|
||||
clear(&t.bad_free_array)
|
||||
t.total_memory_allocated = 0
|
||||
t.total_allocation_count = 0
|
||||
t.total_memory_freed = 0
|
||||
t.total_free_count = 0
|
||||
t.peak_memory_allocated = 0
|
||||
t.current_memory_allocated = 0
|
||||
}
|
||||
|
||||
@(no_sanitize_address)
|
||||
tracking_allocator_bad_free_callback_panic :: proc(
|
||||
t: ^Tracking_Allocator,
|
||||
memory: rawptr,
|
||||
backtrace: Capture_Const,
|
||||
location: runtime.Source_Code_Location,
|
||||
) {
|
||||
buf: [256]byte
|
||||
offset := 0
|
||||
_ = runtime.write_string(&offset, buf[:], "Tracking allocator error: Bad free of pointer ")
|
||||
_ = runtime.write_u64(&offset, buf[:], u64(uintptr(memory)))
|
||||
panic(string(buf[:offset]), loc = location)
|
||||
}
|
||||
|
||||
@(no_sanitize_address)
|
||||
tracking_allocator_bad_free_callback_add_to_array :: proc(
|
||||
t: ^Tracking_Allocator,
|
||||
memory: rawptr,
|
||||
backtrace: Capture_Const,
|
||||
location: runtime.Source_Code_Location,
|
||||
) {
|
||||
append(&t.bad_free_array, Tracking_Allocator_Bad_Free_Entry {
|
||||
memory = memory,
|
||||
location = location,
|
||||
backtrace = backtrace,
|
||||
})
|
||||
}
|
||||
|
||||
@(require_results, no_sanitize_address)
|
||||
tracking_allocator :: proc(data: ^Tracking_Allocator) -> mem.Allocator {
|
||||
return mem.Allocator{data = data, procedure = tracking_allocator_proc}
|
||||
}
|
||||
|
||||
@(no_sanitize_address)
|
||||
tracking_allocator_proc :: proc(
|
||||
allocator_data: rawptr,
|
||||
mode: mem.Allocator_Mode,
|
||||
@@ -106,6 +166,23 @@ tracking_allocator_proc :: proc(
|
||||
result: []byte,
|
||||
err: mem.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
|
||||
data.current_memory_allocated += i64(entry.size)
|
||||
if data.current_memory_allocated > data.peak_memory_allocated {
|
||||
data.peak_memory_allocated = data.current_memory_allocated
|
||||
}
|
||||
}
|
||||
|
||||
@(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
|
||||
data.current_memory_allocated -= i64(entry.size)
|
||||
}
|
||||
|
||||
data := (^Tracking_Allocator)(allocator_data)
|
||||
|
||||
sync.mutex_guard(&data.mutex)
|
||||
@@ -124,14 +201,9 @@ tracking_allocator_proc :: proc(
|
||||
}
|
||||
|
||||
if mode == .Free && old_memory != nil && old_memory not_in data.allocation_map {
|
||||
append(
|
||||
&data.bad_free_array,
|
||||
Tracking_Allocator_Bad_Free_Entry{
|
||||
memory = old_memory,
|
||||
location = loc,
|
||||
backtrace = capture(skip=1),
|
||||
},
|
||||
)
|
||||
if data.bad_free_callback != nil {
|
||||
data.bad_free_callback(data, old_memory, capture(skip=1), loc)
|
||||
}
|
||||
} else {
|
||||
result = data.backing.procedure(
|
||||
data.backing.data,
|
||||
@@ -160,13 +232,21 @@ tracking_allocator_proc :: proc(
|
||||
location = loc,
|
||||
backtrace = capture(skip=1),
|
||||
}
|
||||
track_alloc(data, &data.allocation_map[result_ptr])
|
||||
case .Free:
|
||||
if old_memory != nil && old_memory in data.allocation_map {
|
||||
track_free(data, &data.allocation_map[old_memory])
|
||||
}
|
||||
delete_key(&data.allocation_map, old_memory)
|
||||
case .Free_All:
|
||||
if data.clear_on_free_all {
|
||||
clear_map(&data.allocation_map)
|
||||
data.current_memory_allocated = 0
|
||||
}
|
||||
case .Resize, .Resize_Non_Zeroed:
|
||||
if old_memory != nil && old_memory in data.allocation_map {
|
||||
track_free(data, &data.allocation_map[old_memory])
|
||||
}
|
||||
if old_memory != result_ptr {
|
||||
delete_key(&data.allocation_map, old_memory)
|
||||
}
|
||||
@@ -179,6 +259,7 @@ tracking_allocator_proc :: proc(
|
||||
location = loc,
|
||||
backtrace = capture(skip=1),
|
||||
}
|
||||
track_alloc(data, &data.allocation_map[result_ptr])
|
||||
|
||||
case .Query_Features:
|
||||
set := (^mem.Allocator_Mode_Set)(old_memory)
|
||||
|
||||
Reference in New Issue
Block a user