From 788b1995b956d1677e6593b0bb12f905ca25a73d Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 9 Oct 2024 21:42:24 -0700 Subject: [PATCH 1/5] libsysprof-capture: add version macros for 48 --- src/libsysprof-capture/sysprof-version-macros.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/libsysprof-capture/sysprof-version-macros.h b/src/libsysprof-capture/sysprof-version-macros.h index 67dac287..5bfb4a25 100644 --- a/src/libsysprof-capture/sysprof-version-macros.h +++ b/src/libsysprof-capture/sysprof-version-macros.h @@ -91,6 +91,7 @@ #define SYSPROF_VERSION_3_38 (SYSPROF_ENCODE_VERSION (3, 38, 0)) #define SYSPROF_VERSION_3_40 (SYSPROF_ENCODE_VERSION (3, 40, 0)) #define SYSPROF_VERSION_3_46 (SYSPROF_ENCODE_VERSION (3, 46, 0)) +#define SYSPROF_VERSION_48 (SYSPROF_ENCODE_VERSION (48, 0, 0)) #if (SYSPROF_MINOR_VERSION == 99) # define SYSPROF_VERSION_CUR_STABLE (SYSPROF_ENCODE_VERSION (SYSPROF_MAJOR_VERSION + 1, 0, 0)) @@ -232,3 +233,17 @@ #else # define SYSPROF_AVAILABLE_IN_3_46 _SYSPROF_EXTERN #endif + +#if SYSPROF_VERSION_MIN_REQUIRED >= SYSPROF_VERSION_48 +# define SYSPROF_DEPRECATED_IN_48 SYSPROF_DEPRECATED +# define SYSPROF_DEPRECATED_IN_48_FOR(f) SYSPROF_DEPRECATED_FOR(f) +#else +# define SYSPROF_DEPRECATED_IN_48 _SYSPROF_EXTERN +# define SYSPROF_DEPRECATED_IN_48_FOR(f) _SYSPROF_EXTERN +#endif + +#if SYSPROF_VERSION_MAX_ALLOWED < SYSPROF_VERSION_48 +# define SYSPROF_AVAILABLE_IN_48 SYSPROF_UNAVAILABLE(48, 0) +#else +# define SYSPROF_AVAILABLE_IN_48 _SYSPROF_EXTERN +#endif From 50d556b13e1a9e7171e2dcb2b5241639134814a6 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 10 Oct 2024 17:02:06 -0700 Subject: [PATCH 2/5] sysprof: add SysprofDocumentTask abstraction This provides a task abstraction to SysprofDocumentLoader so that we can elevate information about tasks to the user interface. It also moves the spinner to a menu button w/ popover to display those tasks. --- src/libsysprof/meson.build | 2 + .../sysprof-document-loader-private.h | 34 ++ src/libsysprof/sysprof-document-loader.c | 129 ++++++- src/libsysprof/sysprof-document-loader.h | 2 + .../sysprof-document-task-private.h | 53 +++ src/libsysprof/sysprof-document-task.c | 327 ++++++++++++++++++ src/libsysprof/sysprof-document-task.h | 46 +++ src/libsysprof/sysprof.h | 1 + src/sysprof/meson.build | 1 + src/sysprof/style.css | 9 + src/sysprof/sysprof-task-row.c | 139 ++++++++ src/sysprof/sysprof-task-row.h | 32 ++ src/sysprof/sysprof-task-row.ui | 71 ++++ src/sysprof/sysprof-window.c | 36 +- src/sysprof/sysprof-window.ui | 75 +++- src/sysprof/sysprof.gresource.xml | 1 + 16 files changed, 937 insertions(+), 21 deletions(-) create mode 100644 src/libsysprof/sysprof-document-loader-private.h create mode 100644 src/libsysprof/sysprof-document-task-private.h create mode 100644 src/libsysprof/sysprof-document-task.c create mode 100644 src/libsysprof/sysprof-document-task.h create mode 100644 src/sysprof/sysprof-task-row.c create mode 100644 src/sysprof/sysprof-task-row.h create mode 100644 src/sysprof/sysprof-task-row.ui diff --git a/src/libsysprof/meson.build b/src/libsysprof/meson.build index fd53ee11..697e9665 100644 --- a/src/libsysprof/meson.build +++ b/src/libsysprof/meson.build @@ -32,6 +32,7 @@ libsysprof_public_sources = [ 'sysprof-document-overlay.c', 'sysprof-document-process.c', 'sysprof-document-sample.c', + 'sysprof-document-task.c', 'sysprof-document-traceable.c', 'sysprof-document.c', 'sysprof-elf-symbolizer.c', @@ -96,6 +97,7 @@ libsysprof_public_headers = [ 'sysprof-document-overlay.h', 'sysprof-document-process.h', 'sysprof-document-sample.h', + 'sysprof-document-task.h', 'sysprof-document-traceable.h', 'sysprof-document.h', 'sysprof-elf-symbolizer.h', diff --git a/src/libsysprof/sysprof-document-loader-private.h b/src/libsysprof/sysprof-document-loader-private.h new file mode 100644 index 00000000..f7982489 --- /dev/null +++ b/src/libsysprof/sysprof-document-loader-private.h @@ -0,0 +1,34 @@ +/* + * sysprof-document-loader-private.h + * + * Copyright 2024 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "sysprof-document-loader.h" +#include "sysprof-document-task.h" + +G_BEGIN_DECLS + +void _sysprof_document_loader_add_task (SysprofDocumentLoader *self, + SysprofDocumentTask *task); +void _sysprof_document_loader_remove_task (SysprofDocumentLoader *self, + SysprofDocumentTask *task); + +G_END_DECLS diff --git a/src/libsysprof/sysprof-document-loader.c b/src/libsysprof/sysprof-document-loader.c index 6622f325..3cd408c4 100644 --- a/src/libsysprof/sysprof-document-loader.c +++ b/src/libsysprof/sysprof-document-loader.c @@ -27,18 +27,21 @@ #include #include "sysprof-bundled-symbolizer.h" +#include "sysprof-debuginfod-symbolizer.h" #include "sysprof-document-bitset-index-private.h" -#include "sysprof-document-loader.h" +#include "sysprof-document-loader-private.h" #include "sysprof-document-private.h" #include "sysprof-elf-symbolizer.h" #include "sysprof-jitmap-symbolizer.h" #include "sysprof-kallsyms-symbolizer.h" #include "sysprof-multi-symbolizer.h" +#include "sysprof-symbolizer-private.h" struct _SysprofDocumentLoader { GObject parent_instance; GMutex mutex; + GListStore *tasks; SysprofSymbolizer *symbolizer; char *filename; char *message; @@ -53,6 +56,7 @@ enum { PROP_FRACTION, PROP_MESSAGE, PROP_SYMBOLIZER, + PROP_TASKS, N_PROPS }; @@ -192,6 +196,8 @@ static void set_default_symbolizer (SysprofDocumentLoader *self) { g_autoptr(SysprofMultiSymbolizer) multi = NULL; + g_autoptr(SysprofSymbolizer) debuginfod = NULL; + g_autoptr(GError) error = NULL; g_assert (SYSPROF_IS_DOCUMENT_LOADER (self)); @@ -202,9 +208,25 @@ set_default_symbolizer (SysprofDocumentLoader *self) sysprof_multi_symbolizer_take (multi, sysprof_kallsyms_symbolizer_new ()); sysprof_multi_symbolizer_take (multi, sysprof_elf_symbolizer_new ()); sysprof_multi_symbolizer_take (multi, sysprof_jitmap_symbolizer_new ()); + + if (!(debuginfod = sysprof_debuginfod_symbolizer_new (&error))) + g_warning ("Failed to create debuginfod symbolizer: %s", error->message); + else + sysprof_multi_symbolizer_take (multi, g_steal_pointer (&debuginfod)); + self->symbolizer = SYSPROF_SYMBOLIZER (g_steal_pointer (&multi)); } +static void +sysprof_document_loader_dispose (GObject *object) +{ + SysprofDocumentLoader *self = (SysprofDocumentLoader *)object; + + g_list_store_remove_all (self->tasks); + + G_OBJECT_CLASS (sysprof_document_loader_parent_class)->dispose (object); +} + static void sysprof_document_loader_finalize (GObject *object) { @@ -212,6 +234,7 @@ sysprof_document_loader_finalize (GObject *object) g_clear_handle_id (&self->notify_source, g_source_remove); g_clear_object (&self->symbolizer); + g_clear_object (&self->tasks); g_clear_pointer (&self->filename, g_free); g_clear_pointer (&self->message, g_free); g_clear_fd (&self->fd, NULL); @@ -242,6 +265,10 @@ sysprof_document_loader_get_property (GObject *object, g_value_set_object (value, sysprof_document_loader_get_symbolizer (self)); break; + case PROP_TASKS: + g_value_take_object (value, sysprof_document_loader_list_tasks (self)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } @@ -271,6 +298,7 @@ sysprof_document_loader_class_init (SysprofDocumentLoaderClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); + object_class->dispose = sysprof_document_loader_dispose; object_class->finalize = sysprof_document_loader_finalize; object_class->get_property = sysprof_document_loader_get_property; object_class->set_property = sysprof_document_loader_set_property; @@ -290,6 +318,11 @@ sysprof_document_loader_class_init (SysprofDocumentLoaderClass *klass) SYSPROF_TYPE_SYMBOLIZER, (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); + properties[PROP_TASKS] = + g_param_spec_object ("tasks", NULL, NULL, + G_TYPE_LIST_MODEL, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_properties (object_class, N_PROPS, properties); g_type_ensure (SYSPROF_TYPE_DOCUMENT); @@ -302,6 +335,7 @@ sysprof_document_loader_init (SysprofDocumentLoader *self) g_mutex_init (&self->mutex); self->fd = -1; + self->tasks = g_list_store_new (SYSPROF_TYPE_DOCUMENT_TASK); set_default_symbolizer (self); } @@ -546,6 +580,8 @@ sysprof_document_loader_load_async (SysprofDocumentLoader *self, set_progress (0., _("Loading document"), self); + _sysprof_symbolizer_setup (self->symbolizer, self); + if (self->fd != -1) mapped_file_new_from_fd_async (self->fd, cancellable, @@ -657,3 +693,94 @@ sysprof_document_loader_load (SysprofDocumentLoader *self, return state.document; } + +typedef struct _TaskOp +{ + SysprofDocumentLoader *loader; + SysprofDocumentTask *task; + guint remove : 1; +} TaskOp; + +static void +_g_list_store_remove (GListStore *store, + gpointer instance) +{ + GListModel *model = G_LIST_MODEL (store); + guint n_items = g_list_model_get_n_items (model); + + for (guint i = 0; i < n_items; i++) + { + g_autoptr(GObject) element = g_list_model_get_item (model, i); + + if (element == instance) + { + g_list_store_remove (store, i); + return; + } + } +} + +static gboolean +task_op_run (gpointer data) +{ + TaskOp *op = data; + + if (op->remove) + _g_list_store_remove (op->loader->tasks, op->task); + else + g_list_store_append (op->loader->tasks, op->task); + + g_clear_object (&op->loader); + g_clear_object (&op->task); + g_free (op); + + return G_SOURCE_REMOVE; +} + +static TaskOp * +task_op_new (SysprofDocumentLoader *loader, + SysprofDocumentTask *task, + gboolean remove) +{ + TaskOp op = { + g_object_ref (loader), + g_object_ref (task), + !!remove + }; + + return g_memdup2 (&op, sizeof op); +} + +void +_sysprof_document_loader_add_task (SysprofDocumentLoader *self, + SysprofDocumentTask *task) +{ + g_return_if_fail (SYSPROF_IS_DOCUMENT_LOADER (self)); + g_return_if_fail (SYSPROF_IS_DOCUMENT_TASK (task)); + + g_idle_add (task_op_run, task_op_new (self, task, FALSE)); +} + +void +_sysprof_document_loader_remove_task (SysprofDocumentLoader *self, + SysprofDocumentTask *task) +{ + g_return_if_fail (SYSPROF_IS_DOCUMENT_LOADER (self)); + g_return_if_fail (SYSPROF_IS_DOCUMENT_TASK (task)); + + g_idle_add (task_op_run, task_op_new (self, task, TRUE)); +} + +/** + * sysprof_document_loader_list_tasks: + * @self: a #SysprofDocumentLoader + * + * Returns: (transfer full): a #GListModel of #SysprofDocumentTask. + */ +GListModel * +sysprof_document_loader_list_tasks (SysprofDocumentLoader *self) +{ + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_LOADER (self), NULL); + + return g_object_ref (G_LIST_MODEL (self->tasks)); +} diff --git a/src/libsysprof/sysprof-document-loader.h b/src/libsysprof/sysprof-document-loader.h index 289dae30..1a1ea7da 100644 --- a/src/libsysprof/sysprof-document-loader.h +++ b/src/libsysprof/sysprof-document-loader.h @@ -48,6 +48,8 @@ SYSPROF_AVAILABLE_IN_ALL double sysprof_document_loader_get_fraction (SysprofDocumentLoader *self); SYSPROF_AVAILABLE_IN_ALL const char *sysprof_document_loader_get_message (SysprofDocumentLoader *self); +SYSPROF_AVAILABLE_IN_48 +GListModel *sysprof_document_loader_list_tasks (SysprofDocumentLoader *self); SYSPROF_AVAILABLE_IN_ALL SysprofDocument *sysprof_document_loader_load (SysprofDocumentLoader *self, GCancellable *cancellable, diff --git a/src/libsysprof/sysprof-document-task-private.h b/src/libsysprof/sysprof-document-task-private.h new file mode 100644 index 00000000..3590b234 --- /dev/null +++ b/src/libsysprof/sysprof-document-task-private.h @@ -0,0 +1,53 @@ +/* + * sysprof-document-task-private.h + * + * Copyright 2024 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +#include "sysprof-document-loader.h" +#include "sysprof-document-task.h" + +G_BEGIN_DECLS + +typedef struct _SysprofDocumentTask SysprofDocumentTaskScope; + +struct _SysprofDocumentTaskClass +{ + GObjectClass parent_class; + + void (*cancel) (SysprofDocumentTask *self); +}; + +GCancellable *_sysprof_document_task_get_cancellable (SysprofDocumentTask *self); +void _sysprof_document_task_set_title (SysprofDocumentTask *self, + const char *title); +void _sysprof_document_task_take_message (SysprofDocumentTask *self, + char *message); +void _sysprof_document_task_set_progress (SysprofDocumentTask *self, + double progress); +SysprofDocumentTaskScope *_sysprof_document_task_register (SysprofDocumentTask *self, + SysprofDocumentLoader *loader); +void _sysprof_document_task_unregister (SysprofDocumentTask *self); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofDocumentTaskScope, _sysprof_document_task_unregister) + +G_END_DECLS diff --git a/src/libsysprof/sysprof-document-task.c b/src/libsysprof/sysprof-document-task.c new file mode 100644 index 00000000..a8bfb30a --- /dev/null +++ b/src/libsysprof/sysprof-document-task.c @@ -0,0 +1,327 @@ +/* + * sysprof-document-task.c + * + * Copyright 2024 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-document-loader-private.h" +#include "sysprof-document-task-private.h" + +typedef struct +{ + GMutex mutex; + char *message; + char *title; + double progress; + GCancellable *cancellable; + guint notify_source; + GWeakRef loader_wr; +} SysprofDocumentTaskPrivate; + +enum { + PROP_0, + PROP_CANCELLED, + PROP_MESSAGE, + PROP_PROGRESS, + PROP_TITLE, + N_PROPS +}; + +G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (SysprofDocumentTask, sysprof_document_task, G_TYPE_OBJECT) + +static GParamSpec *properties[N_PROPS]; + +static void +sysprof_document_task_finalize (GObject *object) +{ + SysprofDocumentTask *self = (SysprofDocumentTask *)object; + SysprofDocumentTaskPrivate *priv = sysprof_document_task_get_instance_private (self); + + g_mutex_clear (&priv->mutex); + g_clear_handle_id (&priv->notify_source, g_source_remove); + g_clear_pointer (&priv->message, g_free); + g_clear_pointer (&priv->title, g_free); + g_clear_object (&priv->cancellable); + g_weak_ref_clear (&priv->loader_wr); + + G_OBJECT_CLASS (sysprof_document_task_parent_class)->finalize (object); +} + +static void +sysprof_document_task_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SysprofDocumentTask *self = SYSPROF_DOCUMENT_TASK (object); + + switch (prop_id) + { + case PROP_CANCELLED: + g_value_set_boolean (value, sysprof_document_task_is_cancelled (self)); + break; + + case PROP_MESSAGE: + g_value_take_string (value, sysprof_document_task_dup_message (self)); + break; + + case PROP_PROGRESS: + g_value_set_double (value, sysprof_document_task_get_progress (self)); + break; + + case PROP_TITLE: + g_value_take_string (value, sysprof_document_task_dup_title (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_document_task_class_init (SysprofDocumentTaskClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = sysprof_document_task_finalize; + object_class->get_property = sysprof_document_task_get_property; + + properties[PROP_CANCELLED] = + g_param_spec_boolean ("cancelled", NULL, NULL, + FALSE, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + properties[PROP_MESSAGE] = + g_param_spec_string ("message", NULL, NULL, + NULL, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + properties[PROP_PROGRESS] = + g_param_spec_double ("progress", NULL, NULL, + 0., 1., 0., + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + properties[PROP_TITLE] = + g_param_spec_string ("title", NULL, NULL, + NULL, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); +} + +static void +sysprof_document_task_init (SysprofDocumentTask *self) +{ + SysprofDocumentTaskPrivate *priv = sysprof_document_task_get_instance_private (self); + + g_weak_ref_init (&priv->loader_wr, NULL); + + priv->cancellable = g_cancellable_new (); +} + +static gboolean +notify_in_idle_cb (gpointer data) +{ + SysprofDocumentTask *self = data; + SysprofDocumentTaskPrivate *priv = sysprof_document_task_get_instance_private (self); + + g_assert (SYSPROF_IS_DOCUMENT_TASK (self)); + + g_mutex_lock (&priv->mutex); + priv->notify_source = 0; + g_mutex_unlock (&priv->mutex); + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_CANCELLED]); + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MESSAGE]); + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_PROGRESS]); + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_TITLE]); + + return G_SOURCE_REMOVE; +} + +static void +notify_in_idle_locked (SysprofDocumentTask *self) +{ + SysprofDocumentTaskPrivate *priv = sysprof_document_task_get_instance_private (self); + + g_assert (SYSPROF_IS_DOCUMENT_TASK (self)); + + if (priv->notify_source == 0) + priv->notify_source = g_idle_add_full (G_PRIORITY_LOW, + notify_in_idle_cb, + g_object_ref (self), + g_object_unref); +} + +char * +sysprof_document_task_dup_message (SysprofDocumentTask *self) +{ + SysprofDocumentTaskPrivate *priv = sysprof_document_task_get_instance_private (self); + char *ret; + + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_TASK (self), NULL); + + g_mutex_lock (&priv->mutex); + ret = g_strdup (priv->message); + g_mutex_unlock (&priv->mutex); + + return ret; +} + +char * +sysprof_document_task_dup_title (SysprofDocumentTask *self) +{ + SysprofDocumentTaskPrivate *priv = sysprof_document_task_get_instance_private (self); + char *ret; + + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_TASK (self), NULL); + + g_mutex_lock (&priv->mutex); + ret = g_strdup (priv->title); + g_mutex_unlock (&priv->mutex); + + return ret; +} + +void +_sysprof_document_task_take_message (SysprofDocumentTask *self, + char *message) +{ + SysprofDocumentTaskPrivate *priv = sysprof_document_task_get_instance_private (self); + + g_return_if_fail (SYSPROF_IS_DOCUMENT_TASK (self)); + + g_mutex_lock (&priv->mutex); + g_free (priv->message); + priv->message = message; + notify_in_idle_locked (self); + g_mutex_unlock (&priv->mutex); +} + +double +sysprof_document_task_get_progress (SysprofDocumentTask *self) +{ + SysprofDocumentTaskPrivate *priv = sysprof_document_task_get_instance_private (self); + double ret; + + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_TASK (self), 0); + + g_mutex_lock (&priv->mutex); + ret = priv->progress; + g_mutex_unlock (&priv->mutex); + + return ret; +} + +void +_sysprof_document_task_set_progress (SysprofDocumentTask *self, + double progress) +{ + SysprofDocumentTaskPrivate *priv = sysprof_document_task_get_instance_private (self); + + g_return_if_fail (SYSPROF_IS_DOCUMENT_TASK (self)); + + progress = CLAMP (progress, 0., 1.); + + g_mutex_lock (&priv->mutex); + priv->progress = progress; + notify_in_idle_locked (self); + g_mutex_unlock (&priv->mutex); +} + +void +_sysprof_document_task_set_title (SysprofDocumentTask *self, + const char *title) +{ + SysprofDocumentTaskPrivate *priv = sysprof_document_task_get_instance_private (self); + + g_return_if_fail (SYSPROF_IS_DOCUMENT_TASK (self)); + + g_mutex_lock (&priv->mutex); + if (g_set_str (&priv->title, title)) + notify_in_idle_locked (self); + g_mutex_unlock (&priv->mutex); +} + +gboolean +sysprof_document_task_is_cancelled (SysprofDocumentTask *self) +{ + SysprofDocumentTaskPrivate *priv = sysprof_document_task_get_instance_private (self); + gboolean ret; + + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_TASK (self), FALSE); + + g_mutex_lock (&priv->mutex); + ret = g_cancellable_is_cancelled (priv->cancellable); + g_mutex_unlock (&priv->mutex); + + return ret; +} + +GCancellable * +_sysprof_document_task_get_cancellable (SysprofDocumentTask *self) +{ + SysprofDocumentTaskPrivate *priv = sysprof_document_task_get_instance_private (self); + + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_TASK (self), NULL); + + return priv->cancellable; +} + +SysprofDocumentTaskScope * +_sysprof_document_task_register (SysprofDocumentTask *self, + SysprofDocumentLoader *loader) +{ + SysprofDocumentTaskPrivate *priv = sysprof_document_task_get_instance_private (self); + + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_TASK (self), NULL); + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_LOADER (loader), NULL); + + g_weak_ref_set (&priv->loader_wr, loader); + + _sysprof_document_loader_add_task (loader, self); + + return self; +} + +void +_sysprof_document_task_unregister (SysprofDocumentTask *self) +{ + SysprofDocumentTaskPrivate *priv = sysprof_document_task_get_instance_private (self); + g_autoptr(SysprofDocumentLoader) loader = NULL; + + g_return_if_fail (SYSPROF_IS_DOCUMENT_TASK (self)); + + if ((loader = g_weak_ref_get (&priv->loader_wr))) + _sysprof_document_loader_remove_task (loader, self); +} + +void +sysprof_document_task_cancel (SysprofDocumentTask *self) +{ + SysprofDocumentTaskPrivate *priv = sysprof_document_task_get_instance_private (self); + + g_return_if_fail (SYSPROF_IS_DOCUMENT_TASK (self)); + + g_cancellable_cancel (priv->cancellable); + + if (SYSPROF_DOCUMENT_TASK_GET_CLASS (self)->cancel) + SYSPROF_DOCUMENT_TASK_GET_CLASS (self)->cancel (self); +} diff --git a/src/libsysprof/sysprof-document-task.h b/src/libsysprof/sysprof-document-task.h new file mode 100644 index 00000000..1f0be928 --- /dev/null +++ b/src/libsysprof/sysprof-document-task.h @@ -0,0 +1,46 @@ +/* + * sysprof-document-task.h + * + * Copyright 2024 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +#include "sysprof-version-macros.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_DOCUMENT_TASK (sysprof_document_task_get_type()) + +SYSPROF_AVAILABLE_IN_48 +G_DECLARE_DERIVABLE_TYPE (SysprofDocumentTask, sysprof_document_task, SYSPROF, DOCUMENT_TASK, GObject) + +SYSPROF_AVAILABLE_IN_48 +double sysprof_document_task_get_progress (SysprofDocumentTask *self); +SYSPROF_AVAILABLE_IN_48 +char *sysprof_document_task_dup_message (SysprofDocumentTask *self); +SYSPROF_AVAILABLE_IN_48 +char *sysprof_document_task_dup_title (SysprofDocumentTask *self); +SYSPROF_AVAILABLE_IN_48 +gboolean sysprof_document_task_is_cancelled (SysprofDocumentTask *self); +SYSPROF_AVAILABLE_IN_48 +void sysprof_document_task_cancel (SysprofDocumentTask *self); + +G_END_DECLS diff --git a/src/libsysprof/sysprof.h b/src/libsysprof/sysprof.h index 529386c8..c2176619 100644 --- a/src/libsysprof/sysprof.h +++ b/src/libsysprof/sysprof.h @@ -56,6 +56,7 @@ G_BEGIN_DECLS # include "sysprof-document-overlay.h" # include "sysprof-document-process.h" # include "sysprof-document-sample.h" +# include "sysprof-document-task.h" # include "sysprof-document-traceable.h" # include "sysprof-document.h" # include "sysprof-elf-symbolizer.h" diff --git a/src/sysprof/meson.build b/src/sysprof/meson.build index 989bbd11..ca42ead3 100644 --- a/src/sysprof/meson.build +++ b/src/sysprof/meson.build @@ -59,6 +59,7 @@ sysprof_sources = [ 'sysprof-split-layer.c', 'sysprof-storage-section.c', 'sysprof-symbol-label.c', + 'sysprof-task-row.c', 'sysprof-time-filter-model.c', 'sysprof-time-label.c', 'sysprof-time-ruler.c', diff --git a/src/sysprof/style.css b/src/sysprof/style.css index 946ab248..d5d7b5e2 100644 --- a/src/sysprof/style.css +++ b/src/sysprof/style.css @@ -87,3 +87,12 @@ flamegraph { timespanlayer { font-size: 10px; } + +popover.tasks listview row { + background: transparent; + padding: 6px; +} + +popover.tasks listview row label.heading { + padding-bottom: 3px; +} diff --git a/src/sysprof/sysprof-task-row.c b/src/sysprof/sysprof-task-row.c new file mode 100644 index 00000000..a8e51754 --- /dev/null +++ b/src/sysprof/sysprof-task-row.c @@ -0,0 +1,139 @@ +/* + * sysprof-task-row.c + * + * Copyright 2024 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include + +#include "sysprof-task-row.h" + +struct _SysprofTaskRow +{ + GtkWidget parent_instance; + SysprofDocumentTask *task; +}; + +enum { + PROP_0, + PROP_TASK, + N_PROPS +}; + +G_DEFINE_FINAL_TYPE (SysprofTaskRow, sysprof_task_row, GTK_TYPE_WIDGET) + +static GParamSpec *properties[N_PROPS]; + +static void +sysprof_task_row_cancel (GtkWidget *widget, + const char *action, + GVariant *param) +{ + SysprofTaskRow *self = SYSPROF_TASK_ROW (widget); + + if (self->task) + sysprof_document_task_cancel (self->task); +} + +static void +sysprof_task_row_dispose (GObject *object) +{ + SysprofTaskRow *self = (SysprofTaskRow *)object; + GtkWidget *child; + + gtk_widget_dispose_template (GTK_WIDGET (self), SYSPROF_TYPE_TASK_ROW); + + while ((child = gtk_widget_get_first_child (GTK_WIDGET (self)))) + gtk_widget_unparent (child); + + g_clear_object (&self->task); + + G_OBJECT_CLASS (sysprof_task_row_parent_class)->dispose (object); +} + +static void +sysprof_task_row_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SysprofTaskRow *self = SYSPROF_TASK_ROW (object); + + switch (prop_id) + { + case PROP_TASK: + g_value_set_object (value, self->task); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_task_row_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + SysprofTaskRow *self = SYSPROF_TASK_ROW (object); + + switch (prop_id) + { + case PROP_TASK: + if (g_set_object (&self->task, g_value_get_object (value))) + g_object_notify_by_pspec (object, pspec); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_task_row_class_init (SysprofTaskRowClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->dispose = sysprof_task_row_dispose; + object_class->get_property = sysprof_task_row_get_property; + object_class->set_property = sysprof_task_row_set_property; + + properties[PROP_TASK] = + g_param_spec_object ("task", NULL, NULL, + SYSPROF_TYPE_DOCUMENT_TASK, + (G_PARAM_READWRITE | + G_PARAM_EXPLICIT_NOTIFY | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); + + gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/sysprof/sysprof-task-row.ui"); + gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT); + + gtk_widget_class_install_action (widget_class, "task.cancel", NULL, sysprof_task_row_cancel); +} + +static void +sysprof_task_row_init (SysprofTaskRow *self) +{ + gtk_widget_init_template (GTK_WIDGET (self)); +} diff --git a/src/sysprof/sysprof-task-row.h b/src/sysprof/sysprof-task-row.h new file mode 100644 index 00000000..39bc3606 --- /dev/null +++ b/src/sysprof/sysprof-task-row.h @@ -0,0 +1,32 @@ +/* + * sysprof-task-row.h + * + * Copyright 2024 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_TASK_ROW (sysprof_task_row_get_type()) + +G_DECLARE_FINAL_TYPE (SysprofTaskRow, sysprof_task_row, SYSPROF, TASK_ROW, GtkWidget) + +G_END_DECLS diff --git a/src/sysprof/sysprof-task-row.ui b/src/sysprof/sysprof-task-row.ui new file mode 100644 index 00000000..ac91d79f --- /dev/null +++ b/src/sysprof/sysprof-task-row.ui @@ -0,0 +1,71 @@ + + + + diff --git a/src/sysprof/sysprof-window.c b/src/sysprof/sysprof-window.c index 869d6916..5fd6e9a1 100644 --- a/src/sysprof/sysprof-window.c +++ b/src/sysprof/sysprof-window.c @@ -38,29 +38,32 @@ #include "sysprof-samples-section.h" #include "sysprof-sidebar.h" #include "sysprof-storage-section.h" +#include "sysprof-task-row.h" #include "sysprof-util.h" #include "sysprof-window.h" struct _SysprofWindow { - AdwApplicationWindow parent_instance; + AdwApplicationWindow parent_instance; - SysprofDocument *document; - SysprofSession *session; + SysprofDocument *document; + SysprofDocumentLoader *loader; + SysprofSession *session; - GtkToggleButton *show_right_sidebar; - GtkWidget *left_split_overlay; - GtkWidget *right_split_overlay; - GtkProgressBar *progress_bar; - AdwWindowTitle *stack_title; + GtkToggleButton *show_right_sidebar; + GtkWidget *left_split_overlay; + GtkWidget *right_split_overlay; + GtkProgressBar *progress_bar; + AdwWindowTitle *stack_title; - guint disposed : 1; + guint disposed : 1; }; enum { PROP_0, PROP_DOCUMENT, PROP_IS_LOADED, + PROP_LOADER, PROP_SESSION, N_PROPS }; @@ -513,6 +516,10 @@ sysprof_window_get_property (GObject *object, g_value_set_object (value, sysprof_window_get_document (self)); break; + case PROP_LOADER: + g_value_set_object (value, self->loader); + break; + case PROP_IS_LOADED: g_value_set_boolean (value, !!sysprof_window_get_document (self)); break; @@ -560,6 +567,11 @@ sysprof_window_class_init (SysprofWindowClass *klass) SYSPROF_TYPE_DOCUMENT, (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + properties[PROP_LOADER] = + g_param_spec_object ("loader", NULL, NULL, + SYSPROF_TYPE_DOCUMENT_LOADER, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + properties [PROP_IS_LOADED] = g_param_spec_boolean ("is-loaded", NULL, NULL, FALSE, @@ -617,6 +629,7 @@ sysprof_window_class_init (SysprofWindowClass *klass) g_type_ensure (SYSPROF_TYPE_SESSION); g_type_ensure (SYSPROF_TYPE_SYMBOL); g_type_ensure (SYSPROF_TYPE_SIDEBAR); + g_type_ensure (SYSPROF_TYPE_TASK_ROW); } static void @@ -711,6 +724,9 @@ sysprof_window_load_cb (GObject *object, g_binding_unbind (g_object_get_data (G_OBJECT (loader), "message-binding")); g_object_set_data (G_OBJECT (loader), "message-binding", NULL); + if (g_set_object (&self->loader, NULL)) + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_LOADER]); + if (!(document = sysprof_document_loader_load_finish (loader, result, &error))) { GtkWidget *dialog; @@ -750,6 +766,8 @@ sysprof_window_create (SysprofApplication *app, self = g_object_new (SYSPROF_TYPE_WINDOW, "application", app, NULL); + self->loader = g_object_ref (loader); + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_LOADER]); g_object_bind_property (loader, "fraction", self->progress_bar, "fraction", G_BINDING_SYNC_CREATE); diff --git a/src/sysprof/sysprof-window.ui b/src/sysprof/sysprof-window.ui index 1b361e37..f4e1e7cb 100644 --- a/src/sysprof/sysprof-window.ui +++ b/src/sysprof/sysprof-window.ui @@ -108,17 +108,6 @@ - - - - - - SysprofWindow - - - - - @@ -215,6 +204,70 @@ Toggle Right Panel + + + true + false + + + + + + + + SysprofWindow + + + + + + + 300 + + + never + true + 600 + + + + + + + SysprofWindow + + + + + + + + + + +]]> + + + + + + + + + + + diff --git a/src/sysprof/sysprof.gresource.xml b/src/sysprof/sysprof.gresource.xml index aefb57a9..70040947 100644 --- a/src/sysprof/sysprof.gresource.xml +++ b/src/sysprof/sysprof.gresource.xml @@ -53,6 +53,7 @@ sysprof-samples-section.ui sysprof-sidebar.ui sysprof-storage-section.ui + sysprof-task-row.ui sysprof-time-scrubber.ui sysprof-traceables-utility.ui sysprof-weighted-callgraph-view.ui From 25b539de54c112654c6f2d037fd78a6fa78c12a6 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 10 Oct 2024 17:03:30 -0700 Subject: [PATCH 3/5] libsysprof: add setup hooks for symbolizers This gives the symbolizer access to the loader so we can propagate tasks back to it. --- src/libsysprof/sysprof-multi-symbolizer.c | 22 +++++++++++++++++++++ src/libsysprof/sysprof-symbolizer-private.h | 6 +++++- src/libsysprof/sysprof-symbolizer.c | 11 +++++++++++ 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/libsysprof/sysprof-multi-symbolizer.c b/src/libsysprof/sysprof-multi-symbolizer.c index 483cdde5..e1ad90b0 100644 --- a/src/libsysprof/sysprof-multi-symbolizer.c +++ b/src/libsysprof/sysprof-multi-symbolizer.c @@ -27,6 +27,7 @@ struct _SysprofMultiSymbolizer { SysprofSymbolizer parent_instance; GPtrArray *symbolizers; + guint frozen : 1; }; struct _SysprofMultiSymbolizerClass @@ -138,6 +139,25 @@ sysprof_multi_symbolizer_symbolize (SysprofSymbolizer *symbolizer, return NULL; } +static void +sysprof_multi_symbolizer_setup (SysprofSymbolizer *symbolizer, + SysprofDocumentLoader *loader) +{ + SysprofMultiSymbolizer *self = (SysprofMultiSymbolizer *)symbolizer; + + g_assert (SYSPROF_IS_MULTI_SYMBOLIZER (self)); + g_assert (SYSPROF_IS_DOCUMENT_LOADER (loader)); + + self->frozen = TRUE; + + for (guint i = 0; i < self->symbolizers->len; i++) + { + SysprofSymbolizer *child = g_ptr_array_index (self->symbolizers, i); + + _sysprof_symbolizer_setup (child, loader); + } +} + static void sysprof_multi_symbolizer_finalize (GObject *object) { @@ -159,6 +179,7 @@ sysprof_multi_symbolizer_class_init (SysprofMultiSymbolizerClass *klass) symbolizer_class->prepare_async = sysprof_multi_symbolizer_prepare_async; symbolizer_class->prepare_finish = sysprof_multi_symbolizer_prepare_finish; symbolizer_class->symbolize = sysprof_multi_symbolizer_symbolize; + symbolizer_class->setup = sysprof_multi_symbolizer_setup; } static void @@ -188,6 +209,7 @@ sysprof_multi_symbolizer_take (SysprofMultiSymbolizer *self, g_return_if_fail (SYSPROF_IS_MULTI_SYMBOLIZER (self)); g_return_if_fail (SYSPROF_IS_SYMBOLIZER (symbolizer)); g_return_if_fail ((gpointer)self != (gpointer)symbolizer); + g_return_if_fail (self->frozen == FALSE); g_ptr_array_add (self->symbolizers, symbolizer); } diff --git a/src/libsysprof/sysprof-symbolizer-private.h b/src/libsysprof/sysprof-symbolizer-private.h index dd917e44..3d68c52b 100644 --- a/src/libsysprof/sysprof-symbolizer-private.h +++ b/src/libsysprof/sysprof-symbolizer-private.h @@ -22,6 +22,7 @@ #include "sysprof-address-layout-private.h" #include "sysprof-document.h" +#include "sysprof-document-loader.h" #include "sysprof-mount-namespace-private.h" #include "sysprof-process-info-private.h" #include "sysprof-strings-private.h" @@ -41,6 +42,8 @@ struct _SysprofSymbolizerClass { GObjectClass parent_class; + void (*setup) (SysprofSymbolizer *self, + SysprofDocumentLoader *loader); void (*prepare_async) (SysprofSymbolizer *self, SysprofDocument *document, GCancellable *cancellable, @@ -56,7 +59,8 @@ struct _SysprofSymbolizerClass SysprofAddress address); }; - +void _sysprof_symbolizer_setup (SysprofSymbolizer *self, + SysprofDocumentLoader *loader); void _sysprof_symbolizer_prepare_async (SysprofSymbolizer *self, SysprofDocument *document, GCancellable *cancellable, diff --git a/src/libsysprof/sysprof-symbolizer.c b/src/libsysprof/sysprof-symbolizer.c index 9ad17ca2..47d6021a 100644 --- a/src/libsysprof/sysprof-symbolizer.c +++ b/src/libsysprof/sysprof-symbolizer.c @@ -99,3 +99,14 @@ _sysprof_symbolizer_symbolize (SysprofSymbolizer *self, { return SYSPROF_SYMBOLIZER_GET_CLASS (self)->symbolize (self, strings, process_info, context, address); } + +void +_sysprof_symbolizer_setup (SysprofSymbolizer *self, + SysprofDocumentLoader *loader) +{ + g_return_if_fail (SYSPROF_IS_SYMBOLIZER (self)); + g_return_if_fail (SYSPROF_IS_DOCUMENT_LOADER (loader)); + + if (SYSPROF_SYMBOLIZER_GET_CLASS (self)->setup) + SYSPROF_SYMBOLIZER_GET_CLASS (self)->setup (self, loader); +} From 1464e3a48ec6c965c64165cd73126236bb6b20f9 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 10 Oct 2024 17:04:19 -0700 Subject: [PATCH 4/5] libsysprof: hoist fallback symbol creation This makes sure that we get that even when not using the Elf symbolizer but also means we can fallback through the Elf symbolizer into the debuginfod symbolizer. --- src/libsysprof/sysprof-document-symbols.c | 69 ++++++++++++++++++++++- src/libsysprof/sysprof-elf-symbolizer.c | 24 +------- 2 files changed, 69 insertions(+), 24 deletions(-) diff --git a/src/libsysprof/sysprof-document-symbols.c b/src/libsysprof/sysprof-document-symbols.c index 2f8d1849..828c7fb6 100644 --- a/src/libsysprof/sysprof-document-symbols.c +++ b/src/libsysprof/sysprof-document-symbols.c @@ -85,6 +85,71 @@ symbolize_free (Symbolize *state) g_free (state); } +static SysprofSymbol * +do_symbolize (SysprofSymbolizer *symbolizer, + SysprofStrings *strings, + SysprofProcessInfo *process_info, + SysprofAddressContext last_context, + SysprofAddress address) +{ + SysprofDocumentMmap *map; + g_autofree char *name = NULL; + SysprofSymbol *ret; + const char *nick = NULL; + const char *path; + guint64 map_begin; + guint64 map_end; + guint64 relative_address; + guint64 begin_address; + guint64 end_address; + guint64 file_offset; + + if ((ret = _sysprof_symbolizer_symbolize (symbolizer, strings, process_info, last_context, address))) + return ret; + + /* Fallback, we failed to locate the symbol within a file we can + * access, so tell the user about what file contained the symbol + * and where (relative to that file) the IP was. + */ + + if (!(map = sysprof_address_layout_lookup (process_info->address_layout, address))) + return NULL; + + map_begin = sysprof_document_mmap_get_start_address (map); + map_end = sysprof_document_mmap_get_end_address (map); + + g_assert (address >= map_begin); + g_assert (address < map_end); + + file_offset = sysprof_document_mmap_get_file_offset (map); + + relative_address = address; + relative_address -= map_begin; + relative_address += file_offset; + + path = sysprof_document_mmap_get_file (map); + + begin_address = CLAMP (begin_address, file_offset, file_offset + (map_end - map_begin)); + end_address = CLAMP (end_address, file_offset, file_offset + (map_end - map_begin)); + if (end_address == begin_address) + end_address++; + + name = g_strdup_printf ("In File %s+0x%"G_GINT64_MODIFIER"x", + sysprof_document_mmap_get_file (map), + relative_address); + begin_address = address; + end_address = address + 1; + + ret = _sysprof_symbol_new (sysprof_strings_get (strings, name), + sysprof_strings_get (strings, path), + sysprof_strings_get (strings, nick), + begin_address, end_address, + SYSPROF_SYMBOL_KIND_USER); + ret->is_fallback = TRUE; + + return ret; +} + static void add_traceable (SysprofDocumentSymbols *self, SysprofStrings *strings, @@ -123,7 +188,7 @@ add_traceable (SysprofDocumentSymbols *self, if (sysprof_symbol_cache_lookup (self->kernel_symbols, address) != NULL) continue; - if ((symbol = _sysprof_symbolizer_symbolize (symbolizer, strings, process_info, last_context, address))) + if ((symbol = do_symbolize (symbolizer, strings, process_info, last_context, address))) sysprof_symbol_cache_take (self->kernel_symbols, g_steal_pointer (&symbol)); } else @@ -134,7 +199,7 @@ add_traceable (SysprofDocumentSymbols *self, sysprof_symbol_cache_lookup (process_info->symbol_cache, address) != NULL) continue; - if ((symbol = _sysprof_symbolizer_symbolize (symbolizer, strings, process_info, last_context, address))) + if ((symbol = do_symbolize (symbolizer, strings, process_info, last_context, address))) sysprof_symbol_cache_take (process_info->symbol_cache, g_steal_pointer (&symbol)); } } diff --git a/src/libsysprof/sysprof-elf-symbolizer.c b/src/libsysprof/sysprof-elf-symbolizer.c index f7aabef7..05bd1d6c 100644 --- a/src/libsysprof/sysprof-elf-symbolizer.c +++ b/src/libsysprof/sysprof-elf-symbolizer.c @@ -113,7 +113,7 @@ sysprof_elf_symbolizer_symbolize (SysprofSymbolizer *symbolizer, build_id, file_inode, NULL))) - goto fallback; + return NULL; nick = sysprof_elf_get_nick (elf); @@ -124,7 +124,7 @@ sysprof_elf_symbolizer_symbolize (SysprofSymbolizer *symbolizer, relative_address, &begin_address, &end_address))) - goto fallback; + return NULL; /* Sanitize address ranges if we have to. Sometimes that can happen * for us, but it seems to be limited to glibc. @@ -142,26 +142,6 @@ sysprof_elf_symbolizer_symbolize (SysprofSymbolizer *symbolizer, SYSPROF_SYMBOL_KIND_USER); return ret; - -fallback: - /* Fallback, we failed to locate the symbol within a file we can - * access, so tell the user about what file contained the symbol - * and where (relative to that file) the IP was. - */ - name = g_strdup_printf ("In File %s+0x%"G_GINT64_MODIFIER"x", - sysprof_document_mmap_get_file (map), - relative_address); - begin_address = address; - end_address = address + 1; - - ret = _sysprof_symbol_new (sysprof_strings_get (strings, name), - sysprof_strings_get (strings, path), - sysprof_strings_get (strings, nick), - begin_address, end_address, - SYSPROF_SYMBOL_KIND_USER); - ret->is_fallback = TRUE; - - return ret; } static void From d2471d4bf571c3a7c8d743a96c9207d748d763e8 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 10 Oct 2024 17:07:21 -0700 Subject: [PATCH 5/5] libsysprof: add debuginfod symbolizer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is based on a debuginfod_client provided by Barnabás Pőcze in !73. It extends it to use the new task infrastructure to elevate the download process to the user while loading a capture file. --- config.h.meson | 2 + meson.build | 2 + meson_options.txt | 2 + src/libsysprof/meson.build | 8 + .../sysprof-debuginfod-symbolizer.c | 229 ++++++++++++++++++ .../sysprof-debuginfod-symbolizer.h | 42 ++++ .../sysprof-debuginfod-task-private.h | 42 ++++ src/libsysprof/sysprof-debuginfod-task.c | 131 ++++++++++ src/libsysprof/sysprof-document-loader.c | 15 +- 9 files changed, 468 insertions(+), 5 deletions(-) create mode 100644 src/libsysprof/sysprof-debuginfod-symbolizer.c create mode 100644 src/libsysprof/sysprof-debuginfod-symbolizer.h create mode 100644 src/libsysprof/sysprof-debuginfod-task-private.h create mode 100644 src/libsysprof/sysprof-debuginfod-task.c diff --git a/config.h.meson b/config.h.meson index d2f7589a..48b3c2c2 100644 --- a/config.h.meson +++ b/config.h.meson @@ -16,6 +16,8 @@ #mesondefine HAVE_LIBSYSTEMD +#mesondefine HAVE_DEBUGINFOD + #mesondefine HAVE_PERF_CLOCKID #mesondefine HAVE_POLKIT diff --git a/meson.build b/meson.build index 6cb55bf2..c6a67f68 100644 --- a/meson.build +++ b/meson.build @@ -63,6 +63,7 @@ gio_unix_dep = dependency('gio-unix-2.0', version: glib_req_version, required: need_glib and host_machine.system() != 'windows') gtk_dep = dependency('gtk4', version: gtk_req_version, required: need_gtk) libsystemd_dep = dependency('libsystemd', required: false) +debuginfod_dep = dependency('libdebuginfod', required: get_option('debuginfod')) config_h = configuration_data() config_h.set_quoted('API_VERSION_S', libsysprof_api_version.to_string()) @@ -99,6 +100,7 @@ config_h.set10('ENABLE_NLS', true) config_h.set_quoted('GETTEXT_PACKAGE', 'sysprof') config_h.set_quoted('PACKAGE_LOCALE_DIR', join_paths(get_option('prefix'), get_option('datadir'), 'locale')) config_h.set10('HAVE_LIBSYSTEMD', libsystemd_dep.found()) +config_h.set10('HAVE_DEBUGINFOD', debuginfod_dep.found()) polkit_agent_dep = dependency('polkit-agent-1', required: get_option('polkit-agent')) config_h.set10('HAVE_POLKIT_AGENT', polkit_agent_dep.found()) diff --git a/meson_options.txt b/meson_options.txt index 2f78fc3b..02bb6981 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -49,3 +49,5 @@ option('tests', type: 'boolean') # Optionally disable the examples (this is mostly only useful for building only # libsysprof-capture as a subproject) option('examples', type: 'boolean') + +option('debuginfod', type: 'feature') diff --git a/src/libsysprof/meson.build b/src/libsysprof/meson.build index 697e9665..b4e58078 100644 --- a/src/libsysprof/meson.build +++ b/src/libsysprof/meson.build @@ -153,6 +153,13 @@ libsysprof_private_sources = [ 'timsort/gtktimsort.c', ] +if debuginfod_dep.found() and get_option('debuginfod').enabled() + libsysprof_private_sources += [ + 'sysprof-debuginfod-symbolizer.c', + 'sysprof-debuginfod-task.c' + ] +endif + if polkit_dep.found() libsysprof_private_sources += ['sysprof-polkit.c'] endif @@ -192,6 +199,7 @@ libsysprof_deps = [ libsystemd_dep, polkit_dep, + debuginfod_dep, libeggbitset_static_dep, libelfparser_static_dep, diff --git a/src/libsysprof/sysprof-debuginfod-symbolizer.c b/src/libsysprof/sysprof-debuginfod-symbolizer.c new file mode 100644 index 00000000..8dd60d19 --- /dev/null +++ b/src/libsysprof/sysprof-debuginfod-symbolizer.c @@ -0,0 +1,229 @@ +/* sysprof-debuginfod-symbolizer.c + * + * Copyright 2024 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include +#include +#include + +#include + +#include "sysprof-symbolizer-private.h" +#include "sysprof-debuginfod-symbolizer.h" +#include "sysprof-debuginfod-task-private.h" +#include "sysprof-elf-loader-private.h" +#include "sysprof-symbol-private.h" + +struct _SysprofDebuginfodSymbolizer +{ + SysprofSymbolizer parent_instance; + + GWeakRef loader_wr; + + debuginfod_client *client; + SysprofElfLoader *loader; + GHashTable *cache; + GHashTable *failed; +}; + +struct _SysprofDebuginfodSymbolizerClass +{ + SysprofSymbolizerClass parent_class; +}; + +G_DEFINE_FINAL_TYPE (SysprofDebuginfodSymbolizer, sysprof_debuginfod_symbolizer, SYSPROF_TYPE_SYMBOLIZER) + +static SysprofSymbol * +sysprof_debuginfod_symbolizer_symbolize (SysprofSymbolizer *symbolizer, + SysprofStrings *strings, + const SysprofProcessInfo *process_info, + SysprofAddressContext context, + SysprofAddress address) +{ + SysprofDebuginfodSymbolizer *self = SYSPROF_DEBUGINFOD_SYMBOLIZER (symbolizer); + g_autoptr(SysprofElf) elf = NULL; + g_autofree char *name = NULL; + SysprofSymbol *sym = NULL; + SysprofDocumentMmap *map; + const char *build_id; + const char *path; + guint64 relative_address; + guint64 begin_address; + guint64 end_address; + guint64 file_offset; + guint64 map_begin; + guint64 map_end; + + if (process_info == NULL || + process_info->address_layout == NULL || + process_info->mount_namespace == NULL || + (context != SYSPROF_ADDRESS_CONTEXT_NONE && context != SYSPROF_ADDRESS_CONTEXT_USER) || + !(map = sysprof_address_layout_lookup (process_info->address_layout, address))) + return NULL; + + map_begin = sysprof_document_mmap_get_start_address (map); + map_end = sysprof_document_mmap_get_end_address (map); + + g_assert (address < map_end); + g_assert (address >= map_begin); + + file_offset = sysprof_document_mmap_get_file_offset (map); + path = sysprof_document_mmap_get_file (map); + + if (g_hash_table_contains (self->failed, path)) + return NULL; + + elf = sysprof_elf_loader_load (self->loader, + process_info->mount_namespace, + path, + sysprof_document_mmap_get_build_id (map), + sysprof_document_mmap_get_file_inode (map), + NULL); + if (elf == NULL) + return NULL; + + if (!(build_id = sysprof_elf_get_build_id (elf))) + return NULL; + + if (!g_hash_table_contains (self->cache, elf)) + { + g_autoptr(SysprofDebuginfodTask) task = sysprof_debuginfod_task_new (); + g_autoptr(SysprofDocumentLoader) loader = g_weak_ref_get (&self->loader_wr); + g_autoptr(SysprofDocumentTaskScope) scope = _sysprof_document_task_register (SYSPROF_DOCUMENT_TASK (task), loader); + g_autoptr(SysprofElf) debuginfo_elf = NULL; + + if (!(debuginfo_elf = sysprof_debuginfod_task_find_debuginfo (task, self->client, path, build_id, NULL))) + { + g_hash_table_insert (self->failed, g_strdup (path), NULL); + return NULL; + } + + sysprof_elf_set_debug_link_elf (elf, debuginfo_elf); + + g_hash_table_insert (self->cache, g_object_ref (elf), NULL); + } + + relative_address = address; + relative_address -= map_begin; + relative_address += file_offset; + + name = sysprof_elf_get_symbol_at_address (elf, + relative_address, + &begin_address, + &end_address); + if (!name) + return NULL; + + begin_address = CLAMP (begin_address, file_offset, file_offset + (map_end - map_begin)); + end_address = CLAMP (end_address, file_offset, file_offset + (map_end - map_begin)); + if (end_address == begin_address) + end_address++; + + sym = _sysprof_symbol_new (sysprof_strings_get (strings, name), + sysprof_strings_get (strings, path), + sysprof_strings_get (strings, sysprof_elf_get_nick (elf)), + map_begin + (begin_address - file_offset), + map_begin + (end_address - file_offset), + SYSPROF_SYMBOL_KIND_USER); + + return sym; +} + +static void +sysprof_debuginfod_symbolizer_setup (SysprofSymbolizer *symbolizer, + SysprofDocumentLoader *loader) +{ + SysprofDebuginfodSymbolizer *self = SYSPROF_DEBUGINFOD_SYMBOLIZER (symbolizer); + + g_weak_ref_set (&self->loader_wr, loader); +} + +static void +sysprof_debuginfod_symbolizer_dispose (GObject *object) +{ + SysprofDebuginfodSymbolizer *self = SYSPROF_DEBUGINFOD_SYMBOLIZER (object); + + g_hash_table_remove_all (self->cache); + + g_weak_ref_set (&self->loader_wr, NULL); + + G_OBJECT_CLASS (sysprof_debuginfod_symbolizer_parent_class)->dispose (object); +} + +static void +sysprof_debuginfod_symbolizer_finalize (GObject *object) +{ + SysprofDebuginfodSymbolizer *self = SYSPROF_DEBUGINFOD_SYMBOLIZER (object); + + g_clear_object (&self->loader); + + g_clear_pointer (&self->cache, g_hash_table_unref); + g_clear_pointer (&self->failed, g_hash_table_unref); + g_clear_pointer (&self->client, debuginfod_end); + + g_weak_ref_clear (&self->loader_wr); + + G_OBJECT_CLASS (sysprof_debuginfod_symbolizer_parent_class)->finalize (object); +} + +static void +sysprof_debuginfod_symbolizer_class_init (SysprofDebuginfodSymbolizerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + SysprofSymbolizerClass *symbolizer_class = SYSPROF_SYMBOLIZER_CLASS (klass); + + object_class->dispose = sysprof_debuginfod_symbolizer_dispose; + object_class->finalize = sysprof_debuginfod_symbolizer_finalize; + + symbolizer_class->setup = sysprof_debuginfod_symbolizer_setup; + symbolizer_class->symbolize = sysprof_debuginfod_symbolizer_symbolize; +} + +static void +sysprof_debuginfod_symbolizer_init (SysprofDebuginfodSymbolizer *self) +{ + g_weak_ref_init (&self->loader_wr, NULL); +} + +SysprofSymbolizer * +sysprof_debuginfod_symbolizer_new (GError **error) +{ + g_autoptr(SysprofDebuginfodSymbolizer) self = NULL; + + self = g_object_new (SYSPROF_TYPE_DEBUGINFOD_SYMBOLIZER, NULL); + self->client = debuginfod_begin (); + + if (self->client == NULL) + { + int errsv = errno; + g_set_error_literal (error, + G_IO_ERROR, + g_io_error_from_errno (errsv), + g_strerror (errsv)); + return NULL; + } + + self->loader = sysprof_elf_loader_new (); + self->cache = g_hash_table_new_full (NULL, NULL, g_object_unref, NULL); + self->failed = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + + return SYSPROF_SYMBOLIZER (g_steal_pointer (&self)); +} diff --git a/src/libsysprof/sysprof-debuginfod-symbolizer.h b/src/libsysprof/sysprof-debuginfod-symbolizer.h new file mode 100644 index 00000000..32dd347e --- /dev/null +++ b/src/libsysprof/sysprof-debuginfod-symbolizer.h @@ -0,0 +1,42 @@ +/* sysprof-debuginfod-symbolizer.h + * + * Copyright 2024 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "sysprof-symbolizer.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_DEBUGINFOD_SYMBOLIZER (sysprof_debuginfod_symbolizer_get_type()) +#define SYSPROF_IS_DEBUGINFOD_SYMBOLIZER(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_DEBUGINFOD_SYMBOLIZER) +#define SYSPROF_DEBUGINFOD_SYMBOLIZER(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_DEBUGINFOD_SYMBOLIZER, SysprofDebuginfodSymbolizer) +#define SYSPROF_DEBUGINFOD_SYMBOLIZER_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_DEBUGINFOD_SYMBOLIZER, SysprofDebuginfodSymbolizerClass) + +typedef struct _SysprofDebuginfodSymbolizer SysprofDebuginfodSymbolizer; +typedef struct _SysprofDebuginfodSymbolizerClass SysprofDebuginfodSymbolizerClass; + +SYSPROF_AVAILABLE_IN_48 +GType sysprof_debuginfod_symbolizer_get_type (void) G_GNUC_CONST; +SYSPROF_AVAILABLE_IN_48 +SysprofSymbolizer *sysprof_debuginfod_symbolizer_new (GError **error); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofDebuginfodSymbolizer, g_object_unref) + +G_END_DECLS diff --git a/src/libsysprof/sysprof-debuginfod-task-private.h b/src/libsysprof/sysprof-debuginfod-task-private.h new file mode 100644 index 00000000..33e595ec --- /dev/null +++ b/src/libsysprof/sysprof-debuginfod-task-private.h @@ -0,0 +1,42 @@ +/* + * sysprof-debuginfod-task-private.h + * + * Copyright 2024 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +#include "sysprof-document-task-private.h" +#include "sysprof-elf-private.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_DEBUGINFOD_TASK (sysprof_debuginfod_task_get_type()) + +G_DECLARE_FINAL_TYPE (SysprofDebuginfodTask, sysprof_debuginfod_task, SYSPROF, DEBUGINFOD_TASK, SysprofDocumentTask) + +SysprofDebuginfodTask *sysprof_debuginfod_task_new (void); +SysprofElf *sysprof_debuginfod_task_find_debuginfo (SysprofDebuginfodTask *self, + debuginfod_client *client, + const char *path, + const char *build_id_string, + GError **error); + +G_END_DECLS diff --git a/src/libsysprof/sysprof-debuginfod-task.c b/src/libsysprof/sysprof-debuginfod-task.c new file mode 100644 index 00000000..0f996d09 --- /dev/null +++ b/src/libsysprof/sysprof-debuginfod-task.c @@ -0,0 +1,131 @@ +/* + * sysprof-debuginfod-task.c + * + * Copyright 2024 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include +#include + +#include + +#include "sysprof-debuginfod-task-private.h" + +struct _SysprofDebuginfodTask +{ + SysprofDocumentTask parent_instance; +}; + +G_DEFINE_FINAL_TYPE (SysprofDebuginfodTask, sysprof_debuginfod_task, SYSPROF_TYPE_DOCUMENT_TASK) + +static void +sysprof_debuginfod_task_class_init (SysprofDebuginfodTaskClass *klass) +{ +} + +static void +sysprof_debuginfod_task_init (SysprofDebuginfodTask *self) +{ +} + +SysprofDebuginfodTask * +sysprof_debuginfod_task_new (void) +{ + return g_object_new (SYSPROF_TYPE_DEBUGINFOD_TASK, NULL); +} + +static int +sysprof_debuginfod_task_progress_cb (debuginfod_client *client, + long a, + long b) +{ + SysprofDocumentTask *task = debuginfod_get_user_data (client); + double progress; + + g_assert (client != NULL); + g_assert (SYSPROF_IS_DEBUGINFOD_TASK (task)); + + if (b > 0) + progress = (double)a / (double)b; + else + progress = 0; + + _sysprof_document_task_set_progress (task, progress); + + if (sysprof_document_task_is_cancelled (task)) + return -1; + + return 0; +} + +SysprofElf * +sysprof_debuginfod_task_find_debuginfo (SysprofDebuginfodTask *self, + debuginfod_client *client, + const char *path, + const char *build_id_string, + GError **error) +{ + g_autoptr(GMappedFile) mapped_file = NULL; + g_autoptr(SysprofElf) debuginfo_elf = NULL; + g_autofd int fd = -1; + char *debuginfo_path = NULL; + + g_return_val_if_fail (SYSPROF_IS_DEBUGINFOD_TASK (self), NULL); + g_return_val_if_fail (client != NULL, NULL); + g_return_val_if_fail (build_id_string != NULL, NULL); + + debuginfod_set_user_data (client, self); + debuginfod_set_progressfn (client, sysprof_debuginfod_task_progress_cb); + + _sysprof_document_task_set_title (SYSPROF_DOCUMENT_TASK (self), _("Downloading Symbols…")); + _sysprof_document_task_take_message (SYSPROF_DOCUMENT_TASK (self), g_strdup (path)); + + fd = debuginfod_find_debuginfo (client, + (const unsigned char *)build_id_string, 0, + &debuginfo_path); + + if (fd < 0) + { + if (error != NULL) + { + int errsv = errno; + g_set_error_literal (error, + G_IO_ERROR, + g_io_error_from_errno (errsv), + g_strerror (errsv)); + } + + goto failure; + } + + if (!(mapped_file = g_mapped_file_new_from_fd (fd, FALSE, error))) + goto failure; + + if (!(debuginfo_elf = sysprof_elf_new (debuginfo_path, g_steal_pointer (&mapped_file), 0, error))) + goto failure; + +failure: + free (debuginfo_path); + + debuginfod_set_user_data (client, NULL); + debuginfod_set_progressfn (client, NULL); + + return g_steal_pointer (&debuginfo_elf); +} diff --git a/src/libsysprof/sysprof-document-loader.c b/src/libsysprof/sysprof-document-loader.c index 3cd408c4..00b525df 100644 --- a/src/libsysprof/sysprof-document-loader.c +++ b/src/libsysprof/sysprof-document-loader.c @@ -196,7 +196,6 @@ static void set_default_symbolizer (SysprofDocumentLoader *self) { g_autoptr(SysprofMultiSymbolizer) multi = NULL; - g_autoptr(SysprofSymbolizer) debuginfod = NULL; g_autoptr(GError) error = NULL; g_assert (SYSPROF_IS_DOCUMENT_LOADER (self)); @@ -209,10 +208,16 @@ set_default_symbolizer (SysprofDocumentLoader *self) sysprof_multi_symbolizer_take (multi, sysprof_elf_symbolizer_new ()); sysprof_multi_symbolizer_take (multi, sysprof_jitmap_symbolizer_new ()); - if (!(debuginfod = sysprof_debuginfod_symbolizer_new (&error))) - g_warning ("Failed to create debuginfod symbolizer: %s", error->message); - else - sysprof_multi_symbolizer_take (multi, g_steal_pointer (&debuginfod)); +#if HAVE_DEBUGINFOD + { + g_autoptr(SysprofSymbolizer) debuginfod = NULL; + + if (!(debuginfod = sysprof_debuginfod_symbolizer_new (&error))) + g_warning ("Failed to create debuginfod symbolizer: %s", error->message); + else + sysprof_multi_symbolizer_take (multi, g_steal_pointer (&debuginfod)); + } +#endif self->symbolizer = SYSPROF_SYMBOLIZER (g_steal_pointer (&multi)); }