From 4a8d5007e51af8d79989f84c89496b2801986684 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 20 Jul 2023 16:04:42 -0700 Subject: [PATCH] libsysprof: add scaffolding to perform category summaries The goal here is to summarize the categories starting from a specific frame. --- src/libsysprof/meson.build | 2 + src/libsysprof/sysprof-callgraph-frame.c | 74 +++++++++++++ src/libsysprof/sysprof-callgraph-frame.h | 9 ++ src/libsysprof/sysprof-category-summary.c | 124 ++++++++++++++++++++++ src/libsysprof/sysprof-category-summary.h | 37 +++++++ src/libsysprof/sysprof.h | 1 + 6 files changed, 247 insertions(+) create mode 100644 src/libsysprof/sysprof-category-summary.c create mode 100644 src/libsysprof/sysprof-category-summary.h diff --git a/src/libsysprof/meson.build b/src/libsysprof/meson.build index 0b55302b..1d039b1e 100644 --- a/src/libsysprof/meson.build +++ b/src/libsysprof/meson.build @@ -6,6 +6,7 @@ libsysprof_public_sources = [ 'sysprof-callgraph.c', 'sysprof-callgraph-categorize.c', 'sysprof-categories.c', + 'sysprof-category-summary.c', 'sysprof-cpu-info.c', 'sysprof-cpu-usage.c', 'sysprof-diagnostic.c', @@ -64,6 +65,7 @@ libsysprof_public_headers = [ 'sysprof-callgraph-frame.h', 'sysprof-callgraph-symbol.h', 'sysprof-callgraph.h', + 'sysprof-category-summary.h', 'sysprof-cpu-info.h', 'sysprof-cpu-usage.h', 'sysprof-diagnostic.h', diff --git a/src/libsysprof/sysprof-callgraph-frame.c b/src/libsysprof/sysprof-callgraph-frame.c index 3b918874..655187e6 100644 --- a/src/libsysprof/sysprof-callgraph-frame.c +++ b/src/libsysprof/sysprof-callgraph-frame.c @@ -24,6 +24,7 @@ #include "sysprof-callgraph-private.h" #include "sysprof-callgraph-frame-private.h" +#include "sysprof-category-summary.h" #include "sysprof-enums.h" #include "sysprof-symbol-private.h" #include "sysprof-document-bitset-index-private.h" @@ -508,3 +509,76 @@ sysprof_callgraph_frame_get_category (SysprofCallgraphFrame *self) return SYSPROF_CALLGRAPH_CATEGORY_UNCATEGORIZED; } + +static void +summarize_node (const SysprofCallgraphNode *node, + GListStore *store, + GHashTable *category_to_summary) +{ + for (const SysprofCallgraphNode *iter = node->children; iter; iter = iter->next) + summarize_node (iter, store, category_to_summary); +} + +static void +sysprof_callgraph_frame_summarize (GTask *task, + gpointer source_object, + gpointer task_data, + GCancellable *cancellable) +{ + SysprofCallgraphFrame *self = source_object; + g_autoptr(GHashTable) category_to_summary = NULL; + g_autoptr(GListStore) store = NULL; + + g_assert (G_IS_TASK (task)); + g_assert (SYSPROF_IS_CALLGRAPH_FRAME (self)); + g_assert (SYSPROF_IS_CALLGRAPH (task_data)); + + store = g_list_store_new (G_TYPE_OBJECT); + category_to_summary = g_hash_table_new (NULL, NULL); + + summarize_node (self->node, store, category_to_summary); + + g_task_return_pointer (task, g_steal_pointer (&store), g_object_unref); +} + +void +sysprof_callgraph_frame_summarize_async (SysprofCallgraphFrame *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_autoptr(GTask) task = NULL; + + g_return_if_fail (SYSPROF_IS_CALLGRAPH_FRAME (self)); + + task = g_task_new (self, cancellable, callback, user_data); + g_task_set_source_tag (task, sysprof_callgraph_frame_summarize_async); + + if (self->callgraph == NULL) + { + g_task_return_new_error (task, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "Callgraph disposed"); + return; + } + + g_task_set_task_data (task, g_object_ref (self->callgraph), g_object_unref); + g_task_run_in_thread (task, sysprof_callgraph_frame_summarize); +} + +/** + * sysprof_callgraph_frame_summarize_finish: + * + * Returns: (transfer full): a #GListModel of #SysprofCategorySummary + */ +GListModel * +sysprof_callgraph_frame_summarize_finish (SysprofCallgraphFrame *self, + GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (SYSPROF_IS_CALLGRAPH_FRAME (self), NULL); + g_return_val_if_fail (G_IS_TASK (result), NULL); + + return g_task_propagate_pointer (G_TASK (result), error); +} diff --git a/src/libsysprof/sysprof-callgraph-frame.h b/src/libsysprof/sysprof-callgraph-frame.h index 083dd681..3e173392 100644 --- a/src/libsysprof/sysprof-callgraph-frame.h +++ b/src/libsysprof/sysprof-callgraph-frame.h @@ -50,5 +50,14 @@ GListModel *sysprof_callgraph_frame_list_traceables_finish (SysprofCallgraphF GError **error); SYSPROF_AVAILABLE_IN_ALL gboolean sysprof_callgraph_frame_is_leaf (SysprofCallgraphFrame *self); +SYSPROF_AVAILABLE_IN_ALL +void sysprof_callgraph_frame_summarize_async (SysprofCallgraphFrame *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +SYSPROF_AVAILABLE_IN_ALL +GListModel *sysprof_callgraph_frame_summarize_finish (SysprofCallgraphFrame *self, + GAsyncResult *result, + GError **error); G_END_DECLS diff --git a/src/libsysprof/sysprof-category-summary.c b/src/libsysprof/sysprof-category-summary.c new file mode 100644 index 00000000..df2d7c47 --- /dev/null +++ b/src/libsysprof/sysprof-category-summary.c @@ -0,0 +1,124 @@ +/* sysprof-category-summary.c + * + * Copyright 2023 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-category-summary.h" +#include "sysprof-enums.h" + +struct _SysprofCategorySummary +{ + GObject parent_instance; + SysprofCallgraphCategory category; + guint64 count; + guint64 total; +}; + +enum { + PROP_0, + PROP_CATEGORY, + PROP_FRACTION, + N_PROPS +}; + +G_DEFINE_FINAL_TYPE (SysprofCategorySummary, sysprof_category_summary, G_TYPE_OBJECT) + +static GParamSpec *properties [N_PROPS]; + +static void +sysprof_category_summary_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SysprofCategorySummary *self = SYSPROF_CATEGORY_SUMMARY (object); + + switch (prop_id) + { + case PROP_CATEGORY: + g_value_set_enum (value, sysprof_category_summary_get_category (self)); + break; + + case PROP_FRACTION: + g_value_set_double (value, sysprof_category_summary_get_fraction (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_category_summary_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + SysprofCategorySummary *self = SYSPROF_CATEGORY_SUMMARY (object); + + switch (prop_id) + { + case PROP_CATEGORY: + self->category = g_value_get_enum (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_category_summary_class_init (SysprofCategorySummaryClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->get_property = sysprof_category_summary_get_property; + object_class->set_property = sysprof_category_summary_set_property; + + properties[PROP_CATEGORY] = + g_param_spec_enum ("category", NULL, NULL, + SYSPROF_TYPE_CALLGRAPH_CATEGORY, + SYSPROF_CALLGRAPH_CATEGORY_UNCATEGORIZED, + (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + + properties[PROP_FRACTION] = + g_param_spec_double ("fraction", NULL, NULL, + 0, 1, 0, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); +} + +static void +sysprof_category_summary_init (SysprofCategorySummary *self) +{ +} + +SysprofCallgraphCategory +sysprof_category_summary_get_category (SysprofCategorySummary *self) +{ + return self->category; +} + +double +sysprof_category_summary_get_fraction (SysprofCategorySummary *self) +{ + return CLAMP (self->count / (double)self->total, .0, 1.); +} diff --git a/src/libsysprof/sysprof-category-summary.h b/src/libsysprof/sysprof-category-summary.h new file mode 100644 index 00000000..f8679024 --- /dev/null +++ b/src/libsysprof/sysprof-category-summary.h @@ -0,0 +1,37 @@ +/* sysprof-category-summary.h + * + * Copyright 2023 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-callgraph.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_CATEGORY_SUMMARY (sysprof_category_summary_get_type()) + +SYSPROF_AVAILABLE_IN_ALL +G_DECLARE_FINAL_TYPE (SysprofCategorySummary, sysprof_category_summary, SYSPROF, CATEGORY_SUMMARY, GObject) + +SYSPROF_AVAILABLE_IN_ALL +SysprofCallgraphCategory sysprof_category_summary_get_category (SysprofCategorySummary *self); +SYSPROF_AVAILABLE_IN_ALL +double sysprof_category_summary_get_fraction (SysprofCategorySummary *self); + +G_END_DECLS diff --git a/src/libsysprof/sysprof.h b/src/libsysprof/sysprof.h index b29afc15..b284e7e4 100644 --- a/src/libsysprof/sysprof.h +++ b/src/libsysprof/sysprof.h @@ -30,6 +30,7 @@ G_BEGIN_DECLS # include "sysprof-callgraph-frame.h" # include "sysprof-callgraph-symbol.h" # include "sysprof-callgraph.h" +# include "sysprof-category-summary.h" # include "sysprof-cpu-info.h" # include "sysprof-cpu-usage.h" # include "sysprof-diagnostic.h"