diff options
author | Alexander Larsson <alexl@redhat.com> | 2017-08-29 12:54:09 +0200 |
---|---|---|
committer | Alexander Larsson <alexl@redhat.com> | 2017-08-29 13:42:48 +0200 |
commit | 6fb5b6d0f293a8a7ae67301c2b4f6f7187959d36 (patch) | |
tree | 4d0e816d5e5fccf72221a726c33b154c0c4bc774 | |
parent | 728857e2610560a876c41840ac6c69b3ad4f4e8b (diff) | |
download | glib-wip/alexl/gtask-bt.tar.gz |
Add some support code to GTask and a gtask-bt gdb commandwip/alexl/gtask-bt
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
-rw-r--r-- | gio/gtask.c | 135 | ||||
-rw-r--r-- | gio/gtask.h | 9 | ||||
-rw-r--r-- | glib/glib_gdb.py | 29 |
3 files changed, 154 insertions, 19 deletions
diff --git a/gio/gtask.c b/gio/gtask.c index 7eb018560..c18f499b2 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 92cd2b144..25d6241c8 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 38f101ad5..0a56eff14 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() |