tracking_allocator.odin 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. //+build !freestanding
  2. package mem
  3. import "base:runtime"
  4. import "core:sync"
  5. Tracking_Allocator_Entry :: struct {
  6. memory: rawptr,
  7. size: int,
  8. alignment: int,
  9. mode: Allocator_Mode,
  10. err: Allocator_Error,
  11. location: runtime.Source_Code_Location,
  12. }
  13. Tracking_Allocator_Bad_Free_Entry :: struct {
  14. memory: rawptr,
  15. location: runtime.Source_Code_Location,
  16. }
  17. Tracking_Allocator :: struct {
  18. backing: Allocator,
  19. allocation_map: map[rawptr]Tracking_Allocator_Entry,
  20. bad_free_array: [dynamic]Tracking_Allocator_Bad_Free_Entry,
  21. mutex: sync.Mutex,
  22. clear_on_free_all: bool,
  23. total_memory_allocated: i64,
  24. total_allocation_count: i64,
  25. total_memory_freed: i64,
  26. total_free_count: i64,
  27. peak_memory_allocated: i64,
  28. current_memory_allocated: i64,
  29. }
  30. tracking_allocator_init :: proc(t: ^Tracking_Allocator, backing_allocator: Allocator, internals_allocator := context.allocator) {
  31. t.backing = backing_allocator
  32. t.allocation_map.allocator = internals_allocator
  33. t.bad_free_array.allocator = internals_allocator
  34. if .Free_All in query_features(t.backing) {
  35. t.clear_on_free_all = true
  36. }
  37. }
  38. tracking_allocator_destroy :: proc(t: ^Tracking_Allocator) {
  39. delete(t.allocation_map)
  40. delete(t.bad_free_array)
  41. }
  42. // Clear only the current allocation data while keeping the totals intact.
  43. tracking_allocator_clear :: proc(t: ^Tracking_Allocator) {
  44. sync.mutex_lock(&t.mutex)
  45. clear(&t.allocation_map)
  46. clear(&t.bad_free_array)
  47. t.current_memory_allocated = 0
  48. sync.mutex_unlock(&t.mutex)
  49. }
  50. // Reset all of a Tracking Allocator's allocation data back to zero.
  51. tracking_allocator_reset :: proc(t: ^Tracking_Allocator) {
  52. sync.mutex_lock(&t.mutex)
  53. clear(&t.allocation_map)
  54. clear(&t.bad_free_array)
  55. t.total_memory_allocated = 0
  56. t.total_allocation_count = 0
  57. t.total_memory_freed = 0
  58. t.total_free_count = 0
  59. t.peak_memory_allocated = 0
  60. t.current_memory_allocated = 0
  61. sync.mutex_unlock(&t.mutex)
  62. }
  63. @(require_results)
  64. tracking_allocator :: proc(data: ^Tracking_Allocator) -> Allocator {
  65. return Allocator{
  66. data = data,
  67. procedure = tracking_allocator_proc,
  68. }
  69. }
  70. tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
  71. size, alignment: int,
  72. old_memory: rawptr, old_size: int, loc := #caller_location) -> (result: []byte, err: Allocator_Error) {
  73. track_alloc :: proc(data: ^Tracking_Allocator, entry: ^Tracking_Allocator_Entry) {
  74. data.total_memory_allocated += i64(entry.size)
  75. data.total_allocation_count += 1
  76. data.current_memory_allocated += i64(entry.size)
  77. if data.current_memory_allocated > data.peak_memory_allocated {
  78. data.peak_memory_allocated = data.current_memory_allocated
  79. }
  80. }
  81. track_free :: proc(data: ^Tracking_Allocator, entry: ^Tracking_Allocator_Entry) {
  82. data.total_memory_freed += i64(entry.size)
  83. data.total_free_count += 1
  84. data.current_memory_allocated -= i64(entry.size)
  85. }
  86. data := (^Tracking_Allocator)(allocator_data)
  87. sync.mutex_guard(&data.mutex)
  88. if mode == .Query_Info {
  89. info := (^Allocator_Query_Info)(old_memory)
  90. if info != nil && info.pointer != nil {
  91. if entry, ok := data.allocation_map[info.pointer]; ok {
  92. info.size = entry.size
  93. info.alignment = entry.alignment
  94. }
  95. info.pointer = nil
  96. }
  97. return
  98. }
  99. if mode == .Free && old_memory != nil && old_memory not_in data.allocation_map {
  100. append(&data.bad_free_array, Tracking_Allocator_Bad_Free_Entry{
  101. memory = old_memory,
  102. location = loc,
  103. })
  104. } else {
  105. result = data.backing.procedure(data.backing.data, mode, size, alignment, old_memory, old_size, loc) or_return
  106. }
  107. result_ptr := raw_data(result)
  108. if data.allocation_map.allocator.procedure == nil {
  109. data.allocation_map.allocator = context.allocator
  110. }
  111. switch mode {
  112. case .Alloc, .Alloc_Non_Zeroed:
  113. data.allocation_map[result_ptr] = Tracking_Allocator_Entry{
  114. memory = result_ptr,
  115. size = size,
  116. mode = mode,
  117. alignment = alignment,
  118. err = err,
  119. location = loc,
  120. }
  121. track_alloc(data, &data.allocation_map[result_ptr])
  122. case .Free:
  123. if old_memory != nil && old_memory in data.allocation_map {
  124. track_free(data, &data.allocation_map[old_memory])
  125. }
  126. delete_key(&data.allocation_map, old_memory)
  127. case .Free_All:
  128. if data.clear_on_free_all {
  129. clear_map(&data.allocation_map)
  130. data.current_memory_allocated = 0
  131. }
  132. case .Resize, .Resize_Non_Zeroed:
  133. if old_memory != nil && old_memory in data.allocation_map {
  134. track_free(data, &data.allocation_map[old_memory])
  135. }
  136. if old_memory != result_ptr {
  137. delete_key(&data.allocation_map, old_memory)
  138. }
  139. data.allocation_map[result_ptr] = Tracking_Allocator_Entry{
  140. memory = result_ptr,
  141. size = size,
  142. mode = mode,
  143. alignment = alignment,
  144. err = err,
  145. location = loc,
  146. }
  147. track_alloc(data, &data.allocation_map[result_ptr])
  148. case .Query_Features:
  149. set := (^Allocator_Mode_Set)(old_memory)
  150. if set != nil {
  151. set^ = {.Alloc, .Alloc_Non_Zeroed, .Free, .Free_All, .Resize, .Query_Features, .Query_Info}
  152. }
  153. return nil, nil
  154. case .Query_Info:
  155. unreachable()
  156. }
  157. return
  158. }