diff --git a/src/libsysprof-analyze/meson.build b/src/libsysprof-analyze/meson.build index ae7bd11f..723dc595 100644 --- a/src/libsysprof-analyze/meson.build +++ b/src/libsysprof-analyze/meson.build @@ -35,6 +35,7 @@ libsysprof_analyze_public_sources = [ libsysprof_analyze_private_sources = [ 'sysprof-address-layout.c', + 'sysprof-descendants-model.c', 'sysprof-document-bitset-index.c', 'sysprof-document-symbols.c', 'sysprof-elf.c', diff --git a/src/libsysprof-analyze/sysprof-callgraph-frame-private.h b/src/libsysprof-analyze/sysprof-callgraph-frame-private.h index 0d2814e5..2c58fbd2 100644 --- a/src/libsysprof-analyze/sysprof-callgraph-frame-private.h +++ b/src/libsysprof-analyze/sysprof-callgraph-frame-private.h @@ -26,6 +26,7 @@ G_BEGIN_DECLS SysprofCallgraphFrame *_sysprof_callgraph_frame_new_for_node (SysprofCallgraph *callgraph, + GObject *owner, SysprofCallgraphNode *node); G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-callgraph-frame.c b/src/libsysprof-analyze/sysprof-callgraph-frame.c index 0bca4418..ffb0572e 100644 --- a/src/libsysprof-analyze/sysprof-callgraph-frame.c +++ b/src/libsysprof-analyze/sysprof-callgraph-frame.c @@ -35,6 +35,7 @@ struct _SysprofCallgraphFrame { GObject parent_instance; SysprofCallgraph *callgraph; + GObject *owner; SysprofCallgraphNode *node; guint n_children; }; @@ -79,7 +80,7 @@ sysprof_callgraph_frame_get_item (GListModel *model, if (iter == NULL) return NULL; - return _sysprof_callgraph_frame_new_for_node (self->callgraph, iter); + return _sysprof_callgraph_frame_new_for_node (self->callgraph, self->owner, iter); } static void @@ -101,6 +102,7 @@ sysprof_callgraph_frame_finalize (GObject *object) SysprofCallgraphFrame *self = (SysprofCallgraphFrame *)object; g_clear_weak_pointer (&self->callgraph); + g_clear_object (&self->owner); self->node = NULL; G_OBJECT_CLASS (sysprof_callgraph_frame_parent_class)->finalize (object); @@ -157,6 +159,7 @@ sysprof_callgraph_frame_init (SysprofCallgraphFrame *self) SysprofCallgraphFrame * _sysprof_callgraph_frame_new_for_node (SysprofCallgraph *callgraph, + GObject *owner, SysprofCallgraphNode *node) { SysprofCallgraphFrame *self; @@ -166,6 +169,7 @@ _sysprof_callgraph_frame_new_for_node (SysprofCallgraph *callgraph, self = g_object_new (SYSPROF_TYPE_CALLGRAPH_FRAME, NULL); g_set_weak_pointer (&self->callgraph, callgraph); + g_set_object (&self->owner, owner); self->node = node; for (const SysprofCallgraphNode *iter = node->children; diff --git a/src/libsysprof-analyze/sysprof-callgraph.c b/src/libsysprof-analyze/sysprof-callgraph.c index 1c9e178b..a111d9fe 100644 --- a/src/libsysprof-analyze/sysprof-callgraph.c +++ b/src/libsysprof-analyze/sysprof-callgraph.c @@ -23,6 +23,7 @@ #include "sysprof-callgraph-private.h" #include "sysprof-callgraph-frame-private.h" #include "sysprof-callgraph-symbol-private.h" +#include "sysprof-descendants-model-private.h" #include "sysprof-document-bitset-index-private.h" #include "sysprof-document-private.h" #include "sysprof-document-traceable.h" @@ -53,7 +54,7 @@ sysprof_callgraph_get_item (GListModel *model, if (position > 0) return NULL; - return _sysprof_callgraph_frame_new_for_node (self, &self->root); + return _sysprof_callgraph_frame_new_for_node (self, NULL, &self->root); } static void @@ -199,7 +200,8 @@ sysprof_callgraph_populate_callers (SysprofCallgraph *self, SysprofSymbol *parent_symbol = iter->parent->summary->symbol; guint pos; - if (!g_ptr_array_find (iter->summary->callers, parent_symbol, &pos)) + if (!(parent_symbol->is_process || parent_symbol->is_everything) && + !g_ptr_array_find (iter->summary->callers, parent_symbol, &pos)) g_ptr_array_add (iter->summary->callers, parent_symbol); } } @@ -532,3 +534,52 @@ sysprof_callgraph_list_symbols (SysprofCallgraph *self) return _sysprof_callgraph_symbol_list_model_new (self, self->symbols); } + +static void +sysprof_callgraph_descendants_worker (GTask *task, + gpointer source_object, + gpointer task_data, + GCancellable *cancellable) +{ + SysprofCallgraph *self = source_object; + SysprofSymbol *symbol = task_data; + + g_assert (G_IS_TASK (task)); + g_assert (SYSPROF_IS_CALLGRAPH (self)); + g_assert (SYSPROF_IS_SYMBOL (symbol)); + g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); + + g_task_return_pointer (task, + _sysprof_descendants_model_new (self, symbol), + g_object_unref); +} + +void +sysprof_callgraph_descendants_async (SysprofCallgraph *self, + SysprofSymbol *symbol, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_autoptr(GTask) task = NULL; + + g_return_if_fail (SYSPROF_IS_CALLGRAPH (self)); + g_return_if_fail (SYSPROF_IS_SYMBOL (symbol)); + g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); + + task = g_task_new (self, cancellable, callback, user_data); + g_task_set_source_tag (task, sysprof_callgraph_descendants_async); + g_task_set_task_data (task, g_object_ref (symbol), g_object_unref); + g_task_run_in_thread (task, sysprof_callgraph_descendants_worker); +} + +GListModel * +sysprof_callgraph_descendants_finish (SysprofCallgraph *self, + GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (SYSPROF_IS_CALLGRAPH (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-analyze/sysprof-callgraph.h b/src/libsysprof-analyze/sysprof-callgraph.h index 5f3afe3c..5e5e0cb0 100644 --- a/src/libsysprof-analyze/sysprof-callgraph.h +++ b/src/libsysprof-analyze/sysprof-callgraph.h @@ -66,6 +66,16 @@ SYSPROF_AVAILABLE_IN_ALL GListModel *sysprof_callgraph_list_traceables_for_symbol (SysprofCallgraph *self, SysprofSymbol *symbol); SYSPROF_AVAILABLE_IN_ALL +void sysprof_callgraph_descendants_async (SysprofCallgraph *self, + SysprofSymbol *symbol, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +SYSPROF_AVAILABLE_IN_ALL +GListModel *sysprof_callgraph_descendants_finish (SysprofCallgraph *self, + GAsyncResult *result, + GError **error); +SYSPROF_AVAILABLE_IN_ALL gpointer sysprof_callgraph_get_augment (SysprofCallgraph *self, SysprofCallgraphNode *node); SYSPROF_AVAILABLE_IN_ALL diff --git a/src/libsysprof-analyze/sysprof-descendants-model-private.h b/src/libsysprof-analyze/sysprof-descendants-model-private.h new file mode 100644 index 00000000..9560ce6b --- /dev/null +++ b/src/libsysprof-analyze/sysprof-descendants-model-private.h @@ -0,0 +1,35 @@ +/* sysprof-descendants-model-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.h" +#include "sysprof-symbol.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_DESCENDANTS_MODEL (sysprof_descendants_model_get_type()) + +G_DECLARE_FINAL_TYPE (SysprofDescendantsModel, sysprof_descendants_model, SYSPROF, DESCENDANTS_MODEL, GObject) + +GListModel *_sysprof_descendants_model_new (SysprofCallgraph *callgraph, + SysprofSymbol *symbol); + +G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-descendants-model.c b/src/libsysprof-analyze/sysprof-descendants-model.c new file mode 100644 index 00000000..d019418d --- /dev/null +++ b/src/libsysprof-analyze/sysprof-descendants-model.c @@ -0,0 +1,238 @@ +/* sysprof-descendants-model.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-private.h" +#include "sysprof-callgraph-frame-private.h" +#include "sysprof-descendants-model-private.h" +#include "sysprof-symbol-private.h" + +#define MAX_STACK_DEPTH 128 + +struct _SysprofDescendantsModel +{ + GObject parent_instance; + SysprofCallgraph *callgraph; + SysprofSymbol *symbol; + SysprofCallgraphNode root; +}; + +static GType +sysprof_descendants_model_get_item_type (GListModel *model) +{ + return SYSPROF_TYPE_CALLGRAPH_FRAME; +} + +static guint +sysprof_descendants_model_get_n_items (GListModel *model) +{ + return 1; +} + +static gpointer +sysprof_descendants_model_get_item (GListModel *model, + guint position) +{ + SysprofDescendantsModel *self = SYSPROF_DESCENDANTS_MODEL (model); + + if (position != 0) + return NULL; + + g_warning ("TODO: Need to get proper ownership for node and proper summary"); + + return _sysprof_callgraph_frame_new_for_node (self->callgraph, G_OBJECT (self), &self->root); +} + +static void +list_model_iface_init (GListModelInterface *iface) +{ + iface->get_item_type = sysprof_descendants_model_get_item_type; + iface->get_n_items = sysprof_descendants_model_get_n_items; + iface->get_item = sysprof_descendants_model_get_item; +} + +G_DEFINE_FINAL_TYPE_WITH_CODE (SysprofDescendantsModel, sysprof_descendants_model, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, list_model_iface_init)) + +static void +sysprof_descendants_model_finalize (GObject *object) +{ + SysprofDescendantsModel *self = (SysprofDescendantsModel *)object; + + g_clear_object (&self->callgraph); + g_clear_object (&self->symbol); + + G_OBJECT_CLASS (sysprof_descendants_model_parent_class)->finalize (object); +} + +static void +sysprof_descendants_model_class_init (SysprofDescendantsModelClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = sysprof_descendants_model_finalize; +} + +static void +sysprof_descendants_model_init (SysprofDescendantsModel *self) +{ +} + +static SysprofCallgraphNode * +sysprof_descendants_model_add_trace (SysprofDescendantsModel *self, + SysprofSymbol **symbols, + guint n_symbols) +{ + SysprofCallgraphNode *parent = NULL; + + g_assert (SYSPROF_IS_DESCENDANTS_MODEL (self)); + g_assert (symbols != NULL); + g_assert (n_symbols > 0); + + parent = &self->root; + + for (guint i = n_symbols; i > 0; i--) + { + SysprofSymbol *symbol = symbols[i-1]; + SysprofCallgraphNode *node = NULL; + SysprofCallgraphSummary *summary; + + /* Try to find @symbol within the children of @parent */ + for (SysprofCallgraphNode *iter = parent->children; + iter != NULL; + iter = iter->next) + { + g_assert (iter != NULL); + g_assert (iter->summary != NULL); + g_assert (iter->summary->symbol != NULL); + g_assert (symbol != NULL); + + if (_sysprof_symbol_equal (iter->summary->symbol, symbol)) + { + node = iter; + goto next_symbol; + } + } + + if (!(summary = g_hash_table_lookup (self->callgraph->symbol_to_summary, symbol))) + { + node = parent; + goto next_symbol; + } + + /* Otherwise create a new node */ + node = g_new0 (SysprofCallgraphNode, 1); + node->summary = summary; + node->parent = parent; + node->next = parent->children; + if (parent->children) + parent->children->prev = node; + parent->children = node; + + g_assert (node->summary != NULL); + + next_symbol: + parent = node; + } + + return parent; +} + +static void +sysprof_descendants_model_add_traceable (SysprofDescendantsModel *self, + SysprofDocument *document, + SysprofDocumentTraceable *traceable, + SysprofSymbol *from_symbol) +{ + SysprofAddressContext final_context; + SysprofSymbol **symbols; + guint stack_depth; + guint n_symbols; + + g_assert (SYSPROF_IS_DESCENDANTS_MODEL (self)); + g_assert (SYSPROF_IS_DOCUMENT (document)); + g_assert (SYSPROF_IS_DOCUMENT_TRACEABLE (traceable)); + g_assert (SYSPROF_IS_SYMBOL (from_symbol)); + + stack_depth = MIN (MAX_STACK_DEPTH, sysprof_document_traceable_get_stack_depth (traceable)); + symbols = g_alloca (sizeof (SysprofSymbol *) * stack_depth); + n_symbols = sysprof_document_symbolize_traceable (document, traceable, symbols, stack_depth, &final_context); + + for (guint i = n_symbols; i > 0; i--) + { + SysprofSymbol *symbol = symbols[i-1]; + + n_symbols--; + + if (_sysprof_symbol_equal (symbol, from_symbol)) + break; + } + + if (n_symbols > 0) + { + SysprofCallgraphNode *node; + + node = sysprof_descendants_model_add_trace (self, symbols, n_symbols); + + /* TODO: This will fuck up summaries */ + + if (node && self->callgraph->augment_func) + self->callgraph->augment_func (self->callgraph, + node, + SYSPROF_DOCUMENT_FRAME (traceable), + self->callgraph->augment_func_data); + } +} + +GListModel * +_sysprof_descendants_model_new (SysprofCallgraph *callgraph, + SysprofSymbol *symbol) +{ + SysprofDescendantsModel *self; + g_autoptr(SysprofDocument) document = NULL; + g_autoptr(GListModel) model = NULL; + guint n_items; + + g_return_val_if_fail (SYSPROF_IS_CALLGRAPH (callgraph), NULL); + g_return_val_if_fail (SYSPROF_IS_SYMBOL (symbol), NULL); + + model = sysprof_callgraph_list_traceables_for_symbol (callgraph, symbol); + document = g_object_ref (callgraph->document); + + self = g_object_new (SYSPROF_TYPE_DESCENDANTS_MODEL, NULL); + self->callgraph = g_object_ref (callgraph); + self->symbol = g_object_ref (symbol); + self->root.summary = g_hash_table_lookup (callgraph->symbol_to_summary, symbol); + + g_assert (self->root.summary != NULL); + g_assert (_sysprof_symbol_equal (self->root.summary->symbol, symbol)); + + n_items = g_list_model_get_n_items (model); + + for (guint i = 0; i < n_items; i++) + { + g_autoptr(SysprofDocumentTraceable) traceable = g_list_model_get_item (model, i); + + sysprof_descendants_model_add_traceable (self, document, traceable, symbol); + } + + return G_LIST_MODEL (self); +}