aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Willi <martin@revosec.ch>2013-05-06 15:15:58 +0200
committerMartin Willi <martin@revosec.ch>2013-05-06 15:15:58 +0200
commitb4e9f74e428ab0f3026df4175d133b81b16c9d56 (patch)
treeacb573f1c6a716a1b0fc330b1f21dc3413a4421e
parente9b3bd54346d78343f74878c799dd11fd2e2276a (diff)
parent40f2a5306aff2c43065778b378b127e61ecb76cd (diff)
downloadstrongswan-b4e9f74e428ab0f3026df4175d133b81b16c9d56.tar.bz2
strongswan-b4e9f74e428ab0f3026df4175d133b81b16c9d56.tar.xz
Merge branch 'leak-detective-dlsym'
Replaces the use of deprecated glibc malloc hooks by overloading malloc calls and find the originals using dlsym(RTLD_NEXT). On OS X, we now support the use of leak detective by changing the default malloc zone functions, backtraces get resolved using atos. It seems that the performance bottleneck now is stack unwinding. Unfortunately a new libunwind based backtrace() is not much faster than the libc variant; we keep that option nonetheless for platforms without backtrace().
-rw-r--r--configure.in9
-rw-r--r--scripts/.gitignore1
-rw-r--r--scripts/Makefile.am4
-rw-r--r--scripts/malloc_speed.c85
-rw-r--r--src/libstrongswan/Makefile.am2
-rw-r--r--src/libstrongswan/utils/backtrace.c53
-rw-r--r--src/libstrongswan/utils/leak_detective.c514
7 files changed, 519 insertions, 149 deletions
diff --git a/configure.in b/configure.in
index f0a2ec961..311b15c6b 100644
--- a/configure.in
+++ b/configure.in
@@ -238,6 +238,7 @@ ARG_ENABL_SET([radattr], [enable plugin to inject and process custom RADI
ARG_ENABL_SET([vstr], [enforce using the Vstr string library to replace glibc-like printf hooks.])
ARG_ENABL_SET([monolithic], [build monolithic version of libstrongswan that includes all enabled plugins. Similarly, the plugins of charon are assembled in libcharon.])
ARG_ENABL_SET([bfd-backtraces], [use binutils libbfd to resolve backtraces for memory leaks and segfaults.])
+ARG_ENABL_SET([unwind-backtraces],[use libunwind to create backtraces for memory leaks and segfaults.])
ARG_ENABL_SET([unit-tests], [enable unit tests using the check test framework.])
ARG_ENABL_SET([tkm], [enable Trusted Key Manager support.])
@@ -886,6 +887,14 @@ if test x$bfd_backtraces = xtrue; then
AC_SUBST(BFDLIB)
fi
+if test x$unwind_backtraces = xtrue; then
+ AC_CHECK_LIB([unwind],[main],[LIBS="$LIBS"],[AC_MSG_ERROR([libunwind not found!])],[])
+ AC_CHECK_HEADER([libunwind.h],[AC_DEFINE([HAVE_LIBUNWIND_H],,[have libunwind.h])],
+ [AC_MSG_ERROR([libunwind.h header not found!])])
+ UNWINDLIB="-lunwind"
+ AC_SUBST(UNWINDLIB)
+fi
+
AM_CONDITIONAL(USE_DEV_HEADERS, [test "x$dev_headers" != xno])
if test x$dev_headers = xyes; then
dev_headers="$includedir/strongswan"
diff --git a/scripts/.gitignore b/scripts/.gitignore
index b97347fbd..14579cbb4 100644
--- a/scripts/.gitignore
+++ b/scripts/.gitignore
@@ -9,6 +9,7 @@ dh_speed
pubkey_speed
crypt_burn
hash_burn
+malloc_speed
tls_test
fetch
dnssec
diff --git a/scripts/Makefile.am b/scripts/Makefile.am
index f7ecd9ef6..b9b3a70a8 100644
--- a/scripts/Makefile.am
+++ b/scripts/Makefile.am
@@ -4,7 +4,7 @@ AM_CFLAGS = \
noinst_PROGRAMS = bin2array bin2sql id2sql key2keyid keyid2sql oid2der \
thread_analysis dh_speed pubkey_speed crypt_burn hash_burn fetch \
- dnssec
+ dnssec malloc_speed
if USE_TLS
noinst_PROGRAMS += tls_test
@@ -24,6 +24,7 @@ dh_speed_SOURCES = dh_speed.c
pubkey_speed_SOURCES = pubkey_speed.c
crypt_burn_SOURCES = crypt_burn.c
hash_burn_SOURCES = hash_burn.c
+malloc_speed_SOURCES = malloc_speed.c
fetch_SOURCES = fetch.c
dnssec_SOURCES = dnssec.c
id2sql_LDADD = $(top_builddir)/src/libstrongswan/libstrongswan.la
@@ -34,6 +35,7 @@ dh_speed_LDADD = $(top_builddir)/src/libstrongswan/libstrongswan.la -lrt
pubkey_speed_LDADD = $(top_builddir)/src/libstrongswan/libstrongswan.la -lrt
crypt_burn_LDADD = $(top_builddir)/src/libstrongswan/libstrongswan.la
hash_burn_LDADD = $(top_builddir)/src/libstrongswan/libstrongswan.la
+malloc_speed_LDADD = $(top_builddir)/src/libstrongswan/libstrongswan.la
fetch_LDADD = $(top_builddir)/src/libstrongswan/libstrongswan.la
dnssec_LDADD = $(top_builddir)/src/libstrongswan/libstrongswan.la
diff --git a/scripts/malloc_speed.c b/scripts/malloc_speed.c
new file mode 100644
index 000000000..85d51a281
--- /dev/null
+++ b/scripts/malloc_speed.c
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2013 Martin Willi
+ * Copyright (C) 2013 revosec aG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include <stdio.h>
+#include <time.h>
+#include <library.h>
+#include <utils/debug.h>
+
+#ifdef HAVE_MALLINFO
+#include <malloc.h>
+#endif /* HAVE_MALLINFO */
+
+static void start_timing(struct timespec *start)
+{
+ clock_gettime(CLOCK_THREAD_CPUTIME_ID, start);
+}
+
+static double end_timing(struct timespec *start)
+{
+ struct timespec end;
+
+ clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end);
+ return (end.tv_nsec - start->tv_nsec) / 1000000000.0 +
+ (end.tv_sec - start->tv_sec) * 1.0;
+}
+
+static void print_mallinfo()
+{
+#ifdef HAVE_MALLINFO
+ struct mallinfo mi = mallinfo();
+
+ printf("malloc: sbrk %d, mmap %d, used %d, free %d\n",
+ mi.arena, mi.hblkhd, mi.uordblks, mi.fordblks);
+#endif /* HAVE_MALLINFO */
+}
+
+#define ALLOCS 1024
+#define ROUNDS 2048
+
+int main(int argc, char *argv[])
+{
+ struct timespec timing;
+ int i, round;
+ void *m[ALLOCS];
+ /* a random set of allocations we test */
+ int sizes[16] = { 1, 13, 100, 1000, 16, 10000, 50, 17,
+ 123, 32, 8, 64, 8096, 1024, 123, 9 };
+
+ library_init(NULL);
+ atexit(library_deinit);
+
+ print_mallinfo();
+
+ start_timing(&timing);
+
+ for (round = 0; round < ROUNDS; round++)
+ {
+ for (i = 0; i < ALLOCS; i++)
+ {
+ m[i] = malloc(sizes[(round + i) % countof(sizes)]);
+ }
+ for (i = 0; i < ALLOCS; i++)
+ {
+ free(m[i]);
+ }
+ }
+ printf("time for %d malloc/frees, repeating %d rounds: %.4fs\n",
+ ALLOCS, ROUNDS, end_timing(&timing));
+
+ print_mallinfo();
+
+ return 0;
+}
diff --git a/src/libstrongswan/Makefile.am b/src/libstrongswan/Makefile.am
index 81e271a8a..ce6df2855 100644
--- a/src/libstrongswan/Makefile.am
+++ b/src/libstrongswan/Makefile.am
@@ -79,7 +79,7 @@ endif
library.lo : $(top_builddir)/config.status
-libstrongswan_la_LIBADD = $(PTHREADLIB) $(DLLIB) $(BTLIB) $(SOCKLIB) $(RTLIB) $(BFDLIB)
+libstrongswan_la_LIBADD = $(PTHREADLIB) $(DLLIB) $(BTLIB) $(SOCKLIB) $(RTLIB) $(BFDLIB) $(UNWINDLIB)
INCLUDES = -I$(top_srcdir)/src/libstrongswan
AM_CFLAGS = \
diff --git a/src/libstrongswan/utils/backtrace.c b/src/libstrongswan/utils/backtrace.c
index 77137f9f1..820dba44b 100644
--- a/src/libstrongswan/utils/backtrace.c
+++ b/src/libstrongswan/utils/backtrace.c
@@ -299,7 +299,7 @@ static bfd_entry_t *get_bfd_entry(char *filename)
/**
* Print the source file with line number to file, libbfd variant
*/
-static void print_sourceline(FILE *file, char *filename, void *ptr)
+static void print_sourceline(FILE *file, char *filename, void *ptr, void *base)
{
bfd_entry_t *entry;
bfd_find_data_t data = {
@@ -334,13 +334,20 @@ void backtrace_deinit() {}
/**
* Print the source file with line number to file, slow addr2line variant
*/
-static void print_sourceline(FILE *file, char *filename, void *ptr)
+static void print_sourceline(FILE *file, char *filename, void *ptr, void* base)
{
char buf[1024];
FILE *output;
int c, i = 0;
+#ifdef __APPLE__
+ snprintf(buf, sizeof(buf), "atos -o %s -l %p %p 2>&1 | tail -n1",
+ filename, base, ptr);
+#else /* !__APPLE__ */
snprintf(buf, sizeof(buf), "addr2line -e %s %p", filename, ptr);
+#endif /* __APPLE__ */
+
+
output = popen(buf, "r");
if (output)
{
@@ -373,7 +380,7 @@ void backtrace_deinit() {}
METHOD(backtrace_t, log_, void,
private_backtrace_t *this, FILE *file, bool detailed)
{
-#ifdef HAVE_BACKTRACE
+#if defined(HAVE_BACKTRACE) || defined(HAVE_LIBUNWIND_H)
size_t i;
char **strings;
@@ -410,7 +417,8 @@ METHOD(backtrace_t, log_, void,
}
if (detailed && info.dli_fname[0])
{
- print_sourceline(file, (char*)info.dli_fname, ptr);
+ print_sourceline(file, (char*)info.dli_fname,
+ ptr, info.dli_fbase);
}
}
else
@@ -420,9 +428,9 @@ METHOD(backtrace_t, log_, void,
}
}
free (strings);
-#else /* !HAVE_BACKTRACE */
- println(file, "C library does not support backtrace().");
-#endif /* HAVE_BACKTRACE */
+#else /* !HAVE_BACKTRACE && !HAVE_LIBUNWIND_H */
+ println(file, "no support for backtrace()/libunwind");
+#endif /* HAVE_BACKTRACE/HAVE_LIBUNWIND_H */
}
METHOD(backtrace_t, contains_function, bool,
@@ -518,6 +526,33 @@ METHOD(backtrace_t, destroy, void,
free(this);
}
+#ifdef HAVE_LIBUNWIND_H
+#define UNW_LOCAL_ONLY
+#include <libunwind.h>
+
+/**
+ * libunwind variant for glibc backtrace()
+ */
+static inline int backtrace_unwind(void **frames, int count)
+{
+ unw_context_t context;
+ unw_cursor_t cursor;
+ unw_word_t ip;
+ int depth = 0;
+
+ unw_getcontext(&context);
+ unw_init_local(&cursor, &context);
+ do
+ {
+ unw_get_reg(&cursor, UNW_REG_IP, &ip);
+ frames[depth++] = (void*)ip;
+ }
+ while (depth < count && unw_step(&cursor) > 0);
+
+ return depth;
+}
+#endif /* HAVE_UNWIND */
+
/**
* See header
*/
@@ -527,7 +562,9 @@ backtrace_t *backtrace_create(int skip)
void *frames[50];
int frame_count = 0;
-#ifdef HAVE_BACKTRACE
+#ifdef HAVE_LIBUNWIND_H
+ frame_count = backtrace_unwind(frames, countof(frames));
+#elif defined(HAVE_BACKTRACE)
frame_count = backtrace(frames, countof(frames));
#endif /* HAVE_BACKTRACE */
frame_count = max(frame_count - skip, 0);
diff --git a/src/libstrongswan/utils/leak_detective.c b/src/libstrongswan/utils/leak_detective.c
index 6bf4d63cd..26e3e43fc 100644
--- a/src/libstrongswan/utils/leak_detective.c
+++ b/src/libstrongswan/utils/leak_detective.c
@@ -1,5 +1,6 @@
/*
- * Copyright (C) 2006-2008 Martin Willi
+ * Copyright (C) 2013 Tobias Brunner
+ * Copyright (C) 2006-2013 Martin Willi
* Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
@@ -14,20 +15,29 @@
*/
#define _GNU_SOURCE
-#include <sched.h>
#include <stddef.h>
#include <string.h>
#include <stdio.h>
-#include <malloc.h>
#include <signal.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <syslog.h>
-#include <pthread.h>
#include <netdb.h>
#include <locale.h>
+#include <dlfcn.h>
+#include <time.h>
+#include <errno.h>
+
+#ifdef __APPLE__
+#include <sys/mman.h>
+#include <malloc/malloc.h>
+/* overload some of our types clashing with mach */
+#define host_t strongswan_host_t
+#define processor_t strongswan_processor_t
+#define thread_t strongswan_thread_t
+#endif /* __APPLE__ */
#include "leak_detective.h"
@@ -35,6 +45,8 @@
#include <utils/debug.h>
#include <utils/backtrace.h>
#include <collections/hashtable.h>
+#include <threading/thread_value.h>
+#include <threading/spinlock.h>
typedef struct private_leak_detective_t private_leak_detective_t;
@@ -69,21 +81,6 @@ struct private_leak_detective_t {
*/
#define MEMORY_ALLOC_PATTERN 0xEE
-
-static void install_hooks(void);
-static void uninstall_hooks(void);
-static void *malloc_hook(size_t, const void *);
-static void *realloc_hook(void *, size_t, const void *);
-static void free_hook(void*, const void *);
-
-void *(*old_malloc_hook)(size_t, const void *);
-void *(*old_realloc_hook)(void *, size_t, const void *);
-void (*old_free_hook)(void*, const void *);
-
-static u_int count_malloc = 0;
-static u_int count_free = 0;
-static u_int count_realloc = 0;
-
typedef struct memory_header_t memory_header_t;
typedef struct memory_tail_t memory_tail_t;
@@ -108,6 +105,11 @@ struct memory_header_t {
backtrace_t *backtrace;
/**
+ * Padding to make sizeof(memory_header_t) == 32
+ */
+ u_int32_t padding[sizeof(void*) == sizeof(u_int32_t) ? 3 : 0];
+
+ /**
* Number of bytes following after the header
*/
u_int32_t bytes;
@@ -136,50 +138,282 @@ struct memory_tail_t {
* the others on it...
*/
static memory_header_t first_header = {
- magic: MEMORY_HEADER_MAGIC,
- bytes: 0,
- backtrace: NULL,
- previous: NULL,
- next: NULL
+ .magic = MEMORY_HEADER_MAGIC,
};
/**
- * are the hooks currently installed?
+ * Spinlock to access header linked list
+ */
+static spinlock_t *lock;
+
+/**
+ * Is leak detection currently enabled?
+ */
+static bool enabled = FALSE;
+
+/**
+ * Is leak detection disabled for the current thread?
*/
-static bool installed = FALSE;
+static thread_value_t *thread_disabled;
/**
* Installs the malloc hooks, enables leak detection
*/
-static void install_hooks()
+static void enable_leak_detective()
+{
+ enabled = TRUE;
+}
+
+/**
+ * Uninstalls the malloc hooks, disables leak detection
+ */
+static void disable_leak_detective()
+{
+ enabled = FALSE;
+}
+
+/**
+ * Enable/Disable leak detective for the current thread
+ *
+ * @return Previous value
+ */
+static bool enable_thread(bool enable)
+{
+ bool before;
+
+ before = thread_disabled->get(thread_disabled) == NULL;
+ thread_disabled->set(thread_disabled, enable ? NULL : (void*)TRUE);
+ return before;
+}
+
+#ifdef __APPLE__
+
+/**
+ * Copy of original default zone, with functions we call in hooks
+ */
+static malloc_zone_t original;
+
+/**
+ * Call original malloc()
+ */
+static void* real_malloc(size_t size)
+{
+ return original.malloc(malloc_default_zone(), size);
+}
+
+/**
+ * Call original free()
+ */
+static void real_free(void *ptr)
+{
+ original.free(malloc_default_zone(), ptr);
+}
+
+/**
+ * Call original realloc()
+ */
+static void* real_realloc(void *ptr, size_t size)
+{
+ return original.realloc(malloc_default_zone(), ptr, size);
+}
+
+/**
+ * Hook definition: static function with _hook suffix, takes additional zone
+ */
+#define HOOK(ret, name, ...) \
+ static ret name ## _hook(malloc_zone_t *_z, __VA_ARGS__)
+
+/**
+ * forward declaration of hooks
+ */
+HOOK(void*, malloc, size_t bytes);
+HOOK(void*, calloc, size_t nmemb, size_t size);
+HOOK(void*, valloc, size_t size);
+HOOK(void, free, void *ptr);
+HOOK(void*, realloc, void *old, size_t bytes);
+
+/**
+ * malloc zone size(), must consider the memory header prepended
+ */
+HOOK(size_t, size, const void *ptr)
{
- if (!installed)
+ bool before;
+ size_t size;
+
+ if (enabled)
+ {
+ before = enable_thread(FALSE);
+ if (before)
+ {
+ ptr -= sizeof(memory_header_t);
+ }
+ }
+ size = original.size(malloc_default_zone(), ptr);
+ if (enabled)
{
- old_malloc_hook = __malloc_hook;
- old_realloc_hook = __realloc_hook;
- old_free_hook = __free_hook;
- __malloc_hook = malloc_hook;
- __realloc_hook = realloc_hook;
- __free_hook = free_hook;
- installed = TRUE;
+ enable_thread(before);
}
+ return size;
}
/**
- * Uninstalls the malloc hooks, disables leak detection
+ * Version of malloc zones we currently support
+ */
+#define MALLOC_ZONE_VERSION 8 /* Snow Leopard */
+
+/**
+ * Hook-in our malloc functions into the default zone
+ */
+static bool register_hooks()
+{
+ malloc_zone_t *zone;
+ void *page;
+
+ zone = malloc_default_zone();
+ if (zone->version != MALLOC_ZONE_VERSION)
+ {
+ DBG1(DBG_CFG, "malloc zone version %d unsupported (requiring %d)",
+ zone->version, MALLOC_ZONE_VERSION);
+ return FALSE;
+ }
+
+ original = *zone;
+
+ page = (void*)((uintptr_t)zone / getpagesize() * getpagesize());
+ if (mprotect(page, getpagesize(), PROT_WRITE | PROT_READ) != 0)
+ {
+ DBG1(DBG_CFG, "malloc zone unprotection failed: %s", strerror(errno));
+ return FALSE;
+ }
+
+ zone->size = size_hook;
+ zone->malloc = malloc_hook;
+ zone->calloc = calloc_hook;
+ zone->valloc = valloc_hook;
+ zone->free = free_hook;
+ zone->realloc = realloc_hook;
+
+ /* those other functions can be NULLed out to not use them */
+ zone->batch_malloc = NULL;
+ zone->batch_free = NULL;
+ zone->memalign = NULL;
+ zone->free_definite_size = NULL;
+
+ return TRUE;
+}
+
+#else /* !__APPLE__ */
+
+/**
+ * dlsym() might do a malloc(), but we can't do one before we get the malloc()
+ * function pointer. Use this minimalistic malloc implementation instead.
*/
-static void uninstall_hooks()
+static void* malloc_for_dlsym(size_t size)
{
- if (installed)
+ static char buf[1024] = {};
+ static size_t used = 0;
+ char *ptr;
+
+ /* roundup to a multiple of 32 */
+ size = (size - 1) / 32 * 32 + 32;
+
+ if (used + size > sizeof(buf))
{
- __malloc_hook = old_malloc_hook;
- __free_hook = old_free_hook;
- __realloc_hook = old_realloc_hook;
- installed = FALSE;
+ return NULL;
}
+ ptr = buf + used;
+ used += size;
+ return ptr;
}
/**
+ * Lookup a malloc function, while disabling wrappers
+ */
+static void* get_malloc_fn(char *name)
+{
+ bool before = FALSE;
+ void *fn;
+
+ if (enabled)
+ {
+ before = enable_thread(FALSE);
+ }
+ fn = dlsym(RTLD_NEXT, name);
+ if (enabled)
+ {
+ enable_thread(before);
+ }
+ return fn;
+}
+
+/**
+ * Call original malloc()
+ */
+static void* real_malloc(size_t size)
+{
+ static void* (*fn)(size_t size);
+ static int recursive = 0;
+
+ if (!fn)
+ {
+ /* checking recursiveness should actually be thread-specific. But as
+ * it is very likely that the first allocation is done before we go
+ * multi-threaded, we keep it simple. */
+ if (recursive)
+ {
+ return malloc_for_dlsym(size);
+ }
+ recursive++;
+ fn = get_malloc_fn("malloc");
+ recursive--;
+ }
+ return fn(size);
+}
+
+/**
+ * Call original free()
+ */
+static void real_free(void *ptr)
+{
+ static void (*fn)(void *ptr);
+
+ if (!fn)
+ {
+ fn = get_malloc_fn("free");
+ }
+ return fn(ptr);
+}
+
+/**
+ * Call original realloc()
+ */
+static void* real_realloc(void *ptr, size_t size)
+{
+ static void* (*fn)(void *ptr, size_t size);
+
+ if (!fn)
+ {
+ fn = get_malloc_fn("realloc");
+ }
+ return fn(ptr, size);
+}
+
+/**
+ * Hook definition: plain function overloading existing malloc calls
+ */
+#define HOOK(ret, name, ...) ret name(__VA_ARGS__)
+
+/**
+ * Hook initialization when not using hooks
+ */
+static bool register_hooks()
+{
+ return TRUE;
+}
+
+#endif /* !__APPLE__ */
+
+/**
* Leak report white list
*
* List of functions using static allocation buffers or should be suppressed
@@ -194,12 +428,6 @@ char *whitelist[] = {
"pthread_setspecific",
"__pthread_setspecific",
/* glibc functions */
- "mktime",
- "ctime",
- "__gmtime_r",
- "localtime_r",
- "tzset",
- "time_printf_hook",
"inet_ntoa",
"strerror",
"getprotobyname",
@@ -277,6 +505,14 @@ char *whitelist[] = {
"gnutls_global_init",
};
+/**
+ * Some functions are hard to whitelist, as they don't use a symbol directly.
+ * Use some static initialization to suppress them on leak reports
+ */
+static void init_static_allocations()
+{
+ tzset();
+}
/**
* Hashtable hash function
@@ -323,11 +559,13 @@ static int print_traces(private_leak_detective_t *this,
/** number of allocations */
u_int count;
} *entry;
+ bool before;
- uninstall_hooks();
+ before = enable_thread(FALSE);
entries = hashtable_create((hashtable_hash_t)hash,
(hashtable_equals_t)equals, 1024);
+ lock->lock(lock);
for (hdr = first_header.next; hdr != NULL; hdr = hdr->next)
{
if (whitelisted &&
@@ -354,6 +592,7 @@ static int print_traces(private_leak_detective_t *this,
}
leaks++;
}
+ lock->unlock(lock);
enumerator = entries->create_enumerator(entries);
while (enumerator->enumerate(enumerator, NULL, &entry))
{
@@ -368,7 +607,7 @@ static int print_traces(private_leak_detective_t *this,
enumerator->destroy(enumerator);
entries->destroy(entries);
- install_hooks();
+ enable_thread(before);
return leaks;
}
@@ -403,85 +642,65 @@ METHOD(leak_detective_t, report, void,
METHOD(leak_detective_t, set_state, bool,
private_leak_detective_t *this, bool enable)
{
- static struct sched_param oldparams;
- static int oldpolicy;
- struct sched_param params;
- pthread_t thread_id;
-
- if (enable == installed)
+ if (enable == enabled)
{
- return installed;
+ return enabled;
}
- thread_id = pthread_self();
if (enable)
{
- install_hooks();
- pthread_setschedparam(thread_id, oldpolicy, &oldparams);
+ enable_leak_detective();
}
else
{
- pthread_getschedparam(thread_id, &oldpolicy, &oldparams);
- params.__sched_priority = sched_get_priority_max(SCHED_FIFO);
- pthread_setschedparam(thread_id, SCHED_FIFO, &params);
- uninstall_hooks();
+ disable_leak_detective();
}
- installed = enable;
- return !installed;
+ return !enabled;
}
METHOD(leak_detective_t, usage, void,
private_leak_detective_t *this, FILE *out)
{
- int oldpolicy, thresh;
bool detailed;
- pthread_t thread_id = pthread_self();
- struct sched_param oldparams, params;
+ int thresh;
thresh = lib->settings->get_int(lib->settings,
"libstrongswan.leak_detective.usage_threshold", 10240);
detailed = lib->settings->get_bool(lib->settings,
"libstrongswan.leak_detective.detailed", TRUE);
- pthread_getschedparam(thread_id, &oldpolicy, &oldparams);
- params.__sched_priority = sched_get_priority_max(SCHED_FIFO);
- pthread_setschedparam(thread_id, SCHED_FIFO, &params);
-
print_traces(this, out, thresh, detailed, NULL);
-
- pthread_setschedparam(thread_id, oldpolicy, &oldparams);
}
/**
- * Hook function for malloc()
+ * Wrapped malloc() function
*/
-void *malloc_hook(size_t bytes, const void *caller)
+HOOK(void*, malloc, size_t bytes)
{
memory_header_t *hdr;
memory_tail_t *tail;
- pthread_t thread_id = pthread_self();
- int oldpolicy;
- struct sched_param oldparams, params;
+ bool before;
- pthread_getschedparam(thread_id, &oldpolicy, &oldparams);
-
- params.__sched_priority = sched_get_priority_max(SCHED_FIFO);
- pthread_setschedparam(thread_id, SCHED_FIFO, &params);
+ if (!enabled || thread_disabled->get(thread_disabled))
+ {
+ return real_malloc(bytes);
+ }
- count_malloc++;
- uninstall_hooks();
- hdr = malloc(sizeof(memory_header_t) + bytes + sizeof(memory_tail_t));
+ hdr = real_malloc(sizeof(memory_header_t) + bytes + sizeof(memory_tail_t));
tail = ((void*)hdr) + bytes + sizeof(memory_header_t);
/* set to something which causes crashes */
memset(hdr, MEMORY_ALLOC_PATTERN,
sizeof(memory_header_t) + bytes + sizeof(memory_tail_t));
+ before = enable_thread(FALSE);
+ hdr->backtrace = backtrace_create(2);
+ enable_thread(before);
+
hdr->magic = MEMORY_HEADER_MAGIC;
hdr->bytes = bytes;
- hdr->backtrace = backtrace_create(2);
tail->magic = MEMORY_TAIL_MAGIC;
- install_hooks();
/* insert at the beginning of the list */
+ lock->lock(lock);
hdr->next = first_header.next;
if (hdr->next)
{
@@ -489,25 +708,49 @@ void *malloc_hook(size_t bytes, const void *caller)
}
hdr->previous = &first_header;
first_header.next = hdr;
-
- pthread_setschedparam(thread_id, oldpolicy, &oldparams);
+ lock->unlock(lock);
return hdr + 1;
}
/**
- * Hook function for free()
+ * Wrapped calloc() function
+ */
+HOOK(void*, calloc, size_t nmemb, size_t size)
+{
+ void *ptr;
+
+ size *= nmemb;
+ ptr = malloc(size);
+ memset(ptr, 0, size);
+
+ return ptr;
+}
+
+/**
+ * Wrapped valloc(), TODO: currently not supported
+ */
+HOOK(void*, valloc, size_t size)
+{
+ DBG1(DBG_LIB, "valloc() used, but leak-detective hook missing");
+ return NULL;
+}
+
+/**
+ * Wrapped free() function
*/
-void free_hook(void *ptr, const void *caller)
+HOOK(void, free, void *ptr)
{
memory_header_t *hdr, *current;
memory_tail_t *tail;
backtrace_t *backtrace;
- pthread_t thread_id = pthread_self();
- int oldpolicy;
- struct sched_param oldparams, params;
- bool found = FALSE;
+ bool found = FALSE, before;
+ if (!enabled || thread_disabled->get(thread_disabled))
+ {
+ real_free(ptr);
+ return;
+ }
/* allow freeing of NULL */
if (ptr == NULL)
{
@@ -516,16 +759,11 @@ void free_hook(void *ptr, const void *caller)
hdr = ptr - sizeof(memory_header_t);
tail = ptr + hdr->bytes;
- pthread_getschedparam(thread_id, &oldpolicy, &oldparams);
-
- params.__sched_priority = sched_get_priority_max(SCHED_FIFO);
- pthread_setschedparam(thread_id, SCHED_FIFO, &params);
-
- count_free++;
- uninstall_hooks();
+ before = enable_thread(FALSE);
if (hdr->magic != MEMORY_HEADER_MAGIC ||
tail->magic != MEMORY_TAIL_MAGIC)
{
+ lock->lock(lock);
for (current = &first_header; current != NULL; current = current->next)
{
if (current == hdr)
@@ -534,6 +772,7 @@ void free_hook(void *ptr, const void *caller)
break;
}
}
+ lock->unlock(lock);
if (found)
{
/* memory was allocated by our hooks but is corrupted */
@@ -544,7 +783,7 @@ void free_hook(void *ptr, const void *caller)
else
{
/* memory was not allocated by our hooks */
- fprintf(stderr, "freeing invalid memory (%p)", ptr);
+ fprintf(stderr, "freeing invalid memory (%p)\n", ptr);
}
backtrace = backtrace_create(2);
backtrace->log(backtrace, stderr, TRUE);
@@ -553,52 +792,48 @@ void free_hook(void *ptr, const void *caller)
else
{
/* remove item from list */
+ lock->lock(lock);
if (hdr->next)
{
hdr->next->previous = hdr->previous;
}
hdr->previous->next = hdr->next;
+ lock->unlock(lock);
+
hdr->backtrace->destroy(hdr->backtrace);
/* clear MAGIC, set mem to something remarkable */
memset(hdr, MEMORY_FREE_PATTERN,
sizeof(memory_header_t) + hdr->bytes + sizeof(memory_tail_t));
- free(hdr);
+ real_free(hdr);
}
-
- install_hooks();
- pthread_setschedparam(thread_id, oldpolicy, &oldparams);
+ enable_thread(before);
}
/**
- * Hook function for realloc()
+ * Wrapped realloc() function
*/
-void *realloc_hook(void *old, size_t bytes, const void *caller)
+HOOK(void*, realloc, void *old, size_t bytes)
{
memory_header_t *hdr;
memory_tail_t *tail;
backtrace_t *backtrace;
- pthread_t thread_id = pthread_self();
- int oldpolicy;
- struct sched_param oldparams, params;
+ bool before;
+ if (!enabled || thread_disabled->get(thread_disabled))
+ {
+ return real_realloc(old, bytes);
+ }
/* allow reallocation of NULL */
if (old == NULL)
{
- return malloc_hook(bytes, caller);
+ return malloc(bytes);
}
hdr = old - sizeof(memory_header_t);
tail = old + hdr->bytes;
- pthread_getschedparam(thread_id, &oldpolicy, &oldparams);
-
- params.__sched_priority = sched_get_priority_max(SCHED_FIFO);
- pthread_setschedparam(thread_id, SCHED_FIFO, &params);
-
- count_realloc++;
- uninstall_hooks();
if (hdr->magic != MEMORY_HEADER_MAGIC ||
tail->magic != MEMORY_TAIL_MAGIC)
{
@@ -613,33 +848,37 @@ void *realloc_hook(void *old, size_t bytes, const void *caller)
/* clear tail magic, allocate, set tail magic */
memset(&tail->magic, MEMORY_ALLOC_PATTERN, sizeof(tail->magic));
}
- hdr = realloc(hdr, sizeof(memory_header_t) + bytes + sizeof(memory_tail_t));
+ hdr = real_realloc(hdr,
+ sizeof(memory_header_t) + bytes + sizeof(memory_tail_t));
tail = ((void*)hdr) + bytes + sizeof(memory_header_t);
tail->magic = MEMORY_TAIL_MAGIC;
/* update statistics */
hdr->bytes = bytes;
+
+ before = enable_thread(FALSE);
hdr->backtrace->destroy(hdr->backtrace);
hdr->backtrace = backtrace_create(2);
+ enable_thread(before);
/* update header of linked list neighbours */
+ lock->lock(lock);
if (hdr->next)
{
hdr->next->previous = hdr;
}
hdr->previous->next = hdr;
- install_hooks();
- pthread_setschedparam(thread_id, oldpolicy, &oldparams);
+ lock->unlock(lock);
+
return hdr + 1;
}
METHOD(leak_detective_t, destroy, void,
private_leak_detective_t *this)
{
- if (installed)
- {
- uninstall_hooks();
- }
+ disable_leak_detective();
+ lock->destroy(lock);
+ thread_disabled->destroy(thread_disabled);
free(this);
}
@@ -659,20 +898,17 @@ leak_detective_t *leak_detective_create()
},
);
- if (getenv("LEAK_DETECTIVE_DISABLE") == NULL)
- {
- cpu_set_t mask;
+ lock = spinlock_create();
+ thread_disabled = thread_value_create(NULL);
- CPU_ZERO(&mask);
- CPU_SET(0, &mask);
+ init_static_allocations();
- if (sched_setaffinity(0, sizeof(cpu_set_t), &mask) != 0)
+ if (getenv("LEAK_DETECTIVE_DISABLE") == NULL)
+ {
+ if (register_hooks())
{
- fprintf(stderr, "setting CPU affinity failed: %m");
+ enable_leak_detective();
}
-
- install_hooks();
}
return &this->public;
}
-