summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorPavel Cisler <pavel@eazel.com>2000-06-14 00:26:58 +0000
committerPavel Cisler <pce@src.gnome.org>2000-06-14 00:26:58 +0000
commit1dc046ab3e5ac9b09dff12df02392f431a4cec95 (patch)
tree3629a77da19c4681901b143080a45cd4cf10f31e /test
parentfd18cb806c4b0344e8b39e8200ebd9690c30f78a (diff)
downloadnautilus-1dc046ab3e5ac9b09dff12df02392f431a4cec95.tar.gz
New small leakchecking library. It is a bit simpler than MemProf in that
2000-06-13 Pavel Cisler <pavel@eazel.com> * test/nautilus-leak-checker-stubs.h: * test/nautilus-leak-checker.c: (nautilus_leak_allocation_record_init), (nautilus_leak_allocation_record_copy), (nautilus_leak_allocation_record_finalize), (nautilus_leak_allocation_record_free), (nautilus_leak_stack_crawl_compare), (nautilus_leak_initialize), (get_stack_trace), (detect_reentry), (nautilus_leak_record_malloc), (nautilus_leak_record_realloc), (nautilus_leak_record_free), (nautilus_leak_initialize_if_needed), (__libc_malloc), (__libc_memalign), (__libc_calloc), (__libc_realloc), (__libc_free), (malloc), (realloc), (memalign), (calloc), (free), (print_one_leak), (nautilus_leak_print_leaks), (nautilus_leak_checker_init), (allocate_lots), (leak_mem2), (leak_mem), (main): * test/nautilus-leak-checker.h: * test/nautilus-leak-hash-table.c: (nautilus_leak_hash_element_finalize), (nautilus_leak_hash_element_hash), (nautilus_leak_hash_element_match), (nautilus_leak_hash_element_vector_inititalize), (nautilus_leak_hash_element_vector_finalize), (nautilus_leak_hash_element_vector_at), (nautilus_leak_hash_element_vector_add), (nautilus_leak_hash_element_vector_remove), (nautilus_leak_hash_table_optimal_size), (nautilus_leak_hash_table_initialize), (nautilus_leak_hash_table_finalize), (nautilus_leak_hash_table_new), (nautilus_leak_hash_table_free), (nautilus_leak_hash_table_hash), (nautilus_leak_hash_table_find), (nautilus_leak_hash_table_add), (nautilus_leak_hash_table_remove_element), (nautilus_leak_hash_table_remove), (nautilus_leak_table_new_entry_at), (nautilus_leak_table_add_entry), (nautilus_leak_table_new), (nautilus_leak_table_free), (sort_by_count), (nautilus_leak_table_sort_by_count), (sort_by_size), (nautilus_leak_table_sort_by_size), (nautilus_leak_table_each_item): * test/nautilus-leak-hash-table.h: * test/nautilus-leak-symbol-lookup.c: (nautilus_leak_find_symbol_in_map), (nautilus_leak_symbol_map_load), (nautilus_leak_symbol_map_load_if_needed), (nautilus_leak_print_symbol_cleanup), (nautilus_leak_find_symbol_address), (nautilus_leak_print_symbol_address): * test/nautilus-leak-symbol-lookup.h: New small leakchecking library. It is a bit simpler than MemProf in that it doesn't try to find leaks, just lists all the outstanding allocations (which if done at application quit is a list of leaks). It borrows heavily from MemProf. * test/Makefile.am: Currently broken makefile changes for the leakchecker library. Needs some libtool wrestling that Ramiro kindly offered to help with. * src/nautilus-main.c: (nautilus_leak_checker_init), (nautilus_leak_print_leaks), (main): Call the leakchecker. Noop unless the leakchecker lib is loaded with Nautilus using the LD_PRELOAD variable (the only way of turning the thing on/off).
Diffstat (limited to 'test')
-rw-r--r--test/Makefile.am28
-rw-r--r--test/nautilus-leak-checker-stubs.h78
-rw-r--r--test/nautilus-leak-checker.c693
-rw-r--r--test/nautilus-leak-checker.h54
-rw-r--r--test/nautilus-leak-hash-table.c511
-rw-r--r--test/nautilus-leak-hash-table.h57
-rw-r--r--test/nautilus-leak-symbol-lookup.c223
-rw-r--r--test/nautilus-leak-symbol-lookup.h31
8 files changed, 1675 insertions, 0 deletions
diff --git a/test/Makefile.am b/test/Makefile.am
index b6d026869..2f84aa833 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -1,5 +1,7 @@
NULL=
+lib_LTLIBRARIES = libleakcheck.la
+
INCLUDES =\
-I$(top_srcdir) \
-I$(top_builddir) \
@@ -38,6 +40,32 @@ test_nautilus_mime_actions_SOURCES = test-nautilus-mime-actions.c
test_nautilus_mime_actions_set_SOURCES = test-nautilus-mime-actions-set.c
+libleakcheck_la_SOURCES = \
+ nautilus-leak-checker.c \
+ nautilus-leak-checker.h \
+ nautilus-leak-checker-stubs.h \
+ nautilus-leak-hash-table.c \
+ nautilus-leak-hash-table.h \
+ nautilus-leak-symbol-lookup.c \
+ nautilus-leak-symbol-lookup.h \
+ $(NULL)
+
+# we need to link libbfd and liberty statically
+# this is currently broken because of libtool dumbness
+
+# LIBLEAKCHECK_STATIC_LIBS = "-Wl,-Bstatic -lbfd -liberty -Wl,-Bdynamic"
+
+libleakcheck_la_LDFLAGS = -module -avoid-version $(LIBLEAKCHECK_STATIC_LIBS)
+libleakcheck_la_LIBADD = -ldl
+
EXTRA_DIST = \
test-nautilus-mime-actions.c \
$(NULL)
+
+# the link rule is here so we can remove -rpath $(libdir)
+# however -
+# if we leave -rpath $(libdir) here, libtool refuses to link with -liberty
+# if we dont, .libs/libleakcheck.lai doesn't get generated and make install fails
+
+libleakcheck.la: $(libleakcheck_la_OBJECTS) $(libleakcheck_la_DEPENDENCIES)
+ $(LINK) -rpath $(libdir) $(libleakcheck_la_LDFLAGS) $(libleakcheck_la_OBJECTS) $(libleakcheck_la_LIBADD) $(LIBS)
diff --git a/test/nautilus-leak-checker-stubs.h b/test/nautilus-leak-checker-stubs.h
new file mode 100644
index 000000000..8f175dfda
--- /dev/null
+++ b/test/nautilus-leak-checker-stubs.h
@@ -0,0 +1,78 @@
+/* nautilus-leak-checker-stubs.h - simple leak checking library
+ Virtual File System Library
+
+ Copyright (C) 2000 Eazel
+
+ The Gnome Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The Gnome Library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the Gnome Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ Author: Pavel Cisler <pavel@eazel.com>
+ based on MemProf by Owen Taylor, <otaylor@redhat.com>
+*/
+
+#ifndef LEAK_CHECKER_STUBS_H
+#define LEAK_CHECKER_STUBS_H
+
+
+extern void *(* real_malloc) (size_t size);
+extern void *(* real_memalign) (size_t boundary, size_t size);
+extern void *(* real_realloc) (void *ptr, size_t size);
+extern void *(* real_calloc) (void *ptr, size_t size);
+extern void (*real_free) (void *ptr);
+
+
+void *__libc_malloc (size_t size);
+void *__libc_memalign (size_t boundary, size_t size);
+void *__libc_calloc (size_t count, size_t size);
+void *__libc_realloc (void *ptr, size_t size);
+void __libc_free (void *ptr);
+
+/* Records the context of an allocation.
+ * We could add pid, allocation time, etc. if needed.
+ */
+typedef struct {
+ /* pointer returned by malloc/realloc */
+ void *block;
+
+ /* allocated size */
+ size_t size;
+
+ /* NULL-terminated array of return addresses */
+ void **stack_crawl;
+} NautilusLeakAllocationRecord;
+
+void nautilus_leak_allocation_record_finalize
+ (NautilusLeakAllocationRecord *record);
+void nautilus_leak_allocation_record_free (NautilusLeakAllocationRecord *record);
+void nautilus_leak_allocation_record_init (NautilusLeakAllocationRecord *record,
+ void *block,
+ size_t initial_size,
+ void **stack_crawl,
+ int max_depth);
+NautilusLeakAllocationRecord *nautilus_leak_allocation_record_copy
+ (const NautilusLeakAllocationRecord *record);
+
+int nautilus_leak_stack_crawl_compare (void **stack_crawl1,
+ void **stack_crawl2,
+ int levels);
+
+/* Hash table entry. */
+struct NautilusHashEntry {
+ int next;
+ NautilusLeakAllocationRecord data;
+};
+
+
+#endif \ No newline at end of file
diff --git a/test/nautilus-leak-checker.c b/test/nautilus-leak-checker.c
new file mode 100644
index 000000000..ffc105607
--- /dev/null
+++ b/test/nautilus-leak-checker.c
@@ -0,0 +1,693 @@
+/* nautilus-leak-checker.c - simple leak checking library
+ Virtual File System Library
+
+ Copyright (C) 2000 Eazel
+
+ The Gnome Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The Gnome Library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the Gnome Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ Author: Pavel Cisler <pavel@eazel.com>
+ based on MemProf by Owen Taylor, <otaylor@redhat.com>
+*/
+
+#include "nautilus-leak-checker.h"
+/* included first, defines following switch*/
+#if LEAK_CHECKER
+
+#include <malloc.h>
+#include <dlfcn.h>
+#include <malloc.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "nautilus-leak-checker-stubs.h"
+#include "nautilus-leak-hash-table.h"
+#include "nautilus-leak-symbol-lookup.h"
+
+/* this is the maximum number of stack crawl levels we can capture */
+enum {
+ TRACE_ARRAY_MAX = 512
+};
+
+static volatile gboolean nautilus_leak_hooks_initialized;
+static volatile gboolean nautilus_leak_initializing_hooks;
+static volatile gboolean nautilus_leak_check_leaks;
+
+void *(* real_malloc) (size_t size);
+void *(* real_memalign) (size_t boundary, size_t size);
+void *(* real_realloc) (void *ptr, size_t size);
+void *(* real_calloc) (void *ptr, size_t size);
+void (* real_free) (void *ptr);
+
+const char *app_path;
+
+void
+nautilus_leak_allocation_record_init (NautilusLeakAllocationRecord *record, void *block, size_t initial_size,
+ void **stack_crawl, int max_depth)
+{
+ int stack_depth, index;
+
+ record->block = block;
+ record->size = initial_size;
+
+ for (index = 0; index < max_depth; index++) {
+ if (stack_crawl[index] == NULL)
+ break;
+ }
+
+ stack_depth = index;
+
+ /* call real_malloc to avoid recursion and messing up results */
+ record->stack_crawl = real_malloc ((stack_depth + 1) * sizeof(void *));
+ memcpy (record->stack_crawl, stack_crawl, stack_depth * sizeof(void *));
+ record->stack_crawl[stack_depth] = NULL;
+}
+
+NautilusLeakAllocationRecord *
+nautilus_leak_allocation_record_copy (const NautilusLeakAllocationRecord *record)
+{
+ int stack_depth, index;
+ NautilusLeakAllocationRecord *result;
+
+ result = real_malloc (sizeof(*result));
+
+ result->block = record->block;
+ result->size = record->size;
+
+ for (index = 0; ; index++) {
+ if (record->stack_crawl[index] == NULL)
+ break;
+ }
+ stack_depth = index;
+ result->stack_crawl = real_malloc ((stack_depth + 1) * sizeof(void *));
+ memcpy (result->stack_crawl, record->stack_crawl, (stack_depth + 1) * sizeof(void *));
+
+ return result;
+}
+
+void
+nautilus_leak_allocation_record_finalize (NautilusLeakAllocationRecord *record)
+{
+ /* call real_free to avoid recursion and messing up results */
+ real_free (record->stack_crawl);
+}
+
+void
+nautilus_leak_allocation_record_free (NautilusLeakAllocationRecord *record)
+{
+ /* call real_free to avoid recursion and messing up results */
+ real_free (record->stack_crawl);
+ real_free (record);
+}
+
+/* return a strcmp-like result to be used in sort funcitons */
+int
+nautilus_leak_stack_crawl_compare (void **stack_crawl1, void **stack_crawl2, int levels)
+{
+ int index;
+ for (index = 0; index < levels; index++) {
+ if (stack_crawl1 [index] == NULL && stack_crawl2 [index] == NULL) {
+ return 0;
+ }
+
+ if (stack_crawl1 [index] < stack_crawl2 [index]) {
+ return -1;
+ } else if (stack_crawl1 [index] > stack_crawl2 [index]) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static void
+nautilus_leak_initialize (void)
+{
+ /* Locate the original malloc calls. The dlsym calls
+ * will allocate memory when doing lookups. We use a special
+ * trick to deal with the fact that real_malloc is not set up
+ * yet while we are doing the first dlsym -- see malloc.
+ */
+ real_malloc = dlsym (RTLD_NEXT, "__libc_malloc");
+ real_realloc = dlsym (RTLD_NEXT, "__libc_realloc");
+ real_free = dlsym (RTLD_NEXT, "__libc_free");
+ real_memalign = dlsym (RTLD_NEXT, "__libc_memalign");
+ real_calloc = dlsym (RTLD_NEXT, "__libc_calloc");
+
+ nautilus_leak_hooks_initialized = TRUE;
+ nautilus_leak_check_leaks = TRUE;
+}
+
+typedef struct StackFrame StackFrame;
+
+struct StackFrame {
+ StackFrame *next_frame;
+ void *return_address;
+ /* first argument is here */
+};
+
+static void
+get_stack_trace (void **trace_array, int trace_array_max)
+{
+ int index;
+ const StackFrame *stack_frame;
+
+ /* point to the stack frame pointer, two words before the
+ * address of the first argument
+ */
+ stack_frame = (const StackFrame *)((void **)&trace_array - 2);
+
+
+ /* Record stack frames; skip first two in the malloc calls. */
+ for (index = -2; index < trace_array_max; index++) {
+ if (index >= 0) {
+ /* Return address is the next pointer after
+ * stack frame.
+ */
+ trace_array [index] = stack_frame->return_address;
+ }
+ stack_frame = stack_frame->next_frame;
+ if (stack_frame == NULL) {
+ break;
+ }
+ }
+ if (index < trace_array_max) {
+ trace_array [index] = NULL;
+ }
+}
+
+/* Figure out if one of the malloc calls is reentering itself.
+ * There seems no cleaner way to handle this -- we want to override
+ * malloc calls at the __libc_* level.
+ * The malloc hooks recurse when initializing themselves.
+ * When this happens we need a reliable way to tell that a recursion is
+ * underway so as to not record the corresponding malloc call twice
+ */
+static gboolean
+detect_reentry (void *parent_caller)
+{
+ int count;
+ const StackFrame *stack_frame;
+
+ stack_frame = (const StackFrame *)((void **)&parent_caller - 2);
+ stack_frame = stack_frame->next_frame;
+
+ for (count = 0; count < 7; count++) {
+ /* See if we return address on the stack
+ * that is near the parent_caller -- the start address
+ * of the calling function.
+ * We are using two arbitrary numbers here - 5 should be a "deep enough"
+ * check to detect the recursion, 512 bytes should be a large enough distance
+ * from the start of the malloc call to the point where the old malloc call is
+ * being called.
+ */
+ if (stack_frame->return_address >= parent_caller
+ && stack_frame->return_address < (void *)((char *)parent_caller + 0x100)) {
+ /* printf("detected reentry at level %d\n", count); */
+ return TRUE;
+ }
+ stack_frame = stack_frame->next_frame;
+ if (stack_frame == NULL) {
+ break;
+ }
+ }
+ return FALSE;
+}
+
+static pthread_mutex_t nautilus_leak_hash_table_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
+static NautilusLeakHashTable *hash_table;
+
+static int nautilus_leak_malloc_count;
+
+static void
+nautilus_leak_record_malloc (void *ptr, size_t size)
+{
+ void *trace_array [TRACE_ARRAY_MAX];
+ NautilusHashEntry *element;
+
+/* printf("new block %p, %d\n", ptr, size); */
+
+ if (!nautilus_leak_check_leaks)
+ return;
+
+ ++nautilus_leak_malloc_count;
+
+ get_stack_trace (trace_array, TRACE_ARRAY_MAX);
+
+ pthread_mutex_lock (&nautilus_leak_hash_table_mutex);
+
+ if (hash_table == NULL) {
+ hash_table = nautilus_leak_hash_table_new (10 * 1024);
+ }
+ if (nautilus_leak_hash_table_find (hash_table, (guint)ptr) != NULL) {
+ printf("*** block %p appears to already be allocated "
+ "- someone must have sneaked a free past us\n", ptr);
+ nautilus_leak_hash_table_remove (hash_table, (guint)ptr);
+ }
+ /* insert a new item into the hash table, using the block address as the key */
+ element = nautilus_leak_hash_table_add (hash_table, (guint)ptr);
+
+ /* fill out the new allocated element */
+ nautilus_leak_allocation_record_init (&element->data, ptr, size, trace_array, TRACE_ARRAY_MAX);
+
+ pthread_mutex_unlock (&nautilus_leak_hash_table_mutex);
+}
+
+static void
+nautilus_leak_record_realloc (void *old_ptr, void *new_ptr, size_t size)
+{
+ void *trace_array [TRACE_ARRAY_MAX];
+ NautilusHashEntry *element;
+
+/* printf("reallocing block %p, %d was %p\n", new_ptr, size, old_ptr); */
+
+ if (!nautilus_leak_check_leaks)
+ return;
+
+ get_stack_trace (trace_array, TRACE_ARRAY_MAX);
+
+ pthread_mutex_lock (&nautilus_leak_hash_table_mutex);
+
+ /* must have hash table by now */
+ g_assert (hash_table != NULL);
+ /* must have seen the block already */
+ if (nautilus_leak_hash_table_find (hash_table, (guint)old_ptr) == NULL) {
+ printf("*** we haven't seen block %p yet "
+ "- someone must have sneaked a malloc past us\n", old_ptr);
+ } else {
+ nautilus_leak_hash_table_remove (hash_table, (guint)old_ptr);
+ }
+
+ /* shouldn't have this block yet */
+ if (nautilus_leak_hash_table_find (hash_table, (guint)new_ptr) != NULL) {
+ printf("*** block %p appears to already be allocated "
+ "- someone must have sneaked a free past us\n", new_ptr);
+ nautilus_leak_hash_table_remove (hash_table, (guint)new_ptr);
+ }
+
+ /* insert a new item into the hash table, using the block address as the key */
+ element = nautilus_leak_hash_table_add (hash_table, (guint)new_ptr);
+
+ /* Fill out the new allocated element.
+ * This way the last call to relloc will be the stack crawl that shows up in the
+ * final balance.
+ */
+ nautilus_leak_allocation_record_init (&element->data, new_ptr, size, trace_array, TRACE_ARRAY_MAX);
+
+ pthread_mutex_unlock (&nautilus_leak_hash_table_mutex);
+}
+
+static void
+nautilus_leak_record_free (void *ptr)
+{
+/* printf("freeing block %p\n", ptr); */
+ if (!nautilus_leak_check_leaks)
+ return;
+
+ --nautilus_leak_malloc_count;
+
+ pthread_mutex_lock (&nautilus_leak_hash_table_mutex);
+
+ /* must have hash table by now */
+ g_assert (hash_table != NULL);
+ /* must have seen the block already */
+ if (nautilus_leak_hash_table_find (hash_table, (guint)ptr) == NULL) {
+ printf("*** we haven't seen block %p yet "
+ "- someone must have sneaked a malloc past us\n", ptr);
+ } else {
+ nautilus_leak_hash_table_remove (hash_table, (guint)ptr);
+ }
+ pthread_mutex_unlock (&nautilus_leak_hash_table_mutex);
+
+}
+
+static void
+nautilus_leak_initialize_if_needed (void)
+{
+ if (nautilus_leak_hooks_initialized)
+ return;
+
+ if (nautilus_leak_initializing_hooks)
+ /* guard against reentrancy */
+ return;
+
+ nautilus_leak_initializing_hooks = TRUE;
+ nautilus_leak_initialize ();
+ nautilus_leak_initializing_hooks = FALSE;
+}
+
+/* we are overlaying the original __libc_malloc */
+enum {
+ STARTUP_FALLBACK_MEMORY_SIZE = 1024
+};
+
+static int startup_fallback_memory_index = 0;
+static char startup_fallback_memory [STARTUP_FALLBACK_MEMORY_SIZE];
+
+void *
+__libc_malloc (size_t size)
+{
+ void *result;
+
+ nautilus_leak_initialize_if_needed ();
+ if (real_malloc == NULL) {
+ /* Our malloc hook is not installed yet - we are being
+ * called from the initialize_if_needed routine. We
+ * have to fall back on returning a chunk of static
+ * memory to carry us through the initialization
+ */
+
+ /* align to natural word boundary */
+ size = (size + sizeof(void *) - 1 ) & ~(sizeof(void *) - 1);
+ if (size + startup_fallback_memory_index
+ > STARTUP_FALLBACK_MEMORY_SIZE) {
+ g_warning ("trying to allocate to much space during startup");
+ return NULL;
+ }
+ result = &startup_fallback_memory [startup_fallback_memory_index];
+ startup_fallback_memory_index += size;
+
+ return result;
+ }
+ result = (*real_malloc) (size);
+
+ if (result != NULL) {
+ if (detect_reentry(&__libc_malloc)) {
+ printf("avoiding reentry in __libc_malloc\n");
+ } else {
+ nautilus_leak_record_malloc (result, size);
+ }
+ }
+
+ return result;
+}
+
+void *
+__libc_memalign (size_t boundary, size_t size)
+{
+ void *result;
+
+ nautilus_leak_initialize_if_needed ();
+ result = (*real_memalign) (boundary, size);
+
+ if (result != NULL) {
+ if (detect_reentry(&__libc_memalign)) {
+ printf("avoiding reentry in __libc_memalign\n");
+ } else {
+ nautilus_leak_record_malloc (result, size);
+ }
+ }
+
+ return result;
+}
+
+/* We are implementing __libc_calloc by calling __libc_malloc and memset
+ * instead of calling real_calloc for a reason. dlsym calls
+ * calloc and this way we prevent recursion during initialization.
+ * If we didn't do this, we would have to teach __libc_calloc to use
+ * fallback startup memory like __libc_malloc does.
+ */
+void *
+__libc_calloc (size_t count, size_t size)
+{
+ size_t total;
+ void *result;
+
+ total = count * size;
+ nautilus_leak_initialize_if_needed ();
+ result = __libc_malloc (total);
+
+ if (result != NULL) {
+ memset (result, 0, total);
+ }
+
+ return result;
+}
+
+void *
+__libc_realloc (void *ptr, size_t size)
+{
+ void *result;
+ nautilus_leak_initialize_if_needed ();
+ result = (*real_realloc) (ptr, size);
+
+ if (detect_reentry(&__libc_realloc)) {
+ printf("avoiding reentry in __libc_realloc\n");
+ } else {
+ if (result != NULL && ptr == NULL) {
+ /* we are allocating a new block */
+ nautilus_leak_record_malloc (result, size);
+ } else {
+ nautilus_leak_record_realloc (ptr, result, size);
+ }
+ }
+ return result;
+}
+
+void
+__libc_free (void *ptr)
+{
+ nautilus_leak_initialize_if_needed ();
+
+ if (ptr > (void *)&startup_fallback_memory[0]
+ && ptr < (void *)&startup_fallback_memory[STARTUP_FALLBACK_MEMORY_SIZE]) {
+ /* this is a temporary startup fallback block, don't do anything
+ * with it
+ */
+ return;
+ }
+ if (ptr != NULL) {
+ nautilus_leak_record_free (ptr);
+ }
+
+ (real_free) (ptr);
+}
+
+void *
+malloc (size_t size)
+{
+ return __libc_malloc (size);
+}
+
+void *
+realloc (void *ptr, size_t size)
+{
+ return __libc_realloc (ptr, size);
+}
+
+void *
+memalign (size_t boundary, size_t size)
+{
+ return __libc_memalign (boundary, size);
+}
+
+void *
+calloc (size_t nmemb, size_t size)
+{
+ return __libc_calloc (nmemb, size);
+}
+
+void
+free (void *ptr)
+{
+ __libc_free (ptr);
+}
+
+typedef struct {
+ int max_count;
+ int counter;
+ int stack_print_depth;
+} PrintOneLeakParams;
+
+
+/* we don't care if printf, etc. allocates (as long as it doesn't leak)
+ * because by now we have a snapshot of the leaks at the time of
+ * calling nautilus_leak_print_leaks
+ */
+static gboolean
+print_one_leak (NautilusLeakTableEntry *entry, void *context)
+{
+ int index;
+ PrintOneLeakParams *params = (PrintOneLeakParams *)context;
+
+ printf("block %p total_size %d count %d\n", entry->sample_allocation->block,
+ entry->total_size, entry->count);
+
+ for (index = 0; index < params->stack_print_depth; index++) {
+ /* only print stack_grouping worth of stack crawl -
+ * beyond that different blocks may have different addresses
+ * and we would be printing a lie
+ */
+ if (entry->sample_allocation->stack_crawl[index] == NULL)
+ break;
+ printf("\t");
+
+ nautilus_leak_print_symbol_address (app_path,
+ entry->sample_allocation->stack_crawl[index]);
+ }
+
+ /* only print max_counter groups */
+ return params->counter++ < params->max_count;
+}
+
+void
+nautilus_leak_print_leaks (int stack_grouping_depth, int stack_print_depth,
+ int max_count, gboolean sort_by_count)
+{
+ NautilusLeakTable *temp_leak_table;
+ PrintOneLeakParams each_context;
+
+ pthread_mutex_lock (&nautilus_leak_hash_table_mutex);
+ /* must have hash table by now */
+ g_assert (hash_table != NULL);
+
+ /* Build a leak table by grouping blocks with the same
+ * stackcrawls (stack_grouping_depth levels considered)
+ * from the allocated block hash table.
+ */
+ temp_leak_table = nautilus_leak_table_new (hash_table, stack_grouping_depth);
+ pthread_mutex_unlock (&nautilus_leak_hash_table_mutex);
+
+ printf("%d outstanding allocations ============ \n", nautilus_leak_malloc_count);
+ printf("stack trace match depth %d\n", stack_grouping_depth);
+
+ /* sort the leak table */
+ if (sort_by_count) {
+ nautilus_leak_table_sort_by_count (temp_leak_table);
+ } else {
+ nautilus_leak_table_sort_by_size (temp_leak_table);
+ }
+
+ /* we have a sorted table of all the leakers, we can print it out. */
+ each_context.counter = 0;
+ each_context.max_count = max_count;
+ each_context.stack_print_depth = stack_print_depth;
+ nautilus_leak_table_each_item (temp_leak_table, print_one_leak, &each_context);
+
+ /* we are done with it, free the leak table */
+ nautilus_leak_table_free (temp_leak_table);
+
+ /* we are done with it, clean up cached up data used by the symbol lookup */
+ nautilus_leak_print_symbol_cleanup ();
+}
+
+void
+nautilus_leak_checker_init (const char *path)
+{
+ /* we should get rid of this and find another way to find our
+ * binary's name
+ */
+ printf("setting up the leakchecker for %s\n", path);
+ app_path = path;
+}
+
+#endif
+
+#ifdef LEAK_CHECK_TESTING
+/* normally disabled */
+
+static void
+allocate_lots(int count)
+{
+ GList *list;
+ GList *p;
+
+ list = NULL;
+ for (; count > 0; count--) {
+ list = g_list_prepend (list, g_malloc (rand() % 256));
+ }
+ for (p = list; p != NULL; p = p->next) {
+ g_free (p->data);
+ }
+ g_list_free (list);
+}
+
+
+static void
+leak_mem2(void)
+{
+ int i;
+ for (i = 0; i < 40; i++) {
+ g_strdup("bla");
+ }
+ allocate_lots (128);
+}
+
+static void
+leak_mem(void)
+{
+ int i;
+ for (i = 0; i < 1010; i++) {
+ malloc(13);
+ }
+ leak_mem2();
+ allocate_lots (200);
+}
+
+
+int
+main (int argc, char **argv)
+{
+ void *non_leak;
+ void *leak;
+ int i;
+
+ nautilus_leak_checker_init (*argv);
+
+ non_leak = g_malloc(100);
+ leak = g_malloc(200);
+ g_assert(non_leak != NULL);
+ non_leak = g_realloc(non_leak, 1000);
+ g_assert(non_leak != NULL);
+ non_leak = g_realloc(non_leak, 10000);
+ leak = g_malloc(200);
+ non_leak = g_realloc(non_leak, 100000);
+ leak = g_malloc(200);
+ g_assert(non_leak != NULL);
+ g_free(non_leak);
+
+ non_leak = calloc(1, 100);
+ g_assert(non_leak != NULL);
+ g_free(non_leak);
+ leak = g_malloc(200);
+
+ non_leak = memalign(16, 100);
+ g_assert(non_leak != NULL);
+ g_free(non_leak);
+ leak = g_malloc(200);
+ leak = memalign(16, 100);
+ leak = memalign(16, 100);
+ leak = memalign(16, 100);
+ leak = memalign(16, 100);
+ leak = memalign(16, 100);
+ leak = memalign(16, 100);
+
+ for (i = 0; i < 13; i++) {
+ leak = malloc(13);
+ }
+
+ leak_mem();
+ leak_mem2();
+
+ for (i = 0; i < 100; i++) {
+ allocate_lots(rand() % 40);
+ }
+ printf("done\n");
+ nautilus_leak_print_leaks (6, 12, 20, TRUE);
+
+ return 0;
+}
+
+#endif \ No newline at end of file
diff --git a/test/nautilus-leak-checker.h b/test/nautilus-leak-checker.h
new file mode 100644
index 000000000..b58a633dd
--- /dev/null
+++ b/test/nautilus-leak-checker.h
@@ -0,0 +1,54 @@
+/* nautilus-leak-checker.h - simple leak checking library
+ Virtual File System Library
+
+ Copyright (C) 2000 Eazel
+
+ The Gnome Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The Gnome Library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the Gnome Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ Author: Pavel Cisler <pavel@eazel.com>
+ based on MemProf by Owen Taylor, <otaylor@redhat.com>
+*/
+
+#ifndef LEAK_CHECKER_H
+#define LEAK_CHECKER_H
+
+#define LEAK_CHECKER 1
+
+#if LEAK_CHECKER
+
+#define __USE_GNU
+#define _GNU_SOURCE
+/* need this for dlsym */
+#include <pthread.h>
+#include <glib.h>
+
+/* This is a leakchecker simpler than MemProf - it tracks all the outstanding
+ * allocations and allows you print a total when your app quits. It doesn't actually
+ * try to identify leaks like MemProf does. The entire leakchecker machinery runs
+ * in the same process as the target app and shares the same heap for it's data
+ * structures.
+ */
+
+extern void nautilus_leak_checker_init (const char *app_path);
+
+extern void nautilus_leak_print_leaks (int stack_grouping_depth,
+ int stack_print_depth,
+ int max_count,
+ gboolean sort_by_count);
+
+#endif
+
+#endif
diff --git a/test/nautilus-leak-hash-table.c b/test/nautilus-leak-hash-table.c
new file mode 100644
index 000000000..5f5b5b5a9
--- /dev/null
+++ b/test/nautilus-leak-hash-table.c
@@ -0,0 +1,511 @@
+/* nautilus-leak-hash-table.c - hash table for a leak checking library
+ Virtual File System Library
+
+ Copyright (C) 2000 Eazel
+
+ The Gnome Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The Gnome Library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the Gnome Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ Author: Pavel Cisler <pavel@eazel.com>
+*/
+
+#include "nautilus-leak-checker.h"
+/* included first, defines following switch*/
+#if LEAK_CHECKER
+
+#include <glib.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "nautilus-leak-checker-stubs.h"
+#include "nautilus-leak-hash-table.h"
+
+/* We have our own hash table here mainly to allow avoiding calls to
+ * malloc and realloc that would cause reentry
+ */
+
+static void
+nautilus_leak_hash_element_finalize (NautilusHashEntry *element)
+{
+ nautilus_leak_allocation_record_finalize (&element->data);
+ memset (element, 0, sizeof(NautilusHashEntry));
+}
+
+static unsigned long
+nautilus_leak_hash_element_hash (NautilusHashEntry *element)
+{
+ return (unsigned long)element->data.block;
+}
+
+static gboolean
+nautilus_leak_hash_element_match (NautilusHashEntry *element, unsigned long key)
+{
+ return (unsigned long)element->data.block == key;
+}
+
+/* NautilusHashEntries are allocated inside a NautilusHashEntryVector.
+ * NautilusHashEntryVector keeps a linked list of deleted entries.
+ */
+typedef struct {
+ NautilusHashEntry *data;
+ size_t size;
+ int next_free;
+ int next_deleted;
+} NautilusHashEntryVector;
+
+static void
+nautilus_leak_hash_element_vector_inititalize (NautilusHashEntryVector *vector, size_t initial_size)
+{
+ int index;
+
+ vector->data = (NautilusHashEntry *)real_malloc(initial_size * sizeof(NautilusHashEntry));
+ if (vector->data == NULL) {
+ g_warning ("leak checker out of memory");
+ abort();
+ }
+ memset (vector->data, 0, initial_size * sizeof(NautilusHashEntry));
+ for (index = initial_size - 1; index >= 0; index --) {
+ vector->data[index].next = -1;
+ }
+
+ vector->size = initial_size;
+ vector->next_free = 0;
+ vector->next_deleted = -1;
+}
+
+static void
+nautilus_leak_hash_element_vector_finalize (NautilusHashEntryVector *vector)
+{
+ int index;
+ for (index = 0; index < vector->size; index++) {
+ nautilus_leak_hash_element_finalize (&vector->data[index]);
+ }
+ real_free (vector->data);
+}
+
+static NautilusHashEntry *
+nautilus_leak_hash_element_vector_at (NautilusHashEntryVector *vector, int index)
+{
+ return &vector->data[index];
+}
+
+enum {
+ HASH_ELEMENT_VECTOR_GROW_CHUNK = 1024
+};
+
+static int
+nautilus_leak_hash_element_vector_add (NautilusHashEntryVector *vector)
+{
+ int index;
+ NautilusHashEntry *new_element;
+ size_t new_size;
+ NautilusHashEntry *new_data;
+
+ if (vector->next_deleted >= 0) {
+ /* Reuse a previously deleted item. */
+ index = vector->next_deleted;
+ vector->next_deleted = nautilus_leak_hash_element_vector_at (vector, index)->next;
+ } else if (vector->next_free >= vector->size - 1) {
+ /* We need grow the vector because it cannot fit more entries. */
+ new_size = vector->size + HASH_ELEMENT_VECTOR_GROW_CHUNK;
+ new_data = (NautilusHashEntry *)real_malloc(new_size * sizeof(NautilusHashEntry));
+ if (new_data == NULL) {
+ g_warning ("leak checker out of memory");
+ abort();
+ }
+ /* FIXME: only clean the unused part */
+ memset (new_data, 0, new_size * sizeof(NautilusHashEntry));
+
+ /* copy all the existing items over*/
+ memcpy (new_data, vector->data, vector->size * sizeof(NautilusHashEntry));
+ /* delete the old array */
+ real_free (vector->data);
+ vector->data = new_data;
+ vector->size = new_size;
+ index = vector->next_free;
+ ++vector->next_free;
+ } else {
+ /* Just take the next free item. */
+ index = vector->next_free;
+ ++vector->next_free;
+ }
+
+ /* Initialize the new element to an empty state. */
+ new_element = nautilus_leak_hash_element_vector_at (vector, index);
+ memset (new_element, 0, sizeof(NautilusHashEntry));
+ new_element->next = -1;
+
+ return index;
+}
+
+static void
+nautilus_leak_hash_element_vector_remove (NautilusHashEntryVector *vector, int index)
+{
+ /* free the data */
+ nautilus_leak_hash_element_finalize (&vector->data[index]);
+
+ /* insert item as first into deleted item list */
+ nautilus_leak_hash_element_vector_at (vector, index)->next = vector->next_deleted;
+ vector->next_deleted = index;
+}
+
+struct NautilusLeakHashTable {
+ size_t array_size;
+ int *hash_array;
+ NautilusHashEntryVector element_vector;
+};
+
+/* These primes are close to 2^n numbers for optimal hashing performance
+ * and near-2^n size.
+ */
+long nautilus_leak_hash_table_primes [] = {
+ 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521, 131071, 262139,
+ 524287, 1048573, 2097143, 4194301, 8388593, 16777213, 33554393, 67108859,
+ 134217689, 268435399, 536870909, 1073741789, 2147483647, 0
+};
+
+
+static size_t
+nautilus_leak_hash_table_optimal_size (size_t size)
+{
+ int index;
+ for (index = 0; ; index++) {
+ if (!nautilus_leak_hash_table_primes [index] || nautilus_leak_hash_table_primes [index] >= size) {
+ return nautilus_leak_hash_table_primes [index];
+ }
+ }
+
+ return 0;
+}
+
+static void
+nautilus_leak_hash_table_initialize (NautilusLeakHashTable *table, size_t initial_size)
+{
+ /* calculate the size of the bucket array */
+ table->array_size = nautilus_leak_hash_table_optimal_size (initial_size);
+
+ /* allocate the element array */
+ nautilus_leak_hash_element_vector_inititalize (&table->element_vector, table->array_size * 5);
+
+ /* allocate the bucket array */
+ table->hash_array = (int *)real_malloc (table->array_size * sizeof(int));
+
+ /* initialize the to empty state */
+ memset (table->hash_array, -1, table->array_size * sizeof(int));
+}
+
+static void
+nautilus_leak_hash_table_finalize (NautilusLeakHashTable *table)
+{
+ nautilus_leak_hash_element_vector_finalize (&table->element_vector);
+ real_free (table->hash_array);
+}
+
+NautilusLeakHashTable *
+nautilus_leak_hash_table_new (size_t initial_size)
+{
+ NautilusLeakHashTable *new_table;
+ new_table = real_malloc (sizeof(NautilusLeakHashTable));
+ nautilus_leak_hash_table_initialize (new_table, initial_size);
+
+ return new_table;
+}
+
+void
+nautilus_leak_hash_table_free (NautilusLeakHashTable *hash_table)
+{
+ nautilus_leak_hash_table_finalize (hash_table);
+ real_free (hash_table);
+}
+
+static unsigned long
+nautilus_leak_hash_table_hash (NautilusLeakHashTable *table, unsigned long seed)
+{
+ return (seed >> 2) % table->array_size;
+}
+
+NautilusHashEntry *
+nautilus_leak_hash_table_find (NautilusLeakHashTable *table, unsigned long key)
+{
+ int index;
+ NautilusHashEntry *result;
+
+ for (index = table->hash_array [nautilus_leak_hash_table_hash (table, key)]; index >= 0;) {
+ result = nautilus_leak_hash_element_vector_at (&table->element_vector, index);
+ if (nautilus_leak_hash_element_match (result, key)) {
+ return result;
+ }
+ index = result->next;
+ }
+
+ return NULL;
+}
+
+NautilusHashEntry *
+nautilus_leak_hash_table_add (NautilusLeakHashTable *table, unsigned long key)
+{
+ int new_index;
+ NautilusHashEntry *result;
+ unsigned long hash;
+
+ /* calculate the index of the bucket */
+ hash = nautilus_leak_hash_table_hash (table, key);
+
+ /* allocate space for new item in element vector */
+ new_index = nautilus_leak_hash_element_vector_add (&table->element_vector);
+ result = nautilus_leak_hash_element_vector_at (&table->element_vector, new_index);
+
+ /* insert new item first in the list for bucket <hash> */
+ result->next = table->hash_array[hash];
+ table->hash_array[hash] = new_index;
+
+ return result;
+}
+
+static void
+nautilus_leak_hash_table_remove_element (NautilusLeakHashTable *table, NautilusHashEntry *element)
+{
+ unsigned long hash;
+ int next;
+ int index;
+ NautilusHashEntry *tmp_element;
+
+ /* find the bucket */
+ hash = nautilus_leak_hash_table_hash (table, nautilus_leak_hash_element_hash (element));
+ next = table->hash_array[hash];
+
+ g_assert (next >= 0);
+
+ /* try to match bucket list head */
+ if (nautilus_leak_hash_element_vector_at (&table->element_vector, next) == element) {
+ table->hash_array[hash] = element->next;
+ nautilus_leak_hash_element_vector_remove (&table->element_vector, next);
+ return;
+ }
+
+ for (index = next; index >= 0; ) {
+ /* look for an existing match in table */
+ next = nautilus_leak_hash_element_vector_at (&table->element_vector, index)->next;
+ if (next < 0) {
+ g_assert (!"should not be here");
+ return;
+ }
+
+ tmp_element = nautilus_leak_hash_element_vector_at (&table->element_vector, index);
+ if (nautilus_leak_hash_element_vector_at (&table->element_vector, next) == element) {
+ nautilus_leak_hash_element_vector_at (&table->element_vector, index)->next = element->next;
+ nautilus_leak_hash_element_vector_remove (&table->element_vector, next);
+ return;
+ }
+ index = next;
+ }
+}
+
+gboolean
+nautilus_leak_hash_table_remove (NautilusLeakHashTable *table, unsigned long key)
+{
+ NautilusHashEntry *element;
+
+ element = nautilus_leak_hash_table_find (table, key);
+ if (element != NULL) {
+ /* FIXME: this could be faster if we just found the element
+ * here and deleted it.
+ */
+ nautilus_leak_hash_table_remove_element (table, element);
+
+ return TRUE;
+ }
+ return FALSE;
+}
+
+struct NautilusLeakTable {
+ size_t size;
+ NautilusLeakTableEntry *data;
+};
+
+static NautilusLeakTableEntry *
+nautilus_leak_table_new_entry_at (NautilusLeakTable *table, int index)
+{
+ /* Allocate a new slot. Avoid using real_realloc here because
+ * it ends up calling our version of __libc_malloc and messes up
+ * the leak table
+ */
+ NautilusLeakTableEntry *new_table = (NautilusLeakTableEntry *) real_malloc
+ ((table->size + 1) * sizeof (NautilusLeakTableEntry));
+
+ if (new_table == NULL) {
+ g_warning ("Ran out of memory while allocating leak checker structures");
+ abort ();
+ }
+
+ /* finish what realloc would have done if we could call it */
+ memcpy (new_table, table->data, (table->size) * sizeof (NautilusLeakTableEntry));
+ real_free (table->data);
+ table->data = new_table;
+
+ /* move the items over by one to make room for new item */
+ if (index < table->size) {
+ memmove (&table->data[index + 1],
+ &table->data[index],
+ (table->size - index) * sizeof (NautilusLeakTableEntry));
+ }
+
+ table->size++;
+
+ return &table->data[index];
+}
+
+static void
+nautilus_leak_table_add_entry (NautilusLeakTable *table, NautilusHashEntry *entry, int stack_grouping_depth)
+{
+ int r, l;
+ int resulting_index;
+ int compare_result;
+
+ /* do a binary lookup of the item */
+ r = table->size - 1;
+ resulting_index = 0;
+ compare_result = 0;
+
+ for (l = 0; l <= r; ) {
+ resulting_index = (l + r) / 2;
+
+ compare_result = nautilus_leak_stack_crawl_compare (
+ table->data[resulting_index].sample_allocation->stack_crawl,
+ entry->data.stack_crawl,
+ stack_grouping_depth);
+
+ if (compare_result > 0) {
+ r = resulting_index - 1;
+ } else if (compare_result < 0) {
+ l = resulting_index + 1;
+ } else {
+ break;
+ }
+ }
+
+ if (compare_result < 0) {
+ resulting_index++;
+ }
+
+ if (compare_result == 0 && resulting_index < table->size) {
+ /* we already have a match, just bump up the count and size */
+ table->data[resulting_index].count++;
+ table->data[resulting_index].total_size += entry->data.size;
+ return;
+ }
+
+ nautilus_leak_table_new_entry_at (table, resulting_index);
+ table->data[resulting_index].count = 1;
+ table->data[resulting_index].total_size = entry->data.size;
+ table->data[resulting_index].sample_allocation = nautilus_leak_allocation_record_copy (&entry->data);
+}
+
+NautilusLeakTable *
+nautilus_leak_table_new (NautilusLeakHashTable *hash_table, int stack_grouping_depth)
+{
+ NautilusLeakTable *result;
+ NautilusHashEntry *nautilus_leak_hash_table_entry;
+ int index;
+
+ result = real_malloc (sizeof(NautilusLeakTable));
+ result->size = 0;
+ result->data = NULL;
+
+ for (index = 0; index < hash_table->element_vector.size; index++) {
+ /* traverse the hash table element vector */
+ nautilus_leak_hash_table_entry = nautilus_leak_hash_element_vector_at (&hash_table->element_vector, index);
+ if (nautilus_leak_hash_table_entry->data.stack_crawl != NULL) {
+ nautilus_leak_table_add_entry (result, nautilus_leak_hash_table_entry, stack_grouping_depth);
+ }
+ }
+
+ return result;
+}
+
+void
+nautilus_leak_table_free (NautilusLeakTable *leak_table)
+{
+ int index;
+ if (leak_table != NULL) {
+ for (index = 0; index < leak_table->size; index++) {
+ nautilus_leak_allocation_record_free(leak_table->data[index].sample_allocation);
+ }
+ real_free (leak_table->data);
+ }
+
+ real_free (leak_table);
+}
+
+static int
+sort_by_count (const void *entry1, const void *entry2)
+{
+ int result;
+
+ result = ((NautilusLeakTableEntry *)entry2)->count
+ - ((NautilusLeakTableEntry *)entry1)->count;
+
+ if (result == 0) {
+ /* match, secondary sort order by size */
+ return ((NautilusLeakTableEntry *)entry2)->total_size
+ - ((NautilusLeakTableEntry *)entry1)->total_size;
+ }
+ return result;
+}
+
+void
+nautilus_leak_table_sort_by_count (NautilusLeakTable *leak_table)
+{
+ qsort (leak_table->data, leak_table->size,
+ sizeof(NautilusLeakTableEntry), sort_by_count);
+}
+
+static int
+sort_by_size (const void *entry1, const void *entry2)
+{
+ int result;
+
+ result = ((NautilusLeakTableEntry *)entry2)->total_size
+ - ((NautilusLeakTableEntry *)entry1)->total_size;
+
+ if (result == 0) {
+ /* match, secondary sort order by count */
+ return ((NautilusLeakTableEntry *)entry2)->count
+ - ((NautilusLeakTableEntry *)entry1)->count;
+ }
+ return result;
+}
+
+void
+nautilus_leak_table_sort_by_size (NautilusLeakTable *leak_table)
+{
+ qsort (leak_table->data, leak_table->size,
+ sizeof (NautilusLeakTableEntry), sort_by_size);
+}
+
+void
+nautilus_leak_table_each_item (NautilusLeakTable *leak_table, NautilusEachLeakTableFunction function,
+ void *context)
+{
+ int index;
+ for (index = 0; index < leak_table->size; index++) {
+ if (!function (&leak_table->data[index], context)) {
+ /* break early */
+ return;
+ }
+ }
+}
+
+#endif \ No newline at end of file
diff --git a/test/nautilus-leak-hash-table.h b/test/nautilus-leak-hash-table.h
new file mode 100644
index 000000000..08e295417
--- /dev/null
+++ b/test/nautilus-leak-hash-table.h
@@ -0,0 +1,57 @@
+/* nautilus-leak-hash-table.h - hash table for a leak checking library
+ Virtual File System Library
+
+ Copyright (C) 2000 Eazel
+
+ The Gnome Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The Gnome Library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the Gnome Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ Author: Pavel Cisler <pavel@eazel.com>
+*/
+
+#ifndef LEAK_HASH_TABLE__
+#define LEAK_HASH_TABLE__
+
+typedef struct NautilusLeakHashTable NautilusLeakHashTable;
+typedef struct NautilusHashEntry NautilusHashEntry;
+
+NautilusLeakHashTable *nautilus_leak_hash_table_new (size_t initial_size);
+void nautilus_leak_hash_table_free (NautilusLeakHashTable *hash_table);
+gboolean nautilus_leak_hash_table_remove (NautilusLeakHashTable *table,
+ unsigned long key);
+NautilusHashEntry *nautilus_leak_hash_table_add (NautilusLeakHashTable *table,
+ unsigned long key);
+NautilusHashEntry *nautilus_leak_hash_table_find (NautilusLeakHashTable *table,
+ unsigned long key);
+
+
+typedef struct {
+ int count;
+ size_t total_size;
+ NautilusLeakAllocationRecord *sample_allocation;
+} NautilusLeakTableEntry;
+
+typedef struct NautilusLeakTable NautilusLeakTable;
+typedef gboolean (* NautilusEachLeakTableFunction) (NautilusLeakTableEntry *entry, void *context);
+
+NautilusLeakTable *nautilus_leak_table_new (NautilusLeakHashTable *hash_table,
+ int stack_grouping_depth);
+void nautilus_leak_table_free (NautilusLeakTable *leak_table);
+void nautilus_leak_table_sort_by_count (NautilusLeakTable *leak_table);
+void nautilus_leak_table_sort_by_size (NautilusLeakTable *leak_table);
+void nautilus_leak_table_each_item (NautilusLeakTable *leak_table,
+ NautilusEachLeakTableFunction function,
+ void *context);
+#endif \ No newline at end of file
diff --git a/test/nautilus-leak-symbol-lookup.c b/test/nautilus-leak-symbol-lookup.c
new file mode 100644
index 000000000..41f00d888
--- /dev/null
+++ b/test/nautilus-leak-symbol-lookup.c
@@ -0,0 +1,223 @@
+/* nautilus-leak-symbol-lookup.c - symbol lookup for a leak checking library
+ Virtual File System Library
+
+ Copyright (C) 2000 Eazel
+
+ The Gnome Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The Gnome Library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the Gnome Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ Author: Pavel Cisler <pavel@eazel.com>
+ based on MemProf by Owen Taylor, <otaylor@redhat.com>
+*/
+
+#define _GNU_SOURCE
+ /* need this for dladdr */
+
+#include "nautilus-leak-symbol-lookup.h"
+
+#include <bfd.h>
+#include <dlfcn.h>
+#include <glib.h>
+#include <stdio.h>
+#include <malloc.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+
+static GList *symbol_table_list;
+
+typedef struct {
+ char *path;
+ bfd *abfd;
+ asymbol **symbol_table;
+ asection *text_section;
+} NautilusLeakSymbolLookupMap;
+
+static gboolean
+nautilus_leak_find_symbol_in_map (const NautilusLeakSymbolLookupMap *map,
+ unsigned long address, char **function_name, char **source_file_name,
+ unsigned int *line, unsigned long offset, const char *known_function_name)
+{
+ const char *file;
+ const char *function;
+
+ address -= offset;
+ address -= map->text_section->vma;
+
+ if (address < 0 || address > map->text_section->_cooked_size) {
+ /* not a valid address range for this binary */
+ return FALSE;
+ }
+
+ if (!bfd_find_nearest_line (map->abfd, map->text_section, map->symbol_table,
+ address,
+ &file, &function, line)) {
+ printf ("error looking up address in binary %s\n", map->path);
+ return FALSE;
+ }
+
+ if (known_function_name != NULL &&
+ strcmp (function, known_function_name) != 0) {
+ return FALSE;
+ }
+ *function_name = g_strdup (function);
+ *source_file_name = g_strdup (file);
+
+ return TRUE;
+}
+
+static NautilusLeakSymbolLookupMap *
+nautilus_leak_symbol_map_load (const char *binary_path)
+{
+ NautilusLeakSymbolLookupMap *map;
+ char *target = NULL;
+ size_t storage_needed;
+ int number_of_symbols;
+
+ map = g_new0 (NautilusLeakSymbolLookupMap, 1);
+
+ map->abfd = bfd_openr (binary_path, target);
+
+ if (map->abfd == NULL) {
+ fprintf (stderr, "%s: ", binary_path);
+ bfd_perror (binary_path);
+ return NULL;
+ }
+
+ if (!bfd_check_format (map->abfd, bfd_object)) {
+ fprintf (stderr, "%s is not an object file\n", binary_path);
+ bfd_close (map->abfd);
+ return NULL;
+ }
+
+ /* Use the ".text" section. */
+ map->text_section = bfd_get_section_by_name (map->abfd, ".text");
+
+ /* Read the symbol table. */
+ storage_needed = bfd_get_symtab_upper_bound (map->abfd);
+ if (storage_needed == 0) {
+ fprintf (stderr, "no symbols\n");
+ bfd_close (map->abfd);
+ return NULL;
+ }
+ map->symbol_table = (asymbol **)g_malloc (storage_needed);
+ if (map->symbol_table == NULL) {
+ fprintf (stderr, "no memory allocating symbol table\n");
+ bfd_close (map->abfd);
+ return NULL;
+ }
+ number_of_symbols = bfd_canonicalize_symtab (map->abfd, map->symbol_table);
+ map->path = g_strdup (binary_path);
+
+ symbol_table_list = g_list_append (symbol_table_list, map);
+
+ return map;
+}
+
+static NautilusLeakSymbolLookupMap *
+nautilus_leak_symbol_map_load_if_needed (const char *binary_path)
+{
+ GList *p;
+ NautilusLeakSymbolLookupMap *map;
+
+ for (p = symbol_table_list; p != NULL; p = p->next) {
+ map = p->data;
+ if (strcmp (map->path, binary_path) == 0)
+ /* no need to load the symbols, already got the map */
+ return map;
+ }
+ return nautilus_leak_symbol_map_load (binary_path);
+}
+
+void
+nautilus_leak_print_symbol_cleanup (void)
+{
+ /* free the cached symbol tables */
+ GList *p;
+ NautilusLeakSymbolLookupMap *map;
+
+ for (p = symbol_table_list; p != NULL; p = p->next) {
+ map = p->data;
+ bfd_close (map->abfd);
+ g_free (map->symbol_table);
+ g_free (map->path);
+
+ g_free (map);
+ }
+
+ g_list_free (symbol_table_list);
+ symbol_table_list = NULL;
+}
+
+static gboolean
+nautilus_leak_find_symbol_address (void *address, char **function_name, char **source_file_name,
+ int *line)
+{
+ GList *p;
+ NautilusLeakSymbolLookupMap *map;
+ Dl_info info;
+
+ if (dladdr (address, &info) != 0) {
+ /* We know the function name and the binary it lives in, now try to find
+ * the function and the offset.
+ */
+ map = nautilus_leak_symbol_map_load_if_needed (info.dli_fname);
+ if (map != NULL
+ && nautilus_leak_find_symbol_in_map (map, (long)address,
+ function_name, source_file_name, line,
+ (unsigned long)info.dli_fbase, info.dli_sname)) {
+ return TRUE;
+ }
+ /* just return the function name and the library binary path */
+ *function_name = g_strdup (info.dli_sname);
+ *source_file_name = g_strdup (info.dli_fname);
+ *line = -1;
+ return TRUE;
+ } else {
+ /* Usually dladdr will succeed, it seems to only fail for
+ * address lookups for functions in the main binary.
+ */
+ for (p = symbol_table_list; p != NULL; p = p->next) {
+ map = p->data;
+ if (nautilus_leak_find_symbol_in_map (map, (long)address, function_name,
+ source_file_name, (unsigned int *)line, 0, NULL))
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+void
+nautilus_leak_print_symbol_address (const char *app_path, void *address)
+{
+ char *function_name;
+ char *source_file_name;
+ int line;
+
+ nautilus_leak_symbol_map_load_if_needed (app_path);
+
+ if (nautilus_leak_find_symbol_address (address, &function_name, &source_file_name, &line)) {
+ if (line >= 0) {
+ printf("%p %s %s:%d\n", address, function_name, source_file_name, line);
+ } else {
+ printf("%p %s in library %s\n", address, function_name, source_file_name);
+ }
+ g_free (function_name);
+ g_free (source_file_name);
+ } else {
+ printf("%p (unknown function)\n", address);
+ }
+}
diff --git a/test/nautilus-leak-symbol-lookup.h b/test/nautilus-leak-symbol-lookup.h
new file mode 100644
index 000000000..c56f00a1c
--- /dev/null
+++ b/test/nautilus-leak-symbol-lookup.h
@@ -0,0 +1,31 @@
+/* nautilus-leak-symbol-lookup.h - symbol lookup for a leak checking library
+ Virtual File System Library
+
+ Copyright (C) 2000 Eazel
+
+ The Gnome Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The Gnome Library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the Gnome Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ Author: Pavel Cisler <pavel@eazel.com>
+ based on MemProf by Owen Taylor, <otaylor@redhat.com>
+*/
+
+#ifndef SYMBOL_LOOKUP_H
+#define SYMBOL_LOOKUP_
+
+void nautilus_leak_print_symbol_address (const char *app_path, void *address);
+void nautilus_leak_print_symbol_cleanup (void);
+
+#endif