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).
This commit is contained in:
Pavel Cisler 2000-06-14 00:26:58 +00:00 committed by Pavel Cisler
parent fd18cb806c
commit 1dc046ab3e
10 changed files with 1785 additions and 0 deletions

View file

@ -1,3 +1,68 @@
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).
2000-06-14 Anders Carlsson <andersca@gnu.org>
* libnautilus-extensions/nautilus-background-canvas-group.c

View file

@ -32,6 +32,7 @@
#include "nautilus-self-check-functions.h"
#include "nautilus-application.h"
#include <dlfcn.h>
#include <libnautilus-extensions/nautilus-debug.h>
#include <libnautilus-extensions/nautilus-lib-self-check-functions.h>
#include <libnautilus-extensions/nautilus-self-checks.h>
@ -44,6 +45,44 @@
#include <bonobo/bonobo-main.h>
#include <stdlib.h>
/* FIXME:
* Replace the leakchecker calls with weakly exported ones in libnautilus-extensions.
*/
void nautilus_leak_checker_init (const char *path);
void nautilus_leak_print_leaks (int stack_grouping_depth, int stack_print_depth,
int max_count, int sort_by_count);
void
nautilus_leak_checker_init (const char *path)
{
void (*real_nautilus_leak_checker_init)(const char *path);
/* Try to hook up with the leakchecker library.
* This only succeeds if we run nautilus with a LD_PRELOAD=./libleakcheck.so
* prefix.
* If there isn't one, this call is benign.
*/
real_nautilus_leak_checker_init = dlsym (RTLD_NEXT, "nautilus_leak_checker_init");
if (real_nautilus_leak_checker_init != NULL) {
real_nautilus_leak_checker_init (path);
}
}
void
nautilus_leak_print_leaks (int stack_grouping_depth, int stack_print_depth,
int max_count, int sort_by_count)
{
void (*real_nautilus_leak_print_leaks)(int stack_grouping_depth,
int stack_print_depth, int max_count, int sort_by_count);
/* Try to hook up with the leakchecker library. */
real_nautilus_leak_print_leaks = dlsym (RTLD_NEXT, "nautilus_leak_print_leaks");
if (real_nautilus_leak_print_leaks != NULL) {
real_nautilus_leak_print_leaks (stack_grouping_depth, stack_print_depth,
max_count, sort_by_count);
}
}
int
main(int argc, char *argv[])
{
@ -61,6 +100,9 @@ main(int argc, char *argv[])
POPT_AUTOHELP
{ NULL, '\0', 0, NULL, 0, NULL, NULL }
};
/* call to set up the leak checker symbol lookup */
nautilus_leak_checker_init (*argv);
/* Make criticals and warnings stop in the debugger if NAUTILUS_DEBUG is set.
* Unfortunately, this has to be done explicitly for each domain.
@ -107,5 +149,8 @@ main(int argc, char *argv[])
gnome_vfs_shutdown ();
/* If leak checking, before exiting dump all the outstaniding allocations. */
nautilus_leak_print_leaks (8, 15, 40, TRUE);
return EXIT_SUCCESS;
}

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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);
}
}

View file

@ -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