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