2005-01-16 Havoc Pennington <hp@redhat.com>

* test/unused-code-gc.py: hacky script to find code that's used
	only by the bus (not libdbus) or used only by tests or not used at
	all. It has some false alarms, but looks like we can clean up a
	lot of size from libdbus.

	* dbus/dbus-sysdeps.c, dbus/dbus-sysdeps-utils.c,
	dbus/Makefile.am: initially move 10K of binary size out of libdbus
This commit is contained in:
Havoc Pennington 2005-01-16 22:13:35 +00:00
parent 31988af408
commit 7f9721a6d3
7 changed files with 282 additions and 602 deletions

View file

@ -1,3 +1,13 @@
2005-01-16 Havoc Pennington <hp@redhat.com>
* test/unused-code-gc.py: hacky script to find code that's used
only by the bus (not libdbus) or used only by tests or not used at
all. It has some false alarms, but looks like we can clean up a
lot of size from libdbus.
* dbus/dbus-sysdeps.c, dbus/dbus-sysdeps-utils.c,
dbus/Makefile.am: initially move 10K of binary size out of libdbus
2005-01-16 Havoc Pennington <hp@redhat.com>
* Add and fix docs according to Doxygen warnings throughout

View file

@ -75,8 +75,6 @@ DBUS_LIB_SOURCES= \
dbus-server-unix.h \
dbus-sha.c \
dbus-sha.h \
dbus-test.c \
dbus-test.h \
dbus-timeout.c \
dbus-timeout.h \
dbus-threads.c \
@ -109,8 +107,6 @@ DBUS_SHARED_SOURCES= \
dbus-memory.c \
dbus-mempool.c \
dbus-mempool.h \
dbus-spawn.c \
dbus-spawn.h \
dbus-string.c \
dbus-string.h \
dbus-string-private.h \
@ -126,7 +122,13 @@ DBUS_SHARED_SOURCES= \
### to be unless they move to DBUS_SHARED_SOURCES later)
DBUS_UTIL_SOURCES= \
dbus-mainloop.c \
dbus-mainloop.h
dbus-mainloop.h \
dbus-spawn.c \
dbus-spawn.h \
dbus-sysdeps-util.c \
dbus-sysdeps-util.h \
dbus-test.c \
dbus-test.h
libdbus_1_la_SOURCES= \
$(DBUS_LIB_SOURCES) \
@ -167,7 +169,7 @@ noinst_PROGRAMS=$(TESTS)
dbus_test_SOURCES= \
dbus-test-main.c
dbus_test_LDADD= $(DBUS_CLIENT_LIBS) libdbus-1.la
dbus_test_LDADD=libdbus-convenience.la
## mop up the gcov files
clean-local:

View file

@ -231,6 +231,7 @@ _dbus_get_fail_alloc_failures (void)
return n_failures_per_failure;
}
#ifdef DBUS_BUILD_TESTS
/**
* Called when about to alloc some memory; if
* it returns #TRUE, then the allocation should
@ -272,6 +273,7 @@ _dbus_decrement_fail_alloc_counter (void)
return FALSE;
}
}
#endif /* DBUS_BUILD_TESTS */
/**
* Get the number of outstanding malloc()'d blocks.

View file

@ -26,7 +26,7 @@
#include "dbus-sysdeps.h"
#include "dbus-threads.h"
#include "dbus-protocol.h"
#include "dbus-test.h"
#include "dbus-string.h"
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
@ -71,6 +71,7 @@
* @addtogroup DBusInternalsUtils
* @{
*/
#ifndef DBUS_DISABLE_ASSERT
/**
* Aborts the program with SIGABRT (dumping core).
*/
@ -86,6 +87,7 @@ _dbus_abort (void)
abort ();
_exit (1); /* in case someone manages to ignore SIGABRT */
}
#endif
/**
* Wrapper for setenv(). If the value is #NULL, unsets
@ -1051,6 +1053,7 @@ _dbus_string_append_uint (DBusString *str,
return TRUE;
}
#ifdef DBUS_BUILD_TESTS
/**
* Appends a double to a DBusString.
*
@ -1087,6 +1090,7 @@ _dbus_string_append_double (DBusString *str,
return TRUE;
}
#endif /* DBUS_BUILD_TESTS */
/**
* Parses an integer contained in a DBusString. Either return parameter
@ -1169,6 +1173,7 @@ _dbus_string_parse_uint (const DBusString *str,
}
#endif /* DBUS_BUILD_TESTS */
#ifdef DBUS_BUILD_TESTS
static dbus_bool_t
ascii_isspace (char c)
{
@ -1179,13 +1184,17 @@ ascii_isspace (char c)
c == '\t' ||
c == '\v');
}
#endif /* DBUS_BUILD_TESTS */
#ifdef DBUS_BUILD_TESTS
static dbus_bool_t
ascii_isdigit (char c)
{
return c >= '0' && c <= '9';
}
#endif /* DBUS_BUILD_TESTS */
#ifdef DBUS_BUILD_TESTS
static dbus_bool_t
ascii_isxdigit (char c)
{
@ -1193,8 +1202,9 @@ ascii_isxdigit (char c)
(c >= 'a' && c <= 'f') ||
(c >= 'A' && c <= 'F'));
}
#endif /* DBUS_BUILD_TESTS */
#ifdef DBUS_BUILD_TESTS
/* Calls strtod in a locale-independent fashion, by looking at
* the locale data and patching the decimal comma to a point.
*
@ -1323,8 +1333,9 @@ ascii_strtod (const char *nptr,
return val;
}
#endif /* DBUS_BUILD_TESTS */
#ifdef DBUS_BUILD_TESTS
/**
* Parses a floating point number contained in a DBusString. Either
* return parameter may be #NULL if you aren't interested in it. The
@ -1363,6 +1374,7 @@ _dbus_string_parse_double (const DBusString *str,
return TRUE;
}
#endif /* DBUS_BUILD_TESTS */
/** @} */ /* DBusString group */
@ -2415,34 +2427,6 @@ _dbus_create_directory (const DBusString *filename,
return TRUE;
}
/**
* Removes a directory; Directory must be empty
*
* @param filename directory filename
* @param error initialized error object
* @returns #TRUE on success
*/
dbus_bool_t
_dbus_delete_directory (const DBusString *filename,
DBusError *error)
{
const char *filename_c;
_DBUS_ASSERT_ERROR_IS_CLEAR (error);
filename_c = _dbus_string_get_const_data (filename);
if (rmdir (filename_c) != 0)
{
dbus_set_error (error, DBUS_ERROR_FAILED,
"Failed to remove directory %s: %s\n",
filename_c, _dbus_strerror (errno));
return FALSE;
}
return TRUE;
}
/**
* Appends the given filename to the given directory.
*
@ -2483,69 +2467,6 @@ _dbus_concat_dir_and_file (DBusString *dir,
_dbus_string_get_length (dir));
}
/**
* Get the directory name from a complete filename
* @param filename the filename
* @param dirname string to append directory name to
* @returns #FALSE if no memory
*/
dbus_bool_t
_dbus_string_get_dirname (const DBusString *filename,
DBusString *dirname)
{
int sep;
_dbus_assert (filename != dirname);
_dbus_assert (filename != NULL);
_dbus_assert (dirname != NULL);
/* Ignore any separators on the end */
sep = _dbus_string_get_length (filename);
if (sep == 0)
return _dbus_string_append (dirname, "."); /* empty string passed in */
while (sep > 0 && _dbus_string_get_byte (filename, sep - 1) == '/')
--sep;
_dbus_assert (sep >= 0);
if (sep == 0)
return _dbus_string_append (dirname, "/");
/* Now find the previous separator */
_dbus_string_find_byte_backward (filename, sep, '/', &sep);
if (sep < 0)
return _dbus_string_append (dirname, ".");
/* skip multiple separators */
while (sep > 0 && _dbus_string_get_byte (filename, sep - 1) == '/')
--sep;
_dbus_assert (sep >= 0);
if (sep == 0 &&
_dbus_string_get_byte (filename, 0) == '/')
return _dbus_string_append (dirname, "/");
else
return _dbus_string_copy_len (filename, 0, sep - 0,
dirname, _dbus_string_get_length (dirname));
}
/**
* Checks whether the filename is an absolute path
*
* @param filename the filename
* @returns #TRUE if an absolute path
*/
dbus_bool_t
_dbus_path_is_absolute (const DBusString *filename)
{
if (_dbus_string_get_length (filename) > 0)
return _dbus_string_get_byte (filename, 0) == '/';
else
return FALSE;
}
/**
* Internals of directory iterator
*/
@ -2937,45 +2858,6 @@ _dbus_exit (int code)
_exit (code);
}
/**
* stat() wrapper.
*
* @param filename the filename to stat
* @param statbuf the stat info to fill in
* @param error return location for error
* @returns #FALSE if error was set
*/
dbus_bool_t
_dbus_stat (const DBusString *filename,
DBusStat *statbuf,
DBusError *error)
{
const char *filename_c;
struct stat sb;
_DBUS_ASSERT_ERROR_IS_CLEAR (error);
filename_c = _dbus_string_get_const_data (filename);
if (stat (filename_c, &sb) < 0)
{
dbus_set_error (error, _dbus_error_from_errno (errno),
"%s", _dbus_strerror (errno));
return FALSE;
}
statbuf->mode = sb.st_mode;
statbuf->nlink = sb.st_nlink;
statbuf->uid = sb.st_uid;
statbuf->gid = sb.st_gid;
statbuf->size = sb.st_size;
statbuf->atime = sb.st_atime;
statbuf->mtime = sb.st_mtime;
statbuf->ctime = sb.st_ctime;
return TRUE;
}
/**
* Creates a full-duplex pipe (as in socketpair()).
* Sets both ends of the pipe nonblocking.
@ -3099,6 +2981,7 @@ _dbus_set_fd_nonblocking (int fd,
return TRUE;
}
#if !defined (DBUS_DISABLE_ASSERT) || defined(DBUS_BUILD_TESTS)
/**
* On GNU libc systems, print a crude backtrace to the verbose log.
* On other systems, print "no backtrace support"
@ -3129,425 +3012,6 @@ _dbus_print_backtrace (void)
_dbus_verbose (" D-BUS not compiled with backtrace support\n");
#endif
}
/**
* Does the chdir, fork, setsid, etc. to become a daemon process.
*
* @param pidfile #NULL, or pidfile to create
* @param print_pid_fd file descriptor to print pid to, or -1 for none
* @param error return location for errors
* @returns #FALSE on failure
*/
dbus_bool_t
_dbus_become_daemon (const DBusString *pidfile,
int print_pid_fd,
DBusError *error)
{
const char *s;
pid_t child_pid;
int dev_null_fd;
_dbus_verbose ("Becoming a daemon...\n");
_dbus_verbose ("chdir to /\n");
if (chdir ("/") < 0)
{
dbus_set_error (error, DBUS_ERROR_FAILED,
"Could not chdir() to root directory");
return FALSE;
}
_dbus_verbose ("forking...\n");
switch ((child_pid = fork ()))
{
case -1:
_dbus_verbose ("fork failed\n");
dbus_set_error (error, _dbus_error_from_errno (errno),
"Failed to fork daemon: %s", _dbus_strerror (errno));
return FALSE;
break;
case 0:
_dbus_verbose ("in child, closing std file descriptors\n");
/* silently ignore failures here, if someone
* doesn't have /dev/null we may as well try
* to continue anyhow
*/
dev_null_fd = open ("/dev/null", O_RDWR);
if (dev_null_fd >= 0)
{
dup2 (dev_null_fd, 0);
dup2 (dev_null_fd, 1);
s = _dbus_getenv ("DBUS_DEBUG_OUTPUT");
if (s == NULL || *s == '\0')
dup2 (dev_null_fd, 2);
else
_dbus_verbose ("keeping stderr open due to DBUS_DEBUG_OUTPUT\n");
}
/* Get a predictable umask */
_dbus_verbose ("setting umask\n");
umask (022);
break;
default:
if (pidfile)
{
_dbus_verbose ("parent writing pid file\n");
if (!_dbus_write_pid_file (pidfile,
child_pid,
error))
{
_dbus_verbose ("pid file write failed, killing child\n");
kill (child_pid, SIGTERM);
return FALSE;
}
}
/* Write PID if requested */
if (print_pid_fd >= 0)
{
DBusString pid;
int bytes;
if (!_dbus_string_init (&pid))
{
_DBUS_SET_OOM (error);
kill (child_pid, SIGTERM);
return FALSE;
}
if (!_dbus_string_append_int (&pid, _dbus_getpid ()) ||
!_dbus_string_append (&pid, "\n"))
{
_dbus_string_free (&pid);
_DBUS_SET_OOM (error);
kill (child_pid, SIGTERM);
return FALSE;
}
bytes = _dbus_string_get_length (&pid);
if (_dbus_write (print_pid_fd, &pid, 0, bytes) != bytes)
{
dbus_set_error (error, DBUS_ERROR_FAILED,
"Printing message bus PID: %s\n",
_dbus_strerror (errno));
_dbus_string_free (&pid);
kill (child_pid, SIGTERM);
return FALSE;
}
_dbus_string_free (&pid);
}
_dbus_verbose ("parent exiting\n");
_exit (0);
break;
}
_dbus_verbose ("calling setsid()\n");
if (setsid () == -1)
_dbus_assert_not_reached ("setsid() failed");
return TRUE;
}
/**
* Creates a file containing the process ID.
*
* @param filename the filename to write to
* @param pid our process ID
* @param error return location for errors
* @returns #FALSE on failure
*/
dbus_bool_t
_dbus_write_pid_file (const DBusString *filename,
unsigned long pid,
DBusError *error)
{
const char *cfilename;
int fd;
FILE *f;
cfilename = _dbus_string_get_const_data (filename);
fd = open (cfilename, O_WRONLY|O_CREAT|O_EXCL|O_BINARY, 0644);
if (fd < 0)
{
dbus_set_error (error, _dbus_error_from_errno (errno),
"Failed to open \"%s\": %s", cfilename,
_dbus_strerror (errno));
return FALSE;
}
if ((f = fdopen (fd, "w")) == NULL)
{
dbus_set_error (error, _dbus_error_from_errno (errno),
"Failed to fdopen fd %d: %s", fd, _dbus_strerror (errno));
close (fd);
return FALSE;
}
if (fprintf (f, "%lu\n", pid) < 0)
{
dbus_set_error (error, _dbus_error_from_errno (errno),
"Failed to write to \"%s\": %s", cfilename,
_dbus_strerror (errno));
return FALSE;
}
if (fclose (f) == EOF)
{
dbus_set_error (error, _dbus_error_from_errno (errno),
"Failed to close \"%s\": %s", cfilename,
_dbus_strerror (errno));
return FALSE;
}
return TRUE;
}
/**
* Changes the user and group the bus is running as.
*
* @param uid the new user ID
* @param gid the new group ID
* @param error return location for errors
* @returns #FALSE on failure
*/
dbus_bool_t
_dbus_change_identity (dbus_uid_t uid,
dbus_gid_t gid,
DBusError *error)
{
/* setgroups() only works if we are a privileged process,
* so we don't return error on failure; the only possible
* failure is that we don't have perms to do it.
* FIXME not sure this is right, maybe if setuid()
* is going to work then setgroups() should also work.
*/
if (setgroups (0, NULL) < 0)
_dbus_warn ("Failed to drop supplementary groups: %s\n",
_dbus_strerror (errno));
/* Set GID first, or the setuid may remove our permission
* to change the GID
*/
if (setgid (gid) < 0)
{
dbus_set_error (error, _dbus_error_from_errno (errno),
"Failed to set GID to %lu: %s", gid,
_dbus_strerror (errno));
return FALSE;
}
if (setuid (uid) < 0)
{
dbus_set_error (error, _dbus_error_from_errno (errno),
"Failed to set UID to %lu: %s", uid,
_dbus_strerror (errno));
return FALSE;
}
return TRUE;
}
/** Installs a UNIX signal handler
*
* @param sig the signal to handle
* @param handler the handler
*/
void
_dbus_set_signal_handler (int sig,
DBusSignalHandler handler)
{
struct sigaction act;
sigset_t empty_mask;
sigemptyset (&empty_mask);
act.sa_handler = handler;
act.sa_mask = empty_mask;
act.sa_flags = 0;
sigaction (sig, &act, 0);
}
/** Checks if a file exists
*
* @param file full path to the file
* @returns #TRUE if file exists
*/
dbus_bool_t
_dbus_file_exists (const char *file)
{
return (access (file, F_OK) == 0);
}
/** Checks if user is at the console
*
* @param username user to check
* @param error return location for errors
* @returns #TRUE is the user is at the consolei and there are no errors
*/
dbus_bool_t
_dbus_user_at_console (const char *username,
DBusError *error)
{
DBusString f;
dbus_bool_t result;
result = FALSE;
if (!_dbus_string_init (&f))
{
_DBUS_SET_OOM (error);
return FALSE;
}
if (!_dbus_string_append (&f, DBUS_CONSOLE_DIR))
{
_DBUS_SET_OOM (error);
goto out;
}
if (!_dbus_string_append (&f, username))
{
_DBUS_SET_OOM (error);
goto out;
}
result = _dbus_file_exists (_dbus_string_get_const_data (&f));
out:
_dbus_string_free (&f);
return result;
}
#ifdef DBUS_BUILD_TESTS
#include <stdlib.h>
static void
check_dirname (const char *filename,
const char *dirname)
{
DBusString f, d;
_dbus_string_init_const (&f, filename);
if (!_dbus_string_init (&d))
_dbus_assert_not_reached ("no memory");
if (!_dbus_string_get_dirname (&f, &d))
_dbus_assert_not_reached ("no memory");
if (!_dbus_string_equal_c_str (&d, dirname))
{
_dbus_warn ("For filename \"%s\" got dirname \"%s\" and expected \"%s\"\n",
filename,
_dbus_string_get_const_data (&d),
dirname);
exit (1);
}
_dbus_string_free (&d);
}
static void
check_path_absolute (const char *path,
dbus_bool_t expected)
{
DBusString p;
_dbus_string_init_const (&p, path);
if (_dbus_path_is_absolute (&p) != expected)
{
_dbus_warn ("For path \"%s\" expected absolute = %d got %d\n",
path, expected, _dbus_path_is_absolute (&p));
exit (1);
}
}
/**
* Unit test for dbus-sysdeps.c.
*
* @returns #TRUE on success.
*/
dbus_bool_t
_dbus_sysdeps_test (void)
{
DBusString str;
double val;
int pos;
check_dirname ("foo", ".");
check_dirname ("foo/bar", "foo");
check_dirname ("foo//bar", "foo");
check_dirname ("foo///bar", "foo");
check_dirname ("foo/bar/", "foo");
check_dirname ("foo//bar/", "foo");
check_dirname ("foo///bar/", "foo");
check_dirname ("foo/bar//", "foo");
check_dirname ("foo//bar////", "foo");
check_dirname ("foo///bar///////", "foo");
check_dirname ("/foo", "/");
check_dirname ("////foo", "/");
check_dirname ("/foo/bar", "/foo");
check_dirname ("/foo//bar", "/foo");
check_dirname ("/foo///bar", "/foo");
check_dirname ("/", "/");
check_dirname ("///", "/");
check_dirname ("", ".");
_dbus_string_init_const (&str, "3.5");
if (!_dbus_string_parse_double (&str,
0, &val, &pos))
{
_dbus_warn ("Failed to parse double");
exit (1);
}
if (ABS(3.5 - val) > 1e-6)
{
_dbus_warn ("Failed to parse 3.5 correctly, got: %f", val);
exit (1);
}
if (pos != 3)
{
_dbus_warn ("_dbus_string_parse_double of \"3.5\" returned wrong position %d", pos);
exit (1);
}
_dbus_string_init_const (&str, "0xff");
if (!_dbus_string_parse_double (&str,
0, &val, &pos))
{
_dbus_warn ("Failed to parse double");
exit (1);
}
if (ABS (0xff - val) > 1e-6)
{
_dbus_warn ("Failed to parse 0xff correctly, got: %f\n", val);
exit (1);
}
if (pos != 4)
{
_dbus_warn ("_dbus_string_parse_double of \"0xff\" returned wrong position %d", pos);
exit (1);
}
check_path_absolute ("/", TRUE);
check_path_absolute ("/foo", TRUE);
check_path_absolute ("", FALSE);
check_path_absolute ("foo", FALSE);
check_path_absolute ("foo/bar", FALSE);
return TRUE;
}
#endif /* DBUS_BUILD_TESTS */
#endif /* asserts or tests enabled */
/** @} end of sysdeps */

View file

@ -65,7 +65,7 @@ free_group_info (void *data)
dbus_free (info);
}
static DBusUserInfo*
DBusUserInfo*
_dbus_user_database_lookup (DBusUserDatabase *db,
dbus_uid_t uid,
const DBusString *username,
@ -399,47 +399,6 @@ _dbus_get_user_id (const DBusString *username,
return TRUE;
}
/**
* Checks to see if the UID sent in is the console user
*
* @param uid UID of person to check
* @param error return location for errors
* @returns #TRUE if the UID is the same as the console user and there are no errors
*/
dbus_bool_t
_dbus_is_console_user (dbus_uid_t uid,
DBusError *error)
{
DBusUserDatabase *db;
const DBusUserInfo *info;
dbus_bool_t result = FALSE;
_dbus_user_database_lock_system ();
db = _dbus_user_database_get_system ();
if (db == NULL)
{
dbus_set_error (error, DBUS_ERROR_FAILED, "Could not get system database.");
_dbus_user_database_unlock_system ();
return FALSE;
}
info = _dbus_user_database_lookup (db, uid, NULL, error);
if (info == NULL)
{
_dbus_user_database_unlock_system ();
return FALSE;
}
result = _dbus_user_at_console (info->username, error);
_dbus_user_database_unlock_system ();
return result;
}
/**
* Gets group ID given groupname
*

View file

@ -54,7 +54,10 @@ dbus_bool_t _dbus_user_database_get_groupname (DBusUserDatabase *db,
const DBusString *groupname,
const DBusGroupInfo **info,
DBusError *error);
DBusUserInfo* _dbus_user_database_lookup (DBusUserDatabase *db,
dbus_uid_t uid,
const DBusString *username,
DBusError *error);
DBusUserDatabase* _dbus_user_database_get_system (void);
void _dbus_user_database_lock_system (void);

240
test/unused-code-gc.py Executable file
View file

@ -0,0 +1,240 @@
#! /usr/bin/python
import os
import sys
import string
import re
## hash from symbol name to list of symbols with that name,
## where the list of symbols contains a list representing each symbol
symbols = {}
roots = {}
def createBacklinks(name, syms):
for s in syms:
refs = s[2]
for r in refs:
## for each ref, add ourselves as a referencer
if symbols.has_key(r):
targets = symbols[r]
for t in targets:
if name not in t[5]:
t[5].append(name)
def markSymbol(frm, name):
if not symbols.has_key(name):
print "%s referenced but was not in the objdump"
syms = symbols[name]
## print ambiguous references unless they are internal noise like ".L129"
if len(syms) > 1 and name[0] != '.':
print "Reference to symbol '%s' from '%s' is ambiguous, marking all '%s'" % (name, frm, name)
print syms
for s in syms:
if s[4]:
pass ## already marked
else:
s[4] = 1
refs = s[2]
for r in refs:
markSymbol(s[0], r)
def cmpFilename(a, b):
v = cmp(a[1], b[1])
if v == 0:
v = cmp(a[0], b[0])
return v
def sizeAsString(bytes):
if bytes < 1024:
return "%d bytes" % bytes
elif bytes < 1024*1024:
return "%.2gK" % (bytes / 1024.0)
else:
return "%.2gM" % (bytes / 1024.0 / 1024.0)
def printLost():
list = []
filename = None
for (name, syms) in symbols.items():
s = syms[0] ## we always mark all or none for now
if not s[4] and name[0] != '.': ## skip .L129 type symbols
filename = s[3]
if not filename:
filename = "unknown file"
list.append ((name, filename, s[5], s[7]))
file_summaries = []
total_unused = 0
total_this_file = 0
filename = None
list.sort(cmpFilename)
for l in list:
next_filename = l[1]
if next_filename != filename:
if total_this_file > 0:
file_summaries.append (" %s may be unused in %s" % (sizeAsString(total_this_file), filename))
print "%s has these symbols not reachable from exported symbols:" % next_filename
filename = next_filename
total_this_file = 0
print " %s %s" % (l[0], sizeAsString(l[3]))
total_unused = total_unused + l[3]
total_this_file = total_this_file + l[3]
for trace in l[2]:
print " referenced from %s" % trace
for fs in file_summaries:
print fs
print "%s total may be unused" % sizeAsString(total_unused)
def main():
## 0001aa44 <_dbus_message_get_network_data>:
sym_re = re.compile ('([0-9a-f]+) <([^>]+)>:')
## 1aa49: e8 00 00 00 00 call 1aa4e <_dbus_message_get_network_data+0xa>
ref_re = re.compile (' <([^>]+)> *$')
## /home/hp/dbus-cvs/dbus/dbus/dbus-message.c:139
file_re = re.compile ('^(\/[^:].*):[0-9]+$')
## _dbus_message_get_network_data+0xa
funcname_re = re.compile ('([^+]+)\+[0-9a-fx]+')
## 00005410 T dbus_address_entries_free
dynsym_re = re.compile ('T ([^ \n]+)$')
filename = sys.argv[1]
command = """
objdump -D --demangle -l %s
""" % filename
command = string.strip (command)
print "Running: %s" % command
f = os.popen(command)
## first we find which functions reference which other functions
current_sym = None
lines = f.readlines()
for l in lines:
addr = None
name = None
target = None
file = None
match = sym_re.match(l)
if match:
addr = match.group(1)
name = match.group(2)
else:
match = ref_re.search(l)
if match:
target = match.group(1)
else:
match = file_re.match(l)
if match:
file = match.group(1)
if name:
## 0 symname, 1 address, 2 references, 3 filename, 4 reached, 5 referenced-by 6 backlinked 7 approx size
item = [name, addr, [], None, 0, [], 0, 0]
if symbols.has_key(name):
symbols[name].append(item)
else:
symbols[name] = [item]
if current_sym:
prev_addr = long(current_sym[1], 16)
our_addr = long(item[1], 16)
item[7] = our_addr - prev_addr
if item[7] < 0:
print "Computed negative size %d for %s" % (item[7], item[0])
item[7] = 0
current_sym = item
elif target and current_sym:
match = funcname_re.match(target)
if match:
## dump the "+address"
target = match.group(1)
if target == current_sym[0]:
pass ## skip self-references
else:
current_sym[2].append (target)
elif file and current_sym:
if file.startswith('/usr/include'):
## inlined libc thingy
pass
elif current_sym[0].startswith('.debug'):
## debug info
pass
elif current_sym[3] and current_sym[3] != file:
raise Exception ("%s in both %s and %s" % (current_sym[0], current_sym[3], file))
else:
current_sym[3] = file
## now we need to find the roots (exported symbols)
command = "nm -D %s" % filename
print "Running: %s" % command
f = os.popen(command)
lines = f.readlines ()
for l in lines:
match = dynsym_re.search(l)
if match:
name = match.group(1)
if roots.has_key(name):
raise Exception("symbol %s exported twice?" % name)
else:
roots[name] = 1
print "%d symbols exported from this object" % len(roots)
## these functions are used only indirectly, so we don't
## notice they are used. Manually add them as roots...
vtable_roots = ['unix_finalize',
'unix_handle_watch',
'unix_disconnect',
'unix_connection_set',
'unix_do_iteration',
'unix_live_messages_changed',
'unix_get_unix_fd',
'handle_client_data_cookie_sha1_mech',
'handle_client_data_external_mech',
'handle_server_data_cookie_sha1_mech',
'handle_server_data_external_mech',
'handle_client_initial_response_cookie_sha1_mech',
'handle_client_initial_response_external_mech',
'handle_client_shutdown_cookie_sha1_mech',
'handle_client_shutdown_external_mech',
'handle_server_shutdown_cookie_sha1_mech',
'handle_server_shutdown_external_mech'
]
for vr in vtable_roots:
if roots.has_key(vr):
raise Exception("%s is already a root" % vr)
roots[vr] = 1
for k in roots.keys():
markSymbol("root", k)
for (k, v) in symbols.items():
createBacklinks(k, v)
print """
The symbols mentioned below don't appear to be reachable starting from
the dynamic exports of the library. However, this program is pretty
dumb; a limitation that creates false positives is that it can only
trace 'reachable' through hardcoded function calls, if a function is
called only through a vtable, it won't be marked reachable (and
neither will its children in the call graph).
"""
print "The following are hardcoded in as vtable roots: %s" % vtable_roots
printLost()
if __name__ == "__main__":
main()