Merge stackstash-reorg branch into HEAD

2005-10-30  Soren Sandmann <sandmann@redhat.com>

	* Merge stackstash-reorg branch into HEAD
This commit is contained in:
Soren Sandmann
2005-10-30 20:14:31 +00:00
committed by Søren Sandmann Pedersen
parent 3783be00a8
commit dff4affaab
15 changed files with 1098 additions and 934 deletions

187
ChangeLog
View File

@ -1,3 +1,79 @@
2005-10-30 Soren Sandmann <sandmann@redhat.com>
* Merge stackstash-reorg branch into HEAD
2005-10-30 Soren Sandmann <sandmann@redhat.com>
* Makefile.am, sysprof.c, sysprof-text.c, collector.[ch]: Rename
profiler -> collector
2005-10-30 Soren Sandmann <sandmann@redhat.com>
* profile.c (profile_load): Reenable loading.
2005-10-30 Soren Sandmann <sandmann@redhat.com>
* profile.c (profile_save): Reenable saving.
Sat Oct 29 21:37:42 2005 Soeren Sandmann <sandmann@redhat.com>
* stackstash.c (stack_stash_foreach): Rename
stack_stash_foreach_reversed() to stack_stash_foreach(). Delete
the old, unused stack_stash_foreach().
* stackstash.h: Remove stack_stash_foreach_reversed().
* profiler.c: Use new name.
Sat Oct 29 18:37:37 2005 Søren Sandmann <sandmann@redhat.com>
* TODO: Updates
Sat Oct 29 17:38:01 2005 Søren Sandmann <sandmann@redhat.com>
* stackstash.[ch]: Add stack_stash_get_root().
* profile.c (profile_get_size): Make this function work again.
Sat Oct 29 16:58:22 2005 Søren Sandmann <sandmann@redhat.com>
* profile.c: Delete all the obsolete stuff related to call tree.
* TODO: update
Sat Oct 29 16:52:32 2005 Søren Sandmann <sandmann@redhat.com>
* TODO: Updates
* profile.c: Comment out a lot of unused stuff. Also temporarily
comment out loading and saving.
Sat Oct 29 16:45:34 2005 Søren Sandmann <sandmann@redhat.com>
* profile.c (compute_total): New function.
* profile.c (profile_list_callers): Port this function over to use
StackNodes instead.
Sat Oct 29 16:22:28 2005 Søren Sandmann <sandmann@redhat.com>
* profile.c (build_object_list): Make this function allocate
the ProfileObjects.
* stackstash.[ch]: Add stack_stash_foreach_by_address()
* profile.c (profile_get_objects): Use it here to reimplement
profile_get_objects() in terms of the stackstash.
Sat Oct 29 15:33:07 2005 Søren Sandmann <sandmann@redhat.com>
* profile.c (profile_new): Add stash field to profile.
* stackstash.[ch]: Add stack_node_list_leaves();
* profile.c (profile_create_descendants): Port this function over
to use StackNodes instead.
Sat Oct 29 14:43:00 2005 Søren Sandmann <sandmann@redhat.com> Sat Oct 29 14:43:00 2005 Søren Sandmann <sandmann@redhat.com>
Fix crash pointed reported by Rudi Chiarito. Fix crash pointed reported by Rudi Chiarito.
@ -7,6 +83,21 @@ Sat Oct 29 14:43:00 2005 Søren Sandmann <sandmann@redhat.com>
* sysprof.c (on_read): Only trace if n_addresses != 0. * sysprof.c (on_read): Only trace if n_addresses != 0.
Sat Oct 29 03:47:03 2005 Soeren Sandmann <sandmann@redhat.com>
* profile.[ch], sysprof.c: Get rid of ProfileObject for callers
and descendants.
* TODO: updates.
Sat Oct 29 02:57:34 2005 Soeren Sandmann <sandmann@redhat.com>
* stackstash.[ch]: Export the StackNode struct, add new
nodes_by_data hashtable to StackStash object, keep track of
whether objects are toplevels.
* TODO: some updates.
Sat Oct 29 14:29:55 2005 Søren Sandmann <sandmann@redhat.com> Sat Oct 29 14:29:55 2005 Søren Sandmann <sandmann@redhat.com>
* README, TODO: updates * README, TODO: updates
@ -24,6 +115,102 @@ Tue Oct 11 22:40:24 2005 Soeren Sandmann <sandmann@redhat.com>
constants, but this time make sure we won't divide by 0 or constants, but this time make sure we won't divide by 0 or
anything like that. anything like that.
Mon Oct 10 22:50:57 2005 Soeren Sandmann <sandmann@redhat.com>
* Merge in changes from HEAD
Thu Oct 6 22:28:39 2005 Soeren Sandmann <sandmann@redhat.com>
* stackstash.c (do_callback): Call stack func if node->size > 0,
not if node->children != NULL
* stackstash.c (do_reversed_callback): same
* profile.c (generate_presentation_name): Delete this function
* profile.c (generate_key): Delete this function.
Wed Oct 5 23:57:08 2005 Soeren Sandmann <sandmann@redhat.com>
* TODO: Updates
* profile.c (generate_call_tree): Delete all the process stuff
* profile.c (node_add_trace): Delete process argument
* profile.c (lookup_profile_object): Don't generate a string key,
just use the address directly.
* profile.c (ensure_profile_object): Use the address as
presentation name.
* profiler.c (profiler_create_profile): Pass in the resolved
stash.
Sun Oct 2 21:08:16 2005 Soeren Sandmann <sandmann@redhat.com>
* sysprof.c (on_delete): Work around glib bug 317775
* sysprof-text.c (signal_handler): Work around glib bug 317775
Sun Oct 2 16:31:37 2005 Soeren Sandmann <sandmann@redhat.com>
* stackstash.c (stack_stash_foreach_reversed): Add this function
* process.c: Add a per-process undefined symbol.
* profiler.c (resolve_symbols): Add code to do symbol resolution
here.
* TODO: Updates
Sat Oct 1 18:32:52 2005 Soeren Sandmann <sandmann@redhat.com>
* profile.h, sysprof.c: Remove some unnecessary includes.
Sat Oct 1 18:12:44 2005 Soeren Sandmann <sandmann@redhat.com>
* profiler.[ch]: Add profiler_get_n_samples(); add unused
empty_file_descriptor()
* profile.h: Add include guards
* process.[ch]: Delete process_flush_caches();
* helper.[ch]: Remove these files
* sysprof.c: Use the new profiler class.
* sysprof-text.c: Use the new profiler class
* Makefile.am: Various cleanups.
Sat Oct 1 17:05:43 2005 Soeren Sandmann <sandmann@redhat.com>
* profiler.[ch]: New files containing a profiler class with code
that can be shared between gui and command line.
* sysprof.c (main): Remove some commented out code
* stackstash.c (do_callback): Store the trace on the stack instead
of allocating it dynamically.
Sat Oct 1 01:50:15 2005 Soeren Sandmann <sandmann@redhat.com>
* stackstash.c (sstack_stash_add_trace): Simplify this function a
lot.
* helper.c: Include "process.h"
Sat Oct 1 01:29:06 2005 Soeren Sandmann <sandmann@redhat.com>
* profile.c (generate_object_table, generate_call_tree): Don't
take the process as an argument. Instead dig it out of the
stacktrace.
* helper.c (add_trace_to_stash): New file, that adds a stacktrace
and a process to a stackstash.
* stackstash.[ch]: Remove all traces of the concept of
Process. Simplify stack_node_add_trace() somewhat.
Mon Oct 10 22:49:03 2005 Soeren Sandmann <sandmann@redhat.com> Mon Oct 10 22:49:03 2005 Soeren Sandmann <sandmann@redhat.com>
* module/sysprof-module.c: Delete lots of commented-out code. * module/sysprof-module.c: Delete lots of commented-out code.

View File

@ -2,67 +2,54 @@ SUBDIRS = $(MODULE_SUBDIR)
DIST_SUBDIRS = module DIST_SUBDIRS = module
bin_PROGRAMS = sysprof sysprof-text bin_PROGRAMS = sysprof sysprof-text
pkgdata_DATA = sysprof.glade sysprof-icon.png
sysprof_SOURCES = \ SYSPROF_CORE = \
binfile.h \ binfile.h \
binfile.c \ binfile.c \
process.h \ collector.c \
process.c \ collector.h \
profile.h \ process.h \
profile.c \ process.c \
sfile.h \ profile.h \
sfile.c \ profile.c \
stackstash.h \ sfile.h \
stackstash.c \ sfile.c \
module/sysprof-module.h \ stackstash.h \
sysprof.c \ stackstash.c \
treeviewutils.h \ module/sysprof-module.h \
treeviewutils.c \ watch.h \
watch.h \
watch.c watch.c
sysprof_text_SOURCES = \ sysprof_SOURCES = \
binfile.h \ $(SYSPROF_CORE) \
binfile.c \ treeviewutils.h \
process.h \ treeviewutils.c \
process.c \ sysprof.c
profile.h \
profile.c \ sysprof_text_SOURCES = \
sfile.h \ $(SYSPROF_CORE) \
sfile.c \ signal-handler.h \
stackstash.h \ signal-handler.c \
stackstash.c \ sysprof-text.c
module/sysprof-module.h \
signal-handler.h \
signal-handler.c \
sysprof-text.c \
treeviewutils.h \
treeviewutils.c \
watch.h \
watch.c
sysprof_LDADD = $(DEP_LIBS) sysprof_LDADD = $(DEP_LIBS)
sysprof_text_LDADD = $(DEP_LIBS) sysprof_text_LDADD = $(DEP_LIBS)
INCLUDES = \ pixmapsdir = $(datadir)/pixmaps
$(DEP_CFLAGS) \
-DDATADIR=\"$(pkgdatadir)\" \
-DPIXMAPDIR=\"$(datadir)/pixmaps\"
# memprof.desktop INCLUDES = \
# memprof.spec.in $(DEP_CFLAGS) \
-DDATADIR=\"$(pkgdatadir)\" \
-DPIXMAPDIR=\"$(pixmapsdir)\"
EXTRA_DIST = \ EXTRA_DIST = \
sysprof.glade \ module/sysprof-module.c \
sysprof-icon.png \ module/sysprof-module.h \
module/sysprof-module.c \
module/sysprof-module.h \
module/Makefile module/Makefile
pixmapsdir = $(datadir)/pixmaps dist_pkgdata_DATA = sysprof.glade
pixmaps_DATA = sysprof-icon.png dist_pixmaps_DATA = sysprof-icon.png
insert-module: insert-module:
modprobe -r sysprof-module modprobe -r sysprof-module

3
NEWS
View File

@ -0,0 +1,3 @@
- New 'everything' object
- New commandline version
- Assign time spent in kernel to the user process responsible

140
TODO
View File

@ -11,9 +11,9 @@ Before 1.0.1:
See also http://www.fedoraproject.org/wiki/Extras/KernelModuleProposal See also http://www.fedoraproject.org/wiki/Extras/KernelModuleProposal
Before 1.2: Someone already did create a package - should be googlable.
* Crash reported by Rudi Chiarito with n_addrs == 0. Before 1.2:
* Find out why we get hangs with rawhide kernels. This only happens with the * Find out why we get hangs with rawhide kernels. This only happens with the
'trace "current"' code. See this mail: 'trace "current"' code. See this mail:
@ -25,6 +25,54 @@ Before 1.2:
(Reported by Kjartan Marass). (Reported by Kjartan Marass).
- Fix bugs/performance issues:
- total should probably be cached so that compute_total() doesn't
take 80% of the time to generate a profile.
- decorate_node should be done lazily
- Find out why we sometimes get completely ridicoulous stacktraces,
where main seems to be called from within Xlib etc. This happens
even after restarting everything.
- It looks like the stackstash-reorg code confuses "main" from
unrelated processes. - currently it looks like if multiple
"main"s are present, only one gets listed in the object list.
- Numbers in caller view are completely screwed up.
- It looks like it sometimes gets confused with similar but different
processess: Something like:
process a spends 80% in foo() called from bar()
process b spends 1% in foo() called from baz()
we get reports of baz() using > 80% of the time.
Or something.
- make the things we put in a stackstash real
objects so that
- we can save them
- they will know how to delete the presentation
names and themselves (through a virtual function)
- they can contain markup etc.
- a more pragmatic approach might be to just walk the tree and
save it.
- make stasckstash ref counted
- plug all the leaks
- don't leak the presentation strings/objects
- loading and saving probably leak right now
- think about loading and saving. Goals
- Can load 1.0 profiles
- Don't export too much of stackstashes to the rest of the
app
- Reorganise stackstash and profile
- Remaining TODO before merging into head:
- rename profiler->collector
* Consider renaming profiler.[ch] to collector.[ch]
* Make sure sysprof-text is not linked to gtk+
* Figure out how to make sfile.[ch] use less memory. * Figure out how to make sfile.[ch] use less memory.
- In general clean sfile.[ch] up a little: - In general clean sfile.[ch] up a little:
- split out dfa in its own generic class - split out dfa in its own generic class
@ -192,39 +240,6 @@ http://www.linuxbase.org/spec/booksets/LSB-Embedded/LSB-Embedded/ehframe.html
so (can we know the size in advance?)) so (can we know the size in advance?))
- instead of what we do now: set the busy cursor unconditionally - instead of what we do now: set the busy cursor unconditionally
- Reorganise stackstash and profile
- stackstash should just take traces of addresses without knowing
anything about what those addresses mean.
- stacktraces should then begin with a process
- stackstash should be extended so that the "create_descendant"
and "create_ancestor" code in profile.c can use it directly.
At that point, get rid of the profile tree, and rename
profile.c to analyze.c.
- the profile tree will then just be a stackstash where the
addresses are presentation strings instead.
- Doing a profile will then amount to converting the raw stash
to one where the addresses have been looked up and converted to
presentation strings.
-=-=
- profile should take traces of pointers to presentation
objects without knowing anything about these presentation
objects.
- For each stack node, compute a presentation object
(probably need to export opaque stacknode objects
with set/get_user_data)
- Send each stack trace to the profile module, along with
presentation objects. Maybe just a map from stack nodes
to presentation objects.
- Charge 'self' properly to processes that don't get any stack trace at all - Charge 'self' properly to processes that don't get any stack trace at all
(probably we get that for free with stackstash reorganisation) (probably we get that for free with stackstash reorganisation)
@ -249,7 +264,8 @@ http://www.linuxbase.org/spec/booksets/LSB-Embedded/LSB-Embedded/ehframe.html
- Add support for line numbers within functions - Add support for line numbers within functions
- Possibly a special "view details" mode, assuming that - Possibly a special "view details" mode, assuming that
the details of a function are not that interesting the details of a function are not that interesting
together with a tree. together with a tree. (Could add radio buttons somewhere in
in the right pane).
- rethink caller list, not terribly useful at the moment. Federico suggested - rethink caller list, not terribly useful at the moment. Federico suggested
listing all ancestors. listing all ancestors.
@ -452,12 +468,64 @@ Later:
DONE: DONE:
* Crash reported by Rudi Chiarito with n_addrs == 0.
* Find out what distributions it actually works on * Find out what distributions it actually works on
(ask for sucess/failure-stories in 1.0 releases) (ask for sucess/failure-stories in 1.0 releases)
* Add note in README about Ubuntu and Debian -dbg packages and how to get * Add note in README about Ubuntu and Debian -dbg packages and how to get
debug symbols for X there. debug symbols for X there.
stackstash reorg:
- make loading and saving work again.
- make stashes loadable and savable.
- add a way to convert 1.0 files to stashes
- Get rid of remaining uses of stack_stash_foreach(), then
rename stack_stash_foreach_reversed() to
stack_stash_foreach()
- stackstash should just take traces of addresses without knowing
anything about what those addresses mean.
- stacktraces should then begin with a process
- stackstash should be extended so that the "create_descendant"
and "create_ancestor" code in profile.c can use it directly.
At that point, get rid of the profile tree, and rename
profile.c to analyze.c.
- the profile tree will then just be a stackstash where the
addresses are presentation strings instead.
- Doing a profile will then amount to converting the raw stash
to one where the addresses have been looked up and converted to
presentation strings.
-=-=
- profile should take traces of pointers to presentation
objects without knowing anything about these presentation
objects.
- For each stack node, compute a presentation object
(probably need to export opaque stacknode objects
with set/get_user_data)
- Send each stack trace to the profile module, along with
presentation objects. Maybe just a map from stack nodes
to presentation objects.
- Make the Profile class use the stash directly instead of
building its own copy.
- store a stash in the profile class
- make sure descendants and callers can be
built from it.
- get rid of other stuff in the profile
struct
* Before 1.0: * Before 1.0:
- Update version numbers in source - Update version numbers in source

300
collector.c Normal file
View File

@ -0,0 +1,300 @@
#include "stackstash.h"
#include "collector.h"
#include "module/sysprof-module.h"
#include "watch.h"
#include "process.h"
#include <errno.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
struct Collector
{
CollectorFunc callback;
gpointer data;
StackStash * stash;
int fd;
GTimeVal latest_reset;
int n_samples;
};
/* callback is called whenever a new sample arrives */
Collector *
collector_new (CollectorFunc callback,
gpointer data)
{
Collector *collector = g_new0 (Collector, 1);
collector->callback = callback;
collector->data = data;
collector->fd = -1;
collector->stash = NULL;
collector_reset (collector);
return collector;
}
static double
timeval_to_ms (const GTimeVal *timeval)
{
return (timeval->tv_sec * G_USEC_PER_SEC + timeval->tv_usec) / 1000.0;
}
static double
time_diff (const GTimeVal *first,
const GTimeVal *second)
{
double first_ms = timeval_to_ms (first);
double second_ms = timeval_to_ms (second);
return first_ms - second_ms;
}
#define RESET_DEAD_PERIOD 250
static void
add_trace_to_stash (SysprofStackTrace *trace,
StackStash *stash)
{
int i;
gulong *addrs;
Process *process = process_get_from_pid (trace->pid);
addrs = g_new (gulong, trace->n_addresses + 1);
for (i = 0; i < trace->n_addresses; ++i)
{
process_ensure_map (process, trace->pid,
(gulong)trace->addresses[i]);
addrs[i] = (gulong)trace->addresses[i];
}
addrs[i] = (gulong)process;
stack_stash_add_trace (
stash, addrs, trace->n_addresses + 1, 1);
g_free (addrs);
}
static void
on_read (gpointer data)
{
SysprofStackTrace trace;
Collector *collector = data;
GTimeVal now;
int rd;
rd = read (collector->fd, &trace, sizeof (trace));
if (rd == -1 && errno == EWOULDBLOCK)
return;
g_get_current_time (&now);
/* After a reset we ignore samples for a short period so that
* a reset will actually cause 'samples' to become 0
*/
if (time_diff (&now, &collector->latest_reset) < RESET_DEAD_PERIOD)
return;
#if 0
int i;
g_print ("pid: %d\n", trace.pid);
for (i=0; i < trace.n_addresses; ++i)
g_print ("rd: %08x\n", trace.addresses[i]);
g_print ("-=-\n");
#endif
if (rd > 0)
{
add_trace_to_stash (&trace, collector->stash);
collector->n_samples++;
}
if (collector->callback)
collector->callback (collector->data);
}
static gboolean
load_module (void)
{
int exit_status = -1;
char *dummy1, *dummy2;
if (g_spawn_command_line_sync ("/sbin/modprobe sysprof-module",
&dummy1, &dummy2,
&exit_status,
NULL))
{
if (WIFEXITED (exit_status))
exit_status = WEXITSTATUS (exit_status);
g_free (dummy1);
g_free (dummy2);
}
return (exit_status == 0);
}
static gboolean
open_fd (Collector *collector,
GError **err)
{
int fd;
fd = open ("/proc/sysprof-trace", O_RDONLY);
if (fd < 0)
{
load_module();
fd = open ("/proc/sysprof-trace", O_RDONLY);
if (fd < 0)
{
/* FIXME: set error */
#if 0
sorry (app->main_window,
"Can't open /proc/sysprof-trace. You need to insert\n"
"the sysprof kernel module. Run\n"
"\n"
" modprobe sysprof-module\n"
"\n"
"as root.");
#endif
return FALSE;
}
}
collector->fd = fd;
fd_add_watch (collector->fd, collector);
return TRUE;
}
static void
empty_file_descriptor (Collector *collector)
{
int rd;
SysprofStackTrace trace;
do
{
rd = read (collector->fd, &trace, sizeof (trace));
} while (rd != -1); /* until EWOULDBLOCK */
}
gboolean
collector_start (Collector *collector,
GError **err)
{
if (collector->fd < 0 && !open_fd (collector, err))
return FALSE;
fd_set_read_callback (collector->fd, on_read);
return TRUE;
}
void
collector_stop (Collector *collector)
{
fd_set_read_callback (collector->fd, NULL);
}
void
collector_reset (Collector *collector)
{
if (collector->stash)
stack_stash_free (collector->stash);
collector->stash = stack_stash_new ();
collector->n_samples = 0;
g_get_current_time (&collector->latest_reset);
}
int
collector_get_n_samples (Collector *collector)
{
return collector->n_samples;
}
typedef struct
{
StackStash *resolved_stash;
GHashTable *unique_symbols;
} ResolveInfo;
static char *
unique_dup (GHashTable *unique_symbols, char *s)
{
char *result;
result = g_hash_table_lookup (unique_symbols, s);
if (!result)
{
result = g_strdup (s);
g_hash_table_insert (unique_symbols, s, result);
}
return result;
}
static char *
lookup_symbol (Process *process, gpointer address, GHashTable *unique_symbols)
{
const Symbol *s = process_lookup_symbol (process, (gulong)address);
return unique_dup (unique_symbols, s->name);
}
static void
resolve_symbols (GSList *trace, gint size, gpointer data)
{
GSList *slist;
ResolveInfo *info = data;
Process *process = g_slist_last (trace)->data;
GPtrArray *resolved_trace = g_ptr_array_new ();
for (slist = trace; slist && slist->next; slist = slist->next)
{
gpointer address = slist->data;
char *symbol;
symbol = lookup_symbol (process, address, info->unique_symbols);
g_ptr_array_add (resolved_trace, symbol);
}
g_ptr_array_add (resolved_trace,
unique_dup (info->unique_symbols,
(char *)process_get_cmdline (process)));
g_ptr_array_add (resolved_trace,
unique_dup (info->unique_symbols,
"Everything"));
stack_stash_add_trace (info->resolved_stash, (gulong *)resolved_trace->pdata, resolved_trace->len, size);
}
Profile *
collector_create_profile (Collector *collector)
{
ResolveInfo info;
info.resolved_stash = stack_stash_new ();
info.unique_symbols = g_hash_table_new (g_direct_hash, g_direct_equal);
stack_stash_foreach (collector->stash, resolve_symbols, &info);
g_hash_table_destroy (info.unique_symbols);
return profile_new (info.resolved_stash);
}

16
collector.h Normal file
View File

@ -0,0 +1,16 @@
#include "profile.h"
typedef struct Collector Collector;
typedef void (* CollectorFunc) (gpointer data);
/* callback is called whenever a new sample arrives */
Collector *collector_new (CollectorFunc callback,
gpointer data);
gboolean collector_start (Collector *collector,
GError **err);
void collector_stop (Collector *collector);
void collector_reset (Collector *collector);
int collector_get_n_samples (Collector *collector);
Profile * collector_create_profile (Collector *collector);

View File

@ -56,6 +56,8 @@ struct Process
GList *bad_pages; GList *bad_pages;
int pid; int pid;
Symbol undefined;
}; };
static void static void
@ -136,7 +138,7 @@ create_process (const char *cmdline, int pid)
{ {
Process *p; Process *p;
p = g_new (Process, 1); p = g_new0 (Process, 1);
if (*cmdline != '\0') if (*cmdline != '\0')
p->cmdline = g_strdup_printf ("[%s]", cmdline); p->cmdline = g_strdup_printf ("[%s]", cmdline);
@ -146,6 +148,8 @@ create_process (const char *cmdline, int pid)
p->bad_pages = NULL; p->bad_pages = NULL;
p->maps = NULL; p->maps = NULL;
p->pid = pid; p->pid = pid;
p->undefined.name = NULL;
p->undefined.address = NULL;
g_assert (!g_hash_table_lookup (processes_by_pid, GINT_TO_POINTER (pid))); g_assert (!g_hash_table_lookup (processes_by_pid, GINT_TO_POINTER (pid)));
g_assert (!g_hash_table_lookup (processes_by_cmdline, cmdline)); g_assert (!g_hash_table_lookup (processes_by_cmdline, cmdline));
@ -340,7 +344,6 @@ process_get_from_pid (int pid)
const Symbol * const Symbol *
process_lookup_symbol (Process *process, gulong address) process_lookup_symbol (Process *process, gulong address)
{ {
static Symbol undefined;
const Symbol *result; const Symbol *result;
static Symbol kernel; static Symbol kernel;
Map *map = process_locate_map (process, address); Map *map = process_locate_map (process, address);
@ -349,18 +352,20 @@ process_lookup_symbol (Process *process, gulong address)
if (address == 0x1) if (address == 0x1)
{ {
kernel.name = "in kernel"; kernel.name = "In kernel";
kernel.address = 0x00001337; kernel.address = 0x00001337;
return &kernel; return &kernel;
} }
else if (!map) else if (!map)
{ {
if (undefined.name) if (!process->undefined.name)
g_free (undefined.name); {
undefined.name = g_strdup_printf ("??? %s", process->cmdline); process->undefined.name =
undefined.address = 0xBABE0001; g_strdup_printf ("(??? %s)", process->cmdline);
process->undefined.address = 0xBABE0001;
}
return &undefined; return &process->undefined;
} }
address -= map->start; address -= map->start;
@ -388,38 +393,3 @@ process_get_cmdline (Process *process)
{ {
return process->cmdline; return process->cmdline;
} }
static void
free_process (gpointer key, gpointer value, gpointer data)
{
char *cmdline = key;
Process *process = value;
#if 0
g_print ("freeing: %p\n", process);
memset (process, '\0', sizeof (Process));
#endif
g_free (process->cmdline);
#if 0
process->cmdline = "You are using free()'d memory";
#endif
process_free_maps (process);
g_list_free (process->bad_pages);
g_free (cmdline);
g_free (process);
}
void
process_flush_caches (void)
{
if (!processes_by_cmdline)
return;
g_hash_table_foreach (processes_by_cmdline, free_process, NULL);
g_hash_table_destroy (processes_by_cmdline);
g_hash_table_destroy (processes_by_pid);
processes_by_cmdline = NULL;
processes_by_pid = NULL;
}

View File

@ -24,6 +24,7 @@
#ifndef PROCESS_H #ifndef PROCESS_H
#define PROCESS_H #define PROCESS_H
#include <glib.h>
#include "binfile.h" #include "binfile.h"
typedef struct Process Process; typedef struct Process Process;
@ -41,9 +42,11 @@ typedef struct Process Process;
* To flush the pid cache, call process_flush_caches(). * To flush the pid cache, call process_flush_caches().
* This will invalidate all instances of Process. * This will invalidate all instances of Process.
* *
* The real fix for this is probably to have the kernel module report the
* maps along with the stacktrace.
*
*/ */
void process_flush_caches (void);
Process * process_get_from_pid (int pid); Process * process_get_from_pid (int pid);
void process_ensure_map (Process *process, void process_ensure_map (Process *process,
int pid, int pid,

522
profile.c
View File

@ -38,37 +38,9 @@ update()
#endif #endif
} }
static guint
direct_hash_no_null (gconstpointer v)
{
g_assert (v != NULL);
return GPOINTER_TO_UINT (v);
}
struct Node
{
ProfileObject *object;
Node *siblings; /* siblings in the call tree */
Node *children; /* children in the call tree */
Node *parent; /* parent in call tree */
Node *next; /* nodes that correspond to same object are linked though
* this pointer
*/
guint total;
guint self;
gboolean toplevel;
};
struct Profile struct Profile
{ {
gint size; StackStash * stash;
Node * call_tree;
/* This table is really a cache. We can build it from the call_tree */
GHashTable * nodes_by_object;
}; };
static SFormat * static SFormat *
@ -105,34 +77,52 @@ create_format (void)
NULL)); NULL));
} }
static void static int
add_object (gpointer key, gpointer value, gpointer data) sum_children (StackNode *node)
{ {
SFileOutput *output = data; int total;
ProfileObject *object = key; StackNode *child;
sfile_begin_add_record (output, "object"); /* FIXME: this is pretty inefficient. Instead perhaps
* maintain or compute it in the stackstash
*/
total = node->size;
sfile_add_string (output, "name", object->name); for (child = node->children; child != NULL; child = child->siblings)
sfile_add_integer (output, "total", object->total); total += sum_children (child);
sfile_add_integer (output, "self", object->self);
sfile_end_add (output, "object", object); return total;
}
static int
compute_total (StackNode *node)
{
StackNode *n;
int total = 0;
for (n = node; n != NULL; n = n->next)
{
if (n->toplevel)
total += sum_children (n);
}
return total;
} }
static void static void
serialize_call_tree (Node *node, SFileOutput *output) serialize_call_tree (StackNode *node,
SFileOutput *output)
{ {
if (!node) if (!node)
return; return;
sfile_begin_add_record (output, "node"); sfile_begin_add_record (output, "node");
sfile_add_pointer (output, "object", node->object); sfile_add_pointer (output, "object", node->address);
sfile_add_pointer (output, "siblings", node->siblings); sfile_add_pointer (output, "siblings", node->siblings);
sfile_add_pointer (output, "children", node->children); sfile_add_pointer (output, "children", node->children);
sfile_add_pointer (output, "parent", node->parent); sfile_add_pointer (output, "parent", node->parent);
sfile_add_integer (output, "total", node->total); sfile_add_integer (output, "total", compute_total (node));
sfile_add_integer (output, "self", node->self); sfile_add_integer (output, "self", node->size);
sfile_add_integer (output, "toplevel", node->toplevel); sfile_add_integer (output, "toplevel", node->toplevel);
sfile_end_add (output, "node", node); sfile_end_add (output, "node", node);
@ -147,20 +137,39 @@ profile_save (Profile *profile,
{ {
gboolean result; gboolean result;
GList *profile_objects;
GList *list;
SFormat *format = create_format (); SFormat *format = create_format ();
SFileOutput *output = sfile_output_new (format); SFileOutput *output = sfile_output_new (format);
sfile_begin_add_record (output, "profile"); sfile_begin_add_record (output, "profile");
sfile_add_integer (output, "size", profile->size); sfile_add_integer (output, "size", profile_get_size (profile));
sfile_add_pointer (output, "call_tree", profile->call_tree); sfile_add_pointer (output, "call_tree",
stack_stash_get_root (profile->stash));
profile_objects = profile_get_objects (profile);
sfile_begin_add_list (output, "objects"); sfile_begin_add_list (output, "objects");
g_hash_table_foreach (profile->nodes_by_object, add_object, output); for (list = profile_objects; list != NULL; list = list->next)
{
ProfileObject *object = list->data;
sfile_begin_add_record (output, "object");
sfile_add_string (output, "name", object->name);
sfile_add_integer (output, "total", object->total);
sfile_add_integer (output, "self", object->self);
sfile_end_add (output, "object", object->name);
}
g_list_foreach (profile_objects, (GFunc)g_free, NULL);
g_list_free (profile_objects);
sfile_end_add (output, "objects", NULL); sfile_end_add (output, "objects", NULL);
sfile_begin_add_list (output, "nodes"); sfile_begin_add_list (output, "nodes");
serialize_call_tree (profile->call_tree, output); serialize_call_tree (stack_stash_get_root (profile->stash), output);
sfile_end_add (output, "nodes", NULL); sfile_end_add (output, "nodes", NULL);
sfile_end_add (output, "profile", NULL); sfile_end_add (output, "profile", NULL);
@ -173,6 +182,7 @@ profile_save (Profile *profile,
return result; return result;
} }
#if 0
static void static void
make_hash_table (Node *node, GHashTable *table) make_hash_table (Node *node, GHashTable *table)
{ {
@ -189,6 +199,7 @@ make_hash_table (Node *node, GHashTable *table)
make_hash_table (node->siblings, table); make_hash_table (node->siblings, table);
make_hash_table (node->children, table); make_hash_table (node->children, table);
} }
#endif
Profile * Profile *
profile_load (const char *filename, GError **err) profile_load (const char *filename, GError **err)
@ -197,6 +208,7 @@ profile_load (const char *filename, GError **err)
SFileInput *input; SFileInput *input;
Profile *profile; Profile *profile;
int n, i; int n, i;
StackNode *root;
format = create_format (); format = create_format ();
input = sfile_load (filename, format, err); input = sfile_load (filename, format, err);
@ -206,43 +218,39 @@ profile_load (const char *filename, GError **err)
profile = g_new (Profile, 1); profile = g_new (Profile, 1);
profile->nodes_by_object =
g_hash_table_new (direct_hash_no_null, g_direct_equal);
sfile_begin_get_record (input, "profile"); sfile_begin_get_record (input, "profile");
sfile_get_integer (input, "size", &profile->size); sfile_get_integer (input, "size", NULL);
sfile_get_pointer (input, "call_tree", (void **)&profile->call_tree); sfile_get_pointer (input, "call_tree", (gpointer *)&root);
n = sfile_begin_get_list (input, "objects"); n = sfile_begin_get_list (input, "objects");
for (i = 0; i < n; ++i) for (i = 0; i < n; ++i)
{ {
ProfileObject *obj = g_new (ProfileObject, 1); char *string;
sfile_begin_get_record (input, "object"); sfile_begin_get_record (input, "object");
sfile_get_string (input, "name", &obj->name); sfile_get_string (input, "name", &string);
sfile_get_integer (input, "total", (gint32 *)&obj->total); sfile_get_integer (input, "total", NULL);
sfile_get_integer (input, "self", (gint32 *)&obj->self); sfile_get_integer (input, "self", NULL);
sfile_end_get (input, "object", obj); sfile_end_get (input, "object", string);
} }
sfile_end_get (input, "objects", NULL); sfile_end_get (input, "objects", NULL);
profile->call_tree = NULL;
n = sfile_begin_get_list (input, "nodes"); n = sfile_begin_get_list (input, "nodes");
for (i = 0; i < n; ++i) for (i = 0; i < n; ++i)
{ {
Node *node = g_new (Node, 1); StackNode *node = g_new (StackNode, 1);
sfile_begin_get_record (input, "node"); sfile_begin_get_record (input, "node");
sfile_get_pointer (input, "object", (gpointer *)&node->object); sfile_get_pointer (input, "object", (gpointer *)&node->address);
sfile_get_pointer (input, "siblings", (gpointer *)&node->siblings); sfile_get_pointer (input, "siblings", (gpointer *)&node->siblings);
sfile_get_pointer (input, "children", (gpointer *)&node->children); sfile_get_pointer (input, "children", (gpointer *)&node->children);
sfile_get_pointer (input, "parent", (gpointer *)&node->parent); sfile_get_pointer (input, "parent", (gpointer *)&node->parent);
sfile_get_integer (input, "total", (gint32 *)&node->total); sfile_get_integer (input, "total", NULL);
sfile_get_integer (input, "self", (gint32 *)&node->self); sfile_get_integer (input, "self", (gint32 *)&node->size);
sfile_get_integer (input, "toplevel", &node->toplevel); sfile_get_integer (input, "toplevel", &node->toplevel);
sfile_end_get (input, "node", node); sfile_end_get (input, "node", node);
@ -255,284 +263,19 @@ profile_load (const char *filename, GError **err)
sformat_free (format); sformat_free (format);
sfile_input_free (input); sfile_input_free (input);
make_hash_table (profile->call_tree, profile->nodes_by_object); profile->stash = stack_stash_new_from_root (root);
return profile; return profile;
} }
static ProfileObject *
profile_object_new (void)
{
ProfileObject *obj = g_new (ProfileObject, 1);
obj->total = 0;
obj->self = 0;
return obj;
}
static void
profile_object_free (ProfileObject *obj)
{
g_free (obj->name);
g_free (obj);
}
static char *
generate_key (Process *process, gulong address)
{
if (address)
{
const Symbol *symbol = process_lookup_symbol (process, address);
return g_strdup_printf ("%p%s", (void *)symbol->address, symbol->name);
}
else
{
return g_strdup_printf ("p:%p", process_get_cmdline (process));
}
}
static char *
generate_presentation_name (Process *process, gulong address)
{
/* FIXME - not10
* using 0 to indicate "process" is broken
*/
if (address)
{
const Symbol *symbol = process_lookup_symbol (process, address);
return g_strdup_printf ("%s", symbol->name);
}
else
{
return g_strdup_printf ("%s", process_get_cmdline (process));
}
}
static void
ensure_profile_object (GHashTable *profile_objects, Process *process, gulong address)
{
char *key = generate_key (process, address);
if (!g_hash_table_lookup (profile_objects, key))
{
ProfileObject *object;
object = profile_object_new ();
object->name = generate_presentation_name (process, address);
g_hash_table_insert (profile_objects, key, object);
}
else
{
g_free (key);
}
}
static ProfileObject *
lookup_profile_object (GHashTable *profile_objects, Process *process, gulong address)
{
ProfileObject *object;
char *key = generate_key (process, address);
object = g_hash_table_lookup (profile_objects, key);
g_free (key);
g_assert (object);
return object;
}
typedef struct Info Info;
struct Info
{
Profile *profile;
GHashTable *profile_objects;
};
static void
generate_object_table (Process *process, GSList *trace, gint size, gpointer data)
{
Info *info = data;
GSList *list;
ensure_profile_object (info->profile_objects, process, 0);
for (list = trace; list != NULL; list = list->next)
{
update ();
ensure_profile_object (info->profile_objects, process, (gulong)list->data);
}
info->profile->size += size;
}
static Node *
node_new ()
{
Node *node = g_new (Node, 1);
node->siblings = NULL;
node->children = NULL;
node->parent = NULL;
node->next = NULL;
node->object = NULL;
node->self = 0;
node->total = 0;
return node;
}
static Node *
node_add_trace (Profile *profile, GHashTable *profile_objects, Node *node, Process *process,
GSList *trace, gint size,
GHashTable *seen_objects)
{
ProfileObject *object;
Node *match = NULL;
if (!trace)
return node;
object = lookup_profile_object (profile_objects, process, (gulong)trace->data);
for (match = node; match != NULL; match = match->siblings)
{
if (match->object == object)
break;
}
if (!match)
{
match = node_new ();
match->object = object;
match->siblings = node;
node = match;
if (g_hash_table_lookup (seen_objects, object))
match->toplevel = FALSE;
else
match->toplevel = TRUE;
match->next = g_hash_table_lookup (profile->nodes_by_object, object);
g_hash_table_insert (profile->nodes_by_object, object, match);
}
g_hash_table_insert (seen_objects, object, GINT_TO_POINTER (1));
#if 0
g_print ("%s adds %d\n", match->object->name, size);
#endif
match->total += size;
if (!trace->next)
match->self += size;
match->children = node_add_trace (profile, profile_objects, match->children, process, trace->next, size,
seen_objects);
return node;
}
#if 0
static void
dump_trace (GSList *trace)
{
g_print ("TRACE: ");
while (trace)
{
g_print ("%x ", trace->data);
trace = trace->next;
}
g_print ("\n\n");
}
#endif
static void
generate_call_tree (Process *process, GSList *trace, gint size, gpointer data)
{
Info *info = data;
Node *match = NULL;
ProfileObject *proc = lookup_profile_object (info->profile_objects, process, 0);
GHashTable *seen_objects;
for (match = info->profile->call_tree; match; match = match->siblings)
{
if (match->object == proc)
break;
}
if (!match)
{
match = node_new ();
match->object = proc;
match->siblings = info->profile->call_tree;
info->profile->call_tree = match;
match->toplevel = TRUE;
}
g_hash_table_insert (info->profile->nodes_by_object, proc, match);
match->total += size;
if (!trace)
match->self += size;
seen_objects = g_hash_table_new (direct_hash_no_null, g_direct_equal);
g_hash_table_insert (seen_objects, proc, GINT_TO_POINTER (1));
update ();
match->children = node_add_trace (info->profile, info->profile_objects, match->children, process,
trace, size, seen_objects);
g_hash_table_destroy (seen_objects);
}
static void
link_parents (Node *node, Node *parent)
{
if (!node)
return;
node->parent = parent;
link_parents (node->siblings, parent);
link_parents (node->children, node);
}
static void
compute_object_total (gpointer key, gpointer value, gpointer data)
{
Node *node;
ProfileObject *object = key;
for (node = value; node != NULL; node = node->next)
{
object->self += node->self;
if (node->toplevel)
object->total += node->total;
}
}
Profile * Profile *
profile_new (StackStash *stash) profile_new (StackStash *stash)
{ {
Info info; Profile *profile = g_new (Profile, 1);
info.profile = g_new (Profile, 1); profile->stash = stash;
info.profile->call_tree = NULL;
info.profile->nodes_by_object =
g_hash_table_new (direct_hash_no_null, g_direct_equal);
info.profile->size = 0;
/* profile objects */ return profile;
info.profile_objects = g_hash_table_new_full (g_str_hash, g_str_equal,
g_free, NULL);
stack_stash_foreach (stash, generate_object_table, &info);
stack_stash_foreach (stash, generate_call_tree, &info);
link_parents (info.profile->call_tree, NULL);
g_hash_table_foreach (info.profile->nodes_by_object, compute_object_total, NULL);
g_hash_table_destroy (info.profile_objects);
return info.profile;
} }
static void static void
@ -548,14 +291,14 @@ add_trace_to_tree (ProfileDescendant **tree, GList *trace, guint size)
for (list = trace; list != NULL; list = list->next) for (list = trace; list != NULL; list = list->next)
{ {
Node *node = list->data; StackNode *node = list->data;
ProfileDescendant *match = NULL; ProfileDescendant *match = NULL;
update(); update();
for (match = *tree; match != NULL; match = match->siblings) for (match = *tree; match != NULL; match = match->siblings)
{ {
if (match->object == node->object) if (match->name == node->address)
break; break;
} }
@ -568,7 +311,7 @@ add_trace_to_tree (ProfileDescendant **tree, GList *trace, guint size)
for (i = 0; i < seen_objects->len; ++i) for (i = 0; i < seen_objects->len; ++i)
{ {
ProfileDescendant *n = seen_objects->pdata[i]; ProfileDescendant *n = seen_objects->pdata[i];
if (n->object == node->object) if (n->name == node->address)
seen_tree_node = n; seen_tree_node = n;
} }
@ -596,7 +339,7 @@ add_trace_to_tree (ProfileDescendant **tree, GList *trace, guint size)
{ {
match = g_new (ProfileDescendant, 1); match = g_new (ProfileDescendant, 1);
match->object = node->object; match->name = node->address;
match->non_recursion = 0; match->non_recursion = 0;
match->total = 0; match->total = 0;
match->self = 0; match->self = 0;
@ -656,54 +399,42 @@ add_trace_to_tree (ProfileDescendant **tree, GList *trace, guint size)
} }
static void static void
node_list_leaves (Node *node, GList **leaves) add_leaf_to_tree (ProfileDescendant **tree, StackNode *leaf, StackNode *top)
{
Node *n;
if (node->self > 0)
*leaves = g_list_prepend (*leaves, node);
for (n = node->children; n != NULL; n = n->siblings)
node_list_leaves (n, leaves);
}
static void
add_leaf_to_tree (ProfileDescendant **tree, Node *leaf, Node *top)
{ {
GList *trace = NULL; GList *trace = NULL;
Node *node; StackNode *node;
for (node = leaf; node != top->parent; node = node->parent) for (node = leaf; node != top->parent; node = node->parent)
trace = g_list_prepend (trace, node); trace = g_list_prepend (trace, node);
add_trace_to_tree (tree, trace, leaf->self); add_trace_to_tree (tree, trace, leaf->size);
g_list_free (trace); g_list_free (trace);
} }
ProfileDescendant * ProfileDescendant *
profile_create_descendants (Profile *profile, ProfileObject *object) profile_create_descendants (Profile *profile,
char *object_name)
{ {
ProfileDescendant *tree = NULL; ProfileDescendant *tree = NULL;
Node *node;
node = g_hash_table_lookup (profile->nodes_by_object, object); StackNode *node = stack_stash_find_node (profile->stash, object_name);
while (node) while (node)
{ {
update();
if (node->toplevel) if (node->toplevel)
{ {
GList *leaves = NULL; GList *leaves = NULL;
GList *list; GList *list;
node_list_leaves (node, &leaves); stack_node_list_leaves (node, &leaves);
for (list = leaves; list != NULL; list = list->next) for (list = leaves; list != NULL; list = list->next)
add_leaf_to_tree (&tree, list->data, node); add_leaf_to_tree (&tree, list->data, node);
g_list_free (leaves); g_list_free (leaves);
} }
node = node->next; node = node->next;
} }
@ -722,10 +453,10 @@ profile_caller_new (void)
ProfileCaller * ProfileCaller *
profile_list_callers (Profile *profile, profile_list_callers (Profile *profile,
ProfileObject *callee) char *callee_name)
{ {
Node *callee_node; StackNode *callee_node;
Node *node; StackNode *node;
GHashTable *callers_by_object; GHashTable *callers_by_object;
GHashTable *seen_callers; GHashTable *seen_callers;
ProfileCaller *result = NULL; ProfileCaller *result = NULL;
@ -734,20 +465,21 @@ profile_list_callers (Profile *profile,
g_hash_table_new (g_direct_hash, g_direct_equal); g_hash_table_new (g_direct_hash, g_direct_equal);
seen_callers = g_hash_table_new (g_direct_hash, g_direct_equal); seen_callers = g_hash_table_new (g_direct_hash, g_direct_equal);
callee_node = g_hash_table_lookup (profile->nodes_by_object, callee); callee_node = stack_stash_find_node (profile->stash, callee_name);
for (node = callee_node; node; node = node->next) for (node = callee_node; node; node = node->next)
{ {
ProfileObject *object; char *object;
if (node->parent) if (node->parent)
object = node->parent->object; object = node->parent->address;
else else
object = NULL; object = NULL;
if (!g_hash_table_lookup (callers_by_object, object)) if (!g_hash_table_lookup (callers_by_object, object))
{ {
ProfileCaller *caller = profile_caller_new (); ProfileCaller *caller = profile_caller_new ();
caller->object = object; caller->name = object;
g_hash_table_insert (callers_by_object, object, caller); g_hash_table_insert (callers_by_object, object, caller);
caller->next = result; caller->next = result;
@ -757,14 +489,14 @@ profile_list_callers (Profile *profile,
for (node = callee_node; node != NULL; node = node->next) for (node = callee_node; node != NULL; node = node->next)
{ {
Node *top_caller; StackNode *top_caller;
Node *top_callee; StackNode *top_callee;
Node *n; StackNode *n;
ProfileCaller *caller; ProfileCaller *caller;
ProfileObject *object; char *object;
if (node->parent) if (node->parent)
object = node->parent->object; object = node->parent->address;
else else
object = NULL; object = NULL;
@ -775,8 +507,8 @@ profile_list_callers (Profile *profile,
top_callee = node; top_callee = node;
for (n = node; n && n->parent; n = n->parent) for (n = node; n && n->parent; n = n->parent)
{ {
if (n->object == node->object && if (n->address == node->address &&
n->parent->object == node->parent->object) n->parent->address == node->parent->address)
{ {
top_caller = n->parent; top_caller = n->parent;
top_callee = n; top_callee = n;
@ -785,13 +517,13 @@ profile_list_callers (Profile *profile,
if (!g_hash_table_lookup (seen_callers, top_caller)) if (!g_hash_table_lookup (seen_callers, top_caller))
{ {
caller->total += top_callee->total; caller->total += compute_total (top_callee);
g_hash_table_insert (seen_callers, top_caller, (void *)0x1); g_hash_table_insert (seen_callers, top_caller, (void *)0x1);
} }
if (node->self > 0) if (node->size > 0)
caller->self += node->self; caller->self += node->size;
} }
g_hash_table_destroy (seen_callers); g_hash_table_destroy (seen_callers);
@ -801,32 +533,10 @@ profile_list_callers (Profile *profile,
} }
static void
node_free (Node *node)
{
if (!node)
return;
node_free (node->siblings);
node_free (node->children);
g_free (node);
}
static void
free_object (gpointer key, gpointer value, gpointer data)
{
profile_object_free (key);
}
void void
profile_free (Profile *profile) profile_free (Profile *profile)
{ {
g_hash_table_foreach (profile->nodes_by_object, free_object, NULL); /* FIXME unref stash */
node_free (profile->call_tree);
g_hash_table_destroy (profile->nodes_by_object);
g_free (profile); g_free (profile);
} }
@ -853,12 +563,22 @@ profile_caller_free (ProfileCaller *caller)
} }
static void static void
build_object_list (gpointer key, gpointer value, gpointer data) build_object_list (StackNode *node, gpointer data)
{ {
ProfileObject *object = key;
GList **objects = data; GList **objects = data;
ProfileObject *obj;
*objects = g_list_prepend (*objects, object); obj = g_new (ProfileObject, 1);
obj->name = node->address;
obj->total = compute_total (node);
/* FIXME: this is incorrect. We need to sum all the node linked
* through node->next
*/
obj->self = node->size;
*objects = g_list_prepend (*objects, obj);
} }
GList * GList *
@ -866,7 +586,11 @@ profile_get_objects (Profile *profile)
{ {
GList *objects = NULL; GList *objects = NULL;
g_hash_table_foreach (profile->nodes_by_object, build_object_list, &objects); stack_stash_foreach_by_address (profile->stash, build_object_list, &objects);
/* FIXME: everybody still assumes that they don't have to free the
* objects in the list, but these days they do, and so we are leaking.
*/
return objects; return objects;
} }
@ -874,5 +598,5 @@ profile_get_objects (Profile *profile)
gint gint
profile_get_size (Profile *profile) profile_get_size (Profile *profile)
{ {
return profile->size; return compute_total (stack_stash_get_root (profile->stash));
} }

View File

@ -17,9 +17,10 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/ */
#ifndef PROFILE_H
#define PROFILE_H
#include <glib.h> #include <glib.h>
#include "binfile.h"
#include "process.h"
#include "stackstash.h" #include "stackstash.h"
typedef struct Profile Profile; typedef struct Profile Profile;
@ -37,7 +38,7 @@ struct ProfileObject
struct ProfileDescendant struct ProfileDescendant
{ {
ProfileObject * object; char * name;
guint self; guint self;
guint total; guint total;
guint non_recursion; guint non_recursion;
@ -51,7 +52,7 @@ struct ProfileDescendant
struct ProfileCaller struct ProfileCaller
{ {
ProfileObject * object; /* can be NULL */ char * name;
guint total; guint total;
guint self; guint self;
@ -63,9 +64,9 @@ void profile_free (Profile *profile);
gint profile_get_size (Profile *profile); gint profile_get_size (Profile *profile);
GList * profile_get_objects (Profile *profile); GList * profile_get_objects (Profile *profile);
ProfileDescendant *profile_create_descendants (Profile *prf, ProfileDescendant *profile_create_descendants (Profile *prf,
ProfileObject *object); char *object);
ProfileCaller * profile_list_callers (Profile *profile, ProfileCaller * profile_list_callers (Profile *profile,
ProfileObject *callee); char *object);
void profile_caller_free (ProfileCaller *caller); void profile_caller_free (ProfileCaller *caller);
void profile_descendant_free (ProfileDescendant *descendant); void profile_descendant_free (ProfileDescendant *descendant);
@ -74,3 +75,5 @@ gboolean profile_save (Profile *profile,
GError **err); GError **err);
Profile * profile_load (const char *filename, Profile * profile_load (const char *filename,
GError **err); GError **err);
#endif /* PROFILE_H */

22
sfile.h
View File

@ -23,6 +23,24 @@ typedef struct SFileOutput SFileOutput;
typedef guint SType; typedef guint SType;
#if 0
Serializer *serializer_new (const char *version);
void serializer_set_format (Serializer *serializer,
SerializerFormat *format);
SerializerFormat *serializer_make_list (Serializer *serializer,
const char *name,
SerializerFormat *contents);
SerializerFormat *serializer_make_record (Serializer *serializer,
const char *name,
SerializerFormat *contents1,
...);
SerializerFormat *serializer_make_integer (Serializer *serialiser,
const char *name);
SerializerFormat *serializer_make_pointer (Serializer *serialiser,
const char *name,
SerializerFormat *target_type);
#endif
/* A possibly better API/naming scheme /* A possibly better API/naming scheme
* *
* Serializer *serializer_new (SerializerFormat *format); * Serializer *serializer_new (SerializerFormat *format);
@ -65,6 +83,10 @@ typedef guint SType;
* different versions of the format, and they want to be able to sniff the * different versions of the format, and they want to be able to sniff the
* format + version * format + version
* *
* The version should be part of the format. There should be a
* const char *sfile_sniff (const filename);
* that will return NULL (+ error) if the file can't be parsed
*
*/ */
/* - Describing Types - */ /* - Describing Types - */

View File

@ -19,22 +19,10 @@
#include "stackstash.h" #include "stackstash.h"
typedef struct StackNode StackNode;
struct StackNode
{
StackNode * parent;
gpointer address;
StackNode * siblings;
StackNode * children;
StackNode * next; /* next leaf with the same pid */
int size;
};
struct StackStash struct StackStash
{ {
StackNode *root; StackNode *root;
GHashTable *leaves_by_process; GHashTable *nodes_by_data;
}; };
static StackNode * static StackNode *
@ -45,144 +33,115 @@ stack_node_new (void)
node->children = NULL; node->children = NULL;
node->address = NULL; node->address = NULL;
node->parent = NULL; node->parent = NULL;
node->next = NULL;
node->size = 0; node->size = 0;
node->next = NULL;
return node; return node;
} }
static void
stack_node_destroy (gpointer p)
{
StackNode *node = p;
if (node)
{
stack_node_destroy (node->siblings);
stack_node_destroy (node->children);
g_free (node);
}
}
/* Stach */ /* Stach */
StackStash * StackStash *
stack_stash_new (void) stack_stash_new (void)
{ {
StackStash *stash = g_new (StackStash, 1); StackStash *stash = g_new (StackStash, 1);
stash->leaves_by_process =
g_hash_table_new (g_direct_hash, g_direct_equal);
stash->root = NULL; stash->root = NULL;
stash->nodes_by_data = g_hash_table_new (g_direct_hash, g_direct_equal);
return stash; return stash;
} }
static StackNode * void
stack_node_add_trace (StackNode *node, decorate_node (StackStash *stash,
GList *bottom, StackNode *node)
gint size,
StackNode **leaf)
{ {
StackNode *match;
StackNode *n; StackNode *n;
gboolean toplevel = TRUE;
if (!bottom) /* FIXME: we will probably want to do this lazily,
{ * and more efficiently (only walk the tree once).
*leaf = NULL; */
return node;
}
if (!bottom->next) for (n = node->parent; n != NULL; n = n->parent)
{ {
/* A leaf must always be separate, so pids can if (n->address == node->address)
* point to them
*/
match = NULL;
}
else
{
for (match = node; match != NULL; match = match->siblings)
{ {
if (match->address == bottom->data) toplevel = FALSE;
break; break;
} }
} }
if (!match) node->toplevel = toplevel;
{
match = stack_node_new ();
match->address = bottom->data;
match->siblings = node;
node = match;
}
match->children = node->next = g_hash_table_lookup (
stack_node_add_trace (match->children, bottom->next, size, leaf); stash->nodes_by_data, node->address);
g_hash_table_insert (
for (n = match->children; n; n = n->siblings) stash->nodes_by_data, node->address, node);
n->parent = match;
if (!bottom->next)
{
match->size += size;
*leaf = match;
}
return node;
} }
void void
stack_stash_add_trace (StackStash *stash, stack_stash_add_trace (StackStash *stash,
Process *process, gulong *addrs,
gulong *addrs,
int n_addrs, int n_addrs,
int size) int size)
{ {
GList *trace; StackNode **location = &(stash->root);
StackNode *leaf; StackNode *parent = NULL;
int i; int i;
if (!n_addrs) if (!n_addrs)
return; return;
trace = NULL; for (i = n_addrs - 1; i >= 0; --i)
for (i = 0; i < n_addrs; ++i) {
trace = g_list_prepend (trace, GINT_TO_POINTER (addrs[i])); StackNode *match = NULL;
StackNode *n;
stash->root = stack_node_add_trace (stash->root, trace, size, &leaf); for (n = *location; n != NULL; n = n->siblings)
{
if (n->address == (gpointer)addrs[i])
{
match = n;
break;
}
}
leaf->next = g_hash_table_lookup ( if (!match)
stash->leaves_by_process, process); {
g_hash_table_insert ( match = stack_node_new ();
stash->leaves_by_process, process, leaf); match->address = (gpointer)addrs[i];
match->siblings = *location;
match->parent = parent;
*location = match;
g_list_free (trace); decorate_node (stash, match);
}
location = &(match->children);
parent = match;
}
parent->size += size;
} }
typedef struct CallbackInfo
{
StackFunction func;
gpointer data;
} CallbackInfo;
static void static void
do_callback (gpointer key, gpointer value, gpointer data) do_callback (StackNode *node,
GSList *trace,
StackFunction stack_func,
gpointer data)
{ {
CallbackInfo *info = data; GSList link;
Process *process = key;
StackNode *n;
StackNode *leaf = value;
while (leaf)
{
GSList *trace;
trace = NULL; if (!node)
for (n = leaf; n; n = n->parent) return;
trace = g_slist_prepend (trace, n->address);
info->func (process, trace, leaf->size, info->data); link.next = trace;
link.data = node->address;
g_slist_free (trace); do_callback (node->siblings, trace, stack_func, data);
do_callback (node->children, &link, stack_func, data);
leaf = leaf->next; if (node->size)
} stack_func (&link, node->size, data);
} }
void void
@ -190,11 +149,7 @@ stack_stash_foreach (StackStash *stash,
StackFunction stack_func, StackFunction stack_func,
gpointer data) gpointer data)
{ {
CallbackInfo info; do_callback (stash->root, NULL, stack_func, data);
info.func = stack_func;
info.data = data;
g_hash_table_foreach (stash->leaves_by_process, do_callback, &info);
} }
static void static void
@ -213,6 +168,90 @@ void
stack_stash_free (StackStash *stash) stack_stash_free (StackStash *stash)
{ {
stack_node_free (stash->root); stack_node_free (stash->root);
g_hash_table_destroy (stash->leaves_by_process); g_hash_table_destroy (stash->nodes_by_data);
g_free (stash); g_free (stash);
} }
StackNode *
stack_stash_find_node (StackStash *stash,
gpointer data)
{
g_return_val_if_fail (stash != NULL, NULL);
return g_hash_table_lookup (stash->nodes_by_data, data);
}
void
stack_node_list_leaves (StackNode *node,
GList **leaves)
{
StackNode *n;
if (node->size > 0)
*leaves = g_list_prepend (*leaves, node);
for (n = node->children; n != NULL; n = n->siblings)
stack_node_list_leaves (n, leaves);
}
typedef struct
{
StackNodeFunc func;
gpointer data;
} Info;
static void
do_foreach (gpointer key, gpointer value, gpointer data)
{
Info *info = data;
info->func (value, info->data);
}
void
stack_stash_foreach_by_address (StackStash *stash,
StackNodeFunc func,
gpointer data)
{
Info info;
info.func = func;
info.data = data;
g_hash_table_foreach (stash->nodes_by_data, do_foreach, &info);
}
StackNode *
stack_stash_get_root (StackStash *stash)
{
return stash->root;
}
static void
build_hash_table (StackNode *node,
StackStash *stash)
{
if (!node)
return;
build_hash_table (node->siblings, stash);
build_hash_table (node->children, stash);
node->next = g_hash_table_lookup (
stash->nodes_by_data, node->address);
g_hash_table_insert (
stash->nodes_by_data, node->address, node);
}
StackStash *
stack_stash_new_from_root (StackNode *root)
{
StackStash *stash = g_new (StackStash, 1);
stash->root = root;
stash->nodes_by_data = g_hash_table_new (g_direct_hash, g_direct_equal);
build_hash_table (stash->root, stash);
return stash;
}

View File

@ -21,25 +21,50 @@
#define STACK_STASH_H #define STACK_STASH_H
#include <glib.h> #include <glib.h>
#include "process.h"
typedef struct StackStash StackStash; typedef struct StackStash StackStash;
typedef void (* StackFunction) (Process *process, typedef struct StackNode StackNode;
GSList *trace,
struct StackNode
{
gpointer address;
int size;
StackNode * parent;
StackNode * siblings;
StackNode * children;
StackNode * next;
gboolean toplevel;
};
typedef void (* StackFunction) (GSList *trace,
gint size, gint size,
gpointer data); gpointer data);
/* Stach */ /* Stach */
StackStash *stack_stash_new (void); StackStash *stack_stash_new (void);
void stack_stash_add_trace (StackStash *stash, void stack_stash_add_trace (StackStash *stash,
Process *process,
gulong *addrs, gulong *addrs,
gint n_addrs, gint n_addrs,
int size); int size);
void stack_stash_foreach (StackStash *stash, void stack_stash_foreach (StackStash *stash,
StackFunction stack_func, StackFunction stack_func,
gpointer data); gpointer data);
StackNode *stack_stash_find_node (StackStash *stash,
gpointer address);
/* FIXME: should probably return a list */
void stack_node_list_leaves (StackNode *node,
GList **leaves);
typedef void (* StackNodeFunc) (StackNode *node,
gpointer data);
void stack_stash_foreach_by_address (StackStash *stash,
StackNodeFunc func,
gpointer data);
void stack_stash_free (StackStash *stash); void stack_stash_free (StackStash *stash);
StackNode *stack_stash_get_root (StackStash *stash);
StackStash *stack_stash_new_from_root (StackNode *root);
#endif #endif

View File

@ -32,63 +32,21 @@
#include "process.h" #include "process.h"
#include "watch.h" #include "watch.h"
#include "signal-handler.h" #include "signal-handler.h"
#include "collector.h"
typedef struct Application Application; typedef struct Application Application;
struct Application struct Application
{ {
int fd; Collector * collector;
StackStash *stack_stash;
char * outfile; char * outfile;
GMainLoop * main_loop; GMainLoop * main_loop;
}; };
void
read_trace (StackStash *stash,
SysprofStackTrace *trace,
GTimeVal now)
{
Process *process;
int i;
process = process_get_from_pid (trace->pid);
for (i = 0; i < trace->n_addresses; ++i)
{
process_ensure_map (process, trace->pid,
(gulong)trace->addresses[i]);
}
stack_stash_add_trace (
stash, process,
(gulong *)trace->addresses, trace->n_addresses, 1);
}
void
on_read (gpointer data)
{
Application *app = data;
SysprofStackTrace trace;
int bytesread;
GTimeVal now;
bytesread = read (app->fd, &trace, sizeof (trace));
g_get_current_time (&now);
if (bytesread < 0)
{
perror("read");
return;
}
if (bytesread > 0)
read_trace (app->stack_stash, &trace, now);
}
void void
dump_data (Application *app) dump_data (Application *app)
{ {
GError *err = NULL; GError *err = NULL;
Profile *profile = profile_new (app->stack_stash); Profile *profile = collector_create_profile (app->collector);
profile_save (profile, app->outfile, &err); profile_save (profile, app->outfile, &err);
@ -104,10 +62,11 @@ signal_handler (int signo, gpointer data)
{ {
Application *app = data; Application *app = data;
g_print ("signal %d caught: dumping data\n", signo);
dump_data (app); dump_data (app);
while (g_main_iteration (FALSE))
;
g_main_loop_quit (app->main_loop); g_main_loop_quit (app->main_loop);
} }
@ -167,16 +126,17 @@ main (int argc,
if (quit) if (quit)
return -1; return -1;
app->fd = fd; app->collector = collector_new (NULL, NULL);
app->outfile = g_strdup (argv[1]); app->outfile = g_strdup (argv[1]);
app->stack_stash = stack_stash_new ();
app->main_loop = g_main_loop_new (NULL, 0); app->main_loop = g_main_loop_new (NULL, 0);
/* FIXME: check the errors */
signal_set_handler (SIGTERM, signal_handler, app, NULL); signal_set_handler (SIGTERM, signal_handler, app, NULL);
signal_set_handler (SIGINT, signal_handler, app, NULL); signal_set_handler (SIGINT, signal_handler, app, NULL);
fd_add_watch (app->fd, app); /* FIXME: check the error */
fd_set_read_callback (app->fd, on_read); collector_start (app->collector, NULL);
g_main_loop_run (app->main_loop); g_main_loop_run (app->main_loop);
signal_unset_handler (SIGTERM); signal_unset_handler (SIGTERM);

291
sysprof.c
View File

@ -19,23 +19,14 @@
#include <config.h> #include <config.h>
#include <stdio.h>
#include <gtk/gtk.h> #include <gtk/gtk.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <glade/glade.h> #include <glade/glade.h>
#include <errno.h> #include <errno.h>
#include <glib/gprintf.h> #include <glib/gprintf.h>
#include <sys/wait.h>
#include <sys/types.h>
#include "binfile.h"
#include "watch.h"
#include "module/sysprof-module.h"
#include "stackstash.h"
#include "profile.h"
#include "treeviewutils.h" #include "treeviewutils.h"
#include "profile.h"
#include "collector.h"
/* FIXME - not10 */ /* FIXME - not10 */
#define _(a) a #define _(a) a
@ -53,9 +44,9 @@ typedef enum
struct Application struct Application
{ {
int input_fd; Collector * collector;
State state; State state;
StackStash * stash;
GtkWidget * main_window; GtkWidget * main_window;
GdkPixbuf * icon; GdkPixbuf * icon;
@ -82,8 +73,6 @@ struct Application
ProfileDescendant * descendants; ProfileDescendant * descendants;
ProfileCaller * callers; ProfileCaller * callers;
int n_samples;
int timeout_id; int timeout_id;
int generating_profile; int generating_profile;
@ -104,7 +93,6 @@ struct Application
* *
* Model/View/Controller is a possibility. * Model/View/Controller is a possibility.
*/ */
GTimeVal latest_reset;
}; };
static gboolean static gboolean
@ -112,16 +100,20 @@ show_samples_timeout (gpointer data)
{ {
Application *app = data; Application *app = data;
char *label; char *label;
int n_samples;
switch (app->state) switch (app->state)
{ {
case INITIAL: case INITIAL:
label = g_strdup ("Samples: 0"); n_samples = 0;
break; break;
case PROFILING: case PROFILING:
n_samples = collector_get_n_samples (app->collector);
break;
case DISPLAYING: case DISPLAYING:
label = g_strdup_printf ("Samples: %d", app->n_samples); n_samples = profile_get_size (app->profile);
break; break;
default: default:
@ -129,6 +121,8 @@ show_samples_timeout (gpointer data)
break; break;
} }
label = g_strdup_printf ("Samples: %d", n_samples);
gtk_label_set_label (GTK_LABEL (app->samples_label), label); gtk_label_set_label (GTK_LABEL (app->samples_label), label);
g_free (label); g_free (label);
@ -157,6 +151,8 @@ update_sensitivity (Application *app)
GtkWidget *active_radio_button; GtkWidget *active_radio_button;
gboolean has_samples;
switch (app->state) switch (app->state)
{ {
case INITIAL: case INITIAL:
@ -170,9 +166,11 @@ update_sensitivity (Application *app)
break; break;
case PROFILING: case PROFILING:
sensitive_profile_button = (app->n_samples > 0); has_samples = (collector_get_n_samples (app->collector) > 0);
sensitive_save_as_button = (app->n_samples > 0);
sensitive_reset_button = (app->n_samples > 0); sensitive_profile_button = has_samples;
sensitive_save_as_button = has_samples;
sensitive_reset_button = has_samples;
sensitive_start_button = TRUE; sensitive_start_button = TRUE;
sensitive_tree_views = FALSE; sensitive_tree_views = FALSE;
sensitive_samples_label = TRUE; sensitive_samples_label = TRUE;
@ -250,82 +248,6 @@ set_busy (GtkWidget *widget, gboolean busy)
gdk_flush (); gdk_flush ();
} }
static double
timeval_to_ms (const GTimeVal *timeval)
{
return (timeval->tv_sec * G_USEC_PER_SEC + timeval->tv_usec) / 1000.0;
}
static double
time_diff (const GTimeVal *first,
const GTimeVal *second)
{
double first_ms = timeval_to_ms (first);
double second_ms = timeval_to_ms (second);
return first_ms - second_ms;
}
#define RESET_DEAD_PERIOD 25
static void
on_read (gpointer data)
{
Application *app = data;
SysprofStackTrace trace;
GTimeVal now;
int rd;
rd = read (app->input_fd, &trace, sizeof (trace));
if (app->state != PROFILING)
return;
if (rd == -1 && errno == EWOULDBLOCK)
return;
g_get_current_time (&now);
/* After a reset we ignore samples for a short period so that
* a reset will actually cause 'samples' to become 0
*/
if (time_diff (&now, &app->latest_reset) < RESET_DEAD_PERIOD)
return;
#if 0
int i;
g_print ("pid: %d\n", trace.pid);
for (i=0; i < trace.n_addresses; ++i)
g_print ("rd: %08x\n", trace.addresses[i]);
g_print ("-=-\n");
#endif
if (rd > 0 && !app->generating_profile && trace.n_addresses)
{
Process *process = process_get_from_pid (trace.pid);
int i;
/* char *filename = NULL; */
/* if (*trace.filename) */
/* filename = trace.filename; */
for (i = 0; i < trace.n_addresses; ++i)
{
process_ensure_map (process, trace.pid,
(gulong)trace.addresses[i]);
}
g_assert (!app->generating_profile);
stack_stash_add_trace (
app->stash, process,
(gulong *)trace.addresses, trace.n_addresses, 1);
app->n_samples++;
}
update_sensitivity (app);
}
static void static void
set_application_title (Application *app, set_application_title (Application *app,
const char * name) const char * name)
@ -349,7 +271,7 @@ set_application_title (Application *app,
else else
{ {
gtk_window_set_title (GTK_WINDOW (app->main_window), gtk_window_set_title (GTK_WINDOW (app->main_window),
"System Profiler"); "System Collector");
} }
} }
@ -366,45 +288,12 @@ delete_data (Application *app)
gtk_tree_view_set_model (GTK_TREE_VIEW (app->descendants_view), NULL); gtk_tree_view_set_model (GTK_TREE_VIEW (app->descendants_view), NULL);
} }
if (app->stash) collector_reset (app->collector);
stack_stash_free (app->stash);
app->stash = stack_stash_new ();
process_flush_caches ();
app->n_samples = 0;
queue_show_samples (app); queue_show_samples (app);
app->profile_from_file = FALSE; app->profile_from_file = FALSE;
set_application_title (app, NULL); set_application_title (app, NULL);
g_get_current_time (&app->latest_reset);
}
static void
empty_file_descriptor (Application *app)
{
int rd;
SysprofStackTrace trace;
do
{
rd = read (app->input_fd, &trace, sizeof (trace));
} while (rd != -1); /* until EWOULDBLOCK */
}
static gboolean
start_profiling (gpointer data)
{
Application *app = data;
app->state = PROFILING;
update_sensitivity (app);
/* Make sure samples generated between 'start clicked' and now
* are deleted
*/
empty_file_descriptor (app);
return FALSE;
} }
static void static void
@ -432,27 +321,6 @@ sorry (GtkWidget *parent_window,
gtk_widget_destroy (dialog); gtk_widget_destroy (dialog);
} }
static gboolean
load_module (void)
{
int exit_status = -1;
char *dummy1, *dummy2;
if (g_spawn_command_line_sync ("/sbin/modprobe sysprof-module",
&dummy1, &dummy2,
&exit_status,
NULL))
{
if (WIFEXITED (exit_status))
exit_status = WEXITSTATUS (exit_status);
g_free (dummy1);
g_free (dummy2);
}
return (exit_status == 0);
}
static void static void
on_menu_item_activated (GtkWidget *menu_item, GtkWidget *tool_button) on_menu_item_activated (GtkWidget *menu_item, GtkWidget *tool_button)
{ {
@ -471,41 +339,25 @@ on_start_toggled (GtkWidget *widget, gpointer data)
GTK_TOGGLE_TOOL_BUTTON (app->start_button))) GTK_TOGGLE_TOOL_BUTTON (app->start_button)))
return; return;
if (app->input_fd == -1)
{
int fd;
fd = open ("/proc/sysprof-trace", O_RDONLY);
if (fd < 0)
{
load_module();
fd = open ("/proc/sysprof-trace", O_RDONLY);
if (fd < 0)
{
sorry (app->main_window,
"Can't open /proc/sysprof-trace. You need to insert\n"
"the sysprof kernel module. Run\n"
"\n"
" modprobe sysprof-module\n"
"\n"
"as root.");
update_sensitivity (app);
return;
}
}
app->input_fd = fd;
fd_add_watch (app->input_fd, app);
}
fd_set_read_callback (app->input_fd, on_read);
delete_data (app); delete_data (app);
g_idle_add_full (G_PRIORITY_LOW, start_profiling, app, NULL); /* FIXME: get the real error message */
if (!collector_start (app->collector, NULL))
{
sorry (app->main_window,
"Can't open /proc/sysprof-trace. You need to insert\n"
"the sysprof kernel module. Run\n"
"\n"
" modprobe sysprof-module\n"
"\n"
"as root.");
return;
}
app->state = PROFILING;
update_sensitivity (app);
} }
enum enum
@ -533,13 +385,13 @@ enum
DESCENDANTS_OBJECT DESCENDANTS_OBJECT
}; };
static ProfileObject * static char *
get_current_object (Application *app) get_current_object (Application *app)
{ {
GtkTreeSelection *selection; GtkTreeSelection *selection;
GtkTreeModel *model; GtkTreeModel *model;
GtkTreeIter selected; GtkTreeIter selected;
ProfileObject *object; char *object;
selection = gtk_tree_view_get_selection (app->object_view); selection = gtk_tree_view_get_selection (app->object_view);
@ -587,7 +439,7 @@ fill_main_list (Application *app)
OBJECT_NAME, object->name, OBJECT_NAME, object->name,
OBJECT_SELF, 100.0 * object->self / profile_size, OBJECT_SELF, 100.0 * object->self / profile_size,
OBJECT_TOTAL, 100.0 * object->total / profile_size, OBJECT_TOTAL, 100.0 * object->total / profile_size,
OBJECT_OBJECT, object, OBJECT_OBJECT, object->name,
-1); -1);
} }
g_list_free (objects); g_list_free (objects);
@ -627,11 +479,11 @@ add_node (GtkTreeStore *store,
gtk_tree_store_insert (store, &iter, (GtkTreeIter *)parent, 0); gtk_tree_store_insert (store, &iter, (GtkTreeIter *)parent, 0);
gtk_tree_store_set (store, &iter, gtk_tree_store_set (store, &iter,
DESCENDANTS_NAME, node->object->name, DESCENDANTS_NAME, node->name,
DESCENDANTS_SELF, 100 * (node->self)/(double)size, DESCENDANTS_SELF, 100 * (node->self)/(double)size,
DESCENDANTS_NON_RECURSE, 100 * (node->non_recursion)/(double)size, DESCENDANTS_NON_RECURSE, 100 * (node->non_recursion)/(double)size,
DESCENDANTS_TOTAL, 100 * (node->total)/(double)size, DESCENDANTS_TOTAL, 100 * (node->total)/(double)size,
DESCENDANTS_OBJECT, node->object, DESCENDANTS_OBJECT, node->name,
-1); -1);
add_node (store, size, parent, node->siblings); add_node (store, size, parent, node->siblings);
@ -662,7 +514,7 @@ fill_descendants_tree (Application *app)
if (app->profile) if (app->profile)
{ {
ProfileObject *object = get_current_object (app); char *object = get_current_object (app);
if (object) if (object)
{ {
app->descendants = app->descendants =
@ -702,8 +554,8 @@ add_callers (GtkListStore *list_store,
GtkTreeIter iter; GtkTreeIter iter;
double profile_size = profile_get_size (profile); double profile_size = profile_get_size (profile);
if (callers->object) if (callers->name)
name = callers->object->name; name = callers->name;
else else
name = "<spontaneous>"; name = "<spontaneous>";
@ -713,7 +565,7 @@ add_callers (GtkListStore *list_store,
CALLERS_NAME, name, CALLERS_NAME, name,
CALLERS_SELF, 100.0 * callers->self / profile_size, CALLERS_SELF, 100.0 * callers->self / profile_size,
CALLERS_TOTAL, 100.0 * callers->total / profile_size, CALLERS_TOTAL, 100.0 * callers->total / profile_size,
CALLERS_OBJECT, callers->object, CALLERS_OBJECT, callers->name,
-1); -1);
callers = callers->next; callers = callers->next;
@ -743,7 +595,7 @@ fill_callers_list (Application *app)
if (app->profile) if (app->profile)
{ {
ProfileObject *object = get_current_object (app); char *object = get_current_object (app);
if (object) if (object)
{ {
app->callers = profile_list_callers (app->profile, object); app->callers = profile_list_callers (app->profile, object);
@ -784,7 +636,7 @@ ensure_profile (Application *app)
if (app->profile) if (app->profile)
return; return;
app->profile = profile_new (app->stash); app->profile = collector_create_profile (app->collector);
fill_lists (app); fill_lists (app);
@ -956,8 +808,6 @@ set_loaded_profile (Application *app,
app->state = DISPLAYING; app->state = DISPLAYING;
app->n_samples = profile_get_size (profile);
app->profile = profile; app->profile = profile;
app->profile_from_file = TRUE; app->profile_from_file = TRUE;
@ -1041,8 +891,14 @@ on_open_clicked (gpointer widget,
} }
static void static void
on_delete (GtkWidget *window) on_delete (GtkWidget *window,
Application *app)
{ {
/* Workaround for http://bugzilla.gnome.org/show_bug.cgi?id=317775
*/
while (gtk_main_iteration ())
;
gtk_main_quit (); gtk_main_quit ();
} }
@ -1123,6 +979,7 @@ expand_descendants_tree (Application *app)
path = gtk_tree_path_copy (path); path = gtk_tree_path_copy (path);
gtk_tree_path_next (path); gtk_tree_path_next (path);
} }
gtk_tree_path_free (path); gtk_tree_path_free (path);
} }
} }
@ -1158,7 +1015,7 @@ on_object_selection_changed (GtkTreeSelection *selection,
static void static void
really_goto_object (Application *app, really_goto_object (Application *app,
ProfileObject *object) char *object)
{ {
GtkTreeModel *profile_objects; GtkTreeModel *profile_objects;
GtkTreeIter iter; GtkTreeIter iter;
@ -1170,13 +1027,13 @@ really_goto_object (Application *app,
{ {
do do
{ {
ProfileObject *profile_object; char *list_object;
gtk_tree_model_get (profile_objects, &iter, gtk_tree_model_get (profile_objects, &iter,
OBJECT_OBJECT, &profile_object, OBJECT_OBJECT, &list_object,
-1); -1);
if (profile_object == object) if (list_object == object)
{ {
found = TRUE; found = TRUE;
break; break;
@ -1202,7 +1059,7 @@ goto_object (Application *app,
{ {
GtkTreeIter iter; GtkTreeIter iter;
GtkTreeModel *model = gtk_tree_view_get_model (tree_view); GtkTreeModel *model = gtk_tree_view_get_model (tree_view);
ProfileObject *object; char *object;
if (!gtk_tree_model_get_iter (model, &iter, path)) if (!gtk_tree_model_get_iter (model, &iter, path))
return; return;
@ -1213,7 +1070,6 @@ goto_object (Application *app,
return; return;
really_goto_object (app, object); really_goto_object (app, object);
} }
static void static void
@ -1424,17 +1280,23 @@ build_gui (Application *app)
return TRUE; return TRUE;
} }
static void
on_new_sample (gpointer data)
{
Application *app = data;
if (app->state == PROFILING)
update_sensitivity (app);
}
static Application * static Application *
application_new (void) application_new (void)
{ {
Application *app = g_new0 (Application, 1); Application *app = g_new0 (Application, 1);
app->stash = stack_stash_new (); app->collector = collector_new (on_new_sample, app);
app->input_fd = -1;
app->state = INITIAL; app->state = INITIAL;
g_get_current_time (&app->latest_reset);
return app; return app;
} }
@ -1481,11 +1343,6 @@ main (int argc, char **argv)
app = application_new (); app = application_new ();
#if 0
nice (-19);
g_timeout_add (10, on_timeout, app);
#endif
if (!build_gui (app)) if (!build_gui (app))
return -1; return -1;