/* * comparison_report.c * * Copyright (C) 2014, Linaro Limited. * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * Contributors: * Tuukka Tikkanen * */ #include #include #include #include #include #include #include #include #include #include "report_ops.h" #include "idlestat.h" #include "utils.h" #include "compiler.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(UNUSED 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);