[gnome-software] Add profiling functionality that can show a boot-chart log of startup



commit cc985e2bca398dee92a01f0efd54320144497a89
Author: Richard Hughes <richard hughsie com>
Date:   Wed Oct 2 08:28:56 2013 +0100

    Add profiling functionality that can show a boot-chart log of startup

 src/Makefile.am                           |    2 +
 src/gs-main.c                             |    7 +
 src/gs-plugin-loader.c                    |  131 +++++++------
 src/gs-plugin.h                           |    3 +-
 src/gs-profile.c                          |  312 +++++++++++++++++++++++++++++
 src/gs-profile.h                          |   66 ++++++
 src/plugins/gs-plugin-packagekit-refine.c |    6 +
 7 files changed, 466 insertions(+), 61 deletions(-)
---
diff --git a/src/Makefile.am b/src/Makefile.am
index c15890e..81e4a2e 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -57,6 +57,8 @@ gnome_software_SOURCES =                              \
        gs-box.c                                        \
        gs-plugin.c                                     \
        gs-plugin.h                                     \
+       gs-profile.c                                    \
+       gs-profile.h                                    \
        gs-shell.c                                      \
        gs-shell.h                                      \
        gs-shell-details.c                              \
diff --git a/src/gs-main.c b/src/gs-main.c
index 03e72cb..c690b74 100644
--- a/src/gs-main.c
+++ b/src/gs-main.c
@@ -27,12 +27,14 @@
 #include <locale.h>
 
 #include "gs-application.h"
+#include "gs-profile.h"
 
 int
 main (int argc, char **argv)
 {
        int status = 0;
        GsApplication *application;
+       GsProfile *profile;
 
        setlocale (LC_ALL, "");
 
@@ -40,9 +42,14 @@ main (int argc, char **argv)
        bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
        textdomain (GETTEXT_PACKAGE);
 
+       profile = gs_profile_new ();
+       gs_profile_start (profile, "GsMain");
        application = gs_application_new ();
        status = g_application_run (G_APPLICATION (application), argc, argv);
        g_object_unref (application);
+       gs_profile_stop (profile, "GsMain");
+       gs_profile_dump (profile);
+       g_object_unref (profile);
 
        return status;
 }
diff --git a/src/gs-plugin-loader.c b/src/gs-plugin-loader.c
index 53472cd..c3fb460 100644
--- a/src/gs-plugin-loader.c
+++ b/src/gs-plugin-loader.c
@@ -25,6 +25,7 @@
 
 #include "gs-plugin-loader.h"
 #include "gs-plugin.h"
+#include "gs-profile.h"
 
 static void    gs_plugin_loader_finalize       (GObject        *object);
 
@@ -35,6 +36,7 @@ struct GsPluginLoaderPrivate
        GPtrArray               *plugins;
        gchar                   *location;
        GsPluginStatus           status_last;
+       GsProfile               *profile;
 
        GMutex                   pending_apps_mutex;
        GPtrArray               *pending_apps;
@@ -198,16 +200,17 @@ gs_plugin_loader_list_dedupe (GsPluginLoader *plugin_loader, GList *list)
  **/
 static gboolean
 gs_plugin_loader_run_refine (GsPluginLoader *plugin_loader,
+                            const gchar *function_name_parent,
                             GList *list,
                             GCancellable *cancellable,
                             GError **error)
 {
        gboolean ret = TRUE;
+       gchar *profile_id;
        GsPlugin *plugin;
        const gchar *function_name = "gs_plugin_refine";
        GsPluginRefineFunc plugin_func = NULL;
        guint i;
-       gdouble elapsed_ms;
 
        /* run each plugin */
        for (i = 0; i < plugin_loader->priv->plugins->len; i++) {
@@ -219,18 +222,17 @@ gs_plugin_loader_run_refine (GsPluginLoader *plugin_loader,
                                       (gpointer *) &plugin_func);
                if (!ret)
                        continue;
-               g_debug ("run %s on %s", function_name,
-                        g_module_name (plugin->module));
-               g_timer_start (plugin->timer);
+               profile_id = g_strdup_printf ("GsPlugin::%s(%s;%s)",
+                                             plugin->name,
+                                             function_name_parent,
+                                             function_name);
+               gs_profile_start (plugin_loader->priv->profile, profile_id);
                ret = plugin_func (plugin, list, cancellable, error);
                if (!ret)
                        goto out;
                gs_plugin_status_update (plugin, NULL, GS_PLUGIN_STATUS_FINISHED);
-               elapsed_ms = g_timer_elapsed (plugin->timer, NULL) * 1000;
-               if (elapsed_ms > 1) {
-                       g_debug ("%s(%s) took %.0fms",
-                                plugin->name, function_name, elapsed_ms);
-               }
+               gs_profile_stop (plugin_loader->priv->profile, profile_id);
+               g_free (profile_id);
        }
 
        /* success */
@@ -249,17 +251,23 @@ gs_plugin_loader_run_results (GsPluginLoader *plugin_loader,
                              GError **error)
 {
        gboolean ret = TRUE;
+       gchar *profile_id_parent;
+       gchar *profile_id;
        GList *list = NULL;
        GsPlugin *plugin;
        GsPluginResultsFunc plugin_func = NULL;
        guint i;
-       gdouble elapsed_ms;
 
        g_return_val_if_fail (GS_IS_PLUGIN_LOADER (plugin_loader), NULL);
        g_return_val_if_fail (function_name != NULL, NULL);
        g_return_val_if_fail (error == NULL || *error == NULL, NULL);
        g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
 
+       /* profile */
+       profile_id_parent = g_strdup_printf ("GsPlugin::*(%s)",
+                                            function_name);
+       gs_profile_start (plugin_loader->priv->profile, profile_id_parent);
+
        /* run each plugin */
        for (i = 0; i < plugin_loader->priv->plugins->len; i++) {
                plugin = g_ptr_array_index (plugin_loader->priv->plugins, i);
@@ -275,21 +283,16 @@ gs_plugin_loader_run_results (GsPluginLoader *plugin_loader,
                                       (gpointer *) &plugin_func);
                if (!ret)
                        continue;
-               g_debug ("run %s on %s", function_name,
-                        g_module_name (plugin->module));
-               g_timer_start (plugin->timer);
-
+               profile_id = g_strdup_printf ("GsPlugin::%s(%s)",
+                                             plugin->name, function_name);
+               gs_profile_start (plugin_loader->priv->profile, profile_id);
                g_assert (error == NULL || *error == NULL);
                ret = plugin_func (plugin, &list, cancellable, error);
                if (!ret)
                        goto out;
-
                gs_plugin_status_update (plugin, NULL, GS_PLUGIN_STATUS_FINISHED);
-               elapsed_ms = g_timer_elapsed (plugin->timer, NULL) * 1000;
-               if (elapsed_ms > 1) {
-                       g_debug ("%s(%s) took %.0fms",
-                                plugin->name, function_name, elapsed_ms);
-               }
+               gs_profile_stop (plugin_loader->priv->profile, profile_id);
+               g_free (profile_id);
        }
 
        /* dedupe applications we already know about */
@@ -297,6 +300,7 @@ gs_plugin_loader_run_results (GsPluginLoader *plugin_loader,
 
        /* run refine() on each one */
        ret = gs_plugin_loader_run_refine (plugin_loader,
+                                          function_name,
                                           list,
                                           cancellable,
                                           error);
@@ -306,6 +310,9 @@ gs_plugin_loader_run_results (GsPluginLoader *plugin_loader,
        /* remove duplicates */
        list = gs_plugin_loader_list_uniq (plugin_loader, list);
 
+       /* profile */
+       gs_profile_stop (plugin_loader->priv->profile, profile_id_parent);
+
        /* no results */
        if (list == NULL) {
                g_set_error (error,
@@ -315,6 +322,7 @@ gs_plugin_loader_run_results (GsPluginLoader *plugin_loader,
                goto out;
        }
 out:
+       g_free (profile_id_parent);
        if (!ret) {
                gs_plugin_list_free (list);
                list = NULL;
@@ -1086,13 +1094,13 @@ gs_plugin_loader_search_thread_cb (GSimpleAsyncResult *res,
 {
        const gchar *function_name = "gs_plugin_add_search";
        gboolean ret = TRUE;
+       gchar *profile_id;
        GError *error = NULL;
        GsPluginLoaderAsyncState *state = (GsPluginLoaderAsyncState *) g_object_get_data (G_OBJECT 
(cancellable), "state");
        GsPluginLoader *plugin_loader = GS_PLUGIN_LOADER (object);
        GsPlugin *plugin;
        GsPluginSearchFunc plugin_func = NULL;
        guint i;
-       gdouble elapsed_ms;
 
        /* run each plugin */
        for (i = 0; i < plugin_loader->priv->plugins->len; i++) {
@@ -1110,9 +1118,9 @@ gs_plugin_loader_search_thread_cb (GSimpleAsyncResult *res,
                                       (gpointer *) &plugin_func);
                if (!ret)
                        continue;
-               g_debug ("run %s on %s", function_name,
-                        g_module_name (plugin->module));
-               g_timer_start (plugin->timer);
+               profile_id = g_strdup_printf ("GsPlugin::%s(%s)",
+                                             plugin->name, function_name);
+               gs_profile_start (plugin_loader->priv->profile, profile_id);
                ret = plugin_func (plugin, state->value, &state->list, cancellable, &error);
                if (!ret) {
                        gs_plugin_loader_get_all_state_finish (state, error);
@@ -1120,11 +1128,8 @@ gs_plugin_loader_search_thread_cb (GSimpleAsyncResult *res,
                        goto out;
                }
                gs_plugin_status_update (plugin, NULL, GS_PLUGIN_STATUS_FINISHED);
-               elapsed_ms = g_timer_elapsed (plugin->timer, NULL) * 1000;
-               if (elapsed_ms > 1) {
-                       g_debug ("%s(%s) took %.0fms",
-                                plugin->name, function_name, elapsed_ms);
-               }
+               gs_profile_stop (plugin_loader->priv->profile, profile_id);
+               g_free (profile_id);
        }
 
        /* dedupe applications we already know about */
@@ -1132,6 +1137,7 @@ gs_plugin_loader_search_thread_cb (GSimpleAsyncResult *res,
 
        /* run refine() on each one */
        ret = gs_plugin_loader_run_refine (plugin_loader,
+                                          function_name,
                                           state->list,
                                           cancellable,
                                           &error);
@@ -1270,6 +1276,7 @@ gs_plugin_loader_get_categories_thread_cb (GSimpleAsyncResult *res,
 {
        const gchar *function_name = "gs_plugin_add_categories";
        gboolean ret = TRUE;
+       gchar *profile_id;
        GError *error = NULL;
        GsPluginLoaderAsyncState *state = (GsPluginLoaderAsyncState *) g_object_get_data (G_OBJECT 
(cancellable), "state");
        GsPluginLoader *plugin_loader = GS_PLUGIN_LOADER (object);
@@ -1277,7 +1284,6 @@ gs_plugin_loader_get_categories_thread_cb (GSimpleAsyncResult *res,
        GsPluginResultsFunc plugin_func = NULL;
        GList *l;
        guint i;
-       gdouble elapsed_ms;
 
        /* run each plugin */
        for (i = 0; i < plugin_loader->priv->plugins->len; i++) {
@@ -1295,9 +1301,9 @@ gs_plugin_loader_get_categories_thread_cb (GSimpleAsyncResult *res,
                                       (gpointer *) &plugin_func);
                if (!ret)
                        continue;
-               g_debug ("run %s on %s", function_name,
-                        g_module_name (plugin->module));
-               g_timer_start (plugin->timer);
+               profile_id = g_strdup_printf ("GsPlugin::%s(%s)",
+                                             plugin->name, function_name);
+               gs_profile_start (plugin_loader->priv->profile, profile_id);
                ret = plugin_func (plugin, &state->list, cancellable, &error);
                if (!ret) {
                        gs_plugin_loader_get_all_state_finish (state, error);
@@ -1305,11 +1311,8 @@ gs_plugin_loader_get_categories_thread_cb (GSimpleAsyncResult *res,
                        goto out;
                }
                gs_plugin_status_update (plugin, NULL, GS_PLUGIN_STATUS_FINISHED);
-               elapsed_ms = g_timer_elapsed (plugin->timer, NULL) * 1000;
-               if (elapsed_ms > 1) {
-                       g_debug ("%s(%s) took %.0fms",
-                                plugin->name, function_name, elapsed_ms);
-               }
+               gs_profile_stop (plugin_loader->priv->profile, profile_id);
+               g_free (profile_id);
        }
 
        /* sort by name */
@@ -1412,13 +1415,13 @@ gs_plugin_loader_get_category_apps_thread_cb (GSimpleAsyncResult *res,
 {
        const gchar *function_name = "gs_plugin_add_category_apps";
        gboolean ret = TRUE;
+       gchar *profile_id;
        GError *error = NULL;
        GsPluginLoaderAsyncState *state = (GsPluginLoaderAsyncState *) g_object_get_data (G_OBJECT 
(cancellable), "state");
        GsPluginLoader *plugin_loader = GS_PLUGIN_LOADER (object);
        GsPlugin *plugin;
        GsPluginCategoryFunc plugin_func = NULL;
        guint i;
-       gdouble elapsed_ms;
 
        /* run each plugin */
        for (i = 0; i < plugin_loader->priv->plugins->len; i++) {
@@ -1436,9 +1439,9 @@ gs_plugin_loader_get_category_apps_thread_cb (GSimpleAsyncResult *res,
                                       (gpointer *) &plugin_func);
                if (!ret)
                        continue;
-               g_debug ("run %s on %s", function_name,
-                        g_module_name (plugin->module));
-               g_timer_start (plugin->timer);
+               profile_id = g_strdup_printf ("GsPlugin::%s(%s)",
+                                             plugin->name, function_name);
+               gs_profile_start (plugin_loader->priv->profile, profile_id);
                ret = plugin_func (plugin, state->category, &state->list, cancellable, &error);
                if (!ret) {
                        gs_plugin_loader_get_all_state_finish (state, error);
@@ -1446,11 +1449,8 @@ gs_plugin_loader_get_category_apps_thread_cb (GSimpleAsyncResult *res,
                        goto out;
                }
                gs_plugin_status_update (plugin, NULL, GS_PLUGIN_STATUS_FINISHED);
-               elapsed_ms = g_timer_elapsed (plugin->timer, NULL) * 1000;
-               if (elapsed_ms > 1) {
-                       g_debug ("%s(%s) took %.0fms",
-                                plugin->name, function_name, elapsed_ms);
-               }
+               gs_profile_stop (plugin_loader->priv->profile, profile_id);
+               g_free (profile_id);
        }
 
        /* dedupe applications we already know about */
@@ -1458,6 +1458,7 @@ gs_plugin_loader_get_category_apps_thread_cb (GSimpleAsyncResult *res,
 
        /* run refine() on each one */
        ret = gs_plugin_loader_run_refine (plugin_loader,
+                                          function_name,
                                           state->list,
                                           cancellable,
                                           &error);
@@ -1594,11 +1595,11 @@ gs_plugin_loader_run_action (GsPluginLoader *plugin_loader,
        gboolean exists;
        gboolean ret = FALSE;
        gboolean anything_ran = FALSE;
+       gchar *profile_id;
        GError *error_local = NULL;
        GsPluginActionFunc plugin_func = NULL;
        GsPlugin *plugin;
        guint i;
-       gdouble elapsed_ms;
 
        /* run each plugin */
        for (i = 0; i < plugin_loader->priv->plugins->len; i++) {
@@ -1615,9 +1616,9 @@ gs_plugin_loader_run_action (GsPluginLoader *plugin_loader,
                                          (gpointer *) &plugin_func);
                if (!exists)
                        continue;
-               g_debug ("run %s on %s", function_name,
-                        g_module_name (plugin->module));
-               g_timer_start (plugin->timer);
+               profile_id = g_strdup_printf ("GsPlugin::%s(%s)",
+                                             plugin->name, function_name);
+               gs_profile_start (plugin_loader->priv->profile, profile_id);
                ret = plugin_func (plugin, app, cancellable, &error_local);
                if (!ret) {
                        if (g_error_matches (error_local,
@@ -1633,11 +1634,8 @@ gs_plugin_loader_run_action (GsPluginLoader *plugin_loader,
                        }
                }
                gs_plugin_status_update (plugin, NULL, GS_PLUGIN_STATUS_FINISHED);
-               elapsed_ms = g_timer_elapsed (plugin->timer, NULL) * 1000;
-               if (elapsed_ms > 1) {
-                       g_debug ("%s(%s) took %.0fms",
-                                plugin->name, function_name, elapsed_ms);
-               }
+               gs_profile_stop (plugin_loader->priv->profile, profile_id);
+               g_free (profile_id);
                anything_ran = TRUE;
        }
 
@@ -1852,6 +1850,7 @@ gs_plugin_loader_app_refine (GsPluginLoader *plugin_loader,
 
        gs_plugin_add_app (&list, app);
        ret = gs_plugin_loader_run_refine (plugin_loader,
+                                          NULL,
                                           list,
                                           cancellable,
                                           error);
@@ -1869,6 +1868,7 @@ static void
 gs_plugin_loader_run (GsPluginLoader *plugin_loader, const gchar *function_name)
 {
        gboolean ret;
+       gchar *profile_id;
        GsPluginFunc plugin_func = NULL;
        GsPlugin *plugin;
        guint i;
@@ -1882,9 +1882,12 @@ gs_plugin_loader_run (GsPluginLoader *plugin_loader, const gchar *function_name)
                if (!ret)
                        continue;
                gs_plugin_status_update (plugin, NULL, GS_PLUGIN_STATUS_FINISHED);
-               g_debug ("run %s on %s", function_name,
-                        g_module_name (plugin->module));
+               profile_id = g_strdup_printf ("GsPlugin::%s(%s)",
+                                             plugin->name, function_name);
+               gs_profile_start (plugin_loader->priv->profile, profile_id);
                plugin_func (plugin);
+               gs_profile_stop (plugin_loader->priv->profile, profile_id);
+               g_free (profile_id);
        }
 }
 
@@ -1983,9 +1986,9 @@ gs_plugin_loader_open_plugin (GsPluginLoader *plugin_loader,
        plugin->pixbuf_size = 64;
        plugin->priority = plugin_prio (plugin);
        plugin->name = g_strdup (plugin_name ());
-       plugin->timer = g_timer_new ();
        plugin->status_update_fn = gs_plugin_loader_status_update_cb;
        plugin->status_update_user_data = plugin_loader;
+       plugin->profile = g_object_ref (plugin_loader->priv->profile);
        g_debug ("opened plugin %s: %s", filename, plugin->name);
 
        /* add to array */
@@ -2101,8 +2104,8 @@ gs_plugin_loader_plugin_free (GsPlugin *plugin)
 {
        g_free (plugin->priv);
        g_free (plugin->name);
+       g_object_unref (plugin->profile);
        g_module_close (plugin->module);
-       g_timer_destroy (plugin->timer);
        g_slice_free (GsPlugin, plugin);
 }
 
@@ -2145,6 +2148,7 @@ gs_plugin_loader_init (GsPluginLoader *plugin_loader)
        plugin_loader->priv->plugins = g_ptr_array_new_with_free_func ((GDestroyNotify) 
gs_plugin_loader_plugin_free);
        plugin_loader->priv->status_last = GS_PLUGIN_STATUS_LAST;
        plugin_loader->priv->pending_apps = g_ptr_array_new_with_free_func ((GFreeFunc) g_object_unref);
+       plugin_loader->priv->profile = gs_profile_new ();
        plugin_loader->priv->app_cache = g_hash_table_new_full (g_str_hash,
                                                                 g_str_equal,
                                                                 NULL,
@@ -2153,6 +2157,9 @@ gs_plugin_loader_init (GsPluginLoader *plugin_loader)
        g_mutex_init (&plugin_loader->priv->pending_apps_mutex);
        g_mutex_init (&plugin_loader->priv->app_cache_mutex);
 
+       /* application start */
+       gs_profile_start (plugin_loader->priv->profile, "GsPluginLoader");
+
        /* by default we only show project-less apps or compatible projects */
        tmp = g_getenv ("GNOME_SOFTWARE_COMPATIBLE_PROJECTS");
        if (tmp == NULL)
@@ -2176,9 +2183,13 @@ gs_plugin_loader_finalize (GObject *object)
 
        g_return_if_fail (plugin_loader->priv != NULL);
 
+       /* application stop */
+       gs_profile_stop (plugin_loader->priv->profile, "GsPluginLoader");
+
        /* run the plugins */
        gs_plugin_loader_run (plugin_loader, "gs_plugin_destroy");
 
+       g_object_unref (plugin_loader->priv->profile);
        g_strfreev (plugin_loader->priv->compatible_projects);
        g_hash_table_unref (plugin_loader->priv->app_cache);
        g_ptr_array_unref (plugin_loader->priv->pending_apps);
diff --git a/src/gs-plugin.h b/src/gs-plugin.h
index beac05e..208c1dc 100644
--- a/src/gs-plugin.h
+++ b/src/gs-plugin.h
@@ -29,6 +29,7 @@
 
 #include "gs-app.h"
 #include "gs-category.h"
+#include "gs-profile.h"
 
 G_BEGIN_DECLS
 
@@ -57,9 +58,9 @@ struct GsPlugin {
        gchar                   *name;
        GsPluginPrivate         *priv;
        guint                    pixbuf_size;
-       GTimer                  *timer;
        GsPluginStatusUpdate     status_update_fn;
        gpointer                 status_update_user_data;
+       GsProfile               *profile;
 };
 
 typedef enum {
diff --git a/src/gs-profile.c b/src/gs-profile.c
new file mode 100644
index 0000000..9b1d0b4
--- /dev/null
+++ b/src/gs-profile.c
@@ -0,0 +1,312 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2013 Richard Hughes <richard hughsie com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+
+#include "gs-profile.h"
+
+static void    gs_profile_finalize     (GObject        *object);
+
+#define GS_PROFILE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GS_TYPE_PROFILE, GsProfilePrivate))
+
+struct GsProfilePrivate
+{
+       GPtrArray       *current;
+       GPtrArray       *archived;
+       GMutex           mutex;
+};
+
+typedef struct {
+       gchar           *id;
+       gint64           time_start;
+       gint64           time_stop;
+} GsProfileItem;
+
+G_DEFINE_TYPE (GsProfile, gs_profile, G_TYPE_OBJECT)
+
+static gpointer gs_profile_object = NULL;
+
+/**
+ * gs_profile_item_free:
+ **/
+static void
+gs_profile_item_free (GsProfileItem *item)
+{
+       g_free (item->id);
+       g_free (item);
+}
+
+/**
+ * gs_profile_item_find:
+ **/
+static GsProfileItem *
+gs_profile_item_find (GPtrArray *array, const gchar *id)
+{
+       GsProfileItem *tmp;
+       guint i;
+
+       g_return_val_if_fail (id != NULL, NULL);
+
+       for (i = 0; i < array->len; i++) {
+               tmp = g_ptr_array_index (array, i);
+               if (g_strcmp0 (tmp->id, id) == 0)
+                       return tmp;
+       }
+       return NULL;
+}
+
+/**
+ * gs_profile_start:
+ **/
+void
+gs_profile_start (GsProfile *profile, const gchar *id)
+{
+       GsProfileItem *item;
+
+       g_return_if_fail (GS_IS_PROFILE (profile));
+       g_return_if_fail (id != NULL);
+
+       /* lock */
+       g_mutex_lock (&profile->priv->mutex);
+
+       /* already started */
+       item = gs_profile_item_find (profile->priv->current, id);
+       if (item != NULL) {
+               g_warning ("Already a started task for %s", id);
+               goto out;
+       }
+
+       /* add new item */
+       item = g_new0 (GsProfileItem, 1);
+       item->id = g_strdup (id);
+       item->time_start = g_get_real_time ();
+       g_ptr_array_add (profile->priv->current, item);
+       g_debug ("run %s", id);
+out:
+       /* unlock */
+       g_mutex_unlock (&profile->priv->mutex);
+}
+
+/**
+ * gs_profile_start_full:
+ **/
+void
+gs_profile_start_full (GsProfile *profile, const gchar *id)
+{
+       gchar *id_thr;
+       id_thr = g_strdup_printf ("%p~%s", g_thread_self (), id);
+       gs_profile_start (profile, id_thr);
+       g_free (id_thr);
+}
+
+/**
+ * gs_profile_stop:
+ **/
+void
+gs_profile_stop (GsProfile *profile, const gchar *id)
+{
+       GsProfileItem *item;
+       gdouble elapsed_ms;
+
+       g_return_if_fail (GS_IS_PROFILE (profile));
+       g_return_if_fail (id != NULL);
+
+       /* lock */
+       g_mutex_lock (&profile->priv->mutex);
+
+       /* already started */
+       item = gs_profile_item_find (profile->priv->current, id);
+       if (item == NULL) {
+               g_warning ("Not already a started task for %s", id);
+               goto out;
+       }
+
+       /* debug */
+       elapsed_ms = (item->time_stop - item->time_start) / 1000;
+       if (elapsed_ms > 1)
+               g_debug ("%s took %.0fms", id, elapsed_ms);
+
+       /* update */
+       item->time_stop = g_get_real_time ();
+
+       /* move to archive */
+       g_ptr_array_remove (profile->priv->current, item);
+       g_ptr_array_add (profile->priv->archived, item);
+out:
+       /* unlock */
+       g_mutex_unlock (&profile->priv->mutex);
+}
+
+/**
+ * gs_profile_stop_full:
+ **/
+void
+gs_profile_stop_full (GsProfile *profile, const gchar *id)
+{
+       gchar *id_thr;
+       id_thr = g_strdup_printf ("%p~%s", g_thread_self (), id);
+       gs_profile_stop (profile, id_thr);
+       g_free (id_thr);
+}
+
+/**
+ * gs_profile_sort_cb:
+ **/
+static gint
+gs_profile_sort_cb (gconstpointer a, gconstpointer b)
+{
+       GsProfileItem *item_a = *((GsProfileItem **) a);
+       GsProfileItem *item_b = *((GsProfileItem **) b);
+       if (item_a->time_start < item_b->time_start)
+               return -1;
+       if (item_a->time_start > item_b->time_start)
+               return 1;
+       return 0;
+}
+
+/**
+ * gs_profile_dump:
+ **/
+void
+gs_profile_dump (GsProfile *profile)
+{
+       GsProfileItem *item;
+       gint64 time_start = G_MAXINT64;
+       gint64 time_stop = 0;
+       gint64 time_ms;
+       guint console_width = 86;
+       guint i;
+       guint j;
+       gdouble scale;
+       guint bar_offset;
+       guint bar_length;
+
+       g_return_if_fail (GS_IS_PROFILE (profile));
+
+       /* nothing to show */
+       if (profile->priv->archived->len == 0)
+               return;
+
+       /* get the start and end times */
+       for (i = 0; i < profile->priv->archived->len; i++) {
+               item = g_ptr_array_index (profile->priv->archived, i);
+               if (item->time_start < time_start)
+                       time_start = item->time_start;
+               if (item->time_stop > time_stop)
+                       time_stop = item->time_stop;
+       }
+       scale = (gdouble) console_width / (gdouble) ((time_stop - time_start) / 1000);
+
+       /* sort the list */
+       g_ptr_array_sort (profile->priv->archived, gs_profile_sort_cb);
+
+       /* dump a list of what happened when */
+       for (i = 0; i < profile->priv->archived->len; i++) {
+               item = g_ptr_array_index (profile->priv->archived, i);
+               time_ms = (item->time_stop - item->time_start) / 1000;
+               if (time_ms < 2)
+                       continue;
+
+               /* print a timechart of what we've done */
+               bar_offset = scale * (item->time_start - time_start) / 1000;
+               for (j = 0; j < bar_offset; j++)
+                       g_print (" ");
+               bar_length = scale * time_ms;
+               if (bar_length == 0)
+                       bar_length = 1;
+               for (j = 0; j < bar_length; j++)
+                       g_print ("#");
+               for (j = bar_offset + bar_length; j < console_width + 1; j++)
+                       g_print (" ");
+               g_print ("@%04lims ", (item->time_stop - time_start) / 1000);
+               g_print ("%s %lims\n", item->id, time_ms);
+       }
+
+       /* not all complete */
+       if (profile->priv->current->len > 0) {
+               for (i = 0; i < profile->priv->current->len; i++) {
+                       item = g_ptr_array_index (profile->priv->current, i);
+                       item->time_stop = g_get_real_time ();
+                       for (j = 0; j < console_width; j++)
+                               g_print ("$");
+                       time_ms = (item->time_stop - item->time_start) / 1000;
+                       g_print (" @????ms %s %lims\n", item->id, time_ms);
+               }
+       }
+}
+
+/**
+ * gs_profile_class_init:
+ **/
+static void
+gs_profile_class_init (GsProfileClass *klass)
+{
+       GObjectClass *object_class = G_OBJECT_CLASS (klass);
+       object_class->finalize = gs_profile_finalize;
+       g_type_class_add_private (klass, sizeof (GsProfilePrivate));
+}
+
+/**
+ * gs_profile_init:
+ **/
+static void
+gs_profile_init (GsProfile *profile)
+{
+       profile->priv = GS_PROFILE_GET_PRIVATE (profile);
+       profile->priv->current = g_ptr_array_new ();
+       profile->priv->archived = g_ptr_array_new_with_free_func ((GDestroyNotify) gs_profile_item_free);
+       g_mutex_init (&profile->priv->mutex);
+}
+
+/**
+ * gs_profile_finalize:
+ **/
+static void
+gs_profile_finalize (GObject *object)
+{
+       GsProfile *profile = GS_PROFILE (object);
+       GsProfilePrivate *priv = profile->priv;
+
+       g_ptr_array_foreach (priv->current, (GFunc) gs_profile_item_free, NULL);
+       g_ptr_array_unref (priv->current);
+       g_ptr_array_unref (priv->archived);
+
+       G_OBJECT_CLASS (gs_profile_parent_class)->finalize (object);
+}
+
+/**
+ * gs_profile_new:
+ **/
+GsProfile *
+gs_profile_new (void)
+{
+       if (gs_profile_object != NULL) {
+               g_object_ref (gs_profile_object);
+       } else {
+               gs_profile_object = g_object_new (GS_TYPE_PROFILE, NULL);
+               g_object_add_weak_pointer (gs_profile_object, &gs_profile_object);
+       }
+       return GS_PROFILE (gs_profile_object);
+}
+
+/* vim: set noexpandtab: */
diff --git a/src/gs-profile.h b/src/gs-profile.h
new file mode 100644
index 0000000..ff106be
--- /dev/null
+++ b/src/gs-profile.h
@@ -0,0 +1,66 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2013 Richard Hughes <richard hughsie com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef __GS_PROFILE_H
+#define __GS_PROFILE_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GS_TYPE_PROFILE                (gs_profile_get_type ())
+#define GS_PROFILE(o)          (G_TYPE_CHECK_INSTANCE_CAST ((o), GS_TYPE_PROFILE, GsProfile))
+#define GS_PROFILE_CLASS(k)    (G_TYPE_CHECK_CLASS_CAST((k), GS_TYPE_PROFILE, GsProfileClass))
+#define GS_IS_PROFILE(o)       (G_TYPE_CHECK_INSTANCE_TYPE ((o), GS_TYPE_PROFILE))
+#define GS_IS_PROFILE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GS_TYPE_PROFILE))
+#define GS_PROFILE_GET_CLASS(o)        (G_TYPE_INSTANCE_GET_CLASS ((o), GS_TYPE_PROFILE, GsProfileClass))
+
+typedef struct GsProfilePrivate GsProfilePrivate;
+
+typedef struct
+{
+        GObject                 parent;
+        GsProfilePrivate       *priv;
+} GsProfile;
+
+typedef struct
+{
+       GObjectClass             parent_class;
+} GsProfileClass;
+
+GType           gs_profile_get_type            (void);
+
+GsProfile      *gs_profile_new                 (void);
+void            gs_profile_start               (GsProfile      *profile,
+                                                const gchar    *id);
+void            gs_profile_stop                (GsProfile      *profile,
+                                                const gchar    *id);
+void            gs_profile_start_full          (GsProfile      *profile,
+                                                const gchar    *id);
+void            gs_profile_stop_full           (GsProfile      *profile,
+                                                const gchar    *id);
+void            gs_profile_dump                (GsProfile      *profile);
+
+G_END_DECLS
+
+#endif /* __GS_PROFILE_H */
+
+/* vim: set noexpandtab: */
diff --git a/src/plugins/gs-plugin-packagekit-refine.c b/src/plugins/gs-plugin-packagekit-refine.c
index aeaf685..30a7439 100644
--- a/src/plugins/gs-plugin-packagekit-refine.c
+++ b/src/plugins/gs-plugin-packagekit-refine.c
@@ -353,6 +353,7 @@ gs_plugin_refine (GsPlugin *plugin,
        GList *updatedetails_all = NULL;
 
        /* can we resolve in one go? */
+       gs_profile_start_full (plugin->profile, "packagekit-refine[name->id]");
        for (l = list; l != NULL; l = l->next) {
                app = GS_APP (l->data);
                if (gs_app_get_metadata_item (app, "PackageKit::package-id") != NULL)
@@ -367,8 +368,10 @@ gs_plugin_refine (GsPlugin *plugin,
                                                            cancellable,
                                                            error);
        }
+       gs_profile_stop_full (plugin->profile, "packagekit-refine[name->id]");
 
        /* add any missing ratings data */
+       gs_profile_start_full (plugin->profile, "packagekit-refine[desktop-filename->id]");
        for (l = list; l != NULL; l = l->next) {
                app = GS_APP (l->data);
                if (gs_app_get_metadata_item (app, "PackageKit::package-id") != NULL)
@@ -384,8 +387,10 @@ gs_plugin_refine (GsPlugin *plugin,
                if (!ret)
                        goto out;
        }
+       gs_profile_stop_full (plugin->profile, "packagekit-refine[desktop-filename->id]");
 
        /* any update details missing? */
+       gs_profile_start_full (plugin->profile, "packagekit-refine[id->update-details]");
        for (l = list; l != NULL; l = l->next) {
                app = GS_APP (l->data);
                if (gs_app_get_state (app) != GS_APP_STATE_UPDATABLE)
@@ -402,6 +407,7 @@ gs_plugin_refine (GsPlugin *plugin,
                                                                 cancellable,
                                                                 error);
        }
+       gs_profile_stop_full (plugin->profile, "packagekit-refine[id->update-details]");
 out:
        g_list_free (resolve_all);
        g_list_free (updatedetails_all);


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