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