#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 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_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"); } } static gboolean print_probes (const char *probe_name, const char *file, const char *function, gint32 line, gint32 n_samples, GVariant *samples, gpointer data G_GNUC_UNUSED) { print_probe (probe_name); print_location (file, line, function); if (n_samples > 0) print_samples (probe_name, n_samples, samples); return TRUE; } 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); } eos_profile_util_foreach_probe_v1 (db, print_probes, NULL); gvdb_table_free (db); } return 0; }