mirror of
https://gitlab.gnome.org/GNOME/nautilus
synced 2024-08-25 02:36:07 +00:00
Move the leakchecker library to its own place.
* configure.in: * test/Makefile.am: * test/nautilus-leak-checker-stubs.h: * test/nautilus-leak-checker.c: * test/nautilus-leak-checker.h: * test/nautilus-leak-hash-table.c: * test/nautilus-leak-hash-table.h: * test/nautilus-leak-symbol-lookup.c: * test/nautilus-leak-symbol-lookup.h: * tools/leak-checker: Move the leakchecker library to its own place.
This commit is contained in:
parent
f7263fbde7
commit
27c8b5ab89
15
ChangeLog
15
ChangeLog
|
@ -1,3 +1,18 @@
|
|||
2000-07-14 Ramiro Estrugo <ramiro@eazel.com>
|
||||
|
||||
* configure.in:
|
||||
* test/Makefile.am:
|
||||
* test/nautilus-leak-checker-stubs.h:
|
||||
* test/nautilus-leak-checker.c:
|
||||
* test/nautilus-leak-checker.h:
|
||||
* test/nautilus-leak-hash-table.c:
|
||||
* test/nautilus-leak-hash-table.h:
|
||||
* test/nautilus-leak-symbol-lookup.c:
|
||||
* test/nautilus-leak-symbol-lookup.h:
|
||||
* tools/leak-checker:
|
||||
|
||||
Move the leakchecker library to its own place.
|
||||
|
||||
2000-07-14 Ramiro Estrugo <ramiro@eazel.com>
|
||||
|
||||
*
|
||||
|
|
|
@ -460,6 +460,8 @@ components/loser/sidebar/Makefile
|
|||
components/tree/Makefile
|
||||
helper-utilities/Makefile
|
||||
helper-utilities/authenticate/Makefile
|
||||
tools/Makefile
|
||||
tools/leak-checker/Makefile
|
||||
po/Makefile.in
|
||||
intl/Makefile
|
||||
test/Makefile
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
NULL=
|
||||
|
||||
#lib_LTLIBRARIES = libleakcheck.la
|
||||
|
||||
INCLUDES =\
|
||||
-I$(top_srcdir) \
|
||||
-I$(top_builddir) \
|
||||
|
@ -50,52 +48,6 @@ test_nautilus_image_SOURCES = test-nautilus-image.c
|
|||
|
||||
test_nautilus_password_dialog_SOURCES = test-nautilus-password-dialog.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)
|
||||
|
||||
EXTRA_DIST = \
|
||||
test-nautilus-mime-actions.c \
|
||||
$(NULL)
|
||||
|
||||
# the libtool way of building libleakcheck.so is totally broken, libtool cannot
|
||||
# build an .so with statically linked -liberty
|
||||
|
||||
# LIBLEAKCHECK_STATIC_LIBS = "-Wl,-Bstatic -lbfd -liberty -Wl,-Bdynamic"
|
||||
#libleakcheck_la_LDADD = -module -avoid-version $(LIBLEAKCHECK_STATIC_LIBS)
|
||||
#libleakcheck_la_LIBADD = -ldl
|
||||
|
||||
|
||||
|
||||
# libleakcheck.la: $(libleakcheck_la_OBJECTS) $(libleakcheck_la_DEPENDENCIES)
|
||||
# $(LINK) -rpath $(libdir) $(libleakcheck_la_LDADD) $(libleakcheck_la_OBJECTS) $(libleakcheck_la_LIBADD) $(LIBS)
|
||||
|
||||
|
||||
# for now just hardcode a plain old link line, someday someone can
|
||||
#figure out what the magic password is to make libtool do this cleanly
|
||||
|
||||
all: libleakcheck.so
|
||||
CLEANFILES += libleakcheck.so
|
||||
|
||||
libleakcheck.so: nautilus-leak-checker.c nautilus-leak-hash-table.c nautilus-leak-symbol-lookup.c
|
||||
gcc $(srcdir)/nautilus-leak-checker.c $(srcdir)/nautilus-leak-hash-table.c \
|
||||
$(srcdir)/nautilus-leak-symbol-lookup.c \
|
||||
-Wall -o $@ -shared $(GLIB_CFLAGS) $(GLIB_LIBS) -lpthread -ldl -Wl,-Bstatic -lbfd -liberty -Wl,-Bdynamic
|
||||
|
||||
# to include them in "make dist"
|
||||
EXTRA_DIST = nautilus-leak-checker.c nautilus-leak-hash-table.c nautilus-leak-symbol-lookup.c \
|
||||
nautilus-leak-checker-stubs.h nautilus-leak-hash-table.h nautilus-leak-checker.h nautilus-leak-symbol-lookup.h
|
||||
|
||||
leakcheck_DATA = libleakcheck.so
|
||||
leakcheckdir= $(DESTDIR)$(libdir)
|
||||
|
||||
leakchecktest: nautilus-leak-checker.c nautilus-leak-hash-table.c nautilus-leak-symbol-lookup.c
|
||||
gcc $(srcdir)/nautilus-leak-checker.c $(srcdir)/nautilus-leak-hash-table.c \
|
||||
$(srcdir)/nautilus-leak-symbol-lookup.c \
|
||||
-Wall -o $@ -DLEAK_CHECK_TESTING $(GLIB_CFLAGS) $(GLIB_LIBS) -lpthread -ldl -Wl,-Bstatic -lbfd -liberty -Wl,-Bdynamic
|
||||
|
|
|
@ -1,78 +0,0 @@
|
|||
/* 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
|
|
@ -1,760 +0,0 @@
|
|||
/* 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 <string.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);
|
||||
int (* real_start_main) (int (*main) (int, char **, char **), int argc,
|
||||
char **argv, void (*init) (void), void (*fini) (void),
|
||||
void (*rtld_fini) (void), void *stack_end);
|
||||
|
||||
|
||||
|
||||
static 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");
|
||||
real_start_main = dlsym (RTLD_NEXT, "__libc_start_main");
|
||||
|
||||
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, 0x40 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.
|
||||
* FIXME: this the value 0x40 works well only with certain function sizes, optimization levels,
|
||||
* etc. need a more robust way of doing this. One way might be adding stuffing into the
|
||||
* __libc_* calls that is never executed but makes the funcitons longer. We could
|
||||
* then up the value without the danger of hitting the next function
|
||||
*/
|
||||
if (stack_frame->return_address >= parent_caller
|
||||
&& stack_frame->return_address < (void *)((char *)parent_caller + 0x40)) {
|
||||
/* 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 size_t nautilus_leak_malloc_outstanding_size;
|
||||
|
||||
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;
|
||||
nautilus_leak_malloc_outstanding_size += size;
|
||||
|
||||
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, (gulong)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, (gulong)ptr);
|
||||
}
|
||||
/* insert a new item into the hash table, using the block address as the key */
|
||||
element = nautilus_leak_hash_table_add (hash_table, (gulong)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 */
|
||||
element = nautilus_leak_hash_table_find (hash_table, (gulong)old_ptr);
|
||||
if (element == NULL) {
|
||||
printf("*** we haven't seen block %p yet "
|
||||
"- someone must have sneaked a malloc past us\n", old_ptr);
|
||||
} else {
|
||||
nautilus_leak_malloc_outstanding_size -= element->data.size;
|
||||
nautilus_leak_hash_table_remove (hash_table, (gulong)old_ptr);
|
||||
}
|
||||
|
||||
/* shouldn't have this block yet */
|
||||
if (nautilus_leak_hash_table_find (hash_table, (gulong)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, (gulong)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, (gulong)new_ptr);
|
||||
nautilus_leak_malloc_outstanding_size += size;
|
||||
|
||||
/* 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)
|
||||
{
|
||||
NautilusHashEntry *element;
|
||||
/* 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 */
|
||||
element = nautilus_leak_hash_table_find (hash_table, (gulong)ptr);
|
||||
if (element == NULL) {
|
||||
printf("*** we haven't seen block %p yet "
|
||||
"- someone must have sneaked a malloc past us\n", ptr);
|
||||
} else {
|
||||
nautilus_leak_malloc_outstanding_size -= element->data.size;
|
||||
nautilus_leak_hash_table_remove (hash_table, (gulong)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];
|
||||
|
||||
/* If our malloc hook is not installed yet - for instance when 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.
|
||||
*/
|
||||
static void *
|
||||
allocate_temporary_fallback_memory (ssize_t size)
|
||||
{
|
||||
void *result;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
void *
|
||||
__libc_malloc (size_t size)
|
||||
{
|
||||
void *result;
|
||||
|
||||
nautilus_leak_initialize_if_needed ();
|
||||
|
||||
if (real_malloc == NULL) {
|
||||
return allocate_temporary_fallback_memory (size);
|
||||
}
|
||||
|
||||
result = (*real_malloc) (size);
|
||||
|
||||
if (result != NULL) {
|
||||
if (detect_reentry(&__libc_malloc)
|
||||
|| detect_reentry(&__libc_realloc)
|
||||
|| detect_reentry(&__libc_memalign)) {
|
||||
/* printf("avoiding reentry in __libc_malloc, block %p\n", result); */
|
||||
} 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, block %p\n", result); */
|
||||
} 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, block %p, old block %p\n",
|
||||
result, ptr); */
|
||||
} 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) {
|
||||
if (detect_reentry(&__libc_realloc)) {
|
||||
/* printf("avoiding reentry in __libc_free, block %p\n", ptr); */
|
||||
} else {
|
||||
nautilus_leak_record_free (ptr);
|
||||
}
|
||||
}
|
||||
|
||||
(real_free) (ptr);
|
||||
}
|
||||
|
||||
static void
|
||||
print_leaks_at_exit (void)
|
||||
{
|
||||
/* If leak checking, dump all the outstanding allocations just before exiting. */
|
||||
nautilus_leak_print_leaks (8, 15, 100, TRUE);
|
||||
}
|
||||
|
||||
int
|
||||
__libc_start_main (int (*main) (int, char **, char **), int argc,
|
||||
char **argv, void (*init) (void), void (*fini) (void),
|
||||
void (*rtld_fini) (void), void *stack_end)
|
||||
{
|
||||
nautilus_leak_initialize_if_needed ();
|
||||
|
||||
nautilus_leak_checker_init (argv[0]);
|
||||
|
||||
g_atexit (print_leaks_at_exit);
|
||||
|
||||
return real_start_main (main, argc, argv, init, fini, rtld_fini, stack_end);
|
||||
}
|
||||
|
||||
/* We try to keep a lot of code in between __libc_free and malloc to make
|
||||
* the reentry detection that depends on call address proximity work.
|
||||
*/
|
||||
typedef struct {
|
||||
int max_count;
|
||||
int counter;
|
||||
int stack_print_depth;
|
||||
int stack_match_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("----------------- total_size %ld count %d -------------------\n",
|
||||
(long)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(" %c ", index >= params->stack_match_depth ? '?' : ' ');
|
||||
|
||||
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 %d bytes total ============ \n",
|
||||
nautilus_leak_malloc_count, nautilus_leak_malloc_outstanding_size);
|
||||
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;
|
||||
each_context.stack_match_depth = stack_grouping_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;
|
||||
|
||||
g_atexit (print_leaks_at_exit);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
#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));
|
||||
list = g_list_prepend (list, NULL);
|
||||
}
|
||||
for (p = list; p != NULL; p = p->next) {
|
||||
g_free (p->data);
|
||||
p->data = NULL;
|
||||
}
|
||||
g_list_free (list);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
leak_mem2 (void)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < 40; i++) {
|
||||
g_strdup("bla");
|
||||
}
|
||||
allocate_lots (1280);
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
allocate_lots (1);
|
||||
for (i = 0; i < 100; i++) {
|
||||
allocate_lots(rand() % 40);
|
||||
}
|
||||
printf("done\n");
|
||||
nautilus_leak_print_leaks (6, 12, 100, TRUE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#endif
|
|
@ -1,54 +0,0 @@
|
|||
/* 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
|
|
@ -1,511 +0,0 @@
|
|||
/* 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
|
|
@ -1,57 +0,0 @@
|
|||
/* 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
|
|
@ -1,272 +0,0 @@
|
|||
/* 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;
|
||||
unsigned long start;
|
||||
unsigned long end;
|
||||
} 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)
|
||||
{
|
||||
const char *file;
|
||||
const char *function;
|
||||
|
||||
address -= map->start;
|
||||
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;
|
||||
}
|
||||
|
||||
*function_name = g_strdup (function);
|
||||
*source_file_name = g_strdup (file);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
nautilus_leak_symbol_map_get_offsets (NautilusLeakSymbolLookupMap *map)
|
||||
{
|
||||
gchar buffer[1024];
|
||||
FILE *in;
|
||||
gchar perms[26];
|
||||
gchar file[256];
|
||||
unsigned long start, end;
|
||||
guint major, minor;
|
||||
ino_t inode;
|
||||
struct stat library_stat;
|
||||
struct stat entry_stat;
|
||||
int count;
|
||||
|
||||
/* find the library we are looking for in the proc directories
|
||||
* to find out at which addresses it is mapped
|
||||
*/
|
||||
snprintf (buffer, 1023, "/proc/%d/maps", getpid());
|
||||
in = fopen (buffer, "r");
|
||||
|
||||
if (stat (map->path, &library_stat) != 0) {
|
||||
/* we will use st_ino and st_dev to do a file match */
|
||||
return;
|
||||
}
|
||||
|
||||
while (fgets(buffer, 1023, in)) {
|
||||
gulong tmp;
|
||||
|
||||
count = sscanf (buffer, "%lx-%lx %15s %*x %u:%u %lu %255s",
|
||||
&start, &end, perms, &major, &minor, &tmp, file);
|
||||
inode = tmp;
|
||||
|
||||
if (count >= 6 && strcmp (perms, "r-xp") == 0) {
|
||||
if (stat (file, &entry_stat) != 0) {
|
||||
break;
|
||||
}
|
||||
/* check if this is the library we are loading */
|
||||
if (library_stat.st_ino == entry_stat.st_ino
|
||||
&& library_stat.st_dev == entry_stat.st_dev) {
|
||||
map->start = start;
|
||||
map->end = end;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
fclose (in);
|
||||
}
|
||||
|
||||
static NautilusLeakSymbolLookupMap *
|
||||
nautilus_leak_symbol_map_load (const char *binary_path, gboolean executable)
|
||||
{
|
||||
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);
|
||||
|
||||
if (!executable) {
|
||||
nautilus_leak_symbol_map_get_offsets (map);
|
||||
}
|
||||
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, gboolean executable)
|
||||
{
|
||||
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, executable);
|
||||
}
|
||||
|
||||
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, false);
|
||||
if (map != NULL
|
||||
&& nautilus_leak_find_symbol_in_map (map, (long)address,
|
||||
function_name, source_file_name, line)) {
|
||||
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))
|
||||
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, true);
|
||||
|
||||
if (nautilus_leak_find_symbol_address (address, &function_name, &source_file_name, &line)) {
|
||||
if (line >= 0) {
|
||||
printf("%10p %-30s %s:%d\n", address, function_name, source_file_name, line);
|
||||
} else {
|
||||
printf("%10p %-30s 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);
|
||||
}
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
/* 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
|
Loading…
Reference in a new issue