+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.
* 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
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.
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
+- New 'everything' object
+- New commandline version
+- Assign time spent in kernel to the user process responsible
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:
(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
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)
- 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.
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
--- /dev/null
+#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);
+}
--- /dev/null
+#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);
GList *bad_pages;
int pid;
+
+ Symbol undefined;
};
static void
{
Process *p;
- p = g_new (Process, 1);
+ p = g_new0 (Process, 1);
if (*cmdline != '\0')
p->cmdline = g_strdup_printf ("[%s]", cmdline);
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));
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);
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;
{
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;
-}
#ifndef PROCESS_H
#define PROCESS_H
+#include <glib.h>
#include "binfile.h"
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,
#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 *
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);
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);
return result;
}
+#if 0
static void
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)
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);
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
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;
}
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;
}
{
match = g_new (ProfileDescendant, 1);
- match->object = node->object;
+ match->name = node->address;
match->non_recursion = 0;
match->total = 0;
match->self = 0;
}
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;
}
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;
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;
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;
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);
}
-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);
}
}
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;
}
gint
profile_get_size (Profile *profile)
{
- return profile->size;
+ return compute_total (stack_stash_get_root (profile->stash));
}
* 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;
struct ProfileDescendant
{
- ProfileObject * object;
+ char * name;
guint self;
guint total;
guint non_recursion;
struct ProfileCaller
{
- ProfileObject * object; /* can be NULL */
+ char * name;
guint total;
guint self;
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);
GError **err);
Profile * profile_load (const char *filename,
GError **err);
+
+#endif /* PROFILE_H */
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);
* 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 - */
#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 *
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
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
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;
+}
#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
#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);
{
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);
}
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);
-/* 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
struct Application
{
- int input_fd;
+ Collector * collector;
+
State state;
- StackStash * stash;
GtkWidget * main_window;
GdkPixbuf * icon;
ProfileDescendant * descendants;
ProfileCaller * callers;
- int n_samples;
-
int timeout_id;
int generating_profile;
*
* Model/View/Controller is a possibility.
*/
- GTimeVal latest_reset;
};
static gboolean
{
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:
break;
}
+ label = g_strdup_printf ("Samples: %d", n_samples);
+
gtk_label_set_label (GTK_LABEL (app->samples_label), label);
g_free (label);
gboolean sensitive_reset_button;
GtkWidget *active_radio_button;
-
+
+ gboolean has_samples;
+
switch (app->state)
{
case INITIAL:
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;
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)
else
{
gtk_window_set_title (GTK_WINDOW (app->main_window),
- "System Profiler");
+ "System Collector");
}
}
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
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)
{
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
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);
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);
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);
if (app->profile)
{
- ProfileObject *object = get_current_object (app);
+ char *object = get_current_object (app);
if (object)
{
app->descendants =
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>";
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;
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);
if (app->profile)
return;
- app->profile = profile_new (app->stash);
+ app->profile = collector_create_profile (app->collector);
fill_lists (app);
app->state = DISPLAYING;
- app->n_samples = profile_get_size (profile);
-
app->profile = profile;
app->profile_from_file = TRUE;
}
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 ();
}
path = gtk_tree_path_copy (path);
gtk_tree_path_next (path);
}
+
gtk_tree_path_free (path);
}
}
static void
really_goto_object (Application *app,
- ProfileObject *object)
+ char *object)
{
GtkTreeModel *profile_objects;
GtkTreeIter iter;
{
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;
{
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;
return;
really_goto_object (app, object);
-
}
static void
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;
}
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;