aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTuukka Tikkanen <tuukka.tikkanen@linaro.org>2014-12-17 16:44:48 +0200
committerTuukka Tikkanen <tuukka.tikkanen@linaro.org>2014-12-29 10:17:16 +0200
commitc4433ad725b6128eda8018dbc8aa1094e01f9b88 (patch)
tree1109774dfddda9b5218a718eb81ddec438d0296b
parent68f7dbab41a862c3879795ae84a14113b72fc872 (diff)
Idlestat: Add comparison report style
This patch adds comparison report style. It is similar to default report style, except that it displays changes in active trace compared to the baseline trace. It becomes active by providing a baseline trace (--baseline) and specifying the comparison report style with "-r comparison". Signed-off-by: Tuukka Tikkanen <tuukka.tikkanen@linaro.org>
-rw-r--r--Makefile2
-rw-r--r--comparison_report.c312
2 files changed, 313 insertions, 1 deletions
diff --git a/Makefile b/Makefile
index 590c54d..0739707 100644
--- a/Makefile
+++ b/Makefile
@@ -25,7 +25,7 @@ CFLAGS?=-g -Wall
CC=gcc
TRACE_OBJS = tracefile_idlestat.o tracefile_ftrace.o
-REPORT_OBJS = default_report.o csv_report.o
+REPORT_OBJS = default_report.o csv_report.o comparison_report.o
OBJS = idlestat.o topology.o trace.o utils.o energy_model.o reports.o \
diff --git a/comparison_report.c b/comparison_report.c
new file mode 100644
index 0000000..e7cd1c5
--- /dev/null
+++ b/comparison_report.c
@@ -0,0 +1,312 @@
+#include <float.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <malloc.h>
+#include <math.h>
+#include <assert.h>
+#include "report_ops.h"
+#include "idlestat.h"
+#include "utils.h"
+
+struct compare_report_data {
+ struct cpuidle_cstate *curr_cstate_baseline;
+ struct cpufreq_pstate *curr_pstate_baseline;
+};
+
+static void display_factored_time_delta(double time, int align)
+{
+ char buffer[128];
+
+ if (fabs(time) < 1000.0) {
+ snprintf(buffer, sizeof(buffer), "%+.0lfus", time);
+ printf("%*s", align, buffer);
+ }
+ else if (fabs(time) < 1000000.0) {
+ snprintf(buffer, sizeof(buffer), "%+.1lfms", time / 1000.0);
+ printf("%*s", align, buffer);
+ }
+ else if (fabs(time) < 100000000000.0) {
+ snprintf(buffer, sizeof(buffer), "%+.1lfs", time / 1000000.0);
+ printf("%*s", align, buffer);
+ }
+ else
+ printf("%.*s", align, " ");
+}
+
+static void display_int_delta(int value, int align)
+{
+ printf(value ? " %+*d |" : " %*d |", align, value);
+}
+
+
+static int compare_check_options(struct program_options *options)
+{
+ if (options->baseline_filename == NULL) {
+ fprintf(stderr,
+ "Error: Comparison report requires baseline trace\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static void * compare_alloc_data(struct program_options *options)
+{
+ struct compare_report_data *ret = calloc(sizeof(*ret), 1);
+
+ if (ret == NULL)
+ return ptrerror(__func__);
+
+ return ret;
+}
+
+static void compare_release_data(void *data)
+{
+ free(data);
+}
+
+
+static void compare_cstate_single_state(struct cpuidle_cstate *c, void *report_data)
+{
+ struct cpuidle_cstate empty;
+ struct cpuidle_cstate diff;
+ struct cpuidle_cstate *b;
+ struct compare_report_data *rdata;
+
+ assert(report_data != NULL);
+ rdata = report_data;
+
+ /*
+ * On entry, either current state or baseline state might be NULL:
+ * c = NULL implies this state exists only in baseline
+ * curr_baseline = NULL implies this state exists only in trace
+ * It should never occur that both c and current baseline are NULL.
+ *
+ * If c is NULL, set c to point to empty state and alias the name
+ * in baseline.
+ *
+ * If no current baseline exists, use empty state as baseline.
+ */
+ b = rdata->curr_cstate_baseline;
+ rdata->curr_cstate_baseline = NULL;
+
+ assert(c != NULL || b != NULL);
+
+ if (c == NULL) {
+ memset(&empty, 0, sizeof(empty));
+ empty.name = b->name;
+ c = &empty;
+ }
+
+ if (b == NULL) {
+ memset(&empty, 0, sizeof(empty));
+ b = &empty;
+ }
+
+ diff.min_time = c->min_time - b->min_time;
+ diff.max_time = c->max_time - b->max_time;
+ diff.avg_time = c->avg_time - b->avg_time;
+ diff.duration = c->duration - b->duration;
+ diff.nrdata = c->nrdata - b->nrdata;
+ diff.early_wakings = c->early_wakings - b->early_wakings;
+ diff.late_wakings = b->late_wakings - b->late_wakings;
+
+ printf("| %8s | ", c->name);
+ display_factored_time(c->min_time == DBL_MAX ? 0. :
+ c->min_time, 8);
+ printf(" | ");
+ display_factored_time(c->max_time, 8);
+ printf(" | ");
+ display_factored_time(c->avg_time, 8);
+ printf(" | ");
+ display_factored_time(c->duration, 8);
+ printf(" | ");
+ printf("%5d | %5d | %5d |\n", c->nrdata, c->early_wakings, c->late_wakings);
+ /* Delta */
+ printf("| | ");
+ display_factored_time_delta(diff.min_time, 8);
+ printf(" | ");
+ display_factored_time_delta(diff.max_time, 8);
+ printf(" | ");
+ display_factored_time_delta(diff.avg_time, 8);
+ printf(" | ");
+ display_factored_time_delta(diff.duration, 8);
+ printf(" |");
+ display_int_delta(diff.nrdata, 5);
+ display_int_delta(diff.early_wakings, 5);
+ display_int_delta(diff.late_wakings, 5);
+ printf("\n");
+}
+
+static void compare_set_baseline_cstate(struct cpuidle_cstate *b, void *report_data)
+{
+ struct compare_report_data *rdata;
+
+ assert(report_data != NULL);
+ rdata = report_data;
+
+ /* Process previous state if trace did not have data */
+ if (rdata->curr_cstate_baseline)
+ compare_cstate_single_state(NULL, report_data);
+
+ if (b == NULL)
+ return;
+
+ rdata->curr_cstate_baseline = b;
+}
+
+
+static void compare_cstate_end_cpu(void *report_data)
+{
+ compare_set_baseline_cstate(NULL, report_data);
+}
+
+
+static void compare_pstate_single_freq(struct cpufreq_pstate *p, void *report_data)
+{
+ struct cpufreq_pstate empty;
+ struct cpufreq_pstate diff;
+ struct cpufreq_pstate *b;
+ struct compare_report_data *rdata;
+
+ assert(report_data != NULL);
+ rdata = report_data;
+
+ /*
+ * On entry, either current state or baseline state might be NULL:
+ * p = NULL implies this state exists only in baseline
+ * curr_baseline = NULL implies this state exists only in trace
+ * It should never occur that both p and current baseline are NULL.
+ *
+ * If p is NULL, set p to point to empty state and copy the frequency
+ * from baseline.
+ *
+ * If no current baseline exists, use empty state as baseline.
+ */
+ b = rdata->curr_pstate_baseline;
+ rdata->curr_pstate_baseline = NULL;
+
+ assert(p != NULL || b != NULL);
+
+ if (p == NULL) {
+ memset(&empty, 0, sizeof(empty));
+ empty.freq = b->freq;
+ p = &empty;
+ }
+
+ if (b == NULL) {
+ memset(&empty, 0, sizeof(empty));
+ b = &empty;
+ }
+
+ diff.min_time = p->min_time - b->min_time;
+ diff.max_time = p->max_time - b->max_time;
+ diff.avg_time = p->avg_time - b->avg_time;
+ diff.duration = p->duration - b->duration;
+ diff.count = p->count - b->count;
+
+ printf("| ");
+ display_factored_freq(p->freq, 8);
+ printf(" | ");
+ display_factored_time(p->min_time == DBL_MAX ? 0. : p->min_time, 8);
+ printf(" | ");
+ display_factored_time(p->max_time, 8);
+ printf(" | ");
+ display_factored_time(p->avg_time, 8);
+ printf(" | ");
+ display_factored_time(p->duration, 8);
+ printf(" | %5d |\n", p->count);
+
+ printf("| | ");
+ display_factored_time_delta(diff.min_time, 8);
+ printf(" | ");
+ display_factored_time_delta(diff.max_time, 8);
+ printf(" | ");
+ display_factored_time_delta(diff.avg_time, 8);
+ printf(" | ");
+ display_factored_time_delta(diff.duration, 8);
+ printf(" |");
+ display_int_delta(diff.count, 5);
+ printf("\n");
+}
+
+static void compare_set_baseline_pstate(struct cpufreq_pstate *b, void *report_data)
+{
+ struct compare_report_data *rdata;
+
+ assert(report_data != NULL);
+ rdata = report_data;
+
+ /* Process previous state if trace did not have data */
+ if (rdata->curr_pstate_baseline)
+ compare_pstate_single_freq(NULL, report_data);
+
+ if (b == NULL)
+ return;
+
+ rdata->curr_pstate_baseline = b;
+}
+
+
+static void compare_pstate_end_cpu(void *report_data)
+{
+ compare_set_baseline_pstate(NULL, report_data);
+}
+
+
+static int copy_ops_from_default(struct report_ops *);
+
+static struct report_ops comparison_report_ops = {
+ .name = "comparison",
+ .prepare = copy_ops_from_default,
+ .check_options = compare_check_options,
+ .allocate_report_data = compare_alloc_data,
+ .release_report_data = compare_release_data,
+
+ .cstate_baseline_state = compare_set_baseline_cstate,
+ .cstate_single_state = compare_cstate_single_state,
+ .cstate_end_cpu = compare_cstate_end_cpu,
+
+ .pstate_baseline_freq = compare_set_baseline_pstate,
+ .pstate_single_freq = compare_pstate_single_freq,
+ .pstate_end_cpu = compare_pstate_end_cpu,
+};
+
+static int copy_ops_from_default(struct report_ops *self)
+{
+ struct report_ops *def = get_report_ops("default");
+
+ assert(self == &comparison_report_ops);
+
+ if (is_err(def)) {
+ fprintf(stderr,
+ "Comparison report: cannot copy ops from default\n");
+ return -1;
+ }
+
+ comparison_report_ops.check_output = def->check_output;
+
+ comparison_report_ops.open_report_file = def->open_report_file;
+ comparison_report_ops.close_report_file = def->close_report_file;
+
+ comparison_report_ops.cstate_table_header = def->cstate_table_header;
+ comparison_report_ops.cstate_table_footer = def->cstate_table_footer;
+ comparison_report_ops.cstate_cpu_header = def->cstate_cpu_header;
+
+ comparison_report_ops.pstate_table_header = def->pstate_table_header;
+ comparison_report_ops.pstate_table_footer = def->pstate_table_footer;
+ comparison_report_ops.pstate_cpu_header = def->pstate_cpu_header;
+
+ comparison_report_ops.wakeup_table_header = def->wakeup_table_header;
+ comparison_report_ops.wakeup_table_footer = def->wakeup_table_footer;
+ comparison_report_ops.wakeup_cpu_header = def->wakeup_cpu_header;
+ comparison_report_ops.wakeup_single_irq = def->wakeup_single_irq;
+ comparison_report_ops.wakeup_end_cpu = def->wakeup_end_cpu;
+
+ return 0;
+}
+
+EXPORT_REPORT_OPS(comparison);