Branch data Line data Source code
1 : : /*
2 : : * Copyright 2025 Jinwoo Sung
3 : : *
4 : : * Licensed under the Apache License, Version 2.0 (the "License");
5 : : * you may not use this file except in compliance with the License.
6 : : * You may obtain a copy of the License at
7 : : *
8 : : * http://www.apache.org/licenses/LICENSE-2.0
9 : : *
10 : : * Unless required by applicable law or agreed to in writing, software
11 : : * distributed under the License is distributed on an "AS IS" BASIS,
12 : : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 : : * See the License for the specific language governing permissions and
14 : : * limitations under the License.
15 : : */
16 : :
17 : : #include "wirestead/memory/memory_tracker.hpp"
18 : :
19 : : #include <iomanip>
20 : : #include <iostream>
21 : : #include <sstream>
22 : :
23 : : #include "wirestead/diagnostics/logger.hpp"
24 : :
25 : : namespace wirestead {
26 : : namespace memory {
27 : :
28 : 31 : MemoryTracker& MemoryTracker::instance() {
29 : 31 : static MemoryTracker instance;
30 : 31 : return instance;
31 : : }
32 : :
33 : 7 : void MemoryTracker::track_allocation(void* ptr, size_t size, const char* file, int line, const char* function) {
34 [ + + ]: 7 : if (!tracking_enabled_.load()) {
35 : 1 : return;
36 : : }
37 : :
38 : 6 : std::lock_guard<std::mutex> lock(allocations_mutex_);
39 : :
40 : 6 : AllocationInfo info;
41 : 6 : info.ptr = ptr;
42 : 6 : info.size = size;
43 [ + + ]: 6 : info.file = file ? file : "unknown";
44 : 6 : info.line = line;
45 [ + + ]: 6 : info.function = function ? function : "unknown";
46 : 6 : info.timestamp = std::chrono::steady_clock::now();
47 : :
48 : 6 : allocations_[ptr] = info;
49 : :
50 : : // Update statistics
51 : 6 : stats_.total_allocations++;
52 : 6 : stats_.current_allocations++;
53 : 6 : stats_.total_bytes_allocated += size;
54 : 6 : stats_.current_bytes_allocated += size;
55 : :
56 : : // Update peak values
57 [ + - ]: 6 : if (stats_.current_allocations > stats_.peak_allocations) {
58 : 6 : stats_.peak_allocations = stats_.current_allocations;
59 : : }
60 [ + - ]: 6 : if (stats_.current_bytes_allocated > stats_.peak_bytes_allocated) {
61 : 6 : stats_.peak_bytes_allocated = stats_.current_bytes_allocated;
62 : : }
63 : 6 : }
64 : :
65 : 2 : void MemoryTracker::track_deallocation(void* ptr) {
66 [ - + ]: 2 : if (!tracking_enabled_.load()) {
67 : 0 : return;
68 : : }
69 : :
70 : 2 : std::lock_guard<std::mutex> lock(allocations_mutex_);
71 : :
72 : 2 : auto it = allocations_.find(ptr);
73 [ + - ]: 2 : if (it != allocations_.end()) {
74 : 2 : size_t size = it->second.size;
75 : 2 : allocations_.erase(it);
76 : :
77 : : // Update statistics
78 : 2 : stats_.total_deallocations++;
79 : 2 : stats_.current_allocations--;
80 : 2 : stats_.total_bytes_deallocated += size;
81 : 2 : stats_.current_bytes_allocated -= size;
82 : : }
83 : 2 : }
84 : :
85 : 8 : MemoryTracker::MemoryStats MemoryTracker::stats() const {
86 : 8 : std::lock_guard<std::mutex> lock(allocations_mutex_);
87 : 8 : return stats_;
88 : 8 : }
89 : :
90 : 5 : std::vector<MemoryTracker::AllocationInfo> MemoryTracker::current_allocations() const {
91 : 5 : std::lock_guard<std::mutex> lock(allocations_mutex_);
92 : :
93 : 5 : std::vector<AllocationInfo> result;
94 : 5 : result.reserve(allocations_.size());
95 : :
96 [ + + ]: 10 : for (const auto& pair : allocations_) {
97 : 5 : result.push_back(pair.second);
98 : : }
99 : :
100 : 10 : return result;
101 : 5 : }
102 : :
103 : 3 : std::vector<MemoryTracker::AllocationInfo> MemoryTracker::leaked_allocations() const {
104 : 3 : return current_allocations(); // Current allocations are potential leaks
105 : : }
106 : :
107 : 9 : void MemoryTracker::enable_tracking(bool enable) { tracking_enabled_.store(enable); }
108 : :
109 : 1 : void MemoryTracker::disable_tracking() { tracking_enabled_.store(false); }
110 : :
111 : 1 : bool MemoryTracker::tracking_enabled() const { return tracking_enabled_.load(); }
112 : :
113 : 16 : void MemoryTracker::clear_tracking_data() {
114 : 16 : std::lock_guard<std::mutex> lock(allocations_mutex_);
115 : 16 : allocations_.clear();
116 : :
117 : : // Reset statistics
118 : 16 : stats_.total_allocations = 0;
119 : 16 : stats_.total_deallocations = 0;
120 : 16 : stats_.current_allocations = 0;
121 : 16 : stats_.peak_allocations = 0;
122 : 16 : stats_.total_bytes_allocated = 0;
123 : 16 : stats_.total_bytes_deallocated = 0;
124 : 16 : stats_.current_bytes_allocated = 0;
125 : 16 : stats_.peak_bytes_allocated = 0;
126 : 16 : }
127 : :
128 : 1 : void MemoryTracker::print_memory_report() const {
129 : 1 : auto mem_stats = stats();
130 : 1 : auto allocs = current_allocations();
131 : :
132 : 1 : std::cout << "\n=== Memory Tracker Report ===" << std::endl;
133 : 1 : std::cout << "Total allocations: " << mem_stats.total_allocations << std::endl;
134 : 1 : std::cout << "Total deallocations: " << mem_stats.total_deallocations << std::endl;
135 : 1 : std::cout << "Current allocations: " << mem_stats.current_allocations << std::endl;
136 : 1 : std::cout << "Peak allocations: " << mem_stats.peak_allocations << std::endl;
137 : 1 : std::cout << "Total bytes allocated: " << mem_stats.total_bytes_allocated << std::endl;
138 : 1 : std::cout << "Total bytes deallocated: " << mem_stats.total_bytes_deallocated << std::endl;
139 : 1 : std::cout << "Current bytes allocated: " << mem_stats.current_bytes_allocated << std::endl;
140 : 1 : std::cout << "Peak bytes allocated: " << mem_stats.peak_bytes_allocated << std::endl;
141 : 1 : std::cout << "Current active allocations: " << allocs.size() << std::endl;
142 : :
143 [ + - ]: 1 : if (!allocs.empty()) {
144 : 1 : std::cout << "\n=== Current Allocations ===" << std::endl;
145 [ + + ]: 2 : for (const auto& alloc : allocs) {
146 : 1 : std::cout << "Ptr: " << alloc.ptr << ", Size: " << alloc.size << ", File: " << alloc.file << ":" << alloc.line
147 : 1 : << ", Function: " << alloc.function << std::endl;
148 : : }
149 : : }
150 : 1 : }
151 : :
152 : 1 : void MemoryTracker::print_leak_report() const {
153 : 1 : auto leaks = leaked_allocations();
154 : :
155 [ - + ]: 1 : if (leaks.empty()) {
156 : 0 : std::cout << "\n=== No Memory Leaks Detected ===" << std::endl;
157 : 0 : return;
158 : : }
159 : :
160 : 1 : std::cout << "\n=== Memory Leak Report ===" << std::endl;
161 : 1 : std::cout << "Found " << leaks.size() << " potential memory leaks:" << std::endl;
162 : :
163 : 1 : size_t total_leaked_bytes = 0;
164 [ + + ]: 2 : for (const auto& alloc : leaks) {
165 : 1 : std::cout << "Leaked: " << alloc.size << " bytes at " << alloc.ptr << " allocated in " << alloc.file << ":"
166 : 1 : << alloc.line << " (" << alloc.function << ")" << std::endl;
167 : 1 : total_leaked_bytes += alloc.size;
168 : : }
169 : :
170 : 1 : std::cout << "Total leaked bytes: " << total_leaked_bytes << std::endl;
171 : 1 : }
172 : :
173 : 1 : void MemoryTracker::log_memory_report() const {
174 : 1 : auto mem_stats = stats();
175 : 1 : auto allocs = current_allocations();
176 : :
177 : 1 : std::ostringstream oss;
178 : 1 : oss << "\n=== Memory Tracker Report ===\n";
179 : 1 : oss << "Total allocations: " << mem_stats.total_allocations << "\n";
180 : 1 : oss << "Total deallocations: " << mem_stats.total_deallocations << "\n";
181 : 1 : oss << "Current allocations: " << mem_stats.current_allocations << "\n";
182 : 1 : oss << "Peak allocations: " << mem_stats.peak_allocations << "\n";
183 : 1 : oss << "Total bytes allocated: " << mem_stats.total_bytes_allocated << "\n";
184 : 1 : oss << "Total bytes deallocated: " << mem_stats.total_bytes_deallocated << "\n";
185 : 1 : oss << "Current bytes allocated: " << mem_stats.current_bytes_allocated << "\n";
186 : 1 : oss << "Peak bytes allocated: " << mem_stats.peak_bytes_allocated << "\n";
187 : 1 : oss << "Current active allocations: " << allocs.size();
188 : :
189 : 1 : WIRESTEAD_LOG_INFO("memory_tracker", "report", oss.str());
190 : :
191 [ + - ]: 1 : if (!allocs.empty()) {
192 : 1 : std::ostringstream alloc_oss;
193 : 1 : alloc_oss << "\n=== Current Allocations ===\n";
194 [ + + ]: 2 : for (const auto& alloc : allocs) {
195 : 1 : alloc_oss << "Ptr: " << alloc.ptr << ", Size: " << alloc.size << ", File: " << alloc.file << ":" << alloc.line
196 : 1 : << ", Function: " << alloc.function << "\n";
197 : : }
198 : 1 : WIRESTEAD_LOG_INFO("memory_tracker", "allocations", alloc_oss.str());
199 : 1 : }
200 : 1 : }
201 : :
202 : 1 : void MemoryTracker::log_leak_report() const {
203 : 1 : auto leaks = leaked_allocations();
204 : :
205 [ - + ]: 1 : if (leaks.empty()) {
206 : 0 : WIRESTEAD_LOG_INFO("memory_tracker", "leak_check", "No Memory Leaks Detected");
207 : 0 : return;
208 : : }
209 : :
210 : 1 : std::ostringstream oss;
211 : 1 : oss << "\n=== Memory Leak Report ===\n";
212 : 1 : oss << "Found " << leaks.size() << " potential memory leaks:\n";
213 : :
214 : 1 : size_t total_leaked_bytes = 0;
215 [ + + ]: 2 : for (const auto& alloc : leaks) {
216 : 1 : oss << "Leaked: " << alloc.size << " bytes at " << alloc.ptr << " allocated in " << alloc.file << ":" << alloc.line
217 : 1 : << " (" << alloc.function << ")\n";
218 : 1 : total_leaked_bytes += alloc.size;
219 : : }
220 : :
221 : 1 : oss << "Total leaked bytes: " << total_leaked_bytes;
222 : 1 : WIRESTEAD_LOG_ERROR("memory_tracker", "leak_check", oss.str());
223 : 1 : }
224 : :
225 : : // ScopedMemoryTracker implementation
226 : 1 : ScopedMemoryTracker::ScopedMemoryTracker(const char* file, int line, const char* function)
227 : 1 : : file_(file), line_(line), function_(function) {}
228 : :
229 : 1 : ScopedMemoryTracker::~ScopedMemoryTracker() {
230 : : // Destructor can be used for cleanup if needed
231 : 1 : }
232 : :
233 : 1 : void ScopedMemoryTracker::track_allocation(void* ptr, size_t size) {
234 : 1 : MemoryTracker::instance().track_allocation(ptr, size, file_, line_, function_);
235 : 1 : }
236 : :
237 : 1 : void ScopedMemoryTracker::track_deallocation(void* ptr) { MemoryTracker::instance().track_deallocation(ptr); }
238 : :
239 : : } // namespace memory
240 : : } // namespace wirestead
|