Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
epidemian
GitHub Repository: epidemian/gravity
Path: blob/master/src/shared/gravity_memory.c
1214 views
1
//
2
// gravity_memory.c
3
// gravity
4
//
5
// Created by Marco Bambini on 20/03/16.
6
// Copyright © 2016 Creolabs. All rights reserved.
7
//
8
9
#include "gravity_memory.h"
10
#if GRAVITY_MEMORY_DEBUG
11
12
#include <stdlib.h>
13
#include <strings.h>
14
15
#if _WIN32
16
#include <imagehlp.h>
17
#else
18
#include <execinfo.h>
19
#endif
20
21
static void _ptr_add (void *ptr, size_t size);
22
static void _ptr_replace (void *old_ptr, void *new_ptr, size_t new_size);
23
static void _ptr_remove (void *ptr);
24
static uint32_t _ptr_lookup (void *ptr);
25
static char **_ptr_stacktrace (size_t *nframes);
26
static bool _is_internal(const char *s);
27
28
#define STACK_DEPTH 128
29
#define SLOT_MIN 128
30
#define SLOT_NOTFOUND UINT32_MAX
31
#define BUILD_ERROR(...) char current_error[1024]; snprintf(current_error, sizeof(current_error), __VA_ARGS__)
32
#define BUILD_STACK(v1,v2) size_t v1; char **v2 = _ptr_stacktrace(&v1)
33
#define CHECK_FLAG() if (!check_flag) return
34
35
typedef struct {
36
bool deleted;
37
void *ptr;
38
size_t size;
39
size_t nrealloc;
40
41
// record where it has been allocated/reallocated
42
size_t nframe;
43
char **frames;
44
45
// record where is has been freed
46
size_t nframe2;
47
char **frames2;
48
} memslot;
49
50
typedef struct {
51
uint32_t nalloc;
52
uint32_t nrealloc;
53
uint32_t nfree;
54
uint32_t currmem;
55
uint32_t maxmem;
56
uint32_t nslot; // number of slot filled with data
57
uint32_t aslot; // number of allocated slot
58
memslot *slot;
59
} _memdebug;
60
61
static _memdebug memdebug;
62
static bool check_flag = true;
63
64
static void memdebug_report (char *str, char **stack, size_t nstack, memslot *slot) {
65
printf("%s\n", str);
66
for (size_t i=0; i<nstack; ++i) {
67
if (_is_internal(stack[i])) continue;
68
printf("%s\n", stack[i]);
69
}
70
71
if (slot) {
72
printf("\nallocated:\n");
73
for (size_t i=0; i<slot->nframe; ++i) {
74
if (_is_internal(slot->frames[i])) continue;
75
printf("%s\n", slot->frames[i]);
76
}
77
78
printf("\nfreed:\n");
79
for (size_t i=0; i<slot->nframe2; ++i) {
80
if (_is_internal(slot->frames2[i])) continue;
81
printf("%s\n", slot->frames2[i]);
82
}
83
}
84
85
abort();
86
}
87
88
void memdebug_init (void) {
89
if (memdebug.slot) free(memdebug.slot);
90
bzero(&memdebug, sizeof(_memdebug));
91
92
memdebug.slot = (memslot *) malloc(sizeof(memslot) * SLOT_MIN);
93
memdebug.aslot = SLOT_MIN;
94
}
95
96
void *memdebug_malloc(size_t size) {
97
void *ptr = malloc(size);
98
if (!ptr) {
99
BUILD_ERROR("Unable to allocated a block of %zu bytes", size);
100
BUILD_STACK(n, stack);
101
memdebug_report(current_error, stack, n, NULL);
102
return NULL;
103
}
104
105
_ptr_add(ptr, size);
106
return ptr;
107
}
108
109
void *memdebug_malloc0(size_t size) {
110
return memdebug_calloc(1, size);
111
}
112
113
void *memdebug_calloc(size_t num, size_t size) {
114
void *ptr = calloc(num, size);
115
if (!ptr) {
116
BUILD_ERROR("Unable to allocated a block of %zu bytes", size);
117
BUILD_STACK(n, stack);
118
memdebug_report(current_error, stack, n, NULL);
119
return NULL;
120
}
121
122
_ptr_add(ptr, num*size);
123
return ptr;
124
}
125
126
void *memdebug_realloc(void *ptr, size_t new_size) {
127
// ensure ptr has been previously allocated by malloc, calloc or realloc and not yet freed with free
128
uint32_t index = _ptr_lookup(ptr);
129
if (index == SLOT_NOTFOUND) {
130
BUILD_ERROR("Pointer being reallocated was now previously allocated");
131
BUILD_STACK(n, stack);
132
memdebug_report(current_error, stack, n, NULL);
133
return NULL;
134
}
135
136
void *new_ptr = realloc(ptr, new_size);
137
if (!ptr) {
138
BUILD_ERROR("Unable to reallocate a block of %zu bytes", new_size);
139
BUILD_STACK(n, stack);
140
memdebug_report(current_error, stack, n, &memdebug.slot[index]);
141
return NULL;
142
}
143
144
_ptr_replace(ptr, new_ptr, new_size);
145
return new_ptr;
146
}
147
148
bool memdebug_remove(void *ptr) {
149
// ensure ptr has been previously allocated by malloc, calloc or realloc and not yet freed with free
150
if (check_flag) {
151
uint32_t index = _ptr_lookup(ptr);
152
if (index == SLOT_NOTFOUND) {
153
BUILD_ERROR("Pointer being freed was not previously allocated");
154
BUILD_STACK(n, stack);
155
memdebug_report(current_error, stack, n, NULL);
156
return false;
157
}
158
159
memslot m = memdebug.slot[index];
160
if (m.deleted) {
161
BUILD_ERROR("Pointer already freed");
162
BUILD_STACK(n, stack);
163
memdebug_report(current_error, stack, n, &m);
164
return false;
165
}
166
}
167
168
_ptr_remove(ptr);
169
return true;
170
}
171
172
void memdebug_free(void *ptr) {
173
if (!memdebug_remove(ptr)) return;
174
free(ptr);
175
}
176
void memdebug_setcheck(bool flag) {
177
check_flag = flag;
178
}
179
180
void memdebug_stat(void) {
181
printf("\n========== MEMORY STATS ==========\n");
182
printf("Allocations count: %d\n", memdebug.nalloc);
183
printf("Reallocations count: %d\n", memdebug.nrealloc);
184
printf("Free count: %d\n", memdebug.nfree);
185
printf("Leaked: %d (bytes)\n", memdebug.currmem);
186
printf("Max memory usage: %d (bytes)\n", memdebug.maxmem);
187
printf("==================================\n\n");
188
189
if (memdebug.currmem > 0) {
190
printf("\n");
191
for (uint32_t i=0; i<memdebug.nslot; ++i) {
192
memslot m = memdebug.slot[i];
193
if ((!m.ptr) || (m.deleted)) continue;
194
195
printf("Block %p size: %zu (reallocated %zu)\n", m.ptr, m.size, m.nrealloc);
196
printf("Call stack:\n");
197
printf("===========\n");
198
for (size_t j=0; j<m.nframe; ++j) {
199
if (_is_internal(m.frames[j])) continue;
200
printf("%s\n", m.frames[j]);
201
}
202
printf("===========\n\n");
203
}
204
}
205
}
206
207
size_t memdebug_status (void) {
208
return memdebug.maxmem;
209
}
210
211
size_t memdebug_leaks (void) {
212
return memdebug.currmem;
213
}
214
215
// Internals
216
void _ptr_add (void *ptr, size_t size) {
217
CHECK_FLAG();
218
219
if (memdebug.nslot + 1 >= memdebug.aslot) {
220
size_t old_size = sizeof(memslot) * memdebug.nslot;
221
size_t new_size = sizeof(memslot) * SLOT_MIN;
222
memslot *new_slot = (memslot *) realloc(memdebug.slot, old_size+new_size);
223
if (!new_slot) {
224
BUILD_ERROR("Unable to reallocate internal slots");
225
memdebug_report(current_error, NULL, 0, NULL);
226
abort();
227
}
228
memdebug.slot = new_slot;
229
memdebug.aslot += SLOT_MIN;
230
}
231
232
memslot slot = {
233
.deleted = false,
234
.ptr = ptr,
235
.size = size,
236
.nrealloc = 0,
237
.nframe2 = 0,
238
.frames = NULL
239
};
240
slot.frames = _ptr_stacktrace(&slot.nframe);
241
242
memdebug.slot[memdebug.nslot] = slot;
243
++memdebug.nslot;
244
245
++memdebug.nalloc;
246
memdebug.currmem += size;
247
if (memdebug.currmem > memdebug.maxmem)
248
memdebug.maxmem = memdebug.currmem;
249
}
250
251
void _ptr_replace (void *old_ptr, void *new_ptr, size_t new_size) {
252
CHECK_FLAG();
253
254
uint32_t index = _ptr_lookup(old_ptr);
255
256
if (index == SLOT_NOTFOUND) {
257
BUILD_ERROR("Unable to find old pointer to realloc");
258
memdebug_report(current_error, NULL, 0, NULL);
259
}
260
261
memslot slot = memdebug.slot[index];
262
if (slot.deleted) {
263
BUILD_ERROR("Pointer already freed");
264
BUILD_STACK(n, stack);
265
memdebug_report(current_error, stack, n, &slot);
266
}
267
size_t old_size = memdebug.slot[index].size;
268
slot.ptr = new_ptr;
269
slot.size = new_size;
270
if (slot.frames) free((void *)slot.frames);
271
slot.frames = _ptr_stacktrace(&slot.nframe);
272
++slot.nrealloc;
273
274
memdebug.slot[index] = slot;
275
++memdebug.nrealloc;
276
memdebug.currmem += (new_size - old_size);
277
if (memdebug.currmem > memdebug.maxmem)
278
memdebug.maxmem = memdebug.currmem;
279
}
280
281
void _ptr_remove (void *ptr) {
282
CHECK_FLAG();
283
284
uint32_t index = _ptr_lookup(ptr);
285
286
if (index == SLOT_NOTFOUND) {
287
BUILD_ERROR("Unable to find old pointer to realloc");
288
memdebug_report(current_error, NULL, 0, NULL);
289
}
290
291
memslot slot = memdebug.slot[index];
292
if (slot.deleted) {
293
BUILD_ERROR("Pointer already freed");
294
BUILD_STACK(n, stack);
295
memdebug_report(current_error, stack, n, &slot);
296
}
297
298
size_t old_size = memdebug.slot[index].size;
299
memdebug.slot[index].deleted = true;
300
memdebug.slot[index].frames2 = _ptr_stacktrace(&memdebug.slot[index].nframe2);
301
302
++memdebug.nfree;
303
memdebug.currmem -= old_size;
304
}
305
306
uint32_t _ptr_lookup (void *ptr) {
307
for (uint32_t i=0; i<memdebug.nslot; ++i) {
308
if (memdebug.slot[i].ptr == ptr) return i;
309
}
310
return SLOT_NOTFOUND;
311
}
312
313
char **_ptr_stacktrace (size_t *nframes) {
314
#if _WIN32
315
http://www.codeproject.com/Articles/11132/Walking-the-callstack
316
https://spin.atomicobject.com/2013/01/13/exceptions-stack-traces-c/
317
#else
318
void *callstack[STACK_DEPTH];
319
int n = backtrace(callstack, STACK_DEPTH);
320
char **strs = backtrace_symbols(callstack, n);
321
*nframes = (size_t)n;
322
return strs;
323
#endif
324
}
325
326
// Default callback
327
bool _is_internal(const char *s) {
328
static const char *reserved[] = {"??? ", "libdyld.dylib ", "memdebug_", "_ptr_", NULL};
329
330
const char **r = reserved;
331
while (*r) {
332
if (strstr(s, *r)) return true;
333
++r;
334
}
335
return false;
336
}
337
338
#undef STACK_DEPTH
339
#undef SLOT_MIN
340
#undef SLOT_NOTFOUND
341
#undef BUILD_ERROR
342
#undef BUILD_STACK
343
#undef CHECK_FLAG
344
345
#endif
346
347
348
349
350