summaryrefslogtreecommitdiff
path: root/tools/eos-profile-tool/eos-profile-cmd-convert.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/eos-profile-tool/eos-profile-cmd-convert.c')
-rw-r--r--tools/eos-profile-tool/eos-profile-cmd-convert.c320
1 files changed, 320 insertions, 0 deletions
diff --git a/tools/eos-profile-tool/eos-profile-cmd-convert.c b/tools/eos-profile-tool/eos-profile-cmd-convert.c
new file mode 100644
index 0000000..ef374b1
--- /dev/null
+++ b/tools/eos-profile-tool/eos-profile-cmd-convert.c
@@ -0,0 +1,320 @@
+#include "config.h"
+
+#include "eos-profile-cmds.h"
+#include "eos-profile-utils.h"
+
+#include "endless/eosprofile-private.h"
+#include "endless/gvdb/gvdb-reader.h"
+
+#include <math.h>
+#include <json-glib/json-glib.h>
+
+static char *opt_format;
+static char *opt_output;
+static char *opt_input;
+static gboolean opt_pretty;
+
+static GOptionEntry opts[] = {
+ {
+ .long_name = "format",
+ .short_name = 'f',
+ .flags = G_OPTION_FLAG_NONE,
+ .arg = G_OPTION_ARG_STRING,
+ .arg_data = &opt_format,
+ .description = "The output format (valid values: json)",
+ .arg_description = "FORMAT",
+ },
+ {
+ .long_name = "pretty",
+ .short_name = 0,
+ .flags = G_OPTION_FLAG_NONE,
+ .arg = G_OPTION_ARG_NONE,
+ .arg_data = &opt_pretty,
+ .description = "Enable pretty-printing the output",
+ .arg_description = NULL,
+ },
+ {
+ .long_name = "output",
+ .short_name = 0,
+ .flags = G_OPTION_FLAG_NONE,
+ .arg = G_OPTION_ARG_FILENAME,
+ .arg_data = &opt_output,
+ .description = "The output file",
+ .arg_description = "FILE",
+ },
+
+ { NULL, },
+};
+
+gboolean
+eos_profile_cmd_convert_parse_args (int argc,
+ char **argv)
+{
+ g_autoptr(GError) error = NULL;
+
+ g_autoptr(GOptionContext) context = g_option_context_new (NULL);
+
+ g_option_context_set_help_enabled (context, TRUE);
+ g_option_context_add_main_entries (context, opts, GETTEXT_PACKAGE);
+
+ if (!g_option_context_parse (context, &argc, &argv, &error))
+ {
+ eos_profile_util_print_error ("Invalid argument: %s", error->message);
+ return FALSE;
+ }
+
+ if (argc < 2)
+ return FALSE;
+
+ opt_input = argv[1];
+
+ return TRUE;
+}
+
+static JsonNode *
+collect_probe_samples (GVariant *array,
+ JsonObject *probe_obj)
+{
+ g_autoptr(GArray) samples = g_array_new (FALSE, FALSE, sizeof (ProfileSample));
+
+ GVariantIter iter;
+ g_variant_iter_init (&iter, array);
+
+ gint64 start, end;
+ while (g_variant_iter_next (&iter, "(xx)", &start, &end))
+ {
+ g_array_append_vals (samples,
+ &(ProfileSample) {
+ .start_time = start,
+ .end_time = end,
+ },
+ 1);
+ }
+
+ gint64 min_sample = G_MAXINT64, max_sample = 0;
+ gint64 total = 0;
+
+ g_autoptr(GArray) valid_samples = g_array_new (FALSE, FALSE, sizeof (guint));
+
+ for (int i = 0; i < samples->len; i++)
+ {
+ const ProfileSample *sample = &g_array_index (samples, ProfileSample, i);
+
+ gint64 delta = sample->end_time - sample->start_time;
+
+ /* If the probe never got stopped we need to skip this sample */
+ if (delta < 0)
+ continue;
+
+ g_array_append_val (valid_samples, i);
+
+ if (delta < min_sample)
+ min_sample = delta;
+ if (delta > max_sample)
+ max_sample = delta;
+
+ total += delta;
+ }
+
+ g_autofree char *msg = NULL;
+
+ if (valid_samples->len > 1)
+ {
+ JsonObject *obj = json_object_new ();
+
+ double avg = total / (double) valid_samples->len;
+ double s = 0;
+ double s_part = 0;
+
+ JsonArray *raw_array = json_array_sized_new (valid_samples->len);
+
+ for (int i = 1; i < valid_samples->len - 1; i++)
+ {
+ guint idx = g_array_index (valid_samples, guint, i);
+ const ProfileSample *sample = &g_array_index (samples, ProfileSample, idx);
+
+ gint64 delta = sample->end_time - sample->start_time;
+ g_assert (delta >= 0);
+
+ double deviation = delta - avg;
+ s_part += (deviation * deviation);
+
+ json_array_add_int_element (raw_array, delta);
+ }
+
+ json_object_set_int_member (probe_obj, "numSamples", valid_samples->len);
+ json_object_set_array_member (probe_obj, "rawSamples", raw_array);
+
+ if (valid_samples->len > 1)
+ s = sqrt (s_part / (double) valid_samples->len - 1);
+ else
+ s = 0.0;
+
+ if (s != 0.0 && !isnan (s))
+ json_object_set_double_member (probe_obj, "sigma", s);
+
+ json_object_set_int_member (probe_obj, "totalTime", total);
+ json_object_set_double_member (probe_obj, "minSample", min_sample);
+ json_object_set_double_member (probe_obj, "maxSample", max_sample);
+ json_object_set_double_member (probe_obj, "average", avg);
+ }
+ else
+ {
+ json_object_set_int_member (probe_obj, "totalTime", total);
+ json_object_set_int_member (probe_obj, "numSamples", 0);
+ json_object_set_array_member (probe_obj, "rawSamples", NULL);
+ }
+}
+
+int
+eos_profile_cmd_convert_main (void)
+{
+ g_assert (opt_input != NULL);
+
+ if (opt_format == NULL)
+ opt_format = "json";
+
+ if (g_strcmp0 (opt_format, "json") != 0)
+ {
+ eos_profile_util_print_error ("Unknown format '%s'; please, use 'json'", opt_format);
+ return 1;
+ }
+
+ g_autoptr(GError) error = NULL;
+
+ GvdbTable *db = gvdb_table_new (opt_input, TRUE, &error);
+ if (error != NULL)
+ {
+ eos_profile_util_print_error ("Unable to load '%s': %s\n",
+ opt_input,
+ error->message);
+ return 1;
+ }
+
+ GVariant *v = gvdb_table_get_raw_value (db, PROBE_DB_META_VERSION_KEY);
+ gint32 version = v != NULL ? g_variant_get_int32 (v) : -1;
+ g_clear_pointer (&v, g_variant_unref);
+
+ if (version != PROBE_DB_VERSION)
+ {
+ eos_profile_util_print_error ("Unable to load '%s': invalid version\n", opt_input);
+ gvdb_table_free (db);
+ return 1;
+ }
+
+ v = gvdb_table_get_raw_value (db, PROBE_DB_META_APPID_KEY);
+ g_autofree char *appid = v != NULL ? g_variant_dup_string (v, NULL) : NULL;
+ g_clear_pointer (&v, g_variant_unref);
+
+ v = gvdb_table_get_raw_value (db, PROBE_DB_META_PROFILE_KEY);
+ gint64 profile_time = v != NULL ? g_variant_get_int64 (v) : -1;
+ g_clear_pointer (&v, g_variant_unref);
+
+ v = gvdb_table_get_raw_value (db, PROBE_DB_META_START_KEY);
+ g_autofree char *start_time = NULL;
+ if (v != NULL)
+ {
+ g_autoptr(GDateTime) dt =
+ g_date_time_new_from_unix_local (g_variant_get_int64 (v));
+
+ start_time = g_date_time_format (dt, "%Y-%m-%d %T");
+ }
+ g_clear_pointer (&v, g_variant_unref);
+
+ JsonNode *root = json_node_new (JSON_NODE_OBJECT);
+ JsonObject *obj = json_object_new ();
+ json_node_take_object (root, obj);
+
+ JsonObject *meta = json_object_new ();
+ json_object_set_int_member (meta, "version", version);
+ json_object_set_string_member (meta, "appId", appid);
+ json_object_set_int_member (meta, "profileTime", profile_time);
+ json_object_set_string_member (meta, "startTime", start_time);
+
+ json_object_set_object_member (obj, "meta", meta);
+
+ int names_len = 0;
+ g_auto(GStrv) names = gvdb_table_get_names (db, &names_len);
+
+ const char * const meta_keys[] = {
+ PROBE_DB_META_VERSION_KEY,
+ PROBE_DB_META_APPID_KEY,
+ PROBE_DB_META_PROFILE_KEY,
+ PROBE_DB_META_START_KEY,
+ NULL,
+ };
+
+ JsonArray *probes_arr = json_array_new ();
+
+ for (int i = 0; i < names_len; i++)
+ {
+ const char *key_name = names[i];
+
+ if (g_strv_contains (meta_keys, key_name))
+ continue;
+
+ if (!gvdb_table_has_value (db, key_name))
+ continue;
+
+ g_autoptr(GVariant) value = gvdb_table_get_raw_value (db, key_name);
+ if (value == NULL)
+ continue;
+
+ const char *file = NULL;
+ const char *function = NULL;
+ const char *probe_name = NULL;
+ g_autoptr(GVariant) samples = NULL;
+ gint32 line, n_samples;
+
+ g_variant_get (value, "(&s&s&suu@a(xx))",
+ &probe_name,
+ &function,
+ &file,
+ &line,
+ &n_samples,
+ &samples);
+
+ JsonObject *probe_obj = json_object_new ();
+
+ json_object_set_string_member (probe_obj, "name", probe_name);
+ json_object_set_string_member (probe_obj, "file", file);
+ json_object_set_int_member (probe_obj, "line", line);
+ json_object_set_string_member (probe_obj, "function", function);
+
+ JsonObject *samples_obj = json_object_new ();
+ collect_probe_samples (samples, samples_obj);
+ json_object_set_object_member (probe_obj, "samples", samples_obj);
+
+ json_array_add_object_element (probes_arr, probe_obj);
+ }
+
+ json_object_set_array_member (obj, "probes", probes_arr);
+
+ gvdb_table_free (db);
+
+ g_autoptr(JsonGenerator) gen = json_generator_new ();
+ json_generator_set_pretty (gen, opt_pretty);
+ json_generator_set_root (gen, root);
+
+ if (opt_output != NULL)
+ {
+ g_autoptr(GError) gen_error = NULL;
+
+ json_generator_to_file (gen, opt_output, &error);
+ if (error != NULL)
+ {
+ eos_profile_util_print_error ("Unable to write to '%s': %s",
+ opt_output,
+ error->message);
+ return 1;
+ }
+ }
+ else
+ {
+ g_autofree char *str = json_generator_to_data (gen, NULL);
+
+ g_print ("%s\n", str);
+ }
+
+ return 0;
+}