diff --git a/src/libsysprof-gtk/libsysprof-gtk.gresource.xml b/src/libsysprof-gtk/libsysprof-gtk.gresource.xml
new file mode 100644
index 00000000..2a36c015
--- /dev/null
+++ b/src/libsysprof-gtk/libsysprof-gtk.gresource.xml
@@ -0,0 +1,6 @@
+
+
+
+ sysprof-callgraph-view.ui
+
+
diff --git a/src/libsysprof-gtk/meson.build b/src/libsysprof-gtk/meson.build
new file mode 100644
index 00000000..2c517f6e
--- /dev/null
+++ b/src/libsysprof-gtk/meson.build
@@ -0,0 +1,74 @@
+libsysprof_gtk_public_sources = [
+ 'sysprof-callgraph-view.c',
+ 'sysprof-weighted-callgraph-view.c',
+]
+
+libsysprof_gtk_private_sources = [
+]
+
+libsysprof_gtk_public_headers = [
+ 'sysprof-gtk.h',
+
+ 'sysprof-callgraph-view.h',
+ 'sysprof-weighted-callgraph-view.h',
+]
+
+libsysprof_gtk_deps = [
+ dependency('gtk4', version: gtk_req_version),
+
+ libsysprof_analyze_dep,
+]
+
+libsysprof_gtk_resources = gnome.compile_resources(
+ 'libsysprof-gtk-resources',
+ 'libsysprof-gtk.gresource.xml',
+ c_name: 'libsysprof_gtk',
+)
+
+libsysprof_gtk_static = static_library(
+ 'sysprof-gtk-@0@'.format(soname_major_version),
+ (libsysprof_gtk_public_sources +
+ libsysprof_gtk_private_sources +
+ libsysprof_gtk_resources +
+ mapped_ring_buffer_sources),
+
+ include_directories: [include_directories('.'),
+ ipc_include_dirs,
+ libsysprof_capture_include_dirs],
+ dependencies: libsysprof_gtk_deps,
+ gnu_symbol_visibility: 'hidden',
+)
+
+libsysprof_gtk_static_dep = declare_dependency(
+ link_with: libsysprof_gtk_static,
+ dependencies: libsysprof_gtk_deps,
+ include_directories: [include_directories('.'),
+ libsysprof_capture_include_dirs],
+)
+
+libsysprof_gtk = library('sysprof-gtk-@0@'.format(soname_major_version),
+ dependencies: [libsysprof_gtk_static_dep],
+ gnu_symbol_visibility: 'hidden',
+ version: '@0@.0.0'.format(soname_major_version),
+ darwin_versions: '@0@.0'.format(soname_major_version),
+ install: true,
+)
+
+libsysprof_gtk_dep = declare_dependency(
+ link_with: libsysprof_gtk,
+ dependencies: libsysprof_gtk_deps,
+ include_directories: [include_directories('.'), libsysprof_capture_include_dirs],
+)
+meson.override_dependency('sysprof-gtk-@0@'.format(soname_major_version), libsysprof_gtk_dep)
+
+pkgconfig.generate(libsysprof_gtk,
+ subdirs: [sysprof_header_subdir],
+ description: 'Provides GTK widgets for sysprof visualizion',
+ install_dir: join_paths(get_option('libdir'), 'pkgconfig'),
+ requires: ['gtk4'],
+ variables: ['datadir=' + datadir_for_pc_file],
+)
+
+install_headers(libsysprof_gtk_public_headers, subdir: sysprof_header_subdir)
+
+subdir('tests')
diff --git a/src/libsysprof-gtk/sysprof-callgraph-view-private.h b/src/libsysprof-gtk/sysprof-callgraph-view-private.h
new file mode 100644
index 00000000..b33a482d
--- /dev/null
+++ b/src/libsysprof-gtk/sysprof-callgraph-view-private.h
@@ -0,0 +1,55 @@
+/* sysprof-callgraph-view-private.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-view.h"
+
+G_BEGIN_DECLS
+
+#define SYSPROF_CALLGRAPH_VIEW_GET_CLASS(instance) G_TYPE_INSTANCE_GET_CLASS(instance, SYSPROF_TYPE_CALLGRAPH_VIEW, SysprofCallgraphViewClass)
+
+struct _SysprofCallgraphView
+{
+ GtkWidget parent_instance;
+
+ SysprofDocument *document;
+ GListModel *traceables;
+
+ GtkColumnView *column_view;
+ GtkWidget *scrolled_window;
+
+ GCancellable *cancellable;
+
+ guint reload_source;
+};
+
+struct _SysprofCallgraphViewClass
+{
+ GtkWidgetClass parent_class;
+
+ gsize augment_size;
+ void (*augment_func) (SysprofCallgraph *callgraph,
+ SysprofCallgraphNode *node,
+ SysprofDocumentFrame *frame,
+ gpointer user_data);
+};
+
+G_END_DECLS
diff --git a/src/libsysprof-gtk/sysprof-callgraph-view.c b/src/libsysprof-gtk/sysprof-callgraph-view.c
new file mode 100644
index 00000000..7d2a5b23
--- /dev/null
+++ b/src/libsysprof-gtk/sysprof-callgraph-view.c
@@ -0,0 +1,375 @@
+/* sysprof-callgraph-view.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 "libsysprof-gtk-resources.h"
+
+#include "sysprof-callgraph-view-private.h"
+
+enum {
+ PROP_0,
+ PROP_DOCUMENT,
+ PROP_TRACEABLES,
+ N_PROPS
+};
+
+G_DEFINE_ABSTRACT_TYPE (SysprofCallgraphView, sysprof_callgraph_view, GTK_TYPE_WIDGET)
+
+static GParamSpec *properties [N_PROPS];
+
+static gboolean
+sysprof_callgraph_view_key_pressed_cb (GtkTreeExpander *expander,
+ guint keyval,
+ guint keycode,
+ GdkModifierType state,
+ GtkEventControllerKey *controller)
+{
+ GtkTreeListRow *row;
+
+ g_assert (GTK_IS_TREE_EXPANDER (expander));
+ g_assert (GTK_IS_EVENT_CONTROLLER_KEY (controller));
+
+ row = gtk_tree_expander_get_list_row (expander);
+
+ if (keyval == GDK_KEY_space)
+ gtk_tree_list_row_set_expanded (row, !gtk_tree_list_row_get_expanded (row));
+ else if (keyval == GDK_KEY_Right)
+ gtk_tree_list_row_set_expanded (row, TRUE);
+ else if (keyval == GDK_KEY_Left)
+ gtk_tree_list_row_set_expanded (row, FALSE);
+ else
+ return FALSE;
+
+ return TRUE;
+}
+
+static void
+sysprof_callgraph_view_dispose (GObject *object)
+{
+ SysprofCallgraphView *self = (SysprofCallgraphView *)object;
+
+ g_clear_handle_id (&self->reload_source, g_source_remove);
+
+ g_clear_pointer ((GtkWidget **)&self->scrolled_window, gtk_widget_unparent);
+
+ g_cancellable_cancel (self->cancellable);
+ g_clear_object (&self->cancellable);
+
+ g_clear_object (&self->document);
+ g_clear_object (&self->traceables);
+
+ G_OBJECT_CLASS (sysprof_callgraph_view_parent_class)->dispose (object);
+}
+
+static void
+sysprof_callgraph_view_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ SysprofCallgraphView *self = SYSPROF_CALLGRAPH_VIEW (object);
+
+ switch (prop_id)
+ {
+ case PROP_DOCUMENT:
+ g_value_set_object (value, sysprof_callgraph_view_get_document (self));
+ break;
+
+ case PROP_TRACEABLES:
+ g_value_set_object (value, sysprof_callgraph_view_get_traceables (self));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+sysprof_callgraph_view_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ SysprofCallgraphView *self = SYSPROF_CALLGRAPH_VIEW (object);
+
+ switch (prop_id)
+ {
+ case PROP_DOCUMENT:
+ sysprof_callgraph_view_set_document (self, g_value_get_object (value));
+ break;
+
+ case PROP_TRACEABLES:
+ sysprof_callgraph_view_set_traceables (self, g_value_get_object (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+sysprof_callgraph_view_class_init (SysprofCallgraphViewClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->dispose = sysprof_callgraph_view_dispose;
+ object_class->get_property = sysprof_callgraph_view_get_property;
+ object_class->set_property = sysprof_callgraph_view_set_property;
+
+ properties[PROP_DOCUMENT] =
+ g_param_spec_object ("document", NULL, NULL,
+ SYSPROF_TYPE_DOCUMENT,
+ (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ properties[PROP_TRACEABLES] =
+ g_param_spec_object ("traceables", NULL, NULL,
+ G_TYPE_LIST_MODEL,
+ (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_properties (object_class, N_PROPS, properties);
+
+ gtk_widget_class_set_template_from_resource (widget_class, "/libsysprof-gtk/sysprof-callgraph-view.ui");
+ gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
+ gtk_widget_class_bind_template_child (widget_class, SysprofCallgraphView, column_view);
+ gtk_widget_class_bind_template_child (widget_class, SysprofCallgraphView, scrolled_window);
+ gtk_widget_class_bind_template_callback (widget_class, sysprof_callgraph_view_key_pressed_cb);
+
+ klass->augment_size = GLIB_SIZEOF_VOID_P;
+
+ g_resources_register (libsysprof_gtk_get_resource ());
+}
+
+static void
+sysprof_callgraph_view_init (SysprofCallgraphView *self)
+{
+ gtk_widget_init_template (GTK_WIDGET (self));
+}
+
+static GListModel *
+sysprof_callgraph_view_create_model_func (gpointer item,
+ gpointer user_data)
+{
+ if (g_list_model_get_n_items (G_LIST_MODEL (item)) > 0)
+ return g_object_ref (G_LIST_MODEL (item));
+
+ return NULL;
+}
+
+static void
+sysprof_callgraph_view_reload_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ SysprofDocument *document = (SysprofDocument *)object;
+ g_autoptr(SysprofCallgraphView) self = user_data;
+ g_autoptr(SysprofCallgraph) callgraph = NULL;
+ g_autoptr(GtkTreeListRowSorter) sorter = NULL;
+ g_autoptr(GtkMultiSelection) model = NULL;
+ g_autoptr(GtkSortListModel) sort_model = NULL;
+ g_autoptr(GtkTreeListModel) tree = NULL;
+ g_autoptr(GError) error = NULL;
+ GtkSorter *column_sorter;
+
+ g_assert (SYSPROF_IS_DOCUMENT (document));
+ g_assert (G_IS_ASYNC_RESULT (result));
+ g_assert (SYSPROF_IS_CALLGRAPH_VIEW (self));
+
+ if (!(callgraph = sysprof_document_callgraph_finish (document, result, &error)))
+ {
+ g_debug ("Failed to generate callgraph: %s", error->message);
+ return;
+ }
+
+ tree = gtk_tree_list_model_new (g_object_ref (G_LIST_MODEL (callgraph)),
+ FALSE,
+ FALSE,
+ sysprof_callgraph_view_create_model_func,
+ NULL,
+ NULL);
+ column_sorter = gtk_column_view_get_sorter (self->column_view);
+ sorter = gtk_tree_list_row_sorter_new (g_object_ref (column_sorter));
+ sort_model = gtk_sort_list_model_new (g_object_ref (G_LIST_MODEL (tree)),
+ g_object_ref (GTK_SORTER (sorter)));
+ model = gtk_multi_selection_new (g_object_ref (G_LIST_MODEL (sort_model)));
+ gtk_column_view_set_model (self->column_view, GTK_SELECTION_MODEL (model));
+}
+
+static gboolean
+sysprof_callgraph_view_reload (SysprofCallgraphView *self)
+{
+ g_assert (SYSPROF_IS_CALLGRAPH_VIEW (self));
+
+ g_clear_handle_id (&self->reload_source, g_source_remove);
+
+ sysprof_document_callgraph_async (self->document,
+ self->traceables,
+ SYSPROF_CALLGRAPH_VIEW_GET_CLASS (self)->augment_size,
+ SYSPROF_CALLGRAPH_VIEW_GET_CLASS (self)->augment_func,
+ NULL,
+ NULL,
+ self->cancellable,
+ sysprof_callgraph_view_reload_cb,
+ g_object_ref (self));
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
+sysprof_callgraph_view_queue_reload (SysprofCallgraphView *self)
+{
+ g_assert (SYSPROF_IS_CALLGRAPH_VIEW (self));
+
+ gtk_column_view_set_model (self->column_view, NULL);
+
+ g_clear_handle_id (&self->reload_source, g_source_remove);
+ g_cancellable_cancel (self->cancellable);
+
+ g_clear_object (&self->cancellable);
+ self->cancellable = g_cancellable_new ();
+
+ if (self->document != NULL && self->traceables != NULL)
+ self->reload_source = g_idle_add_full (G_PRIORITY_LOW,
+ (GSourceFunc)sysprof_callgraph_view_reload,
+ g_object_ref (self),
+ g_object_unref);
+}
+
+/**
+ * sysprof_callgraph_view_get_document:
+ * @self: a #SysprofCallgraphView
+ *
+ * Gets the document for the callgraph.
+ *
+ * Returns: (transfer none) (nullable): a #SysprofDocument or %NULL
+ */
+SysprofDocument *
+sysprof_callgraph_view_get_document (SysprofCallgraphView *self)
+{
+ g_return_val_if_fail (SYSPROF_IS_CALLGRAPH_VIEW (self), NULL);
+
+ return self->document;
+}
+
+void
+sysprof_callgraph_view_set_document (SysprofCallgraphView *self,
+ SysprofDocument *document)
+{
+ g_return_if_fail (SYSPROF_IS_CALLGRAPH_VIEW (self));
+
+ if (g_set_object (&self->document, document))
+ {
+ sysprof_callgraph_view_queue_reload (self);
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DOCUMENT]);
+ }
+}
+
+/**
+ * sysprof_callgraph_view_get_traceables:
+ * @self: a #SysprofCallgraphView
+ *
+ * Gets the list of traceables to be displayed.
+ *
+ * Returns: (transfer none) (nullable): a #GListModel or %NULL
+ */
+GListModel *
+sysprof_callgraph_view_get_traceables (SysprofCallgraphView *self)
+{
+ g_return_val_if_fail (SYSPROF_IS_CALLGRAPH_VIEW (self), NULL);
+
+ return self->traceables;
+}
+
+void
+sysprof_callgraph_view_set_traceables (SysprofCallgraphView *self,
+ GListModel *traceables)
+{
+ g_return_if_fail (SYSPROF_IS_CALLGRAPH_VIEW (self));
+ g_return_if_fail (!traceables || G_IS_LIST_MODEL (traceables));
+
+ if (g_set_object (&self->traceables, traceables))
+ {
+ sysprof_callgraph_view_queue_reload (self);
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_TRACEABLES]);
+ }
+}
+
+#if 0
+/**
+ * sysprof_callgraph_view_get_callgraph:
+ * @self: a #SysprofCallgraphView
+ *
+ * Gets the #SysprofCallgraph for the view.
+ *
+ * Returns: (transfer none) (nullable): a #SysprofCallgraph or %NULL
+ */
+SysprofCallgraph *
+sysprof_callgraph_view_get_callgraph (SysprofCallgraphView *self)
+{
+ g_return_val_if_fail (SYSPROF_IS_CALLGRAPH_VIEW (self), NULL);
+
+ return self->callgraph;
+}
+
+/**
+ * sysprof_callgraph_view_set_callgraph:
+ * @self: a #SysprofCallgraphView
+ * @callgraph: the #SysprofCallgraph
+ *
+ * Sets the callgraph for the view.
+ */
+void
+sysprof_callgraph_view_set_callgraph (SysprofCallgraphView *self,
+ SysprofCallgraph *callgraph)
+{
+ g_return_if_fail (SYSPROF_IS_CALLGRAPH_VIEW (self));
+ g_return_if_fail (SYSPROF_IS_CALLGRAPH (callgraph));
+
+ if (g_set_object (&self->callgraph, callgraph))
+ {
+ g_autoptr(GtkMultiSelection) model = NULL;
+
+ if (callgraph != NULL)
+ {
+ g_autoptr(GtkTreeListRowSorter) sorter = NULL;
+ g_autoptr(GtkSortListModel) sort_model = NULL;
+ g_autoptr(GtkTreeListModel) tree = NULL;
+ GtkSorter *column_sorter;
+
+ tree = gtk_tree_list_model_new (g_object_ref (G_LIST_MODEL (callgraph)),
+ FALSE,
+ FALSE,
+ sysprof_callgraph_view_create_model_func,
+ NULL,
+ NULL);
+
+ column_sorter = gtk_column_view_get_sorter (self->column_view);
+ sorter = gtk_tree_list_row_sorter_new (g_object_ref (column_sorter));
+ sort_model = gtk_sort_list_model_new (g_object_ref (G_LIST_MODEL (tree)), g_object_ref (GTK_SORTER (sorter)));
+ model = gtk_multi_selection_new (g_object_ref (G_LIST_MODEL (sort_model)));
+ }
+
+ gtk_column_view_set_model (self->column_view, GTK_SELECTION_MODEL (model));
+
+ g_object_notify_by_pspec (G_OBJECT (callgraph), properties[PROP_CALLGRAPH]);
+ }
+}
+#endif
diff --git a/src/libsysprof-gtk/sysprof-callgraph-view.h b/src/libsysprof-gtk/sysprof-callgraph-view.h
new file mode 100644
index 00000000..f61de95e
--- /dev/null
+++ b/src/libsysprof-gtk/sysprof-callgraph-view.h
@@ -0,0 +1,53 @@
+/* sysprof-callgraph-view.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
+
+#include
+#include
+
+G_BEGIN_DECLS
+
+#define SYSPROF_TYPE_CALLGRAPH_VIEW (sysprof_callgraph_view_get_type())
+#define SYSPROF_IS_CALLGRAPH_VIEW(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_CALLGRAPH_VIEW)
+#define SYSPROF_CALLGRAPH_VIEW(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_CALLGRAPH_VIEW, SysprofCallgraphView)
+#define SYSPROF_CALLGRAPH_VIEW_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_CALLGRAPH_VIEW, SysprofCallgraphViewClass)
+
+typedef struct _SysprofCallgraphView SysprofCallgraphView;
+typedef struct _SysprofCallgraphViewClass SysprofCallgraphViewClass;
+
+SYSPROF_AVAILABLE_IN_ALL
+GType sysprof_callgraph_view_get_type (void) G_GNUC_CONST;
+SYSPROF_AVAILABLE_IN_ALL
+SysprofDocument *sysprof_callgraph_view_get_document (SysprofCallgraphView *self);
+SYSPROF_AVAILABLE_IN_ALL
+void sysprof_callgraph_view_set_document (SysprofCallgraphView *self,
+ SysprofDocument *document);
+SYSPROF_AVAILABLE_IN_ALL
+GListModel *sysprof_callgraph_view_get_traceables (SysprofCallgraphView *self);
+SYSPROF_AVAILABLE_IN_ALL
+void sysprof_callgraph_view_set_traceables (SysprofCallgraphView *self,
+ GListModel *model);
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofCallgraphView, g_object_unref)
+
+G_END_DECLS
diff --git a/src/libsysprof-gtk/sysprof-callgraph-view.ui b/src/libsysprof-gtk/sysprof-callgraph-view.ui
new file mode 100644
index 00000000..97d5b5a1
--- /dev/null
+++ b/src/libsysprof-gtk/sysprof-callgraph-view.ui
@@ -0,0 +1,90 @@
+
+
+
+
+
+
+
+
diff --git a/src/libsysprof-gtk/sysprof-gtk.h b/src/libsysprof-gtk/sysprof-gtk.h
new file mode 100644
index 00000000..31e1cd5f
--- /dev/null
+++ b/src/libsysprof-gtk/sysprof-gtk.h
@@ -0,0 +1,30 @@
+/* sysprof-gtk.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
+
+G_BEGIN_DECLS
+
+#define SYSPROF_GTK_INSIDE
+# include "sysprof-callgraph-view.h"
+# include "sysprof-weighted-callgraph-view.h"
+#undef SYSPROF_GTK_INSIDE
+
+G_END_DECLS
diff --git a/src/libsysprof-gtk/sysprof-weighted-callgraph-view.c b/src/libsysprof-gtk/sysprof-weighted-callgraph-view.c
new file mode 100644
index 00000000..8a7198fe
--- /dev/null
+++ b/src/libsysprof-gtk/sysprof-weighted-callgraph-view.c
@@ -0,0 +1,85 @@
+/* sysprof-weighted-callgraph-view.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-callgraph-view-private.h"
+#include "sysprof-weighted-callgraph-view.h"
+
+struct _SysprofWeightedCallgraphView
+{
+ SysprofCallgraphView parent_instance;
+};
+
+struct _SysprofWeightedCallgraphViewClass
+{
+ SysprofCallgraphViewClass parent_class;
+};
+
+typedef struct _AugmentWeight
+{
+ guint size;
+ guint total;
+} AugmentWeight;
+
+G_DEFINE_FINAL_TYPE (SysprofWeightedCallgraphView, sysprof_weighted_callgraph_view, SYSPROF_TYPE_CALLGRAPH_VIEW)
+
+static void
+augment_weight (SysprofCallgraph *callgraph,
+ SysprofCallgraphNode *node,
+ SysprofDocumentFrame *frame,
+ gpointer user_data)
+{
+ AugmentWeight *aug;
+
+ g_assert (SYSPROF_IS_CALLGRAPH (callgraph));
+ g_assert (node != NULL);
+ g_assert (SYSPROF_IS_DOCUMENT_SAMPLE (frame));
+ g_assert (user_data == NULL);
+
+ aug = sysprof_callgraph_get_augment (callgraph, node);
+ aug->size += 1;
+
+ for (; node; node = sysprof_callgraph_node_parent (node))
+ {
+ aug = sysprof_callgraph_get_augment (callgraph, node);
+ aug->total += 1;
+ }
+}
+
+static void
+sysprof_weighted_callgraph_view_class_init (SysprofWeightedCallgraphViewClass *klass)
+{
+ SysprofCallgraphViewClass *callgraph_view_class = SYSPROF_CALLGRAPH_VIEW_CLASS (klass);
+
+ callgraph_view_class->augment_size = sizeof (AugmentWeight);
+ callgraph_view_class->augment_func = augment_weight;
+}
+
+static void
+sysprof_weighted_callgraph_view_init (SysprofWeightedCallgraphView *self)
+{
+}
+
+GtkWidget *
+sysprof_weighted_callgraph_view_new (void)
+{
+ return g_object_new (SYSPROF_TYPE_WEIGHTED_CALLGRAPH_VIEW, NULL);
+}
diff --git a/src/libsysprof-gtk/sysprof-weighted-callgraph-view.h b/src/libsysprof-gtk/sysprof-weighted-callgraph-view.h
new file mode 100644
index 00000000..51297a4c
--- /dev/null
+++ b/src/libsysprof-gtk/sysprof-weighted-callgraph-view.h
@@ -0,0 +1,42 @@
+/* sysprof-weighted-callgraph-view.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-view.h"
+
+G_BEGIN_DECLS
+
+#define SYSPROF_TYPE_WEIGHTED_CALLGRAPH_VIEW (sysprof_weighted_callgraph_view_get_type())
+#define SYSPROF_IS_WEIGHTED_CALLGRAPH_VIEW(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_WEIGHTED_CALLGRAPH_VIEW)
+#define SYSPROF_WEIGHTED_CALLGRAPH_VIEW(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_WEIGHTED_CALLGRAPH_VIEW, SysprofWeightedCallgraphView)
+#define SYSPROF_WEIGHTED_CALLGRAPH_VIEW_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_WEIGHTED_CALLGRAPH_VIEW, SysprofWeightedCallgraphViewClass)
+
+typedef struct _SysprofWeightedCallgraphView SysprofWeightedCallgraphView;
+typedef struct _SysprofWeightedCallgraphViewClass SysprofWeightedCallgraphViewClass;
+
+SYSPROF_AVAILABLE_IN_ALL
+GType sysprof_weighted_callgraph_view_get_type (void) G_GNUC_CONST;
+SYSPROF_AVAILABLE_IN_ALL
+GtkWidget *sysprof_weighted_weighted_callgraph_view_new (void);
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofWeightedCallgraphView, g_object_unref)
+
+G_END_DECLS
diff --git a/src/libsysprof-gtk/tests/meson.build b/src/libsysprof-gtk/tests/meson.build
new file mode 100644
index 00000000..8c3c93e2
--- /dev/null
+++ b/src/libsysprof-gtk/tests/meson.build
@@ -0,0 +1,31 @@
+libsysprof_gtk_test_env = [
+ 'G_DEBUG=gc-friendly',
+ 'GSETTINGS_BACKEND=memory',
+ 'MALLOC_CHECK_=2',
+]
+
+libsysprof_gtk_testsuite_c_args = [
+ '-DG_LOG_DOMAIN="libdex"',
+ '-DG_ENABLE_DEBUG',
+ '-UG_DISABLE_ASSERT',
+ '-UG_DISABLE_CAST_CHECKS',
+]
+
+libsysprof_gtk_testsuite = {
+ 'test-callgraph' : {'skip': true},
+}
+
+libsysprof_gtk_testsuite_deps = [
+ libsysprof_analyze_static_dep,
+ libsysprof_gtk_static_dep,
+]
+
+foreach test, params: libsysprof_gtk_testsuite
+ test_exe = executable(test, '@0@.c'.format(test),
+ c_args: libsysprof_gtk_testsuite_c_args,
+ dependencies: libsysprof_gtk_testsuite_deps,
+ )
+ if not params.get('skip', false)
+ test(test, test_exe, env: libsysprof_gtk_test_env)
+ endif
+endforeach
diff --git a/src/libsysprof-gtk/tests/test-callgraph.c b/src/libsysprof-gtk/tests/test-callgraph.c
new file mode 100644
index 00000000..40376362
--- /dev/null
+++ b/src/libsysprof-gtk/tests/test-callgraph.c
@@ -0,0 +1,112 @@
+/* test-callgraph.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
+#include
+#include
+
+static GMainLoop *main_loop;
+static char *kallsyms_path;
+static char *filename;
+static const GOptionEntry entries[] = {
+ { "kallsyms", 'k', 0, G_OPTION_ARG_FILENAME, &kallsyms_path, "The path to kallsyms to use for decoding", "PATH" },
+ { 0 }
+};
+
+int
+main (int argc,
+ char *argv[])
+{
+ g_autoptr(GOptionContext) context = g_option_context_new ("- show a callgraph");
+ 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;
+ GtkWindow *window;
+
+ gtk_init ();
+ sysprof_clock_init ();
+
+ g_option_context_add_main_entries (context, entries, NULL);
+ if (!g_option_context_parse (context, &argc, &argv, &error))
+ {
+ g_printerr ("%s\n", error->message);
+ return 1;
+ }
+
+ if (argc != 2)
+ {
+ g_print ("usage: %s [OPTIONS] CAPTURE_FILE\n", argv[0]);
+ return 1;
+ }
+
+ filename = argv[1];
+
+ main_loop = g_main_loop_new (NULL, FALSE);
+
+ multi = sysprof_multi_symbolizer_new ();
+
+ if (kallsyms_path)
+ {
+ g_autoptr(GFile) kallsyms_file = g_file_new_for_path (kallsyms_path);
+ GFileInputStream *kallsyms_stream = g_file_read (kallsyms_file, NULL, NULL);
+
+ sysprof_multi_symbolizer_take (multi, sysprof_kallsyms_symbolizer_new_for_symbols (G_INPUT_STREAM (kallsyms_stream)));
+ }
+ else
+ {
+ 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 ());
+
+ 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,
+ NULL);
+ view = g_object_new (SYSPROF_TYPE_WEIGHTED_CALLGRAPH_VIEW,
+ "traceables", model,
+ "document", document,
+ NULL);
+ g_signal_connect_swapped (window,
+ "close-request",
+ G_CALLBACK (g_main_loop_quit),
+ main_loop);
+ gtk_window_set_child (window, GTK_WIDGET (view));
+ gtk_window_present (window);
+
+ g_main_loop_run (main_loop);
+
+ return 0;
+}
diff --git a/src/meson.build b/src/meson.build
index 616fcbf8..a2a841c5 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -75,6 +75,7 @@ if get_option('libsysprof') or get_option('agent')
endif
if get_option('gtk') and get_option('libsysprof')
subdir('libsysprof-ui')
+ subdir('libsysprof-gtk')
endif
if get_option('gtk') and get_option('libsysprof')
subdir('sysprof')