[glib/wip/alexl/gtask-bt] Add some support code to GTask and a gtask-bt gdb command



commit 6fb5b6d0f293a8a7ae67301c2b4f6f7187959d36
Author: Alexander Larsson <alexl redhat com>
Date:   Tue Aug 29 12:54:09 2017 +0200

    Add some support code to GTask and a gtask-bt gdb command
    
    Whenever we invoke the GTask callback we push the current
    task to a thread-local variable, and during construction we
    pick this up as the "parent" of the task which lets us
    construct call chains for GTask invocations.
    
    Additionally, g_task_new is #defined to g_task_new_with_caller
    with an additional __builtin_return_address(0) argument, which
    gives the caller of the function that created the GTask, which
    is typically the interesting user-level code.
    
    We then read this back in a gtask-bt gdb command that prints a
    backtrace like:
    
    Thread 5 "gdbus" hit Breakpoint 1, g_task_new_with_caller (source_object=source_object@entry=0x1003e0130, 
cancellable=cancellable@entry=0x1003bbf00,
        callback=callback@entry=0x7fa8d0efa890 <_g_dbus_worker_do_read_cb>, 
callback_data=callback_data@entry=0x1003e3800, caller=0x7fa8d0ef8a81 <_g_dbus_worker_do_read_unlocked+161>) 
at gtask.c:743
    743 {
    (gdb) gtask-bt
     #1 GTask 0x7fa8b4004cc0 - _g_dbus_worker_do_read_unlocked (gdbusprivate.c:854)
         _g_socket_read_with_control_messages -> _g_dbus_worker_do_read_cb
     #2 GTask 0x7fa8b8012200 - _g_dbus_worker_do_read_unlocked (gdbusprivate.c:854)
         _g_socket_read_with_control_messages -> _g_dbus_worker_do_read_cb
     #3 GTask 0x7fa8b80122f0 - _g_dbus_worker_do_read_unlocked (gdbusprivate.c:854)
         _g_socket_read_with_control_messages -> _g_dbus_worker_do_read_cb
     #4 GTask 0x7fa8b80123e0 - _g_dbus_worker_do_read_unlocked (gdbusprivate.c:854)
         _g_socket_read_with_control_messages -> _g_dbus_worker_do_read_cb
     #5 GTask 0x7fa8b80124d0 - _g_dbus_worker_do_read_unlocked (gdbusprivate.c:854)
         _g_socket_read_with_control_messages -> _g_dbus_worker_do_read_cb
     #6 GTask 0x7fa8b8012020 - _g_dbus_worker_do_read_unlocked (gdbusprivate.c:854)
         _g_socket_read_with_control_messages -> _g_dbus_worker_do_read_cb
     #7 GTask 0x100c5c690 - _g_dbus_worker_do_read_unlocked (gdbusprivate.c:854)
         _g_socket_read_with_control_messages -> _g_dbus_worker_do_read_cb
     #8 GTask 0x100988030 - _g_dbus_worker_do_read_unlocked (gdbusprivate.c:854)
         _g_socket_read_with_control_messages -> _g_dbus_worker_do_read_cb
     #9 GTask 0x1009886c0 - _g_dbus_worker_do_read_unlocked (gdbusprivate.c:854)
          _g_socket_read_with_control_messages -> _g_dbus_worker_do_read_cb
     #10 GTask 0x1009883f0 - _g_dbus_worker_do_read_unlocked (gdbusprivate.c:854)
         _g_socket_read_with_control_messages -> _g_dbus_worker_do_read_cb
     #11 GTask 0x1009d1a00 - _g_dbus_worker_do_read_unlocked (gdbusprivate.c:854)
         _g_socket_read_with_control_messages -> _g_dbus_worker_do_read_cb
     #12 GTask 0x100766930 - _g_dbus_worker_do_read_unlocked (gdbusprivate.c:854)
         _g_socket_read_with_control_messages -> _g_dbus_worker_do_read_cb
     #13 GTask 0x10072dae0 - _g_dbus_worker_do_read_unlocked (gdbusprivate.c:854)
         _g_socket_read_with_control_messages -> _g_dbus_worker_do_read_cb

 gio/gtask.c      |  135 ++++++++++++++++++++++++++++++++++++++++++++++--------
 gio/gtask.h      |    9 ++++
 glib/glib_gdb.py |   29 ++++++++++++
 3 files changed, 154 insertions(+), 19 deletions(-)
---
diff --git a/gio/gtask.c b/gio/gtask.c
index 7eb0185..c18f499 100644
--- a/gio/gtask.c
+++ b/gio/gtask.c
@@ -27,6 +27,8 @@
 
 #include "glibintl.h"
 
+#undef g_task_new
+
 /**
  * SECTION:gtask
  * @short_description: Cancellable synchronous or asynchronous task
@@ -544,6 +546,11 @@ struct _GTask {
   gpointer task_data;
   GDestroyNotify task_data_destroy;
 
+  GTask *invoke_stack_parent;
+  GTask *parent;
+  guint n_children;
+  gpointer caller;
+
   GMainContext *context;
   gint64 creation_time;
   gint priority;
@@ -601,6 +608,32 @@ static GSource *task_pool_manager;
 static guint64 task_wait_time;
 static gint tasks_running;
 
+__thread GTask *g_task_invoke_stack = NULL;
+
+static void
+g_task_push (GTask *task)
+{
+  g_assert (task->invoke_stack_parent == NULL);
+  task->invoke_stack_parent = g_task_invoke_stack;
+  g_task_invoke_stack = task;
+}
+
+static void
+g_task_pop (GTask *task)
+{
+  g_assert (g_task_invoke_stack == task);
+  g_task_invoke_stack = task->invoke_stack_parent;
+  task->invoke_stack_parent = NULL;
+}
+
+GTask * g_task_peek (void);
+
+GTask *
+g_task_peek (void)
+{
+  return g_task_invoke_stack;
+}
+
 /* When the task pool fills up and blocks, and the program keeps
  * queueing more tasks, we will slowly add more threads to the pool
  * (in case the existing tasks are trying to queue subtasks of their
@@ -617,10 +650,51 @@ static gint tasks_running;
 #define G_TASK_WAIT_TIME_MULTIPLIER 1.03
 #define G_TASK_WAIT_TIME_MAX (30 * 60 * 1000000)
 
+static GList *all_tasks = NULL;
+G_LOCK_DEFINE_STATIC (all_tasks);
+
+static int
+g_task_depth (GTask *task)
+{
+  if (task->parent)
+    return 1 + g_task_depth (task->parent);
+  return 1;
+}
+
 static void
 g_task_init (GTask *task)
 {
+  GTask *parent;
+
+  G_LOCK (all_tasks);
+  all_tasks = g_list_prepend (all_tasks, task);
+  G_UNLOCK (all_tasks);
+
   task->check_cancellable = TRUE;
+  parent = g_task_peek ();
+  if (parent)
+    {
+      task->parent = g_object_ref (parent);
+      parent->n_children++;
+      g_print ("GTask %p created with parent %p (depth %d)\n", task, parent, g_task_depth (task));
+    }
+}
+
+gpointer * g_tasks_get (void);
+
+gpointer *
+g_tasks_get (void)
+{
+  GPtrArray *array = g_ptr_array_new ();
+  GList *l;
+
+  G_LOCK (all_tasks);
+  for (l = all_tasks; l != NULL; l = l->next)
+    g_ptr_array_add (array, l->data);
+  g_ptr_array_add (array, NULL);
+  G_UNLOCK (all_tasks);
+
+  return g_ptr_array_free (array, FALSE);
 }
 
 static void
@@ -628,6 +702,16 @@ g_task_finalize (GObject *object)
 {
   GTask *task = G_TASK (object);
 
+  G_LOCK (all_tasks);
+  all_tasks = g_list_remove (all_tasks, task);
+  G_UNLOCK (all_tasks);
+  if (task->parent)
+    {
+      g_print ("GTask %p freed with parent %p\n", task, task->parent);
+      task->parent->n_children--;
+      g_clear_object (&task->parent);
+    }
+
   g_clear_object (&task->source_object);
   g_clear_object (&task->cancellable);
 
@@ -652,6 +736,35 @@ g_task_finalize (GObject *object)
   G_OBJECT_CLASS (g_task_parent_class)->finalize (object);
 }
 
+GTask *
+g_task_new_with_caller (gpointer              source_object,
+                        GCancellable         *cancellable,
+                        GAsyncReadyCallback   callback,
+                        gpointer              callback_data,
+                        gpointer              caller)
+{
+  GTask *task;
+  GSource *source;
+
+  task = g_object_new (G_TYPE_TASK, NULL);
+  task->source_object = source_object ? g_object_ref (source_object) : NULL;
+  task->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
+  task->caller = caller;
+  task->callback = callback;
+  task->callback_data = callback_data;
+  task->context = g_main_context_ref_thread_default ();
+
+  source = g_main_current_source ();
+  if (source)
+    task->creation_time = g_source_get_time (source);
+
+  TRACE (GIO_TASK_NEW (task, source_object, cancellable,
+                       callback, callback_data));
+
+  return task;
+}
+
+
 /**
  * g_task_new:
  * @source_object: (nullable) (type GObject): the #GObject that owns
@@ -687,24 +800,7 @@ g_task_new (gpointer              source_object,
             GAsyncReadyCallback   callback,
             gpointer              callback_data)
 {
-  GTask *task;
-  GSource *source;
-
-  task = g_object_new (G_TYPE_TASK, NULL);
-  task->source_object = source_object ? g_object_ref (source_object) : NULL;
-  task->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
-  task->callback = callback;
-  task->callback_data = callback_data;
-  task->context = g_main_context_ref_thread_default ();
-
-  source = g_main_current_source ();
-  if (source)
-    task->creation_time = g_source_get_time (source);
-
-  TRACE (GIO_TASK_NEW (task, source_object, cancellable,
-                       callback, callback_data));
-
-  return task;
+  return g_task_new_with_caller (source_object, cancellable, callback, callback_data, NULL);
 }
 
 /**
@@ -1131,7 +1227,6 @@ g_task_get_source_tag (GTask *task)
   return task->source_tag;
 }
 
-
 static void
 g_task_return_now (GTask *task)
 {
@@ -1139,6 +1234,7 @@ g_task_return_now (GTask *task)
                                  task->callback_data));
 
   g_main_context_push_thread_default (task->context);
+  g_task_push (task);
 
   if (task->callback != NULL)
     {
@@ -1150,6 +1246,7 @@ g_task_return_now (GTask *task)
   task->completed = TRUE;
   g_object_notify (G_OBJECT (task), "completed");
 
+  g_task_pop (task);
   g_main_context_pop_thread_default (task->context);
 }
 
diff --git a/gio/gtask.h b/gio/gtask.h
index 92cd2b1..25d6241 100644
--- a/gio/gtask.h
+++ b/gio/gtask.h
@@ -44,6 +44,12 @@ GTask        *g_task_new                   (gpointer             source_object,
                                             GCancellable        *cancellable,
                                             GAsyncReadyCallback  callback,
                                             gpointer             callback_data);
+GLIB_AVAILABLE_IN_2_54
+GTask        *g_task_new_with_caller       (gpointer             source_object,
+                                            GCancellable        *cancellable,
+                                            GAsyncReadyCallback  callback,
+                                            gpointer             callback_data,
+                                            gpointer             caller);
 
 GLIB_AVAILABLE_IN_2_36
 void          g_task_report_error          (gpointer             source_object,
@@ -155,6 +161,9 @@ gboolean      g_task_had_error                 (GTask           *task);
 GLIB_AVAILABLE_IN_2_44
 gboolean      g_task_get_completed             (GTask           *task);
 
+
+#define g_task_new(_o,_c,_cb,_cbd) g_task_new_with_caller(_o,_c,_cb,_cbd,__builtin_return_address(0))
+
 G_END_DECLS
 
 #endif /* __G_TASK_H__ */
diff --git a/glib/glib_gdb.py b/glib/glib_gdb.py
index 38f101a..0a56eff 100644
--- a/glib/glib_gdb.py
+++ b/glib/glib_gdb.py
@@ -259,3 +259,32 @@ class ForeachCommand (gdb.Command):
         func(var, container, command)
 
 ForeachCommand ()
+
+
+class GTaskBacktrace (gdb.Command):
+      """GTask backtrace"""
+      def __init__ (self):
+            super (GTaskBacktrace, self).__init__ ("gtask-bt", gdb.COMMAND_STACK)
+      def decode_pc_function(self, value):
+            block = gdb.block_for_pc(int(value))
+            return block.function.name
+      def decode_pc(self, value):
+            f = self.decode_pc_function(value)
+            sal = gdb.find_pc_line(int(value))
+            return "%s (%s:%d)" % (f, sal.symtab.filename, sal.line)
+      def print_task_frame(self, frame, task):
+            print ("#%d GTask %s - %s\n     %s -> %s" % (
+                  frame,
+                  hex(int(task)),
+                  self.decode_pc(task["caller"]),
+                  self.decode_pc_function(task["source_tag"]),
+                  self.decode_pc_function(task["callback"])))
+      def invoke (self, arg, from_tty):
+            task = gdb.lookup_global_symbol("g_task_invoke_stack").value()
+            frame = 1
+            while int(task) != 0:
+                  self.print_task_frame(frame, task)
+                  task = task["parent"]
+                  frame = frame + 1
+
+GTaskBacktrace()


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