[telepathy-gabble/master] initial implementation of the debug interface

Dafydd Harries daf at rhydd.org
Wed Jun 3 06:10:51 PDT 2009


---
 src/Makefile.am             |    2 +
 src/debugger.c              |  235 +++++++++++++++++++++++++++++++++++++++++++
 src/debugger.h              |   82 +++++++++++++++
 src/gabble.c                |   26 +++++
 tests/twisted/Makefile.am   |    1 +
 tests/twisted/test-debug.py |   52 ++++++++++
 6 files changed, 398 insertions(+), 0 deletions(-)
 create mode 100644 src/debugger.c
 create mode 100644 src/debugger.h
 create mode 100644 tests/twisted/test-debug.py

diff --git a/src/Makefile.am b/src/Makefile.am
index e29ca75..f3c10b2 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -47,6 +47,8 @@ libgabble_convenience_la_SOURCES = \
     connection-manager.c \
     debug.h \
     debug.c \
+    debugger.h \
+    debugger.c \
     disco.h \
     disco.c \
     error.c \
diff --git a/src/debugger.c b/src/debugger.c
new file mode 100644
index 0000000..31b2ffd
--- /dev/null
+++ b/src/debugger.c
@@ -0,0 +1,235 @@
+/*
+ * debugger.h - Telepathy debug interface implementation
+ * Copyright (C) 2009 Collabora Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "debugger.h"
+#include "config.h"
+
+#include <telepathy-glib/dbus.h>
+
+#include "extensions/extensions.h"
+#include "gabble-signals-marshal.h"
+
+static GabbleDebugger *singleton = NULL;
+
+static void
+debug_iface_init (gpointer g_iface, gpointer iface_data);
+
+G_DEFINE_TYPE_WITH_CODE (GabbleDebugger, gabble_debugger, G_TYPE_OBJECT,
+    G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_DBUS_PROPERTIES,
+        tp_dbus_properties_mixin_iface_init);
+    G_IMPLEMENT_INTERFACE (GABBLE_TYPE_SVC_DEBUG, debug_iface_init));
+
+/* properties */
+enum
+{
+  PROP_ENABLED = 1,
+  NUM_PROPERTIES
+};
+
+static GabbleDebugMessage *
+debug_message_new (gdouble timestamp, const gchar *string)
+{
+  GabbleDebugMessage *msg;
+
+  msg = g_slice_new0 (GabbleDebugMessage);
+  msg->timestamp = timestamp;
+  msg->string = g_strdup (string);
+  return msg;
+}
+
+static void
+debug_message_free (GabbleDebugMessage *msg)
+{
+  g_free (msg->string);
+  g_slice_free (GabbleDebugMessage, msg);
+}
+
+static void
+gabble_debugger_get_property (GObject *object, guint property_id,
+                              GValue *value, GParamSpec *pspec)
+{
+  GabbleDebugger *self = GABBLE_DEBUGGER (object);
+
+  switch (property_id)
+    {
+      case PROP_ENABLED:
+        g_value_set_boolean (value, self->enabled);
+        break;
+
+      default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+    }
+}
+
+static void
+gabble_debugger_set_property (GObject *object, guint property_id,
+                              const GValue *value, GParamSpec *pspec)
+{
+  GabbleDebugger *self = GABBLE_DEBUGGER (object);
+
+  switch (property_id)
+    {
+      case PROP_ENABLED:
+        self->enabled = g_value_get_boolean (value);
+        break;
+
+     default:
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+  }
+}
+
+static void
+gabble_debugger_finalize (GObject *object)
+{
+  GabbleDebugger *self = GABBLE_DEBUGGER (object);
+
+  g_queue_foreach (self->messages, (GFunc) debug_message_free, NULL);
+  g_queue_free (self->messages);
+  self->messages = NULL;
+
+  G_OBJECT_CLASS (gabble_debugger_parent_class)->finalize (object);
+}
+
+static void
+gabble_debugger_class_init (GabbleDebuggerClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  static TpDBusPropertiesMixinPropImpl debug_props[] = {
+      { "Enabled", "enabled", "enabled" },
+      { NULL }
+  };
+  static TpDBusPropertiesMixinIfaceImpl prop_interfaces[] = {
+      { GABBLE_IFACE_DEBUG,
+        tp_dbus_properties_mixin_getter_gobject_properties,
+        tp_dbus_properties_mixin_setter_gobject_properties,
+        debug_props,
+      },
+      { NULL }
+  };
+
+  object_class->get_property = gabble_debugger_get_property;
+  object_class->set_property = gabble_debugger_set_property;
+  object_class->finalize = gabble_debugger_finalize;
+
+  g_object_class_install_property (object_class, PROP_ENABLED,
+      g_param_spec_boolean ("enabled", "Enabled?",
+          "True if the new-debug-message signal is enabled.",
+          FALSE,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  klass->dbus_props_class.interfaces = prop_interfaces;
+  tp_dbus_properties_mixin_class_init (object_class,
+      G_STRUCT_OFFSET (GabbleDebuggerClass, dbus_props_class));
+}
+
+static void
+get_messages (GabbleSvcDebug *self, DBusGMethodInvocation *context)
+{
+  GabbleDebugger *dbg = GABBLE_DEBUGGER (self);
+  GPtrArray *messages;
+  static GType struct_type = 0;
+  GList *i;
+  guint j;
+
+  if (G_UNLIKELY (struct_type == 0))
+    {
+      struct_type = dbus_g_type_get_struct (
+          "GValueArray", G_TYPE_DOUBLE, G_TYPE_STRING, G_TYPE_INVALID);
+    }
+
+  messages = g_ptr_array_sized_new (g_queue_get_length (dbg->messages));
+
+  for (i = dbg->messages->head; i; i = i->next)
+    {
+      GValue gvalue = { 0 };
+      GabbleDebugMessage *message = (GabbleDebugMessage *) i->data;
+
+      g_value_init (&gvalue, struct_type);
+      g_value_take_boxed (&gvalue,
+          dbus_g_type_specialized_construct (struct_type));
+      dbus_g_type_struct_set (&gvalue,
+          0, message->timestamp,
+          1, message->string,
+          G_MAXUINT);
+      g_ptr_array_add (messages, g_value_get_boxed (&gvalue));
+    }
+
+  gabble_svc_debug_return_from_get_messages (context, messages);
+
+  for (j = 0; j < messages->len; j++)
+    g_boxed_free (struct_type, messages->pdata[j]);
+
+  g_ptr_array_free (messages, TRUE);
+}
+
+static void
+debug_iface_init (gpointer g_iface, gpointer iface_data)
+{
+  GabbleSvcDebugClass *klass = (GabbleSvcDebugClass *) g_iface;
+
+  gabble_svc_debug_implement_get_messages (klass, get_messages);
+}
+
+static void
+gabble_debugger_init (GabbleDebugger *self)
+{
+  self->messages = g_queue_new ();
+}
+
+GabbleDebugger *
+gabble_debugger_get_singleton (void)
+{
+  if (G_UNLIKELY (singleton == NULL))
+    {
+      DBusGConnection *bus;
+
+      singleton = g_object_new (GABBLE_TYPE_DEBUGGER, NULL);
+      bus = tp_get_bus ();
+      dbus_g_connection_register_g_object (bus,
+          "/org/freedesktop/Telepathy/debug", (GObject *) singleton);
+    }
+
+  return singleton;
+}
+
+void
+gabble_debugger_add_message (GabbleDebugger *self,
+    gdouble timestamp,
+    const gchar *string)
+{
+  GabbleDebugMessage *new_msg;
+
+  if (g_queue_get_length (self->messages) >= DEBUG_MESSAGE_LIMIT)
+    {
+      GabbleDebugMessage *old_head =
+        (GabbleDebugMessage *) g_queue_pop_head (self->messages);
+
+      debug_message_free (old_head);
+    }
+
+  new_msg = debug_message_new (timestamp, string);
+  g_queue_push_tail (self->messages, new_msg);
+
+  if (self->enabled)
+    {
+      g_signal_emit_by_name (
+          G_OBJECT (self), "new-debug-message", timestamp, string);
+    }
+}
+
diff --git a/src/debugger.h b/src/debugger.h
new file mode 100644
index 0000000..b10545d
--- /dev/null
+++ b/src/debugger.h
@@ -0,0 +1,82 @@
+/*
+ * debugger.h - header for Telepathy debug interface implementation
+ * Copyright (C) 2009 Collabora Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef _GABBLE_DEBUGGER
+#define _GABBLE_DEBUGGER
+
+#include <glib-object.h>
+
+#include <telepathy-glib/properties-mixin.h>
+#include <telepathy-glib/dbus-properties-mixin.h>
+
+G_BEGIN_DECLS
+
+#define GABBLE_TYPE_DEBUGGER gabble_debugger_get_type()
+
+#define GABBLE_DEBUGGER(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST ((obj), GABBLE_TYPE_DEBUGGER, GabbleDebugger))
+
+#define GABBLE_DEBUGGER_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST ((klass), GABBLE_TYPE_DEBUGGER, GabbleDebuggerClass))
+
+#define GABBLE_IS_DEBUGGER(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GABBLE_TYPE_DEBUGGER))
+
+#define GABBLE_IS_DEBUGGER_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE ((klass), GABBLE_TYPE_DEBUGGER))
+
+#define GABBLE_DEBUGGER_GET_CLASS(obj) \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_DEBUGGER, GabbleDebuggerClass))
+
+/* On the basis that messages are around 60 bytes on average, and that 50kb is
+ * a reasonable maximum size for a frame buffer.
+ */
+
+#define DEBUG_MESSAGE_LIMIT 800
+
+typedef struct {
+  gdouble timestamp;
+  gchar *string;
+} GabbleDebugMessage;
+
+typedef struct {
+  GObject parent;
+
+  gboolean enabled;
+  GQueue *messages;
+} GabbleDebugger;
+
+typedef struct {
+  GObjectClass parent_class;
+  TpDBusPropertiesMixinClass dbus_props_class;
+} GabbleDebuggerClass;
+
+GType gabble_debugger_get_type (void);
+
+GabbleDebugger *
+gabble_debugger_get_singleton (void);
+
+void
+gabble_debugger_add_message (GabbleDebugger *self,
+    gdouble timestamp,
+    const gchar *string);
+
+G_END_DECLS
+
+#endif /* _GABBLE_DEBUGGER */
diff --git a/src/gabble.c b/src/gabble.c
index 8e50a47..beae223 100644
--- a/src/gabble.c
+++ b/src/gabble.c
@@ -27,6 +27,7 @@
 #include <telepathy-glib/run.h>
 
 #include "debug.h"
+#include "debugger.h"
 #include "connection-manager.h"
 
 static TpBaseConnectionManager *
@@ -38,6 +39,28 @@ construct_cm (void)
 
 #ifdef ENABLE_DEBUG
 static void
+log_to_debugger (GTimeVal *timestamp, const gchar *string)
+{
+  GabbleDebugger *dbg = gabble_debugger_get_singleton ();
+  gdouble seconds = timestamp->tv_sec + timestamp->tv_usec / 1e6;
+
+  gabble_debugger_add_message (dbg, seconds, string);
+}
+
+static void
+simple_log (const gchar *log_domain,
+            GLogLevelFlags log_level,
+            const gchar *message,
+            gpointer user_data)
+{
+  GTimeVal now;
+
+  g_log_default_handler (log_domain, log_level, message, NULL);
+  g_get_current_time (&now);
+  log_to_debugger (&now, message);
+}
+
+static void
 stamp_log (const gchar *log_domain,
            GLogLevelFlags log_level,
            const gchar *message,
@@ -54,6 +77,7 @@ stamp_log (const gchar *log_domain,
   tmp = g_strdup_printf ("%s.%06ld: %s", now_str, now.tv_usec, message);
   g_log_default_handler (log_domain, log_level, tmp, NULL);
   g_free (tmp);
+  log_to_debugger (&now, message);
 }
 #endif
 
@@ -68,6 +92,8 @@ gabble_main (int argc,
 
   if (g_getenv ("GABBLE_TIMING") != NULL)
     g_log_set_handler (NULL, G_LOG_LEVEL_DEBUG, stamp_log, NULL);
+  else
+    g_log_set_handler (NULL, G_LOG_LEVEL_DEBUG, simple_log, NULL);
 
   if (g_getenv ("GABBLE_PERSIST") != NULL)
     tp_debug_set_persistent (TRUE);
diff --git a/tests/twisted/Makefile.am b/tests/twisted/Makefile.am
index 281c171..fb414ca 100644
--- a/tests/twisted/Makefile.am
+++ b/tests/twisted/Makefile.am
@@ -113,6 +113,7 @@ TWISTED_TESTS = \
 	test-caps-cache.py \
 	test-caps-hash.py \
 	test-caps-tubes.py \
+	test-debug.py \
 	caps_helper.py \
 	connect/test-fail.py \
 	connect/test-success.py \
diff --git a/tests/twisted/test-debug.py b/tests/twisted/test-debug.py
new file mode 100644
index 0000000..dd99795
--- /dev/null
+++ b/tests/twisted/test-debug.py
@@ -0,0 +1,52 @@
+
+"""
+Test the debug message interface.
+"""
+
+import dbus
+
+from gabbletest import exec_test
+import constants as cs
+
+path = '/org/freedesktop/Telepathy/debug'
+iface = 'org.freedesktop.Telepathy.Debug'
+
+def test(q, bus, conn, stream):
+    messages = []
+
+    def new_message(timestamp, string):
+        messages.append((timestamp, string))
+
+    debug = bus.get_object(conn.bus_name, path)
+    debug_iface = dbus.Interface(debug, iface)
+    debug_iface.connect_to_signal('NewDebugMessage', new_message)
+    props_iface = dbus.Interface(debug, cs.PROPERTIES_IFACE)
+
+    assert len(debug.GetMessages()) > 0
+
+    # Turn signalling on and generate some messages.
+
+    assert len(messages) == 0
+    conn.Connect()
+    q.expect('dbus-signal', signal='StatusChanged', args=[0, 1])
+    assert props_iface.Get(iface, 'Enabled') == False
+    props_iface.Set(iface, 'Enabled', True)
+
+    conn.RequestChannel(
+        cs.CHANNEL_TYPE_TEXT, cs.HT_CONTACT, conn.GetSelfHandle(), True)
+    q.expect('dbus-signal', signal='NewChannel')
+    assert len(messages) > 0
+
+    # Turn signalling off and check we have no new messages.
+
+    props_iface.Set(iface, 'Enabled', False)
+    snapshot = list(messages)
+
+    conn.Disconnect()
+    q.expect('dbus-signal', signal='StatusChanged', args=[2, 1])
+
+    assert snapshot == messages
+
+if __name__ == '__main__':
+    exec_test(test)
+
-- 
1.5.6.5




More information about the telepathy-commits mailing list