diff options
author | Tuukka Tikkanen <tuukka.tikkanen@linaro.org> | 2014-12-17 16:44:48 +0200 |
---|---|---|
committer | Tuukka Tikkanen <tuukka.tikkanen@linaro.org> | 2014-12-29 10:17:16 +0200 |
commit | c4433ad725b6128eda8018dbc8aa1094e01f9b88 (patch) | |
tree | 1109774dfddda9b5218a718eb81ddec438d0296b | |
parent | 68f7dbab41a862c3879795ae84a14113b72fc872 (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-- | Makefile | 2 | ||||
-rw-r--r-- | comparison_report.c | 312 |
2 files changed, 313 insertions, 1 deletions
@@ -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 = ∅ + } + + if (b == NULL) { + memset(&empty, 0, sizeof(empty)); + b = ∅ + } + + 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 = ∅ + } + + if (b == NULL) { + memset(&empty, 0, sizeof(empty)); + b = ∅ + } + + 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); |