extended statusall output
[strongswan.git] / src / libstrongswan / utils / leak_detective.c
1 /**
2 * @file leak_detective.c
3 *
4 * @brief Allocation hooks to find memory leaks.
5 */
6
7 /*
8 * Copyright (C) 2006 Martin Willi
9 * Hochschule fuer Technik Rapperswil
10 *
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>.
15 *
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
19 * for more details.
20 */
21
22 #include <stddef.h>
23 #include <string.h>
24 #include <stdio.h>
25 #include <malloc.h>
26 #include <execinfo.h>
27 #include <signal.h>
28 #include <sys/socket.h>
29 #include <netinet/in.h>
30 #include <arpa/inet.h>
31 #include <dlfcn.h>
32 #include <unistd.h>
33 #include <syslog.h>
34 #include <pthread.h>
35 #include <netdb.h>
36
37 #include "leak_detective.h"
38
39 #include <types.h>
40
41 #ifdef LEAK_DETECTIVE
42
43 /**
44 * Magic value which helps to detect memory corruption. Yummy!
45 */
46 #define MEMORY_HEADER_MAGIC 0x7ac0be11
47
48 /**
49 * Pattern which is filled in memory before freeing it
50 */
51 #define MEMORY_FREE_PATTERN 0xFF
52
53 /**
54 * Pattern which is filled in newly allocated memory
55 */
56 #define MEMORY_ALLOC_PATTERN 0xEE
57
58
59 static void install_hooks(void);
60 static void uninstall_hooks(void);
61 static void *malloc_hook(size_t, const void *);
62 static void *realloc_hook(void *, size_t, const void *);
63 static void free_hook(void*, const void *);
64
65 static u_int count_malloc = 0;
66 static u_int count_free = 0;
67 static u_int count_realloc = 0;
68
69 typedef struct memory_header_t memory_header_t;
70
71 /**
72 * Header which is prepended to each allocated memory block
73 */
74 struct memory_header_t {
75 /**
76 * Magci byte which must(!) hold MEMORY_HEADER_MAGIC
77 */
78 u_int32_t magic;
79
80 /**
81 * Number of bytes following after the header
82 */
83 size_t bytes;
84
85 /**
86 * Stack frames at the time of allocation
87 */
88 void *stack_frames[STACK_FRAMES_COUNT];
89
90 /**
91 * Number of stacks frames obtained in stack_frames
92 */
93 int stack_frame_count;
94
95 /**
96 * Pointer to previous entry in linked list
97 */
98 memory_header_t *previous;
99
100 /**
101 * Pointer to next entry in linked list
102 */
103 memory_header_t *next;
104 };
105
106 /**
107 * first mem header is just a dummy to chain
108 * the others on it...
109 */
110 static memory_header_t first_header = {
111 magic: MEMORY_HEADER_MAGIC,
112 bytes: 0,
113 stack_frame_count: 0,
114 previous: NULL,
115 next: NULL
116 };
117
118 /**
119 * logger for the leak detective
120 */
121 static logger_t *logger;
122
123 /**
124 * standard hooks, used to temparily remove hooking
125 */
126 static void *old_malloc_hook, *old_realloc_hook, *old_free_hook;
127
128 /**
129 * are the hooks currently installed?
130 */
131 static bool installed = FALSE;
132
133 /**
134 * Mutex to exclusivly uninstall hooks, access heap list
135 */
136 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
137
138
139 /**
140 * log stack frames queried by backtrace()
141 * TODO: Dump symbols of static functions!!!
142 */
143 static void log_stack_frames(void **stack_frames, int stack_frame_count)
144 {
145 char **strings;
146 size_t i;
147
148 strings = backtrace_symbols (stack_frames, stack_frame_count);
149
150 logger->log(logger, ERROR, " dumping %d stack frame addresses", stack_frame_count);
151
152 for (i = 0; i < stack_frame_count; i++)
153 {
154 logger->log(logger, ERROR, " %s", strings[i]);
155 }
156 free (strings);
157 }
158
159 /**
160 * Whitelist, which contains address ranges in stack frames ignored when leaking.
161 *
162 * This is necessary, as some function use allocation hacks (static buffers)
163 * and so on, which we want to suppress on leak reports.
164 */
165 typedef struct whitelist_t whitelist_t;
166
167 struct whitelist_t {
168 void* range_start;
169 size_t range_size;
170 };
171
172 whitelist_t whitelist[] = {
173 {pthread_create, 0x500},
174 {pthread_setspecific, 0xFF},
175 {mktime, 0xFF},
176 {inet_ntoa, 0xFF},
177 {strerror, 0xFF},
178 {getprotobynumber, 0xFF},
179 {getservbyport, 0xFF},
180 };
181
182 /**
183 * Check if this stack frame is whitelisted.
184 */
185 static bool is_whitelisted(void **stack_frames, int stack_frame_count)
186 {
187 int i, j;
188
189 for (i=0; i< stack_frame_count; i++)
190 {
191 for (j=0; j<sizeof(whitelist)/sizeof(whitelist_t); j++)
192 {
193 if (stack_frames[i] >= whitelist[j].range_start &&
194 stack_frames[i] <= (whitelist[j].range_start + whitelist[j].range_size))
195 {
196 return TRUE;
197 }
198 }
199 }
200 return FALSE;
201 }
202
203 /**
204 * Report leaks at library destruction
205 */
206 void report_leaks()
207 {
208 memory_header_t *hdr;
209 int leaks = 0;
210
211 for (hdr = first_header.next; hdr != NULL; hdr = hdr->next)
212 {
213 if (!is_whitelisted(hdr->stack_frames, hdr->stack_frame_count))
214 {
215 logger->log(logger, ERROR, "Leak (%d bytes at %p):", hdr->bytes, hdr + 1);
216 log_stack_frames(hdr->stack_frames, hdr->stack_frame_count);
217 leaks++;
218 }
219 }
220
221 switch (leaks)
222 {
223 case 0:
224 logger->log(logger, CONTROL, "No leaks detected");
225 break;
226 case 1:
227 logger->log(logger, ERROR, "One leak detected");
228 break;
229 default:
230 logger->log(logger, ERROR, "%d leaks detected", leaks);
231 break;
232 }
233 }
234
235 /**
236 * Installs the malloc hooks, enables leak detection
237 */
238 static void install_hooks()
239 {
240 if (!installed)
241 {
242 old_malloc_hook = __malloc_hook;
243 old_realloc_hook = __realloc_hook;
244 old_free_hook = __free_hook;
245 __malloc_hook = malloc_hook;
246 __realloc_hook = realloc_hook;
247 __free_hook = free_hook;
248 installed = TRUE;
249 }
250 }
251
252 /**
253 * Uninstalls the malloc hooks, disables leak detection
254 */
255 static void uninstall_hooks()
256 {
257 if (installed)
258 {
259 __malloc_hook = old_malloc_hook;
260 __free_hook = old_free_hook;
261 __realloc_hook = old_realloc_hook;
262 installed = FALSE;
263 }
264 }
265
266 /**
267 * Hook function for malloc()
268 */
269 void *malloc_hook(size_t bytes, const void *caller)
270 {
271 memory_header_t *hdr;
272
273 pthread_mutex_lock(&mutex);
274 count_malloc++;
275 uninstall_hooks();
276 hdr = malloc(bytes + sizeof(memory_header_t));
277 /* set to something which causes crashes */
278 memset(hdr, MEMORY_ALLOC_PATTERN, bytes + sizeof(memory_header_t));
279
280 hdr->magic = MEMORY_HEADER_MAGIC;
281 hdr->bytes = bytes;
282 hdr->stack_frame_count = backtrace(hdr->stack_frames, STACK_FRAMES_COUNT);
283 install_hooks();
284
285 /* insert at the beginning of the list */
286 hdr->next = first_header.next;
287 if (hdr->next)
288 {
289 hdr->next->previous = hdr;
290 }
291 hdr->previous = &first_header;
292 first_header.next = hdr;
293 pthread_mutex_unlock(&mutex);
294 return hdr + 1;
295 }
296
297 /**
298 * Hook function for free()
299 */
300 void free_hook(void *ptr, const void *caller)
301 {
302 void *stack_frames[STACK_FRAMES_COUNT];
303 int stack_frame_count;
304 memory_header_t *hdr = ptr - sizeof(memory_header_t);
305
306 /* allow freeing of NULL */
307 if (ptr == NULL)
308 {
309 return;
310 }
311
312 pthread_mutex_lock(&mutex);
313 count_free++;
314 uninstall_hooks();
315 if (hdr->magic != MEMORY_HEADER_MAGIC)
316 {
317 logger->log(logger, ERROR, "freeing of invalid memory (%p, MAGIC 0x%x != 0x%x):",
318 ptr, hdr->magic, MEMORY_HEADER_MAGIC);
319 stack_frame_count = backtrace(stack_frames, STACK_FRAMES_COUNT);
320 log_stack_frames(stack_frames, stack_frame_count);
321 install_hooks();
322 pthread_mutex_unlock(&mutex);
323 return;
324 }
325
326 /* remove item from list */
327 if (hdr->next)
328 {
329 hdr->next->previous = hdr->previous;
330 }
331 hdr->previous->next = hdr->next;
332
333 /* clear MAGIC, set mem to something remarkable */
334 memset(hdr, MEMORY_FREE_PATTERN, hdr->bytes + sizeof(memory_header_t));
335
336 free(hdr);
337 install_hooks();
338 pthread_mutex_unlock(&mutex);
339 }
340
341 /**
342 * Hook function for realloc()
343 */
344 void *realloc_hook(void *old, size_t bytes, const void *caller)
345 {
346 memory_header_t *hdr;
347 void *stack_frames[STACK_FRAMES_COUNT];
348 int stack_frame_count;
349
350 /* allow reallocation of NULL */
351 if (old == NULL)
352 {
353 return malloc_hook(bytes, caller);
354 }
355
356 hdr = old - sizeof(memory_header_t);
357
358 pthread_mutex_lock(&mutex);
359 count_realloc++;
360 uninstall_hooks();
361 if (hdr->magic != MEMORY_HEADER_MAGIC)
362 {
363 logger->log(logger, ERROR, "reallocation of invalid memory (%p):", old);
364 stack_frame_count = backtrace(stack_frames, STACK_FRAMES_COUNT);
365 log_stack_frames(stack_frames, stack_frame_count);
366 install_hooks();
367 pthread_mutex_unlock(&mutex);
368 raise(SIGKILL);
369 return NULL;
370 }
371
372 hdr = realloc(hdr, bytes + sizeof(memory_header_t));
373
374 /* update statistics */
375 hdr->bytes = bytes;
376 hdr->stack_frame_count = backtrace(hdr->stack_frames, STACK_FRAMES_COUNT);
377
378 /* update header of linked list neighbours */
379 if (hdr->next)
380 {
381 hdr->next->previous = hdr;
382 }
383 hdr->previous->next = hdr;
384 install_hooks();
385 pthread_mutex_unlock(&mutex);
386 return hdr + 1;
387 }
388
389 /**
390 * Setup leak detective
391 */
392 void leak_detective_init()
393 {
394 logger = logger_manager->get_logger(logger_manager, LEAK_DETECT);
395 install_hooks();
396 }
397
398 /**
399 * Clean up leak detective
400 */
401 void leak_detective_cleanup()
402 {
403 uninstall_hooks();
404 report_leaks();
405 }
406
407 /**
408 * Log memory allocation statistics
409 */
410 void leak_detective_status(logger_t *logger)
411 {
412 u_int blocks = 0;
413 size_t bytes = 0;
414 memory_header_t *hdr = &first_header;
415
416 pthread_mutex_lock(&mutex);
417 while ((hdr = hdr->next))
418 {
419 blocks++;
420 bytes += hdr->bytes;
421 }
422 pthread_mutex_unlock(&mutex);
423
424 logger->log(logger, CONTROL|LEVEL1, "allocation statistics:");
425 logger->log(logger, CONTROL|LEVEL1, " call stats: malloc: %d, free: %d, realloc: %d",
426 count_malloc, count_free, count_realloc);
427 logger->log(logger, CONTROL|LEVEL1, " allocated %d blocks, total size %d bytes (avg. %d bytes)",
428 blocks, bytes, bytes/blocks);
429 }
430
431 #else /* !LEAK_DETECTION */
432
433 /**
434 * Dummy when !using LEAK_DETECTIVE
435 */
436 void leak_detective_status(logger_t *logger)
437 {
438
439 }
440
441 #endif /* LEAK_DETECTION */