diff options
author | Martin Abente Lahaye <martin.abente.lahaye@gmail.com> | 2018-01-22 13:53:01 -0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-01-22 13:53:01 -0300 |
commit | 90342f56a3dd5afa7825f00001562843eebcb749 (patch) | |
tree | a76df910270c651b8232aa50e6566f06e5763c18 | |
parent | 12ead3785058adecd32fa302855758035817fe29 (diff) | |
parent | eb46fb93d851295dfffbb290382eaa1cd7426303 (diff) |
Merge pull request #4179 from endlessm/T20694
T20694
-rw-r--r-- | endless/eosprofile-private.h | 25 | ||||
-rw-r--r-- | endless/eosprofile.c | 84 | ||||
-rw-r--r-- | tools/Makefile.am.inc | 29 | ||||
-rw-r--r-- | tools/eos-profile-tool/eos-profile-cmd-help.c | 28 | ||||
-rw-r--r-- | tools/eos-profile-tool/eos-profile-cmd-show.c | 306 | ||||
-rw-r--r-- | tools/eos-profile-tool/eos-profile-cmds.h | 13 | ||||
-rw-r--r-- | tools/eos-profile-tool/eos-profile-main.c | 70 | ||||
-rw-r--r-- | tools/eos-profile-tool/eos-profile-utils.c | 93 | ||||
-rw-r--r-- | tools/eos-profile-tool/eos-profile-utils.h | 23 |
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); |