mirror of
https://github.com/varun-r-mallya/sysprof.git
synced 2025-12-31 20:36:25 +00:00
Merge branch 'gbsneto/sysprof-cat' into 'master'
Introduce sysprof-cat See merge request GNOME/sysprof!104
This commit is contained in:
@ -28,6 +28,7 @@ endif
|
||||
|
||||
if get_option('tools')
|
||||
subdir('sysprof-agent')
|
||||
subdir('sysprof-cat')
|
||||
subdir('sysprof-cli')
|
||||
subdir('sysprof-diff')
|
||||
endif
|
||||
|
||||
9
src/sysprof-cat/meson.build
Normal file
9
src/sysprof-cat/meson.build
Normal file
@ -0,0 +1,9 @@
|
||||
sysprof_cat_deps = [
|
||||
libsysprof_static_dep,
|
||||
]
|
||||
|
||||
sysprof_cat = executable('sysprof-cat', 'sysprof-cat.c',
|
||||
dependencies: sysprof_cat_deps,
|
||||
c_args: release_flags,
|
||||
install: true,
|
||||
)
|
||||
482
src/sysprof-cat/sysprof-cat.c
Normal file
482
src/sysprof-cat/sysprof-cat.c
Normal file
@ -0,0 +1,482 @@
|
||||
/* sysprof-cat.c
|
||||
*
|
||||
* Copyright 2024 Igalia S.L.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Georges Basile Stavracas Neto <feaneron@igalia.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <libdex.h>
|
||||
|
||||
#include <sysprof.h>
|
||||
|
||||
#include "sysprof-callgraph-private.h"
|
||||
#include "sysprof-symbol-private.h"
|
||||
|
||||
static GMainLoop *main_loop;
|
||||
static int exit_code = -1;
|
||||
static gboolean opt_skip_callgraph = FALSE;
|
||||
static gboolean opt_skip_marks = FALSE;
|
||||
static gboolean opt_skip_counters = FALSE;
|
||||
static gboolean opt_skip_metadata = FALSE;
|
||||
static const GOptionEntry options[] = {
|
||||
{ "no-callgraph", 0, 0, G_OPTION_ARG_NONE, &opt_skip_callgraph, "Do not dump the callgraph" , NULL },
|
||||
{ "no-counters", 0, 0, G_OPTION_ARG_NONE, &opt_skip_counters, "Do not dump counters" , NULL },
|
||||
{ "no-marks", 0, 0, G_OPTION_ARG_NONE, &opt_skip_marks, "Do not dump marks" , NULL },
|
||||
{ "no-metadata", 0, 0, G_OPTION_ARG_NONE, &opt_skip_metadata, "Do not dump capture metadata" , NULL },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
static inline void
|
||||
begin_document (SysprofDocument *document)
|
||||
{
|
||||
const SysprofTimeSpan *timespan;
|
||||
char *aux;
|
||||
|
||||
g_print ("document {\n");
|
||||
if ((aux = sysprof_document_dup_title (document)))
|
||||
{
|
||||
g_autofree char *markup = g_markup_escape_text (aux, -1);
|
||||
g_print (" title: \"%s\";\n", markup);
|
||||
g_clear_pointer (&aux, g_free);
|
||||
}
|
||||
|
||||
if ((aux = sysprof_document_dup_subtitle (document)))
|
||||
{
|
||||
g_autofree char *markup = g_markup_escape_text (aux, -1);
|
||||
g_print (" subtitle: \"%s\";\n", markup);
|
||||
g_clear_pointer (&aux, g_free);
|
||||
}
|
||||
|
||||
timespan = sysprof_document_get_time_span (document);
|
||||
g_print (" timespan: %" G_GINT64_FORMAT " %" G_GINT64_FORMAT ";\n",
|
||||
timespan->begin_nsec,
|
||||
timespan->end_nsec);
|
||||
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
guint size;
|
||||
guint total;
|
||||
} Augment;
|
||||
|
||||
static void
|
||||
augment_cb (SysprofCallgraph *callgraph,
|
||||
SysprofCallgraphNode *node,
|
||||
SysprofDocumentFrame *frame,
|
||||
gboolean summarize,
|
||||
gpointer user_data)
|
||||
{
|
||||
Augment *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;
|
||||
aug->total += 1;
|
||||
|
||||
for (SysprofCallgraphNode *iter = node->parent; iter; iter = iter->parent)
|
||||
{
|
||||
aug = sysprof_callgraph_get_augment (callgraph, iter);
|
||||
aug->total += 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
load_callgraph_cb (GObject *object,
|
||||
GAsyncResult *result,
|
||||
gpointer user_data)
|
||||
{
|
||||
g_autoptr(DexPromise) promise = user_data;
|
||||
SysprofCallgraph *callgraph;
|
||||
GError *error = NULL;
|
||||
|
||||
if ((callgraph = sysprof_document_callgraph_finish (SYSPROF_DOCUMENT (object), result, &error)))
|
||||
dex_promise_resolve_object (promise, callgraph);
|
||||
else
|
||||
dex_promise_reject (promise, error);
|
||||
}
|
||||
|
||||
static void
|
||||
print_node_recursively (SysprofCallgraphNode *node,
|
||||
int depth)
|
||||
{
|
||||
const Augment *aug;
|
||||
|
||||
if (!node)
|
||||
return;
|
||||
|
||||
aug = (const Augment *) &node->augment[0];
|
||||
|
||||
g_print ("%*cnode {\n", depth * 2, ' ');
|
||||
g_print ("%*csymbol: \"%s\",\n", (depth + 1) * 2, ' ', node->summary->symbol->name);
|
||||
g_print ("%*cself: %u,\n", (depth + 1) * 2, ' ', aug->size);
|
||||
g_print ("%*ctotal: %u,\n", (depth + 1) * 2, ' ', aug->total);
|
||||
|
||||
for (SysprofCallgraphNode *child = node->children; child != NULL; child = child->next)
|
||||
{
|
||||
if (!child->prev)
|
||||
g_print ("\n");
|
||||
|
||||
print_node_recursively (child, depth + 1);
|
||||
}
|
||||
|
||||
g_print ("%*c}\n", depth * 2, ' ');
|
||||
}
|
||||
|
||||
static void
|
||||
dump_callgraph (SysprofDocument *document)
|
||||
{
|
||||
g_autoptr(SysprofCallgraph) callgraph = NULL;
|
||||
g_autoptr(GListModel) traceables = NULL;
|
||||
g_autoptr(GError) error = NULL;
|
||||
SysprofCallgraphFlags flags = 0;
|
||||
DexPromise *promise;
|
||||
|
||||
if (opt_skip_callgraph)
|
||||
return;
|
||||
|
||||
traceables = sysprof_document_list_samples (document);
|
||||
|
||||
promise = dex_promise_new ();
|
||||
|
||||
sysprof_document_callgraph_async (document,
|
||||
flags,
|
||||
traceables,
|
||||
sizeof (Augment),
|
||||
augment_cb,
|
||||
NULL, NULL,
|
||||
dex_promise_get_cancellable (promise),
|
||||
load_callgraph_cb,
|
||||
dex_ref (promise));
|
||||
|
||||
callgraph = dex_await_object (DEX_FUTURE (promise), &error);
|
||||
|
||||
if (error)
|
||||
return;
|
||||
|
||||
g_print ("\n");
|
||||
g_print (" callgraph {\n");
|
||||
|
||||
print_node_recursively (&callgraph->root, 2);
|
||||
|
||||
g_print (" }\n");
|
||||
}
|
||||
|
||||
static void
|
||||
dump_counters (SysprofDocument *document)
|
||||
{
|
||||
g_autoptr(GListModel) counters = NULL;
|
||||
unsigned int n_counters;
|
||||
|
||||
if (opt_skip_counters)
|
||||
return;
|
||||
|
||||
counters = sysprof_document_list_counters (document);
|
||||
g_assert (G_IS_LIST_MODEL (counters));
|
||||
|
||||
n_counters = g_list_model_get_n_items (counters);
|
||||
|
||||
if (n_counters == 0)
|
||||
return;
|
||||
|
||||
g_print ("\n");
|
||||
g_print (" counters {\n");
|
||||
|
||||
for (unsigned int i = 0; i < n_counters; i++)
|
||||
{
|
||||
g_autoptr(SysprofDocumentCounter) counter = NULL;
|
||||
unsigned int n_values;
|
||||
const char *aux;
|
||||
|
||||
counter = g_list_model_get_item (counters, i);
|
||||
g_assert (SYSPROF_IS_DOCUMENT_COUNTER (counter));
|
||||
|
||||
g_print (" counter {\n");
|
||||
|
||||
if ((aux = sysprof_document_counter_get_category (counter)))
|
||||
{
|
||||
g_autofree char *markup = g_markup_escape_text (aux, -1);
|
||||
g_print (" category: \"%s\";\n", markup);
|
||||
}
|
||||
|
||||
if ((aux = sysprof_document_counter_get_name (counter)))
|
||||
{
|
||||
g_autofree char *markup = g_markup_escape_text (aux, -1);
|
||||
g_print (" name: \"%s\";\n", markup);
|
||||
}
|
||||
|
||||
if ((aux = sysprof_document_counter_get_description (counter)))
|
||||
{
|
||||
g_autofree char *markup = g_markup_escape_text (aux, -1);
|
||||
g_print (" description: \"%s\";\n", markup);
|
||||
}
|
||||
|
||||
n_values = sysprof_document_counter_get_n_values (counter);
|
||||
if (n_values)
|
||||
{
|
||||
g_print ("\n");
|
||||
g_print (" values {\n");
|
||||
for (unsigned int j = 0; j < n_values; j++)
|
||||
{
|
||||
g_autoptr(SysprofDocumentCounterValue) value = NULL;
|
||||
g_autofree char *formatted_value = NULL;
|
||||
|
||||
value = g_list_model_get_item (G_LIST_MODEL (counter), j);
|
||||
g_assert (SYSPROF_IS_DOCUMENT_COUNTER_VALUE (value));
|
||||
|
||||
formatted_value = sysprof_document_counter_value_format (value);
|
||||
|
||||
g_print (" value {\n");
|
||||
g_print (" time: %" G_GINT64_FORMAT ";\n", sysprof_document_counter_value_get_time (value));
|
||||
g_print (" offset: %" G_GINT64_FORMAT ";\n", sysprof_document_counter_value_get_time (value));
|
||||
g_print (" value: %s;\n", formatted_value);
|
||||
g_print (" }\n");
|
||||
}
|
||||
g_print (" }\n");
|
||||
}
|
||||
|
||||
g_print (" }\n");
|
||||
}
|
||||
|
||||
g_print (" }\n");
|
||||
}
|
||||
|
||||
static void
|
||||
dump_marks (SysprofDocument *document)
|
||||
{
|
||||
g_autoptr(GListModel) groups = NULL;
|
||||
unsigned int n_groups;
|
||||
|
||||
if (opt_skip_marks)
|
||||
return;
|
||||
|
||||
groups = sysprof_document_catalog_marks (document);
|
||||
g_assert (G_IS_LIST_MODEL (groups));
|
||||
|
||||
n_groups = g_list_model_get_n_items (groups);
|
||||
|
||||
if (n_groups == 0)
|
||||
return;
|
||||
|
||||
g_print ("\n");
|
||||
g_print (" marks {\n");
|
||||
|
||||
for (unsigned int i = 0; i < n_groups; i++)
|
||||
{
|
||||
g_autoptr(GListModel) catalogs = NULL;
|
||||
unsigned int n_catalogs;
|
||||
|
||||
catalogs = g_list_model_get_item (groups, i);
|
||||
g_assert (G_IS_LIST_MODEL (catalogs));
|
||||
|
||||
g_print ("\n");
|
||||
g_print (" group {\n");
|
||||
|
||||
n_catalogs = g_list_model_get_n_items (catalogs);
|
||||
for (unsigned int j = 0; j < n_catalogs; j++)
|
||||
{
|
||||
g_autoptr(SysprofMarkCatalog) catalog = NULL;
|
||||
unsigned int n_marks;
|
||||
const char *aux;
|
||||
|
||||
catalog = g_list_model_get_item (catalogs, j);
|
||||
g_assert (SYSPROF_IS_MARK_CATALOG (catalog));
|
||||
|
||||
if (j == 0 &&
|
||||
(aux = sysprof_mark_catalog_get_group (catalog)))
|
||||
{
|
||||
g_autofree char *markup = g_markup_escape_text (aux, -1);
|
||||
g_print (" name: \"%s\";\n", markup);
|
||||
g_print ("\n");
|
||||
}
|
||||
|
||||
n_marks = g_list_model_get_n_items (G_LIST_MODEL (catalog));
|
||||
for (unsigned int k = 0; k < n_marks; k++)
|
||||
{
|
||||
g_autoptr(SysprofDocumentMark) mark = NULL;
|
||||
|
||||
mark = g_list_model_get_item (G_LIST_MODEL (catalog), k);
|
||||
g_assert (SYSPROF_IS_DOCUMENT_MARK (mark));
|
||||
|
||||
g_print (" mark {\n");
|
||||
|
||||
if ((aux = sysprof_document_mark_get_name (mark)))
|
||||
{
|
||||
g_autofree char *markup = g_markup_escape_text (aux, -1);
|
||||
g_print (" name: \"%s\";\n", markup);
|
||||
}
|
||||
|
||||
if ((aux = sysprof_document_mark_get_message (mark)))
|
||||
{
|
||||
g_autofree char *markup = g_markup_escape_text (aux, -1);
|
||||
g_print (" message: \"%s\";\n", markup);
|
||||
}
|
||||
|
||||
g_print (" duration: %" G_GINT64_FORMAT ";\n", sysprof_document_mark_get_duration (mark));
|
||||
g_print (" end-time: %" G_GINT64_FORMAT ";\n", sysprof_document_mark_get_end_time (mark));
|
||||
g_print (" }\n");
|
||||
}
|
||||
}
|
||||
|
||||
g_print (" }\n");
|
||||
}
|
||||
|
||||
g_print (" }\n");
|
||||
}
|
||||
|
||||
static void
|
||||
dump_metadata (SysprofDocument *document)
|
||||
{
|
||||
g_autoptr(GListModel) metadatas = NULL;
|
||||
unsigned int n_metadatas;
|
||||
|
||||
if (opt_skip_metadata)
|
||||
return;
|
||||
|
||||
metadatas = sysprof_document_list_metadata (document);
|
||||
g_assert (G_IS_LIST_MODEL (metadatas));
|
||||
|
||||
n_metadatas = g_list_model_get_n_items (metadatas);
|
||||
|
||||
if (metadatas == 0)
|
||||
return;
|
||||
|
||||
g_print ("\n");
|
||||
g_print (" metadata {\n");
|
||||
|
||||
for (unsigned int i = 0; i < n_metadatas; i++)
|
||||
{
|
||||
g_autoptr(SysprofDocumentMetadata) metadata = NULL;
|
||||
g_autofree char *value = NULL;
|
||||
g_autofree char *id = NULL;
|
||||
|
||||
metadata = g_list_model_get_item (metadatas, i);
|
||||
g_assert (SYSPROF_IS_DOCUMENT_METADATA (metadata));
|
||||
|
||||
id = g_markup_escape_text (sysprof_document_metadata_get_id (metadata), -1);
|
||||
value = g_markup_escape_text (sysprof_document_metadata_get_value (metadata), -1);
|
||||
|
||||
g_print (" \"%s\": \"%s\";\n", id, value);
|
||||
}
|
||||
|
||||
g_print (" }\n");
|
||||
}
|
||||
|
||||
static inline void
|
||||
end_document (SysprofDocument *document)
|
||||
{
|
||||
g_print ("}\n");
|
||||
}
|
||||
|
||||
static DexFuture *
|
||||
sysprof_cat_fiber (gpointer data)
|
||||
{
|
||||
g_autoptr(SysprofDocumentLoader) loader = NULL;
|
||||
g_autoptr(SysprofDocument) document = NULL;
|
||||
g_autoptr(GError) error = NULL;
|
||||
const char *filename = data;
|
||||
|
||||
g_assert (filename != NULL);
|
||||
|
||||
loader = sysprof_document_loader_new (filename);
|
||||
|
||||
if (!(document = sysprof_document_loader_load (loader, NULL, &error)))
|
||||
return dex_future_new_for_error (g_steal_pointer (&error));
|
||||
|
||||
begin_document (document);
|
||||
{
|
||||
dump_metadata (document);
|
||||
dump_callgraph (document);
|
||||
dump_marks (document);
|
||||
dump_counters (document);
|
||||
}
|
||||
end_document (document);
|
||||
|
||||
exit_code = EXIT_SUCCESS;
|
||||
|
||||
g_main_loop_quit (main_loop);
|
||||
|
||||
return dex_future_new_for_boolean (TRUE);
|
||||
}
|
||||
|
||||
static DexFuture *
|
||||
sysprof_cat_catch_error_cb (DexFuture *future,
|
||||
gpointer user_data)
|
||||
{
|
||||
g_autoptr(GError) error = NULL;
|
||||
|
||||
dex_await (dex_ref (future), &error);
|
||||
|
||||
g_printerr ("Error: %s\n", error->message);
|
||||
|
||||
exit_code = EXIT_FAILURE;
|
||||
|
||||
g_main_loop_quit (main_loop);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc,
|
||||
char *argv[])
|
||||
{
|
||||
g_autoptr(GOptionContext) context = NULL;
|
||||
g_autoptr(GError) error = NULL;
|
||||
|
||||
dex_init ();
|
||||
|
||||
g_set_prgname ("sysprof-cat");
|
||||
g_set_application_name ("sysprof-cat");
|
||||
|
||||
main_loop = g_main_loop_new (NULL, FALSE);
|
||||
|
||||
context = g_option_context_new ("CAPTURE -- Dump the contents of a capture");
|
||||
g_option_context_add_main_entries (context, options, NULL);
|
||||
|
||||
if (!g_option_context_parse (context, &argc, &argv, &error))
|
||||
{
|
||||
g_printerr ("%s\n", error->message);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (argc < 2)
|
||||
{
|
||||
g_printerr ("usage: %s CAPTURE\n", argv[0]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
dex_future_disown (
|
||||
dex_future_catch (
|
||||
dex_scheduler_spawn (NULL, 0,
|
||||
sysprof_cat_fiber,
|
||||
g_strdup (argv[1]),
|
||||
g_free),
|
||||
sysprof_cat_catch_error_cb,
|
||||
NULL, NULL));
|
||||
|
||||
if (exit_code == -1)
|
||||
g_main_loop_run (main_loop);
|
||||
|
||||
return exit_code;
|
||||
}
|
||||
Reference in New Issue
Block a user