mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-02-23 18:52:09 +01:00
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
This commit is contained in:
parent
728857e261
commit
6fb5b6d0f2
135
gio/gtask.c
135
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);
|
||||
}
|
||||
|
||||
|
@ -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__ */
|
||||
|
@ -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()
|
||||
|
Loading…
x
Reference in New Issue
Block a user