summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEmmanuele Bassi <ebassi@gnome.org>2018-03-16 14:44:51 +0000
committerP. F. Chimento <philip.chimento@gmail.com>2018-03-21 12:14:59 -0700
commit1fe059365819942330f669d14238b334f4701252 (patch)
tree6f1069588074a458f4b6c46dbe35ac8fd72b1672
parent4caf9df8b070140ee62218d3ea678f9732c1b8a7 (diff)
Add machine-readable diff output
If we're using eos-profile-diff in a CI infrastructure then we should not be printing out data in a human readable format, as we may want to interpret the output using scripts at a later point. We can use JSON, instead, and allow redirecting the output to a file; this way, we can store the output as an artifact, collect it, and parse it later.
-rw-r--r--tools/eos-profile-tool/eos-profile-cmd-diff.c168
1 files changed, 152 insertions, 16 deletions
diff --git a/tools/eos-profile-tool/eos-profile-cmd-diff.c b/tools/eos-profile-tool/eos-profile-cmd-diff.c
index bfa671e..efff656 100644
--- a/tools/eos-profile-tool/eos-profile-cmd-diff.c
+++ b/tools/eos-profile-tool/eos-profile-cmd-diff.c
@@ -6,7 +6,13 @@
#include "endless/eosprofile-private.h"
#include "endless/gvdb/gvdb-reader.h"
+#include <json-glib/json-glib.h>
#include <math.h>
+#include <float.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
static const double
scale_val (double val)
@@ -45,9 +51,29 @@ unit_for (double val)
}
static char **opt_files;
+static char *opt_format;
+static char *opt_output;
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: plain, json)",
+ .arg_description = "FORMAT",
+ },
+ {
+ .long_name = "output",
+ .short_name = 'o',
+ .flags = G_OPTION_FLAG_NONE,
+ .arg = G_OPTION_ARG_FILENAME,
+ .arg_data = &opt_output,
+ .description = "The output file, or '-' for the standard output",
+ .arg_description = "FILE",
+ },
+ {
.long_name = G_OPTION_REMAINING,
.short_name = 0,
.flags = G_OPTION_FLAG_NONE,
@@ -60,6 +86,9 @@ static GOptionEntry opts[] = {
{ NULL, },
};
+static int output_fd = -1;
+static char *output_tmpfile = NULL;
+
gboolean
eos_profile_cmd_diff_parse_args (int argc,
char **argv)
@@ -77,6 +106,38 @@ eos_profile_cmd_diff_parse_args (int argc,
return FALSE;
}
+ if (opt_format == NULL)
+ opt_format = "plain";
+
+ if (g_strcmp0 (opt_format, "plain") != 0 &&
+ g_strcmp0 (opt_format, "json") != 0)
+ {
+ eos_profile_util_print_error ("Invalid output format");
+ return FALSE;
+ }
+
+ if (opt_output == NULL)
+ opt_output = "-";
+
+ if (opt_output != NULL)
+ {
+ if (opt_output[0] == '-' && opt_output[1] == '\0')
+ {
+ output_fd = STDOUT_FILENO;
+ }
+ else
+ {
+ g_autoptr(GError) error = NULL;
+
+ output_fd = g_file_open_tmp ("eos-profile-diff-XXXXXX", &output_tmpfile, &error);
+ if (error != NULL)
+ {
+ eos_profile_util_print_error ("Unable to open output file: %s", error->message);
+ return FALSE;
+ }
+ }
+ }
+
if (opt_files == NULL || g_strv_length (opt_files) < 2)
{
eos_profile_util_print_error ("Not enough files to compare");
@@ -249,6 +310,21 @@ eos_profile_cmd_diff_main (void)
i += 1;
}
+ g_autoptr(JsonNode) json_res = NULL;
+ g_autoptr(GString) buf = NULL;
+
+ if (g_strcmp0 (opt_format, "json") == 0)
+ {
+ json_res = json_node_new (JSON_NODE_ARRAY);
+
+ JsonArray *arr = json_array_new ();
+ json_node_take_array (json_res, arr);
+ }
+ else if (g_strcmp0 (opt_format, "plain") == 0)
+ {
+ buf = g_string_new (NULL);
+ }
+
GHashTableIter iter;
gpointer value;
@@ -256,31 +332,91 @@ eos_profile_cmd_diff_main (void)
while (g_hash_table_iter_next (&iter, NULL, &value))
{
ProbeData *p = value;
+ JsonArray *res_array = NULL;
+ JsonArray *avg_array = NULL;
+ JsonObject *obj = NULL;
+
+ if (json_res != NULL)
+ {
+ res_array = json_node_get_array (json_res);
- g_print ("Probe: %s\n", p->probe_name);
+ obj = json_object_new ();
+ json_array_add_object_element (res_array, obj);
+
+ json_object_set_string_member (obj, "probeName", p->probe_name);
+
+ avg_array = json_array_sized_new (p->results->len);
+ json_object_set_array_member (obj, "averageResults", avg_array);
+ }
+ else
+ {
+ g_string_append_printf (buf,
+ "Probe: %s\n"
+ " ┕━ • avg: ",
+ p->probe_name);
+ }
- g_print (" ┕━ • avg: ");
for (int j = 0; j < p->results->len; j++)
{
ProbeResult *r = &g_array_index (p->results, ProbeResult, j);
- g_print ("%.02f %s", scale_val (r->avg), unit_for (r->avg));
-
- if (r->avg > p->avg)
- g_print ("[+]");
- else if (r->avg < p->avg)
- g_print ("[-]");
- else if (fabs (r->avg - p->avg) < 0.0001)
- g_print ("[~]");
+ if (avg_array != NULL)
+ {
+ json_array_add_double_element (avg_array, r->avg);
+ }
else
- g_print ("[=]");
+ {
+ g_string_append_printf (buf, "%.02f %s", scale_val (r->avg), unit_for (r->avg));
+
+ if (fabs (r->avg - p->avg) < FLT_EPSILON)
+ g_string_append (buf, "[=]");
+ else if (r->avg > p->avg)
+ g_string_append (buf, "[+]");
+ else if (r->avg < p->avg)
+ g_string_append (buf, "[-]");
+
+ if (j == p->results->len - 1)
+ g_string_append (buf, "\n");
+ else
+ g_string_append (buf, ", ");
+ }
+ }
+ }
- if (j == p->results->len - 1)
- g_print ("\n");
- else
- g_print (", ");
+ g_autofree char *data = NULL;
+
+ if (json_res != NULL)
+ {
+ g_autoptr(JsonGenerator) gen = json_generator_new ();
+
+ json_generator_set_root (gen, json_res);
+ data = json_generator_to_data (gen, NULL);
+ }
+ else if (buf != NULL)
+ {
+ data = g_string_free (buf, FALSE);
+ buf = NULL;
+ }
+
+ write (output_fd, data, strlen (data));
+ if (output_fd != STDOUT_FILENO)
+ {
+ close (output_fd);
+
+ if (rename (output_tmpfile, opt_output) != 0)
+ {
+ int errno_sv = errno;
+
+ eos_profile_util_print_error ("Unable to save output to '%s': %s",
+ opt_output,
+ g_strerror (errno_sv));
+ unlink (output_tmpfile);
+
+ return EXIT_FAILURE;
}
}
+ else
+ write (output_fd, "\n", 1);
- return 0;
+ return EXIT_SUCCESS;
}