From 143554c68e692a783aae4f9c19d76de34ee1d060 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 24 Feb 2021 14:01:01 -0800 Subject: [PATCH] profiler: preroll authorization before starting This helps some annoying cases where we keep checking over and over for each CPU, procfile, etc during startup. It makes the tool less useful on systems without D-Bus, but let's be honest, profiling is a Linux only game for Sysprof. --- src/libsysprof/sysprof-local-profiler.c | 128 +++++++++++++++--------- 1 file changed, 83 insertions(+), 45 deletions(-) diff --git a/src/libsysprof/sysprof-local-profiler.c b/src/libsysprof/sysprof-local-profiler.c index 2c126e37..7de1a356 100644 --- a/src/libsysprof/sysprof-local-profiler.c +++ b/src/libsysprof/sysprof-local-profiler.c @@ -535,61 +535,18 @@ sysprof_local_profiler_wait_cb (GObject *object, } static void -sysprof_local_profiler_start (SysprofProfiler *profiler) +sysprof_local_profiler_start_after_auth (SysprofLocalProfiler *self) { - SysprofLocalProfiler *self = (SysprofLocalProfiler *)profiler; SysprofLocalProfilerPrivate *priv = sysprof_local_profiler_get_instance_private (self); - g_autoptr(SysprofControlSource) control_source = NULL; g_autofree gchar *keydata = NULL; g_autoptr(GError) error = NULL; g_autoptr(GKeyFile) keyfile = NULL; gsize keylen = 0; - g_return_if_fail (SYSPROF_IS_LOCAL_PROFILER (self)); - g_return_if_fail (priv->is_running == FALSE); - g_return_if_fail (priv->is_stopping == FALSE); - g_return_if_fail (priv->is_starting == FALSE); - - g_clear_pointer (&priv->timer, g_timer_destroy); - g_object_notify (G_OBJECT (self), "elapsed"); - - control_source = sysprof_control_source_new (); - sysprof_profiler_add_source (SYSPROF_PROFILER (self), SYSPROF_SOURCE (control_source)); + g_assert (SYSPROF_IS_LOCAL_PROFILER (self)); keyfile = g_key_file_new (); - if (priv->writer == NULL) - { - SysprofCaptureWriter *writer; - int fd; - - if ((-1 == (fd = sysprof_memfd_create ("[sysprof]"))) || - (NULL == (writer = sysprof_capture_writer_new_from_fd (fd, 0)))) - { - const GError werror = { - G_FILE_ERROR, - g_file_error_from_errno (errno), - (gchar *)g_strerror (errno) - }; - - if (fd != -1) - close (fd); - - sysprof_profiler_emit_failed (SYSPROF_PROFILER (self), &werror); - - return; - } - - sysprof_profiler_set_writer (SYSPROF_PROFILER (self), writer); - g_clear_pointer (&writer, sysprof_capture_writer_unref); - } - - priv->is_running = TRUE; - priv->is_starting = TRUE; - - if (priv->failures->len > 0) - g_ptr_array_remove_range (priv->failures, 0, priv->failures->len); - g_key_file_set_boolean (keyfile, "profiler", "whole-system", priv->whole_system); if (priv->pids->len > 0) g_key_file_set_integer_list (keyfile, "profiler", "pids", @@ -708,6 +665,87 @@ sysprof_local_profiler_start (SysprofProfiler *profiler) sysprof_local_profiler_finish_startup (self); } +static void +sysprof_local_profiler_preroll_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + SysprofHelpers *helpers = (SysprofHelpers *)object; + g_autoptr(SysprofLocalProfiler) self = user_data; + g_autoptr(GError) error = NULL; + + g_assert (SYSPROF_IS_HELPERS (helpers)); + g_assert (SYSPROF_IS_LOCAL_PROFILER (self)); + + /* For almost everything at this point, we need to have authorization + * to the helper daemon. So if this fails, just assume we are going to + * fail in general. It doesn't really help us to optimize for the case + * of user-space only profiling since we are rarely used for that. + */ + + if (!sysprof_helpers_authorize_finish (helpers, result, &error)) + sysprof_profiler_emit_failed (SYSPROF_PROFILER (self), error); + else + sysprof_local_profiler_start_after_auth (self); +} + +static void +sysprof_local_profiler_start (SysprofProfiler *profiler) +{ + SysprofLocalProfiler *self = (SysprofLocalProfiler *)profiler; + SysprofLocalProfilerPrivate *priv = sysprof_local_profiler_get_instance_private (self); + g_autoptr(SysprofControlSource) control_source = NULL; + + g_return_if_fail (SYSPROF_IS_LOCAL_PROFILER (self)); + g_return_if_fail (priv->is_running == FALSE); + g_return_if_fail (priv->is_stopping == FALSE); + g_return_if_fail (priv->is_starting == FALSE); + + g_clear_pointer (&priv->timer, g_timer_destroy); + g_object_notify (G_OBJECT (self), "elapsed"); + + control_source = sysprof_control_source_new (); + sysprof_profiler_add_source (SYSPROF_PROFILER (self), SYSPROF_SOURCE (control_source)); + + if (priv->writer == NULL) + { + SysprofCaptureWriter *writer; + int fd; + + if ((-1 == (fd = sysprof_memfd_create ("[sysprof]"))) || + (NULL == (writer = sysprof_capture_writer_new_from_fd (fd, 0)))) + { + const GError werror = { + G_FILE_ERROR, + g_file_error_from_errno (errno), + (gchar *)g_strerror (errno) + }; + + if (fd != -1) + close (fd); + + sysprof_profiler_emit_failed (SYSPROF_PROFILER (self), &werror); + + return; + } + + sysprof_profiler_set_writer (SYSPROF_PROFILER (self), writer); + g_clear_pointer (&writer, sysprof_capture_writer_unref); + } + + priv->is_running = TRUE; + priv->is_starting = TRUE; + + if (priv->failures->len > 0) + g_ptr_array_remove_range (priv->failures, 0, priv->failures->len); + + /* Start by prefolling our authorization so that future calls are cheap */ + sysprof_helpers_authorize_async (sysprof_helpers_get_default (), + NULL, + sysprof_local_profiler_preroll_cb, + g_object_ref (self)); +} + static void sysprof_local_profiler_set_writer (SysprofProfiler *profiler, SysprofCaptureWriter *writer)