[libdazzle] counters: add helper counter window for debugging



commit 393ac8e610b7b15d951c58ff5a32914d1ffa1fa8
Author: Christian Hergert <chergert redhat com>
Date:   Wed Jun 14 01:10:40 2017 -0700

    counters: add helper counter window for debugging
    
    This can be a useful tool for applications that want to display counters
    for the current process.

 src/dazzle.gresources.xml          |    1 +
 src/dazzle.h                       |    1 +
 src/meson.build                    |    2 +
 src/widgets/dzl-counters-window.c  |  231 ++++++++++++++++++++++++++++++++++++
 src/widgets/dzl-counters-window.h  |   49 ++++++++
 src/widgets/dzl-counters-window.ui |   60 +++++++++
 tests/meson.build                  |    6 +
 tests/test-counters-window.c       |   67 +++++++++++
 8 files changed, 417 insertions(+), 0 deletions(-)
---
diff --git a/src/dazzle.gresources.xml b/src/dazzle.gresources.xml
index b4a5dbb..5c152cb 100644
--- a/src/dazzle.gresources.xml
+++ b/src/dazzle.gresources.xml
@@ -1,6 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <gresources>
   <gresource prefix="/org/gnome/dazzle/ui">
+    <file compressed="true" preprocess="xml-stripblanks" 
alias="dzl-counters-window.ui">widgets/dzl-counters-window.ui</file>
     <file compressed="true" preprocess="xml-stripblanks" 
alias="dzl-simple-popover.ui">widgets/dzl-simple-popover.ui</file>
     <file compressed="true" preprocess="xml-stripblanks" 
alias="dzl-empty-state.ui">widgets/dzl-empty-state.ui</file>
     <file compressed="true" preprocess="xml-stripblanks" 
alias="dzl-pill-box.ui">widgets/dzl-pill-box.ui</file>
diff --git a/src/dazzle.h b/src/dazzle.h
index 473cdd8..6fe97c5 100644
--- a/src/dazzle.h
+++ b/src/dazzle.h
@@ -135,6 +135,7 @@ G_BEGIN_DECLS
 #include "widgets/dzl-box.h"
 #include "widgets/dzl-centering-bin.h"
 #include "widgets/dzl-column-layout.h"
+#include "widgets/dzl-counters-window.h"
 #include "widgets/dzl-elastic-bin.h"
 #include "widgets/dzl-empty-state.h"
 #include "widgets/dzl-entry-box.h"
diff --git a/src/meson.build b/src/meson.build
index 6f8cf91..7dbcc0d 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -153,6 +153,7 @@ libdazzle_public_headers = [
   'widgets/dzl-box.h',
   'widgets/dzl-centering-bin.h',
   'widgets/dzl-column-layout.h',
+  'widgets/dzl-counters-window.h',
   'widgets/dzl-elastic-bin.h',
   'widgets/dzl-empty-state.h',
   'widgets/dzl-entry-box.h',
@@ -297,6 +298,7 @@ libdazzle_public_sources = [
   'widgets/dzl-box.c',
   'widgets/dzl-centering-bin.c',
   'widgets/dzl-column-layout.c',
+  'widgets/dzl-counters-window.c',
   'widgets/dzl-elastic-bin.c',
   'widgets/dzl-empty-state.c',
   'widgets/dzl-entry-box.c',
diff --git a/src/widgets/dzl-counters-window.c b/src/widgets/dzl-counters-window.c
new file mode 100644
index 0000000..3b4db50
--- /dev/null
+++ b/src/widgets/dzl-counters-window.c
@@ -0,0 +1,231 @@
+/* dzl-counters-window.c
+ *
+ * Copyright (C) 2017 Christian Hergert <chergert redhat com>
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "dzl-counters-window"
+
+#include "dzl-counters-window.h"
+
+typedef struct
+{
+  GtkTreeView         *tree_view;
+  GtkTreeStore        *tree_store;
+  GtkTreeViewColumn   *value_column;
+  GtkCellRendererText *value_cell;
+
+  DzlCounterArena     *arena;
+
+  guint                update_source;
+} DzlCountersWindowPrivate;
+
+G_DEFINE_TYPE_WITH_PRIVATE (DzlCountersWindow, dzl_counters_window, GTK_TYPE_WINDOW)
+
+static void
+get_value_cell_data_func (GtkCellLayout   *cell_layout,
+                          GtkCellRenderer *cell,
+                          GtkTreeModel    *model,
+                          GtkTreeIter     *iter,
+                          gpointer         user_data)
+{
+  static PangoAttrList *attrs;
+  DzlCounter *counter = NULL;
+  g_autofree gchar *str = NULL;
+  gint64 value = 0;
+
+  if G_UNLIKELY (attrs == NULL)
+    {
+      attrs = pango_attr_list_new ();
+      pango_attr_list_insert (attrs, pango_attr_foreground_alpha_new (0.35 * G_MAXUSHORT));
+    }
+
+  gtk_tree_model_get (model, iter, 0, &counter, -1);
+
+  if (counter != NULL)
+    value = dzl_counter_get (counter);
+
+  str = g_strdup_printf ("%"G_GINT64_FORMAT, value);
+
+  g_object_set (cell,
+                "attributes", value == 0 ? attrs : NULL,
+                "text", str,
+                NULL);
+}
+
+static gboolean
+update_display (gpointer data)
+{
+  DzlCountersWindow *self = data;
+  DzlCountersWindowPrivate *priv = dzl_counters_window_get_instance_private (self);
+  GdkWindow *window;
+
+  g_assert (DZL_IS_COUNTERS_WINDOW (self));
+
+  window = gtk_tree_view_get_bin_window (priv->tree_view);
+  gdk_window_invalidate_rect (window, NULL, FALSE);
+
+  return G_SOURCE_CONTINUE;
+}
+
+static void
+dzl_counters_window_realize (GtkWidget *widget)
+{
+  DzlCountersWindow *self = (DzlCountersWindow *)widget;
+  DzlCountersWindowPrivate *priv = dzl_counters_window_get_instance_private (self);
+
+  g_assert (DZL_IS_COUNTERS_WINDOW (self));
+
+  priv->update_source =
+    g_timeout_add_seconds_full (G_PRIORITY_LOW, 1, update_display, self, NULL);
+
+  GTK_WIDGET_CLASS (dzl_counters_window_parent_class)->realize (widget);
+}
+
+static void
+dzl_counters_window_unrealize (GtkWidget *widget)
+{
+  DzlCountersWindow *self = (DzlCountersWindow *)widget;
+  DzlCountersWindowPrivate *priv = dzl_counters_window_get_instance_private (self);
+
+  g_assert (DZL_IS_COUNTERS_WINDOW (self));
+
+  if (priv->update_source != 0)
+    {
+      g_source_remove (priv->update_source);
+      priv->update_source = 0;
+    }
+
+  GTK_WIDGET_CLASS (dzl_counters_window_parent_class)->unrealize (widget);
+}
+
+static void
+dzl_counters_window_finalize (GObject *object)
+{
+  DzlCountersWindow *self = (DzlCountersWindow *)object;
+  DzlCountersWindowPrivate *priv = dzl_counters_window_get_instance_private (self);
+
+  g_clear_pointer (&priv->arena, dzl_counter_arena_unref);
+  g_clear_object (&priv->tree_store);
+
+  G_OBJECT_CLASS (dzl_counters_window_parent_class)->finalize (object);
+}
+
+static void
+dzl_counters_window_class_init (DzlCountersWindowClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+  object_class->finalize = dzl_counters_window_finalize;
+
+  widget_class->realize = dzl_counters_window_realize;
+  widget_class->unrealize = dzl_counters_window_unrealize;
+
+  gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/dazzle/ui/dzl-counters-window.ui");
+  gtk_widget_class_bind_template_child_private (widget_class, DzlCountersWindow, tree_view);
+  gtk_widget_class_bind_template_child_private (widget_class, DzlCountersWindow, value_cell);
+  gtk_widget_class_bind_template_child_private (widget_class, DzlCountersWindow, value_column);
+}
+
+static void
+dzl_counters_window_init (DzlCountersWindow *self)
+{
+  DzlCountersWindowPrivate *priv = dzl_counters_window_get_instance_private (self);
+
+  gtk_widget_init_template (GTK_WIDGET (self));
+
+  priv->tree_store = gtk_tree_store_new (4, G_TYPE_POINTER, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
+  gtk_tree_view_set_model (priv->tree_view, GTK_TREE_MODEL (priv->tree_store));
+
+  gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (priv->value_column),
+                                      GTK_CELL_RENDERER (priv->value_cell),
+                                      get_value_cell_data_func,
+                                      NULL, NULL);
+}
+
+static void
+foreach_counter_cb (DzlCounter *counter,
+                    gpointer    user_data)
+{
+  DzlCountersWindow *self = user_data;
+  DzlCountersWindowPrivate *priv = dzl_counters_window_get_instance_private (self);
+  GtkTreeIter iter;
+
+  g_assert (DZL_IS_COUNTERS_WINDOW (self));
+
+  gtk_tree_store_append (priv->tree_store, &iter, NULL);
+  gtk_tree_store_set (priv->tree_store, &iter,
+                      0, counter,
+                      1, counter->category,
+                      2, counter->name,
+                      3, counter->description,
+                      -1);
+}
+
+static void
+dzl_counters_window_reload (DzlCountersWindow *self)
+{
+  DzlCountersWindowPrivate *priv = dzl_counters_window_get_instance_private (self);
+
+  g_assert (DZL_IS_COUNTERS_WINDOW (self));
+
+  gtk_tree_store_clear (priv->tree_store);
+  if (priv->arena == NULL)
+    return;
+
+  dzl_counter_arena_foreach (priv->arena, foreach_counter_cb, self);
+}
+
+GtkWidget *
+dzl_counters_window_new (void)
+{
+  return g_object_new (DZL_TYPE_COUNTERS_WINDOW, NULL);
+}
+
+/**
+ * dzl_counters_window_get_arena:
+ * @self: a #DzlCountersWindow
+ *
+ * Gets the currently viewed arena, if any.
+ *
+ * Returns: (transfer none) (nullable): A #DzlCounterArena or %NULL.
+ */
+DzlCounterArena *
+dzl_counters_window_get_arena (DzlCountersWindow *self)
+{
+  DzlCountersWindowPrivate *priv = dzl_counters_window_get_instance_private (self);
+
+  g_return_val_if_fail (DZL_IS_COUNTERS_WINDOW (self), NULL);
+
+  return priv->arena;
+}
+
+void
+dzl_counters_window_set_arena (DzlCountersWindow *self,
+                               DzlCounterArena   *arena)
+{
+  DzlCountersWindowPrivate *priv = dzl_counters_window_get_instance_private (self);
+
+  g_return_if_fail (DZL_IS_COUNTERS_WINDOW (self));
+
+  if (arena != priv->arena)
+    {
+      g_clear_pointer (&priv->arena, dzl_counter_arena_unref);
+      if (arena != NULL)
+        priv->arena = dzl_counter_arena_ref (arena);
+      dzl_counters_window_reload (self);
+    }
+}
diff --git a/src/widgets/dzl-counters-window.h b/src/widgets/dzl-counters-window.h
new file mode 100644
index 0000000..67fc825
--- /dev/null
+++ b/src/widgets/dzl-counters-window.h
@@ -0,0 +1,49 @@
+/* dzl-counters-window.h
+ *
+ * Copyright (C) 2017 Christian Hergert <chergert redhat com>
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef DZL_COUNTERS_WINDOW_H
+#define DZL_COUNTERS_WINDOW_H
+
+#include <gtk/gtk.h>
+
+#include "util/dzl-counter.h"
+
+G_BEGIN_DECLS
+
+#define DZL_TYPE_COUNTERS_WINDOW (dzl_counters_window_get_type())
+
+G_DECLARE_DERIVABLE_TYPE (DzlCountersWindow, dzl_counters_window, DZL, COUNTERS_WINDOW, GtkWindow)
+
+struct _DzlCountersWindowClass
+{
+  GtkWindowClass parent_class;
+
+  gpointer _reserved1;
+  gpointer _reserved2;
+  gpointer _reserved3;
+  gpointer _reserved4;
+};
+
+GtkWidget       *dzl_counters_window_new       (void);
+DzlCounterArena *dzl_counters_window_get_arena (DzlCountersWindow *self);
+void             dzl_counters_window_set_arena (DzlCountersWindow *self,
+                                                DzlCounterArena   *arena);
+
+G_END_DECLS
+
+#endif /* DZL_COUNTERS_WINDOW_H */
diff --git a/src/widgets/dzl-counters-window.ui b/src/widgets/dzl-counters-window.ui
new file mode 100644
index 0000000..7a6f5c4
--- /dev/null
+++ b/src/widgets/dzl-counters-window.ui
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <template class="DzlCountersWindow" parent="GtkWindow">
+    <child>
+      <object class="GtkScrolledWindow">
+        <property name="expand">true</property>
+        <property name="visible">true</property>
+        <child>
+          <object class="GtkTreeView" id="tree_view">
+            <property name="visible">true</property>
+            <child>
+              <object class="GtkTreeViewColumn">
+                <property name="title" translatable="yes">Category</property>
+                <child>
+                  <object class="GtkCellRendererText"/>
+                  <attributes>
+                    <attribute name="text">1</attribute>
+                  </attributes>
+                </child>
+              </object>
+            </child>
+            <child>
+              <object class="GtkTreeViewColumn">
+                <property name="title" translatable="yes">Name</property>
+                <child>
+                  <object class="GtkCellRendererText"/>
+                  <attributes>
+                    <attribute name="text">2</attribute>
+                  </attributes>
+                </child>
+              </object>
+            </child>
+            <child>
+              <object class="GtkTreeViewColumn" id="value_column">
+                <property name="expand">false</property>
+                <property name="title" translatable="yes">Value</property>
+                <child>
+                  <object class="GtkCellRendererText" id="value_cell">
+                  </object>
+                </child>
+              </object>
+            </child>
+            <child>
+              <object class="GtkTreeViewColumn">
+                <property name="expand">true</property>
+                <property name="title" translatable="yes">Description</property>
+                <child>
+                  <object class="GtkCellRendererText"/>
+                  <attributes>
+                    <attribute name="text">3</attribute>
+                  </attributes>
+                </child>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/tests/meson.build b/tests/meson.build
index 7f40618..d7e4871 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -265,4 +265,10 @@ test_ring = executable('test-ring', 'test-ring.c',
   dependencies: libdazzle_deps + [libdazzle_dep],
 )
 
+test_counters_window = executable('test-counters-window', 'test-counters-window.c',
+        c_args: test_cflags,
+     link_args: test_link_args,
+  dependencies: libdazzle_deps + [libdazzle_dep],
+)
+
 endif
diff --git a/tests/test-counters-window.c b/tests/test-counters-window.c
new file mode 100644
index 0000000..88f4595
--- /dev/null
+++ b/tests/test-counters-window.c
@@ -0,0 +1,67 @@
+#include <dazzle.h>
+#include <stdlib.h>
+#include <errno.h>
+
+static gboolean
+int_parse_with_range (gint        *value,
+                      gint         lower,
+                      gint         upper,
+                      const gchar *str)
+{
+  gint64 v64;
+
+  g_assert (value);
+  g_assert (lower <= upper);
+
+  v64 = g_ascii_strtoll (str, NULL, 10);
+
+  if (((v64 == G_MININT64) || (v64 == G_MAXINT64)) && (errno == ERANGE))
+    return FALSE;
+
+  if ((v64 < lower) || (v64 > upper))
+    return FALSE;
+
+  *value = (gint)v64;
+
+  return TRUE;
+}
+
+gint
+main (gint argc,
+      gchar *argv[])
+{
+  GtkWidget *window;
+  DzlCounterArena *arena;
+  gint pid;
+
+  gtk_init (&argc, &argv);
+
+  if (argc < 2)
+    {
+      g_printerr ("usage: %s [PID]\n", argv[0]);
+      return EXIT_FAILURE;
+    }
+
+  if (!int_parse_with_range (&pid, 1, G_MAXUSHORT, argv[1]))
+    {
+      g_printerr ("usage: %s [PID]\n", argv[0]);
+      return EXIT_FAILURE;
+    }
+
+  arena = dzl_counter_arena_new_for_pid (pid);
+
+  if (arena == NULL)
+    {
+      g_printerr ("Failed to access counters for process %u.\n", pid);
+      return EXIT_FAILURE;
+    }
+
+  window = dzl_counters_window_new ();
+  dzl_counters_window_set_arena (DZL_COUNTERS_WINDOW (window), arena);
+  g_signal_connect (window, "delete-event", gtk_main_quit, NULL);
+  gtk_window_present (GTK_WINDOW (window));
+
+  gtk_main ();
+
+  return EXIT_SUCCESS;
+}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]