Line data Source code
1 : #include "memory_manager.h"
2 :
3 : #include <algorithm>
4 : #include <iomanip>
5 : #include <iostream>
6 :
7 : namespace hashbrowns {
8 :
9 777 : MemoryTracker& MemoryTracker::instance() {
10 777 : static MemoryTracker instance;
11 777 : return instance;
12 : }
13 :
14 309 : void MemoryTracker::record_allocation(void* ptr, size_t size, const char* file, int line) {
15 309 : if (!ptr)
16 0 : return;
17 :
18 309 : std::lock_guard<std::mutex> lock(mutex_);
19 :
20 : // Update statistics
21 309 : stats_.total_allocated += size;
22 309 : stats_.current_usage += size;
23 309 : stats_.allocation_count += 1;
24 :
25 : // Update peak usage
26 309 : if (stats_.current_usage > stats_.peak_usage) {
27 219 : stats_.peak_usage = stats_.current_usage;
28 : }
29 :
30 : // Always record ptr->size mapping so deallocation can reverse the stats.
31 : // Verbose logging is gated on detailed_tracking_.
32 309 : allocations_[ptr] = size;
33 :
34 309 : if (detailed_tracking_ && file && line > 0) {
35 0 : std::cout << "[ALLOC] " << size << " bytes at " << ptr << " (" << file << ":" << line << ")\n";
36 : }
37 309 : }
38 :
39 309 : void MemoryTracker::record_deallocation(void* ptr) {
40 309 : if (!ptr)
41 0 : return;
42 :
43 309 : std::lock_guard<std::mutex> lock(mutex_);
44 :
45 309 : size_t size = 0;
46 :
47 : // Always look up the size so stats stay correct regardless of tracking mode.
48 309 : auto it = allocations_.find(ptr);
49 309 : if (it != allocations_.end()) {
50 284 : size = it->second;
51 284 : allocations_.erase(it);
52 284 : if (detailed_tracking_) {
53 82 : std::cout << "[DEALLOC] " << size << " bytes at " << ptr << "\n";
54 : }
55 : } else {
56 25 : if (detailed_tracking_) {
57 0 : std::cout << "[DEALLOC] Unknown allocation at " << ptr << "\n";
58 : }
59 25 : stats_.deallocation_count += 1; // Still count the deallocation
60 25 : return;
61 : }
62 :
63 : // Update statistics
64 284 : if (size > 0) {
65 284 : stats_.total_deallocated += size;
66 284 : stats_.current_usage -= size;
67 : }
68 284 : stats_.deallocation_count += 1;
69 309 : }
70 :
71 41 : void MemoryTracker::reset() {
72 41 : std::lock_guard<std::mutex> lock(mutex_);
73 :
74 41 : stats_.total_allocated = 0;
75 41 : stats_.total_deallocated = 0;
76 41 : stats_.current_usage = 0;
77 41 : stats_.peak_usage = 0;
78 41 : stats_.allocation_count = 0;
79 41 : stats_.deallocation_count = 0;
80 :
81 41 : allocations_.clear();
82 82 : }
83 :
84 2 : bool MemoryTracker::check_leaks() const {
85 2 : std::lock_guard<std::mutex> lock(mutex_);
86 :
87 2 : Stats current_stats = stats_;
88 :
89 2 : std::cout << "\n=== Memory Leak Report ===\n";
90 2 : std::cout << std::fixed << std::setprecision(2);
91 :
92 : // Summary statistics
93 2 : std::cout << "Total allocated: " << current_stats.total_allocated << " bytes\n";
94 2 : std::cout << "Total deallocated: " << current_stats.total_deallocated << " bytes\n";
95 2 : std::cout << "Current usage: " << current_stats.current_usage << " bytes\n";
96 2 : std::cout << "Peak usage: " << current_stats.peak_usage << " bytes\n";
97 2 : std::cout << "Allocation count: " << current_stats.allocation_count << "\n";
98 2 : std::cout << "Deallocation count: " << current_stats.deallocation_count << "\n";
99 :
100 : // Memory efficiency metrics
101 2 : if (current_stats.peak_usage > 0) {
102 2 : double efficiency = static_cast<double>(current_stats.total_allocated) / current_stats.peak_usage;
103 2 : std::cout << "Memory efficiency: " << efficiency << "x (allocated/peak)\n";
104 : }
105 :
106 : // Leak detection
107 2 : size_t leaked_bytes = current_stats.memory_leaked();
108 2 : size_t outstanding_allocs = current_stats.outstanding_allocations();
109 :
110 2 : std::cout << "\n--- Leak Analysis ---\n";
111 2 : std::cout << "Leaked bytes: " << leaked_bytes << "\n";
112 2 : std::cout << "Outstanding allocs: " << outstanding_allocs << "\n";
113 :
114 2 : bool has_leaks = false;
115 :
116 2 : if (detailed_tracking_ && !allocations_.empty()) {
117 0 : has_leaks = true;
118 0 : std::cout << "\n--- Unfreed Allocations ---\n";
119 :
120 0 : size_t total_unfreed = 0;
121 0 : for (const auto& [ptr, size] : allocations_) {
122 0 : std::cout << " " << ptr << ": " << size << " bytes\n";
123 0 : total_unfreed += size;
124 : }
125 0 : std::cout << "Total unfreed: " << total_unfreed << " bytes\n";
126 2 : } else if (leaked_bytes > 0 || outstanding_allocs > 0) {
127 1 : has_leaks = true;
128 1 : std::cout << "WARNING: Potential memory leaks detected!\n";
129 1 : std::cout << "Enable detailed tracking for more information.\n";
130 : }
131 :
132 2 : if (!has_leaks) {
133 1 : std::cout << "✓ No memory leaks detected!\n";
134 : }
135 :
136 2 : std::cout << "=========================\n\n";
137 :
138 2 : return !has_leaks;
139 2 : }
140 :
141 : } // namespace hashbrowns
|