libsysprof: allow listing traceables from a node

This will allow use from the flamegraph which does not use
SysprofCallgraphFrame objects.

Related #95
This commit is contained in:
Christian Hergert
2023-08-29 11:25:06 -07:00
parent e5413f7fd8
commit b46fe4dd75
3 changed files with 250 additions and 185 deletions

View File

@ -277,132 +277,24 @@ sysprof_callgraph_frame_get_callgraph (SysprofCallgraphFrame *self)
return self->callgraph;
}
static gboolean
traceable_has_prefix (SysprofDocument *document,
SysprofDocumentTraceable *traceable,
GPtrArray *prefix)
{
SysprofAddressContext final_context;
SysprofSymbol **symbols;
SysprofAddress *addresses;
guint s = 0;
guint stack_depth;
guint n_symbols;
stack_depth = sysprof_document_traceable_get_stack_depth (traceable);
if (stack_depth > MAX_STACK_DEPTH)
return FALSE;
addresses = g_alloca (sizeof (SysprofAddress) * stack_depth);
sysprof_document_traceable_get_stack_addresses (traceable, addresses, stack_depth);
symbols = g_alloca (sizeof (SysprofSymbol *) * stack_depth);
n_symbols = sysprof_document_symbolize_traceable (document, traceable, symbols, stack_depth, &final_context);
if (n_symbols < prefix->len)
return FALSE;
for (guint p = 0; p < prefix->len; p++)
{
SysprofSymbol *prefix_symbol = g_ptr_array_index (prefix, p);
gboolean found = FALSE;
for (; !found && s < n_symbols; s++)
found = sysprof_symbol_equal (prefix_symbol, symbols[s]);
if (!found)
return FALSE;
}
return TRUE;
}
typedef struct _FilterByPrefix
{
SysprofDocument *document;
GListModel *traceables;
GPtrArray *prefix;
EggBitset *bitset;
guint max_results;
} FilterByPrefix;
static void
filter_by_prefix_free (FilterByPrefix *state)
sysprof_callgraph_frame_list_traceables_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
g_clear_object (&state->document);
g_clear_object (&state->traceables);
g_clear_pointer (&state->prefix, g_ptr_array_unref);
g_clear_pointer (&state->bitset, egg_bitset_unref);
g_free (state);
}
static void
filter_by_prefix_worker (GTask *task,
gpointer source_object,
gpointer task_data,
GCancellable *cancellable)
{
FilterByPrefix *state = task_data;
g_autoptr(EggBitset) bitset = NULL;
SysprofDocument *document;
GListModel *model;
GPtrArray *prefix;
EggBitsetIter iter;
guint n_results = 0;
guint i;
SysprofCallgraph *callgraph = (SysprofCallgraph *)object;
g_autoptr(GListModel) model = NULL;
g_autoptr(GError) error = NULL;
g_autoptr(GTask) task = user_data;
g_assert (SYSPROF_IS_CALLGRAPH (callgraph));
g_assert (G_IS_ASYNC_RESULT (result));
g_assert (G_IS_TASK (task));
g_assert (SYSPROF_IS_CALLGRAPH_FRAME (source_object));
g_assert (state != NULL);
g_assert (G_IS_LIST_MODEL (state->traceables));
g_assert (state->prefix != NULL);
g_assert (state->prefix->len > 0);
g_assert (state->bitset != NULL);
g_assert (!egg_bitset_is_empty (state->bitset));
bitset = egg_bitset_new_empty ();
model = state->traceables;
document = state->document;
prefix = state->prefix;
if (egg_bitset_iter_init_first (&iter, state->bitset, &i))
{
do
{
g_autoptr(SysprofDocumentTraceable) traceable = g_list_model_get_item (model, i);
if (traceable_has_prefix (document, traceable, prefix))
{
egg_bitset_add (bitset, i);
n_results++;
}
}
while (n_results < state->max_results &&
egg_bitset_iter_next (&iter, &i));
}
g_task_return_pointer (task,
_sysprof_document_bitset_index_new (model, bitset),
g_object_unref);
}
static int
sort_by_size_asc (gconstpointer a,
gconstpointer b)
{
const EggBitset *bitset_a = *(const EggBitset * const *)a;
const EggBitset *bitset_b = *(const EggBitset * const *)a;
gsize size_a = egg_bitset_get_size (bitset_a);
gsize size_b = egg_bitset_get_size (bitset_b);
if (size_a < size_b)
return -1;
if (size_a > size_b)
return 1;
return 0;
if (!(model = sysprof_callgraph_list_traceables_for_node_finish (callgraph, result, &error)))
g_task_return_error (task, g_steal_pointer (&error));
else
g_task_return_pointer (task, g_steal_pointer (&model), g_object_unref);
}
/**
@ -421,10 +313,6 @@ sysprof_callgraph_frame_list_traceables_async (SysprofCallgraphFrame *self,
gpointer user_data)
{
g_autoptr(GTask) task = NULL;
g_autoptr(GPtrArray) prefix = NULL;
g_autoptr(GPtrArray) bitsets = NULL;
g_autoptr(EggBitset) bitset = NULL;
FilterByPrefix *state;
g_return_if_fail (SYSPROF_IS_CALLGRAPH_FRAME (self));
g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
@ -433,66 +321,16 @@ sysprof_callgraph_frame_list_traceables_async (SysprofCallgraphFrame *self,
g_task_set_source_tag (task, sysprof_callgraph_frame_list_traceables_async);
if (self->callgraph == NULL)
{
g_task_return_new_error (task,
G_IO_ERROR,
G_IO_ERROR_FAILED,
"Callgraph already disposed");
return;
}
prefix = g_ptr_array_new ();
bitsets = g_ptr_array_new ();
for (SysprofCallgraphNode *node = self->node;
node != NULL;
node = node->parent)
{
SysprofCallgraphSummary *summary = node->summary;
SysprofSymbol *symbol = summary->symbol;
if (symbol->kind != SYSPROF_SYMBOL_KIND_USER &&
symbol->kind != SYSPROF_SYMBOL_KIND_KERNEL)
continue;
g_ptr_array_add (bitsets, summary->traceables);
g_ptr_array_add (prefix, symbol);
}
if (prefix->len == 0)
{
g_task_return_pointer (task,
g_list_store_new (SYSPROF_TYPE_DOCUMENT_TRACEABLE),
g_object_unref);
return;
}
/* Sort the bitsets by size to shrink potential interscetions */
g_ptr_array_sort (bitsets, sort_by_size_asc);
bitset = egg_bitset_copy (g_ptr_array_index (bitsets, 0));
for (guint i = 1; i < bitsets->len; i++)
{
const EggBitset *other = g_ptr_array_index (bitsets, i);
egg_bitset_intersect (bitset, other);
}
if (egg_bitset_is_empty (bitset))
{
g_task_return_pointer (task,
g_list_store_new (SYSPROF_TYPE_DOCUMENT_TRACEABLE),
g_object_unref);
return;
}
state = g_new0 (FilterByPrefix, 1);
state->document = g_object_ref (self->callgraph->document);
state->traceables = g_object_ref (self->callgraph->traceables);
state->prefix = g_steal_pointer (&prefix);
state->bitset = g_steal_pointer (&bitset);
state->max_results = 1000;
g_task_set_task_data (task, state, (GDestroyNotify)filter_by_prefix_free);
g_task_run_in_thread (task, filter_by_prefix_worker);
g_task_return_new_error (task,
G_IO_ERROR,
G_IO_ERROR_FAILED,
"Callgraph already disposed");
else
sysprof_callgraph_list_traceables_for_node_async (self->callgraph,
self->node,
cancellable,
sysprof_callgraph_frame_list_traceables_cb,
g_steal_pointer (&task));
}
/**

View File

@ -816,3 +816,220 @@ sysprof_callgraph_descendants_finish (SysprofCallgraph *self,
return g_task_propagate_pointer (G_TASK (result), error);
}
typedef struct _FilterByPrefix
{
SysprofDocument *document;
GListModel *traceables;
GPtrArray *prefix;
EggBitset *bitset;
guint max_results;
} FilterByPrefix;
static void
filter_by_prefix_free (FilterByPrefix *state)
{
g_clear_object (&state->document);
g_clear_object (&state->traceables);
g_clear_pointer (&state->prefix, g_ptr_array_unref);
g_clear_pointer (&state->bitset, egg_bitset_unref);
g_free (state);
}
static gboolean
traceable_has_prefix (SysprofDocument *document,
SysprofDocumentTraceable *traceable,
GPtrArray *prefix)
{
SysprofAddressContext final_context;
SysprofSymbol **symbols;
SysprofAddress *addresses;
guint s = 0;
guint stack_depth;
guint n_symbols;
stack_depth = sysprof_document_traceable_get_stack_depth (traceable);
if (stack_depth > MAX_STACK_DEPTH)
return FALSE;
addresses = g_alloca (sizeof (SysprofAddress) * stack_depth);
sysprof_document_traceable_get_stack_addresses (traceable, addresses, stack_depth);
symbols = g_alloca (sizeof (SysprofSymbol *) * stack_depth);
n_symbols = sysprof_document_symbolize_traceable (document, traceable, symbols, stack_depth, &final_context);
if (n_symbols < prefix->len)
return FALSE;
for (guint p = 0; p < prefix->len; p++)
{
SysprofSymbol *prefix_symbol = g_ptr_array_index (prefix, p);
gboolean found = FALSE;
for (; !found && s < n_symbols; s++)
found = _sysprof_symbol_equal (prefix_symbol, symbols[s]);
if (!found)
return FALSE;
}
return TRUE;
}
static void
filter_by_prefix_worker (GTask *task,
gpointer source_object,
gpointer task_data,
GCancellable *cancellable)
{
FilterByPrefix *state = task_data;
g_autoptr(EggBitset) bitset = NULL;
SysprofDocument *document;
GListModel *model;
GPtrArray *prefix;
EggBitsetIter iter;
guint n_results = 0;
guint i;
g_assert (G_IS_TASK (task));
g_assert (SYSPROF_IS_CALLGRAPH (source_object));
g_assert (state != NULL);
g_assert (G_IS_LIST_MODEL (state->traceables));
g_assert (state->prefix != NULL);
g_assert (state->prefix->len > 0);
g_assert (state->bitset != NULL);
g_assert (!egg_bitset_is_empty (state->bitset));
bitset = egg_bitset_new_empty ();
model = state->traceables;
document = state->document;
prefix = state->prefix;
if (egg_bitset_iter_init_first (&iter, state->bitset, &i))
{
do
{
g_autoptr(SysprofDocumentTraceable) traceable = g_list_model_get_item (model, i);
if (traceable_has_prefix (document, traceable, prefix))
{
egg_bitset_add (bitset, i);
n_results++;
}
}
while (n_results < state->max_results &&
egg_bitset_iter_next (&iter, &i));
}
g_task_return_pointer (task,
_sysprof_document_bitset_index_new (model, bitset),
g_object_unref);
}
static int
sort_by_size_asc (gconstpointer a,
gconstpointer b)
{
const EggBitset *bitset_a = *(const EggBitset * const *)a;
const EggBitset *bitset_b = *(const EggBitset * const *)a;
gsize size_a = egg_bitset_get_size (bitset_a);
gsize size_b = egg_bitset_get_size (bitset_b);
if (size_a < size_b)
return -1;
if (size_a > size_b)
return 1;
return 0;
}
void
sysprof_callgraph_list_traceables_for_node_async (SysprofCallgraph *self,
SysprofCallgraphNode *node,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_autoptr(GTask) task = NULL;
g_autoptr(GPtrArray) prefix = NULL;
g_autoptr(GPtrArray) bitsets = NULL;
g_autoptr(EggBitset) bitset = NULL;
FilterByPrefix *state;
g_return_if_fail (SYSPROF_IS_CALLGRAPH (self));
g_return_if_fail (node != NULL);
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_list_traceables_for_node_async);
prefix = g_ptr_array_new ();
bitsets = g_ptr_array_new ();
for (; node; node = node->parent)
{
SysprofCallgraphSummary *summary = node->summary;
SysprofSymbol *symbol = summary->symbol;
if (symbol->kind != SYSPROF_SYMBOL_KIND_USER &&
symbol->kind != SYSPROF_SYMBOL_KIND_KERNEL)
continue;
g_ptr_array_add (bitsets, summary->traceables);
g_ptr_array_add (prefix, symbol);
}
if (prefix->len == 0)
{
g_task_return_pointer (task,
g_list_store_new (SYSPROF_TYPE_DOCUMENT_TRACEABLE),
g_object_unref);
return;
}
/* Sort the bitsets by size to shrink potential interscetions */
g_ptr_array_sort (bitsets, sort_by_size_asc);
bitset = egg_bitset_copy (g_ptr_array_index (bitsets, 0));
for (guint i = 1; i < bitsets->len; i++)
{
const EggBitset *other = g_ptr_array_index (bitsets, i);
egg_bitset_intersect (bitset, other);
}
if (egg_bitset_is_empty (bitset))
{
g_task_return_pointer (task,
g_list_store_new (SYSPROF_TYPE_DOCUMENT_TRACEABLE),
g_object_unref);
return;
}
state = g_new0 (FilterByPrefix, 1);
state->document = g_object_ref (self->document);
state->traceables = g_object_ref (self->traceables);
state->prefix = g_steal_pointer (&prefix);
state->bitset = g_steal_pointer (&bitset);
state->max_results = 1000;
g_task_set_task_data (task, state, (GDestroyNotify)filter_by_prefix_free);
g_task_run_in_thread (task, filter_by_prefix_worker);
}
GListModel *
sysprof_callgraph_list_traceables_for_node_finish (SysprofCallgraph *self,
GAsyncResult *result,
GError **error)
{
GListModel *ret;
g_return_val_if_fail (SYSPROF_IS_CALLGRAPH (self), NULL);
g_return_val_if_fail (G_IS_TASK (result), NULL);
ret = g_task_propagate_pointer (G_TASK (result), error);
g_return_val_if_fail (!ret || G_IS_LIST_MODEL (ret), NULL);
return ret;
}

View File

@ -139,5 +139,15 @@ SYSPROF_AVAILABLE_IN_ALL
SysprofCallgraphCategory sysprof_callgraph_frame_get_category (SysprofCallgraphFrame *self);
SYSPROF_AVAILABLE_IN_ALL
SysprofCallgraph *sysprof_callgraph_symbol_get_callgraph (SysprofCallgraphSymbol *self);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_callgraph_list_traceables_for_node_async (SysprofCallgraph *self,
SysprofCallgraphNode *node,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
SYSPROF_AVAILABLE_IN_ALL
GListModel *sysprof_callgraph_list_traceables_for_node_finish (SysprofCallgraph *self,
GAsyncResult *result,
GError **error);
G_END_DECLS