mirror of
https://gitlab.gnome.org/GNOME/nautilus
synced 2024-08-25 02:36:07 +00:00
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:
parent
fd18cb806c
commit
1dc046ab3e
65
ChangeLog
65
ChangeLog
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
78
test/nautilus-leak-checker-stubs.h
Normal file
78
test/nautilus-leak-checker-stubs.h
Normal 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
|
693
test/nautilus-leak-checker.c
Normal file
693
test/nautilus-leak-checker.c
Normal 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
|
54
test/nautilus-leak-checker.h
Normal file
54
test/nautilus-leak-checker.h
Normal 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
|
511
test/nautilus-leak-hash-table.c
Normal file
511
test/nautilus-leak-hash-table.c
Normal 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
|
57
test/nautilus-leak-hash-table.h
Normal file
57
test/nautilus-leak-hash-table.h
Normal 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
|
223
test/nautilus-leak-symbol-lookup.c
Normal file
223
test/nautilus-leak-symbol-lookup.c
Normal 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);
|
||||
}
|
||||
}
|
31
test/nautilus-leak-symbol-lookup.h
Normal file
31
test/nautilus-leak-symbol-lookup.h
Normal 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
|
Loading…
Reference in a new issue