mirror of
https://github.com/strongswan/strongswan.git
synced 2025-10-05 00:00:45 -04:00
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().
This commit is contained in:
commit
b4e9f74e42
@ -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"
|
||||
|
1
scripts/.gitignore
vendored
1
scripts/.gitignore
vendored
@ -9,6 +9,7 @@ dh_speed
|
||||
pubkey_speed
|
||||
crypt_burn
|
||||
hash_burn
|
||||
malloc_speed
|
||||
tls_test
|
||||
fetch
|
||||
dnssec
|
||||
|
@ -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
|
||||
|
||||
|
85
scripts/malloc_speed.c
Normal file
85
scripts/malloc_speed.c
Normal file
@ -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;
|
||||
}
|
@ -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 = \
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
@ -107,6 +104,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
|
||||
*/
|
||||
@ -136,49 +138,281 @@ 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 bool installed = FALSE;
|
||||
static spinlock_t *lock;
|
||||
|
||||
/**
|
||||
* Is leak detection currently enabled?
|
||||
*/
|
||||
static bool enabled = FALSE;
|
||||
|
||||
/**
|
||||
* Is leak detection disabled for the current thread?
|
||||
*/
|
||||
static thread_value_t *thread_disabled;
|
||||
|
||||
/**
|
||||
* Installs the malloc hooks, enables leak detection
|
||||
*/
|
||||
static void install_hooks()
|
||||
static void enable_leak_detective()
|
||||
{
|
||||
if (!installed)
|
||||
{
|
||||
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;
|
||||
}
|
||||
enabled = TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uninstalls the malloc hooks, disables leak detection
|
||||
*/
|
||||
static void uninstall_hooks()
|
||||
static void disable_leak_detective()
|
||||
{
|
||||
if (installed)
|
||||
{
|
||||
__malloc_hook = old_malloc_hook;
|
||||
__free_hook = old_free_hook;
|
||||
__realloc_hook = old_realloc_hook;
|
||||
installed = FALSE;
|
||||
}
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
enable_thread(before);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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* malloc_for_dlsym(size_t size)
|
||||
{
|
||||
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))
|
||||
{
|
||||
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
|
||||
*
|
||||
@ -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, ¶ms);
|
||||
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, ¶ms);
|
||||
|
||||
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);
|
||||
if (!enabled || thread_disabled->get(thread_disabled))
|
||||
{
|
||||
return real_malloc(bytes);
|
||||
}
|
||||
|
||||
params.__sched_priority = sched_get_priority_max(SCHED_FIFO);
|
||||
pthread_setschedparam(thread_id, SCHED_FIFO, ¶ms);
|
||||
|
||||
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
|
||||
*/
|
||||
void free_hook(void *ptr, const void *caller)
|
||||
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
|
||||
*/
|
||||
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, ¶ms);
|
||||
|
||||
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, ¶ms);
|
||||
|
||||
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()
|
||||
},
|
||||
);
|
||||
|
||||
lock = spinlock_create();
|
||||
thread_disabled = thread_value_create(NULL);
|
||||
|
||||
init_static_allocations();
|
||||
|
||||
if (getenv("LEAK_DETECTIVE_DISABLE") == NULL)
|
||||
{
|
||||
cpu_set_t mask;
|
||||
|
||||
CPU_ZERO(&mask);
|
||||
CPU_SET(0, &mask);
|
||||
|
||||
if (sched_setaffinity(0, sizeof(cpu_set_t), &mask) != 0)
|
||||
if (register_hooks())
|
||||
{
|
||||
fprintf(stderr, "setting CPU affinity failed: %m");
|
||||
enable_leak_detective();
|
||||
}
|
||||
|
||||
install_hooks();
|
||||
}
|
||||
return &this->public;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user