2 * lttng-memleak-finder.c
4 * LTTng memory leak finder
6 * Copyright (c) 2013 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
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.
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.
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
25 #include <sys/types.h>
36 static volatile int print_to_console
;
38 static pthread_mutex_t mh_mutex
= PTHREAD_MUTEX_INITIALIZER
;
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 *);
46 static volatile int initialized
;
47 static __thread
int thread_in_hook
;
49 #define STATIC_CALLOC_LEN 4096
50 static char static_calloc_buf
[STATIC_CALLOC_LEN
];
51 static size_t static_calloc_len
;
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
];
58 struct cds_hlist_node hlist
;
60 const void *alloc_caller
;
65 static struct mh_entry
*
66 get_mh(const void *ptr
)
68 struct cds_hlist_head
*head
;
69 struct cds_hlist_node
*node
;
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
) {
83 add_mh(void *ptr
, size_t alloc_size
, const void *caller
)
85 struct cds_hlist_head
*head
;
86 struct cds_hlist_node
*node
;
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
) {
97 fprintf(stderr
, "[warning] add_mh pointer %p is already there\n",
99 //assert(0); /* already there */
102 e
= malloc(sizeof(*e
));
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
);
109 e
->caller_symbol
= NULL
;
111 cds_hlist_add_head(&e
->hlist
, head
);
115 del_mh(void *ptr
, const void *caller
)
124 "[warning] trying to free unallocated ptr %p caller %p\n",
128 cds_hlist_del(&e
->hlist
);
129 free(e
->caller_symbol
);
133 static void __attribute__((constructor
))
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");
146 env
= getenv("LTTNG_MEMLEAK_PRINT");
147 if (env
&& strcmp(env
, "1") == 0)
148 print_to_console
= 1;
154 void *static_calloc(size_t nmemb
, size_t size
)
158 if (nmemb
* size
> sizeof(static_calloc_buf
) - static_calloc_len
)
160 prev_len
= static_calloc_len
;
161 static_calloc_len
+= nmemb
+ size
;
162 return &static_calloc_buf
[prev_len
];
166 calloc(size_t nmemb
, size_t size
)
169 const void *caller
= __builtin_return_address(0);
171 if (callocp
== NULL
) {
172 return static_calloc(nmemb
, size
);
177 if (thread_in_hook
) {
178 return callocp(nmemb
, size
);
183 pthread_mutex_lock(&mh_mutex
);
185 /* Call resursively */
186 result
= callocp(nmemb
, size
);
188 add_mh(result
, nmemb
* size
, caller
);
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
);
194 pthread_mutex_unlock(&mh_mutex
);
205 const void *caller
= __builtin_return_address(0);
209 if (thread_in_hook
) {
210 return mallocp(size
);
215 pthread_mutex_lock(&mh_mutex
);
217 /* Call resursively */
218 result
= mallocp(size
);
220 add_mh(result
, size
, caller
);
222 /* printf might call malloc, so protect it too. */
223 if (print_to_console
)
224 fprintf(stderr
, "malloc(%zu) returns %p\n", size
, result
);
226 pthread_mutex_unlock(&mh_mutex
);
234 realloc(void *ptr
, size_t size
)
237 const void *caller
= __builtin_return_address(0);
241 if (thread_in_hook
) {
242 return reallocp(ptr
, size
);
247 pthread_mutex_lock(&mh_mutex
);
249 /* Call resursively */
250 result
= reallocp(ptr
, size
);
252 if (size
== 0 && ptr
) {
253 /* equivalent to free() */
257 add_mh(result
, size
, caller
);
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
);
264 pthread_mutex_unlock(&mh_mutex
);
272 memalign(size_t alignment
, size_t size
)
275 const void *caller
= __builtin_return_address(0);
279 if (thread_in_hook
) {
280 return memalignp(alignment
, size
);
285 pthread_mutex_lock(&mh_mutex
);
287 /* Call resursively */
288 result
= memalignp(alignment
, size
);
290 add_mh(result
, size
, caller
);
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
);
297 pthread_mutex_unlock(&mh_mutex
);
307 const void *caller
= __builtin_return_address(0);
311 if (thread_in_hook
) {
317 pthread_mutex_lock(&mh_mutex
);
319 /* Call resursively */
324 /* printf might call free, so protect it too. */
325 if (print_to_console
)
326 fprintf(stderr
, "freed pointer %p\n", ptr
);
328 pthread_mutex_unlock(&mh_mutex
);
332 static __attribute__((destructor
))
333 void print_leaks(void)
337 for (i
= 0; i
< MH_TABLE_SIZE
; i
++) {
338 struct cds_hlist_head
*head
;
339 struct cds_hlist_node
*node
;
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
,