1 | // LAF Base Library |
2 | // Copyright (c) 2022 Igara Studio S.A. |
3 | // Copyright (c) 2001-2016 David Capello |
4 | // |
5 | // This file is released under the terms of the MIT license. |
6 | // Read LICENSE.txt for more information. |
7 | |
8 | #ifdef HAVE_CONFIG_H |
9 | #include "config.h" |
10 | #endif |
11 | |
12 | #include <cassert> |
13 | #include <cstdio> |
14 | #include <cstdlib> |
15 | #include <cstring> |
16 | #include <mutex> |
17 | |
18 | using namespace std; |
19 | |
20 | #if !defined LAF_MEMLEAK // Without leak detection |
21 | |
22 | void* base_malloc(size_t bytes) |
23 | { |
24 | return malloc(bytes); |
25 | } |
26 | |
27 | void* base_malloc0(size_t bytes) |
28 | { |
29 | return calloc(1, bytes); |
30 | } |
31 | |
32 | void* base_realloc(void* mem, size_t bytes) |
33 | { |
34 | return realloc(mem, bytes); |
35 | } |
36 | |
37 | void base_free(void* mem) |
38 | { |
39 | assert(mem); |
40 | free(mem); |
41 | } |
42 | |
43 | char* base_strdup(const char* string) |
44 | { |
45 | assert(string); |
46 | #ifdef _MSC_VER |
47 | return _strdup(string); |
48 | #else |
49 | return strdup(string); |
50 | #endif |
51 | } |
52 | |
53 | #else // With leak detection |
54 | |
55 | #define BACKTRACE_LEVELS 16 |
56 | |
57 | #ifdef _MSC_VER |
58 | #include <windows.h> |
59 | #include <dbghelp.h> |
60 | |
61 | typedef USHORT (WINAPI* RtlCaptureStackBackTraceType)(ULONG, ULONG, PVOID*, PULONG); |
62 | static RtlCaptureStackBackTraceType pRtlCaptureStackBackTrace; |
63 | #endif |
64 | |
65 | struct slot_t { |
66 | void* backtrace[BACKTRACE_LEVELS]; |
67 | void* ptr; |
68 | size_t size; |
69 | struct slot_t* next; |
70 | }; |
71 | |
72 | static bool memleak_status = false; |
73 | static slot_t* headslot; |
74 | static std::mutex g_mutex; |
75 | |
76 | void base_memleak_init() |
77 | { |
78 | #ifdef _MSC_VER |
79 | pRtlCaptureStackBackTrace = |
80 | (RtlCaptureStackBackTraceType)(::GetProcAddress( |
81 | ::LoadLibrary(L"kernel32.dll" ), |
82 | "RtlCaptureStackBackTrace" )); |
83 | #endif |
84 | |
85 | assert(!memleak_status); |
86 | |
87 | headslot = NULL; |
88 | |
89 | memleak_status = true; |
90 | } |
91 | |
92 | void base_memleak_exit() |
93 | { |
94 | assert(memleak_status); |
95 | memleak_status = false; |
96 | |
97 | FILE* f = fopen("_ase_memlog.txt" , "wt" ); |
98 | slot_t* it; |
99 | |
100 | if (f != NULL) { |
101 | #ifdef _MSC_VER |
102 | struct SYMBOL_INFO_EX { |
103 | IMAGEHLP_SYMBOL64 header; |
104 | char filename[MAX_SYM_NAME]; |
105 | } si; |
106 | si.header.SizeOfStruct = sizeof(SYMBOL_INFO_EX); |
107 | si.header.MaxNameLength = MAX_SYM_NAME; |
108 | |
109 | IMAGEHLP_LINE64 line; |
110 | line.SizeOfStruct = sizeof(IMAGEHLP_LINE64); |
111 | |
112 | ::SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS); |
113 | |
114 | HANDLE hproc = ::GetCurrentProcess(); |
115 | if (!::SymInitialize(hproc, NULL, TRUE)) |
116 | fprintf(f, "Error initializing SymInitialize()\nGetLastError = %d\n" , ::GetLastError()); |
117 | |
118 | char filename[MAX_PATH]; |
119 | ::GetModuleFileNameA(NULL, filename, sizeof(filename) / sizeof(filename[0])); |
120 | ::SymLoadModule64(hproc, NULL, filename, NULL, 0, 0); |
121 | #endif |
122 | |
123 | // Memory leaks |
124 | for (it=headslot; it!=NULL; it=it->next) { |
125 | fprintf(f, "\nLEAK address: %p, size: %lu\n" , it->ptr, it->size); |
126 | |
127 | for (int c=0; c<BACKTRACE_LEVELS; ++c) { |
128 | #ifdef _MSC_VER |
129 | DWORD displacement; |
130 | |
131 | if (::SymGetLineFromAddr64(hproc, (DWORD)it->backtrace[c], &displacement, &line)) { |
132 | si.header.Name[0] = 0; |
133 | |
134 | ::SymGetSymFromAddr64(hproc, (DWORD)it->backtrace[c], NULL, &si.header); |
135 | |
136 | fprintf(f, "%p : %s(%lu) [%s]\n" , |
137 | it->backtrace[c], |
138 | line.FileName, line.LineNumber, |
139 | si.header.Name); |
140 | } |
141 | else |
142 | #endif |
143 | fprintf(f, "%p\n" , it->backtrace[c]); |
144 | } |
145 | } |
146 | fclose(f); |
147 | |
148 | #ifdef _MSC_VER |
149 | ::SymCleanup(hproc); |
150 | #endif |
151 | } |
152 | } |
153 | |
154 | static void addslot(void* ptr, size_t size) |
155 | { |
156 | if (!memleak_status) |
157 | return; |
158 | |
159 | slot_t* p = reinterpret_cast<slot_t*>(malloc(sizeof(slot_t))); |
160 | |
161 | assert(ptr); |
162 | |
163 | // __builtin_return_address is a GCC extension |
164 | #if defined(__GNUC__) |
165 | p->backtrace[0] = __builtin_return_address(4); |
166 | p->backtrace[1] = __builtin_return_address(3); |
167 | p->backtrace[2] = __builtin_return_address(2); |
168 | p->backtrace[3] = __builtin_return_address(1); |
169 | #elif defined(_MSC_VER) |
170 | { |
171 | for (int c=0; c<BACKTRACE_LEVELS; ++c) |
172 | p->backtrace[c] = 0; |
173 | |
174 | pRtlCaptureStackBackTrace(0, BACKTRACE_LEVELS, p->backtrace, NULL); |
175 | } |
176 | #else |
177 | #error Not supported |
178 | #endif |
179 | |
180 | p->ptr = ptr; |
181 | p->size = size; |
182 | |
183 | std::lock_guard lock(g_mutex); |
184 | p->next = headslot; |
185 | headslot = p; |
186 | } |
187 | |
188 | static void delslot(void* ptr) |
189 | { |
190 | if (!memleak_status) |
191 | return; |
192 | |
193 | slot_t *it, *prev = NULL; |
194 | |
195 | assert(ptr); |
196 | |
197 | std::lock_guard lock(g_mutex); |
198 | |
199 | for (it=headslot; it!=nullptr; prev=it, it=it->next) { |
200 | if (it->ptr == ptr) { |
201 | if (prev) |
202 | prev->next = it->next; |
203 | else |
204 | headslot = it->next; |
205 | |
206 | free(it); |
207 | break; |
208 | } |
209 | } |
210 | } |
211 | |
212 | void* base_malloc(size_t bytes) |
213 | { |
214 | void* mem = malloc(bytes); |
215 | if (mem) { |
216 | addslot(mem, bytes); |
217 | return mem; |
218 | } |
219 | else |
220 | return nullptr; |
221 | } |
222 | |
223 | void* base_malloc0(size_t bytes) |
224 | { |
225 | void* mem = calloc(1, bytes); |
226 | if (mem) { |
227 | addslot(mem, bytes); |
228 | return mem; |
229 | } |
230 | else |
231 | return nullptr; |
232 | } |
233 | |
234 | void* base_realloc(void* mem, size_t bytes) |
235 | { |
236 | void* newmem = realloc(mem, bytes); |
237 | if (newmem) { |
238 | if (mem) |
239 | delslot(mem); |
240 | |
241 | addslot(newmem, bytes); |
242 | return newmem; |
243 | } |
244 | else |
245 | return nullptr; |
246 | } |
247 | |
248 | void base_free(void* mem) |
249 | { |
250 | assert(mem); |
251 | if (mem) { |
252 | delslot(mem); |
253 | free(mem); |
254 | } |
255 | } |
256 | |
257 | char* base_strdup(const char* string) |
258 | { |
259 | assert(string); |
260 | |
261 | char* mem = strdup(string); |
262 | if (mem) |
263 | addslot(mem, strlen(mem) + 1); |
264 | |
265 | return mem; |
266 | } |
267 | |
268 | // C++ operators |
269 | |
270 | void* operator new(std::size_t size) |
271 | { |
272 | void* ptr = base_malloc(size); |
273 | if (!ptr) |
274 | throw std::bad_alloc(); |
275 | return ptr; |
276 | } |
277 | |
278 | void* operator new[](std::size_t size) |
279 | { |
280 | void* ptr = base_malloc(size); |
281 | if (!ptr) |
282 | throw std::bad_alloc(); |
283 | return ptr; |
284 | } |
285 | |
286 | void operator delete(void* ptr) LAF_NOEXCEPT |
287 | { |
288 | if (!ptr) |
289 | return; |
290 | base_free(ptr); |
291 | } |
292 | |
293 | void operator delete[](void* ptr) LAF_NOEXCEPT |
294 | { |
295 | if (!ptr) |
296 | return; |
297 | base_free(ptr); |
298 | } |
299 | |
300 | #endif |
301 | |