mirror of
https://github.com/varun-r-mallya/sysprof.git
synced 2025-12-31 20:36:25 +00:00
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:
@ -277,132 +277,24 @@ sysprof_callgraph_frame_get_callgraph (SysprofCallgraphFrame *self)
|
|||||||
return self->callgraph;
|
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
|
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);
|
SysprofCallgraph *callgraph = (SysprofCallgraph *)object;
|
||||||
g_clear_object (&state->traceables);
|
g_autoptr(GListModel) model = NULL;
|
||||||
g_clear_pointer (&state->prefix, g_ptr_array_unref);
|
g_autoptr(GError) error = NULL;
|
||||||
g_clear_pointer (&state->bitset, egg_bitset_unref);
|
g_autoptr(GTask) task = user_data;
|
||||||
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;
|
|
||||||
|
|
||||||
|
g_assert (SYSPROF_IS_CALLGRAPH (callgraph));
|
||||||
|
g_assert (G_IS_ASYNC_RESULT (result));
|
||||||
g_assert (G_IS_TASK (task));
|
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 ();
|
if (!(model = sysprof_callgraph_list_traceables_for_node_finish (callgraph, result, &error)))
|
||||||
|
g_task_return_error (task, g_steal_pointer (&error));
|
||||||
model = state->traceables;
|
else
|
||||||
document = state->document;
|
g_task_return_pointer (task, g_steal_pointer (&model), g_object_unref);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -421,10 +313,6 @@ sysprof_callgraph_frame_list_traceables_async (SysprofCallgraphFrame *self,
|
|||||||
gpointer user_data)
|
gpointer user_data)
|
||||||
{
|
{
|
||||||
g_autoptr(GTask) task = NULL;
|
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 (SYSPROF_IS_CALLGRAPH_FRAME (self));
|
||||||
g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
|
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);
|
g_task_set_source_tag (task, sysprof_callgraph_frame_list_traceables_async);
|
||||||
|
|
||||||
if (self->callgraph == NULL)
|
if (self->callgraph == NULL)
|
||||||
{
|
g_task_return_new_error (task,
|
||||||
g_task_return_new_error (task,
|
G_IO_ERROR,
|
||||||
G_IO_ERROR,
|
G_IO_ERROR_FAILED,
|
||||||
G_IO_ERROR_FAILED,
|
"Callgraph already disposed");
|
||||||
"Callgraph already disposed");
|
else
|
||||||
return;
|
sysprof_callgraph_list_traceables_for_node_async (self->callgraph,
|
||||||
}
|
self->node,
|
||||||
|
cancellable,
|
||||||
prefix = g_ptr_array_new ();
|
sysprof_callgraph_frame_list_traceables_cb,
|
||||||
bitsets = g_ptr_array_new ();
|
g_steal_pointer (&task));
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -816,3 +816,220 @@ sysprof_callgraph_descendants_finish (SysprofCallgraph *self,
|
|||||||
|
|
||||||
return g_task_propagate_pointer (G_TASK (result), error);
|
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;
|
||||||
|
}
|
||||||
|
|||||||
@ -139,5 +139,15 @@ SYSPROF_AVAILABLE_IN_ALL
|
|||||||
SysprofCallgraphCategory sysprof_callgraph_frame_get_category (SysprofCallgraphFrame *self);
|
SysprofCallgraphCategory sysprof_callgraph_frame_get_category (SysprofCallgraphFrame *self);
|
||||||
SYSPROF_AVAILABLE_IN_ALL
|
SYSPROF_AVAILABLE_IN_ALL
|
||||||
SysprofCallgraph *sysprof_callgraph_symbol_get_callgraph (SysprofCallgraphSymbol *self);
|
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
|
G_END_DECLS
|
||||||
|
|||||||
Reference in New Issue
Block a user