2 * Copyright (C) 2006-2008 Martin Willi
3 * Hochschule fuer Technik Rapperswil
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
23 #include <sys/socket.h>
24 #include <netinet/in.h>
25 #include <arpa/inet.h>
33 #include "leak_detective.h"
37 #include <utils/backtrace.h>
39 typedef struct private_leak_detective_t private_leak_detective_t
;
42 * private data of leak_detective
44 struct private_leak_detective_t
{
49 leak_detective_t
public;
53 * Magic value which helps to detect memory corruption. Yummy!
55 #define MEMORY_HEADER_MAGIC 0x7ac0be11
58 * Magic written to tail of allocation
60 #define MEMORY_TAIL_MAGIC 0xcafebabe
63 * Pattern which is filled in memory before freeing it
65 #define MEMORY_FREE_PATTERN 0xFF
68 * Pattern which is filled in newly allocated memory
70 #define MEMORY_ALLOC_PATTERN 0xEE
73 static void install_hooks(void);
74 static void uninstall_hooks(void);
75 static void *malloc_hook(size_t, const void *);
76 static void *realloc_hook(void *, size_t, const void *);
77 static void free_hook(void*, const void *);
79 void *(*old_malloc_hook
)(size_t, const void *);
80 void *(*old_realloc_hook
)(void *, size_t, const void *);
81 void (*old_free_hook
)(void*, const void *);
83 static u_int count_malloc
= 0;
84 static u_int count_free
= 0;
85 static u_int count_realloc
= 0;
87 typedef struct memory_header_t memory_header_t
;
88 typedef struct memory_tail_t memory_tail_t
;
91 * Header which is prepended to each allocated memory block
93 struct memory_header_t
{
96 * Number of bytes following after the header
101 * Pointer to previous entry in linked list
103 memory_header_t
*previous
;
106 * Pointer to next entry in linked list
108 memory_header_t
*next
;
111 * backtrace taken during (re-)allocation
113 backtrace_t
*backtrace
;
116 * magic bytes to detect bad free or heap underflow, MEMORY_HEADER_MAGIC
120 }__attribute__((__packed__
));
123 * tail appended to each allocated memory block
125 struct memory_tail_t
{
128 * Magic bytes to detect heap overflow, MEMORY_TAIL_MAGIC
132 }__attribute__((__packed__
));
135 * first mem header is just a dummy to chain
136 * the others on it...
138 static memory_header_t first_header
= {
139 magic
: MEMORY_HEADER_MAGIC
,
147 * are the hooks currently installed?
149 static bool installed
= FALSE
;
152 * Leak report white list
154 * List of functions using static allocation buffers or should be suppressed
155 * otherwise on leak report.
157 char *whitelist
[] = {
158 /* backtraces, including own */
162 "pthread_setspecific",
163 "__pthread_setspecific",
164 /* glibc functions */
178 "register_printf_function",
183 /* ignore dlopen, as we do not dlclose to get proper leak reports */
187 /* mysql functions */
188 "mysql_init_character_set",
191 /* fastcgi library */
194 "xmlInitCharEncodingHandlers",
202 "ENGINE_load_builtin_engines",
207 * check if a stack frame contains functions listed above
209 static bool is_whitelisted(backtrace_t
*backtrace
)
212 for (i
= 0; i
< sizeof(whitelist
)/sizeof(char*); i
++)
214 if (backtrace
->contains_function(backtrace
, whitelist
[i
]))
223 * Report leaks at library destruction
227 memory_header_t
*hdr
;
228 int leaks
= 0, whitelisted
= 0;
230 for (hdr
= first_header
.next
; hdr
!= NULL
; hdr
= hdr
->next
)
232 if (is_whitelisted(hdr
->backtrace
))
238 fprintf(stderr
, "Leak (%d bytes at %p):\n", hdr
->bytes
, hdr
+ 1);
239 /* skip the first frame, contains leak detective logic */
240 hdr
->backtrace
->log(hdr
->backtrace
, stderr
);
248 fprintf(stderr
, "No leaks detected");
251 fprintf(stderr
, "One leak detected");
254 fprintf(stderr
, "%d leaks detected", leaks
);
257 fprintf(stderr
, ", %d suppressed by whitelist\n", whitelisted
);
261 * Installs the malloc hooks, enables leak detection
263 static void install_hooks()
267 old_malloc_hook
= __malloc_hook
;
268 old_realloc_hook
= __realloc_hook
;
269 old_free_hook
= __free_hook
;
270 __malloc_hook
= malloc_hook
;
271 __realloc_hook
= realloc_hook
;
272 __free_hook
= free_hook
;
278 * Uninstalls the malloc hooks, disables leak detection
280 static void uninstall_hooks()
284 __malloc_hook
= old_malloc_hook
;
285 __free_hook
= old_free_hook
;
286 __realloc_hook
= old_realloc_hook
;
292 * Hook function for malloc()
294 void *malloc_hook(size_t bytes
, const void *caller
)
296 memory_header_t
*hdr
;
298 pthread_t thread_id
= pthread_self();
300 struct sched_param oldparams
, params
;
302 pthread_getschedparam(thread_id
, &oldpolicy
, &oldparams
);
304 params
.__sched_priority
= sched_get_priority_max(SCHED_FIFO
);
305 pthread_setschedparam(thread_id
, SCHED_FIFO
, ¶ms
);
309 hdr
= malloc(sizeof(memory_header_t
) + bytes
+ sizeof(memory_tail_t
));
310 tail
= ((void*)hdr
) + bytes
+ sizeof(memory_header_t
);
311 /* set to something which causes crashes */
312 memset(hdr
, MEMORY_ALLOC_PATTERN
,
313 sizeof(memory_header_t
) + bytes
+ sizeof(memory_tail_t
));
315 hdr
->magic
= MEMORY_HEADER_MAGIC
;
317 hdr
->backtrace
= backtrace_create(3);
318 tail
->magic
= MEMORY_TAIL_MAGIC
;
321 /* insert at the beginning of the list */
322 hdr
->next
= first_header
.next
;
325 hdr
->next
->previous
= hdr
;
327 hdr
->previous
= &first_header
;
328 first_header
.next
= hdr
;
330 pthread_setschedparam(thread_id
, oldpolicy
, &oldparams
);
336 * Hook function for free()
338 void free_hook(void *ptr
, const void *caller
)
340 memory_header_t
*hdr
;
342 backtrace_t
*backtrace
;
343 pthread_t thread_id
= pthread_self();
345 struct sched_param oldparams
, params
;
347 /* allow freeing of NULL */
352 hdr
= ptr
- sizeof(memory_header_t
);
353 tail
= ptr
+ hdr
->bytes
;
355 pthread_getschedparam(thread_id
, &oldpolicy
, &oldparams
);
357 params
.__sched_priority
= sched_get_priority_max(SCHED_FIFO
);
358 pthread_setschedparam(thread_id
, SCHED_FIFO
, ¶ms
);
362 if (hdr
->magic
!= MEMORY_HEADER_MAGIC
||
363 tail
->magic
!= MEMORY_TAIL_MAGIC
)
365 fprintf(stderr
, "freeing invalid memory (%p): "
366 "header magic 0x%x, tail magic 0x%x:\n",
367 ptr
, hdr
->magic
, tail
->magic
);
368 backtrace
= backtrace_create(3);
369 backtrace
->log(backtrace
, stderr
);
370 backtrace
->destroy(backtrace
);
374 /* remove item from list */
377 hdr
->next
->previous
= hdr
->previous
;
379 hdr
->previous
->next
= hdr
->next
;
380 hdr
->backtrace
->destroy(hdr
->backtrace
);
382 /* clear MAGIC, set mem to something remarkable */
383 memset(hdr
, MEMORY_FREE_PATTERN
, hdr
->bytes
+ sizeof(memory_header_t
));
389 pthread_setschedparam(thread_id
, oldpolicy
, &oldparams
);
393 * Hook function for realloc()
395 void *realloc_hook(void *old
, size_t bytes
, const void *caller
)
397 memory_header_t
*hdr
;
399 backtrace_t
*backtrace
;
400 pthread_t thread_id
= pthread_self();
402 struct sched_param oldparams
, params
;
404 /* allow reallocation of NULL */
407 return malloc_hook(bytes
, caller
);
410 hdr
= old
- sizeof(memory_header_t
);
411 tail
= old
+ hdr
->bytes
;
413 pthread_getschedparam(thread_id
, &oldpolicy
, &oldparams
);
415 params
.__sched_priority
= sched_get_priority_max(SCHED_FIFO
);
416 pthread_setschedparam(thread_id
, SCHED_FIFO
, ¶ms
);
420 if (hdr
->magic
!= MEMORY_HEADER_MAGIC
||
421 tail
->magic
!= MEMORY_TAIL_MAGIC
)
423 fprintf(stderr
, "reallocating invalid memory (%p): "
424 "header magic 0x%x, tail magic 0x%x:\n",
425 old
, hdr
->magic
, tail
->magic
);
426 backtrace
= backtrace_create(3);
427 backtrace
->log(backtrace
, stderr
);
428 backtrace
->destroy(backtrace
);
430 /* clear tail magic, allocate, set tail magic */
431 memset(&tail
->magic
, MEMORY_ALLOC_PATTERN
, sizeof(tail
->magic
));
432 hdr
= realloc(hdr
, sizeof(memory_header_t
) + bytes
+ sizeof(memory_tail_t
));
433 tail
= ((void*)hdr
) + bytes
+ sizeof(memory_header_t
);
434 tail
->magic
= MEMORY_TAIL_MAGIC
;
436 /* update statistics */
438 hdr
->backtrace
->destroy(hdr
->backtrace
);
439 hdr
->backtrace
= backtrace_create(3);
441 /* update header of linked list neighbours */
444 hdr
->next
->previous
= hdr
;
446 hdr
->previous
->next
= hdr
;
448 pthread_setschedparam(thread_id
, oldpolicy
, &oldparams
);
453 * Implementation of leak_detective_t.destroy
455 static void destroy(private_leak_detective_t
*this)
468 leak_detective_t
*leak_detective_create()
470 private_leak_detective_t
*this = malloc_thing(private_leak_detective_t
);
472 this->public.destroy
= (void(*)(leak_detective_t
*))destroy
;
474 if (getenv("LEAK_DETECTIVE_DISABLE") == NULL
)
476 lib
->leak_detective
= TRUE
;
479 return &this->public;