diff --git a/ChangeLog b/ChangeLog index eab45c45..56146321 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,79 @@ +2005-10-30 Soren Sandmann + + * Merge stackstash-reorg branch into HEAD + +2005-10-30 Soren Sandmann + + * Makefile.am, sysprof.c, sysprof-text.c, collector.[ch]: Rename + profiler -> collector + +2005-10-30 Soren Sandmann + + * profile.c (profile_load): Reenable loading. + +2005-10-30 Soren Sandmann + + * profile.c (profile_save): Reenable saving. + +Sat Oct 29 21:37:42 2005 Soeren Sandmann + + * 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 + + * TODO: Updates + +Sat Oct 29 17:38:01 2005 Søren Sandmann + + * 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 + + * profile.c: Delete all the obsolete stuff related to call tree. + + * TODO: update + +Sat Oct 29 16:52:32 2005 Søren Sandmann + + * 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 + + * 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 + + * 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 + + * 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 Fix crash pointed reported by Rudi Chiarito. @@ -7,6 +83,21 @@ Sat Oct 29 14:43:00 2005 Søren Sandmann * sysprof.c (on_read): Only trace if n_addresses != 0. +Sat Oct 29 03:47:03 2005 Soeren Sandmann + + * profile.[ch], sysprof.c: Get rid of ProfileObject for callers + and descendants. + + * TODO: updates. + +Sat Oct 29 02:57:34 2005 Soeren Sandmann + + * 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 * README, TODO: updates @@ -24,6 +115,102 @@ Tue Oct 11 22:40:24 2005 Soeren Sandmann 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 + + * Merge in changes from HEAD + +Thu Oct 6 22:28:39 2005 Soeren Sandmann + + * 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 + + * 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 + + * 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 + + * 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 + + * profile.h, sysprof.c: Remove some unnecessary includes. + +Sat Oct 1 18:12:44 2005 Soeren Sandmann + + * 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 + + * 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 + + * 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 + + * 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 * module/sysprof-module.c: Delete lots of commented-out code. diff --git a/Makefile.am b/Makefile.am index 975d2bbe..288ee21e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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 e69de29b..a97e151c 100644 --- 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 4ac8763e..cf8e6b65 100644 --- 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 index 00000000..75fa8160 --- /dev/null +++ b/collector.c @@ -0,0 +1,300 @@ +#include "stackstash.h" +#include "collector.h" +#include "module/sysprof-module.h" +#include "watch.h" +#include "process.h" + +#include +#include +#include +#include +#include + +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 index 00000000..6379b10b --- /dev/null +++ b/collector.h @@ -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); diff --git a/process.c b/process.c index ff14b479..657ecced 100644 --- 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; -} diff --git a/process.h b/process.h index 6ad1eba2..d4ff4d50 100644 --- a/process.h +++ b/process.h @@ -24,6 +24,7 @@ #ifndef PROCESS_H #define PROCESS_H +#include #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, diff --git a/profile.c b/profile.c index f3dd0e97..42aca18a 100644 --- 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) { - SFileOutput *output = data; - ProfileObject *object = key; - - 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); + int total; + StackNode *child; - sfile_end_add (output, "object", object); + /* 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) +{ + StackNode *n; + int total = 0; + + for (n = node; n != NULL; n = n->next) + { + if (n->toplevel) + total += sum_children (n); + } + + 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); + profile->stash = stack_stash_new_from_root (root); + return profile; } -static ProfileObject * -profile_object_new (void) -{ - ProfileObject *obj = g_new (ProfileObject, 1); - obj->total = 0; - obj->self = 0; - - return obj; -} - -static void -profile_object_free (ProfileObject *obj) -{ - g_free (obj->name); - g_free (obj); -} - -static char * -generate_key (Process *process, gulong address) -{ - if (address) - { - const Symbol *symbol = process_lookup_symbol (process, address); - - return g_strdup_printf ("%p%s", (void *)symbol->address, symbol->name); - } - else - { - return g_strdup_printf ("p:%p", process_get_cmdline (process)); - } -} - -static char * -generate_presentation_name (Process *process, gulong address) -{ - /* FIXME - not10 - * using 0 to indicate "process" is broken - */ - if (address) - { - const Symbol *symbol = process_lookup_symbol (process, address); - - return g_strdup_printf ("%s", symbol->name); - } - else - { - return g_strdup_printf ("%s", process_get_cmdline (process)); - } -} - -static void -ensure_profile_object (GHashTable *profile_objects, Process *process, gulong address) -{ - char *key = generate_key (process, address); - - if (!g_hash_table_lookup (profile_objects, key)) - { - ProfileObject *object; - - object = profile_object_new (); - object->name = generate_presentation_name (process, address); - - g_hash_table_insert (profile_objects, key, object); - } - else - { - g_free (key); - } -} - -static ProfileObject * -lookup_profile_object (GHashTable *profile_objects, Process *process, gulong address) -{ - ProfileObject *object; - char *key = generate_key (process, address); - object = g_hash_table_lookup (profile_objects, key); - g_free (key); - g_assert (object); - return object; -} - -typedef struct Info Info; -struct Info -{ - Profile *profile; - GHashTable *profile_objects; -}; - -static void -generate_object_table (Process *process, GSList *trace, gint size, gpointer data) -{ - Info *info = data; - GSList *list; - - ensure_profile_object (info->profile_objects, process, 0); - - for (list = trace; list != NULL; list = list->next) - { - update (); - ensure_profile_object (info->profile_objects, process, (gulong)list->data); - } - - info->profile->size += size; -} - -static Node * -node_new () -{ - Node *node = g_new (Node, 1); - node->siblings = NULL; - node->children = NULL; - node->parent = NULL; - node->next = NULL; - node->object = NULL; - node->self = 0; - node->total = 0; - - return node; -} - -static Node * -node_add_trace (Profile *profile, GHashTable *profile_objects, Node *node, Process *process, - GSList *trace, gint size, - GHashTable *seen_objects) -{ - ProfileObject *object; - Node *match = NULL; - - if (!trace) - return node; - - object = lookup_profile_object (profile_objects, process, (gulong)trace->data); - for (match = node; match != NULL; match = match->siblings) - { - if (match->object == object) - break; - } - - if (!match) - { - match = node_new (); - match->object = object; - match->siblings = node; - node = match; - - if (g_hash_table_lookup (seen_objects, object)) - match->toplevel = FALSE; - else - match->toplevel = TRUE; - - match->next = g_hash_table_lookup (profile->nodes_by_object, object); - g_hash_table_insert (profile->nodes_by_object, object, match); - } - - g_hash_table_insert (seen_objects, object, GINT_TO_POINTER (1)); - -#if 0 - g_print ("%s adds %d\n", match->object->name, size); -#endif - match->total += size; - if (!trace->next) - match->self += size; - - match->children = node_add_trace (profile, profile_objects, match->children, process, trace->next, size, - seen_objects); - - return node; -} - -#if 0 -static void -dump_trace (GSList *trace) -{ - g_print ("TRACE: "); - while (trace) - { - g_print ("%x ", trace->data); - trace = trace->next; - } - g_print ("\n\n"); -} -#endif - -static void -generate_call_tree (Process *process, GSList *trace, gint size, gpointer data) -{ - Info *info = data; - Node *match = NULL; - ProfileObject *proc = lookup_profile_object (info->profile_objects, process, 0); - GHashTable *seen_objects; - - for (match = info->profile->call_tree; match; match = match->siblings) - { - if (match->object == proc) - break; - } - - if (!match) - { - match = node_new (); - match->object = proc; - match->siblings = info->profile->call_tree; - info->profile->call_tree = match; - match->toplevel = TRUE; - } - - g_hash_table_insert (info->profile->nodes_by_object, proc, match); - - match->total += size; - if (!trace) - match->self += size; - - seen_objects = g_hash_table_new (direct_hash_no_null, g_direct_equal); - - g_hash_table_insert (seen_objects, proc, GINT_TO_POINTER (1)); - - update (); - match->children = node_add_trace (info->profile, info->profile_objects, match->children, process, - trace, size, seen_objects); - - g_hash_table_destroy (seen_objects); -} - -static void -link_parents (Node *node, Node *parent) -{ - if (!node) - return; - - node->parent = parent; - - link_parents (node->siblings, parent); - link_parents (node->children, node); -} - -static void -compute_object_total (gpointer key, gpointer value, gpointer data) -{ - Node *node; - ProfileObject *object = key; - - for (node = value; node != NULL; node = node->next) - { - object->self += node->self; - if (node->toplevel) - object->total += node->total; - } -} - Profile * profile_new (StackStash *stash) { - Info info; + Profile *profile = g_new (Profile, 1); - 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->stash = stash; - /* profile objects */ - info.profile_objects = g_hash_table_new_full (g_str_hash, g_str_equal, - g_free, NULL); - - stack_stash_foreach (stash, generate_object_table, &info); - stack_stash_foreach (stash, generate_call_tree, &info); - link_parents (info.profile->call_tree, NULL); - - g_hash_table_foreach (info.profile->nodes_by_object, compute_object_total, NULL); - - g_hash_table_destroy (info.profile_objects); - - return info.profile; + 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)); } diff --git a/profile.h b/profile.h index edc1c45d..7f6cd3cb 100644 --- a/profile.h +++ b/profile.h @@ -17,9 +17,10 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +#ifndef PROFILE_H +#define PROFILE_H + #include -#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 3ab07577..00f0a0c8 100644 --- 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 - */ diff --git a/stackstash.c b/stackstash.c index 9f66f158..b717bfcb 100644 --- a/stackstash.c +++ b/stackstash.c @@ -19,22 +19,10 @@ #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) + for (n = node->parent; n != NULL; n = n->parent) { - /* A leaf must always be separate, so pids can - * point to them - */ - match = NULL; - } - else - { - 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); + } + + location = &(match->children); + parent = match; + } + + parent->size += size; } -typedef struct CallbackInfo -{ - StackFunction func; - gpointer data; -} CallbackInfo; - 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; + GSList link; + + if (!node) + return; - trace = NULL; - for (n = leaf; n; n = n->parent) - trace = g_slist_prepend (trace, n->address); + link.next = trace; + link.data = node->address; + + do_callback (node->siblings, trace, stack_func, data); + do_callback (node->children, &link, stack_func, data); - info->func (process, trace, leaf->size, info->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; +} diff --git a/stackstash.h b/stackstash.h index aa058e1d..1372be7c 100644 --- a/stackstash.h +++ b/stackstash.h @@ -21,25 +21,50 @@ #define STACK_STASH_H #include -#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 diff --git a/sysprof-text.c b/sysprof-text.c index 74a09c5d..bc9b4c6b 100644 --- a/sysprof-text.c +++ b/sysprof-text.c @@ -32,63 +32,21 @@ #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); diff --git a/sysprof.c b/sysprof.c index 67507e7d..eccace15 100644 --- 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 * @@ -19,23 +19,14 @@ #include -#include #include -#include -#include -#include #include #include #include -#include -#include -#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) + delete_data (app); + + /* FIXME: get the real error message */ + if (!collector_start (app->collector, NULL)) { - int fd; + 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."); - fd = open ("/proc/sysprof-trace", O_RDONLY); - if (fd < 0) - { - load_module(); - - fd = open ("/proc/sysprof-trace", O_RDONLY); - - if (fd < 0) - { - sorry (app->main_window, - "Can't open /proc/sysprof-trace. You need to insert\n" - "the sysprof kernel module. Run\n" - "\n" - " modprobe sysprof-module\n" - "\n" - "as root."); - - update_sensitivity (app); - return; - } - } - - app->input_fd = fd; - fd_add_watch (app->input_fd, app); + 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 = ""; @@ -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;