|
@@ -34,12 +34,18 @@ Tracking_Allocator_Bad_Free_Entry :: struct {
|
|
|
location: runtime.Source_Code_Location,
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+Callback type for when tracking allocator runs into a bad free.
|
|
|
+*/
|
|
|
+Tracking_Allocator_Bad_Free_Callback :: proc(t: ^Tracking_Allocator, memory: rawptr, location: runtime.Source_Code_Location)
|
|
|
+
|
|
|
/*
|
|
|
Tracking allocator data.
|
|
|
*/
|
|
|
Tracking_Allocator :: struct {
|
|
|
backing: 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,
|
|
@@ -61,6 +67,7 @@ allocate the tracked data.
|
|
|
tracking_allocator_init :: proc(t: ^Tracking_Allocator, backing_allocator: Allocator, internals_allocator := context.allocator) {
|
|
|
t.backing = backing_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 query_features(t.backing) {
|
|
|
t.clear_on_free_all = true
|
|
@@ -109,6 +116,33 @@ tracking_allocator_reset :: proc(t: ^Tracking_Allocator) {
|
|
|
sync.mutex_unlock(&t.mutex)
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+Default behavior for a bad free: Crash with error message that says where the
|
|
|
+bad free happened.
|
|
|
+
|
|
|
+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.
|
|
|
+*/
|
|
|
+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 ")
|
|
|
+ runtime.print_uintptr(uintptr(memory))
|
|
|
+ runtime.print_string("\n")
|
|
|
+ runtime.trap()
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+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.
|
|
|
+*/
|
|
|
+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,
|
|
|
+ location = location,
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
Tracking allocator.
|
|
|
|
|
@@ -116,8 +150,10 @@ The tracking allocator is an allocator wrapper that tracks memory allocations.
|
|
|
This allocator stores all the allocations in a map. Whenever a pointer that's
|
|
|
not inside of the map is freed, the `bad_free_array` entry is added.
|
|
|
|
|
|
-An example of how to use the `Tracking_Allocator` to track subsequent allocations
|
|
|
-in your program and report leaks and bad frees:
|
|
|
+Here follows an example of how to use the `Tracking_Allocator` to track
|
|
|
+subsequent allocations in your program and report leaks. By default, the
|
|
|
+tracking allocator will crash on bad frees. You can override that behavior by
|
|
|
+overriding `track.bad_free_callback`.
|
|
|
|
|
|
Example:
|
|
|
|
|
@@ -137,9 +173,6 @@ Example:
|
|
|
for _, leak in track.allocation_map {
|
|
|
fmt.printf("%v leaked %m\n", leak.location, leak.size)
|
|
|
}
|
|
|
- for bad_free in track.bad_free_array {
|
|
|
- fmt.printf("%v allocation %p was freed badly\n", bad_free.location, bad_free.memory)
|
|
|
- }
|
|
|
}
|
|
|
*/
|
|
|
@(require_results)
|
|
@@ -191,10 +224,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,
|
|
|
- })
|
|
|
+ if data.bad_free_callback != nil {
|
|
|
+ data.bad_free_callback(data, old_memory, loc)
|
|
|
+ }
|
|
|
} else {
|
|
|
result = data.backing.procedure(data.backing.data, mode, size, alignment, old_memory, old_size, loc) or_return
|
|
|
}
|