leak-detective: Reset leak list during cleanup
[strongswan.git] / src / libstrongswan / utils / leak_detective.c
1 /*
2 * Copyright (C) 2013 Tobias Brunner
3 * Copyright (C) 2006-2013 Martin Willi
4 * Hochschule fuer Technik Rapperswil
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * for more details.
15 */
16
17 #define _GNU_SOURCE
18 #include <stddef.h>
19 #include <string.h>
20 #include <stdio.h>
21 #include <signal.h>
22 #include <sys/socket.h>
23 #include <netinet/in.h>
24 #include <arpa/inet.h>
25 #include <unistd.h>
26 #include <syslog.h>
27 #include <netdb.h>
28 #include <locale.h>
29 #include <dlfcn.h>
30 #include <time.h>
31 #include <errno.h>
32
33 #ifdef __APPLE__
34 #include <sys/mman.h>
35 #include <malloc/malloc.h>
36 /* overload some of our types clashing with mach */
37 #define host_t strongswan_host_t
38 #define processor_t strongswan_processor_t
39 #define thread_t strongswan_thread_t
40 #endif /* __APPLE__ */
41
42 #include "leak_detective.h"
43
44 #include <library.h>
45 #include <utils/debug.h>
46 #include <utils/backtrace.h>
47 #include <collections/hashtable.h>
48 #include <threading/thread_value.h>
49 #include <threading/spinlock.h>
50
51 typedef struct private_leak_detective_t private_leak_detective_t;
52
53 /**
54 * private data of leak_detective
55 */
56 struct private_leak_detective_t {
57
58 /**
59 * public functions
60 */
61 leak_detective_t public;
62
63 /**
64 * Registered report() function
65 */
66 leak_detective_report_cb_t report_cb;
67
68 /**
69 * Registered report() summary function
70 */
71 leak_detective_summary_cb_t report_scb;
72
73 /**
74 * Registered user data for callbacks
75 */
76 void *report_data;
77 };
78
79 /**
80 * Magic value which helps to detect memory corruption. Yummy!
81 */
82 #define MEMORY_HEADER_MAGIC 0x7ac0be11
83
84 /**
85 * Magic written to tail of allocation
86 */
87 #define MEMORY_TAIL_MAGIC 0xcafebabe
88
89 /**
90 * Pattern which is filled in memory before freeing it
91 */
92 #define MEMORY_FREE_PATTERN 0xFF
93
94 /**
95 * Pattern which is filled in newly allocated memory
96 */
97 #define MEMORY_ALLOC_PATTERN 0xEE
98
99 typedef struct memory_header_t memory_header_t;
100 typedef struct memory_tail_t memory_tail_t;
101
102 /**
103 * Header which is prepended to each allocated memory block
104 */
105 struct memory_header_t {
106
107 /**
108 * Pointer to previous entry in linked list
109 */
110 memory_header_t *previous;
111
112 /**
113 * Pointer to next entry in linked list
114 */
115 memory_header_t *next;
116
117 /**
118 * backtrace taken during (re-)allocation
119 */
120 backtrace_t *backtrace;
121
122 /**
123 * Padding to make sizeof(memory_header_t) == 32
124 */
125 u_int32_t padding[sizeof(void*) == sizeof(u_int32_t) ? 3 : 0];
126
127 /**
128 * Number of bytes following after the header
129 */
130 u_int32_t bytes;
131
132 /**
133 * magic bytes to detect bad free or heap underflow, MEMORY_HEADER_MAGIC
134 */
135 u_int32_t magic;
136
137 }__attribute__((__packed__));
138
139 /**
140 * tail appended to each allocated memory block
141 */
142 struct memory_tail_t {
143
144 /**
145 * Magic bytes to detect heap overflow, MEMORY_TAIL_MAGIC
146 */
147 u_int32_t magic;
148
149 }__attribute__((__packed__));
150
151 /**
152 * first mem header is just a dummy to chain
153 * the others on it...
154 */
155 static memory_header_t first_header = {
156 .magic = MEMORY_HEADER_MAGIC,
157 };
158
159 /**
160 * Spinlock to access header linked list
161 */
162 static spinlock_t *lock;
163
164 /**
165 * Is leak detection currently enabled?
166 */
167 static bool enabled = FALSE;
168
169 /**
170 * Is leak detection disabled for the current thread?
171 */
172 static thread_value_t *thread_disabled;
173
174 /**
175 * Installs the malloc hooks, enables leak detection
176 */
177 static void enable_leak_detective()
178 {
179 enabled = TRUE;
180 }
181
182 /**
183 * Uninstalls the malloc hooks, disables leak detection
184 */
185 static void disable_leak_detective()
186 {
187 enabled = FALSE;
188 }
189
190 /**
191 * Enable/Disable leak detective for the current thread
192 *
193 * @return Previous value
194 */
195 static bool enable_thread(bool enable)
196 {
197 bool before;
198
199 before = thread_disabled->get(thread_disabled) == NULL;
200 thread_disabled->set(thread_disabled, enable ? NULL : (void*)TRUE);
201 return before;
202 }
203
204 /**
205 * Add a header to the beginning of the list
206 */
207 static void add_hdr(memory_header_t *hdr)
208 {
209 lock->lock(lock);
210 hdr->next = first_header.next;
211 if (hdr->next)
212 {
213 hdr->next->previous = hdr;
214 }
215 hdr->previous = &first_header;
216 first_header.next = hdr;
217 lock->unlock(lock);
218 }
219
220 /**
221 * Remove a header from the list
222 */
223 static void remove_hdr(memory_header_t *hdr)
224 {
225 lock->lock(lock);
226 if (hdr->next)
227 {
228 hdr->next->previous = hdr->previous;
229 }
230 hdr->previous->next = hdr->next;
231 lock->unlock(lock);
232 }
233
234 /**
235 * Check if a header is in the list
236 */
237 static bool has_hdr(memory_header_t *hdr)
238 {
239 memory_header_t *current;
240 bool found = FALSE;
241
242 lock->lock(lock);
243 for (current = &first_header; current != NULL; current = current->next)
244 {
245 if (current == hdr)
246 {
247 found = TRUE;
248 break;
249 }
250 }
251 lock->unlock(lock);
252
253 return found;
254 }
255
256 #ifdef __APPLE__
257
258 /**
259 * Copy of original default zone, with functions we call in hooks
260 */
261 static malloc_zone_t original;
262
263 /**
264 * Call original malloc()
265 */
266 static void* real_malloc(size_t size)
267 {
268 return original.malloc(malloc_default_zone(), size);
269 }
270
271 /**
272 * Call original free()
273 */
274 static void real_free(void *ptr)
275 {
276 original.free(malloc_default_zone(), ptr);
277 }
278
279 /**
280 * Call original realloc()
281 */
282 static void* real_realloc(void *ptr, size_t size)
283 {
284 return original.realloc(malloc_default_zone(), ptr, size);
285 }
286
287 /**
288 * Hook definition: static function with _hook suffix, takes additional zone
289 */
290 #define HOOK(ret, name, ...) \
291 static ret name ## _hook(malloc_zone_t *_z, __VA_ARGS__)
292
293 /**
294 * forward declaration of hooks
295 */
296 HOOK(void*, malloc, size_t bytes);
297 HOOK(void*, calloc, size_t nmemb, size_t size);
298 HOOK(void*, valloc, size_t size);
299 HOOK(void, free, void *ptr);
300 HOOK(void*, realloc, void *old, size_t bytes);
301
302 /**
303 * malloc zone size(), must consider the memory header prepended
304 */
305 HOOK(size_t, size, const void *ptr)
306 {
307 bool before;
308 size_t size;
309
310 if (enabled)
311 {
312 before = enable_thread(FALSE);
313 if (before)
314 {
315 ptr -= sizeof(memory_header_t);
316 }
317 }
318 size = original.size(malloc_default_zone(), ptr);
319 if (enabled)
320 {
321 enable_thread(before);
322 }
323 return size;
324 }
325
326 /**
327 * Version of malloc zones we currently support
328 */
329 #define MALLOC_ZONE_VERSION 8 /* Snow Leopard */
330
331 /**
332 * Hook-in our malloc functions into the default zone
333 */
334 static bool register_hooks()
335 {
336 malloc_zone_t *zone;
337 void *page;
338
339 zone = malloc_default_zone();
340 if (zone->version != MALLOC_ZONE_VERSION)
341 {
342 DBG1(DBG_CFG, "malloc zone version %d unsupported (requiring %d)",
343 zone->version, MALLOC_ZONE_VERSION);
344 return FALSE;
345 }
346
347 original = *zone;
348
349 page = (void*)((uintptr_t)zone / getpagesize() * getpagesize());
350 if (mprotect(page, getpagesize(), PROT_WRITE | PROT_READ) != 0)
351 {
352 DBG1(DBG_CFG, "malloc zone unprotection failed: %s", strerror(errno));
353 return FALSE;
354 }
355
356 zone->size = size_hook;
357 zone->malloc = malloc_hook;
358 zone->calloc = calloc_hook;
359 zone->valloc = valloc_hook;
360 zone->free = free_hook;
361 zone->realloc = realloc_hook;
362
363 /* those other functions can be NULLed out to not use them */
364 zone->batch_malloc = NULL;
365 zone->batch_free = NULL;
366 zone->memalign = NULL;
367 zone->free_definite_size = NULL;
368
369 return TRUE;
370 }
371
372 #else /* !__APPLE__ */
373
374 /**
375 * dlsym() might do a malloc(), but we can't do one before we get the malloc()
376 * function pointer. Use this minimalistic malloc implementation instead.
377 */
378 static void* malloc_for_dlsym(size_t size)
379 {
380 static char buf[1024] = {};
381 static size_t used = 0;
382 char *ptr;
383
384 /* roundup to a multiple of 32 */
385 size = (size - 1) / 32 * 32 + 32;
386
387 if (used + size > sizeof(buf))
388 {
389 return NULL;
390 }
391 ptr = buf + used;
392 used += size;
393 return ptr;
394 }
395
396 /**
397 * Lookup a malloc function, while disabling wrappers
398 */
399 static void* get_malloc_fn(char *name)
400 {
401 bool before = FALSE;
402 void *fn;
403
404 if (enabled)
405 {
406 before = enable_thread(FALSE);
407 }
408 fn = dlsym(RTLD_NEXT, name);
409 if (enabled)
410 {
411 enable_thread(before);
412 }
413 return fn;
414 }
415
416 /**
417 * Call original malloc()
418 */
419 static void* real_malloc(size_t size)
420 {
421 static void* (*fn)(size_t size);
422 static int recursive = 0;
423
424 if (!fn)
425 {
426 /* checking recursiveness should actually be thread-specific. But as
427 * it is very likely that the first allocation is done before we go
428 * multi-threaded, we keep it simple. */
429 if (recursive)
430 {
431 return malloc_for_dlsym(size);
432 }
433 recursive++;
434 fn = get_malloc_fn("malloc");
435 recursive--;
436 }
437 return fn(size);
438 }
439
440 /**
441 * Call original free()
442 */
443 static void real_free(void *ptr)
444 {
445 static void (*fn)(void *ptr);
446
447 if (!fn)
448 {
449 fn = get_malloc_fn("free");
450 }
451 return fn(ptr);
452 }
453
454 /**
455 * Call original realloc()
456 */
457 static void* real_realloc(void *ptr, size_t size)
458 {
459 static void* (*fn)(void *ptr, size_t size);
460
461 if (!fn)
462 {
463 fn = get_malloc_fn("realloc");
464 }
465 return fn(ptr, size);
466 }
467
468 /**
469 * Hook definition: plain function overloading existing malloc calls
470 */
471 #define HOOK(ret, name, ...) ret name(__VA_ARGS__)
472
473 /**
474 * Hook initialization when not using hooks, resolve functions.
475 */
476 static bool register_hooks()
477 {
478 void *buf = real_malloc(8);
479 real_realloc(buf, 16);
480 real_free(buf);
481 return TRUE;
482 }
483
484 #endif /* !__APPLE__ */
485
486 /**
487 * Leak report white list
488 *
489 * List of functions using static allocation buffers or should be suppressed
490 * otherwise on leak report.
491 */
492 char *whitelist[] = {
493 /* backtraces, including own */
494 "backtrace_create",
495 "safe_strerror",
496 /* pthread stuff */
497 "pthread_create",
498 "pthread_setspecific",
499 "__pthread_setspecific",
500 /* glibc functions */
501 "inet_ntoa",
502 "strerror",
503 "getprotobyname",
504 "getprotobynumber",
505 "getservbyport",
506 "getservbyname",
507 "gethostbyname",
508 "gethostbyname2",
509 "gethostbyname_r",
510 "gethostbyname2_r",
511 "getnetbyname",
512 "getpwnam_r",
513 "getgrnam_r",
514 "register_printf_function",
515 "register_printf_specifier",
516 "syslog",
517 "vsyslog",
518 "__syslog_chk",
519 "__vsyslog_chk",
520 "getaddrinfo",
521 "setlocale",
522 "getpass",
523 "getpwent_r",
524 "setpwent",
525 "endpwent",
526 "getspnam_r",
527 "getpwuid_r",
528 "initgroups",
529 "tzset",
530 /* ignore dlopen, as we do not dlclose to get proper leak reports */
531 "dlopen",
532 "dlerror",
533 "dlclose",
534 "dlsym",
535 /* mysql functions */
536 "mysql_init_character_set",
537 "init_client_errs",
538 "my_thread_init",
539 /* fastcgi library */
540 "FCGX_Init",
541 /* libxml */
542 "xmlInitCharEncodingHandlers",
543 "xmlInitParser",
544 "xmlInitParserCtxt",
545 /* libcurl */
546 "Curl_client_write",
547 /* ClearSilver */
548 "nerr_init",
549 /* libgcrypt */
550 "gcry_control",
551 "gcry_check_version",
552 "gcry_randomize",
553 "gcry_create_nonce",
554 /* OpenSSL: These are needed for unit-tests only, the openssl plugin
555 * does properly clean up any memory during destroy(). */
556 "ECDSA_do_sign_ex",
557 "ECDSA_verify",
558 "RSA_new_method",
559 /* NSPR */
560 "PR_CallOnce",
561 /* libapr */
562 "apr_pool_create_ex",
563 /* glib */
564 "g_type_init_with_debug_flags",
565 "g_type_register_static",
566 "g_type_class_ref",
567 "g_type_create_instance",
568 "g_type_add_interface_static",
569 "g_type_interface_add_prerequisite",
570 "g_socket_connection_factory_lookup_type",
571 /* libgpg */
572 "gpg_err_init",
573 /* gnutls */
574 "gnutls_global_init",
575 };
576
577 /**
578 * Some functions are hard to whitelist, as they don't use a symbol directly.
579 * Use some static initialization to suppress them on leak reports
580 */
581 static void init_static_allocations()
582 {
583 tzset();
584 }
585
586 /**
587 * Hashtable hash function
588 */
589 static u_int hash(backtrace_t *key)
590 {
591 enumerator_t *enumerator;
592 void *addr;
593 u_int hash = 0;
594
595 enumerator = key->create_frame_enumerator(key);
596 while (enumerator->enumerate(enumerator, &addr))
597 {
598 hash = chunk_hash_inc(chunk_from_thing(addr), hash);
599 }
600 enumerator->destroy(enumerator);
601
602 return hash;
603 }
604
605 /**
606 * Hashtable equals function
607 */
608 static bool equals(backtrace_t *a, backtrace_t *b)
609 {
610 return a->equals(a, b);
611 }
612
613 /**
614 * Summarize and print backtraces
615 */
616 static int print_traces(private_leak_detective_t *this,
617 leak_detective_report_cb_t cb, void *user,
618 int thresh, int thresh_count,
619 bool detailed, int *whitelisted, size_t *sum)
620 {
621 int leaks = 0;
622 memory_header_t *hdr;
623 enumerator_t *enumerator;
624 hashtable_t *entries;
625 struct {
626 /** associated backtrace */
627 backtrace_t *backtrace;
628 /** total size of all allocations */
629 size_t bytes;
630 /** number of allocations */
631 u_int count;
632 } *entry;
633 bool before;
634
635 before = enable_thread(FALSE);
636
637 entries = hashtable_create((hashtable_hash_t)hash,
638 (hashtable_equals_t)equals, 1024);
639 lock->lock(lock);
640 for (hdr = first_header.next; hdr != NULL; hdr = hdr->next)
641 {
642 if (whitelisted &&
643 hdr->backtrace->contains_function(hdr->backtrace,
644 whitelist, countof(whitelist)))
645 {
646 (*whitelisted)++;
647 continue;
648 }
649 entry = entries->get(entries, hdr->backtrace);
650 if (entry)
651 {
652 entry->bytes += hdr->bytes;
653 entry->count++;
654 }
655 else
656 {
657 INIT(entry,
658 .backtrace = hdr->backtrace->clone(hdr->backtrace),
659 .bytes = hdr->bytes,
660 .count = 1,
661 );
662 entries->put(entries, entry->backtrace, entry);
663 }
664 if (sum)
665 {
666 *sum += hdr->bytes;
667 }
668 leaks++;
669 }
670 lock->unlock(lock);
671
672 enumerator = entries->create_enumerator(entries);
673 while (enumerator->enumerate(enumerator, NULL, &entry))
674 {
675 if (cb)
676 {
677 if (!thresh || entry->bytes >= thresh)
678 {
679 if (!thresh_count || entry->count >= thresh_count)
680 {
681 this->report_cb(this->report_data, entry->count,
682 entry->bytes, entry->backtrace, detailed);
683 }
684 }
685 }
686 entry->backtrace->destroy(entry->backtrace);
687 free(entry);
688 }
689 enumerator->destroy(enumerator);
690 entries->destroy(entries);
691
692 enable_thread(before);
693 return leaks;
694 }
695
696 METHOD(leak_detective_t, report, void,
697 private_leak_detective_t *this, bool detailed)
698 {
699 if (lib->leak_detective)
700 {
701 int leaks, whitelisted = 0;
702 size_t sum = 0;
703
704 leaks = print_traces(this, this->report_cb, this->report_data,
705 0, 0, detailed, &whitelisted, &sum);
706 if (this->report_scb)
707 {
708 this->report_scb(this->report_data, leaks, sum, whitelisted);
709 }
710 }
711 }
712
713 METHOD(leak_detective_t, set_report_cb, void,
714 private_leak_detective_t *this, leak_detective_report_cb_t cb,
715 leak_detective_summary_cb_t scb, void *user)
716 {
717 this->report_cb = cb;
718 this->report_scb = scb;
719 this->report_data = user;
720 }
721
722 METHOD(leak_detective_t, leaks, int,
723 private_leak_detective_t *this)
724 {
725 int whitelisted = 0;
726
727 return print_traces(this, NULL, NULL, 0, 0, FALSE, &whitelisted, NULL);
728 }
729
730 METHOD(leak_detective_t, set_state, bool,
731 private_leak_detective_t *this, bool enable)
732 {
733 return enable_thread(enable);
734 }
735
736 METHOD(leak_detective_t, usage, void,
737 private_leak_detective_t *this, leak_detective_report_cb_t cb,
738 leak_detective_summary_cb_t scb, void *user)
739 {
740 bool detailed;
741 int thresh, thresh_count, leaks, whitelisted = 0;
742 size_t sum = 0;
743
744 thresh = lib->settings->get_int(lib->settings,
745 "libstrongswan.leak_detective.usage_threshold", 10240);
746 thresh_count = lib->settings->get_int(lib->settings,
747 "libstrongswan.leak_detective.usage_threshold_count", 0);
748 detailed = lib->settings->get_bool(lib->settings,
749 "libstrongswan.leak_detective.detailed", TRUE);
750
751 leaks = print_traces(this, cb, user, thresh, thresh_count,
752 detailed, &whitelisted, &sum);
753 if (scb)
754 {
755 scb(user, leaks, sum, whitelisted);
756 }
757 }
758
759 /**
760 * Wrapped malloc() function
761 */
762 HOOK(void*, malloc, size_t bytes)
763 {
764 memory_header_t *hdr;
765 memory_tail_t *tail;
766 bool before;
767
768 if (!enabled || thread_disabled->get(thread_disabled))
769 {
770 return real_malloc(bytes);
771 }
772
773 hdr = real_malloc(sizeof(memory_header_t) + bytes + sizeof(memory_tail_t));
774 tail = ((void*)hdr) + bytes + sizeof(memory_header_t);
775 /* set to something which causes crashes */
776 memset(hdr, MEMORY_ALLOC_PATTERN,
777 sizeof(memory_header_t) + bytes + sizeof(memory_tail_t));
778
779 before = enable_thread(FALSE);
780 hdr->backtrace = backtrace_create(2);
781 enable_thread(before);
782
783 hdr->magic = MEMORY_HEADER_MAGIC;
784 hdr->bytes = bytes;
785 tail->magic = MEMORY_TAIL_MAGIC;
786
787 add_hdr(hdr);
788
789 return hdr + 1;
790 }
791
792 /**
793 * Wrapped calloc() function
794 */
795 HOOK(void*, calloc, size_t nmemb, size_t size)
796 {
797 void *ptr;
798
799 size *= nmemb;
800 ptr = malloc(size);
801 memset(ptr, 0, size);
802
803 return ptr;
804 }
805
806 /**
807 * Wrapped valloc(), TODO: currently not supported
808 */
809 HOOK(void*, valloc, size_t size)
810 {
811 DBG1(DBG_LIB, "valloc() used, but leak-detective hook missing");
812 return NULL;
813 }
814
815 /**
816 * Wrapped free() function
817 */
818 HOOK(void, free, void *ptr)
819 {
820 memory_header_t *hdr;
821 memory_tail_t *tail;
822 backtrace_t *backtrace;
823 bool before;
824
825 if (!enabled || thread_disabled->get(thread_disabled))
826 {
827 real_free(ptr);
828 return;
829 }
830 /* allow freeing of NULL */
831 if (ptr == NULL)
832 {
833 return;
834 }
835 hdr = ptr - sizeof(memory_header_t);
836 tail = ptr + hdr->bytes;
837
838 before = enable_thread(FALSE);
839 if (hdr->magic != MEMORY_HEADER_MAGIC ||
840 tail->magic != MEMORY_TAIL_MAGIC)
841 {
842 if (has_hdr(hdr))
843 {
844 /* memory was allocated by our hooks but is corrupted */
845 fprintf(stderr, "freeing corrupted memory (%p): "
846 "header magic 0x%x, tail magic 0x%x:\n",
847 ptr, hdr->magic, tail->magic);
848 }
849 else
850 {
851 /* memory was not allocated by our hooks */
852 fprintf(stderr, "freeing invalid memory (%p)\n", ptr);
853 }
854 backtrace = backtrace_create(2);
855 backtrace->log(backtrace, stderr, TRUE);
856 backtrace->destroy(backtrace);
857 }
858 else
859 {
860 remove_hdr(hdr);
861
862 hdr->backtrace->destroy(hdr->backtrace);
863
864 /* clear MAGIC, set mem to something remarkable */
865 memset(hdr, MEMORY_FREE_PATTERN,
866 sizeof(memory_header_t) + hdr->bytes + sizeof(memory_tail_t));
867
868 real_free(hdr);
869 }
870 enable_thread(before);
871 }
872
873 /**
874 * Wrapped realloc() function
875 */
876 HOOK(void*, realloc, void *old, size_t bytes)
877 {
878 memory_header_t *hdr;
879 memory_tail_t *tail;
880 backtrace_t *backtrace;
881 bool before;
882
883 if (!enabled || thread_disabled->get(thread_disabled))
884 {
885 return real_realloc(old, bytes);
886 }
887 /* allow reallocation of NULL */
888 if (old == NULL)
889 {
890 return malloc(bytes);
891 }
892 /* handle zero size as a free() */
893 if (bytes == 0)
894 {
895 free(old);
896 return NULL;
897 }
898
899 hdr = old - sizeof(memory_header_t);
900 tail = old + hdr->bytes;
901
902 remove_hdr(hdr);
903
904 if (hdr->magic != MEMORY_HEADER_MAGIC ||
905 tail->magic != MEMORY_TAIL_MAGIC)
906 {
907 fprintf(stderr, "reallocating invalid memory (%p):\n"
908 "header magic 0x%x:\n", old, hdr->magic);
909 backtrace = backtrace_create(2);
910 backtrace->log(backtrace, stderr, TRUE);
911 backtrace->destroy(backtrace);
912 }
913 else
914 {
915 /* clear tail magic, allocate, set tail magic */
916 memset(&tail->magic, MEMORY_ALLOC_PATTERN, sizeof(tail->magic));
917 }
918 hdr = real_realloc(hdr,
919 sizeof(memory_header_t) + bytes + sizeof(memory_tail_t));
920 tail = ((void*)hdr) + bytes + sizeof(memory_header_t);
921 tail->magic = MEMORY_TAIL_MAGIC;
922
923 /* update statistics */
924 hdr->bytes = bytes;
925
926 before = enable_thread(FALSE);
927 hdr->backtrace->destroy(hdr->backtrace);
928 hdr->backtrace = backtrace_create(2);
929 enable_thread(before);
930
931 add_hdr(hdr);
932
933 return hdr + 1;
934 }
935
936 METHOD(leak_detective_t, destroy, void,
937 private_leak_detective_t *this)
938 {
939 disable_leak_detective();
940 lock->destroy(lock);
941 thread_disabled->destroy(thread_disabled);
942 free(this);
943 first_header.next = NULL;
944 }
945
946 /*
947 * see header file
948 */
949 leak_detective_t *leak_detective_create()
950 {
951 private_leak_detective_t *this;
952
953 INIT(this,
954 .public = {
955 .report = _report,
956 .set_report_cb = _set_report_cb,
957 .usage = _usage,
958 .leaks = _leaks,
959 .set_state = _set_state,
960 .destroy = _destroy,
961 },
962 );
963
964 lock = spinlock_create();
965 thread_disabled = thread_value_create(NULL);
966
967 init_static_allocations();
968
969 if (getenv("LEAK_DETECTIVE_DISABLE") == NULL)
970 {
971 if (register_hooks())
972 {
973 enable_leak_detective();
974 }
975 }
976 return &this->public;
977 }