Messages in this thread | | | From | Namhyung Kim <> | Subject | Re: [PATCH 08/14] perf diff: Change diff command to work over multiple data files | Date | Thu, 29 Nov 2012 20:37:50 +0900 |
| |
On Wed, 28 Nov 2012 14:52:43 +0100, Jiri Olsa wrote: > Adding diff command the flexibility to specify multiple data > files on input. If not input file is given the standard behaviour > stands and diff inspects 'perf.data' and 'perf.data.old' files. > > Also changing the processing and output display for data files. > > For 'perf diff A B' command: > > - the current way is to iterate over B data and display > A (baseline) data (symbol samples) only if found in B > > - this patch iterates over A (baseline) data and display > B data (symbol samples) only if found in A > > For 'perf diff A B C' command, the diff now iterates the > A (baseline) data and display B and C data (symbol samples) > only if they found in A (baseline) > > All other standard diff command computation features > (delta,ratio,wdiff) stays.
This is quite a large change and IMHO can be separated into 3 patches at least.
1) change he->pairs from list to array 2) introduce diff_data__fmt 3) convert to diff_data__fmt
Thanks, Namhyung
> 5 files changed, 560 insertions(+), 392 deletions(-) > > diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c > index 6361b55..9f4ef76 100644 > --- a/tools/perf/builtin-diff.c > +++ b/tools/perf/builtin-diff.c > @@ -18,11 +18,49 @@ > #include "util/util.h" > > #include <stdlib.h> > +#include <math.h> > > -static char const *input_old = "perf.data.old", > - *input_new = "perf.data"; > -static char diff__default_sort_order[] = "dso,symbol"; > -static bool force; > +/* Diff command specific HPP columns. */ > +enum { > + PERF_HPP_DIFF__BASELINE, > + PERF_HPP_DIFF__PERIOD, > + PERF_HPP_DIFF__PERIOD_BASELINE, > + PERF_HPP_DIFF__DELTA, > + PERF_HPP_DIFF__RATIO, > + PERF_HPP_DIFF__WEIGHTED_DIFF, > + PERF_HPP_DIFF__DISPL, > + PERF_HPP_DIFF__FORMULA, > + > + PERF_HPP_DIFF__MAX_INDEX > +}; > + > +struct diff_data__fmt { > + struct perf_hpp_fmt fmt; > + int idx; > + > + char *header; > + int header_width; > +}; > + > +struct diff_data { > + struct perf_session *session; > + const char *file; > + int idx; > + > + struct diff_data__fmt fmt[PERF_HPP_DIFF__MAX_INDEX]; > +}; > + > +static struct diff_data *data; > +static int data_cnt; > + > +#define for_each_data(i, d) \ > + for (i = 0, d = &data[0]; i < data_cnt; i++, d = &data[i]) > + > +#define for_each_data_new(i, d) \ > + for (i = 1, d = &data[1]; i < data_cnt; i++, d = &data[i]) > + > +static const char diff__default_sort_order[] = "dso,symbol"; > +static bool force; > static bool show_displacement; > static bool show_period; > static bool show_formula; > @@ -39,12 +77,55 @@ enum { > COMPUTE_MAX, > }; > > +static int compute_2_hpp[COMPUTE_MAX] = { > + [COMPUTE_DELTA] = PERF_HPP_DIFF__DELTA, > + [COMPUTE_RATIO] = PERF_HPP_DIFF__RATIO, > + [COMPUTE_WEIGHTED_DIFF] = PERF_HPP_DIFF__WEIGHTED_DIFF, > +}; > + > + > const char *compute_names[COMPUTE_MAX] = { > [COMPUTE_DELTA] = "delta", > [COMPUTE_RATIO] = "ratio", > [COMPUTE_WEIGHTED_DIFF] = "wdiff", > }; > > +static struct header_column { > + const char *name; > + int width; > +} columns[PERF_HPP_DIFF__MAX_INDEX] = { > + [PERF_HPP_DIFF__BASELINE] = { > + .name = "Baseline", > + }, > + [PERF_HPP_DIFF__PERIOD] = { > + .name = "Period", > + }, > + [PERF_HPP_DIFF__PERIOD_BASELINE] = { > + .name = "Base period", > + }, > + [PERF_HPP_DIFF__DELTA] = { > + .name = "Delta", > + .width = 7, > + }, > + [PERF_HPP_DIFF__RATIO] = { > + .name = "Ratio", > + .width = 14, > + }, > + [PERF_HPP_DIFF__WEIGHTED_DIFF] = { > + .name = "Weighted diff", > + .width = 14, > + }, > + [PERF_HPP_DIFF__DISPL] = { > + .name = "Displ", > + }, > + [PERF_HPP_DIFF__FORMULA] = { > + .name = "Formula", > + .width = 70, > + } > +}; > + > +#define MAX_COL_WIDTH 70 > + > static int compute; > > static int setup_compute_opt_wdiff(char *opt) > @@ -146,7 +227,7 @@ static int setup_compute(const struct option *opt, const char *str, > return -EINVAL; > } > > -double perf_diff__period_percent(struct hist_entry *he, u64 period) > +static double get_period_percent(struct hist_entry *he, u64 period) > { > u64 total = he->hists->stats.total_period; > return (period * 100.0) / total; > @@ -154,34 +235,34 @@ double perf_diff__period_percent(struct hist_entry *he, u64 period) > > double perf_diff__compute_delta(struct hist_entry *he, struct hist_entry *pair) > { > - double new_percent = perf_diff__period_percent(he, he->stat.period); > - double old_percent = perf_diff__period_percent(pair, pair->stat.period); > + double old_percent = get_period_percent(he, he->stat.period); > + double new_percent = get_period_percent(pair, pair->stat.period); > > - he->diff.period_ratio_delta = new_percent - old_percent; > - he->diff.computed = true; > - return he->diff.period_ratio_delta; > + pair->diff.period_ratio_delta = new_percent - old_percent; > + pair->diff.computed = true; > + return pair->diff.period_ratio_delta; > } > > double perf_diff__compute_ratio(struct hist_entry *he, struct hist_entry *pair) > { > - double new_period = he->stat.period; > - double old_period = pair->stat.period; > + double old_period = he->stat.period ?: 1; > + double new_period = pair->stat.period; > > - he->diff.computed = true; > - he->diff.period_ratio = new_period / old_period; > - return he->diff.period_ratio; > + pair->diff.computed = true; > + pair->diff.period_ratio = (new_period / old_period); > + return pair->diff.period_ratio; > } > > s64 perf_diff__compute_wdiff(struct hist_entry *he, struct hist_entry *pair) > { > - u64 new_period = he->stat.period; > - u64 old_period = pair->stat.period; > + u64 old_period = he->stat.period; > + u64 new_period = pair->stat.period; > > - he->diff.computed = true; > - he->diff.wdiff = new_period * compute_wdiff_w2 - > - old_period * compute_wdiff_w1; > + pair->diff.computed = true; > + pair->diff.wdiff = new_period * compute_wdiff_w1 - > + old_period * compute_wdiff_w2; > > - return he->diff.wdiff; > + return pair->diff.wdiff; > } > > static int formula_delta(struct hist_entry *he, struct hist_entry *pair, > @@ -197,8 +278,8 @@ static int formula_delta(struct hist_entry *he, struct hist_entry *pair, > static int formula_ratio(struct hist_entry *he, struct hist_entry *pair, > char *buf, size_t size) > { > - double new_period = he->stat.period; > - double old_period = pair->stat.period; > + double old_period = he->stat.period; > + double new_period = pair->stat.period; > > return scnprintf(buf, size, "%.0F / %.0F", new_period, old_period); > } > @@ -206,12 +287,12 @@ static int formula_ratio(struct hist_entry *he, struct hist_entry *pair, > static int formula_wdiff(struct hist_entry *he, struct hist_entry *pair, > char *buf, size_t size) > { > - u64 new_period = he->stat.period; > - u64 old_period = pair->stat.period; > + u64 old_period = he->stat.period; > + u64 new_period = pair->stat.period; > > return scnprintf(buf, size, > "(%" PRIu64 " * " "%" PRId64 ") - (%" PRIu64 " * " "%" PRId64 ")", > - new_period, compute_wdiff_w2, old_period, compute_wdiff_w1); > + new_period, compute_wdiff_w1, old_period, compute_wdiff_w2); > } > > int perf_diff__formula(struct hist_entry *he, struct hist_entry *pair, > @@ -356,22 +437,21 @@ static void hists__baseline_only(struct hists *hists) > struct hist_entry *he = rb_entry(next, struct hist_entry, rb_node); > > next = rb_next(&he->rb_node); > - if (!hist_entry__next_pair(he)) { > + > + if (!he->pairs) { > rb_erase(&he->rb_node, &hists->entries); > hist_entry__free(he); > } > } > } > > -static void hists__precompute(struct hists *hists) > +static void __hists__precompute(struct hist_entry *he) > { > - struct rb_node *next = rb_first(&hists->entries); > + int i; > > - while (next != NULL) { > - struct hist_entry *he = rb_entry(next, struct hist_entry, rb_node); > - struct hist_entry *pair = hist_entry__next_pair(he); > + for (i = 0; i < data_cnt; i++) { > + struct hist_entry *pair = he->pairs[i]; > > - next = rb_next(&he->rb_node); > if (!pair) > continue; > > @@ -391,6 +471,21 @@ static void hists__precompute(struct hists *hists) > } > } > > +static void hists__precompute(struct hists *hists) > +{ > + struct rb_node *next = rb_first(&hists->entries); > + > + while (next != NULL) { > + struct hist_entry *he = rb_entry(next, struct hist_entry, > + rb_node); > + > + next = rb_next(&he->rb_node); > + > + if (he->pairs) > + __hists__precompute(he); > + } > +} > + > static int64_t cmp_doubles(double l, double r) > { > if (l > r) > @@ -402,8 +497,8 @@ static int64_t cmp_doubles(double l, double r) > } > > static int64_t > -hist_entry__cmp_compute(struct hist_entry *left, struct hist_entry *right, > - int c) > +__hist_entry__cmp_compute(struct hist_entry *left, struct hist_entry *right, > + int c) > { > switch (c) { > case COMPUTE_DELTA: > @@ -434,6 +529,39 @@ hist_entry__cmp_compute(struct hist_entry *left, struct hist_entry *right, > return 0; > } > > +static int64_t > +hist_entry__cmp_compute(struct hist_entry *left, struct hist_entry *right, > + int c) > +{ > + int i; > + > + for (i = 0; i < data_cnt; i++) { > + struct hist_entry **pairs_left = left->pairs; > + struct hist_entry **pairs_right = right->pairs; > + struct hist_entry *p_right, *p_left; > + static int64_t cmp; > + > + if (!pairs_left || !pairs_right) > + return pairs_right - pairs_left; > + > + p_right = pairs_right[i]; > + p_left = pairs_left[i]; > + > + if (!p_left || !p_right) > + return p_right - p_left; > + > + /* > + * If we differ, we are done, otherwise continue until all > + * is processed or we find a difference. > + */ > + cmp = __hist_entry__cmp_compute(p_left, p_right, c); > + if (cmp) > + return cmp; > + } > + > + return 0; > +} > + > static void insert_hist_entry_by_compute(struct rb_root *root, > struct hist_entry *he, > int c) > @@ -473,83 +601,112 @@ static void hists__compute_resort(struct hists *hists) > } > > static int match_cb(struct hist_entry *a, struct hist_entry *b, > - void *data __maybe_unused) > + void *_data) > { > - hist__entry_add_pair(a, b); > + intptr_t i = (intptr_t) _data; > + struct hist_entry **p; > + > + if (!a->pairs) { > + p = zalloc(sizeof(p) * data_cnt); > + if (!p) > + return -ENOMEM; > + > + a->pairs = p; > + } > + > + a->pairs[i] = b; > + b->paired = true; > return 0; > } > > -static void hists__process(struct hists *old, struct hists *new) > +static void hists__process(struct hists *hists) > { > - hists__match(new, old, match_cb, NULL); > - > if (show_baseline_only) > - hists__baseline_only(new); > - else > - hists__link(new, old, match_cb, NULL); > + hists__baseline_only(hists); > > if (sort_compute) { > - hists__precompute(new); > - hists__compute_resort(new); > + hists__precompute(hists); > + hists__compute_resort(hists); > } > > - hists__fprintf(new, true, 0, 0, stdout); > + hists__fprintf(hists, true, 0, 0, stdout); > } > > -static int __cmd_diff(void) > +static int data_process(void) > { > - int ret, i; > -#define older (session[0]) > -#define newer (session[1]) > - struct perf_session *session[2]; > - struct perf_evlist *evlist_new, *evlist_old; > - struct perf_evsel *evsel; > + struct perf_evlist *evlist_base = data[0].session->evlist; > + struct perf_evsel *evsel_base; > bool first = true; > > - older = perf_session__new(input_old, O_RDONLY, force, false, > - &tool); > - newer = perf_session__new(input_new, O_RDONLY, force, false, > - &tool); > - if (session[0] == NULL || session[1] == NULL) > - return -ENOMEM; > + list_for_each_entry(evsel_base, &evlist_base->entries, node) { > + struct diff_data *d; > + int i; > > - for (i = 0; i < 2; ++i) { > - ret = perf_session__process_events(session[i], &tool); > - if (ret) > - goto out_delete; > - } > - > - evlist_old = older->evlist; > - evlist_new = newer->evlist; > + for_each_data_new(i, d) { > + struct perf_evlist *evlist = d->session->evlist; > + struct perf_evsel *evsel_new; > > - perf_evlist__resort_hists(evlist_old, true); > - perf_evlist__resort_hists(evlist_new, false); > + evsel_new = evsel_match(evsel_base, evlist); > + if (!evsel_new) > + continue; > > - list_for_each_entry(evsel, &evlist_new->entries, node) { > - struct perf_evsel *evsel_old; > + hists__match(&evsel_base->hists, &evsel_new->hists, > + match_cb, (void *) (intptr_t) i); > > - evsel_old = evsel_match(evsel, evlist_old); > - if (!evsel_old) > - continue; > + if (!show_baseline_only) > + hists__link(&evsel_base->hists, > + &evsel_new->hists, > + match_cb, (void *) (intptr_t) i); > + } > > fprintf(stdout, "%s# Event '%s'\n#\n", first ? "" : "\n", > - perf_evsel__name(evsel)); > + perf_evsel__name(evsel_base)); > + > + hists__process(&evsel_base->hists); > > first = false; > + } > + > + return 0; > +} > + > +static int __cmd_diff(void) > +{ > + struct diff_data *d; > + int ret = -EINVAL, i; > + > + for_each_data(i, d) { > + d->session = perf_session__new(d->file, O_RDONLY, force, > + false, &tool); > + if (!d->session) { > + pr_err("Failed to open %s\n", d->file); > + ret = -ENOMEM; > + goto out_delete; > + } > > - hists__process(&evsel_old->hists, &evsel->hists); > + ret = perf_session__process_events(d->session, &tool); > + if (ret) { > + pr_err("Failed to process %s\n", d->file); > + goto out_delete; > + } > + > + /* Sort by name all but baseline. */ > + perf_evlist__resort_hists(d->session->evlist, i != 0); > } > > + ret = data_process(); > + > out_delete: > - for (i = 0; i < 2; ++i) > - perf_session__delete(session[i]); > + for_each_data(i, d) { > + if (d->session) > + perf_session__delete(d->session); > + } > + > return ret; > -#undef older > -#undef newer > } > > static const char * const diff_usage[] = { > - "perf diff [<options>] [old_file] [new_file]", > + "perf diff [<options>] <base file> <file1> [file2] ...", > NULL, > }; > > @@ -589,62 +746,340 @@ static const struct option options[] = { > OPT_END() > }; > > -static void ui_init(void) > +static double baseline_percent(struct hist_entry *he) > { > - /* > - * Display baseline/delta/ratio/displacement/ > - * formula/periods columns. > - */ > - perf_hpp__column_enable(PERF_HPP__BASELINE); > + struct hists *hists = he->hists; > + u64 total_period = hists->stats.total_period; > + u64 base_period = he->stat.period; > > - switch (compute) { > - case COMPUTE_DELTA: > - perf_hpp__column_enable(PERF_HPP__DELTA); > + return 100.0 * base_period / total_period; > +} > + > +static int hpp__color_baseline(struct perf_hpp_fmt *fmt __maybe_unused, > + struct perf_hpp *hpp, struct hist_entry *he) > +{ > + double percent = baseline_percent(he); > + struct diff_data__fmt *dfmt = > + container_of(fmt, struct diff_data__fmt, fmt); > + char pfmt[20] = " "; > + > + if (percent) { > + scnprintf(pfmt, 20, "%%%d.2f%%%%", dfmt->header_width - 1); > + return percent_color_snprintf(hpp->buf, hpp->size, > + pfmt, percent); > + } else > + return scnprintf(hpp->buf, hpp->size, "%*s", > + dfmt->header_width, pfmt); > +} > + > +static int hpp__entry_baseline(struct perf_hpp_fmt *_fmt, struct perf_hpp *hpp, > + struct hist_entry *he) > +{ > + struct diff_data__fmt *dfmt = > + container_of(_fmt, struct diff_data__fmt, fmt); > + double percent = baseline_percent(he); > + const char *fmt = symbol_conf.field_sep ? "%.2f" : " %6.2f%%"; > + char buf[32] = " "; > + > + if ((percent && he->pairs) || symbol_conf.field_sep) > + return scnprintf(hpp->buf, hpp->size, fmt, percent); > + else > + return scnprintf(hpp->buf, hpp->size, "%*s", > + dfmt->header_width, buf); > +} > + > +static struct hist_entry *get_pair(struct hist_entry *he, > + struct perf_hpp_fmt *fmt) > +{ > + struct hist_entry *pair = NULL; > + > + if (he->pairs) { > + struct diff_data *d; > + struct diff_data__fmt *dfmt; > + void *ptr; > + > + dfmt = container_of(fmt, struct diff_data__fmt, fmt); > + ptr = dfmt - dfmt->idx; > + d = container_of(ptr, struct diff_data, fmt); > + pair = he->pairs[d->idx]; > + } > + > + return pair; > +} > + > +static void > +hpp__entry_unpair(struct hist_entry *he, int idx, char *buf, size_t size) > +{ > + switch (idx) { > + case PERF_HPP_DIFF__PERIOD_BASELINE: > + scnprintf(buf, size, "%" PRIu64, he->stat.period); > break; > - case COMPUTE_RATIO: > - perf_hpp__column_enable(PERF_HPP__RATIO); > + > + default: > break; > - case COMPUTE_WEIGHTED_DIFF: > - perf_hpp__column_enable(PERF_HPP__WEIGHTED_DIFF); > + } > +} > + > +static void > +hpp__entry_pair(struct hist_entry *he, struct hist_entry *pair, > + int idx, char *buf, size_t size) > +{ > + long displacement; > + double diff; > + double ratio; > + s64 wdiff; > + > + switch (idx) { > + case PERF_HPP_DIFF__DELTA: > + if (pair->diff.computed) > + diff = pair->diff.period_ratio_delta; > + else > + diff = perf_diff__compute_delta(he, pair); > + > + if (fabs(diff) >= 0.01) > + scnprintf(buf, size, "%+4.2F%%", diff); > + break; > + > + case PERF_HPP_DIFF__RATIO: > + if (pair->diff.computed) > + ratio = pair->diff.period_ratio; > + else > + ratio = perf_diff__compute_ratio(he, pair); > + > + if (ratio > 0.0) > + scnprintf(buf, size, "%14.6F", ratio); > + break; > + > + case PERF_HPP_DIFF__WEIGHTED_DIFF: > + if (pair->diff.computed) > + wdiff = pair->diff.wdiff; > + else > + wdiff = perf_diff__compute_wdiff(he, pair); > + > + if (wdiff != 0) > + scnprintf(buf, size, "%14ld", wdiff); > + break; > + > + case PERF_HPP_DIFF__DISPL: > + displacement = pair->position - he->position; > + > + if (displacement) > + scnprintf(buf, size, "%+4ld", displacement); > break; > + > + case PERF_HPP_DIFF__FORMULA: > + perf_diff__formula(he, pair, buf, size); > + break; > + > + case PERF_HPP_DIFF__PERIOD: > + scnprintf(buf, size, "%" PRIu64, pair->stat.period); > + break; > + > default: > BUG_ON(1); > - }; > + } > +} > + > +static int hpp__entry_global(struct perf_hpp_fmt *_fmt, struct perf_hpp *hpp, > + struct hist_entry *he) > +{ > + struct diff_data__fmt *dfmt = > + container_of(_fmt, struct diff_data__fmt, fmt); > + struct hist_entry *pair; > + char buf[MAX_COL_WIDTH] = " "; > + > + pair = get_pair(he, _fmt); > + > + if (pair) > + hpp__entry_pair(he, pair, dfmt->idx, buf, MAX_COL_WIDTH); > + else > + hpp__entry_unpair(he, dfmt->idx, buf, MAX_COL_WIDTH); > + > + if (symbol_conf.field_sep) > + return scnprintf(hpp->buf, hpp->size, "%s", buf); > + else > + return scnprintf(hpp->buf, hpp->size, "%*s", > + dfmt->header_width, buf); > +} > + > +static int diff_hpp__header(struct perf_hpp_fmt *fmt, > + struct perf_hpp *hpp) > +{ > + struct diff_data__fmt *dfmt = > + container_of(fmt, struct diff_data__fmt, fmt); > + > + BUG_ON(!dfmt->header); > + > + return scnprintf(hpp->buf, hpp->size, dfmt->header); > +} > + > +static int diff_hpp__width(struct perf_hpp_fmt *fmt, > + struct perf_hpp *hpp __maybe_unused) > +{ > + struct diff_data__fmt *dfmt = > + container_of(fmt, struct diff_data__fmt, fmt); > + > + BUG_ON(dfmt->header_width <= 0); > + > + return dfmt->header_width; > +} > > - if (show_displacement) > - perf_hpp__column_enable(PERF_HPP__DISPL); > +static void init_header(struct diff_data *d, struct diff_data__fmt *dfmt) > +{ > +#define MAX_HEADER_NAME 100 > + char buf_indent[MAX_HEADER_NAME]; > + char buf[MAX_HEADER_NAME]; > + const char *header = NULL; > + int width = 0; > + > + BUG_ON(dfmt->idx >= PERF_HPP_DIFF__MAX_INDEX); > + header = columns[dfmt->idx].name; > + width = columns[dfmt->idx].width; > + > + /* Only our defined HPP fmts should appear here. */ > + BUG_ON(!header); > + > + if (data_cnt > 2) > + scnprintf(buf, MAX_HEADER_NAME, "%s/%d", header, d->idx); > + > +#define NAME (data_cnt > 2 ? buf : header) > + dfmt->header_width = width; > + width = (int) strlen(NAME); > + if (dfmt->header_width < width) > + dfmt->header_width = width; > + > + scnprintf(buf_indent, MAX_HEADER_NAME, "%*s", > + dfmt->header_width, NAME); > + > + dfmt->header = strdup(buf_indent); > +#undef MAX_HEADER_NAME > +#undef NAME > +} > > - if (show_formula) > - perf_hpp__column_enable(PERF_HPP__FORMULA); > +static void data__hpp_register(struct diff_data *d, int idx) > +{ > + struct diff_data__fmt *dfmt = &d->fmt[idx]; > + struct perf_hpp_fmt *fmt = &dfmt->fmt; > > - if (show_period) { > - perf_hpp__column_enable(PERF_HPP__PERIOD); > - perf_hpp__column_enable(PERF_HPP__PERIOD_BASELINE); > + dfmt->idx = idx; > + > + fmt->header = diff_hpp__header; > + fmt->width = diff_hpp__width; > + > + switch (idx) { > +#define CASE_COLOR(c, name) \ > + case c: \ > + fmt->color = hpp__color_##name; \ > + fmt->entry = hpp__entry_##name; \ > + break; > + > +#define CASE(c, name) \ > + case c: \ > + fmt->entry = hpp__entry_##name; \ > + break; > + > + CASE_COLOR(PERF_HPP_DIFF__BASELINE, baseline) > + CASE(PERF_HPP_DIFF__PERIOD_BASELINE, global) > + CASE(PERF_HPP_DIFF__DELTA, global) > + CASE(PERF_HPP_DIFF__RATIO, global) > + CASE(PERF_HPP_DIFF__WEIGHTED_DIFF, global) > + CASE(PERF_HPP_DIFF__DISPL, global) > + CASE(PERF_HPP_DIFF__FORMULA, global) > + CASE(PERF_HPP_DIFF__PERIOD, global) > + > + default: > + BUG_ON(1); > } > + > + perf_hpp__column_register(fmt); > + > + init_header(d, dfmt); > } > > -int cmd_diff(int argc, const char **argv, const char *prefix __maybe_unused) > +static void ui_init(void) > { > - sort_order = diff__default_sort_order; > - argc = parse_options(argc, argv, options, diff_usage, 0); > + struct diff_data *d; > + int i; > + > + for_each_data(i, d) { > + > + /* > + * Baseline or compute realted columns: > + * > + * PERF_HPP_DIFF__BASELINE > + * PERF_HPP_DIFF__DELTA > + * PERF_HPP_DIFF__RATIO > + * PERF_HPP_DIFF__WEIGHTED_DIFF > + */ > + data__hpp_register(d, i ? compute_2_hpp[compute] : > + PERF_HPP_DIFF__BASELINE); > + > + /* > + * And the rest: > + * > + * PERF_HPP_DIFF__DISPL > + * PERF_HPP_DIFF__FORMULA > + * PERF_HPP_DIFF__PERIOD > + * PERF_HPP_DIFF__PERIOD_BASELINE > + */ > + if (show_displacement && i) > + data__hpp_register(d, PERF_HPP_DIFF__DISPL); > + > + if (show_formula && i) > + data__hpp_register(d, PERF_HPP_DIFF__FORMULA); > + > + if (show_period) > + data__hpp_register(d, i ? PERF_HPP_DIFF__PERIOD : > + PERF_HPP_DIFF__PERIOD_BASELINE); > + } > +} > + > +static int data_init(int argc, const char **argv) > +{ > + struct diff_data *d; > + static const char *defaults[] = { > + "perf.data.old", > + "perf.data", > + }; > + int i; > + > + if (argc == 1) > + usage_with_options(diff_usage, options); > + > + data_cnt = 2; > + > if (argc) { > - if (argc > 2) > - usage_with_options(diff_usage, options); > - if (argc == 2) { > - input_old = argv[0]; > - input_new = argv[1]; > - } else > - input_new = argv[0]; > + data_cnt = argc; > } else if (symbol_conf.default_guest_vmlinux_name || > symbol_conf.default_guest_kallsyms) { > - input_old = "perf.data.host"; > - input_new = "perf.data.guest"; > + defaults[0] = "perf.data.host"; > + defaults[1] = "perf.data.guest"; > } > > + data = zalloc(sizeof(*data) * data_cnt); > + if (!data) > + return -ENOMEM; > + > + for_each_data(i, d) { > + d->file = argc ? argv[i] : defaults[i]; > + d->idx = i; > + } > + > + return 0; > +} > + > +int cmd_diff(int argc, const char **argv, const char *prefix __maybe_unused) > +{ > + sort_order = diff__default_sort_order; > + argc = parse_options(argc, argv, options, diff_usage, 0); > symbol_conf.exclude_other = false; > + > if (symbol__init() < 0) > return -1; > > + if (data_init(argc, argv) < 0) > + return -1; > + > ui_init(); > > setup_sorting(diff_usage, options); > diff --git a/tools/perf/ui/hist.c b/tools/perf/ui/hist.c > index d6fdb00..a004f57 100644 > --- a/tools/perf/ui/hist.c > +++ b/tools/perf/ui/hist.c > @@ -177,57 +177,6 @@ hpp__entry_overhead_guest_us(struct perf_hpp_fmt *_fmt __maybe_unused, > return scnprintf(hpp->buf, hpp->size, fmt, percent); > } > > -static int hpp__header_baseline(struct perf_hpp_fmt *fmt __maybe_unused, > - struct perf_hpp *hpp) > -{ > - return scnprintf(hpp->buf, hpp->size, "Baseline"); > -} > - > -static int hpp__width_baseline(struct perf_hpp_fmt *fmt __maybe_unused, > - struct perf_hpp *hpp __maybe_unused) > -{ > - return 8; > -} > - > -static double baseline_percent(struct hist_entry *he) > -{ > - struct hist_entry *pair = hist_entry__next_pair(he); > - struct hists *pair_hists = pair ? pair->hists : NULL; > - double percent = 0.0; > - > - if (pair) { > - u64 total_period = pair_hists->stats.total_period; > - u64 base_period = pair->stat.period; > - > - percent = 100.0 * base_period / total_period; > - } > - > - return percent; > -} > - > -static int hpp__color_baseline(struct perf_hpp_fmt *fmt __maybe_unused, > - struct perf_hpp *hpp, struct hist_entry *he) > -{ > - double percent = baseline_percent(he); > - > - if (hist_entry__has_pairs(he)) > - return percent_color_snprintf(hpp->buf, hpp->size, " %6.2f%%", percent); > - else > - return scnprintf(hpp->buf, hpp->size, " "); > -} > - > -static int hpp__entry_baseline(struct perf_hpp_fmt *_fmt __maybe_unused, > - struct perf_hpp *hpp, struct hist_entry *he) > -{ > - double percent = baseline_percent(he); > - const char *fmt = symbol_conf.field_sep ? "%.2f" : " %6.2f%%"; > - > - if (hist_entry__has_pairs(he) || symbol_conf.field_sep) > - return scnprintf(hpp->buf, hpp->size, fmt, percent); > - else > - return scnprintf(hpp->buf, hpp->size, " "); > -} > - > static int hpp__header_samples(struct perf_hpp_fmt *_fmt __maybe_unused, > struct perf_hpp *hpp) > { > @@ -272,190 +221,6 @@ static int hpp__entry_period(struct perf_hpp_fmt *_fmt __maybe_unused, > return scnprintf(hpp->buf, hpp->size, fmt, he->stat.period); > } > > -static int hpp__header_period_baseline(struct perf_hpp_fmt *_fmt __maybe_unused, > - struct perf_hpp *hpp) > -{ > - const char *fmt = symbol_conf.field_sep ? "%s" : "%12s"; > - > - return scnprintf(hpp->buf, hpp->size, fmt, "Period Base"); > -} > - > -static int hpp__width_period_baseline(struct perf_hpp_fmt *fmt __maybe_unused, > - struct perf_hpp *hpp __maybe_unused) > -{ > - return 12; > -} > - > -static int hpp__entry_period_baseline(struct perf_hpp_fmt *_fmt __maybe_unused, > - struct perf_hpp *hpp, > - struct hist_entry *he) > -{ > - struct hist_entry *pair = hist_entry__next_pair(he); > - u64 period = pair ? pair->stat.period : 0; > - const char *fmt = symbol_conf.field_sep ? "%" PRIu64 : "%12" PRIu64; > - > - return scnprintf(hpp->buf, hpp->size, fmt, period); > -} > - > -static int hpp__header_delta(struct perf_hpp_fmt *_fmt __maybe_unused, > - struct perf_hpp *hpp) > -{ > - const char *fmt = symbol_conf.field_sep ? "%s" : "%7s"; > - > - return scnprintf(hpp->buf, hpp->size, fmt, "Delta"); > -} > - > -static int hpp__width_delta(struct perf_hpp_fmt *fmt __maybe_unused, > - struct perf_hpp *hpp __maybe_unused) > -{ > - return 7; > -} > - > -static int hpp__entry_delta(struct perf_hpp_fmt *_fmt __maybe_unused, > - struct perf_hpp *hpp, struct hist_entry *he) > -{ > - struct hist_entry *pair = hist_entry__next_pair(he); > - const char *fmt = symbol_conf.field_sep ? "%s" : "%7.7s"; > - char buf[32] = " "; > - double diff = 0.0; > - > - if (pair) { > - if (he->diff.computed) > - diff = he->diff.period_ratio_delta; > - else > - diff = perf_diff__compute_delta(he, pair); > - } else > - diff = perf_diff__period_percent(he, he->stat.period); > - > - if (fabs(diff) >= 0.01) > - scnprintf(buf, sizeof(buf), "%+4.2F%%", diff); > - > - return scnprintf(hpp->buf, hpp->size, fmt, buf); > -} > - > -static int hpp__header_ratio(struct perf_hpp_fmt *_fmt __maybe_unused, > - struct perf_hpp *hpp) > -{ > - const char *fmt = symbol_conf.field_sep ? "%s" : "%14s"; > - > - return scnprintf(hpp->buf, hpp->size, fmt, "Ratio"); > -} > - > -static int hpp__width_ratio(struct perf_hpp_fmt *_fmt __maybe_unused, > - struct perf_hpp *hpp __maybe_unused) > -{ > - return 14; > -} > - > -static int hpp__entry_ratio(struct perf_hpp_fmt *_fmt __maybe_unused, > - struct perf_hpp *hpp, struct hist_entry *he) > -{ > - struct hist_entry *pair = hist_entry__next_pair(he); > - const char *fmt = symbol_conf.field_sep ? "%s" : "%14s"; > - char buf[32] = " "; > - double ratio = 0.0; > - > - if (pair) { > - if (he->diff.computed) > - ratio = he->diff.period_ratio; > - else > - ratio = perf_diff__compute_ratio(he, pair); > - } > - > - if (ratio > 0.0) > - scnprintf(buf, sizeof(buf), "%+14.6F", ratio); > - > - return scnprintf(hpp->buf, hpp->size, fmt, buf); > -} > - > -static int hpp__header_wdiff(struct perf_hpp_fmt *_fmt __maybe_unused, > - struct perf_hpp *hpp) > -{ > - const char *fmt = symbol_conf.field_sep ? "%s" : "%14s"; > - > - return scnprintf(hpp->buf, hpp->size, fmt, "Weighted diff"); > -} > - > -static int hpp__width_wdiff(struct perf_hpp_fmt *fmt __maybe_unused, > - struct perf_hpp *hpp __maybe_unused) > -{ > - return 14; > -} > - > -static int hpp__entry_wdiff(struct perf_hpp_fmt *_fmt __maybe_unused, > - struct perf_hpp *hpp, struct hist_entry *he) > -{ > - struct hist_entry *pair = hist_entry__next_pair(he); > - const char *fmt = symbol_conf.field_sep ? "%s" : "%14s"; > - char buf[32] = " "; > - s64 wdiff = 0; > - > - if (pair) { > - if (he->diff.computed) > - wdiff = he->diff.wdiff; > - else > - wdiff = perf_diff__compute_wdiff(he, pair); > - } > - > - if (wdiff != 0) > - scnprintf(buf, sizeof(buf), "%14ld", wdiff); > - > - return scnprintf(hpp->buf, hpp->size, fmt, buf); > -} > - > -static int hpp__header_displ(struct perf_hpp_fmt *_fmt __maybe_unused, > - struct perf_hpp *hpp) > -{ > - return scnprintf(hpp->buf, hpp->size, "Displ."); > -} > - > -static int hpp__width_displ(struct perf_hpp_fmt *fmt __maybe_unused, > - struct perf_hpp *hpp __maybe_unused) > -{ > - return 6; > -} > - > -static int hpp__entry_displ(struct perf_hpp_fmt *_fmt __maybe_unused, > - struct perf_hpp *hpp, struct hist_entry *he) > -{ > - struct hist_entry *pair = hist_entry__next_pair(he); > - long displacement = pair ? pair->position - he->position : 0; > - const char *fmt = symbol_conf.field_sep ? "%s" : "%6.6s"; > - char buf[32] = " "; > - > - if (displacement) > - scnprintf(buf, sizeof(buf), "%+4ld", displacement); > - > - return scnprintf(hpp->buf, hpp->size, fmt, buf); > -} > - > -static int hpp__header_formula(struct perf_hpp_fmt *_fmt __maybe_unused, > - struct perf_hpp *hpp) > -{ > - const char *fmt = symbol_conf.field_sep ? "%s" : "%70s"; > - > - return scnprintf(hpp->buf, hpp->size, fmt, "Formula"); > -} > - > -static int hpp__width_formula(struct perf_hpp_fmt *fmt __maybe_unused, > - struct perf_hpp *hpp __maybe_unused) > -{ > - return 70; > -} > - > -static int hpp__entry_formula(struct perf_hpp_fmt *_fmt __maybe_unused, > - struct perf_hpp *hpp, struct hist_entry *he) > -{ > - struct hist_entry *pair = hist_entry__next_pair(he); > - const char *fmt = symbol_conf.field_sep ? "%s" : "%-70s"; > - char buf[96] = " "; > - > - if (pair) > - perf_diff__formula(he, pair, buf, sizeof(buf)); > - > - return scnprintf(hpp->buf, hpp->size, fmt, buf); > -} > - > #define HPP__COLOR_PRINT_FNS(_name) \ > { \ > .header = hpp__header_ ## _name, \ > @@ -472,7 +237,6 @@ static int hpp__entry_formula(struct perf_hpp_fmt *_fmt __maybe_unused, > } > > struct perf_hpp_fmt perf_hpp__format[] = { > - HPP__COLOR_PRINT_FNS(baseline), > HPP__COLOR_PRINT_FNS(overhead), > HPP__COLOR_PRINT_FNS(overhead_sys), > HPP__COLOR_PRINT_FNS(overhead_us), > @@ -480,12 +244,6 @@ struct perf_hpp_fmt perf_hpp__format[] = { > HPP__COLOR_PRINT_FNS(overhead_guest_us), > HPP__PRINT_FNS(samples), > HPP__PRINT_FNS(period), > - HPP__PRINT_FNS(period_baseline), > - HPP__PRINT_FNS(delta), > - HPP__PRINT_FNS(ratio), > - HPP__PRINT_FNS(wdiff), > - HPP__PRINT_FNS(displ), > - HPP__PRINT_FNS(formula) > }; > > LIST_HEAD(perf_hpp__list); > diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c > index 0c5843b..25f94a4 100644 > --- a/tools/perf/util/hist.c > +++ b/tools/perf/util/hist.c > @@ -244,8 +244,6 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template) > he->ms.map->referenced = true; > if (symbol_conf.use_callchain) > callchain_init(he->callchain); > - > - INIT_LIST_HEAD(&he->pairs.node); > } > > return he; > @@ -812,11 +810,11 @@ int hists__link(struct hists *leader, struct hists *other, > for (nd = rb_first(&other->entries); nd && !ret; nd = rb_next(nd)) { > pos = rb_entry(nd, struct hist_entry, rb_node); > > - if (!hist_entry__has_pairs(pos)) { > + if (!pos->paired) { > pair = hists__add_dummy_entry(leader, pos); > if (pair == NULL) > return -1; > - ret = cb(pos, pair, data); > + ret = cb(pair, pos, data); > } > } > > diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h > index eb4dc83..d4f19eb 100644 > --- a/tools/perf/util/hist.h > +++ b/tools/perf/util/hist.h > @@ -148,7 +148,7 @@ extern struct list_head perf_hpp__list; > extern struct perf_hpp_fmt perf_hpp__format[]; > > enum { > - PERF_HPP__BASELINE, > + /* Matches perf_hpp__format array. */ > PERF_HPP__OVERHEAD, > PERF_HPP__OVERHEAD_SYS, > PERF_HPP__OVERHEAD_US, > @@ -156,12 +156,6 @@ enum { > PERF_HPP__OVERHEAD_GUEST_US, > PERF_HPP__SAMPLES, > PERF_HPP__PERIOD, > - PERF_HPP__PERIOD_BASELINE, > - PERF_HPP__DELTA, > - PERF_HPP__RATIO, > - PERF_HPP__WEIGHTED_DIFF, > - PERF_HPP__DISPL, > - PERF_HPP__FORMULA, > > PERF_HPP__MAX_INDEX > }; > @@ -237,5 +231,4 @@ double perf_diff__compute_ratio(struct hist_entry *he, struct hist_entry *pair); > s64 perf_diff__compute_wdiff(struct hist_entry *he, struct hist_entry *pair); > int perf_diff__formula(struct hist_entry *he, struct hist_entry *pair, > char *buf, size_t size); > -double perf_diff__period_percent(struct hist_entry *he, u64 period); > #endif /* __PERF_HIST_H */ > diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h > index a884be2..377b144 100644 > --- a/tools/perf/util/sort.h > +++ b/tools/perf/util/sort.h > @@ -74,18 +74,12 @@ struct hist_entry_diff { > struct hist_entry { > struct rb_node rb_node_in; > struct rb_node rb_node; > - union { > - struct list_head node; > - struct list_head head; > - } pairs; > struct he_stat stat; > struct map_symbol ms; > struct thread *thread; > u64 ip; > s32 cpu; > > - struct hist_entry_diff diff; > - > /* XXX These two should move to some tree widget lib */ > u16 row_offset; > u16 nr_rows; > @@ -98,29 +92,19 @@ struct hist_entry { > struct symbol *parent; > unsigned long position; > struct rb_root sorted_chain; > + > + /* diff related */ > + union { > + struct hist_entry **pairs; > + bool paired; > + }; > + struct hist_entry_diff diff; > + > struct branch_info *branch_info; > struct hists *hists; > struct callchain_root callchain[0]; > }; > > -static inline bool hist_entry__has_pairs(struct hist_entry *he) > -{ > - return !list_empty(&he->pairs.node); > -} > - > -static inline struct hist_entry *hist_entry__next_pair(struct hist_entry *he) > -{ > - if (hist_entry__has_pairs(he)) > - return list_entry(he->pairs.node.next, struct hist_entry, pairs.node); > - return NULL; > -} > - > -static inline void hist__entry_add_pair(struct hist_entry *he, > - struct hist_entry *pair) > -{ > - list_add_tail(&he->pairs.head, &pair->pairs.node); > -} > - > enum sort_type { > SORT_PID, > SORT_COMM,
| |