From a7982ad1e85ddef64e059bf5a107dc0d423fa8e6 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 14 Apr 2016 04:36:18 -0700 Subject: [PATCH] perf: delay source start until polkit has authorized Without this, after logging in you are already multiple seconds into your profiling session recording. Not ideal. So instead, we do the async polkit auth upfront during SpSource::prepare(), and then toggle ready after we have received notification. --- lib/sp-perf-counter.c | 130 ++++++++++++++++++++++++++++++++++++++++-- lib/sp-perf-counter.h | 8 ++- lib/sp-perf-source.c | 46 +++++++++++++++ 3 files changed, 179 insertions(+), 5 deletions(-) diff --git a/lib/sp-perf-counter.c b/lib/sp-perf-counter.c index d400d5f6..dd01539f 100644 --- a/lib/sp-perf-counter.c +++ b/lib/sp-perf-counter.c @@ -118,6 +118,10 @@ G_DEFINE_BOXED_TYPE (SpPerfCounter, (GBoxedCopyFunc)sp_perf_counter_ref, (GBoxedFreeFunc)sp_perf_counter_unref) +#if ENABLE_SYSPROFD +static GDBusConnection *shared_conn; +#endif + static gboolean perf_gsource_dispatch (GSource *source, GSourceFunc callback, @@ -413,16 +417,17 @@ static GDBusProxy * get_proxy (void) { static GDBusProxy *proxy; - GDBusConnection *bus = NULL; if (proxy != NULL) return g_object_ref (proxy); - bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL); - if (bus == NULL) + if (shared_conn == NULL) + shared_conn = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL); + + if (shared_conn == NULL) return NULL; - proxy = g_dbus_proxy_new_sync (bus, + proxy = g_dbus_proxy_new_sync (shared_conn, (G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS | G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START_AT_CONSTRUCTION), @@ -490,8 +495,125 @@ get_authorized_proxy (void) return NULL; } + + +static void +sp_perf_counter_acquire_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + g_autoptr(GTask) task = user_data; + GPermission *permission = (GPermission *)object; + GError *error = NULL; + + g_assert (G_IS_PERMISSION (permission)); + g_assert (G_IS_ASYNC_RESULT (result)); + g_assert (G_IS_TASK (task)); + + if (!g_permission_acquire_finish (permission, result, &error)) + { + g_task_return_error (task, error); + return; + } + + g_task_return_boolean (task, TRUE); +} + +static void +sp_perf_counter_permission_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + g_autoptr(GTask) task = user_data; + GPermission *permission; + GError *error = NULL; + + g_assert (G_IS_ASYNC_RESULT (result)); + g_assert (G_IS_TASK (task)); + + if (NULL == (permission = polkit_permission_new_finish (result, &error))) + { + g_task_return_error (task, error); + return; + } + + g_permission_acquire_async (permission, + g_task_get_cancellable (task), + sp_perf_counter_acquire_cb, + g_object_ref (task)); + + g_object_unref (permission); +} + +static void +sp_perf_counter_get_bus_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + g_autoptr(GDBusConnection) bus = NULL; + g_autoptr(GTask) task = user_data; + PolkitSubject *subject = NULL; + const gchar *name; + GError *error = NULL; + + g_assert (G_IS_ASYNC_RESULT (result)); + g_assert (G_IS_TASK (task)); + + if (NULL == (bus = g_bus_get_finish (result, &error))) + { + g_task_return_error (task, error); + return; + } + + shared_conn = g_object_ref (bus); + + name = g_dbus_connection_get_unique_name (bus); + subject = polkit_system_bus_name_new (name); + + polkit_permission_new ("org.gnome.sysprof2.perf-event-open", + subject, + g_task_get_cancellable (task), + sp_perf_counter_permission_cb, + g_object_ref (task)); + + g_object_unref (subject); +} + #endif +void +sp_perf_counter_authorize_async (GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_autoptr(GTask) task = NULL; + + g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); + + task = g_task_new (NULL, cancellable, callback, user_data); + +#if ENABLE_SYSPROFD + g_bus_get (G_BUS_TYPE_SYSTEM, + cancellable, + sp_perf_counter_get_bus_cb, + g_object_ref (task)); +#else + g_task_return_new_error (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "Sysprofd is not supported in current configuration"); +#endif +} + +gboolean +sp_perf_counter_authorize_finish (GAsyncResult *result, + GError **error) +{ + g_assert (G_IS_TASK (result)); + + return g_task_propagate_boolean (G_TASK (result), error); +} + gint sp_perf_counter_open (SpPerfCounter *self, struct perf_event_attr *attr, diff --git a/lib/sp-perf-counter.h b/lib/sp-perf-counter.h index d90f8b99..2d870ad6 100644 --- a/lib/sp-perf-counter.h +++ b/lib/sp-perf-counter.h @@ -19,7 +19,7 @@ #ifndef SP_PERF_COUNTER_H #define SP_PERF_COUNTER_H -#include +#include #include @@ -110,6 +110,12 @@ typedef void (*SpPerfCounterCallback) (SpPerfCounterEvent *event, guint cpu, gpointer user_data); +void sp_perf_counter_authorize_async (GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean sp_perf_counter_authorize_finish (GAsyncResult *result, + GError **error); + GType sp_perf_counter_get_type (void); SpPerfCounter *sp_perf_counter_new (GMainContext *context); void sp_perf_counter_set_callback (SpPerfCounter *self, diff --git a/lib/sp-perf-source.c b/lib/sp-perf-source.c index 8a4f7540..e5d44559 100644 --- a/lib/sp-perf-source.c +++ b/lib/sp-perf-source.c @@ -55,6 +55,7 @@ struct _SpPerfSource GHashTable *pids; guint running : 1; + guint is_ready : 1; }; static void source_iface_init (SpSourceInterface *iface); @@ -430,6 +431,49 @@ sp_perf_source_add_pid (SpSource *source, g_hash_table_add (self->pids, GINT_TO_POINTER (pid)); } +static void +sp_perf_source_authorize_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + g_autoptr(SpPerfSource) self = user_data; + g_autoptr(GError) error = NULL; + + g_assert (G_IS_ASYNC_RESULT (result)); + + if (!sp_perf_counter_authorize_finish (result, &error)) + { + sp_source_emit_failed (SP_SOURCE (self), error); + return; + } + + self->is_ready = TRUE; + + sp_source_emit_ready (SP_SOURCE (self)); +} + +static void +sp_perf_source_prepare (SpSource *source) +{ + SpPerfSource *self = (SpPerfSource *)source; + + g_assert (SP_IS_PERF_SOURCE (self)); + + sp_perf_counter_authorize_async (NULL, + sp_perf_source_authorize_cb, + g_object_ref (self)); +} + +static gboolean +sp_perf_source_get_is_ready (SpSource *source) +{ + SpPerfSource *self = (SpPerfSource *)source; + + g_assert (SP_IS_PERF_SOURCE (self)); + + return self->is_ready; +} + static void source_iface_init (SpSourceInterface *iface) { @@ -437,6 +481,8 @@ source_iface_init (SpSourceInterface *iface) iface->stop = sp_perf_source_stop; iface->set_writer = sp_perf_source_set_writer; iface->add_pid = sp_perf_source_add_pid; + iface->prepare = sp_perf_source_prepare; + iface->get_is_ready = sp_perf_source_get_is_ready; } SpSource *