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
18using namespace std;
19
20#if !defined LAF_MEMLEAK // Without leak detection
21
22void* base_malloc(size_t bytes)
23{
24 return malloc(bytes);
25}
26
27void* base_malloc0(size_t bytes)
28{
29 return calloc(1, bytes);
30}
31
32void* base_realloc(void* mem, size_t bytes)
33{
34 return realloc(mem, bytes);
35}
36
37void base_free(void* mem)
38{
39 assert(mem);
40 free(mem);
41}
42
43char* 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
65struct slot_t {
66 void* backtrace[BACKTRACE_LEVELS];
67 void* ptr;
68 size_t size;
69 struct slot_t* next;
70};
71
72static bool memleak_status = false;
73static slot_t* headslot;
74static std::mutex g_mutex;
75
76void 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
92void 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
154static 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
188static 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
212void* 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
223void* 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
234void* 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
248void base_free(void* mem)
249{
250 assert(mem);
251 if (mem) {
252 delslot(mem);
253 free(mem);
254 }
255}
256
257char* 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
270void* 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
278void* 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
286void operator delete(void* ptr) LAF_NOEXCEPT
287{
288 if (!ptr)
289 return;
290 base_free(ptr);
291}
292
293void operator delete[](void* ptr) LAF_NOEXCEPT
294{
295 if (!ptr)
296 return;
297 base_free(ptr);
298}
299
300#endif
301