inspector: Add minimal signal tracing

Add rudimentary signal tracing. The signals tab can now count signal
emissions for all signals of the current object.
This commit is contained in:
Matthias Clasen 2014-05-10 10:40:38 -04:00
parent a0cae6957e
commit 975677872f
3 changed files with 322 additions and 61 deletions

View File

@ -21,26 +21,30 @@
enum
{
COLUMN_ENABLED,
COLUMN_NAME,
COLUMN_CLASS,
COLUMN_CONNECTED
COLUMN_CONNECTED,
COLUMN_COUNT,
COLUMN_NO_HOOKS,
COLUMN_SIGNAL_ID,
COLUMN_HOOK_ID
};
struct _GtkInspectorSignalsListPrivate
{
GtkWidget *view;
GtkListStore *model;
GtkTextBuffer *text;
GtkWidget *log_win;
GtkWidget *trace_button;
GtkTreeViewColumn *count_column;
GtkCellRenderer *count_renderer;
GObject *object;
GHashTable *iters;
gboolean tracing;
};
G_DEFINE_TYPE_WITH_PRIVATE (GtkInspectorSignalsList, gtk_inspector_signals_list, GTK_TYPE_BOX)
static void
gtk_inspector_signals_list_init (GtkInspectorSignalsList *sl)
{
sl->priv = gtk_inspector_signals_list_get_instance_private (sl);
gtk_widget_init_template (GTK_WIDGET (sl));
}
G_DEFINE_TYPE_WITH_PRIVATE (GtkInspectorSignalsList, gtk_inspector_signals_list, GTK_TYPE_PANED)
static GType *
get_types (GObject *object, guint *length)
@ -92,11 +96,16 @@ add_signals (GtkInspectorSignalsList *sl,
has_handler = g_signal_has_handler_pending (object, ids[i], 0, TRUE);
gtk_list_store_append (sl->priv->model, &iter);
gtk_list_store_set (sl->priv->model, &iter,
COLUMN_ENABLED, FALSE,
COLUMN_NAME, query.signal_name,
COLUMN_CLASS, g_type_name (type),
COLUMN_CONNECTED, has_handler ? _("Yes") : "",
COLUMN_COUNT, 0,
COLUMN_NO_HOOKS, (query.signal_flags & G_SIGNAL_NO_HOOKS) != 0,
COLUMN_SIGNAL_ID, ids[i],
COLUMN_HOOK_ID, 0,
-1);
g_hash_table_insert (sl->priv->iters,
GINT_TO_POINTER (ids[i]), gtk_tree_iter_copy (&iter));
}
g_free (ids);
}
@ -115,13 +124,200 @@ read_signals_from_object (GtkInspectorSignalsList *sl,
g_free (types);
}
static void stop_tracing (GtkInspectorSignalsList *sl);
void
gtk_inspector_signals_list_set_object (GtkInspectorSignalsList *sl,
GObject *object)
{
gtk_list_store_clear (sl->priv->model);
if (sl->priv->object == object)
return;
read_signals_from_object (sl, object);
stop_tracing (sl);
gtk_list_store_clear (sl->priv->model);
g_hash_table_remove_all (sl->priv->iters);
sl->priv->object = object;
if (object)
read_signals_from_object (sl, object);
}
static void
render_count (GtkTreeViewColumn *column,
GtkCellRenderer *renderer,
GtkTreeModel *model,
GtkTreeIter *iter,
gpointer data)
{
gint count;
gboolean no_hooks;
gchar text[100];
gtk_tree_model_get (model, iter,
COLUMN_COUNT, &count,
COLUMN_NO_HOOKS, &no_hooks,
-1);
if (no_hooks)
{
g_object_set (renderer, "markup", "<i>(untraceable)</i>", NULL);
}
else if (count != 0)
{
g_snprintf (text, 100, "%d", count);
g_object_set (renderer, "text", text, NULL);
}
else
g_object_set (renderer, "text", "", NULL);
}
static void
gtk_inspector_signals_list_init (GtkInspectorSignalsList *sl)
{
sl->priv = gtk_inspector_signals_list_get_instance_private (sl);
gtk_widget_init_template (GTK_WIDGET (sl));
gtk_tree_view_column_set_cell_data_func (sl->priv->count_column,
sl->priv->count_renderer,
render_count,
NULL, NULL);
sl->priv->iters = g_hash_table_new_full (g_direct_hash,
g_direct_equal,
NULL,
(GDestroyNotify) gtk_tree_iter_free);
}
static gboolean
trace_hook (GSignalInvocationHint *ihint,
guint n_param_values,
const GValue *param_values,
gpointer data)
{
GtkInspectorSignalsList *sl = data;
GObject *object;
object = g_value_get_object (param_values);
if (object == sl->priv->object)
{
gint count;
GtkTreeIter *iter;
iter = (GtkTreeIter *)g_hash_table_lookup (sl->priv->iters, GINT_TO_POINTER (ihint->signal_id));
gtk_tree_model_get (GTK_TREE_MODEL (sl->priv->model), iter, COLUMN_COUNT, &count, -1);
gtk_list_store_set (sl->priv->model, iter, COLUMN_COUNT, count + 1, -1);
}
return TRUE;
}
static gboolean
start_tracing_cb (GtkTreeModel *model,
GtkTreePath *path,
GtkTreeIter *iter,
gpointer data)
{
GtkInspectorSignalsList *sl = data;
guint signal_id;
gulong hook_id;
gboolean no_hooks;
gtk_tree_model_get (model, iter,
COLUMN_SIGNAL_ID, &signal_id,
COLUMN_HOOK_ID, &hook_id,
COLUMN_NO_HOOKS, &no_hooks,
-1);
g_assert (signal_id != 0);
g_assert (hook_id == 0);
if (!no_hooks)
{
hook_id = g_signal_add_emission_hook (signal_id, 0, trace_hook, sl, NULL);
gtk_list_store_set (GTK_LIST_STORE (model), iter,
COLUMN_COUNT, 0,
COLUMN_HOOK_ID, hook_id,
-1);
}
return FALSE;
}
static gboolean
stop_tracing_cb (GtkTreeModel *model,
GtkTreePath *path,
GtkTreeIter *iter,
gpointer data)
{
guint signal_id;
gulong hook_id;
gtk_tree_model_get (model, iter,
COLUMN_SIGNAL_ID, &signal_id,
COLUMN_HOOK_ID, &hook_id,
-1);
g_assert (signal_id != 0);
if (hook_id != 0)
{
g_signal_remove_emission_hook (signal_id, hook_id);
gtk_list_store_set (GTK_LIST_STORE (model), iter,
COLUMN_HOOK_ID, 0,
-1);
}
return FALSE;
}
static void
start_tracing (GtkInspectorSignalsList *sl)
{
sl->priv->tracing = TRUE;
gtk_tree_model_foreach (GTK_TREE_MODEL (sl->priv->model), start_tracing_cb, sl);
}
static void
stop_tracing (GtkInspectorSignalsList *sl)
{
sl->priv->tracing = FALSE;
gtk_tree_model_foreach (GTK_TREE_MODEL (sl->priv->model), stop_tracing_cb, sl);
gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (sl->priv->trace_button), FALSE);
}
static void
toggle_tracing (GtkToggleToolButton *button, GtkInspectorSignalsList *sl)
{
if (gtk_toggle_tool_button_get_active (button) == sl->priv->tracing)
return;
//gtk_widget_show (sl->priv->log_win);
if (gtk_toggle_tool_button_get_active (button))
start_tracing (sl);
else
stop_tracing (sl);
}
static gboolean
clear_log_cb (GtkTreeModel *model,
GtkTreePath *path,
GtkTreeIter *iter,
gpointer data)
{
gtk_list_store_set (GTK_LIST_STORE (model), iter, COLUMN_COUNT, 0, -1);
return FALSE;
}
static void
clear_log (GtkToolButton *button, GtkInspectorSignalsList *sl)
{
gtk_text_buffer_set_text (sl->priv->text, "", -1);
gtk_tree_model_foreach (GTK_TREE_MODEL (sl->priv->model), clear_log_cb, sl);
}
static void
@ -132,6 +328,13 @@ gtk_inspector_signals_list_class_init (GtkInspectorSignalsListClass *klass)
gtk_widget_class_set_template_from_resource (widget_class, "/org/gtk/inspector/signals-list.ui");
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorSignalsList, view);
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorSignalsList, model);
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorSignalsList, text);
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorSignalsList, log_win);
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorSignalsList, count_column);
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorSignalsList, count_renderer);
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorSignalsList, trace_button);
gtk_widget_class_bind_template_callback (widget_class, toggle_tracing);
gtk_widget_class_bind_template_callback (widget_class, clear_log);
}
GtkWidget *

View File

@ -32,13 +32,13 @@ typedef struct _GtkInspectorSignalsListPrivate GtkInspectorSignalsListPrivate;
typedef struct _GtkInspectorSignalsList
{
GtkBox parent;
GtkPaned parent;
GtkInspectorSignalsListPrivate *priv;
} GtkInspectorSignalsList;
typedef struct _GtkInspectorSignalsListClass
{
GtkBoxClass parent;
GtkPanedClass parent;
} GtkInspectorSignalsListClass;
G_BEGIN_DECLS

View File

@ -2,64 +2,122 @@
<interface domain="gtk30">
<object class="GtkListStore" id="model">
<columns>
<column type="gchararray"/>
<column type="gchararray"/>
<column type="gchararray"/>
<column type="gint"/>
<column type="gboolean"/>
<column type="gchararray"/>
<column type="gchararray"/>
<column type="gchararray"/>
<column type="guint"/>
<column type="gulong"/>
</columns>
</object>
<template class="GtkInspectorSignalsList" parent="GtkBox">
<object class="GtkTextBuffer" id="text">
</object>
<template class="GtkInspectorSignalsList" parent="GtkPaned">
<property name="orientation">vertical</property>
<child>
<object class="GtkScrolledWindow">
<object class="GtkBox">
<property name="orientation">vertical</property>
<property name="visible">True</property>
<child>
<object class="GtkToolbar" id="toolbar">
<property name="visible">True</property>
<property name="icon-size">small-toolbar</property>
<child>
<object class="GtkToggleToolButton" id="trace_button">
<property name="visible">True</property>
<property name="icon-name">media-record</property>
<property name="tooltip-text" translatable="yes">Trace signal emissions on this object</property>
<signal name="toggled" handler="toggle_tracing"/>
</object>
</child>
<child>
<object class="GtkToolButton" id="clear_button">
<property name="visible">True</property>
<property name="icon-name">edit-clear</property>
<property name="tooltip-text" translatable="yes">Clear log</property>
<signal name="clicked" handler="clear_log"/>
</object>
</child>
</object>
</child>
<child>
<object class="GtkScrolledWindow">
<property name="visible">True</property>
<property name="expand">True</property>
<property name="hscrollbar-policy">automatic</property>
<property name="vscrollbar-policy">always</property>
<property name="shadow-type">in</property>
<child>
<object class="GtkTreeView" id="view">
<property name="visible">True</property>
<property name="model">model</property>
<child>
<object class="GtkTreeViewColumn">
<property name="title" translatable="yes">Name</property>
<child>
<object class="GtkCellRendererText">
<property name="scale">0.8</property>
</object>
<attributes>
<attribute name="text">0</attribute>
</attributes>
</child>
</object>
</child>
<child>
<object class="GtkTreeViewColumn">
<property name="title" translatable="yes">Connected</property>
<child>
<object class="GtkCellRendererText">
<property name="scale">0.8</property>
</object>
<attributes>
<attribute name="text">2</attribute>
</attributes>
</child>
</object>
</child>
<child>
<object class="GtkTreeViewColumn" id="count_column">
<property name="title" translatable="yes">Count</property>
<child>
<object class="GtkCellRendererText" id="count_renderer">
<property name="scale">0.8</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkTreeViewColumn">
<property name="title" translatable="yes">Defined At</property>
<child>
<object class="GtkCellRendererText">
<property name="scale">0.8</property>
</object>
<attributes>
<attribute name="text">1</attribute>
</attributes>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="GtkScrolledWindow" id="log_win">
<property name="expand">True</property>
<property name="hscrollbar-policy">automatic</property>
<property name="vscrollbar-policy">always</property>
<property name="shadow-type">in</property>
<child>
<object class="GtkTreeView" id="view">
<object class="GtkTextView">
<property name="visible">True</property>
<property name="model">model</property>
<child>
<object class="GtkTreeViewColumn">
<property name="title" translatable="yes">Name</property>
<child>
<object class="GtkCellRendererText">
<property name="scale">0.8</property>
</object>
<attributes>
<attribute name="text">1</attribute>
</attributes>
</child>
</object>
</child>
<child>
<object class="GtkTreeViewColumn">
<property name="title" translatable="yes">Defined At</property>
<child>
<object class="GtkCellRendererText">
<property name="scale">0.8</property>
</object>
<attributes>
<attribute name="text">2</attribute>
</attributes>
</child>
</object>
</child>
<child>
<object class="GtkTreeViewColumn">
<property name="title" translatable="yes">Connected</property>
<child>
<object class="GtkCellRendererText">
<property name="scale">0.8</property>
</object>
<attributes>
<attribute name="text">3</attribute>
</attributes>
</child>
</object>
</child>
<property name="buffer">text</property>
<property name="editable">False</property>
</object>
</child>
</object>