-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtracker.odin
More file actions
165 lines (137 loc) · 6.22 KB
/
tracker.odin
File metadata and controls
165 lines (137 loc) · 6.22 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
package tracker
import "core:mem"
import "base:runtime"
import "core:strings"
// afmt is a required import which is availible on my git page
import "shared:afmt"
// At the top of your project import this package
// import "shared:tracker"
/* Copy-Paste this to top of main in your project
when ODIN_DEBUG {
//tracker.NOPANIC = true // uncomment or override with: -define:nopanic=true
this_tracker := tracker.init_tracker()
context.allocator = tracker.tracking_allocator(&this_tracker)
defer tracker.print_and_destroy_tracker(&this_tracker)
}
*/
// Default is to panic when a bad free is detected.
// Override with: -define:nopanic=true
NOPANIC := #config(nopanic, false)
// Default is to use ansi colors and formatting
// Override with: -define:noansi=true
NOANSI := #config(noansi, false)
// Alias so that only this tracker package needs to be imported and not also core:mem
tracking_allocator :: mem.tracking_allocator
init_tracker :: proc() -> (t: mem.Tracking_Allocator) {
mem.tracking_allocator_init(&t, context.allocator)
if NOPANIC {
t.bad_free_callback = mem.tracking_allocator_bad_free_callback_add_to_array
}
return
}
// Trim long paths to something more readable if possible without allocating any dynamic memory
trim_path :: proc(file_path: string) -> (path: string) {
if index := strings.last_index(file_path, ODIN_BUILD_PROJECT_NAME); index >= 0 {
path = file_path[index:]
} else if strings.contains(file_path, ODIN_ROOT) {
path = file_path[len(ODIN_ROOT):]
} else {
path = file_path
}
return
}
convert_bytes :: proc(size: $T) -> (f64, string) where T == uint || T == i64 {
units := []string{"Bytes", "KBs", "MBs", "GBs", "TBs"}
index := 0
fsize := f64(size)
for index = 0; fsize >= 1024 && index < len(units) - 1; index += 1 {
fsize /= 1024.000
}
if units[index] == "Bytes" {
return fsize / 1024.000, "KBs"
}
return fsize, units[index]
}
// Print allocations not freed and bad frees, then destroy tracker
print_and_destroy_tracker :: proc(t: ^mem.Tracking_Allocator) {
header := [2]afmt.Column(afmt.ANSI24) {
{16, .LEFT, NOANSI ? {} : {fg = afmt.black, bg = [3]u8{074, 165, 240}, at = {.bold}}},
{64, .LEFT, NOANSI ? {} : {fg = afmt.black, bg = [3]u8{077, 196, 255}, at = {.bold}}},
}
metrics := [4]afmt.Column(afmt.ANSI24) {
{16, .LEFT, NOANSI ? {} : {fg = [3]u8{074, 165, 240}, bg = afmt.black, at = {.bold}}},
{31, .LEFT, NOANSI ? {} : {fg = [3]u8{077, 196, 255}, bg = afmt.black, at = {.bold}}},
{1, .LEFT, NOANSI ? {} : {fg = [3]u8{077, 196, 255}, bg = afmt.black, at = {.bold}}},
{32, .RIGHT, NOANSI ? {} : {fg = [3]u8{077, 196, 255}, bg = afmt.black, at = {.bold}}},
}
is_ok_title := [2]afmt.Column(afmt.ANSI24) {
{16, .LEFT, NOANSI ? {} : {fg = afmt.black, bg = [3]u8{140, 194, 101}, at = {.bold}}},
{64, .LEFT, NOANSI ? {} : {fg = afmt.black, bg = [3]u8{165, 224, 117}, at = {.bold}}},
}
not_ok_title := [2]afmt.Column(afmt.ANSI24) {
{16, .LEFT, NOANSI ? {} : {fg = afmt.black, bg = [3]u8{224, 085, 097}, at = {.bold}}},
{64, .LEFT, NOANSI ? {} : {fg = afmt.black, bg = [3]u8{255, 097, 110}, at = {.bold}}},
}
record_even := [2]afmt.Column(afmt.ANSI24) {
{16, .LEFT, NOANSI ? {} : {fg = afmt.black, bg = [3]u8{224, 216, 138}, at = {.bold}}},
{64, .LEFT, NOANSI ? {} : {fg = [3]u8{238, 233, 172}, bg = afmt.black}},
}
record_odd := [2]afmt.Column(afmt.ANSI24) {
{16, .LEFT, NOANSI ? {} : {fg = afmt.black, bg = [3]u8{238, 233, 172}, at = {.bold}}},
{64, .LEFT, NOANSI ? {} : {fg = [3]u8{238, 233, 172}, bg = afmt.black + 25}},
}
title: [2]afmt.Column(afmt.ANSI24)
record: [2]afmt.Column(afmt.ANSI24)
// context.temp_allocator metrics
used := (^runtime.Default_Temp_Allocator)(context.temp_allocator.data).arena.total_used
cap := (^runtime.Default_Temp_Allocator)(context.temp_allocator.data).arena.total_capacity
temp_lt := afmt.tprintf(" %v Bytes (%.2f %v)", used, convert_bytes(used))
temp_rt := afmt.tprintf("%v Bytes (%.2f %v) ", cap, convert_bytes(cap))
afmt.printrow(header, " Allocator", " context.temp_allocator")
afmt.printrow(metrics, " Used/Capacity", temp_lt, "/", temp_rt)
// context.allocator metrics
afmt.printrow(header, " Allocator", " context.allocator")
peak := t.peak_memory_allocated
total := t.total_memory_allocated
ctx_lt := afmt.tprintf(" %v Bytes (%.2f %v)", peak, convert_bytes(peak))
ctx_rt := afmt.tprintf("%v Bytes (%.2f %v) ", total, convert_bytes(total))
afmt.printrow(metrics, " Peak/Allocated", ctx_lt, "/", ctx_rt)
// Print Allocations not freed
title = len(t.allocation_map) == 0 ? is_ok_title : not_ok_title
leaked := title == is_ok_title ? " 0 Bytes Leaked" : " Leaked Bytes"
not_freed := len(t.allocation_map)
allocated := t.total_allocation_count
afmt.printrow(title, leaked, afmt.tprintf(" %d/%d Allocations Not Freed", not_freed, allocated))
if len(t.allocation_map) > 0 {
for _, entry in t.allocation_map {
loc := entry.location
label := afmt.tprintf(" %d", entry.size)
field := afmt.tprintf(" %s:%i:%i", trim_path(loc.file_path), loc.line, loc.column)
record = record == record_even ? record_odd : record_even
length := len(field) + len(loc.procedure) + 1
if length < 256 && length > 64 { record[1].width = u8(length) }
afmt.printrow(record, label, afmt.tprintf("%s%*s", field, int(record[1].width) - len(field), loc.procedure))
}
}
// Print Incorrect frees
if NOPANIC {
title = len(t.bad_free_array) == 0 ? is_ok_title : not_ok_title
memory := title == is_ok_title ? " 0 Bad Frees" : " Memory Address"
bad_frees := len(t.bad_free_array)
total_frees := i64(len(t.bad_free_array)) + t.total_free_count
afmt.printrow(title, memory, afmt.tprintf(" %d/%d Bad Frees", bad_frees, total_frees))
if len(t.bad_free_array) > 0 {
for entry in t.bad_free_array {
loc := entry.location
label := afmt.tprintf(" %p", entry.memory)
field := afmt.tprintf(" %s:%i:%i", trim_path(loc.file_path), loc.line, loc.column)
record = record == record_even ? record_odd : record_even
length := len(field) + len(loc.procedure) + 1
if length < 256 && length > 64 { record[1].width = u8(length) }
afmt.printrow(record, label, afmt.tprintf("%s%*s", field, int(record[1].width) - len(field), loc.procedure))
}
}
}
// Done and destroy tracker
mem.tracking_allocator_destroy(t)
}