Branch data Line data Source code
1 : : /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 : : /*
3 : : * Simple Memory Leakage Detection Tool
4 : : * (C) 2013 Jung-Sang Ahn <jungsang.ahn@gmail.com>
5 : : * see https://github.com/greensky00/memleak
6 : : */
7 : :
8 : : #include <stdio.h>
9 : : #include <stdlib.h>
10 : : #include <string.h>
11 : : #include <stdlib.h>
12 : : #include <assert.h>
13 : : #if !defined(__APPLE__)
14 : : #include <malloc.h>
15 : : #endif
16 : :
17 : : #include "arch.h"
18 : :
19 : : #define _MALLOC_OVERRIDE
20 : : #define INIT_VAL (0x77)
21 : : #define FREE_VAL (0xff)
22 : : //#define _WARN_NOT_ALLOCATED_MEMORY
23 : : //#define _PRINT_DBG
24 : : #if !defined(_WIN32) && !defined(WIN32)
25 : : //#define _STACK_BACKTRACE
26 : : //#define _LIBBFD
27 : : #endif
28 : :
29 : : #include "memleak.h"
30 : :
31 : : #ifdef _STACK_BACKTRACE
32 : : #include <execinfo.h>
33 : :
34 : : #ifdef _LIBBFD
35 : : #include <dlfcn.h>
36 : : #include <signal.h>
37 : : #include <bfd.h>
38 : : #include <unistd.h>
39 : :
40 : : /* globals retained across calls to resolve. */
41 : : static bfd* abfd = 0;
42 : : static asymbol **syms = 0;
43 : : static asection *text = 0;
44 : :
45 : : static void resolve(void *address, char **file, char **func, unsigned *line) {
46 : : if (!abfd) {
47 : : char ename[1024];
48 : : int l = readlink("/proc/self/exe",ename,sizeof(ename));
49 : : if (l == -1) {
50 : : perror("failed to find executable\n");
51 : : return;
52 : : }
53 : : ename[l] = 0;
54 : :
55 : : bfd_init();
56 : :
57 : : abfd = bfd_openr(ename, 0);
58 : : if (!abfd) {
59 : : perror("bfd_openr failed: ");
60 : : return;
61 : : }
62 : :
63 : : bfd_check_format(abfd,bfd_object);
64 : :
65 : : unsigned storage_needed = bfd_get_symtab_upper_bound(abfd);
66 : : syms = (asymbol **) malloc(storage_needed);
67 : : unsigned cSymbols = bfd_canonicalize_symtab(abfd, syms);
68 : :
69 : : text = bfd_get_section_by_name(abfd, ".text");
70 : : }
71 : :
72 : : long offset = ((long)address) - text->vma;
73 : : if (offset > 0) {
74 : : bfd_find_nearest_line(abfd, text, syms, offset,
75 : : (const char**)file, (const char**)func, line);
76 : : }
77 : : }
78 : :
79 : : #endif // _LIBBFD
80 : :
81 : : #endif // _STACK_BACKTRACE
82 : :
83 : :
84 : : #ifdef _PRINT_DBG
85 : : #define DBG(...) fprintf(stderr, __VA_ARGS__)
86 : : #else
87 : : #define DBG(...)
88 : : #endif
89 : :
90 : : #include "avltree.h"
91 : :
92 : : struct memleak_item {
93 : : uint64_t addr;
94 : : char *file;
95 : : size_t size;
96 : : size_t line;
97 : : struct avl_node avl;
98 : : #ifdef _STACK_BACKTRACE
99 : : size_t bt_size;
100 : : void **btrace;
101 : : #endif
102 : : #ifdef _CHK_MODIFY_AFTER_FREE
103 : : uint8_t freed;
104 : : #endif
105 : : };
106 : :
107 : : static struct avl_tree tree_index;
108 : : static uint8_t start_sw = 0;
109 : : static spin_t lock;
110 : :
111 : 0 : int memleak_cmp(struct avl_node *a, struct avl_node *b, void *aux)
112 : : {
113 : : struct memleak_item *aa, *bb;
114 : 0 : aa = _get_entry(a, struct memleak_item, avl);
115 : 0 : bb = _get_entry(b, struct memleak_item, avl);
116 [ # # ]: 0 : if (aa->addr < bb->addr) return -1;
117 [ # # ]: 0 : else if (aa->addr > bb->addr) return 1;
118 : 0 : else return 0;
119 : : }
120 : :
121 : : LIBMEMLEAK_API
122 : 187 : void memleak_start()
123 : : {
124 : 187 : spin_init(&lock);
125 : 188 : avl_init(&tree_index, NULL);
126 : 188 : start_sw = 1;
127 : 188 : }
128 : :
129 : : LIBMEMLEAK_API
130 : 185 : void memleak_end()
131 : : {
132 : : uint8_t is_leaked;
133 : 185 : size_t count = 0;
134 : : struct avl_node *a;
135 : : struct memleak_item *item;
136 : : #ifdef _STACK_BACKTRACE
137 : : int i;
138 : : char **strs;
139 : : char *file, *func;
140 : : unsigned line;
141 : : #endif
142 : :
143 : 185 : spin_lock(&lock);
144 : :
145 : 185 : start_sw = 0;
146 : :
147 : 185 : a = avl_first(&tree_index);
148 [ - + ]: 185 : while(a){
149 : 0 : item = _get_entry(a, struct memleak_item, avl);
150 : 0 : a = avl_next(a);
151 : 0 : avl_remove(&tree_index, &item->avl);
152 : 0 : is_leaked = 1;
153 : :
154 : : #ifdef _CHK_MODIFY_AFTER_FREE
155 : : int i;
156 : : uint8_t *addr;
157 : : if (item->freed) {
158 : : is_leaked = 0;
159 : : addr = (uint8_t*)item->addr;
160 : : for (i=0;i<item->size;++i){
161 : : if (*(addr + i) != FREE_VAL) {
162 : : fprintf(stderr,
163 : : "address 0x%016lx (allocated at %s:%lu, size %lu) "
164 : : "has been modified after being freed\n",
165 : : (unsigned long)item->addr, item->file,
166 : : item->line, item->size);
167 : : break;
168 : : }
169 : : }
170 : : free(addr);
171 : : }
172 : : #endif // _CHK_MODIFY_AFTER_FREE
173 : :
174 [ # # ]: 0 : if (is_leaked) {
175 : : fprintf(stderr, "address 0x%016lx (allocated at %s:%lu, size %lu) "
176 : : "is not freed\n",
177 : : (unsigned long)item->addr, item->file,
178 : 0 : (unsigned long)item->line, (unsigned long)item->size);
179 : 0 : count++;
180 : : #ifdef _STACK_BACKTRACE
181 : : strs = backtrace_symbols(item->btrace, item->bt_size);
182 : : for (i=0;i<item->bt_size;++i){
183 : : #ifdef _LIBBFD
184 : : resolve(item->btrace[i], &file, &func, &line);
185 : : fprintf(stderr, " %s [%s:%d]\n", func, file, line);
186 : : #else // _LIBBFD
187 : : fprintf(stderr, " %s\n", strs[i]);
188 : : #endif // _LIBBFD
189 : : }
190 : : free(item->btrace);
191 : : #endif // _STACK_BACKTRACE
192 : : }
193 : 0 : free(item);
194 : : }
195 [ - + ]: 185 : if (count > 0) fprintf(stderr, "total %d objects\n", (int)count);
196 : :
197 : 185 : spin_unlock(&lock);
198 : 185 : }
199 : :
200 : 0 : void _memleak_add_to_index(void *addr, size_t size, char *file, size_t line, uint8_t init_val)
201 : : {
202 : : DBG("malloc at %s:%ld, size %ld\n", file, line, size);
203 : 0 : struct memleak_item *item = (struct memleak_item *)malloc(sizeof(struct memleak_item));
204 : 0 : item->addr = (uint64_t)addr;
205 : 0 : item->file = file;
206 : 0 : item->line = line;
207 : 0 : item->size = size;
208 : : #ifdef INIT_VAL
209 : 0 : memset(addr, init_val, size);
210 : : #endif
211 : : #ifdef _STACK_BACKTRACE
212 : : void *temp_stack[256];
213 : : item->bt_size = backtrace(temp_stack, 256);
214 : : item->btrace = (void**)malloc(sizeof(void*) * item->bt_size);
215 : : memcpy(item->btrace, temp_stack, sizeof(void*) * item->bt_size);
216 : : #endif
217 : : #ifdef _CHK_MODIFY_AFTER_FREE
218 : : item->freed = 0;
219 : : #endif
220 : 0 : avl_insert(&tree_index, &item->avl, memleak_cmp);
221 : 0 : }
222 : :
223 : : LIBMEMLEAK_API
224 : 0 : void * memleak_alloc(size_t size, char *file, size_t line)
225 : : {
226 : 0 : void *addr = (void*)malloc(size);
227 [ # # ][ # # ]: 0 : if (addr && start_sw) {
228 : 0 : spin_lock(&lock);
229 : 0 : _memleak_add_to_index(addr, size, file, line, INIT_VAL);
230 : 0 : spin_unlock(&lock);
231 : : }
232 : :
233 : 0 : return addr;
234 : : }
235 : :
236 : : LIBMEMLEAK_API
237 : 0 : void * memleak_calloc(size_t nmemb, size_t size, char *file, size_t line)
238 : : {
239 : 0 : void *addr = (void *)calloc(nmemb, size);
240 [ # # ][ # # ]: 0 : if (addr && start_sw) {
241 : 0 : spin_lock(&lock);
242 : 0 : _memleak_add_to_index(addr, size, file, line, 0x0);
243 : 0 : spin_unlock(&lock);
244 : : }
245 : :
246 : 0 : return addr;
247 : : }
248 : :
249 : : #if !defined(WIN32)
250 : :
251 : : #if !defined(__ANDROID__)
252 : : // posix only
253 : : LIBMEMLEAK_API
254 : 0 : int memleak_posix_memalign(void **memptr, size_t alignment, size_t size, char *file, size_t line)
255 : : {
256 : :
257 : 0 : int ret = posix_memalign(memptr, alignment, size);
258 [ # # ][ # # ]: 0 : if (ret==0 && start_sw)
259 : : {
260 : 0 : spin_lock(&lock);
261 : 0 : _memleak_add_to_index(*memptr, size, file, line, INIT_VAL);
262 : 0 : spin_unlock(&lock);
263 : : }
264 : :
265 : 0 : return ret;
266 : : }
267 : : #else // not __ANDROID__
268 : : LIBMEMLEAK_API
269 : : void * memleak_memalign(size_t size, size_t alignment, char *file, size_t line)
270 : : {
271 : : void *addr = memalign(alignment, size);
272 : : if (addr && start_sw)
273 : : {
274 : : spin_lock(&lock);
275 : : _memleak_add_to_index(addr, size, file, line, INIT_VAL);
276 : : spin_unlock(&lock);
277 : : }
278 : : return addr;
279 : : }
280 : : #endif
281 : :
282 : : #else // not WIN32
283 : :
284 : : LIBMEMLEAK_API
285 : : void * memleak_aligned_malloc(size_t size, size_t alignment, char *file, size_t line)
286 : : {
287 : : void *addr = (void*)_aligned_malloc(size, alignment);
288 : : if (addr && start_sw) {
289 : : spin_lock(&lock);
290 : : _memleak_add_to_index(addr, size, file, line, INIT_VAL);
291 : : spin_unlock(&lock);
292 : : }
293 : : return addr;
294 : : }
295 : :
296 : : LIBMEMLEAK_API
297 : : void memleak_aligned_free(void *addr, char *file, size_t line)
298 : : {
299 : : struct avl_node *a;
300 : : struct memleak_item *item, query;
301 : :
302 : : if (start_sw) {
303 : : spin_lock(&lock);
304 : :
305 : : query.addr = (uint64_t)addr;
306 : : a = avl_search(&tree_index, &query.avl, memleak_cmp);
307 : : if (!a) {
308 : : #ifdef _WARN_NOT_ALLOCATED_MEMORY
309 : : fprintf(stderr, "try to free not allocated memory address 0x%016lx at %s:%ld\n",
310 : : (long unsigned int)addr, file, line);
311 : : #endif
312 : : spin_unlock(&lock);
313 : : return;
314 : : }
315 : :
316 : : item = _get_entry(a, struct memleak_item, avl);
317 : : DBG("free address 0x%016lx (allocated at %s:%ld, size %ld)\n",
318 : : item->addr, item->file, item->line, item->size);
319 : : #ifdef FREE_VAL
320 : : memset(addr, FREE_VAL, item->size);
321 : : #endif
322 : :
323 : : avl_remove(&tree_index, a);
324 : : #ifdef _STACK_BACKTRACE
325 : : free(item->btrace);
326 : : #endif
327 : : free(item);
328 : : spin_unlock(&lock);
329 : : }
330 : : _aligned_free(addr);
331 : : }
332 : :
333 : : #endif // not WIN32
334 : :
335 : : LIBMEMLEAK_API
336 : 0 : void *memleak_realloc(void *ptr, size_t size)
337 : : {
338 : :
339 : 0 : void *addr = (void *)realloc(ptr, size);
340 [ # # ][ # # ]: 0 : if (addr && start_sw) {
341 : 0 : spin_lock(&lock);
342 : : struct avl_node *a;
343 : : struct memleak_item *item, query;
344 : :
345 : 0 : query.addr = (uint64_t)ptr;
346 : 0 : a = avl_search(&tree_index, &query.avl, memleak_cmp);
347 [ # # ]: 0 : if (a) {
348 : 0 : item = _get_entry(a, struct memleak_item, avl);
349 : : DBG("realloc from address 0x%016lx (allocated at %s:%ld, size %ld)\n\tto address 0x%016lx (size %ld)\n",
350 : : item->addr, item->file, item->line, item->size, (uint64_t)addr, size);
351 : 0 : avl_remove(&tree_index, a);
352 : 0 : _memleak_add_to_index(addr, size, item->file, item->line, INIT_VAL);
353 : : #ifdef _STACK_BACKTRACE
354 : : free(item->btrace);
355 : : #endif
356 : 0 : free(item);
357 : : }
358 : 0 : spin_unlock(&lock);
359 : : }
360 : :
361 : 0 : return addr;
362 : : }
363 : :
364 : : LIBMEMLEAK_API
365 : 0 : void memleak_free(void *addr, char *file, size_t line)
366 : : {
367 : : struct avl_node *a;
368 : : struct memleak_item *item, query;
369 : :
370 [ # # ]: 0 : if (start_sw) {
371 : 0 : spin_lock(&lock);
372 : :
373 : 0 : query.addr = (uint64_t)addr;
374 : 0 : a = avl_search(&tree_index, &query.avl, memleak_cmp);
375 [ # # ]: 0 : if (!a) {
376 : : #ifdef _WARN_NOT_ALLOCATED_MEMORY
377 : : fprintf(stderr, "try to free not allocated memory address 0x%016lx at %s:%ld\n",
378 : : (long unsigned int)addr, file, line);
379 : : #endif
380 : 0 : spin_unlock(&lock);
381 : 0 : return;
382 : : }
383 : :
384 : 0 : item = _get_entry(a, struct memleak_item, avl);
385 : : DBG("free address 0x%016lx (allocated at %s:%ld, size %ld)\n",
386 : : item->addr, item->file, item->line, item->size);
387 : : #ifdef FREE_VAL
388 : 0 : memset(addr, FREE_VAL, item->size);
389 : : #endif
390 : :
391 : : #ifdef _STACK_BACKTRACE
392 : : free(item->btrace);
393 : : #endif
394 : :
395 : : #ifndef _CHK_MODIFY_AFTER_FREE
396 : 0 : avl_remove(&tree_index, a);
397 : 0 : free(item);
398 : : #else
399 : : item->freed = 1;
400 : : #endif
401 : 0 : spin_unlock(&lock);
402 : : }
403 : : #ifndef _CHK_MODIFY_AFTER_FREE
404 : 0 : free(addr);
405 : : #endif
406 : : }
407 : :
|