/* Sysprof -- Sampling, systemwide CPU profiler * Copyright 2004, Red Hat, Inc. * 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 * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include #include #include "footreestore.h" #include "treeviewutils.h" #include "profile.h" #include "collector.h" /* FIXME - not10 */ #define _(a) a #define APPLICATION_NAME "System Profiler" typedef struct Application Application; typedef enum { INITIAL, DISPLAYING, PROFILING } State; struct Application { Collector * collector; State state; GdkPixbuf * icon; GtkWidget * main_window; 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 * about_item; GtkWidget * quit_item; GtkWidget * scrolled_window; GtkTreeSelection * object_selection; GtkWidget * samples_label; GtkWidget * samples_hbox; char * loaded_profile; Profile * profile; int timeout_id; }; static void show_samples (Application *app) { char *label; int n_samples; switch (app->state) { case INITIAL: n_samples = 0; break; case PROFILING: n_samples = collector_get_n_samples (app->collector); break; case DISPLAYING: 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; } static void queue_show_samples (Application *app) { if (!app->timeout_id) app->timeout_id = g_timeout_add (225, show_samples_timeout, app); } static void update_sensitivity (Application *app) { gboolean sensitive_profile_button; gboolean sensitive_save_as_button; gboolean sensitive_start_button; gboolean sensitive_tree_views; gboolean sensitive_samples_hbox; gboolean sensitive_reset_button; GtkWidget *active_radio_button; gboolean has_samples; switch (app->state) { case INITIAL: sensitive_profile_button = FALSE; sensitive_save_as_button = FALSE; sensitive_start_button = TRUE; sensitive_reset_button = FALSE; sensitive_tree_views = FALSE; sensitive_samples_hbox = FALSE; active_radio_button = app->dummy_button; break; case PROFILING: 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_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_hbox = FALSE; active_radio_button = app->profile_button; break; default: g_assert_not_reached(); break; } 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); gtk_widget_set_sensitive (app->save_as_item, sensitive_save_as_button); /* "start" widgets */ gtk_widget_set_sensitive (GTK_WIDGET (app->start_button), 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 * to a click on the same button very well */ gtk_widget_set_sensitive (GTK_WIDGET (app->reset_button), sensitive_reset_button); gtk_widget_set_sensitive (GTK_WIDGET (app->reset_item), sensitive_reset_button); #endif gtk_widget_set_sensitive (GTK_WIDGET (app->descendants_view), sensitive_tree_views); gtk_widget_set_sensitive (GTK_WIDGET (app->samples_hbox), sensitive_samples_hbox); show_samples (app); } static void set_busy (GtkWidget *widget, gboolean busy) { GdkCursor *cursor; GdkWindow *window; if (busy) cursor = gdk_cursor_new (GDK_WATCH); else cursor = NULL; if (GTK_IS_TEXT_VIEW (widget)) window = gtk_text_view_get_window (GTK_TEXT_VIEW (widget), GTK_TEXT_WINDOW_TEXT); else window = widget->window; gdk_window_set_cursor (window, cursor); if (cursor) gdk_cursor_unref (cursor); gdk_flush(); } static void set_application_title (Application *app, const char * name) { char *new_name; if (name) 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), app->loaded_profile); } else { gtk_window_set_title (GTK_WINDOW (app->main_window), APPLICATION_NAME); } } static void delete_data (Application *app) { if (app->profile) { profile_free (app->profile); app->profile = NULL; gtk_tree_view_set_model (GTK_TREE_VIEW (app->descendants_view), NULL); } collector_reset (app->collector); set_application_title (app, NULL); } static void sorry (GtkWidget *parent_window, const gchar *format, ...) { 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, "%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 void on_menu_item_activated (GtkWidget *menu_item, GtkWidget *tool_button) { GtkToggleToolButton *button = GTK_TOGGLE_TOOL_BUTTON (tool_button); if (!gtk_toggle_tool_button_get_active (button)) gtk_toggle_tool_button_set_active (button, TRUE); } 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 (collector_start (app->collector, -1, &err)) { delete_data (app); app->state = PROFILING; } else { sorry (app->main_window, err->message); g_error_free (err); } update_sensitivity (app); } enum { DESCENDANTS_SELF, DESCENDANTS_CUMULATIVE, DESCENDANTS_NAME, DESCENDANTS_OBJECT }; static char * get_current_object (Application *app) { GtkTreeModel *model; GtkTreeIter selected; char *object; if (gtk_tree_selection_get_selected ( app->object_selection, &model, &selected)) { gtk_tree_model_get (model, &selected, DESCENDANTS_OBJECT, &object, -1); return object; } else { return NULL; } } static void set_row (FooTreeStore *store, GtkTreeIter *iter, const char *name, double self, double total, const char *object) { foo_tree_store_set (store, iter, DESCENDANTS_NAME, name, DESCENDANTS_SELF, self, DESCENDANTS_CUMULATIVE, total, DESCENDANTS_OBJECT, object, -1); } static void set_node (FooTreeStore *store, GtkTreeIter *iter, int size, ProfileDescendant *node) { char *escaped = g_markup_escape_text (node->name, -1); set_row (store, iter, escaped, node->self * 100 / (double)size, node->cumulative * 100 / (double)size, node->name); g_free (escaped); } static void add_node (FooTreeStore *store, int size, const GtkTreeIter *parent, ProfileDescendant *node) { GtkTreeIter iter; if (!node) return; foo_tree_store_insert (store, &iter, (GtkTreeIter *)parent, 0); set_node (store, &iter, size, node); add_node (store, size, parent, node->siblings); add_node (store, size, &iter, node->children); } static void add_callers (FooTreeStore *store, int size, GtkTreeIter *parent, ProfileCaller *callers) { GtkTreeIter iter; while (callers) { if (callers->name) { foo_tree_store_insert (store, &iter, parent, 0); foo_tree_store_set ( store, &iter, DESCENDANTS_NAME, callers->name, DESCENDANTS_SELF, 100.0 * callers->self / size, DESCENDANTS_CUMULATIVE, 100.0 * callers->total / size, #if 0 DESCENDANTS_SELF, (double)callers->self, DESCENDANTS_TOTAL, (double)callers->total, #endif DESCENDANTS_OBJECT, callers->name, -1); } callers = callers->next; } } static void fill_descendants_tree (Application *app) { FooTreeStore *store; store = foo_tree_store_new ( 4, G_TYPE_DOUBLE, G_TYPE_DOUBLE, G_TYPE_STRING, G_TYPE_POINTER); if (app->profile) { GList *objects = profile_get_objects (app->profile); GList *list; for (list = objects; list != NULL; list = list->next) { ProfileObject *object = list->data; if (object->name) { int size = profile_get_size (app->profile); GtkTreeIter iter, iter2; ProfileDescendant *descendants; ProfileCaller *callers; descendants = profile_create_descendants (app->profile, object->name); foo_tree_store_insert (store, &iter, NULL, 0); set_node (store, &iter, size, descendants); if (descendants->children) { #if 0 foo_tree_store_insert (store, &iter2, &iter, 0); set_row (store, &iter2, "Descendants", -1.0, -1.0, NULL); #endif add_node (store, size, &iter, descendants->children); profile_descendant_free (descendants); } if ((callers = profile_list_callers (app->profile, object->name))) { foo_tree_store_insert (store, &iter2, &iter, 0); set_row (store, &iter2, "Callers", -2.0, -2.0, NULL); add_callers (store, size, &iter2, callers); profile_caller_free (callers); } } } } tree_view_set_model_with_default_sort (app->descendants_view, GTK_TREE_MODEL (store), DESCENDANTS_CUMULATIVE, GTK_SORT_DESCENDING); g_object_unref (G_OBJECT (store)); gtk_tree_view_columns_autosize (app->descendants_view); } static void enter_display_mode (Application *app) { app->state = DISPLAYING; update_sensitivity (app); fill_descendants_tree (app); gtk_widget_grab_focus (GTK_WIDGET (app->descendants_view)); } static void ensure_profile (Application *app) { if (app->profile) return; collector_stop (app->collector); app->profile = collector_create_profile (app->collector); collector_reset (app->collector); enter_display_mode (app); } static void 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_property, APPLICATION_NAME, "copyright", "Copyright 2004-2009, S"OSLASH"ren Sandmann", "version", PACKAGE_VERSION, NULL); } static void on_profile_toggled (GtkWidget *widget, gpointer data) { Application *app = data; if (gtk_toggle_tool_button_get_active (GTK_TOGGLE_TOOL_BUTTON (app->profile_button))) { set_busy (app->main_window, TRUE); ensure_profile (app); set_busy (app->main_window, FALSE); } } static void 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); } static gboolean overwrite_file (GtkWindow *window, const char *filename) { GtkWidget *msgbox; 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, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, _("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 on_save_as_clicked (gpointer widget, gpointer data) { 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: 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)) { sorry (app->main_window, "Could not save %s: %s", filename, err->message); set_busy (dialog, FALSE); g_free (filename); goto retry; } set_application_title (app, filename); set_busy (dialog, FALSE); g_free (filename); } gtk_widget_destroy (dialog); } static void set_loaded_profile (Application *app, const char *name, Profile *profile) { g_return_if_fail (name != NULL); g_return_if_fail (profile != NULL); collector_stop (app->collector); delete_data (app); app->profile = profile; set_application_title (app, name); enter_display_mode (app); } static void show_could_not_open (Application *app, const char *filename, GError *err) { sorry (app->main_window, "Could not open %s: %s", filename, err->message); } static void on_open_clicked (gpointer widget, gpointer data) { Application *app = data; gchar *filename = NULL; Profile *profile = NULL; 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: 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); show_could_not_open (app, filename, err); g_error_free (err); g_free (filename); filename = NULL; goto retry; } set_busy (dialog, FALSE); } gtk_widget_destroy (dialog); if (profile) { g_assert (filename); set_loaded_profile (app, filename, profile); g_free (filename); } } static void 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 (); } static GtkTreePath * find_object (GtkTreeModel *model, gpointer object) { GtkTreeIter iter; gboolean found = FALSE; if (gtk_tree_model_get_iter_first (model, &iter)) { do { char *list_object; gtk_tree_model_get (model, &iter, DESCENDANTS_OBJECT, &list_object, -1); if (list_object == object) { found = TRUE; break; } } while (gtk_tree_model_iter_next (model, &iter)); } if (found) { GtkTreePath *path = gtk_tree_model_get_path (model, &iter); return path; } return NULL; } static void expand_descendants_tree (Application *app, gpointer object) { GtkTreeModel *model = gtk_tree_view_get_model (app->descendants_view); GtkTreeIter iter; GList *all_paths = NULL; int n_rows; int max_rows = 40; /* FIXME */ double top_value = 0.0; GtkTreePath *first_path; GtkTreePath *target; GdkRectangle rect; GList *list; GtkAdjustment *adj; if (!(first_path = find_object (model, object))) return; target = gtk_tree_path_copy (first_path); gtk_tree_view_set_cursor (app->descendants_view, target, NULL, FALSE); gtk_tree_view_collapse_all (app->descendants_view); all_paths = g_list_prepend (all_paths, first_path); n_rows = 1; gtk_tree_model_get_iter (model, &iter, first_path); gtk_tree_model_get (model, &iter, DESCENDANTS_CUMULATIVE, &top_value, -1); while (all_paths && n_rows < max_rows) { GtkTreeIter best_iter; GtkTreePath *best_path; double best_value; int n_children; int i; best_value = 0.0; best_path = NULL; for (list = all_paths; list != NULL; list = list->next) { GtkTreePath *path = list->data; GtkTreeIter iter; g_assert (path != NULL); if (gtk_tree_model_get_iter (model, &iter, path)) { double value; gtk_tree_model_get (model, &iter, DESCENDANTS_CUMULATIVE, &value, -1); if (value < 0) value = 100.0; if (value >= best_value) { best_value = value; best_path = path; best_iter = iter; } } } 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) ) { 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) { GtkTreePath *path = gtk_tree_path_copy (best_path); gtk_tree_path_down (path); for (i = 0; i < n_children; ++i) { all_paths = g_list_prepend (all_paths, path); 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); gtk_tree_view_get_cell_area (app->descendants_view, target, NULL, &rect); adj = gtk_scrolled_window_get_vadjustment (app->scrolled_window); gtk_tree_view_convert_bin_window_to_tree_coords ( app->descendants_view, 0, rect.y, NULL, &rect.y); gtk_adjustment_set_value (adj, rect.y - 18); g_list_free (all_paths); } static void on_descendants_row_activated (GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewColumn *column, gpointer data) { Application *app = data; gpointer object; gtk_widget_grab_focus (GTK_WIDGET (app->descendants_view)); if ((object = get_current_object (app))) expand_descendants_tree (app, object); } static void set_sizes (GtkWindow *window) { GdkScreen *screen; int monitor_num; 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 = 0.45 * (monitor.width); height = 0.9 * monitor.height; gtk_window_resize (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->quit_item, "quit" }, { &app->about_item, "about" }, { &app->descendants_view, "descendants_view" }, { &app->scrolled_window, "scrolled_window" }, }; 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->quit_item, "activate", on_delete, NULL }, { app->about_item, "activate", on_about_activated, app }, { app->descendants_view, "row-activated", on_descendants_row_activated, 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 set_shadows (void) { /* Get rid of motif out-bevels */ gtk_rc_parse_string ( "style \"blah\" " "{ " " GtkToolbar::shadow_type = none " " GtkMenuBar::shadow_type = none " " GtkMenuBar::internal_padding = 2 " "} " "widget \"*toolbar\" style : rc \"blah\"\n" "widget \"*menubar\" style : rc \"blah\"\n" ); } 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 %%" static gboolean build_gui (Application *app) { GtkTreeViewColumn *col; set_shadows (); 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 */ set_icons (app); gtk_widget_realize (GTK_WIDGET (app->main_window)); /* Tool items */ gtk_toggle_tool_button_set_active ( GTK_TOGGLE_TOOL_BUTTON (app->profile_button), FALSE); /* TreeViews */ /* descendants view */ gtk_tree_view_set_enable_search (app->descendants_view, FALSE); add_double_format_column (app->descendants_view, _("Self"), DESCENDANTS_SELF, PCT_FORMAT); add_double_format_column (app->descendants_view, _("Total"), DESCENDANTS_CUMULATIVE, PCT_FORMAT); col = add_plain_text_column (app->descendants_view, _("Name"), DESCENDANTS_NAME); gtk_tree_view_set_expander_column (app->descendants_view, col); app->object_selection = gtk_tree_view_get_selection (app->descendants_view); /* set sizes */ set_sizes (GTK_WINDOW (app->main_window)); /* hide/show widgets */ gtk_widget_show_all (app->main_window); gtk_widget_hide (app->dummy_button); 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->collector = collector_new (TRUE, on_new_sample, app); app->state = INITIAL; return app; } typedef struct { const char *filename; Application *app; } FileOpenData; static gboolean load_file (gpointer data) { FileOpenData *file_open_data = data; const char *filename = file_open_data->filename; Application *app = file_open_data->app; GError *err = NULL; Profile *profile; set_busy (app->main_window, TRUE); profile = profile_load (filename, &err); 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) { Application *app; const char *filename; apply_workarounds(); filename = process_options (argc, argv); gtk_init (&argc, &argv); app = application_new (); if (!build_gui (app)) return -1; update_sensitivity (app); if (filename) { FileOpenData *file_open_data = g_new0 (FileOpenData, 1); file_open_data->filename = filename; file_open_data->app = app; /* 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); } gtk_main (); return 0; }