summaryrefslogtreecommitdiff
path: root/sysprof.c
diff options
context:
space:
mode:
Diffstat (limited to 'sysprof.c')
-rw-r--r--sysprof.c1483
1 files changed, 830 insertions, 653 deletions
diff --git a/sysprof.c b/sysprof.c
index 7024497..cc676a9 100644
--- a/sysprof.c
+++ b/sysprof.c
@@ -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;
}