From 43ec0e48f50775e3a0d9322fe6296147f9062e05 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 19 Jun 2023 19:00:22 -0700 Subject: [PATCH] libsysprof-gtk: start on some loading progress --- .../sysprof-document-loader.c | 72 +++++++++++++++-- .../sysprof-document-private.h | 7 ++ src/libsysprof-analyze/sysprof-document.c | 78 +++++++++++++++++-- src/libsysprof-gtk/tests/test-callgraph.c | 51 +++++++++--- 4 files changed, 185 insertions(+), 23 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-document-loader.c b/src/libsysprof-analyze/sysprof-document-loader.c index b3876721..7a85377c 100644 --- a/src/libsysprof-analyze/sysprof-document-loader.c +++ b/src/libsysprof-analyze/sysprof-document-loader.c @@ -23,6 +23,9 @@ #include #include +#include +#include + #include "sysprof-bundled-symbolizer.h" #include "sysprof-document-loader.h" #include "sysprof-document-private.h" @@ -34,11 +37,13 @@ struct _SysprofDocumentLoader { GObject parent_instance; + GMutex mutex; SysprofSymbolizer *symbolizer; char *filename; char *message; double fraction; int fd; + guint notify_source; }; enum { @@ -53,6 +58,43 @@ G_DEFINE_FINAL_TYPE (SysprofDocumentLoader, sysprof_document_loader, G_TYPE_OBJE static GParamSpec *properties [N_PROPS]; +static gboolean +progress_notify_in_idle (gpointer data) +{ + SysprofDocumentLoader *self = data; + + g_assert (SYSPROF_IS_DOCUMENT_LOADER (self)); + + g_mutex_lock (&self->mutex); + self->notify_source = 0; + g_mutex_unlock (&self->mutex); + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FRACTION]); + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MESSAGE]); + + return G_SOURCE_REMOVE; +} + +static void +set_progress (double fraction, + const char *message, + gpointer user_data) +{ + SysprofDocumentLoader *self = user_data; + + g_assert (SYSPROF_IS_DOCUMENT_LOADER (self)); + + g_mutex_lock (&self->mutex); + self->fraction = fraction; + g_set_str (&self->message, message); + if (!self->notify_source) + self->notify_source = g_idle_add_full (G_PRIORITY_LOW, + progress_notify_in_idle, + g_object_ref (self), + g_object_unref); + g_mutex_unlock (&self->mutex); +} + static void mapped_file_by_filename (GTask *task, gpointer source_object, @@ -160,15 +202,12 @@ sysprof_document_loader_finalize (GObject *object) { SysprofDocumentLoader *self = (SysprofDocumentLoader *)object; + g_clear_handle_id (&self->notify_source, g_source_remove); g_clear_object (&self->symbolizer); g_clear_pointer (&self->filename, g_free); g_clear_pointer (&self->message, g_free); - - if (self->fd != -1) - { - close (self->fd); - self->fd = -1; - } + g_clear_fd (&self->fd, NULL); + g_mutex_clear (&self->mutex); G_OBJECT_CLASS (sysprof_document_loader_parent_class)->finalize (object); } @@ -249,6 +288,8 @@ sysprof_document_loader_class_init (SysprofDocumentLoaderClass *klass) static void sysprof_document_loader_init (SysprofDocumentLoader *self) { + g_mutex_init (&self->mutex); + self->fd = -1; set_default_symbolizer (self); @@ -372,11 +413,16 @@ sysprof_document_loader_load_symbols_cb (GObject *object, SysprofDocument *document = (SysprofDocument *)object; g_autoptr(GError) error = NULL; g_autoptr(GTask) task = user_data; + SysprofDocumentLoader *self; g_assert (SYSPROF_IS_DOCUMENT (document)); g_assert (G_IS_ASYNC_RESULT (result)); g_assert (G_IS_TASK (task)); + self = g_task_get_source_object (task); + + set_progress (1., _("Document loaded"), self); + if (!_sysprof_document_symbolize_finish (document, result, &error)) g_task_return_error (task, g_steal_pointer (&error)); else @@ -391,16 +437,20 @@ sysprof_document_loader_load_document_cb (GObject *object, g_autoptr(SysprofDocument) document = NULL; g_autoptr(GError) error = NULL; g_autoptr(GTask) task = user_data; + SysprofDocumentLoader *self; SysprofSymbolizer *symbolizer; g_assert (G_IS_ASYNC_RESULT (result)); g_assert (G_IS_TASK (task)); + self = g_task_get_source_object (task); symbolizer = g_task_get_task_data (task); g_assert (symbolizer != NULL); g_assert (SYSPROF_IS_SYMBOLIZER (symbolizer)); + set_progress (.9, _("Symbolizing stack traces"), self); + if (!(document = _sysprof_document_new_finish (result, &error))) g_task_return_error (task, g_steal_pointer (&error)); else @@ -419,14 +469,20 @@ sysprof_document_loader_load_mapped_file_cb (GObject *object, g_autoptr(GMappedFile) mapped_file = NULL; g_autoptr(GError) error = NULL; g_autoptr(GTask) task = user_data; + SysprofDocumentLoader *self; g_assert (G_IS_ASYNC_RESULT (result)); g_assert (G_IS_TASK (task)); + self = g_task_get_source_object (task); + if (!(mapped_file = mapped_file_new_finish (result, &error))) g_task_return_error (task, g_steal_pointer (&error)); else _sysprof_document_new_async (mapped_file, + set_progress, + g_object_ref (self), + g_object_unref, g_task_get_cancellable (task), sysprof_document_loader_load_document_cb, g_object_ref (task)); @@ -462,6 +518,8 @@ sysprof_document_loader_load_async (SysprofDocumentLoader *self, g_task_set_task_data (task, g_object_ref (self->symbolizer), g_object_unref); g_task_set_source_tag (task, sysprof_document_loader_load_async); + set_progress (0., _("Loading document"), self); + if (self->fd != -1) mapped_file_new_from_fd_async (self->fd, cancellable, @@ -496,6 +554,8 @@ sysprof_document_loader_load_finish (SysprofDocumentLoader *self, g_return_val_if_fail (G_IS_TASK (result), NULL); g_return_val_if_fail (g_task_is_valid (result, self), NULL); + set_progress (1., NULL, self); + ret = g_task_propagate_pointer (G_TASK (result), error); g_return_val_if_fail (!ret || SYSPROF_IS_DOCUMENT (ret), NULL); diff --git a/src/libsysprof-analyze/sysprof-document-private.h b/src/libsysprof-analyze/sysprof-document-private.h index 11906ab3..7bda9ee7 100644 --- a/src/libsysprof-analyze/sysprof-document-private.h +++ b/src/libsysprof-analyze/sysprof-document-private.h @@ -38,7 +38,14 @@ typedef struct _SysprofDocumentTimedValue }; } SysprofDocumentTimedValue; +typedef void (*ProgressFunc) (double fraction, + const char *message, + gpointer user_data); + void _sysprof_document_new_async (GMappedFile *mapped_file, + ProgressFunc progress, + gpointer progress_data, + GDestroyNotify progress_data_destroy, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index fcce54d6..1bb37bcb 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -23,6 +23,7 @@ #include #include +#include #include "sysprof-document-private.h" @@ -682,6 +683,38 @@ is_data_type (SysprofCaptureFrameType type) } } +typedef struct _Load +{ + GMappedFile *mapped_file; + ProgressFunc progress; + gpointer progress_data; + GDestroyNotify progress_data_destroy; +} Load; + +static void +load_free (Load *load) +{ + g_clear_pointer (&load->mapped_file, g_mapped_file_unref); + + if (load->progress_data_destroy) + load->progress_data_destroy (load->progress_data); + + load->progress = NULL; + load->progress_data = NULL; + load->progress_data_destroy = NULL; + + g_free (load); +} + +static inline void +load_progress (Load *load, + double fraction, + const char *message) +{ + if (load->progress) + load->progress (fraction, message, load->progress_data); +} + static void sysprof_document_load_worker (GTask *task, gpointer source_object, @@ -690,18 +723,19 @@ sysprof_document_load_worker (GTask *task, { g_autoptr(SysprofDocument) self = NULL; g_autoptr(GHashTable) files = NULL; - GMappedFile *mapped_file = task_data; + Load *load = task_data; gint64 guessed_end_nsec = 0; goffset pos; gsize len; + guint count; g_assert (source_object == NULL); - g_assert (mapped_file != NULL); + g_assert (load != NULL); self = g_object_new (SYSPROF_TYPE_DOCUMENT, NULL); - self->mapped_file = g_mapped_file_ref (mapped_file); - self->base = (const guint8 *)g_mapped_file_get_contents (mapped_file); - len = g_mapped_file_get_length (mapped_file); + self->mapped_file = g_mapped_file_ref (load->mapped_file); + self->base = (const guint8 *)g_mapped_file_get_contents (load->mapped_file); + len = g_mapped_file_get_length (load->mapped_file); if (len < sizeof self->header) { @@ -729,6 +763,9 @@ sysprof_document_load_worker (GTask *task, self->time_span.begin_nsec = self->header.time; self->time_span.end_nsec = self->header.end_time; + load_progress (load, .1, _("Indexing capture data frames")); + + count = 0; pos = sizeof self->header; while (pos < (len - sizeof(guint16))) { @@ -853,18 +890,35 @@ sysprof_document_load_worker (GTask *task, } pos += frame_len; + count++; g_array_append_val (self->frames, ptr); + + if (count % 100 == 0) + load_progress (load, + (pos / (double)len) * .4, + _("Indexing capture data frames")); } if (guessed_end_nsec != 0) self->time_span.end_nsec = guessed_end_nsec; + load_progress (load, .6, _("Discovering file system mounts")); sysprof_document_load_mounts (self); + + load_progress (load, .65, _("Discovering process mount namespaces")); sysprof_document_load_mountinfos (self); + + load_progress (load, .7, _("Analyzing process address layouts")); sysprof_document_load_memory_maps (self); + + load_progress (load, .75, _("Analyzing process command line")); sysprof_document_load_processes (self); + + load_progress (load, .8, _("Analyzing file system overlays")); sysprof_document_load_overlays (self); + + load_progress (load, .85, _("Processing counters")); sysprof_document_load_counters (self); g_task_return_pointer (task, g_steal_pointer (&self), g_object_unref); @@ -872,20 +926,28 @@ sysprof_document_load_worker (GTask *task, void _sysprof_document_new_async (GMappedFile *mapped_file, + ProgressFunc progress, + gpointer progress_data, + GDestroyNotify progress_data_destroy, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_autoptr(GTask) task = NULL; + Load *load; g_return_if_fail (mapped_file != NULL); g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); + load = g_new0 (Load, 1); + load->mapped_file = g_mapped_file_ref (mapped_file); + load->progress = progress; + load->progress_data = progress_data; + load->progress_data_destroy = progress_data_destroy; + task = g_task_new (NULL, cancellable, callback, user_data); g_task_set_source_tag (task, _sysprof_document_new_async); - g_task_set_task_data (task, - g_mapped_file_ref (mapped_file), - (GDestroyNotify)g_mapped_file_unref); + g_task_set_task_data (task, load, (GDestroyNotify)load_free); g_task_run_in_thread (task, sysprof_document_load_worker); } diff --git a/src/libsysprof-gtk/tests/test-callgraph.c b/src/libsysprof-gtk/tests/test-callgraph.c index 97d9e76a..ae4a681c 100644 --- a/src/libsysprof-gtk/tests/test-callgraph.c +++ b/src/libsysprof-gtk/tests/test-callgraph.c @@ -35,6 +35,28 @@ static const GOptionEntry entries[] = { { 0 } }; +static void +load_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + g_autoptr(GtkWidget) view = user_data; + g_autoptr(SysprofDocument) document = NULL; + g_autoptr(GListModel) model = NULL; + g_autoptr(GError) error = NULL; + + document = sysprof_document_loader_load_finish (SYSPROF_DOCUMENT_LOADER (object), result, &error); + g_assert_no_error (error); + g_assert_nonnull (document); + + model = sysprof_document_list_samples (document); + + g_object_set (view, + "document", document, + "traceables", model, + NULL); +} + int main (int argc, char *argv[]) @@ -43,11 +65,13 @@ main (int argc, g_autoptr(SysprofDocumentLoader) loader = NULL; g_autoptr(SysprofDocument) document = NULL; g_autoptr(SysprofMultiSymbolizer) multi = NULL; - g_autoptr(GListModel) model = NULL; g_autoptr(GError) error = NULL; SysprofCallgraphView *view; GtkWidget *box; GtkWidget *hbox; + GtkWidget *status; + GtkWidget *progress; + GtkWidget *message; GtkWidget *threads; GtkWindow *window; @@ -93,12 +117,6 @@ main (int argc, loader = sysprof_document_loader_new (filename); sysprof_document_loader_set_symbolizer (loader, SYSPROF_SYMBOLIZER (multi)); - g_print ("Loading %s, ignoring embedded symbols...\n", filename); - if (!(document = sysprof_document_loader_load (loader, NULL, &error))) - g_error ("Failed to load document: %s", error->message); - - model = sysprof_document_list_samples (document); - window = g_object_new (GTK_TYPE_WINDOW, "default-width", 800, "default-height", 600, @@ -117,19 +135,34 @@ main (int argc, NULL); gtk_box_append (GTK_BOX (hbox), threads); view = g_object_new (SYSPROF_TYPE_WEIGHTED_CALLGRAPH_VIEW, - "traceables", model, - "document", document, "include-threads", include_threads, + "vexpand", TRUE, NULL); gtk_box_append (GTK_BOX (box), GTK_WIDGET (view)); g_signal_connect_swapped (window, "close-request", G_CALLBACK (g_main_loop_quit), main_loop); + status = g_object_new (GTK_TYPE_BOX, + "spacing", 6, + NULL); + message = g_object_new (GTK_TYPE_LABEL, + "xalign", .0f, + "hexpand", TRUE, + NULL); + progress = g_object_new (GTK_TYPE_PROGRESS_BAR, + NULL); + gtk_box_append (GTK_BOX (status), message); + gtk_box_append (GTK_BOX (status), progress); + gtk_box_append (GTK_BOX (box), status); gtk_window_set_child (window, box); gtk_window_present (window); g_object_bind_property (threads, "active", view, "include-threads", 0); + g_object_bind_property (loader, "message", message, "label", 0); + g_object_bind_property (loader, "fraction", progress, "fraction", 0); + + sysprof_document_loader_load_async (loader, NULL, load_cb, g_object_ref (view)); g_main_loop_run (main_loop);