[folks] Allow printing out status information at runtime



commit 4292b07504c13e70fba6c1b23b1a043cb2120fa3
Author: Philip Withnall <philip tecnocode co uk>
Date:   Sat Apr 23 01:14:20 2011 +0100

    Allow printing out status information at runtime
    
    When Folks.Debug.print-status is emitted, BackendStore, IndividualAggregator
    and Tpf.PersonaStore will now print out their status, including a list
    of all the loaded backends, persona stores, aggregated individuals and their
    personas.
    
    Folks.Debug.print-status could be emitted as a result of the client process
    receiving SIGUSR2, for example.
    
    Helps: bgo#648533

 backends/telepathy/lib/tpf-persona-store.vala |  220 +++++++++++++++++++++++++
 folks/backend-store.vala                      |   69 ++++++++
 folks/debug.vala                              |   50 ++++++
 folks/individual-aggregator.vala              |   85 ++++++++++
 4 files changed, 424 insertions(+), 0 deletions(-)
---
diff --git a/backends/telepathy/lib/tpf-persona-store.vala b/backends/telepathy/lib/tpf-persona-store.vala
index 5adca55..893698a 100644
--- a/backends/telepathy/lib/tpf-persona-store.vala
+++ b/backends/telepathy/lib/tpf-persona-store.vala
@@ -24,6 +24,7 @@ using Gee;
 using TelepathyGLib;
 using Folks;
 
+extern const string G_LOG_DOMAIN;
 extern const string BACKEND_NAME;
 
 /**
@@ -87,6 +88,7 @@ public class Tpf.PersonaStore : Folks.PersonaStore
   private MaybeBool _can_group_personas = MaybeBool.UNSET;
   private MaybeBool _can_remove_personas = MaybeBool.UNSET;
   private bool _is_prepared = false;
+  private Debug _debug;
 
   internal signal void group_members_changed (string group,
       GLib.List<Persona>? added, GLib.List<Persona>? removed);
@@ -190,9 +192,227 @@ public class Tpf.PersonaStore : Folks.PersonaStore
               display_name: account.display_name,
               id: account.get_object_path ());
 
+      this._debug = Debug.dup ();
+      this._debug.print_status.connect (this._debug_print_status);
+
       this._reset ();
     }
 
+  ~PersonaStore ()
+    {
+      this._debug.print_status.disconnect (this._debug_print_status);
+      this._debug = null;
+    }
+
+  private string _format_maybe_bool (MaybeBool input)
+    {
+      switch (input)
+        {
+          case MaybeBool.UNSET:
+            return "unset";
+          case MaybeBool.TRUE:
+            return "true";
+          case MaybeBool.FALSE:
+            return "false";
+          default:
+            assert_not_reached ();
+        }
+    }
+
+  private void _debug_print_status (Debug debug)
+    {
+      const string domain = Debug.STATUS_LOG_DOMAIN;
+      const LogLevelFlags level = LogLevelFlags.LEVEL_INFO;
+
+      debug.print_heading (domain, level, "Tpf.PersonaStore (%p)", this);
+      debug.print_key_value_pairs (domain, level,
+          "ID", this.id,
+          "Prepared?", this._is_prepared ? "yes" : "no",
+          "Publish TpChannel", "%p".printf (this._publish),
+          "Stored TpChannel", "%p".printf (this._stored),
+          "Subscribe TpChannel", "%p".printf (this._subscribe),
+          "TpConnection", "%p".printf (this._conn),
+          "TpAccountManager", "%p".printf (this._account_manager),
+          "Self-TpContact", "%p".printf (this._self_contact),
+          "Can add personas?", this._format_maybe_bool (this._can_add_personas),
+          "Can alias personas?",
+              this._format_maybe_bool (this._can_alias_personas),
+          "Can group personas?",
+              this._format_maybe_bool (this._can_group_personas),
+          "Can remove personas?",
+              this._format_maybe_bool (this._can_remove_personas)
+      );
+
+      debug.print_line (domain, level, "%u Personas:", this._persona_set.size);
+      debug.indent ();
+
+      foreach (var persona in this._persona_set)
+        {
+          debug.print_heading (domain, level, "Persona (%p)", persona);
+          debug.print_key_value_pairs (domain, level,
+              "UID", persona.uid,
+              "IID", persona.iid,
+              "Display ID", persona.display_id,
+              "User?", persona.is_user ? "yes" : "no",
+              "In contact list?", persona.is_in_contact_list ? "yes" : "no",
+              "TpContact", "%p".printf (persona.contact)
+          );
+        }
+
+      debug.unindent ();
+
+      debug.print_line (domain, level, "%u handleâ??Persona mappings:",
+          this._handle_persona_map.size);
+      debug.indent ();
+
+      var iter1 = this._handle_persona_map.map_iterator ();
+      while (iter1.next () == true)
+        {
+          debug.print_line (domain, level,
+              "%u â?? %p", iter1.get_key (), iter1.get_value ());
+        }
+
+      debug.unindent ();
+
+      debug.print_line (domain, level, "%u channel group Persona sets:",
+          this._channel_group_personas_map.size);
+      debug.indent ();
+
+      var iter2 = this._channel_group_personas_map.map_iterator ();
+      while (iter2.next () == true)
+        {
+          debug.print_heading (domain, level,
+              "Channel (%p):", iter2.get_key ());
+
+          debug.indent ();
+
+          foreach (var persona in iter2.get_value ())
+            {
+              debug.print_line (domain, level, "%p", persona);
+            }
+
+          debug.unindent ();
+        }
+
+      debug.unindent ();
+
+      debug.print_line (domain, level, "%u channel group incoming handle sets:",
+          this._channel_group_incoming_adds.size);
+      debug.indent ();
+
+      var iter3 = this._channel_group_incoming_adds.map_iterator ();
+      while (iter3.next () == true)
+        {
+          debug.print_heading (domain, level,
+              "Channel (%p):", iter3.get_key ());
+
+          debug.indent ();
+
+          foreach (var handle in iter3.get_value ())
+            {
+              debug.print_line (domain, level, "%u", handle);
+            }
+
+          debug.unindent ();
+        }
+
+      debug.unindent ();
+
+      debug.print_line (domain, level, "%u group outgoing add sets:",
+          this._group_outgoing_adds.size);
+      debug.indent ();
+
+      var iter4 = this._group_outgoing_adds.map_iterator ();
+      while (iter4.next () == true)
+        {
+          debug.print_heading (domain, level, "Group (%s):", iter4.get_key ());
+
+          debug.indent ();
+
+          foreach (var persona in iter4.get_value ())
+            {
+              debug.print_line (domain, level, "%p", persona);
+            }
+
+          debug.unindent ();
+        }
+
+      debug.unindent ();
+
+      debug.print_line (domain, level, "%u group outgoing remove sets:",
+          this._group_outgoing_removes.size);
+      debug.indent ();
+
+      var iter5 = this._group_outgoing_removes.map_iterator ();
+      while (iter5.next () == true)
+        {
+          debug.print_heading (domain, level, "Group (%s):", iter5.get_key ());
+
+          debug.indent ();
+
+          foreach (var persona in iter5.get_value ())
+            {
+              debug.print_line (domain, level, "%p", persona);
+            }
+
+          debug.unindent ();
+        }
+
+      debug.unindent ();
+
+      debug.print_line (domain, level, "%u unready standard channels:",
+          this._standard_channels_unready.size);
+      debug.indent ();
+
+      var iter6 = this._standard_channels_unready.map_iterator ();
+      while (iter6.next () == true)
+        {
+          debug.print_line (domain, level,
+              "%s â?? %p", iter6.get_key (), iter6.get_value ());
+        }
+
+      debug.unindent ();
+
+      debug.print_line (domain, level, "%u unready group channels:",
+          this._group_channels_unready.size);
+      debug.indent ();
+
+      var iter7 = this._group_channels_unready.map_iterator ();
+      while (iter7.next () == true)
+        {
+          debug.print_line (domain, level,
+              "%s â?? %p", iter7.get_key (), iter7.get_value ());
+        }
+
+      debug.unindent ();
+
+      debug.print_line (domain, level, "%u ready group channels:",
+          this._groups.size);
+      debug.indent ();
+
+      var iter8 = this._groups.map_iterator ();
+      while (iter8.next () == true)
+        {
+          debug.print_line (domain, level,
+              "%s â?? %p", iter8.get_key (), iter8.get_value ());
+        }
+
+      debug.unindent ();
+
+      debug.print_line (domain, level, "%u favourite handles:",
+          this._favourite_handles.size);
+      debug.indent ();
+
+      foreach (var handle in this._favourite_handles)
+        {
+          debug.print_line (domain, level, "%u", handle);
+        }
+
+      debug.unindent ();
+
+      debug.print_line (domain, level, "");
+    }
+
   private void _reset ()
     {
       /* We do not trust local-xmpp or IRC at all, since Persona UIDs can be
diff --git a/folks/backend-store.vala b/folks/backend-store.vala
index 18ee698..d8c21d8 100644
--- a/folks/backend-store.vala
+++ b/folks/backend-store.vala
@@ -139,6 +139,8 @@ public class Folks.BackendStore : Object {
       /* register the core debug messages */
       this._debug._register_domain (G_LOG_DOMAIN);
 
+      this._debug.print_status.connect (this._debug_print_status);
+
       this._modules = new HashMap<string,unowned Module> (str_hash, str_equal);
       this._backend_hash = new HashMap<string,Backend> (str_hash, str_equal);
       this._prepared_backends = new HashMap<string,Backend> (str_hash,
@@ -159,10 +161,77 @@ public class Folks.BackendStore : Object {
             }
         }
 
+      /* Disconnect from the debug handler */
+      this._debug.print_status.disconnect (this._debug_print_status);
+      this._debug = null;
+
       /* manually clear the singleton instance */
       _instance = null;
     }
 
+  private void _debug_print_status (Debug debug)
+    {
+      const string domain = Debug.STATUS_LOG_DOMAIN;
+      const LogLevelFlags level = LogLevelFlags.LEVEL_INFO;
+
+      debug.print_heading (domain, level, "BackendStore (%p)", this);
+      debug.print_line (domain, level, "%u Backends:",
+          this._backend_hash.size);
+
+      debug.indent ();
+
+      foreach (var backend in this._backend_hash.values)
+        {
+          debug.print_heading (domain, level, "Backend (%p)", backend);
+          debug.print_key_value_pairs (domain, level,
+              "Ref. count", this.ref_count.to_string (),
+              "Name", backend.name,
+              "Prepared?", backend.is_prepared ? "yes" : "no"
+          );
+          debug.print_line (domain, level, "%u PersonaStores:",
+              backend.persona_stores.size);
+
+          debug.indent ();
+
+          foreach (var persona_store in backend.persona_stores.values)
+            {
+              string trust_level = null;
+
+              switch (persona_store.trust_level)
+                {
+                  case PersonaStoreTrust.NONE:
+                    trust_level = "none";
+                    break;
+                  case PersonaStoreTrust.PARTIAL:
+                    trust_level = "partial";
+                    break;
+                  case PersonaStoreTrust.FULL:
+                    trust_level = "full";
+                    break;
+                  default:
+                    assert_not_reached ();
+                }
+
+              debug.print_heading (domain, level, "PersonaStore (%p)",
+                  persona_store);
+              debug.print_key_value_pairs (domain, level,
+                  "Ref. count", this.ref_count.to_string (),
+                  "ID", persona_store.id,
+                  "Prepared?", persona_store.is_prepared ? "yes" : "no",
+                  "Writeable?", persona_store.is_writeable ? "yes" : "no",
+                  "Trust level", trust_level,
+                  "Persona count", persona_store.personas.size.to_string ()
+              );
+            }
+
+          debug.unindent ();
+        }
+
+      debug.unindent ();
+
+      debug.print_line (domain, level, "");
+    }
+
   /**
    * Prepare the BackendStore for use.
    *
diff --git a/folks/debug.vala b/folks/debug.vala
index 5bade93..6acad1d 100644
--- a/folks/debug.vala
+++ b/folks/debug.vala
@@ -114,6 +114,38 @@ public class Folks.Debug : Object
         }
     }
 
+  /**
+   * Signal emitted in the main thread whenever objects should print their
+   * current status. All significant objects in the library should connect
+   * to this and print their current status in some suitable format when it's
+   * emitted.
+   *
+   * Client processes should emit this signal by calling
+   * { link Debug.emit_print_status}.
+   *
+   * @since UNRELEASED
+   */
+  public signal void print_status ();
+
+  /**
+   * Log domain for the status messages logged as a result of calling
+   * { link Debug.emit_print_status().
+   *
+   * This could be used in conjunction with a log handler to redirect the
+   * status information to a debug window or log file, for example.
+   *
+   * @since UNRELEASED
+   */
+  public const string STATUS_LOG_DOMAIN = "folks-status";
+
+  private void _print_status_log_handler_cb (string? log_domain,
+      LogLevelFlags log_levels,
+      string message)
+    {
+      /* Print directly to stdout without any adornments */
+      GLib.stdout.printf ("%s\n", message);
+    }
+
   private void _log_handler_cb (string? log_domain,
       LogLevelFlags log_levels,
       string message)
@@ -222,6 +254,11 @@ public class Folks.Debug : Object
   private Debug ()
     {
       /* Private constructor for singleton */
+
+      /* Install a log handler for log messages emitted as a result of
+       * Debug.print-status being emitted. */
+      Log.set_handler (Debug.STATUS_LOG_DOMAIN, LogLevelFlags.LEVEL_MASK,
+          this._print_status_log_handler_cb);
     }
 
   ~Debug ()
@@ -234,6 +271,19 @@ public class Folks.Debug : Object
     }
 
   /**
+   * Causes all significant objects in the library to print their current
+   * status to standard output, obeying the options set on this
+   * { link Debug} instance for colouring and other formatting.
+   *
+   * @since UNRELEASED
+   */
+  public void emit_print_status ()
+    {
+      print ("Dumping status informationâ?¦\n");
+      this.print_status ();
+    }
+
+  /**
    * Increment the indentation level used when printing output through the
    * object.
    *
diff --git a/folks/individual-aggregator.vala b/folks/individual-aggregator.vala
index be208e7..ddaf293 100644
--- a/folks/individual-aggregator.vala
+++ b/folks/individual-aggregator.vala
@@ -63,6 +63,7 @@ public class Folks.IndividualAggregator : Object
   private HashTable<string, Individual> _link_map;
   private bool _linking_enabled = true;
   private bool _is_prepared = false;
+  private Debug _debug;
   private string _configured_writeable_store_type_id;
   private static const string _FOLKS_CONFIG_KEY =
     "/system/folks/backends/primary_store";
@@ -176,6 +177,8 @@ public class Folks.IndividualAggregator : Object
       this._link_map = new HashTable<string, Individual> (str_hash, str_equal);
 
       this._backends = new HashSet<Backend> ();
+      this._debug = Debug.dup ();
+      this._debug.print_status.connect (this._debug_print_status);
 
       /* Check out the configured writeable store */
       var store_type_id = Environment.get_variable ("FOLKS_WRITEABLE_STORE");
@@ -215,6 +218,88 @@ public class Folks.IndividualAggregator : Object
       this._backend_store.backend_available.disconnect (
           this._backend_available_cb);
       this._backend_store = null;
+
+      this._debug.print_status.disconnect (this._debug_print_status);
+    }
+
+  private void _debug_print_status (Debug debug)
+    {
+      const string domain = Debug.STATUS_LOG_DOMAIN;
+      const LogLevelFlags level = LogLevelFlags.LEVEL_INFO;
+
+      debug.print_heading (domain, level, "IndividualAggregator (%p)", this);
+      debug.print_key_value_pairs (domain, level,
+          "Ref. count", this.ref_count.to_string (),
+          "Writeable store", "%p".printf (this._writeable_store),
+          "Linking enabled?", this._linking_enabled ? "yes" : "no",
+          "Prepared?", this._is_prepared ? "yes" : "no"
+      );
+
+      debug.print_line (domain, level,
+          "%u Individuals:", this.individuals.size);
+      debug.indent ();
+
+      foreach (var individual in this.individuals.values)
+        {
+          string trust_level = null;
+
+          switch (individual.trust_level)
+            {
+              case TrustLevel.NONE:
+                trust_level = "none";
+                break;
+              case TrustLevel.PERSONAS:
+                trust_level = "personas";
+                break;
+              default:
+                assert_not_reached ();
+            }
+
+          debug.print_heading (domain, level, "Individual (%p)", individual);
+          debug.print_key_value_pairs (domain, level,
+              "Ref. count", individual.ref_count.to_string (),
+              "ID", individual.id,
+              "User?", individual.is_user ? "yes" : "no",
+              "Trust level", trust_level
+          );
+          debug.print_line (domain, level, "%u Personas:",
+              individual.personas.size);
+
+          debug.indent ();
+
+          foreach (var persona in individual.personas)
+            {
+              debug.print_heading (domain, level, "Persona (%p)", persona);
+              debug.print_key_value_pairs (domain, level,
+                  "Ref. count", persona.ref_count.to_string (),
+                  "UID", persona.uid,
+                  "IID", persona.iid,
+                  "Display ID", persona.display_id,
+                  "User?", persona.is_user ? "yes" : "no"
+              );
+            }
+
+          debug.unindent ();
+        }
+
+      debug.unindent ();
+
+      debug.print_line (domain, level, "%u entries in the link map:",
+          this._link_map.size ());
+      debug.indent ();
+
+      var iter = HashTableIter<string, Individual> (this._link_map);
+      string link_key;
+      Individual individual;
+      while (iter.next (out link_key, out individual) == true)
+        {
+          debug.print_line (domain, level,
+              "%s â?? %p", link_key, individual);
+        }
+
+      debug.unindent ();
+
+      debug.print_line (domain, level, "");
     }
 
   /**



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