perf diff: Move diff related columns into diff command
[deliverable/linux.git] / tools / perf / builtin-diff.c
index 2d0462d89a972ffde8d1ef24e1c9262c45cb3ac7..8734f1cee6dc7ed4d2118122cbab4af8d15c3ee8 100644 (file)
 #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__FORMULA,
+
+       PERF_HPP_DIFF__MAX_INDEX
+};
+
+struct diff_hpp_fmt {
+       struct perf_hpp_fmt      fmt;
+       int                      idx;
+       char                    *header;
+       int                      header_width;
+};
+
+struct data__file {
+       struct perf_session     *session;
+       const char              *file;
+       int                      idx;
+};
+
+static struct data__file *data__files;
+static int data__files_cnt;
+
+#define data__for_each_file_start(i, d, s)     \
+       for (i = s, d = &data__files[s];        \
+            i < data__files_cnt;               \
+            i++, d = &data__files[i])
+
+#define data__for_each_file(i, d) data__for_each_file_start(i, d, 0)
+
+static char diff__default_sort_order[] = "dso,symbol";
+static bool force;
 static bool show_period;
 static bool show_formula;
 static bool show_baseline_only;
@@ -46,6 +81,47 @@ const char *compute_names[COMPUTE_MAX] = {
 
 static int compute;
 
+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,
+};
+
+#define MAX_COL_WIDTH 70
+
+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",
+               .width = 14,
+       },
+       [PERF_HPP_DIFF__PERIOD_BASELINE] = {
+               .name  = "Base period",
+               .width = 14,
+       },
+       [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__FORMULA] = {
+               .name  = "Formula",
+               .width = MAX_COL_WIDTH,
+       }
+};
+
 static int setup_compute_opt_wdiff(char *opt)
 {
        char *w1_str = opt;
@@ -153,34 +229,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 = perf_diff__period_percent(he, he->stat.period);
+       double new_percent = perf_diff__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_w2 -
+                          old_period * compute_wdiff_w1;
 
-       return he->diff.wdiff;
+       return pair->diff.wdiff;
 }
 
 static int formula_delta(struct hist_entry *he, struct hist_entry *pair,
@@ -189,15 +265,15 @@ static int formula_delta(struct hist_entry *he, struct hist_entry *pair,
        return scnprintf(buf, size,
                         "(%" PRIu64 " * 100 / %" PRIu64 ") - "
                         "(%" PRIu64 " * 100 / %" PRIu64 ")",
-                         he->stat.period, he->hists->stats.total_period,
-                         pair->stat.period, pair->hists->stats.total_period);
+                         pair->stat.period, pair->hists->stats.total_period,
+                         he->stat.period, he->hists->stats.total_period);
 }
 
 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);
 }
@@ -205,8 +281,8 @@ 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 ")",
@@ -323,13 +399,20 @@ static void hists__baseline_only(struct hists *hists)
 
 static void hists__precompute(struct hists *hists)
 {
-       struct rb_node *next = rb_first(&hists->entries);
+       struct rb_root *root;
+       struct rb_node *next;
+
+       if (sort__need_collapse)
+               root = &hists->entries_collapsed;
+       else
+               root = hists->entries_in;
 
+       next = rb_first(root);
        while (next != NULL) {
-               struct hist_entry *he = rb_entry(next, struct hist_entry, rb_node);
+               struct hist_entry *he = rb_entry(next, struct hist_entry, rb_node_in);
                struct hist_entry *pair = hist_entry__next_pair(he);
 
-               next = rb_next(&he->rb_node);
+               next = rb_next(&he->rb_node_in);
                if (!pair)
                        continue;
 
@@ -441,75 +524,99 @@ static void hists__compute_resort(struct hists *hists)
        }
 }
 
-static void hists__process(struct hists *old, struct hists *new)
+static void hists__process(struct hists *base, struct hists *new)
 {
-       hists__match(new, old);
+       hists__match(base, new);
 
        if (show_baseline_only)
-               hists__baseline_only(new);
+               hists__baseline_only(base);
        else
-               hists__link(new, old);
+               hists__link(base, new);
 
        if (sort_compute) {
-               hists__precompute(new);
-               hists__compute_resort(new);
+               hists__precompute(base);
+               hists__compute_resort(base);
        } else {
-               hists__output_resort(new);
+               hists__output_resort(base);
        }
 
-       hists__fprintf(new, true, 0, 0, stdout);
+       hists__fprintf(base, true, 0, 0, 0, stdout);
 }
 
-static int __cmd_diff(void)
+static void data__fprintf(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;
-       bool first = true;
+       struct data__file *d;
+       int i;
 
-       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;
+       fprintf(stdout, "# Data files:\n");
 
-       for (i = 0; i < 2; ++i) {
-               ret = perf_session__process_events(session[i], &tool);
-               if (ret)
-                       goto out_delete;
-       }
+       data__for_each_file(i, d)
+               fprintf(stdout, "#  [%d] %s %s\n",
+                       d->idx, d->file,
+                       !d->idx ? "(Baseline)" : "");
 
-       evlist_old = older->evlist;
-       evlist_new = newer->evlist;
+       fprintf(stdout, "#\n");
+}
 
-       perf_evlist__collapse_resort(evlist_old);
-       perf_evlist__collapse_resort(evlist_new);
+static void data_process(void)
+{
+       struct perf_evlist *evlist_old = data__files[0].session->evlist;
+       struct perf_evlist *evlist_new = data__files[1].session->evlist;
+       struct perf_evsel *evsel_old;
+       bool first = true;
 
-       list_for_each_entry(evsel, &evlist_new->entries, node) {
-               struct perf_evsel *evsel_old;
+       list_for_each_entry(evsel_old, &evlist_old->entries, node) {
+               struct perf_evsel *evsel_new;
 
-               evsel_old = evsel_match(evsel, evlist_old);
-               if (!evsel_old)
+               evsel_new = evsel_match(evsel_old, evlist_new);
+               if (!evsel_new)
                        continue;
 
                fprintf(stdout, "%s# Event '%s'\n#\n", first ? "" : "\n",
-                       perf_evsel__name(evsel));
+                       perf_evsel__name(evsel_old));
 
                first = false;
 
-               hists__process(&evsel_old->hists, &evsel->hists);
+               if (verbose)
+                       data__fprintf();
+
+               hists__process(&evsel_old->hists, &evsel_new->hists);
        }
+}
 
-out_delete:
-       for (i = 0; i < 2; ++i)
-               perf_session__delete(session[i]);
+static int __cmd_diff(void)
+{
+       struct data__file *d;
+       int ret = -EINVAL, i;
+
+       data__for_each_file(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;
+               }
+
+               ret = perf_session__process_events(d->session, &tool);
+               if (ret) {
+                       pr_err("Failed to process %s\n", d->file);
+                       goto out_delete;
+               }
+
+               perf_evlist__collapse_resort(d->session->evlist);
+       }
+
+       data_process();
+
+ out_delete:
+       data__for_each_file(i, d) {
+               if (d->session)
+                       perf_session__delete(d->session);
+       }
+
+       free(data__files);
        return ret;
-#undef older
-#undef newer
 }
 
 static const char * const diff_usage[] = {
@@ -551,59 +658,297 @@ static const struct option options[] = {
        OPT_END()
 };
 
-static void ui_init(void)
+static double baseline_percent(struct hist_entry *he)
 {
-       /*
-        * Display baseline/delta/ratio
-        * formula/periods columns.
-        */
-       perf_hpp__column_enable(PERF_HPP__BASELINE);
+       struct hists *hists = he->hists;
+       return 100.0 * he->stat.period / hists->stats.total_period;
+}
 
-       switch (compute) {
-       case COMPUTE_DELTA:
-               perf_hpp__column_enable(PERF_HPP__DELTA);
+static int hpp__color_baseline(struct perf_hpp_fmt *fmt,
+                              struct perf_hpp *hpp, struct hist_entry *he)
+{
+       struct diff_hpp_fmt *dfmt =
+               container_of(fmt, struct diff_hpp_fmt, fmt);
+       double percent = baseline_percent(he);
+       char pfmt[20] = " ";
+
+       if (!he->dummy) {
+               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 hist_entry *he, char *buf, size_t size)
+{
+       double percent = baseline_percent(he);
+       const char *fmt = symbol_conf.field_sep ? "%.2f" : "%6.2f%%";
+       int ret = 0;
+
+       if (!he->dummy)
+               ret = scnprintf(buf, size, fmt, percent);
+
+       return ret;
+}
+
+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)
+{
+       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:
+               /* No point for ratio number if we are dummy.. */
+               if (he->dummy)
+                       break;
+
+               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:
+               /* No point for wdiff number if we are dummy.. */
+               if (he->dummy)
+                       break;
+
+               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__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 void
+__hpp__entry_global(struct hist_entry *he, int idx, char *buf, size_t size)
+{
+       struct hist_entry *pair = hist_entry__next_pair(he);
+
+       /* baseline is special */
+       if (idx == PERF_HPP_DIFF__BASELINE)
+               hpp__entry_baseline(he, buf, size);
+       else {
+               if (pair)
+                       hpp__entry_pair(he, pair, idx, buf, size);
+               else
+                       hpp__entry_unpair(he, idx, buf, size);
+       }
+}
+
+static int hpp__entry_global(struct perf_hpp_fmt *_fmt, struct perf_hpp *hpp,
+                            struct hist_entry *he)
+{
+       struct diff_hpp_fmt *dfmt =
+               container_of(_fmt, struct diff_hpp_fmt, fmt);
+       char buf[MAX_COL_WIDTH] = " ";
+
+       __hpp__entry_global(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 hpp__header(struct perf_hpp_fmt *fmt,
+                      struct perf_hpp *hpp)
+{
+       struct diff_hpp_fmt *dfmt =
+               container_of(fmt, struct diff_hpp_fmt, fmt);
+
+       BUG_ON(!dfmt->header);
+       return scnprintf(hpp->buf, hpp->size, dfmt->header);
+}
+
+static int hpp__width(struct perf_hpp_fmt *fmt,
+                     struct perf_hpp *hpp __maybe_unused)
+{
+       struct diff_hpp_fmt *dfmt =
+               container_of(fmt, struct diff_hpp_fmt, fmt);
+
+       BUG_ON(dfmt->header_width <= 0);
+       return dfmt->header_width;
+}
+
+#define hpp__color_global hpp__entry_global
+
+#define FMT(_i, _entry, _color)                                        \
+       [_i] = {                                                \
+               .fmt = {                                        \
+                       .header = hpp__header,                  \
+                       .width  = hpp__width,                   \
+                       .entry  = hpp__entry_ ## _entry,        \
+                       .color  = hpp__color_ ## _color,        \
+               },                                              \
+               .idx = _i,                                      \
+       }
+
+#define FMT_GLOBAL(_i)  FMT(_i, global, global)
+#define FMT_BASELINE(_i) FMT(_i, global, baseline)
+
+static struct diff_hpp_fmt diff_fmt[] = {
+       FMT_BASELINE(PERF_HPP_DIFF__BASELINE),
+       FMT_GLOBAL(PERF_HPP_DIFF__PERIOD),
+       FMT_GLOBAL(PERF_HPP_DIFF__PERIOD_BASELINE),
+       FMT_GLOBAL(PERF_HPP_DIFF__DELTA),
+       FMT_GLOBAL(PERF_HPP_DIFF__RATIO),
+       FMT_GLOBAL(PERF_HPP_DIFF__WEIGHTED_DIFF),
+       FMT_GLOBAL(PERF_HPP_DIFF__FORMULA),
+};
+
+static void init_header(struct diff_hpp_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);
+
+#define NAME (data__files_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
+}
+
+static void column_enable(unsigned col)
+{
+       struct diff_hpp_fmt *dfmt;
+
+       BUG_ON(col >= PERF_HPP_DIFF__MAX_INDEX);
+       dfmt = &diff_fmt[col];
+       init_header(dfmt);
+       perf_hpp__column_register(&dfmt->fmt);
+}
+
+static void ui_init(void)
+{
+       /*
+        * Display baseline/delta/ratio/
+        * formula/periods columns.
+        */
+       column_enable(PERF_HPP_DIFF__BASELINE);
+       column_enable(compute_2_hpp[compute]);
 
        if (show_formula)
-               perf_hpp__column_enable(PERF_HPP__FORMULA);
+               column_enable(PERF_HPP_DIFF__FORMULA);
 
        if (show_period) {
-               perf_hpp__column_enable(PERF_HPP__PERIOD);
-               perf_hpp__column_enable(PERF_HPP__PERIOD_BASELINE);
+               column_enable(PERF_HPP_DIFF__PERIOD);
+               column_enable(PERF_HPP_DIFF__PERIOD_BASELINE);
        }
 }
 
-int cmd_diff(int argc, const char **argv, const char *prefix __maybe_unused)
+static int data_init(int argc, const char **argv)
 {
-       sort_order = diff__default_sort_order;
-       argc = parse_options(argc, argv, options, diff_usage, 0);
+       struct data__file *d;
+       static const char *defaults[] = {
+               "perf.data.old",
+               "perf.data",
+       };
+       int i;
+
+       data__files_cnt = 2;
+
        if (argc) {
                if (argc > 2)
                        usage_with_options(diff_usage, options);
                if (argc == 2) {
-                       input_old = argv[0];
-                       input_new = argv[1];
+                       defaults[0] = argv[0];
+                       defaults[1] = argv[1];
                } else
-                       input_new = argv[0];
+                       defaults[1] = argv[0];
        } 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";
        }
 
-       symbol_conf.exclude_other = false;
+       data__files = zalloc(sizeof(*data__files) * data__files_cnt);
+       if (!data__files)
+               return -ENOMEM;
+
+       data__for_each_file(i, d) {
+               d->file = 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);
+
        if (symbol__init() < 0)
                return -1;
 
+       if (data_init(argc, argv) < 0)
+               return -1;
+
        ui_init();
 
        if (setup_sorting() < 0)
@@ -611,9 +956,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix __maybe_unused)
 
        setup_pager();
 
-       sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "dso", NULL);
-       sort_entry__setup_elide(&sort_comm, symbol_conf.comm_list, "comm", NULL);
-       sort_entry__setup_elide(&sort_sym, symbol_conf.sym_list, "symbol", NULL);
+       sort__setup_elide(NULL);
 
        return __cmd_diff();
 }
This page took 0.033637 seconds and 5 git commands to generate.