[memleak] Add memleak directory and script to start program with it
[lttng-tools.git] / memleak / lttng-memleak-finder.c
1 /*
2 * lttng-memleak-finder.c
3 *
4 * LTTng memory leak finder
5 *
6 * Copyright (c) 2013 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22
23 #define _GNU_SOURCE
24 #include <dlfcn.h>
25 #include <sys/types.h>
26 #include <stdio.h>
27 #include <pthread.h>
28 #include <string.h>
29 #include <stdint.h>
30 #include <stdlib.h>
31 #include <assert.h>
32 #include "hlist.h"
33
34 #include "jhash.h"
35
36 static volatile int print_to_console;
37
38 static pthread_mutex_t mh_mutex = PTHREAD_MUTEX_INITIALIZER;
39
40 static void *(*callocp)(size_t, size_t);
41 static void *(*mallocp)(size_t);
42 static void *(*reallocp)(void *, size_t);
43 static void *(*memalignp)(size_t, size_t);
44 static void (*freep)(void *);
45
46 static volatile int initialized;
47 static __thread int thread_in_hook;
48
49 #define STATIC_CALLOC_LEN 4096
50 static char static_calloc_buf[STATIC_CALLOC_LEN];
51 static size_t static_calloc_len;
52
53 #define MH_HASH_BITS 20 /* 1 M entries, hardcoded for now */
54 #define MH_TABLE_SIZE (1 << MH_HASH_BITS)
55 static struct cds_hlist_head mh_table[MH_TABLE_SIZE];
56
57 struct mh_entry {
58 struct cds_hlist_node hlist;
59 void *ptr;
60 const void *alloc_caller;
61 char *caller_symbol;
62 size_t alloc_size;
63 };
64
65 static struct mh_entry *
66 get_mh(const void *ptr)
67 {
68 struct cds_hlist_head *head;
69 struct cds_hlist_node *node;
70 struct mh_entry *e;
71 uint32_t hash;
72
73 hash = jhash(&ptr, sizeof(ptr), 0);
74 head = &mh_table[hash & (MH_TABLE_SIZE - 1)];
75 cds_hlist_for_each_entry(e, node, head, hlist) {
76 if (ptr == e->ptr)
77 return e;
78 }
79 return NULL;
80 }
81
82 static void
83 add_mh(void *ptr, size_t alloc_size, const void *caller)
84 {
85 struct cds_hlist_head *head;
86 struct cds_hlist_node *node;
87 struct mh_entry *e;
88 uint32_t hash;
89 Dl_info info;
90
91 if (!ptr)
92 return;
93 hash = jhash(&ptr, sizeof(ptr), 0);
94 head = &mh_table[hash & (MH_TABLE_SIZE - 1)];
95 cds_hlist_for_each_entry(e, node, head, hlist) {
96 if (ptr == e->ptr) {
97 fprintf(stderr, "[warning] add_mh pointer %p is already there\n",
98 ptr);
99 //assert(0); /* already there */
100 }
101 }
102 e = malloc(sizeof(*e));
103 e->ptr = ptr;
104 e->alloc_caller = caller;
105 e->alloc_size = alloc_size;
106 if (dladdr(caller, &info) && info.dli_sname) {
107 e->caller_symbol = strdup(info.dli_sname);
108 } else {
109 e->caller_symbol = NULL;
110 }
111 cds_hlist_add_head(&e->hlist, head);
112 }
113
114 static void
115 del_mh(void *ptr, const void *caller)
116 {
117 struct mh_entry *e;
118
119 if (!ptr)
120 return;
121 e = get_mh(ptr);
122 if (!e) {
123 fprintf(stderr,
124 "[warning] trying to free unallocated ptr %p caller %p\n",
125 ptr, caller);
126 return;
127 }
128 cds_hlist_del(&e->hlist);
129 free(e->caller_symbol);
130 free(e);
131 }
132
133 static void __attribute__((constructor))
134 do_init(void)
135 {
136 char *env;
137
138 if (initialized)
139 return;
140 callocp = (void *(*) (size_t, size_t)) dlsym (RTLD_NEXT, "calloc");
141 mallocp = (void *(*) (size_t)) dlsym (RTLD_NEXT, "malloc");
142 reallocp = (void *(*) (void *, size_t)) dlsym (RTLD_NEXT, "realloc");
143 memalignp = (void *(*)(size_t, size_t)) dlsym (RTLD_NEXT, "memalign");
144 freep = (void (*) (void *)) dlsym (RTLD_NEXT, "free");
145
146 env = getenv("LTTNG_MEMLEAK_PRINT");
147 if (env && strcmp(env, "1") == 0)
148 print_to_console = 1;
149
150 initialized = 1;
151 }
152
153 static
154 void *static_calloc(size_t nmemb, size_t size)
155 {
156 size_t prev_len;
157
158 if (nmemb * size > sizeof(static_calloc_buf) - static_calloc_len)
159 return NULL;
160 prev_len = static_calloc_len;
161 static_calloc_len += nmemb + size;
162 return &static_calloc_buf[prev_len];
163 }
164
165 void *
166 calloc(size_t nmemb, size_t size)
167 {
168 void *result;
169 const void *caller = __builtin_return_address(0);
170
171 if (callocp == NULL) {
172 return static_calloc(nmemb, size);
173 }
174
175 do_init();
176
177 if (thread_in_hook) {
178 return callocp(nmemb, size);
179 }
180
181 thread_in_hook = 1;
182
183 pthread_mutex_lock(&mh_mutex);
184
185 /* Call resursively */
186 result = callocp(nmemb, size);
187
188 add_mh(result, nmemb * size, caller);
189
190 /* printf might call malloc, so protect it too. */
191 if (print_to_console)
192 fprintf(stderr, "calloc(%zu,%zu) returns %p\n", nmemb, size, result);
193
194 pthread_mutex_unlock(&mh_mutex);
195
196 thread_in_hook = 0;
197
198 return result;
199 }
200
201 void *
202 malloc(size_t size)
203 {
204 void *result;
205 const void *caller = __builtin_return_address(0);
206
207 do_init();
208
209 if (thread_in_hook) {
210 return mallocp(size);
211 }
212
213 thread_in_hook = 1;
214
215 pthread_mutex_lock(&mh_mutex);
216
217 /* Call resursively */
218 result = mallocp(size);
219
220 add_mh(result, size, caller);
221
222 /* printf might call malloc, so protect it too. */
223 if (print_to_console)
224 fprintf(stderr, "malloc(%zu) returns %p\n", size, result);
225
226 pthread_mutex_unlock(&mh_mutex);
227
228 thread_in_hook = 0;
229
230 return result;
231 }
232
233 void *
234 realloc(void *ptr, size_t size)
235 {
236 void *result;
237 const void *caller = __builtin_return_address(0);
238
239 do_init();
240
241 if (thread_in_hook) {
242 return reallocp(ptr, size);
243 }
244
245 thread_in_hook = 1;
246
247 pthread_mutex_lock(&mh_mutex);
248
249 /* Call resursively */
250 result = reallocp(ptr, size);
251
252 if (size == 0 && ptr) {
253 /* equivalent to free() */
254 del_mh(ptr, caller);
255 } else if (result) {
256 del_mh(ptr, caller);
257 add_mh(result, size, caller);
258 }
259
260 /* printf might call malloc, so protect it too. */
261 if (print_to_console)
262 fprintf(stderr, "realloc(%p,%zu) returns %p\n", ptr, size, result);
263
264 pthread_mutex_unlock(&mh_mutex);
265
266 thread_in_hook = 0;
267
268 return result;
269 }
270
271 void *
272 memalign(size_t alignment, size_t size)
273 {
274 void *result;
275 const void *caller = __builtin_return_address(0);
276
277 do_init();
278
279 if (thread_in_hook) {
280 return memalignp(alignment, size);
281 }
282
283 thread_in_hook = 1;
284
285 pthread_mutex_lock(&mh_mutex);
286
287 /* Call resursively */
288 result = memalignp(alignment, size);
289
290 add_mh(result, size, caller);
291
292 /* printf might call malloc, so protect it too. */
293 if (print_to_console)
294 fprintf(stderr, "memalign(%zu,%zu) returns %p\n",
295 alignment, size, result);
296
297 pthread_mutex_unlock(&mh_mutex);
298
299 thread_in_hook = 0;
300
301 return result;
302 }
303
304 void
305 free(void *ptr)
306 {
307 const void *caller = __builtin_return_address(0);
308
309 do_init();
310
311 if (thread_in_hook) {
312 freep(ptr);
313 return;
314 }
315
316 thread_in_hook = 1;
317 pthread_mutex_lock(&mh_mutex);
318
319 /* Call resursively */
320 freep(ptr);
321
322 del_mh(ptr, caller);
323
324 /* printf might call free, so protect it too. */
325 if (print_to_console)
326 fprintf(stderr, "freed pointer %p\n", ptr);
327
328 pthread_mutex_unlock(&mh_mutex);
329 thread_in_hook = 0;
330 }
331
332 static __attribute__((destructor))
333 void print_leaks(void)
334 {
335 unsigned long i;
336
337 for (i = 0; i < MH_TABLE_SIZE; i++) {
338 struct cds_hlist_head *head;
339 struct cds_hlist_node *node;
340 struct mh_entry *e;
341
342 head = &mh_table[i];
343 cds_hlist_for_each_entry(e, node, head, hlist) {
344 fprintf(stderr, "[leak] ptr: %p size: %zu caller: %p <%s>\n",
345 e->ptr, e->alloc_size, e->alloc_caller,
346 e->caller_symbol);
347 }
348 }
349 }
This page took 0.037007 seconds and 4 git commands to generate.