2 * @file leak_detective.c
4 * @brief Allocation hooks to find memory leaks.
8 * Copyright (C) 2006 Martin Willi
9 * Hochschule fuer Technik Rapperswil
11 * This program is free software; you can redistribute it and/or modify it
12 * under the terms of the GNU General Public License as published by the
13 * Free Software Foundation; either version 2 of the License, or (at your
14 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
16 * This program is distributed in the hope that it will be useful, but
17 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
18 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
27 #include <sys/socket.h>
28 #include <netinet/in.h>
29 #include <arpa/inet.h>
37 # include <execinfo.h>
38 #endif /* HAVE_BACKTRACE */
40 #include "leak_detective.h"
48 * Magic value which helps to detect memory corruption. Yummy!
50 #define MEMORY_HEADER_MAGIC 0x7ac0be11
53 * Pattern which is filled in memory before freeing it
55 #define MEMORY_FREE_PATTERN 0xFF
58 * Pattern which is filled in newly allocated memory
60 #define MEMORY_ALLOC_PATTERN 0xEE
63 static void install_hooks(void);
64 static void uninstall_hooks(void);
65 static void *malloc_hook(size_t, const void *);
66 static void *realloc_hook(void *, size_t, const void *);
67 static void free_hook(void*, const void *);
69 static u_int count_malloc
= 0;
70 static u_int count_free
= 0;
71 static u_int count_realloc
= 0;
73 typedef struct memory_header_t memory_header_t
;
76 * Header which is prepended to each allocated memory block
78 struct memory_header_t
{
80 * Magci byte which must(!) hold MEMORY_HEADER_MAGIC
85 * Number of bytes following after the header
90 * Stack frames at the time of allocation
92 void *stack_frames
[STACK_FRAMES_COUNT
];
95 * Number of stacks frames obtained in stack_frames
97 int stack_frame_count
;
100 * Pointer to previous entry in linked list
102 memory_header_t
*previous
;
105 * Pointer to next entry in linked list
107 memory_header_t
*next
;
111 * first mem header is just a dummy to chain
112 * the others on it...
114 static memory_header_t first_header
= {
115 magic
: MEMORY_HEADER_MAGIC
,
117 stack_frame_count
: 0,
123 * standard hooks, used to temparily remove hooking
125 static void *old_malloc_hook
, *old_realloc_hook
, *old_free_hook
;
128 * are the hooks currently installed?
130 static bool installed
= FALSE
;
133 * Mutex to exclusivly uninstall hooks, access heap list
135 static pthread_mutex_t mutex
= PTHREAD_MUTEX_INITIALIZER
;
139 * log stack frames queried by backtrace()
140 * TODO: Dump symbols of static functions. This could be done with
141 * the addr2line utility or the GNU BFD Library...
143 static void log_stack_frames(void **stack_frames
, int stack_frame_count
)
145 #ifdef HAVE_BACKTRACE
149 strings
= backtrace_symbols (stack_frames
, stack_frame_count
);
151 DBG1(" dumping %d stack frame addresses", stack_frame_count
);
153 for (i
= 0; i
< stack_frame_count
; i
++)
155 DBG1(" %s", strings
[i
]);
158 #endif /* HAVE_BACKTRACE */
162 * Whitelist, which contains address ranges in stack frames ignored when leaking.
164 * This is necessary, as some function use allocation hacks (static buffers)
165 * and so on, which we want to suppress on leak reports.
167 * The range_size is calculated using the readelf utility, e.g.:
168 * readelf -s /lib/glibc.so.6
169 * The values are for glibc-2.4 and may or may not be correct on other systems.
171 typedef struct whitelist_t whitelist_t
;
179 /* dummy declaration for whitelisting */
180 void *Curl_getaddrinfo(void);
183 whitelist_t whitelist
[] = {
184 {pthread_create
, 2542},
185 {pthread_setspecific
, 217},
190 {getprotobynumber
, 291},
191 {getservbyport
, 311},
192 {register_printf_function
, 159},
196 /* from /usr/lib/libcurl.so.3 */
197 {Curl_getaddrinfo
, 480},
198 # endif /* LIBCURL */
202 * Check if this stack frame is whitelisted.
204 static bool is_whitelisted(void **stack_frames
, int stack_frame_count
)
208 for (i
=0; i
< stack_frame_count
; i
++)
210 for (j
=0; j
<sizeof(whitelist
)/sizeof(whitelist_t
); j
++)
212 if (stack_frames
[i
] >= whitelist
[j
].range_start
&&
213 stack_frames
[i
] <= (whitelist
[j
].range_start
+ whitelist
[j
].range_size
))
223 * Report leaks at library destruction
227 memory_header_t
*hdr
;
230 for (hdr
= first_header
.next
; hdr
!= NULL
; hdr
= hdr
->next
)
232 if (!is_whitelisted(hdr
->stack_frames
, hdr
->stack_frame_count
))
234 DBG1("Leak (%d bytes at %p):", hdr
->bytes
, hdr
+ 1);
235 log_stack_frames(hdr
->stack_frames
, hdr
->stack_frame_count
);
243 DBG1("No leaks detected");
246 DBG1("One leak detected");
249 DBG1("%d leaks detected", leaks
);
255 * Installs the malloc hooks, enables leak detection
257 static void install_hooks()
261 old_malloc_hook
= __malloc_hook
;
262 old_realloc_hook
= __realloc_hook
;
263 old_free_hook
= __free_hook
;
264 __malloc_hook
= malloc_hook
;
265 __realloc_hook
= realloc_hook
;
266 __free_hook
= free_hook
;
272 * Uninstalls the malloc hooks, disables leak detection
274 static void uninstall_hooks()
278 __malloc_hook
= old_malloc_hook
;
279 __free_hook
= old_free_hook
;
280 __realloc_hook
= old_realloc_hook
;
286 * Hook function for malloc()
288 void *malloc_hook(size_t bytes
, const void *caller
)
290 memory_header_t
*hdr
;
292 pthread_mutex_lock(&mutex
);
295 hdr
= malloc(bytes
+ sizeof(memory_header_t
));
296 /* set to something which causes crashes */
297 memset(hdr
, MEMORY_ALLOC_PATTERN
, bytes
+ sizeof(memory_header_t
));
299 hdr
->magic
= MEMORY_HEADER_MAGIC
;
301 hdr
->stack_frame_count
= backtrace(hdr
->stack_frames
, STACK_FRAMES_COUNT
);
304 /* insert at the beginning of the list */
305 hdr
->next
= first_header
.next
;
308 hdr
->next
->previous
= hdr
;
310 hdr
->previous
= &first_header
;
311 first_header
.next
= hdr
;
312 pthread_mutex_unlock(&mutex
);
317 * Hook function for free()
319 void free_hook(void *ptr
, const void *caller
)
321 void *stack_frames
[STACK_FRAMES_COUNT
];
322 int stack_frame_count
;
323 memory_header_t
*hdr
= ptr
- sizeof(memory_header_t
);
325 /* allow freeing of NULL */
331 pthread_mutex_lock(&mutex
);
334 if (hdr
->magic
!= MEMORY_HEADER_MAGIC
)
336 DBG1("freeing of invalid memory (%p, MAGIC 0x%x != 0x%x):",
337 ptr
, hdr
->magic
, MEMORY_HEADER_MAGIC
);
338 stack_frame_count
= backtrace(stack_frames
, STACK_FRAMES_COUNT
);
339 log_stack_frames(stack_frames
, stack_frame_count
);
341 pthread_mutex_unlock(&mutex
);
345 /* remove item from list */
348 hdr
->next
->previous
= hdr
->previous
;
350 hdr
->previous
->next
= hdr
->next
;
352 /* clear MAGIC, set mem to something remarkable */
353 memset(hdr
, MEMORY_FREE_PATTERN
, hdr
->bytes
+ sizeof(memory_header_t
));
357 pthread_mutex_unlock(&mutex
);
361 * Hook function for realloc()
363 void *realloc_hook(void *old
, size_t bytes
, const void *caller
)
365 memory_header_t
*hdr
;
366 void *stack_frames
[STACK_FRAMES_COUNT
];
367 int stack_frame_count
;
369 /* allow reallocation of NULL */
372 return malloc_hook(bytes
, caller
);
375 hdr
= old
- sizeof(memory_header_t
);
377 pthread_mutex_lock(&mutex
);
380 if (hdr
->magic
!= MEMORY_HEADER_MAGIC
)
382 DBG1("reallocation of invalid memory (%p):", old
);
383 stack_frame_count
= backtrace(stack_frames
, STACK_FRAMES_COUNT
);
384 log_stack_frames(stack_frames
, stack_frame_count
);
386 pthread_mutex_unlock(&mutex
);
391 hdr
= realloc(hdr
, bytes
+ sizeof(memory_header_t
));
393 /* update statistics */
395 hdr
->stack_frame_count
= backtrace(hdr
->stack_frames
, STACK_FRAMES_COUNT
);
397 /* update header of linked list neighbours */
400 hdr
->next
->previous
= hdr
;
402 hdr
->previous
->next
= hdr
;
404 pthread_mutex_unlock(&mutex
);
409 * Setup leak detective
411 void __attribute__ ((constructor
)) leak_detective_init()
417 * Clean up leak detective
419 void __attribute__ ((destructor
)) leak_detective_cleanup()
426 * Log memory allocation statistics
428 void leak_detective_status(FILE *stream
)
432 memory_header_t
*hdr
= &first_header
;
434 pthread_mutex_lock(&mutex
);
435 while ((hdr
= hdr
->next
))
440 pthread_mutex_unlock(&mutex
);
442 fprintf(stream
, "allocation statistics:\n");
443 fprintf(stream
, " call stats: malloc: %d, free: %d, realloc: %d\n",
444 count_malloc
, count_free
, count_realloc
);
445 fprintf(stream
, " allocated %d blocks, total size %d bytes (avg. %d bytes)\n",
446 blocks
, bytes
, bytes
/blocks
);
449 #else /* !LEAK_DETECTION */
452 * Dummy when !using LEAK_DETECTIVE
454 void leak_detective_status(FILE *stream
)
459 #endif /* LEAK_DETECTION */