summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Abente Lahaye <martin.abente.lahaye@gmail.com>2018-01-22 13:53:01 -0300
committerGitHub <noreply@github.com>2018-01-22 13:53:01 -0300
commit90342f56a3dd5afa7825f00001562843eebcb749 (patch)
treea76df910270c651b8232aa50e6566f06e5763c18
parent12ead3785058adecd32fa302855758035817fe29 (diff)
parenteb46fb93d851295dfffbb290382eaa1cd7426303 (diff)
Merge pull request #4179 from endlessm/T20694
T20694
-rw-r--r--endless/eosprofile-private.h25
-rw-r--r--endless/eosprofile.c84
-rw-r--r--tools/Makefile.am.inc29
-rw-r--r--tools/eos-profile-tool/eos-profile-cmd-help.c28
-rw-r--r--tools/eos-profile-tool/eos-profile-cmd-show.c306
-rw-r--r--tools/eos-profile-tool/eos-profile-cmds.h13
-rw-r--r--tools/eos-profile-tool/eos-profile-main.c70
-rw-r--r--tools/eos-profile-tool/eos-profile-utils.c93
-rw-r--r--tools/eos-profile-tool/eos-profile-utils.h23
9 files changed, 643 insertions, 28 deletions
diff --git a/endless/eosprofile-private.h b/endless/eosprofile-private.h
index a07de0e..7c367bd 100644
--- a/endless/eosprofile-private.h
+++ b/endless/eosprofile-private.h
@@ -11,17 +11,42 @@ G_BEGIN_DECLS
#define PROBE_DB_META_BASE_KEY "/com/endlessm/Sdk/meta"
#define PROBE_DB_META_VERSION_KEY PROBE_DB_META_BASE_KEY "/db_version"
+#define PROBE_DB_META_APPID_KEY PROBE_DB_META_BASE_KEY "/app_id"
+#define PROBE_DB_META_START_KEY PROBE_DB_META_BASE_KEY "/start_time"
+#define PROBE_DB_META_PROFILE_KEY PROBE_DB_META_BASE_KEY "/profile_time"
+
+#define PROBE_DB_META_PROBE_TYPE "(sssuua(xx))"
typedef struct {
+ /* element-type (key utf8) (value EosProfileProbe) */
GHashTable *probes;
gboolean capture;
char *capture_file;
+
+ /* Wallclock time */
+ gint64 start_time;
+
+ /* Monotonic time */
+ gint64 profile_start;
+ gint64 profile_end;
} ProfileState;
G_LOCK_DEFINE_STATIC (profile_state);
static ProfileState *profile_state;
+struct _EosProfileProbe {
+ char *file;
+ gint32 line;
+ char *function;
+ char *name;
+
+ /* element-type ProfileSample */
+ GArray *samples;
+
+ GMutex probe_lock;
+};
+
typedef struct {
gint64 start_time;
gint64 end_time;
diff --git a/endless/eosprofile.c b/endless/eosprofile.c
index 23959b3..732da79 100644
--- a/endless/eosprofile.c
+++ b/endless/eosprofile.c
@@ -110,19 +110,6 @@ sample_compare (gconstpointer a,
#define N_SAMPLES 64
-struct _EosProfileProbe {
- volatile int ref_count;
-
- char *file;
- gint32 line;
- char *function;
- char *name;
-
- GArray *samples;
-
- GMutex probe_lock;
-};
-
static EosProfileProbe eos_profile_dummy_probe;
static EosProfileProbe *
@@ -133,8 +120,6 @@ eos_profile_probe_new (const char *file,
{
EosProfileProbe *res = g_new0 (EosProfileProbe, 1);
- res->ref_count = 1;
-
res->name = g_strdup (name);
res->function = g_strdup (function);
res->file = g_strdup (file);
@@ -152,9 +137,9 @@ eos_profile_probe_destroy (gpointer data)
{
EosProfileProbe *probe = data;
- g_hash_table_remove (profile_state->probes, probe->name);
+ if (probe->samples != NULL)
+ g_array_unref (probe->samples);
- g_array_unref (probe->samples);
g_free (probe->name);
g_free (probe->function);
g_free (probe->file);
@@ -333,6 +318,12 @@ eos_profile_state_init (void)
g_get_prgname ());
}
}
+
+ GTimeVal now;
+ g_get_current_time (&now);
+ profile_state->start_time = now.tv_sec;
+
+ profile_state->profile_start = g_get_monotonic_time ();
}
}
@@ -530,12 +521,53 @@ get_parent (GHashTable *table,
return parent;
}
+static void
+add_metadata (GHashTable *table)
+{
+ /* version */
+ g_autofree char *version_key = g_strdup (PROBE_DB_META_VERSION_KEY);
+ gsize version_key_len = strlen (version_key);
+ GvdbItem *key_meta = gvdb_hash_table_insert (table, PROBE_DB_META_VERSION_KEY);
+ gvdb_item_set_parent (key_meta, get_parent (table, version_key, version_key_len));
+ gvdb_item_set_value (key_meta, g_variant_new_int32 (PROBE_DB_VERSION));
+
+ /* application id */
+ GApplication *app = g_application_get_default ();
+ if (app != NULL)
+ {
+ const char *appid = g_application_get_application_id (app);
+
+ g_autofree char *appid_key = g_strdup (PROBE_DB_META_APPID_KEY);
+ gsize appid_key_len = strlen (appid_key);
+ GvdbItem *appid_meta = gvdb_hash_table_insert (table, PROBE_DB_META_APPID_KEY);
+ gvdb_item_set_parent (appid_meta, get_parent (table, appid_key, appid_key_len));
+ gvdb_item_set_value (appid_meta, g_variant_new_string (appid));
+ }
+
+ /* start time */
+ g_autofree char *start_key = g_strdup (PROBE_DB_META_START_KEY);
+ gsize start_key_len = strlen (start_key);
+ GvdbItem *start_meta = gvdb_hash_table_insert (table, PROBE_DB_META_START_KEY);
+ gvdb_item_set_parent (start_meta, get_parent (table, start_key, start_key_len));
+ gvdb_item_set_value (start_meta, g_variant_new_int64 (profile_state->start_time));
+
+ /* profile time */
+ g_autofree char *profile_key = g_strdup (PROBE_DB_META_PROFILE_KEY);
+ gsize profile_key_len = strlen (profile_key);
+ GvdbItem *profile_meta = gvdb_hash_table_insert (table, PROBE_DB_META_PROFILE_KEY);
+ gvdb_item_set_parent (profile_meta, get_parent (table, profile_key, profile_key_len));
+ gint64 profile_time = profile_state->profile_end - profile_state->profile_start;
+ gvdb_item_set_value (profile_meta, g_variant_new_int64 (profile_time));
+}
+
void
eos_profile_state_dump (void)
{
if (profile_state == NULL)
return;
+ profile_state->profile_end = g_get_monotonic_time ();
+
if (!profile_state->capture)
{
profile_state_dump_to_console ();
@@ -545,11 +577,7 @@ eos_profile_state_dump (void)
g_autoptr(GHashTable) db_table = gvdb_hash_table_new (NULL, NULL);
/* Metadata for the DB */
- g_autofree char *version_key = g_strdup (PROBE_DB_META_VERSION_KEY);
- gsize version_key_len = strlen (version_key);
- GvdbItem *meta = gvdb_hash_table_insert (db_table, PROBE_DB_META_VERSION_KEY);
- gvdb_item_set_parent (meta, get_parent (db_table, version_key, version_key_len));
- gvdb_item_set_value (meta, g_variant_new_int32 (PROBE_DB_VERSION));
+ add_metadata (db_table);
/* Iterate over the probes */
GHashTableIter iter;
@@ -567,7 +595,7 @@ eos_profile_state_dump (void)
GVariantBuilder builder;
- g_variant_builder_init (&builder, G_VARIANT_TYPE ("(sssuua(xx))"));
+ g_variant_builder_init (&builder, G_VARIANT_TYPE (PROBE_DB_META_PROBE_TYPE));
g_variant_builder_add (&builder, "s", probe->name);
g_variant_builder_add (&builder, "s", probe->function);
@@ -602,11 +630,6 @@ eos_profile_state_dump (void)
gvdb_item_set_value (item, g_variant_builder_end (&builder));
}
- /* Clean up */
- g_hash_table_unref (profile_state->probes);
- g_free (profile_state->capture_file);
- g_free (profile_state);
-
g_autoptr(GError) error = NULL;
gvdb_table_write_contents (db_table, profile_state->capture_file,
G_BYTE_ORDER != G_LITTLE_ENDIAN,
@@ -614,4 +637,9 @@ eos_profile_state_dump (void)
if (error != NULL)
g_printerr ("PROFILE: %s\n", error->message);
+
+ /* Clean up */
+ g_hash_table_unref (profile_state->probes);
+ g_free (profile_state->capture_file);
+ g_free (profile_state);
}
diff --git a/tools/Makefile.am.inc b/tools/Makefile.am.inc
index 8f0015c..d8751bb 100644
--- a/tools/Makefile.am.inc
+++ b/tools/Makefile.am.inc
@@ -41,3 +41,32 @@ dist_commands_DATA = \
$(NULL)
EXTRA_DIST += $(tools_test_modules)
+
+bin_PROGRAMS = \
+ eos-profile \
+ $(NULL)
+
+eos_profile_SOURCES = \
+ tools/eos-profile-tool/eos-profile-cmds.h \
+ tools/eos-profile-tool/eos-profile-cmd-help.c \
+ tools/eos-profile-tool/eos-profile-cmd-show.c \
+ tools/eos-profile-tool/eos-profile-main.c \
+ tools/eos-profile-tool/eos-profile-utils.c \
+ tools/eos-profile-tool/eos-profile-utils.h \
+ endless/gvdb/gvdb-reader.c \
+ $(NULL)
+
+eos_profile_CPPFLAGS = \
+ @EOS_SDK_CFLAGS@ \
+ -DCOMPILING_EOS_SDK \
+ -DG_LOG_DOMAIN=\"EosProfile\" \
+ -I$(top_builddir) \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/endless/ \
+ -I$(top_builddir)/endless/ \
+ -I$(top_srcdir)/endless/gvdb \
+ -I$(top_srcdir)/tools/eos-profile-tool \
+ $(NULL)
+
+eos_profile_CFLAGS = $(AM_CFLAGS)
+eos_profile_LDADD = $(top_builddir)/libendless-@EOS_SDK_API_VERSION@.la -lm @EOS_SDK_LIBS@
diff --git a/tools/eos-profile-tool/eos-profile-cmd-help.c b/tools/eos-profile-tool/eos-profile-cmd-help.c
new file mode 100644
index 0000000..754714f
--- /dev/null
+++ b/tools/eos-profile-tool/eos-profile-cmd-help.c
@@ -0,0 +1,28 @@
+#include "config.h"
+
+#include "eos-profile-cmds.h"
+
+gboolean
+eos_profile_cmd_help_parse_args (int argc,
+ char **argv)
+{
+ return TRUE;
+}
+
+int
+eos_profile_cmd_help_main (void)
+{
+ g_print (
+ "eos-profile\n"
+ "\n"
+ "Usage: eos-profile <COMMAND> [OPTIONS...]\n"
+ "\n"
+ "Examples:\n"
+ "\n"
+ " eos-profile help - This help screen\n"
+ " eos-profile show FILE - Shows a capture file\n"
+ "\n"
+ );
+
+ return 0;
+}
diff --git a/tools/eos-profile-tool/eos-profile-cmd-show.c b/tools/eos-profile-tool/eos-profile-cmd-show.c
new file mode 100644
index 0000000..b01bd14
--- /dev/null
+++ b/tools/eos-profile-tool/eos-profile-cmd-show.c
@@ -0,0 +1,306 @@
+#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>
+
+static GPtrArray *files;
+
+static const double
+scale_val (double val)
+{
+ if (val >= G_USEC_PER_SEC)
+ return val / G_USEC_PER_SEC;
+
+ if (val >= 1000)
+ return val / 1000.0;
+
+ return val;
+}
+
+static const char *
+unit_for (double val)
+{
+ enum {
+ SECONDS,
+ MILLISECONDS,
+ MICROSECONDS
+ };
+
+ const char *units[] = {
+ [SECONDS] = "s",
+ [MILLISECONDS] = "ms",
+ [MICROSECONDS] = "µs",
+ };
+
+ if (val >= G_USEC_PER_SEC)
+ return units[SECONDS];
+
+ if (val >= 1000)
+ return units[MILLISECONDS];
+
+ return units[MICROSECONDS];
+}
+
+gboolean
+eos_profile_cmd_show_parse_args (int argc,
+ char **argv)
+{
+ files = g_ptr_array_new ();
+
+ for (int i = 1; i < argc; i++)
+ g_ptr_array_add (files, argv[i]);
+
+ if (files->len == 0)
+ {
+ g_printerr ("Usage: eos-profile show FILE [FILE...]\n");
+ g_ptr_array_unref (files);
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+print_probe (const char *name)
+{
+ eos_profile_util_print_message ("PROBE", EOS_PRINT_COLOR_GREEN,
+ "%s",
+ name);
+}
+
+static void
+print_location (const char *file,
+ gint32 line,
+ const char *function)
+{
+ eos_profile_util_print_message (NULL, EOS_PRINT_COLOR_NONE,
+ " ┕━ • location: %s() at %s:%d",
+ function,
+ file,
+ line);
+}
+
+static void
+print_samples (const char *name,
+ gint32 n_samples,
+ GVariant *array)
+{
+ 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)
+ {
+ double avg = total / (double) valid_samples->len;
+ double s = 0;
+ double s_part = 0;
+
+ 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);
+ }
+
+ if (valid_samples->len > 1)
+ s = sqrt (s_part / (double) valid_samples->len - 1);
+ else
+ s = 0.0;
+
+ g_autofree char *stddev = g_strdup_printf (", σ: %g", s);
+
+ eos_profile_util_print_message (NULL, EOS_PRINT_COLOR_NONE,
+ " ┕━ • %d samples",
+ valid_samples->len);
+ eos_profile_util_print_message (NULL, EOS_PRINT_COLOR_NONE,
+ " ┕━ • total time: %d %s\n"
+ " ┕━ • avg: %g %s, min: %d %s, max: %d %s%s",
+ (int) scale_val (total), unit_for (total),
+ scale_val (avg), unit_for (avg),
+ (int) scale_val (min_sample), unit_for (min_sample),
+ (int) scale_val (max_sample), unit_for (max_sample),
+ s == 0.0 || isnan (s) ? "" : stddev);
+ }
+ else if (valid_samples->len == 1)
+ {
+ eos_profile_util_print_message (NULL, EOS_PRINT_COLOR_NONE,
+ " ┕━ • 1 sample");
+ eos_profile_util_print_message (NULL, EOS_PRINT_COLOR_NONE,
+ " ┕━ • total time: %d %s",
+ (int) scale_val (total),
+ unit_for (total));
+ }
+ else
+ {
+ eos_profile_util_print_message (NULL, EOS_PRINT_COLOR_NONE,
+ " ┕━ • Not enough valid samples found");
+ }
+}
+
+int
+eos_profile_cmd_show_main (void)
+{
+ g_assert (files != NULL);
+
+ for (int i = 0; i < files->len; i++)
+ {
+ const char *filename = g_ptr_array_index (files, i);
+ g_autoptr(GError) error = NULL;
+
+ eos_profile_util_print_message ("INFO", EOS_PRINT_COLOR_BLUE,
+ "Loading profiling data from '%s'",
+ filename);
+
+ GvdbTable *db = gvdb_table_new (filename, TRUE, &error);
+
+ if (error != NULL)
+ {
+ eos_profile_util_print_error ("Unable to load '%s': %s\n", filename, 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;
+
+ if (version != PROBE_DB_VERSION)
+ {
+ eos_profile_util_print_error ("Unable to load '%s': invalid version\n");
+ return 1;
+ }
+
+ v = gvdb_table_get_raw_value (db, PROBE_DB_META_APPID_KEY);
+ if (v != NULL)
+ {
+ const char *appid = g_variant_get_string (v, NULL);
+
+ eos_profile_util_print_message ("INFO", EOS_PRINT_COLOR_BLUE,
+ "Application: %s",
+ appid);
+ g_clear_pointer (&v, g_variant_unref);
+ }
+
+ g_clear_pointer (&v, g_variant_unref);
+ v = gvdb_table_get_raw_value (db, PROBE_DB_META_PROFILE_KEY);
+ if (v != NULL)
+ {
+ gint64 profile_time = g_variant_get_int64 (v);
+
+ eos_profile_util_print_message ("INFO", EOS_PRINT_COLOR_BLUE,
+ "Total profile time: %d %s",
+ (int) scale_val (profile_time),
+ unit_for (profile_time));
+ g_clear_pointer (&v, g_variant_unref);
+ }
+
+ v = gvdb_table_get_raw_value (db, PROBE_DB_META_START_KEY);
+ if (v != NULL)
+ {
+ g_autoptr(GDateTime) dt =
+ g_date_time_new_from_unix_local (g_variant_get_int64 (v));
+ g_autofree char *start_time =
+ g_date_time_format (dt, "%Y-%m-%d %T");
+
+ eos_profile_util_print_message ("INFO", EOS_PRINT_COLOR_BLUE,
+ "Start time: %s",
+ start_time);
+ g_clear_pointer (&v, g_variant_unref);
+ }
+
+ 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,
+ };
+
+ for (int j = 0; j < names_len; j++)
+ {
+ const char *name = names[j];
+
+ if (g_strv_contains (meta_keys, name))
+ continue;
+
+ g_autoptr(GVariant) value = gvdb_table_get_raw_value (db, 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);
+
+ print_probe (probe_name);
+ print_location (file, line, function);
+ if (n_samples > 0)
+ print_samples (probe_name, n_samples, samples);
+
+ }
+
+ gvdb_table_free (db);
+ }
+
+ return 0;
+}
diff --git a/tools/eos-profile-tool/eos-profile-cmds.h b/tools/eos-profile-tool/eos-profile-cmds.h
new file mode 100644
index 0000000..c6da7d4
--- /dev/null
+++ b/tools/eos-profile-tool/eos-profile-cmds.h
@@ -0,0 +1,13 @@
+#pragma once
+
+#include <glib.h>
+
+typedef gboolean (* EosProfileCmdParseArgs) (int argc, char **argv);
+typedef int (* EosProfileCmdMain) (void);
+
+gboolean eos_profile_cmd_help_parse_args (int argc, char **argv);
+int eos_profile_cmd_help_main (void);
+
+gboolean eos_profile_cmd_show_parse_args (int argc, char **argv);
+int eos_profile_cmd_show_main (void);
+
diff --git a/tools/eos-profile-tool/eos-profile-main.c b/tools/eos-profile-tool/eos-profile-main.c
new file mode 100644
index 0000000..6879cb6
--- /dev/null
+++ b/tools/eos-profile-tool/eos-profile-main.c
@@ -0,0 +1,70 @@
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <locale.h>
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include "eos-profile-cmds.h"
+
+static const struct {
+ const char *name;
+ const char *description;
+
+ EosProfileCmdParseArgs parse_args;
+ EosProfileCmdMain main;
+} profile_commands[] = {
+ {
+ .name = "help",
+ .description = "Prints help",
+ .parse_args = eos_profile_cmd_help_parse_args,
+ .main = eos_profile_cmd_help_main,
+ },
+ {
+ .name = "show",
+ .description = "Shows a capture",
+ .parse_args = eos_profile_cmd_show_parse_args,
+ .main = eos_profile_cmd_show_main,
+ },
+
+ { NULL, },
+};
+
+int
+main (int argc,
+ char *argv[])
+{
+ setlocale (LC_ALL, "");
+ textdomain (GETTEXT_PACKAGE);
+
+ bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+
+ const char *cmd = NULL;
+
+ if (argc < 2)
+ cmd = "help";
+ else
+ cmd = argv[1];
+
+ for (int i = 0; i < G_N_ELEMENTS (profile_commands); i++)
+ {
+ if (g_strcmp0 (cmd, profile_commands[i].name) == 0)
+ {
+ argc -= 1;
+ argv += 1;
+
+ if (!profile_commands[i].parse_args (argc, argv))
+ return EXIT_FAILURE;
+
+ return profile_commands[i].main ();
+ }
+ }
+
+ g_printerr ("Usage: eos-profile <COMMAND> [OPTIONS...]\n");
+
+ return EXIT_FAILURE;
+}
diff --git a/tools/eos-profile-tool/eos-profile-utils.c b/tools/eos-profile-tool/eos-profile-utils.c
new file mode 100644
index 0000000..2d63804
--- /dev/null
+++ b/tools/eos-profile-tool/eos-profile-utils.c
@@ -0,0 +1,93 @@
+#include "config.h"
+
+#include "eos-profile-utils.h"
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <math.h>
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <termios.h>
+#include <fcntl.h>
+
+static const char *ansi_colors[] = {
+ [EOS_PRINT_COLOR_GREEN] = "[1;32m",
+ [EOS_PRINT_COLOR_BLUE] = "[1;34m",
+ [EOS_PRINT_COLOR_YELLOW] = "[1;33m",
+ [EOS_PRINT_COLOR_RED] = "[1;31m",
+ [EOS_PRINT_COLOR_NONE] = "[0m",
+};
+
+static char *
+gen_color_message (const char *prefix,
+ EosPrintColor color,
+ const char *fmt,
+ va_list args)
+{
+ g_autofree char *res = NULL;
+
+ if (prefix != NULL)
+ {
+ g_autofree char *msg = g_strdup_vprintf (fmt, args);
+
+ res = g_strdup_printf ("\033%s%s\033%s: %s",
+ ansi_colors[color],
+ prefix,
+ ansi_colors[EOS_PRINT_COLOR_NONE],
+ msg);
+
+ }
+ else
+ res = g_strdup_vprintf (fmt, args);
+
+ return g_steal_pointer (&res);
+}
+
+void
+eos_profile_util_print_message (const char *prefix,
+ EosPrintColor color,
+ const char *fmt,
+ ...)
+{
+ g_autofree char *msg = NULL;
+ va_list args;
+
+ va_start (args, fmt);
+ msg = gen_color_message (prefix, color, fmt, args);
+ va_end (args);
+
+ g_print ("%s\n", msg);
+}
+
+void
+eos_profile_util_print_error (const char *fmt,
+ ...)
+{
+ g_autofree char *msg = NULL;
+ va_list args;
+
+ va_start (args, fmt);
+ msg = gen_color_message ("ERROR", EOS_PRINT_COLOR_RED, fmt, args);
+ va_end (args);
+
+ g_printerr ("%s\n", msg);
+}
+
+void
+eos_profile_util_print_warning (const char *fmt,
+ ...)
+{
+ g_autofree char *msg = NULL;
+ va_list args;
+
+ va_start (args, fmt);
+ msg = gen_color_message ("WARNING", EOS_PRINT_COLOR_YELLOW, fmt, args);
+ va_end (args);
+
+ g_printerr ("%s\n", msg);
+}
diff --git a/tools/eos-profile-tool/eos-profile-utils.h b/tools/eos-profile-tool/eos-profile-utils.h
new file mode 100644
index 0000000..b6030c2
--- /dev/null
+++ b/tools/eos-profile-tool/eos-profile-utils.h
@@ -0,0 +1,23 @@
+#pragma once
+
+#include <glib.h>
+
+typedef enum {
+ EOS_PRINT_COLOR_GREEN,
+ EOS_PRINT_COLOR_BLUE,
+ EOS_PRINT_COLOR_YELLOW,
+ EOS_PRINT_COLOR_RED,
+
+ EOS_PRINT_COLOR_NONE
+} EosPrintColor;
+
+void eos_profile_util_print_message (const char *prefix,
+ EosPrintColor color,
+ const char *fmt,
+ ...) G_GNUC_PRINTF (3, 4);
+
+void eos_profile_util_print_error (const char *msg,
+ ...) G_GNUC_PRINTF (1, 2);
+
+void eos_profile_util_print_warning (const char *msg,
+ ...) G_GNUC_PRINTF (1, 2);