diff options
Diffstat (limited to 'sysprof.c')
-rw-r--r-- | sysprof.c | 1483 |
1 files changed, 830 insertions, 653 deletions
@@ -1,6 +1,6 @@ /* Sysprof -- Sampling, systemwide CPU profiler * Copyright 2004, Red Hat, Inc. - * Copyright 2004, 2005, 2006, 2007, Soeren Sandmann + * Copyright 2004, 2005, 2006, 2007, 2008, Soeren Sandmann * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,26 +16,19 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ - #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 <string.h> +#include <stdlib.h> -#include "binfile.h" -#include "watch.h" -#include "module/sysprof-module.h" -#include "stackstash.h" -#include "profile.h" +#include "footreestore.h" #include "treeviewutils.h" +#include "profile.h" +#include "collector.h" /* FIXME - not10 */ #define _(a) a @@ -53,88 +46,99 @@ typedef enum struct Application { - int input_fd; + Collector * collector; + State state; - StackStash * stash; - - GtkWidget * main_window; GdkPixbuf * icon; - + + GtkWidget * main_window; + GtkTreeView * object_view; GtkTreeView * callers_view; GtkTreeView * descendants_view; - + GtkWidget * start_button; GtkWidget * profile_button; GtkWidget * reset_button; GtkWidget * save_as_button; GtkWidget * dummy_button; - + GtkWidget * start_item; GtkWidget * profile_item; GtkWidget * reset_item; GtkWidget * save_as_item; GtkWidget * open_item; - + GtkWidget * screenshot_item; + GtkWidget * about_item; + GtkWidget * quit_item; + GtkWidget * hpaned; + GtkWidget * vpaned; + + GtkTreeSelection * object_selection; + GtkWidget * samples_label; - + GtkWidget * samples_hbox; + + gboolean screenshot_window_visible; + GtkWidget * screenshot_textview; + GtkWidget * screenshot_close_button; + GtkWidget * screenshot_window; + Profile * profile; ProfileDescendant * descendants; ProfileCaller * callers; - - int n_samples; - + int timeout_id; - int generating_profile; + int update_screenshot_id; char * loaded_profile; - - gboolean profile_from_file; /* FIXME - not10: This is a kludge. Figure out how - * to maintain the application model properly - * - * The fundamental issue is that the state of - * widgets is controlled by two different - * entities: - * - * The user clicks on them, changing their - * state. - * - * The application model changes, changing their - * state. - * - * Model/View/Controller is a possibility. - */ - GTimeVal latest_reset; + + gboolean inhibit_forced_redraw; }; -static gboolean -show_samples_timeout (gpointer data) +static void update_screenshot_window (Application *app); + +static void +show_samples (Application *app) { - 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: g_assert_not_reached(); break; } - + + label = g_strdup_printf ("%d", n_samples); + gtk_label_set_label (GTK_LABEL (app->samples_label), label); - + g_free (label); - +} + +static gboolean +show_samples_timeout (gpointer data) +{ + Application *app = data; + + show_samples (app); + app->timeout_id = 0; - + return FALSE; } @@ -152,11 +156,13 @@ update_sensitivity (Application *app) gboolean sensitive_save_as_button; gboolean sensitive_start_button; gboolean sensitive_tree_views; - gboolean sensitive_samples_label; + gboolean sensitive_samples_hbox; gboolean sensitive_reset_button; - + GtkWidget *active_radio_button; - + + gboolean has_samples; + switch (app->state) { case INITIAL: @@ -165,27 +171,29 @@ update_sensitivity (Application *app) sensitive_start_button = TRUE; sensitive_reset_button = FALSE; sensitive_tree_views = FALSE; - sensitive_samples_label = FALSE; + sensitive_samples_hbox = FALSE; active_radio_button = app->dummy_button; 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; + sensitive_samples_hbox = TRUE; active_radio_button = app->start_button; break; - + case DISPLAYING: sensitive_profile_button = TRUE; sensitive_save_as_button = TRUE; sensitive_start_button = TRUE; sensitive_tree_views = TRUE; sensitive_reset_button = TRUE; - sensitive_samples_label = FALSE; + sensitive_samples_hbox = FALSE; active_radio_button = app->profile_button; break; @@ -193,15 +201,16 @@ update_sensitivity (Application *app) g_assert_not_reached(); break; } - - gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (active_radio_button), TRUE); + + gtk_toggle_tool_button_set_active ( + GTK_TOGGLE_TOOL_BUTTON (active_radio_button), TRUE); /* "profile" widgets */ gtk_widget_set_sensitive (GTK_WIDGET (app->profile_button), sensitive_profile_button); gtk_widget_set_sensitive (GTK_WIDGET (app->profile_item), sensitive_profile_button); - + /* "save as" widgets */ gtk_widget_set_sensitive (GTK_WIDGET (app->save_as_button), sensitive_save_as_button); @@ -213,9 +222,9 @@ update_sensitivity (Application *app) sensitive_start_button); gtk_widget_set_sensitive (GTK_WIDGET (app->start_item), sensitive_start_button); - + #if 0 - /* FIXME - not10: gtk+ doesn't handle changes in sensitivity in response + /* FIXME - not10: gtk+ doesn't handle changes in sensitivity in response * to a click on the same button very well */ gtk_widget_set_sensitive (GTK_WIDGET (app->reset_button), @@ -223,177 +232,44 @@ update_sensitivity (Application *app) gtk_widget_set_sensitive (GTK_WIDGET (app->reset_item), sensitive_reset_button); #endif - + gtk_widget_set_sensitive (GTK_WIDGET (app->object_view), sensitive_tree_views); gtk_widget_set_sensitive (GTK_WIDGET (app->callers_view), sensitive_tree_views); gtk_widget_set_sensitive (GTK_WIDGET (app->descendants_view), sensitive_tree_views); - gtk_widget_set_sensitive (GTK_WIDGET (app->samples_label), sensitive_samples_label); + gtk_widget_set_sensitive (GTK_WIDGET (app->samples_hbox), sensitive_samples_hbox); - queue_show_samples (app); + if (app->screenshot_window_visible) + gtk_widget_show (app->screenshot_window); + else + gtk_widget_hide (app->screenshot_window); + + gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (app->screenshot_item), + app->screenshot_window_visible); + + show_samples (app); } static void -set_busy (GtkWidget *widget, gboolean busy) +set_busy (GtkWidget *widget, + gboolean busy) { GdkCursor *cursor; - + GdkWindow *window; + if (busy) cursor = gdk_cursor_new (GDK_WATCH); else cursor = NULL; - gdk_window_set_cursor (widget->window, cursor); - - if (cursor) - gdk_cursor_unref (cursor); - - gdk_flush (); -} - -#if 0 -static gchar * -get_name (pid_t pid) -{ - char *cmdline; - char *name = g_strdup_printf ("/proc/%d/cmdline", pid); - - if (g_file_get_contents (name, &cmdline, NULL, NULL)) - return cmdline; + if (GTK_IS_TEXT_VIEW (widget)) + window = gtk_text_view_get_window (GTK_TEXT_VIEW (widget), GTK_TEXT_WINDOW_TEXT); else - return g_strdup ("<unknown>"); -} -#endif + window = widget->window; -#if 0 -static void -on_timeout (gpointer data) -{ - Application *app = data; - GList *pids, *list; - int mypid = getpid(); - - pids = list_processes (); - - for (list = pids; list != NULL; list = list->next) - { - int pid = GPOINTER_TO_INT (list->data); - - if (pid == mypid) - continue; - - if (get_process_state (pid) == PROCESS_RUNNING) - { - Process *process; - SysprofStackTrace trace; - int i; - - if (!generate_stack_trace (pid, &trace)) - { - continue; - } - - process = process_get_from_pid (pid); - -#if 0 - process_ensure_map (process, trace.pid, - (gulong)trace.addresses[i]); -#endif - - g_print ("n addr: %d\n", trace.n_addresses); - 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); - g_list_free (pids); - - return TRUE; -} -#endif - -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 -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; */ + gdk_window_set_cursor (window, cursor); - 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); + if (cursor) + gdk_cursor_unref (cursor); } static void @@ -405,12 +281,12 @@ set_application_title (Application *app, new_name = g_path_get_basename (name); else new_name = NULL; - + if (app->loaded_profile) g_free (app->loaded_profile); app->loaded_profile = new_name; - + if (app->loaded_profile) { gtk_window_set_title (GTK_WINDOW (app->main_window), @@ -419,7 +295,7 @@ set_application_title (Application *app, else { gtk_window_set_title (GTK_WINDOW (app->main_window), - "System Profiler"); + APPLICATION_NAME); } } @@ -430,51 +306,15 @@ delete_data (Application *app) { profile_free (app->profile); app->profile = NULL; - + gtk_tree_view_set_model (GTK_TREE_VIEW (app->object_view), NULL); gtk_tree_view_set_model (GTK_TREE_VIEW (app->callers_view), NULL); gtk_tree_view_set_model (GTK_TREE_VIEW (app->descendants_view), NULL); } - - 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 */ -} + collector_reset (app->collector); -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; + set_application_title (app, NULL); } static void @@ -485,44 +325,23 @@ sorry (GtkWidget *parent_window, va_list args; char *message; GtkWidget *dialog; - + va_start (args, format); g_vasprintf (&message, format, args); va_end (args); - + dialog = gtk_message_dialog_new (parent_window ? GTK_WINDOW (parent_window) : NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_WARNING, - GTK_BUTTONS_OK, message); + GTK_BUTTONS_OK, "%s", message); g_free (message); - + gtk_window_set_title (GTK_WINDOW (dialog), APPLICATION_NAME " Warning"); - + gtk_dialog_run (GTK_DIALOG (dialog)); gtk_widget_destroy (dialog); } -static gboolean -load_module (void) -{ - int exit_status = -1; - char *dummy1, *dummy2; - - if (g_spawn_command_line_sync ("/sbin/modprobe sysprof-module", - &dummy1, &dummy2, - &exit_status, - NULL)) - { - if (WIFEXITED (exit_status)) - exit_status = WEXITSTATUS (exit_status); - - g_free (dummy1); - g_free (dummy2); - } - - return (exit_status == 0); -} - static void on_menu_item_activated (GtkWidget *menu_item, GtkWidget *tool_button) { @@ -536,46 +355,29 @@ static void on_start_toggled (GtkWidget *widget, gpointer data) { Application *app = data; + GError *err = NULL; if (!gtk_toggle_tool_button_get_active ( GTK_TOGGLE_TOOL_BUTTON (app->start_button))) + { return; + } - if (app->input_fd == -1) + if (collector_start (app->collector, &err)) { - int fd; + delete_data (app); - 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->state = PROFILING; + } + else + { + sorry (app->main_window, err->message); - app->input_fd = fd; - fd_add_watch (app->input_fd, app); + g_error_free (err); } - - fd_set_read_callback (app->input_fd, on_read); - - delete_data (app); - - g_idle_add_full (G_PRIORITY_LOW, start_profiling, app, NULL); + + update_screenshot_window (app); + update_sensitivity (app); } enum @@ -598,22 +400,18 @@ enum { DESCENDANTS_NAME, DESCENDANTS_SELF, - DESCENDANTS_NON_RECURSE, - DESCENDANTS_TOTAL, + DESCENDANTS_CUMULATIVE, DESCENDANTS_OBJECT }; -static ProfileObject * +static char * get_current_object (Application *app) { - GtkTreeSelection *selection; GtkTreeModel *model; GtkTreeIter selected; - ProfileObject *object; - - selection = gtk_tree_view_get_selection (app->object_view); - - if (gtk_tree_selection_get_selected (selection, &model, &selected)) + char *object; + + if (gtk_tree_selection_get_selected (app->object_selection, &model, &selected)) { gtk_tree_model_get (model, &selected, OBJECT_OBJECT, &object, @@ -633,77 +431,71 @@ fill_main_list (Application *app) GtkListStore *list_store; Profile *profile = app->profile; GList *objects; - + if (profile) { - gpointer sort_state; - list_store = gtk_list_store_new (4, G_TYPE_STRING, G_TYPE_DOUBLE, G_TYPE_DOUBLE, G_TYPE_POINTER); - + objects = profile_get_objects (profile); for (list = objects; list != NULL; list = list->next) { 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, +#if 0 + OBJECT_SELF, (double)object->self, + OBJECT_TOTAL, (double)object->total, +#endif + OBJECT_OBJECT, object->name, -1); } + g_list_foreach (objects, (GFunc)g_free, NULL); g_list_free (objects); - - sort_state = save_sort_state (app->object_view); - - gtk_tree_view_set_model (app->object_view, GTK_TREE_MODEL (list_store)); - - if (sort_state) - { - restore_sort_state (app->object_view, sort_state); - } - else - { - gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (list_store), - OBJECT_TOTAL, - GTK_SORT_DESCENDING); - } - + + tree_view_set_model_with_default_sort (app->object_view, GTK_TREE_MODEL (list_store), + OBJECT_TOTAL, GTK_SORT_DESCENDING); + g_object_unref (G_OBJECT (list_store)); } - + gtk_tree_view_columns_autosize (app->object_view); } static void -add_node (GtkTreeStore *store, +add_node (FooTreeStore *store, int size, const GtkTreeIter *parent, ProfileDescendant *node) { GtkTreeIter iter; - + if (!node) return; - gtk_tree_store_insert (store, &iter, (GtkTreeIter *)parent, 0); + foo_tree_store_insert (store, &iter, (GtkTreeIter *)parent, 0); - gtk_tree_store_set (store, &iter, - DESCENDANTS_NAME, node->object->name, + foo_tree_store_set (store, &iter, + 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_CUMULATIVE, 100 * (node->cumulative)/(double)size, +#if 0 + DESCENDANTS_SELF, (double)node->self, + DESCENDANTS_CUMULATIVE, (double)node->non_recursion, +#endif + DESCENDANTS_OBJECT, node->name, -1); - + add_node (store, size, parent, node->siblings); add_node (store, size, &iter, node->children); } @@ -711,28 +503,24 @@ add_node (GtkTreeStore *store, static void fill_descendants_tree (Application *app) { - GtkTreeStore *tree_store; - gpointer sort_state; - - sort_state = save_sort_state (app->descendants_view); - + FooTreeStore *tree_store; + if (app->descendants) { profile_descendant_free (app->descendants); app->descendants = NULL; } - + tree_store = - gtk_tree_store_new (5, + foo_tree_store_new (4, G_TYPE_STRING, G_TYPE_DOUBLE, G_TYPE_DOUBLE, - G_TYPE_DOUBLE, G_TYPE_POINTER); - + if (app->profile) { - ProfileObject *object = get_current_object (app); + char *object = get_current_object (app); if (object) { app->descendants = @@ -741,29 +529,18 @@ fill_descendants_tree (Application *app) profile_get_size (app->profile), NULL, app->descendants); } } - - gtk_tree_view_set_model ( - app->descendants_view, GTK_TREE_MODEL (tree_store)); - + + tree_view_set_model_with_default_sort (app->descendants_view, GTK_TREE_MODEL (tree_store), + DESCENDANTS_CUMULATIVE, GTK_SORT_DESCENDING); + g_object_unref (G_OBJECT (tree_store)); - - if (!sort_state) - { - gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (tree_store), - DESCENDANTS_NON_RECURSE, - GTK_SORT_DESCENDING); - } - else - { - restore_sort_state (app->descendants_view, sort_state); - } - + gtk_tree_view_columns_autosize (app->descendants_view); } static void -add_callers (GtkListStore *list_store, - Profile *profile, +add_callers (GtkListStore *list_store, + Profile *profile, ProfileCaller *callers) { while (callers) @@ -771,21 +548,25 @@ add_callers (GtkListStore *list_store, gchar *name; 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>"; - + gtk_list_store_append (list_store, &iter); gtk_list_store_set ( list_store, &iter, CALLERS_NAME, name, CALLERS_SELF, 100.0 * callers->self / profile_size, CALLERS_TOTAL, 100.0 * callers->total / profile_size, - CALLERS_OBJECT, callers->object, +#if 0 + CALLERS_SELF, (double)callers->self, + CALLERS_TOTAL, (double)callers->total, +#endif + CALLERS_OBJECT, callers->name, -1); - + callers = callers->next; } } @@ -794,58 +575,55 @@ static void fill_callers_list (Application *app) { GtkListStore *list_store; - gpointer sort_state; - - sort_state = save_sort_state (app->descendants_view); - + if (app->callers) { profile_caller_free (app->callers); app->callers = NULL; } - + list_store = gtk_list_store_new (4, G_TYPE_STRING, G_TYPE_DOUBLE, G_TYPE_DOUBLE, G_TYPE_POINTER); - + 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); add_callers (list_store, app->profile, app->callers); } } - - gtk_tree_view_set_model ( - app->callers_view, GTK_TREE_MODEL (list_store)); - + + tree_view_set_model_with_default_sort (app->callers_view, GTK_TREE_MODEL (list_store), + CALLERS_TOTAL, GTK_SORT_DESCENDING); + g_object_unref (G_OBJECT (list_store)); - - if (!sort_state) - { - gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (list_store), - CALLERS_TOTAL, - GTK_SORT_DESCENDING); - } - else - { - restore_sort_state (app->callers_view, sort_state); - } - + gtk_tree_view_columns_autosize (app->callers_view); } static void -fill_lists (Application *app) +enter_display_mode (Application *app) { + app->state = DISPLAYING; + + update_sensitivity (app); + + app->inhibit_forced_redraw = TRUE; + fill_main_list (app); - fill_callers_list (app); - fill_descendants_tree (app); + + /* This has the side effect of selecting the first row, which in turn causes + * the other lists to be filled out + */ + gtk_widget_grab_focus (GTK_WIDGET (app->object_view)); + + app->inhibit_forced_redraw = FALSE; } static void @@ -853,14 +631,14 @@ ensure_profile (Application *app) { if (app->profile) return; - - app->profile = profile_new (app->stash); - fill_lists (app); - - app->state = DISPLAYING; - - update_sensitivity (app); + collector_stop (app->collector); + + app->profile = collector_create_profile (app->collector); + + collector_reset (app->collector); + + enter_display_mode (app); } static void @@ -868,11 +646,17 @@ on_about_activated (GtkWidget *widget, gpointer data) { #define OSLASH "\303\270" Application *app = data; + char *name_property; + + if (gtk_minor_version >= 12) + name_property = "program-name"; + else + name_property = "name"; gtk_show_about_dialog (GTK_WINDOW (app->main_window), "logo", app->icon, - "name", APPLICATION_NAME, - "copyright", "Copyright 2004-2008, S"OSLASH"ren Sandmann", + name_property, APPLICATION_NAME, + "copyright", "Copyright 2004-2009, S"OSLASH"ren Sandmann", "version", PACKAGE_VERSION, NULL); } @@ -885,12 +669,6 @@ on_profile_toggled (GtkWidget *widget, gpointer data) if (gtk_toggle_tool_button_get_active (GTK_TOGGLE_TOOL_BUTTON (app->profile_button))) { set_busy (app->main_window, TRUE); - if (app->profile && !app->profile_from_file) - { - profile_free (app->profile); - app->profile = NULL; - } - ensure_profile (app); set_busy (app->main_window, FALSE); } @@ -902,12 +680,15 @@ on_reset_clicked (gpointer widget, gpointer data) Application *app = data; set_busy (app->main_window, TRUE); - + delete_data (app); - + if (app->state == DISPLAYING) + { app->state = INITIAL; - + collector_stop (app->collector); + } + update_sensitivity (app); set_busy (app->main_window, FALSE); @@ -921,7 +702,7 @@ overwrite_file (GtkWindow *window, gchar *utf8_file_name; AtkObject *obj; gint ret; - + utf8_file_name = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL); msgbox = gtk_message_dialog_new (window, (GtkDialogFlags)GTK_DIALOG_DESTROY_WITH_PARENT, @@ -930,30 +711,29 @@ overwrite_file (GtkWindow *window, _("A file named \"%s\" already exists."), utf8_file_name); g_free (utf8_file_name); - + gtk_message_dialog_format_secondary_text ( GTK_MESSAGE_DIALOG (msgbox), _("Do you want to replace it with the one you are saving?")); - + gtk_dialog_add_button (GTK_DIALOG (msgbox), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL); - + gtk_dialog_add_button (GTK_DIALOG (msgbox), _("_Replace"), GTK_RESPONSE_YES); - + gtk_dialog_set_default_response (GTK_DIALOG (msgbox), GTK_RESPONSE_CANCEL); - + obj = gtk_widget_get_accessible (msgbox); - + if (GTK_IS_ACCESSIBLE (obj)) atk_object_set_name (obj, _("Question")); - + ret = gtk_dialog_run (GTK_DIALOG (msgbox)); gtk_widget_destroy (msgbox); - + return (ret == GTK_RESPONSE_YES); - } static void @@ -962,38 +742,38 @@ on_save_as_clicked (gpointer widget, { Application *app = data; GtkWidget *dialog; - + ensure_profile (app); - + set_busy (app->main_window, TRUE); - + dialog = gtk_file_chooser_dialog_new ("Save As", GTK_WINDOW (app->main_window), GTK_FILE_CHOOSER_ACTION_SAVE, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, NULL); - + gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT); gtk_window_set_modal (GTK_WINDOW (dialog), TRUE); - + set_busy (app->main_window, FALSE); - - retry: + +retry: if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) { GError *err = NULL; gchar *filename; - + filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog)); - + if (g_file_test (filename, G_FILE_TEST_EXISTS) && !overwrite_file (GTK_WINDOW (app->main_window), filename)) { g_free (filename); goto retry; } - + set_busy (dialog, TRUE); if (!profile_save (app->profile, filename, &err)) { @@ -1008,7 +788,7 @@ on_save_as_clicked (gpointer widget, set_busy (dialog, FALSE); g_free (filename); } - + gtk_widget_destroy (dialog); } @@ -1019,25 +799,16 @@ set_loaded_profile (Application *app, { g_return_if_fail (name != NULL); g_return_if_fail (profile != NULL); - - set_busy (app->main_window, TRUE); - + + collector_stop (app->collector); + delete_data (app); - - app->state = DISPLAYING; - - app->n_samples = profile_get_size (profile); - + app->profile = profile; - app->profile_from_file = TRUE; - - fill_lists (app); set_application_title (app, name); - - update_sensitivity (app); - - set_busy (app->main_window, FALSE); + + enter_display_mode (app); } static void @@ -1050,7 +821,7 @@ show_could_not_open (Application *app, filename, err->message); } - + static void on_open_clicked (gpointer widget, gpointer data) @@ -1061,29 +832,29 @@ on_open_clicked (gpointer widget, GtkWidget *dialog; set_busy (app->main_window, TRUE); - + dialog = gtk_file_chooser_dialog_new ("Open", GTK_WINDOW (app->main_window), GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL); - + gtk_window_set_modal (GTK_WINDOW (dialog), TRUE); set_busy (app->main_window, FALSE); - - retry: + +retry: if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) { GError *err = NULL; - + filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog)); set_busy (dialog, TRUE); profile = profile_load (filename, &err); - + if (!profile) { set_busy (dialog, FALSE); @@ -1091,11 +862,11 @@ on_open_clicked (gpointer widget, show_could_not_open (app, filename, err); g_error_free (err); g_free (filename); - + filename = NULL; goto retry; } - + set_busy (dialog, FALSE); } @@ -1105,14 +876,23 @@ on_open_clicked (gpointer widget, { g_assert (filename); set_loaded_profile (app, filename, profile); - + g_free (filename); } } static void -on_delete (GtkWidget *window) +on_delete (GtkWidget *window, + Application *app) { + /* Workaround for http://bugzilla.gnome.org/show_bug.cgi?id=317775 + * + * Without it, the read callbacks can fire _after_ gtk_main_quit() + * has been called and cause stuff to be called on destroyed widgets. + */ + while (gtk_main_iteration ()) + ; + gtk_main_quit (); } @@ -1138,7 +918,7 @@ expand_descendants_tree (Application *app) gtk_tree_model_get (model, &iter, OBJECT_TOTAL, &top_value, -1); - + while (all_paths && n_rows < max_rows) { GtkTreeIter best_iter; @@ -1154,31 +934,33 @@ expand_descendants_tree (Application *app) { GtkTreePath *path = list->data; GtkTreeIter iter; - double value; + g_assert (path != NULL); + if (gtk_tree_model_get_iter (model, &iter, path)) { + double value; gtk_tree_model_get (model, &iter, OBJECT_TOTAL, &value, -1); - } - if (value >= best_value) - { - best_value = value; - best_path = path; - gtk_tree_model_get_iter (model, &best_iter, path); + if (value >= best_value) + { + best_value = value; + best_path = path; + best_iter = iter; + } } } - gtk_tree_model_get_iter (model, &iter, best_path); - n_children = gtk_tree_model_iter_n_children (model, &best_iter); - + if (n_children && (best_value / top_value) > 0.04 && - (n_children + gtk_tree_path_get_depth (best_path)) / (double)max_rows < (best_value / top_value) ) + (n_children + gtk_tree_path_get_depth (best_path)) / + (double)max_rows < (best_value / top_value) ) { - gtk_tree_view_expand_row (GTK_TREE_VIEW (app->descendants_view), best_path, FALSE); + gtk_tree_view_expand_row ( + GTK_TREE_VIEW (app->descendants_view), best_path, FALSE); n_rows += n_children; if (gtk_tree_path_get_depth (best_path) < 4) @@ -1193,60 +975,239 @@ expand_descendants_tree (Application *app) path = gtk_tree_path_copy (path); gtk_tree_path_next (path); } + gtk_tree_path_free (path); } } all_paths = g_list_remove (all_paths, best_path); + + if (!all_paths && n_rows == 1) + { + /* Always expand at least once */ + gtk_tree_view_expand_row (GTK_TREE_VIEW (app->descendants_view), + best_path, FALSE); + } + gtk_tree_path_free (best_path); } for (list = all_paths; list != NULL; list = list->next) gtk_tree_path_free (list->data); - + g_list_free (all_paths); } static void +get_data (GtkTreeView *view, + GtkTreeIter *iter, + gchar **name, + double *self, + double *cumulative) +{ + char *dummy1; + double dummy2; + double dummy3; + + GtkTreeModel *model = gtk_tree_view_get_model (view); + gtk_tree_model_get ( + model, iter, + DESCENDANTS_NAME, name? name : &dummy1, + DESCENDANTS_SELF, self? self : &dummy2, + DESCENDANTS_CUMULATIVE, cumulative? cumulative : &dummy3, + -1); +} + +static int +get_indent (GtkTreePath *path) +{ + return 2 * (gtk_tree_path_get_depth (path) - 1); +} + +static void +compute_text_width (GtkTreeView *view, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data) +{ + int *width = data; + char *name; + + get_data (view, iter, &name, NULL, NULL); + + *width = MAX (g_utf8_strlen (name, -1) + get_indent (path), *width); + + g_free (name); +} + +typedef struct +{ + int max_width; + GString *text; +} AddTextInfo; + +static void +set_monospace (GtkWidget *widget) +{ + PangoFontDescription *desc = + pango_font_description_from_string ("monospace"); + + gtk_widget_modify_font (widget, desc); + + pango_font_description_free (desc); +} + +static void +add_text (GtkTreeView *view, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data) +{ + AddTextInfo *info = data; + char *name; + double self; + double cumulative; + int indent; + int i; + + get_data (view, iter, &name, &self, &cumulative); + + indent = get_indent (path); + + for (i = 0; i < indent; ++i) + g_string_append_c (info->text, ' '); + + g_string_append_printf (info->text, "%-*s %6.2f%% %6.2f%%\n", + info->max_width - indent, name, self, cumulative); + + g_free (name); +} + +static gboolean +update_screenshot_window_idle (gpointer data) +{ + Application *app = data; + GtkTextBuffer *text_buffer; + + if (!app->screenshot_window_visible) + return FALSE; + + text_buffer = + gtk_text_view_get_buffer (GTK_TEXT_VIEW (app->screenshot_textview)); + + gtk_text_buffer_set_text (text_buffer, "", -1); + + if (app->descendants) + { + AddTextInfo info; + + info.max_width = 0; + info.text = g_string_new (""); + + tree_view_foreach_visible (app->descendants_view, + compute_text_width, + &info.max_width); + + tree_view_foreach_visible (app->descendants_view, + add_text, + &info); + + gtk_text_buffer_set_text (text_buffer, info.text->str, -1); + + set_monospace (app->screenshot_textview); + + g_string_free (info.text, TRUE); + } + + app->update_screenshot_id = 0; + + if (app->screenshot_window_visible) + { + set_busy (app->screenshot_window, FALSE); + set_busy (app->screenshot_textview, FALSE); + } + + return FALSE; +} + +static void +update_screenshot_window (Application *app) +{ + /* We do this in an idle handler to deal with the case where + * someone presses Shift-RightArrow on the root of a huge + * profile. This causes a ton of 'expanded' notifications, + * each of which would cause us to traverse the tree and + * update the screenshot window. + */ + if (app->update_screenshot_id) + g_source_remove (app->update_screenshot_id); + + if (app->screenshot_window_visible) + { + /* don't swamp the X server with cursor change requests */ + if (!app->update_screenshot_id) + { + set_busy (app->screenshot_window, TRUE); + set_busy (app->screenshot_textview, TRUE); + } + } + + app->update_screenshot_id = g_idle_add ( + update_screenshot_window_idle, app); +} + +static void +on_descendants_row_expanded_or_collapsed (GtkTreeView *tree, + GtkTreeIter *iter, + GtkTreePath *path, + Application *app) +{ + update_screenshot_window (app); +} + +static void on_object_selection_changed (GtkTreeSelection *selection, - gpointer data) + gpointer data) { Application *app = data; set_busy (app->main_window, TRUE); - gdk_window_process_all_updates (); - + update_screenshot_window (app); + + if (!app->inhibit_forced_redraw) + gdk_window_process_all_updates (); /* Display updated selection */ + fill_descendants_tree (app); fill_callers_list (app); if (get_current_object (app)) expand_descendants_tree (app); - + set_busy (app->main_window, FALSE); } static void really_goto_object (Application *app, - ProfileObject *object) + char *object) { GtkTreeModel *profile_objects; GtkTreeIter iter; gboolean found = FALSE; - + profile_objects = gtk_tree_view_get_model (app->object_view); - + if (gtk_tree_model_get_iter_first (profile_objects, &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; @@ -1254,12 +1215,12 @@ really_goto_object (Application *app, } while (gtk_tree_model_iter_next (profile_objects, &iter)); } - + if (found) { GtkTreePath *path = gtk_tree_model_get_path (profile_objects, &iter); - + gtk_tree_view_set_cursor (app->object_view, path, 0, FALSE); } } @@ -1272,18 +1233,17 @@ 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; - + gtk_tree_model_get (model, &iter, column, &object, -1); - + if (!object) return; - + really_goto_object (app, object); - } static void @@ -1293,9 +1253,9 @@ on_descendants_row_activated (GtkTreeView *tree_view, gpointer data) { Application *app = data; - + goto_object (app, tree_view, path, DESCENDANTS_OBJECT); - + gtk_widget_grab_focus (GTK_WIDGET (app->descendants_view)); } @@ -1306,14 +1266,45 @@ on_callers_row_activated (GtkTreeView *tree_view, gpointer data) { Application *app = data; - + goto_object (app, tree_view, path, CALLERS_OBJECT); gtk_widget_grab_focus (GTK_WIDGET (app->callers_view)); } static void +on_screenshot_activated (GtkCheckMenuItem *menu_item, + Application *app) +{ + app->screenshot_window_visible = gtk_check_menu_item_get_active (menu_item); + + update_screenshot_window (app); + + update_sensitivity (app); +} + +static void +on_screenshot_window_delete (GtkWidget *window, + GdkEvent *event, + Application *app) +{ + app->screenshot_window_visible = FALSE; + + update_sensitivity (app); +} + +static void +on_screenshot_close_button_clicked (GtkWidget *widget, + Application *app) +{ + app->screenshot_window_visible = FALSE; + + update_sensitivity (app); +} + +static void set_sizes (GtkWindow *window, + GtkWindow *screenshot_window, GtkWidget *hpaned, GtkWidget *vpaned) { @@ -1322,19 +1313,123 @@ set_sizes (GtkWindow *window, GdkRectangle monitor; int width, height; GtkWidget *widget = GTK_WIDGET (window); - + screen = gtk_widget_get_screen (widget); monitor_num = gdk_screen_get_monitor_at_window (screen, widget->window); - + gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor); - + width = monitor.width * 3 / 4; height = monitor.height * 3 / 4; - + gtk_window_resize (window, width, height); - + gtk_paned_set_position (GTK_PANED (vpaned), height / 2); - gtk_paned_set_position (GTK_PANED (hpaned), width / 2); + gtk_paned_set_position (GTK_PANED (hpaned), width * 3 / 8); + + width = monitor.width * 5 / 8; + height = monitor.height * 5 / 8; + + gtk_window_resize (screenshot_window, width, height); +} + +#define GLADE_FILE DATADIR "/sysprof.glade" + +static void +gather_widgets (Application *app) +{ + typedef struct + { + void *location; + const char *name; + } WidgetInfo; + + const WidgetInfo widgets[] = + { + { &app->main_window, "main_window" }, + { &app->start_button, "start_button" }, + { &app->profile_button, "profile_button" }, + { &app->reset_button, "reset_button" }, + { &app->save_as_button, "save_as_button" }, + { &app->dummy_button, "dummy_button" }, + { &app->samples_label, "samples_label" }, + { &app->samples_hbox, "samples_hbox" }, + { &app->start_item, "start_item" }, + { &app->profile_item, "profile_item" }, + { &app->reset_item, "reset_item" }, + { &app->open_item, "open_item" }, + { &app->save_as_item, "save_as_item" }, + { &app->screenshot_item, "screenshot_item" }, + { &app->quit_item, "quit" }, + { &app->about_item, "about" }, + { &app->object_view, "object_view" }, + { &app->callers_view, "callers_view" }, + { &app->descendants_view, "descendants_view" }, + { &app->screenshot_window, "screenshot_window" }, + { &app->screenshot_textview, "screenshot_textview" }, + { &app->screenshot_close_button, "screenshot_close_button" }, + { &app->vpaned, "vpaned" }, + { &app->hpaned, "hpaned" }, + }; + + GladeXML *xml = glade_xml_new (GLADE_FILE, NULL, NULL); + int i; + + for (i = 0; i < G_N_ELEMENTS (widgets); ++i) + { + const WidgetInfo *info = &(widgets[i]); + + *(GtkWidget **)(info->location) = glade_xml_get_widget (xml, info->name); + + g_assert (GTK_IS_WIDGET (*(GtkWidget **)info->location)); + } + + g_object_unref (xml); +} + +static void +connect_signals (Application *app) +{ + typedef struct + { + gpointer object; + const char *signal; + gpointer callback; + gpointer data; + } SignalInfo; + + const SignalInfo signals[] = + { + { app->main_window, "delete_event", on_delete, NULL }, + { app->start_button, "toggled", on_start_toggled, app }, + { app->profile_button, "toggled", on_profile_toggled, app }, + { app->reset_button, "clicked", on_reset_clicked, app }, + { app->save_as_button, "clicked", on_save_as_clicked, app }, + { app->start_item, "activate", on_menu_item_activated, app->start_button }, + { app->profile_item, "activate", on_menu_item_activated, app->profile_button }, + { app->reset_item, "activate", on_reset_clicked, app }, + { app->open_item, "activate", on_open_clicked, app }, + { app->save_as_item, "activate", on_save_as_clicked, app }, + { app->screenshot_item, "activate", on_screenshot_activated, app }, + { app->quit_item, "activate", on_delete, NULL }, + { app->about_item, "activate", on_about_activated, app }, + { app->object_selection, "changed", on_object_selection_changed, app }, + { app->callers_view, "row-activated", on_callers_row_activated, app }, + { app->descendants_view, "row-activated", on_descendants_row_activated, app }, + { app->descendants_view, "row-expanded", on_descendants_row_expanded_or_collapsed, app }, + { app->descendants_view, "row-collapsed", on_descendants_row_expanded_or_collapsed, app }, + { app->screenshot_window, "delete_event", on_screenshot_window_delete, app }, + { app->screenshot_close_button, "clicked", on_screenshot_close_button_clicked, app }, + }; + + int i; + + for (i = 0; i < G_N_ELEMENTS (signals); ++i) + { + const SignalInfo *info = &(signals[i]); + + g_signal_connect (info->object, info->signal, info->callback, info->data); + } } static void @@ -1354,140 +1449,147 @@ set_shadows (void) } static void +set_icons (Application *app) +{ + const char *icon_files [] = { + PIXMAPDIR "/sysprof-icon-16.png", + PIXMAPDIR "/sysprof-icon-24.png", + PIXMAPDIR "/sysprof-icon-32.png", + PIXMAPDIR "/sysprof-icon-48.png", + NULL + }; + GList *pixbufs = NULL; + int i; + + for (i = 0; icon_files[i] != NULL; ++i) + { + const char *file = icon_files[i]; + GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file (file, NULL); + + if (pixbuf) + { + pixbufs = g_list_prepend (pixbufs, pixbuf); + + if (i == 3) /* 48 x 48 */ + app->icon = g_object_ref (pixbuf); + } + else + { + g_warning ("Could not open %s\n", file); + } + } + + gtk_window_set_icon_list (GTK_WINDOW (app->main_window), pixbufs); + + g_list_foreach (pixbufs, (GFunc)g_object_unref, NULL); + g_list_free (pixbufs); +} + +#define PCT_FORMAT "%.2f<span size='smaller'><span size='smaller'> </span>%%</span>" + +static gboolean build_gui (Application *app) { - GladeXML *xml; - GtkTreeSelection *selection; GtkTreeViewColumn *col; set_shadows (); - - xml = glade_xml_new (DATADIR "/sysprof.glade", NULL, NULL); - + + if (!g_file_test (GLADE_FILE, G_FILE_TEST_EXISTS)) + { + sorry (NULL, + "Sysprof was not compiled or installed correctly.\n" + "\n" + "Running \"make install\" may solve this problem.\n"); + + return FALSE; + } + + gather_widgets (app); + + g_assert (app->main_window); + /* Main Window */ - app->main_window = glade_xml_get_widget (xml, "main_window"); - app->icon = gdk_pixbuf_new_from_file (PIXMAPDIR "/sysprof-icon.png", NULL); + set_icons (app); - gtk_window_set_icon (GTK_WINDOW (app->main_window), app->icon); - - g_signal_connect (G_OBJECT (app->main_window), "delete_event", - G_CALLBACK (on_delete), NULL); - gtk_widget_realize (GTK_WIDGET (app->main_window)); - set_sizes (GTK_WINDOW (app->main_window), - glade_xml_get_widget (xml, "hpaned"), - glade_xml_get_widget (xml, "vpaned")); - + /* Tool items */ - - app->start_button = glade_xml_get_widget (xml, "start_button"); - app->profile_button = glade_xml_get_widget (xml, "profile_button"); - app->reset_button = glade_xml_get_widget (xml, "reset_button"); - app->save_as_button = glade_xml_get_widget (xml, "save_as_button"); - app->dummy_button = glade_xml_get_widget (xml, "dummy_button"); - gtk_toggle_tool_button_set_active ( GTK_TOGGLE_TOOL_BUTTON (app->profile_button), FALSE); - - g_signal_connect (G_OBJECT (app->start_button), "toggled", - G_CALLBACK (on_start_toggled), app); - - g_signal_connect (G_OBJECT (app->profile_button), "toggled", - G_CALLBACK (on_profile_toggled), app); - - g_signal_connect (G_OBJECT (app->reset_button), "clicked", - G_CALLBACK (on_reset_clicked), app); - - g_signal_connect (G_OBJECT (app->save_as_button), "clicked", - G_CALLBACK (on_save_as_clicked), app); - - - app->samples_label = glade_xml_get_widget (xml, "samples_label"); - - /* Menu items */ - app->start_item = glade_xml_get_widget (xml, "start_item"); - app->profile_item = glade_xml_get_widget (xml, "profile_item"); - app->reset_item = glade_xml_get_widget (xml, "reset_item"); - app->open_item = glade_xml_get_widget (xml, "open_item"); - app->save_as_item = glade_xml_get_widget (xml, "save_as_item"); - - g_assert (app->start_item); - g_assert (app->profile_item); - g_assert (app->save_as_item); - g_assert (app->open_item); - - g_signal_connect (G_OBJECT (app->start_item), "activate", - G_CALLBACK (on_menu_item_activated), app->start_button); - - g_signal_connect (G_OBJECT (app->profile_item), "activate", - G_CALLBACK (on_menu_item_activated), app->profile_button); - - g_signal_connect (G_OBJECT (app->reset_item), "activate", - G_CALLBACK (on_reset_clicked), app); - - g_signal_connect (G_OBJECT (app->open_item), "activate", - G_CALLBACK (on_open_clicked), app); - - g_signal_connect (G_OBJECT (app->save_as_item), "activate", - G_CALLBACK (on_save_as_clicked), app); - - g_signal_connect (G_OBJECT (glade_xml_get_widget (xml, "quit")), "activate", - G_CALLBACK (on_delete), NULL); - g_signal_connect (G_OBJECT (glade_xml_get_widget (xml, "about")), "activate", - G_CALLBACK (on_about_activated), app); - /* TreeViews */ - + /* object view */ - app->object_view = (GtkTreeView *)glade_xml_get_widget (xml, "object_view"); gtk_tree_view_set_enable_search (app->object_view, FALSE); - col = add_plain_text_column (app->object_view, _("Functions"), OBJECT_NAME); - add_double_format_column (app->object_view, _("Self"), OBJECT_SELF, "%.2f "); - add_double_format_column (app->object_view, _("Total"), OBJECT_TOTAL, "%.2f "); - selection = gtk_tree_view_get_selection (app->object_view); - g_signal_connect (selection, "changed", G_CALLBACK (on_object_selection_changed), app); + col = add_plain_text_column (app->object_view, _("Functions"), + OBJECT_NAME); + add_double_format_column (app->object_view, _("Self"), + OBJECT_SELF, PCT_FORMAT); + add_double_format_column (app->object_view, _("Total"), + OBJECT_TOTAL, PCT_FORMAT); + app->object_selection = gtk_tree_view_get_selection (app->object_view); gtk_tree_view_column_set_expand (col, TRUE); - + /* callers view */ - app->callers_view = (GtkTreeView *)glade_xml_get_widget (xml, "callers_view"); gtk_tree_view_set_enable_search (app->callers_view, FALSE); - col = add_plain_text_column (app->callers_view, _("Callers"), CALLERS_NAME); - add_double_format_column (app->callers_view, _("Self"), CALLERS_SELF, "%.2f "); - add_double_format_column (app->callers_view, _("Total"), CALLERS_TOTAL, "%.2f "); - g_signal_connect (app->callers_view, "row-activated", - G_CALLBACK (on_callers_row_activated), app); + col = add_plain_text_column (app->callers_view, _("Callers"), + CALLERS_NAME); + add_double_format_column (app->callers_view, _("Self"), + CALLERS_SELF, PCT_FORMAT); + add_double_format_column (app->callers_view, _("Total"), + CALLERS_TOTAL, PCT_FORMAT); gtk_tree_view_column_set_expand (col, TRUE); - + /* descendants view */ - app->descendants_view = (GtkTreeView *)glade_xml_get_widget (xml, "descendants_view"); gtk_tree_view_set_enable_search (app->descendants_view, FALSE); - col = add_plain_text_column (app->descendants_view, _("Descendants"), DESCENDANTS_NAME); - add_double_format_column (app->descendants_view, _("Self"), DESCENDANTS_SELF, "%.2f "); - add_double_format_column (app->descendants_view, _("Cumulative"), DESCENDANTS_NON_RECURSE, "%.2f "); - g_signal_connect (app->descendants_view, "row-activated", - G_CALLBACK (on_descendants_row_activated), app); + col = add_plain_text_column (app->descendants_view, _("Descendants"), + DESCENDANTS_NAME); + add_double_format_column (app->descendants_view, _("Self"), + DESCENDANTS_SELF, PCT_FORMAT); + add_double_format_column (app->descendants_view, _("Cumulative"), + DESCENDANTS_CUMULATIVE, PCT_FORMAT); gtk_tree_view_column_set_expand (col, TRUE); - - gtk_widget_grab_focus (GTK_WIDGET (app->object_view)); + + /* screenshot window */ + + /* set sizes */ + set_sizes (GTK_WINDOW (app->main_window), + GTK_WINDOW (app->screenshot_window), + app->hpaned, app->vpaned); + + /* hide/show widgets */ gtk_widget_show_all (app->main_window); gtk_widget_hide (app->dummy_button); - - /* Statusbar */ + gtk_widget_hide (app->screenshot_window); + + gtk_widget_grab_focus (GTK_WIDGET (app->object_view)); queue_show_samples (app); + + connect_signals (app); + + return TRUE; +} + +static void +on_new_sample (gboolean first_sample, + gpointer data) +{ + Application *app = data; + + if (app->state == PROFILING && first_sample) + update_sensitivity (app); + else + queue_show_samples (app); } static Application * application_new (void) { Application *app = g_new0 (Application, 1); - - app->stash = stack_stash_new (); - app->input_fd = -1; + + app->collector = collector_new (FALSE, on_new_sample, app); app->state = INITIAL; - g_get_current_time (&app->latest_reset); - return app; } @@ -1507,53 +1609,128 @@ load_file (gpointer data) Profile *profile; set_busy (app->main_window, TRUE); - + profile = profile_load (filename, &err); - - set_busy (app->main_window, FALSE); - + if (profile) { set_loaded_profile (app, filename, profile); + + gdk_window_process_all_updates (); + set_busy (app->main_window, FALSE); } else { + set_busy (app->main_window, FALSE); + show_could_not_open (app, filename, err); g_error_free (err); } + g_free (file_open_data); + return FALSE; } +static const char * +process_options (int argc, + char **argv) +{ + int i; + gboolean show_version = FALSE; + const char *filename = NULL; + + for (i = 1; i < argc; ++i) + { + char *option = argv[i]; + + if (strcmp (option, "--version") == 0) + { + show_version = TRUE; + } + else if (!filename) + { + filename = argv[i]; + } + } + + if (show_version) + { + g_print ("%s %s\n", APPLICATION_NAME, PACKAGE_VERSION); + + exit (1); + } + + return filename; +} + +static void +apply_workarounds (void) +{ + + /* Disable gslice, since it + * + * - confuses valgrind + * - caches too much memory + * - hides memory access bugs + * - is not faster than malloc + * + * Note that g_slice_set_config() is broken in some versions of + * GLib (and 'declared internal' according to Tim), so we use the + * environment variable instead. + */ + if (!getenv ("G_SLICE")) + putenv ("G_SLICE=always_malloc"); + + /* Accessibility prevents sysprof from working reliably, so + * disable it. Specifically, it + * + * - causes large amounts of time to be spent in sysprof itself + * whenever the label is updated. + * - sometimes hangs at shutdown + * - does long-running roundtrip requests that prevents + * reading the event buffers, resulting in lost events. + */ + putenv ("NO_GAIL=1"); + putenv ("NO_AT_BRIDGE=1"); +} + int -main (int argc, char **argv) +main (int argc, + char **argv) { Application *app; - + const char *filename; + + apply_workarounds(); + + filename = process_options (argc, argv); + gtk_init (&argc, &argv); - + app = application_new (); - -#if 0 - nice (-19); - g_timeout_add (10, on_timeout, app); -#endif - - build_gui (app); - + + if (!build_gui (app)) + return -1; + update_sensitivity (app); - if (argc > 1) + if (filename) { FileOpenData *file_open_data = g_new0 (FileOpenData, 1); - - file_open_data->filename = argv[1]; + file_open_data->filename = filename; file_open_data->app = app; - g_idle_add (load_file, file_open_data); + /* This has to run at G_PRIORITY_LOW because of bug 350517 + */ + g_idle_add_full (G_PRIORITY_LOW, load_file, file_open_data, NULL); } +#if 0 + g_idle_add (gtk_main_quit, NULL); +#endif + gtk_main (); - + return 0; } |