Merge stackstash-reorg branch into HEAD
authorSoren Sandmann <sandmann@redhat.com>
Sun, 30 Oct 2005 20:14:31 +0000 (20:14 +0000)
committerSøren Sandmann Pedersen <ssp@src.gnome.org>
Sun, 30 Oct 2005 20:14:31 +0000 (20:14 +0000)
2005-10-30  Soren Sandmann <sandmann@redhat.com>

* Merge stackstash-reorg branch into HEAD

15 files changed:
ChangeLog
Makefile.am
NEWS
TODO
collector.c [new file with mode: 0644]
collector.h [new file with mode: 0644]
process.c
process.h
profile.c
profile.h
sfile.h
stackstash.c
stackstash.h
sysprof-text.c
sysprof.c

index eab45c4..5614632 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -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>
 
        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. 
 
+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>
 
        * 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
        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>
 
        * module/sysprof-module.c: Delete lots of commented-out code.
index 975d2bb..288ee21 100644 (file)
@@ -2,67 +2,54 @@ SUBDIRS = $(MODULE_SUBDIR)
 DIST_SUBDIRS = module
 
 bin_PROGRAMS = sysprof sysprof-text
-pkgdata_DATA = sysprof.glade sysprof-icon.png
 
-sysprof_SOURCES =              \
-       binfile.h               \
-       binfile.c               \
-       process.h               \
-       process.c               \
-       profile.h               \
-       profile.c               \
-       sfile.h                 \
-       sfile.c                 \
-       stackstash.h            \
-       stackstash.c            \
-       module/sysprof-module.h \
-       sysprof.c               \
-       treeviewutils.h         \
-       treeviewutils.c         \
-       watch.h                 \
+SYSPROF_CORE =                                 \
+       binfile.h                               \
+       binfile.c                               \
+       collector.c                             \
+       collector.h                             \
+       process.h                               \
+       process.c                               \
+       profile.h                               \
+       profile.c                               \
+       sfile.h                                 \
+       sfile.c                                 \
+       stackstash.h                            \
+       stackstash.c                            \
+       module/sysprof-module.h                 \
+       watch.h                                 \
        watch.c
 
-sysprof_text_SOURCES =         \
-       binfile.h               \
-       binfile.c               \
-       process.h               \
-       process.c               \
-       profile.h               \
-       profile.c               \
-       sfile.h                 \
-       sfile.c                 \
-       stackstash.h            \
-       stackstash.c            \
-       module/sysprof-module.h \
-       signal-handler.h        \
-       signal-handler.c        \
-       sysprof-text.c          \
-       treeviewutils.h         \
-       treeviewutils.c         \
-       watch.h                 \
-       watch.c
+sysprof_SOURCES =                              \
+       $(SYSPROF_CORE)                         \
+       treeviewutils.h                         \
+       treeviewutils.c                         \
+       sysprof.c
+
+sysprof_text_SOURCES =                         \
+       $(SYSPROF_CORE)                         \
+       signal-handler.h                        \
+       signal-handler.c                        \
+       sysprof-text.c
 
 sysprof_LDADD = $(DEP_LIBS)
 
 sysprof_text_LDADD = $(DEP_LIBS)
 
-INCLUDES =                                        \
-       $(DEP_CFLAGS)                              \
-       -DDATADIR=\"$(pkgdatadir)\"                \
-       -DPIXMAPDIR=\"$(datadir)/pixmaps\"
+pixmapsdir = $(datadir)/pixmaps
 
-#      memprof.desktop         
-#      memprof.spec.in
+INCLUDES =                                     \
+       $(DEP_CFLAGS)                           \
+       -DDATADIR=\"$(pkgdatadir)\"             \
+       -DPIXMAPDIR=\"$(pixmapsdir)\"
 
-EXTRA_DIST =                   \
-       sysprof.glade           \
-       sysprof-icon.png        \
-       module/sysprof-module.c \
-       module/sysprof-module.h \
+EXTRA_DIST =                                   \
+       module/sysprof-module.c                 \
+       module/sysprof-module.h                 \
        module/Makefile
 
-pixmapsdir = $(datadir)/pixmaps
-pixmaps_DATA = sysprof-icon.png
+dist_pkgdata_DATA = sysprof.glade
+dist_pixmaps_DATA = sysprof-icon.png
 
 insert-module:
        modprobe -r sysprof-module
diff --git a/NEWS b/NEWS
index e69de29..a97e151 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -0,0 +1,3 @@
+- New 'everything' object
+- New commandline version 
+- Assign time spent in kernel to the user process responsible
diff --git a/TODO b/TODO
index 4ac8763..cf8e6b6 100644 (file)
--- a/TODO
+++ b/TODO
@@ -11,9 +11,9 @@ Before 1.0.1:
 
      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
   'trace "current"' code. See this mail:
@@ -25,6 +25,54 @@ Before 1.2:
 
   (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.
        - In general clean sfile.[ch] up a little:
        - 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?))
        - 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 
        (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
        - Possibly a special "view details" mode, assuming that 
          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
   listing all ancestors.
 
@@ -452,12 +468,64 @@ Later:
 
 DONE:
 
+* Crash reported by Rudi Chiarito with n_addrs == 0.
+
 * Find out what distributions it actually works on
    (ask for sucess/failure-stories in 1.0 releases)
 
 * Add note in README about Ubuntu and Debian -dbg packages and how to get
   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:
 
    - Update version numbers in source
diff --git a/collector.c b/collector.c
new file mode 100644 (file)
index 0000000..75fa816
--- /dev/null
@@ -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);
+}
diff --git a/collector.h b/collector.h
new file mode 100644 (file)
index 0000000..6379b10
--- /dev/null
@@ -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);
index ff14b47..657ecce 100644 (file)
--- a/process.c
+++ b/process.c
@@ -56,6 +56,8 @@ struct Process
     GList *bad_pages;
 
     int pid;
+
+    Symbol undefined;
 };
 
 static void
@@ -136,7 +138,7 @@ create_process (const char *cmdline, int pid)
 {
     Process *p;
     
-    p = g_new (Process, 1);
+    p = g_new0 (Process, 1);
 
     if (*cmdline != '\0')
        p->cmdline = g_strdup_printf ("[%s]", cmdline);
@@ -146,6 +148,8 @@ create_process (const char *cmdline, int pid)
     p->bad_pages = NULL;
     p->maps = NULL;
     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_cmdline, cmdline));
@@ -340,7 +344,6 @@ process_get_from_pid (int pid)
 const Symbol *
 process_lookup_symbol (Process *process, gulong address)
 {
-    static Symbol undefined;
     const Symbol *result;
     static Symbol kernel;
     Map *map = process_locate_map (process, address);
@@ -349,18 +352,20 @@ process_lookup_symbol (Process *process, gulong address)
 
     if (address == 0x1)
     {
-       kernel.name = "in kernel";
+       kernel.name = "In kernel";
        kernel.address = 0x00001337;
        return &kernel;
     }
     else if (!map)
     {
-       if (undefined.name)
-           g_free (undefined.name);
-       undefined.name = g_strdup_printf ("??? %s", process->cmdline);
-       undefined.address = 0xBABE0001;
+       if (!process->undefined.name)
+       {
+           process->undefined.name =
+               g_strdup_printf ("(??? %s)", process->cmdline);
+           process->undefined.address = 0xBABE0001;
+       }
 
-       return &undefined;
+       return &process->undefined;
     }
     
     address -= map->start;
@@ -388,38 +393,3 @@ process_get_cmdline (Process *process)
 {
     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;
-}
index 6ad1eba..d4ff4d5 100644 (file)
--- a/process.h
+++ b/process.h
@@ -24,6 +24,7 @@
 #ifndef PROCESS_H
 #define PROCESS_H
 
+#include <glib.h>
 #include "binfile.h"
 
 typedef struct Process Process;
@@ -41,9 +42,11 @@ typedef struct Process Process;
  * To flush the pid cache, call process_flush_caches().
  * 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);
 void          process_ensure_map                  (Process    *process,
                                                   int         pid,
index f3dd0e9..42aca18 100644 (file)
--- a/profile.c
+++ b/profile.c
@@ -38,37 +38,9 @@ update()
 #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
 {
-    gint               size;
-    Node *             call_tree;
-    
-    /* This table is really a cache. We can build it from the call_tree */
-    GHashTable *       nodes_by_object;
+    StackStash *       stash;
 };
 
 static SFormat *
@@ -105,34 +77,52 @@ create_format (void)
            NULL));
 }
 
-static void
-add_object (gpointer key, gpointer value, gpointer data)
+static int
+sum_children (StackNode *node)
+{
+    int total;
+    StackNode *child;
+    
+    /* FIXME: this is pretty inefficient. Instead perhaps
+     * maintain or compute it in the stackstash
+     */
+    total = node->size;
+
+    for (child = node->children; child != NULL; child = child->siblings)
+       total += sum_children (child);
+
+    return total;
+}
+
+static int
+compute_total (StackNode *node)
 {
-    SFileOutput *output = data;
-    ProfileObject *object = key;
+    StackNode *n;
+    int total = 0;
 
-    sfile_begin_add_record (output, "object");
+    for (n = node; n != NULL; n = n->next)
+    {
+       if (n->toplevel)
+           total += sum_children (n);
+    }
 
-    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);
+    return total;
 }
 
 static void
-serialize_call_tree (Node *node, SFileOutput *output)
+serialize_call_tree (StackNode *node,
+                    SFileOutput *output)
 {
     if (!node)
        return;
     
     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, "children", node->children);
     sfile_add_pointer (output, "parent", node->parent);
-    sfile_add_integer (output, "total", node->total);
-    sfile_add_integer (output, "self", node->self);
+    sfile_add_integer (output, "total", compute_total (node));
+    sfile_add_integer (output, "self", node->size);
     sfile_add_integer (output, "toplevel", node->toplevel);
     sfile_end_add (output, "node", node);
 
@@ -146,21 +136,40 @@ profile_save (Profile              *profile,
              GError            **err)
 {
     gboolean result;
+
+    GList *profile_objects;
+    GList *list;
     
     SFormat *format = create_format ();
     SFileOutput *output = sfile_output_new (format);
 
     sfile_begin_add_record (output, "profile");
 
-    sfile_add_integer (output, "size", profile->size);
-    sfile_add_pointer (output, "call_tree", profile->call_tree);
+    sfile_add_integer (output, "size", profile_get_size (profile));
+    sfile_add_pointer (output, "call_tree",
+                      stack_stash_get_root (profile->stash));
     
+    profile_objects = profile_get_objects (profile);
     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_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, "profile", NULL);
@@ -173,6 +182,7 @@ profile_save (Profile                *profile,
     return result;
 }
 
+#if 0
 static void
 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->children, table);
 }
+#endif
 
 Profile *
 profile_load (const char *filename, GError **err)
@@ -197,52 +208,49 @@ profile_load (const char *filename, GError **err)
     SFileInput *input;
     Profile *profile;
     int n, i;
+    StackNode *root;
     
     format = create_format ();
     input = sfile_load (filename, format, err);
 
     if (!input)
        return NULL;
-    
+   
     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_get_integer (input, "size", &profile->size);
-    sfile_get_pointer (input, "call_tree", (void **)&profile->call_tree);
+    sfile_get_integer (input, "size", NULL);
+    sfile_get_pointer (input, "call_tree", (gpointer *)&root);
 
     n = sfile_begin_get_list (input, "objects");
     for (i = 0; i < n; ++i)
     {
-       ProfileObject *obj = g_new (ProfileObject, 1);
+       char *string;
        
        sfile_begin_get_record (input, "object");
 
-       sfile_get_string (input, "name", &obj->name);
-       sfile_get_integer (input, "total", (gint32 *)&obj->total);
-       sfile_get_integer (input, "self", (gint32 *)&obj->self);
+       sfile_get_string (input, "name", &string);
+       sfile_get_integer (input, "total", NULL);
+       sfile_get_integer (input, "self", NULL);
        
-       sfile_end_get (input, "object", obj);
+       sfile_end_get (input, "object", string);
     }
     sfile_end_get (input, "objects", NULL);
 
-    profile->call_tree = NULL;
     n = sfile_begin_get_list (input, "nodes");
     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_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, "children", (gpointer *)&node->children);
        sfile_get_pointer (input, "parent", (gpointer *)&node->parent);
-       sfile_get_integer (input, "total", (gint32 *)&node->total);
-       sfile_get_integer (input, "self", (gint32 *)&node->self);
+       sfile_get_integer (input, "total", NULL);
+       sfile_get_integer (input, "self", (gint32 *)&node->size);
        sfile_get_integer (input, "toplevel", &node->toplevel);
        
        sfile_end_get (input, "node", node);
@@ -254,285 +262,20 @@ profile_load (const char *filename, GError **err)
     
     sformat_free (format);
     sfile_input_free (input);
-    
-    make_hash_table (profile->call_tree, profile->nodes_by_object);
-
-    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);
+    profile->stash = stack_stash_new_from_root (root);
     
-    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;
-    }
+    return profile;
 }
 
 Profile *
 profile_new (StackStash *stash)
 {
-    Info info;
-
-    info.profile = g_new (Profile, 1);
-    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 *profile = g_new (Profile, 1);
 
-    /* profile objects */
-    info.profile_objects = g_hash_table_new_full (g_str_hash, g_str_equal,
-                                                 g_free, NULL);
+    profile->stash = stash;
 
-    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;
+    return profile;
 }
 
 static void
@@ -548,14 +291,14 @@ add_trace_to_tree (ProfileDescendant **tree, GList *trace, guint size)
     
     for (list = trace; list != NULL; list = list->next)
     {
-       Node *node = list->data;
+       StackNode *node = list->data;
        ProfileDescendant *match = NULL;
        
        update();
        
        for (match = *tree; match != NULL; match = match->siblings)
        {
-           if (match->object == node->object)
+           if (match->name == node->address)
                break;
        }
        
@@ -568,7 +311,7 @@ add_trace_to_tree (ProfileDescendant **tree, GList *trace, guint size)
            for (i = 0; i < seen_objects->len; ++i)
            {
                ProfileDescendant *n = seen_objects->pdata[i];
-               if (n->object == node->object)
+               if (n->name == node->address)
                    seen_tree_node = n;
            }
                    
@@ -596,7 +339,7 @@ add_trace_to_tree (ProfileDescendant **tree, GList *trace, guint size)
        {
            match = g_new (ProfileDescendant, 1);
            
-           match->object = node->object;
+           match->name = node->address;
            match->non_recursion = 0;
            match->total = 0;
            match->self = 0;
@@ -656,57 +399,45 @@ add_trace_to_tree (ProfileDescendant **tree, GList *trace, guint size)
 }
 
 static void
-node_list_leaves (Node *node, GList **leaves)
-{
-    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)
+add_leaf_to_tree (ProfileDescendant **tree, StackNode *leaf, StackNode *top)
 {
     GList *trace = NULL;
-    Node *node;
+    StackNode *node;
     
     for (node = leaf; node != top->parent; node = node->parent)
        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);
 }
 
 ProfileDescendant *
-profile_create_descendants (Profile *profile, ProfileObject *object)
+profile_create_descendants (Profile *profile,
+                           char *object_name)
 {
     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)
     {
-       update();
        if (node->toplevel)
        {
            GList *leaves = NULL;
            GList *list;
-           
-           node_list_leaves (node, &leaves);
-           
+
+           stack_node_list_leaves (node, &leaves);
+
            for (list = leaves; list != NULL; list = list->next)
                add_leaf_to_tree (&tree, list->data, node);
-           
+
            g_list_free (leaves);
        }
+       
        node = node->next;
     }
-    
+
     return tree;
 }
 
@@ -722,32 +453,33 @@ profile_caller_new (void)
 
 ProfileCaller *
 profile_list_callers (Profile       *profile,
-                     ProfileObject *callee)
+                     char          *callee_name)
 {
-    Node *callee_node;
-    Node *node;
+    StackNode *callee_node;
+    StackNode *node;
     GHashTable *callers_by_object;
     GHashTable *seen_callers;
     ProfileCaller *result = NULL;
-    
+
     callers_by_object =
        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)
     {
-       ProfileObject *object;
+       char *object;
+       
        if (node->parent)
-           object = node->parent->object;
+           object = node->parent->address;
        else
            object = NULL;
        
        if (!g_hash_table_lookup (callers_by_object, object))
        {
            ProfileCaller *caller = profile_caller_new ();
-           caller->object = object;
+           caller->name = object;
            g_hash_table_insert (callers_by_object, object, caller);
            
            caller->next = result;
@@ -757,14 +489,14 @@ profile_list_callers (Profile       *profile,
     
     for (node = callee_node; node != NULL; node = node->next)
     {
-       Node *top_caller;
-       Node *top_callee;
-       Node *n;
+       StackNode *top_caller;
+       StackNode *top_callee;
+       StackNode *n;
        ProfileCaller *caller;
-       ProfileObject *object;
+       char *object;
        
        if (node->parent)
-           object = node->parent->object;
+           object = node->parent->address;
        else
            object = NULL;
        
@@ -775,8 +507,8 @@ profile_list_callers (Profile       *profile,
        top_callee = node;
        for (n = node; n && n->parent; n = n->parent)
        {
-           if (n->object == node->object                  &&
-               n->parent->object == node->parent->object)
+           if (n->address == node->address                &&
+               n->parent->address == node->parent->address)
            {
                top_caller = n->parent;
                top_callee = n;
@@ -785,13 +517,13 @@ profile_list_callers (Profile       *profile,
        
        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);
        }
        
-       if (node->self > 0)
-           caller->self += node->self;
+       if (node->size > 0)
+           caller->self += node->size;
     }
     
     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
 profile_free (Profile *profile)
 {
-    g_hash_table_foreach (profile->nodes_by_object, free_object, NULL);
-    
-    node_free (profile->call_tree);
-    
-    g_hash_table_destroy (profile->nodes_by_object);
-    
+    /* FIXME unref stash */
     g_free (profile);
 }
 
@@ -853,20 +563,34 @@ profile_caller_free (ProfileCaller *caller)
 }
 
 static void
-build_object_list (gpointer key, gpointer value, gpointer data)
+build_object_list (StackNode *node, gpointer data)
 {
-    ProfileObject *object = key;
     GList **objects = data;
-    
-    *objects = g_list_prepend (*objects, object);
+    ProfileObject *obj;
+
+    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 *
 profile_get_objects (Profile *profile)
 {
     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;
 }
@@ -874,5 +598,5 @@ profile_get_objects (Profile *profile)
 gint
 profile_get_size (Profile *profile)
 {
-    return profile->size;
+    return compute_total (stack_stash_get_root (profile->stash));
 }
index edc1c45..7f6cd3c 100644 (file)
--- a/profile.h
+++ b/profile.h
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  */
 
+#ifndef PROFILE_H
+#define PROFILE_H
+
 #include <glib.h>
-#include "binfile.h"
-#include "process.h"
 #include "stackstash.h"
 
 typedef struct Profile Profile;
@@ -37,7 +38,7 @@ struct ProfileObject
 
 struct ProfileDescendant
 {
-    ProfileObject *    object;
+    char *             name;
     guint              self;
     guint              total;
     guint              non_recursion;
@@ -51,7 +52,7 @@ struct ProfileDescendant
 
 struct ProfileCaller
 {
-    ProfileObject *    object;         /* can be NULL */
+    char *             name;
     guint              total;
     guint              self;
 
@@ -63,9 +64,9 @@ void               profile_free               (Profile           *profile);
 gint              profile_get_size           (Profile           *profile);
 GList *                   profile_get_objects        (Profile           *profile);
 ProfileDescendant *profile_create_descendants (Profile           *prf,
-                                              ProfileObject     *object);
+                                              char              *object);
 ProfileCaller *           profile_list_callers       (Profile           *profile,
-                                              ProfileObject     *callee);
+                                              char              *object);
 void              profile_caller_free        (ProfileCaller     *caller);
 void               profile_descendant_free    (ProfileDescendant *descendant);
 
@@ -74,3 +75,5 @@ gboolean         profile_save               (Profile           *profile,
                                               GError           **err);
 Profile *         profile_load               (const char        *filename,
                                               GError           **err);
+
+#endif /* PROFILE_H */
diff --git a/sfile.h b/sfile.h
index 3ab0757..00f0a0c 100644 (file)
--- a/sfile.h
+++ b/sfile.h
@@ -23,6 +23,24 @@ typedef struct SFileOutput SFileOutput;
 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
  *
  * 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
  * 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 - */
index 9f66f15..b717bfc 100644 (file)
 
 #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
 {
     StackNode  *root;
-    GHashTable *leaves_by_process;
+    GHashTable *nodes_by_data;
 };
 
 static StackNode *
@@ -45,144 +33,115 @@ stack_node_new (void)
     node->children = NULL;
     node->address = NULL;
     node->parent = NULL;
-    node->next = NULL;
     node->size = 0;
+    node->next = NULL;
     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 */
 StackStash *
 stack_stash_new       (void)
 {
     StackStash *stash = g_new (StackStash, 1);
 
-    stash->leaves_by_process =
-       g_hash_table_new (g_direct_hash, g_direct_equal);
     stash->root = NULL;
+    stash->nodes_by_data = g_hash_table_new (g_direct_hash, g_direct_equal);
+    
     return stash;
 }
 
-static StackNode *
-stack_node_add_trace (StackNode  *node,
-                     GList      *bottom,
-                     gint        size,
-                     StackNode **leaf)
+void
+decorate_node (StackStash *stash,
+              StackNode  *node)
 {
-    StackNode *match;
     StackNode *n;
+    gboolean toplevel = TRUE;
 
-    if (!bottom)
-    {
-       *leaf = NULL;
-       return node;
-    }
+    /* FIXME: we will probably want to do this lazily,
+     * and more efficiently (only walk the tree once).
+     */
 
-    if (!bottom->next)
-    {
-       /* A leaf must always be separate, so pids can
-        * point to them
-        */
-       match = NULL;
-    }
-    else
+    for (n = node->parent; n != NULL; n = n->parent)
     {
-       for (match = node; match != NULL; match = match->siblings)
+       if (n->address == node->address)
        {
-           if (match->address == bottom->data)
-               break;
+           toplevel = FALSE;
+           break;
        }
     }
 
-    if (!match)
-    {
-       match = stack_node_new ();
-       match->address = bottom->data;
-       match->siblings = node;
-       node = match;
-    }
-
-    match->children =
-       stack_node_add_trace (match->children, bottom->next, size, leaf);
-
-    for (n = match->children; n; n = n->siblings)
-       n->parent = match;
+    node->toplevel = toplevel;
     
-    if (!bottom->next)
-    {
-       match->size += size;
-       *leaf = match;
-    }
-
-    return node;
+    node->next = g_hash_table_lookup (
+       stash->nodes_by_data, node->address);
+    g_hash_table_insert (
+       stash->nodes_by_data, node->address, node);
 }
 
 void
 stack_stash_add_trace (StackStash *stash,
-                      Process    *process,
-                      gulong     *addrs,
+                      gulong     *addrs,
                       int         n_addrs,
                       int         size)
 {
-    GList *trace;
-    StackNode *leaf;
+    StackNode **location = &(stash->root);
+    StackNode *parent = NULL;
     int i;
 
     if (!n_addrs)
        return;
     
-    trace = NULL;
-    for (i = 0; i < n_addrs; ++i)
-       trace = g_list_prepend (trace, GINT_TO_POINTER (addrs[i]));
+    for (i = n_addrs - 1; i >= 0; --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 (
-       stash->leaves_by_process, process);
-    g_hash_table_insert (
-       stash->leaves_by_process, process, leaf);
+       if (!match)
+       {
+           match = stack_node_new ();
+           match->address = (gpointer)addrs[i];
+           match->siblings = *location;
+           match->parent = parent;
+           *location = match;
 
-    g_list_free (trace);
-}
+           decorate_node (stash, match);
+       }
 
-typedef struct CallbackInfo
-{
-    StackFunction func;
-    gpointer      data;
-} CallbackInfo;
+       location = &(match->children);
+       parent = match;
+    }
+
+    parent->size += size;
+}
 
 static void
-do_callback (gpointer key, gpointer value, gpointer data)
+do_callback (StackNode *node,
+            GSList    *trace,
+            StackFunction stack_func,
+            gpointer data)
 {
-    CallbackInfo *info = data;
-    Process *process = key;
-    StackNode *n;
-    StackNode *leaf = value;
-    while (leaf)
-    {
-       GSList *trace;
-
-       trace = NULL;
-       for (n = leaf; n; n = n->parent)
-           trace = g_slist_prepend (trace, n->address);
+    GSList link;
+    
+    if (!node)
+       return;
 
-       info->func (process, trace, leaf->size, info->data);
+    link.next = trace;
+    link.data = node->address;
+    
+    do_callback (node->siblings, trace, stack_func, data);
+    do_callback (node->children, &link, stack_func, data);
 
-       g_slist_free (trace);
-       
-       leaf = leaf->next;
-    }
+    if (node->size)
+       stack_func (&link, node->size, data);
 }
 
 void
@@ -190,11 +149,7 @@ stack_stash_foreach   (StackStash      *stash,
                       StackFunction    stack_func,
                       gpointer         data)
 {
-    CallbackInfo info;
-    info.func = stack_func;
-    info.data = data;
-       
-    g_hash_table_foreach (stash->leaves_by_process, do_callback, &info);
+    do_callback (stash->root, NULL, stack_func, data);
 }
 
 static void
@@ -213,6 +168,90 @@ void
 stack_stash_free         (StackStash      *stash)
 {
     stack_node_free (stash->root);
-    g_hash_table_destroy (stash->leaves_by_process);
+    g_hash_table_destroy (stash->nodes_by_data);
+    
     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;
+}
index aa058e1..1372be7 100644 (file)
 #define STACK_STASH_H
 
 #include <glib.h>
-#include "process.h"
 
 typedef struct StackStash StackStash;
 
-typedef void (* StackFunction) (Process *process,
-                               GSList *trace,
+typedef struct StackNode StackNode;
+
+struct StackNode
+{
+    gpointer   address;
+    int                size;
+    
+    StackNode *        parent;
+    StackNode *        siblings;
+    StackNode *        children;
+
+    StackNode * next;
+
+    gboolean   toplevel;
+};
+
+typedef void (* StackFunction) (GSList *trace,
                                gint size,
                                gpointer data);
 
 /* Stach */
 StackStash *stack_stash_new       (void);
 void        stack_stash_add_trace (StackStash      *stash,
-                                  Process         *process,
                                   gulong          *addrs,
                                   gint             n_addrs,
                                   int              size);
 void        stack_stash_foreach   (StackStash      *stash,
                                   StackFunction    stack_func,
                                   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);
+StackNode  *stack_stash_get_root   (StackStash *stash);
+StackStash *stack_stash_new_from_root (StackNode *root);
 
 #endif
index 74a09c5..bc9b4c6 100644 (file)
 #include "process.h"
 #include "watch.h"
 #include "signal-handler.h"
+#include "collector.h"
 
 typedef struct Application Application;
 struct Application
 {
-    int                fd;
-    StackStash *stack_stash;
+    Collector *        collector;
     char *     outfile;
     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
 dump_data (Application *app)
 {
     GError *err = NULL;
-    Profile *profile = profile_new (app->stack_stash);
+    Profile *profile = collector_create_profile (app->collector);
 
     profile_save (profile, app->outfile, &err);
 
@@ -104,10 +62,11 @@ signal_handler (int signo, gpointer data)
 {
     Application *app = data;
     
-    g_print ("signal %d caught: dumping data\n", signo);
-    
     dump_data (app);
 
+    while (g_main_iteration (FALSE))
+       ;
+    
     g_main_loop_quit (app->main_loop);
 }
 
@@ -167,16 +126,17 @@ main (int argc,
     if (quit)
        return -1;
 
-    app->fd = fd;
+    app->collector = collector_new (NULL, NULL);
     app->outfile = g_strdup (argv[1]);
-    app->stack_stash = stack_stash_new ();
     app->main_loop = g_main_loop_new (NULL, 0);
 
+    /* FIXME: check the errors */
     signal_set_handler (SIGTERM, signal_handler, app, NULL);
     signal_set_handler (SIGINT, signal_handler, app, NULL);
+
+    /* FIXME: check the error */
+    collector_start (app->collector, NULL);
     
-    fd_add_watch (app->fd, app);
-    fd_set_read_callback (app->fd, on_read);
     g_main_loop_run (app->main_loop);
 
     signal_unset_handler (SIGTERM);
index 67507e7..eccace1 100644 (file)
--- a/sysprof.c
+++ b/sysprof.c
@@ -1,4 +1,4 @@
-/* Sysprof -- Sampling, systemwide CPU profiler 
+/* Sysprof -- Sampling, systemwide CPU profiler
  * Copyright 2004, Red Hat, Inc.
  * Copyright 2004, 2005, Soeren Sandmann
  *
 
 #include <config.h>
 
-#include <stdio.h>
 #include <gtk/gtk.h>
-#include <stdlib.h>
-#include <fcntl.h>
-#include <unistd.h>
 #include <glade/glade.h>
 #include <errno.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 "profile.h"
+#include "collector.h"
 
 /* FIXME - not10 */
 #define _(a) a
@@ -53,9 +44,9 @@ typedef enum
 
 struct Application
 {
-    int                        input_fd;
+    Collector *                collector;
+    
     State              state;
-    StackStash *       stash;
     
     GtkWidget *                main_window;
     GdkPixbuf *                icon;
@@ -82,8 +73,6 @@ struct Application
     ProfileDescendant * descendants;
     ProfileCaller *    callers;
     
-    int                        n_samples;
-    
     int                        timeout_id;
     int                        generating_profile;
 
@@ -104,7 +93,6 @@ struct Application
                                            *
                                            * Model/View/Controller is a possibility.
                                            */
-    GTimeVal           latest_reset;
 };
 
 static gboolean
@@ -112,16 +100,20 @@ show_samples_timeout (gpointer data)
 {
     Application *app = data;
     char *label;
+    int n_samples;
 
     switch (app->state)
     {
     case INITIAL:
-       label = g_strdup ("Samples: 0");
+       n_samples = 0;
        break;
        
     case PROFILING:
+       n_samples = collector_get_n_samples (app->collector);
+       break;
+       
     case DISPLAYING:
-       label = g_strdup_printf ("Samples: %d", app->n_samples);
+       n_samples = profile_get_size (app->profile);
        break;
 
     default:
@@ -129,6 +121,8 @@ show_samples_timeout (gpointer data)
        break;
     }
     
+    label = g_strdup_printf ("Samples: %d", n_samples);
+
     gtk_label_set_label (GTK_LABEL (app->samples_label), label);
     
     g_free (label);
@@ -156,7 +150,9 @@ update_sensitivity (Application *app)
     gboolean sensitive_reset_button;
     
     GtkWidget *active_radio_button;
-    
+
+    gboolean has_samples;
+
     switch (app->state)
     {
     case INITIAL:
@@ -170,9 +166,11 @@ update_sensitivity (Application *app)
        break;
        
     case PROFILING:
-       sensitive_profile_button = (app->n_samples > 0);
-       sensitive_save_as_button = (app->n_samples > 0);
-       sensitive_reset_button = (app->n_samples > 0);
+       has_samples = (collector_get_n_samples (app->collector) > 0);
+    
+       sensitive_profile_button = has_samples;
+       sensitive_save_as_button = has_samples;
+       sensitive_reset_button = has_samples;
        sensitive_start_button = TRUE;
        sensitive_tree_views = FALSE;
        sensitive_samples_label = TRUE;
@@ -250,82 +248,6 @@ set_busy (GtkWidget *widget, gboolean busy)
     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
 set_application_title (Application *app,
                       const char * name)
@@ -349,7 +271,7 @@ set_application_title (Application *app,
     else
     {
        gtk_window_set_title (GTK_WINDOW (app->main_window),
-                             "System Profiler");
+                             "System Collector");
     }
 }
 
@@ -365,46 +287,13 @@ delete_data (Application *app)
        gtk_tree_view_set_model (GTK_TREE_VIEW (app->callers_view), NULL);
        gtk_tree_view_set_model (GTK_TREE_VIEW (app->descendants_view), NULL);
     }
+
+    collector_reset (app->collector);
     
-    if (app->stash)
-       stack_stash_free (app->stash);
-    app->stash = stack_stash_new ();
-    process_flush_caches ();
-    app->n_samples = 0;
     queue_show_samples (app);
+    
     app->profile_from_file = FALSE;
     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
@@ -432,27 +321,6 @@ sorry (GtkWidget *parent_window,
     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
 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)))
        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);
+    delete_data (app);
 
-           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;
-           }
-       }
+    /* 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.");
 
-       app->input_fd = fd;
-       fd_add_watch (app->input_fd, app);
+       return;
     }
     
-    fd_set_read_callback (app->input_fd, on_read);
-    
-    delete_data (app);
-    
-    g_idle_add_full (G_PRIORITY_LOW, start_profiling, app, NULL);
+    app->state = PROFILING;
+
+    update_sensitivity (app);
 }
 
 enum
@@ -533,13 +385,13 @@ enum
     DESCENDANTS_OBJECT
 };
 
-static ProfileObject *
+static char *
 get_current_object (Application *app)
 {
     GtkTreeSelection *selection;
     GtkTreeModel *model;
     GtkTreeIter selected;
-    ProfileObject *object;
+    char *object;
     
     selection = gtk_tree_view_get_selection (app->object_view);
     
@@ -580,14 +432,14 @@ fill_main_list (Application *app)
            ProfileObject *object = list->data;
            GtkTreeIter iter;
            double profile_size = profile_get_size (profile);
-           
+
            gtk_list_store_append (list_store, &iter);
            
            gtk_list_store_set (list_store, &iter,
                                OBJECT_NAME, object->name,
                                OBJECT_SELF, 100.0 * object->self / profile_size,
                                OBJECT_TOTAL, 100.0 * object->total / profile_size,
-                               OBJECT_OBJECT, object,
+                               OBJECT_OBJECT, object->name,
                                -1);
        }
        g_list_free (objects);
@@ -627,11 +479,11 @@ add_node (GtkTreeStore      *store,
     gtk_tree_store_insert (store, &iter, (GtkTreeIter *)parent, 0);
     
     gtk_tree_store_set (store, &iter,
-                       DESCENDANTS_NAME, node->object->name,
+                       DESCENDANTS_NAME, node->name,
                        DESCENDANTS_SELF, 100 * (node->self)/(double)size,
                        DESCENDANTS_NON_RECURSE, 100 * (node->non_recursion)/(double)size,
                        DESCENDANTS_TOTAL, 100 * (node->total)/(double)size,
-                       DESCENDANTS_OBJECT, node->object,
+                       DESCENDANTS_OBJECT, node->name,
                        -1);
     
     add_node (store, size, parent, node->siblings);
@@ -662,7 +514,7 @@ fill_descendants_tree (Application *app)
     
     if (app->profile)
     {
-       ProfileObject *object = get_current_object (app);
+       char *object = get_current_object (app);
        if (object)
        {
            app->descendants =
@@ -702,8 +554,8 @@ add_callers (GtkListStore *list_store,
        GtkTreeIter iter;
        double profile_size = profile_get_size (profile);
        
-       if (callers->object)
-           name = callers->object->name;
+       if (callers->name)
+           name = callers->name;
        else
            name = "<spontaneous>";
        
@@ -713,7 +565,7 @@ add_callers (GtkListStore *list_store,
            CALLERS_NAME, name,
            CALLERS_SELF, 100.0 * callers->self / profile_size,
            CALLERS_TOTAL, 100.0 * callers->total / profile_size,
-           CALLERS_OBJECT, callers->object,
+           CALLERS_OBJECT, callers->name,
            -1);
        
        callers = callers->next;
@@ -743,7 +595,7 @@ fill_callers_list (Application *app)
     
     if (app->profile)
     {
-       ProfileObject *object = get_current_object (app);
+       char *object = get_current_object (app);
        if (object)
        {
            app->callers = profile_list_callers (app->profile, object);
@@ -784,7 +636,7 @@ ensure_profile (Application *app)
     if (app->profile)
        return;
     
-    app->profile = profile_new (app->stash);
+    app->profile = collector_create_profile (app->collector);
 
     fill_lists (app);
     
@@ -956,8 +808,6 @@ set_loaded_profile (Application *app,
        
     app->state = DISPLAYING;
     
-    app->n_samples = profile_get_size (profile);
-    
     app->profile = profile;
     app->profile_from_file = TRUE;
     
@@ -1041,8 +891,14 @@ on_open_clicked (gpointer widget,
 }
 
 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 ();
 }
 
@@ -1123,6 +979,7 @@ expand_descendants_tree (Application *app)
                    path = gtk_tree_path_copy (path);
                    gtk_tree_path_next (path);
                }
+
                gtk_tree_path_free (path);
            }
        }
@@ -1158,7 +1015,7 @@ on_object_selection_changed (GtkTreeSelection *selection,
 
 static void
 really_goto_object (Application *app,
-                   ProfileObject *object)
+                   char *object)
 {
     GtkTreeModel *profile_objects;
     GtkTreeIter iter;
@@ -1170,13 +1027,13 @@ really_goto_object (Application *app,
     {
        do
        {
-           ProfileObject *profile_object;
+           char *list_object;
            
            gtk_tree_model_get (profile_objects, &iter,
-                               OBJECT_OBJECT, &profile_object,
+                               OBJECT_OBJECT, &list_object,
                                -1);
            
-           if (profile_object == object)
+           if (list_object == object)
            {
                found = TRUE;
                break;
@@ -1202,7 +1059,7 @@ goto_object (Application *app,
 {
     GtkTreeIter iter;
     GtkTreeModel *model = gtk_tree_view_get_model (tree_view);
-    ProfileObject *object;
+    char *object;
     
     if (!gtk_tree_model_get_iter (model, &iter, path))
        return;
@@ -1213,7 +1070,6 @@ goto_object (Application *app,
        return;
     
     really_goto_object (app, object);
-    
 }
 
 static void
@@ -1424,16 +1280,22 @@ build_gui (Application *app)
     return TRUE;
 }
 
+static void
+on_new_sample (gpointer data)
+{
+    Application *app = data;
+
+    if (app->state == PROFILING)
+       update_sensitivity (app);
+}
+
 static Application *
 application_new (void)
 {
     Application *app = g_new0 (Application, 1);
-    
-    app->stash = stack_stash_new ();
-    app->input_fd = -1;
-    app->state = INITIAL;
 
-    g_get_current_time (&app->latest_reset);
+    app->collector = collector_new (on_new_sample, app);
+    app->state = INITIAL;
     
     return app;
 }
@@ -1480,12 +1342,7 @@ main (int argc, char **argv)
     gtk_init (&argc, &argv);
     
     app = application_new ();
-    
-#if 0
-    nice (-19);
-    g_timeout_add (10, on_timeout, app);
-#endif
-    
+
     if (!build_gui (app))
        return -1;