diff options
author | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2013-05-01 08:47:44 -0700 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2013-05-01 08:47:44 -0700 |
commit | bf61c8840efe60fd8f91446860b63338fb424158 (patch) | |
tree | 7a71832407a4f0d6346db773343f4c3ae2257b19 /tools/perf/builtin-diff.c | |
parent | 5846115b30f3a881e542c8bfde59a699c1c13740 (diff) | |
parent | 0c6a61657da78098472fd0eb71cc01f2387fa1bb (diff) |
Merge branch 'next' into for-linus
Prepare first set of updates for 3.10 merge window.
Diffstat (limited to 'tools/perf/builtin-diff.c')
-rw-r--r-- | tools/perf/builtin-diff.c | 484 |
1 files changed, 398 insertions, 86 deletions
diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index a0b531c14b97..d207a97a2db1 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -23,7 +23,212 @@ static char const *input_old = "perf.data.old", *input_new = "perf.data"; static char diff__default_sort_order[] = "dso,symbol"; static bool force; -static bool show_displacement; +static bool show_period; +static bool show_formula; +static bool show_baseline_only; +static bool sort_compute; + +static s64 compute_wdiff_w1; +static s64 compute_wdiff_w2; + +enum { + COMPUTE_DELTA, + COMPUTE_RATIO, + COMPUTE_WEIGHTED_DIFF, + COMPUTE_MAX, +}; + +const char *compute_names[COMPUTE_MAX] = { + [COMPUTE_DELTA] = "delta", + [COMPUTE_RATIO] = "ratio", + [COMPUTE_WEIGHTED_DIFF] = "wdiff", +}; + +static int compute; + +static int setup_compute_opt_wdiff(char *opt) +{ + char *w1_str = opt; + char *w2_str; + + int ret = -EINVAL; + + if (!opt) + goto out; + + w2_str = strchr(opt, ','); + if (!w2_str) + goto out; + + *w2_str++ = 0x0; + if (!*w2_str) + goto out; + + compute_wdiff_w1 = strtol(w1_str, NULL, 10); + compute_wdiff_w2 = strtol(w2_str, NULL, 10); + + if (!compute_wdiff_w1 || !compute_wdiff_w2) + goto out; + + pr_debug("compute wdiff w1(%" PRId64 ") w2(%" PRId64 ")\n", + compute_wdiff_w1, compute_wdiff_w2); + + ret = 0; + + out: + if (ret) + pr_err("Failed: wrong weight data, use 'wdiff:w1,w2'\n"); + + return ret; +} + +static int setup_compute_opt(char *opt) +{ + if (compute == COMPUTE_WEIGHTED_DIFF) + return setup_compute_opt_wdiff(opt); + + if (opt) { + pr_err("Failed: extra option specified '%s'", opt); + return -EINVAL; + } + + return 0; +} + +static int setup_compute(const struct option *opt, const char *str, + int unset __maybe_unused) +{ + int *cp = (int *) opt->value; + char *cstr = (char *) str; + char buf[50]; + unsigned i; + char *option; + + if (!str) { + *cp = COMPUTE_DELTA; + return 0; + } + + if (*str == '+') { + sort_compute = true; + cstr = (char *) ++str; + if (!*str) + return 0; + } + + option = strchr(str, ':'); + if (option) { + unsigned len = option++ - str; + + /* + * The str data are not writeable, so we need + * to use another buffer. + */ + + /* No option value is longer. */ + if (len >= sizeof(buf)) + return -EINVAL; + + strncpy(buf, str, len); + buf[len] = 0x0; + cstr = buf; + } + + for (i = 0; i < COMPUTE_MAX; i++) + if (!strcmp(cstr, compute_names[i])) { + *cp = i; + return setup_compute_opt(option); + } + + pr_err("Failed: '%s' is not computation method " + "(use 'delta','ratio' or 'wdiff')\n", str); + return -EINVAL; +} + +double perf_diff__period_percent(struct hist_entry *he, u64 period) +{ + u64 total = he->hists->stats.total_period; + return (period * 100.0) / total; +} + +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); + + he->diff.period_ratio_delta = new_percent - old_percent; + he->diff.computed = true; + return he->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; + + he->diff.computed = true; + he->diff.period_ratio = new_period / old_period; + return he->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; + + he->diff.computed = true; + he->diff.wdiff = new_period * compute_wdiff_w2 - + old_period * compute_wdiff_w1; + + return he->diff.wdiff; +} + +static int formula_delta(struct hist_entry *he, struct hist_entry *pair, + char *buf, size_t size) +{ + 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); +} + +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; + + return scnprintf(buf, size, "%.0F / %.0F", new_period, old_period); +} + +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; + + return scnprintf(buf, size, + "(%" PRIu64 " * " "%" PRId64 ") - (%" PRIu64 " * " "%" PRId64 ")", + new_period, compute_wdiff_w2, old_period, compute_wdiff_w1); +} + +int perf_diff__formula(struct hist_entry *he, struct hist_entry *pair, + char *buf, size_t size) +{ + switch (compute) { + case COMPUTE_DELTA: + return formula_delta(he, pair, buf, size); + case COMPUTE_RATIO: + return formula_ratio(he, pair, buf, size); + case COMPUTE_WEIGHTED_DIFF: + return formula_wdiff(he, pair, buf, size); + default: + BUG_ON(1); + } + + return -1; +} static int hists__add_entry(struct hists *self, struct addr_location *al, u64 period) @@ -47,7 +252,7 @@ static int diff__process_sample_event(struct perf_tool *tool __maybe_unused, return -1; } - if (al.filtered || al.sym == NULL) + if (al.filtered) return 0; if (hists__add_entry(&evsel->hists, &al, sample->period)) { @@ -63,115 +268,197 @@ static struct perf_tool tool = { .sample = diff__process_sample_event, .mmap = perf_event__process_mmap, .comm = perf_event__process_comm, - .exit = perf_event__process_task, - .fork = perf_event__process_task, + .exit = perf_event__process_exit, + .fork = perf_event__process_fork, .lost = perf_event__process_lost, .ordered_samples = true, .ordering_requires_timestamps = true, }; -static void insert_hist_entry_by_name(struct rb_root *root, - struct hist_entry *he) +static struct perf_evsel *evsel_match(struct perf_evsel *evsel, + struct perf_evlist *evlist) { - struct rb_node **p = &root->rb_node; - struct rb_node *parent = NULL; - struct hist_entry *iter; + struct perf_evsel *e; - while (*p != NULL) { - parent = *p; - iter = rb_entry(parent, struct hist_entry, rb_node); - if (hist_entry__cmp(he, iter) < 0) - p = &(*p)->rb_left; - else - p = &(*p)->rb_right; + list_for_each_entry(e, &evlist->entries, node) + if (perf_evsel__match2(evsel, e)) + return e; + + return NULL; +} + +static void perf_evlist__collapse_resort(struct perf_evlist *evlist) +{ + struct perf_evsel *evsel; + + list_for_each_entry(evsel, &evlist->entries, node) { + struct hists *hists = &evsel->hists; + + hists__collapse_resort(hists); } +} - rb_link_node(&he->rb_node, parent, p); - rb_insert_color(&he->rb_node, root); +static void hists__baseline_only(struct hists *hists) +{ + 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_in); + + next = rb_next(&he->rb_node_in); + if (!hist_entry__next_pair(he)) { + rb_erase(&he->rb_node_in, root); + hist_entry__free(he); + } + } } -static void hists__name_resort(struct hists *self, bool sort) +static void hists__precompute(struct hists *hists) { - unsigned long position = 1; - struct rb_root tmp = RB_ROOT; - struct rb_node *next = rb_first(&self->entries); + struct rb_node *next = rb_first(&hists->entries); while (next != NULL) { - struct hist_entry *n = rb_entry(next, struct hist_entry, rb_node); + struct hist_entry *he = rb_entry(next, struct hist_entry, rb_node); + struct hist_entry *pair = hist_entry__next_pair(he); - next = rb_next(&n->rb_node); - n->position = position++; + next = rb_next(&he->rb_node); + if (!pair) + continue; - if (sort) { - rb_erase(&n->rb_node, &self->entries); - insert_hist_entry_by_name(&tmp, n); + switch (compute) { + case COMPUTE_DELTA: + perf_diff__compute_delta(he, pair); + break; + case COMPUTE_RATIO: + perf_diff__compute_ratio(he, pair); + break; + case COMPUTE_WEIGHTED_DIFF: + perf_diff__compute_wdiff(he, pair); + break; + default: + BUG_ON(1); } } +} - if (sort) - self->entries = tmp; +static int64_t cmp_doubles(double l, double r) +{ + if (l > r) + return -1; + else if (l < r) + return 1; + else + return 0; } -static struct hist_entry *hists__find_entry(struct hists *self, - struct hist_entry *he) +static int64_t +hist_entry__cmp_compute(struct hist_entry *left, struct hist_entry *right, + int c) { - struct rb_node *n = self->entries.rb_node; + switch (c) { + case COMPUTE_DELTA: + { + double l = left->diff.period_ratio_delta; + double r = right->diff.period_ratio_delta; - while (n) { - struct hist_entry *iter = rb_entry(n, struct hist_entry, rb_node); - int64_t cmp = hist_entry__cmp(he, iter); + return cmp_doubles(l, r); + } + case COMPUTE_RATIO: + { + double l = left->diff.period_ratio; + double r = right->diff.period_ratio; - if (cmp < 0) - n = n->rb_left; - else if (cmp > 0) - n = n->rb_right; - else - return iter; + return cmp_doubles(l, r); } + case COMPUTE_WEIGHTED_DIFF: + { + s64 l = left->diff.wdiff; + s64 r = right->diff.wdiff; - return NULL; + return r - l; + } + default: + BUG_ON(1); + } + + return 0; } -static void hists__match(struct hists *older, struct hists *newer) +static void insert_hist_entry_by_compute(struct rb_root *root, + struct hist_entry *he, + int c) { - struct rb_node *nd; + struct rb_node **p = &root->rb_node; + struct rb_node *parent = NULL; + struct hist_entry *iter; - for (nd = rb_first(&newer->entries); nd; nd = rb_next(nd)) { - struct hist_entry *pos = rb_entry(nd, struct hist_entry, rb_node); - pos->pair = hists__find_entry(older, pos); + while (*p != NULL) { + parent = *p; + iter = rb_entry(parent, struct hist_entry, rb_node); + if (hist_entry__cmp_compute(he, iter, c) < 0) + p = &(*p)->rb_left; + else + p = &(*p)->rb_right; } + + rb_link_node(&he->rb_node, parent, p); + rb_insert_color(&he->rb_node, root); } -static struct perf_evsel *evsel_match(struct perf_evsel *evsel, - struct perf_evlist *evlist) +static void hists__compute_resort(struct hists *hists) { - struct perf_evsel *e; + struct rb_root *root; + struct rb_node *next; - list_for_each_entry(e, &evlist->entries, node) - if (perf_evsel__match2(evsel, e)) - return e; + if (sort__need_collapse) + root = &hists->entries_collapsed; + else + root = hists->entries_in; - return NULL; -} + hists->entries = RB_ROOT; + next = rb_first(root); -static void perf_evlist__resort_hists(struct perf_evlist *evlist, bool name) -{ - struct perf_evsel *evsel; + hists->nr_entries = 0; + hists->stats.total_period = 0; + hists__reset_col_len(hists); - list_for_each_entry(evsel, &evlist->entries, node) { - struct hists *hists = &evsel->hists; + while (next != NULL) { + struct hist_entry *he; - hists__output_resort(hists); + he = rb_entry(next, struct hist_entry, rb_node_in); + next = rb_next(&he->rb_node_in); - /* - * The hists__name_resort only sets possition - * if name is false. - */ - if (name || ((!name) && show_displacement)) - hists__name_resort(hists, name); + insert_hist_entry_by_compute(&hists->entries, he, compute); + hists__inc_nr_entries(hists, he); } } +static void hists__process(struct hists *old, struct hists *new) +{ + hists__match(new, old); + + if (show_baseline_only) + hists__baseline_only(new); + else + hists__link(new, old); + + if (sort_compute) { + hists__precompute(new); + hists__compute_resort(new); + } else { + hists__output_resort(new); + } + + hists__fprintf(new, true, 0, 0, stdout); +} + static int __cmd_diff(void) { int ret, i; @@ -198,8 +485,8 @@ static int __cmd_diff(void) evlist_old = older->evlist; evlist_new = newer->evlist; - perf_evlist__resort_hists(evlist_old, true); - perf_evlist__resort_hists(evlist_new, false); + perf_evlist__collapse_resort(evlist_old); + perf_evlist__collapse_resort(evlist_new); list_for_each_entry(evsel, &evlist_new->entries, node) { struct perf_evsel *evsel_old; @@ -213,8 +500,7 @@ static int __cmd_diff(void) first = false; - hists__match(&evsel_old->hists, &evsel->hists); - hists__fprintf(&evsel->hists, true, 0, 0, stdout); + hists__process(&evsel_old->hists, &evsel->hists); } out_delete: @@ -233,8 +519,16 @@ static const char * const diff_usage[] = { static const struct option options[] = { OPT_INCR('v', "verbose", &verbose, "be more verbose (show symbol address, etc)"), - OPT_BOOLEAN('M', "displacement", &show_displacement, - "Show position displacement relative to baseline"), + OPT_BOOLEAN('b', "baseline-only", &show_baseline_only, + "Show only items with match in baseline"), + OPT_CALLBACK('c', "compute", &compute, + "delta,ratio,wdiff:w1,w2 (default delta)", + "Entries differential computation selection", + setup_compute), + OPT_BOOLEAN('p', "period", &show_period, + "Show period values."), + OPT_BOOLEAN('F', "formula", &show_formula, + "Show formula."), OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, "dump raw trace in ASCII"), OPT_BOOLEAN('f', "force", &force, "don't complain, do it"), @@ -258,17 +552,33 @@ static const struct option options[] = { static void ui_init(void) { - perf_hpp__init(); - - /* No overhead column. */ - perf_hpp__column_enable(PERF_HPP__OVERHEAD, false); - - /* Display baseline/delta/displacement columns. */ - perf_hpp__column_enable(PERF_HPP__BASELINE, true); - perf_hpp__column_enable(PERF_HPP__DELTA, true); - - if (show_displacement) - perf_hpp__column_enable(PERF_HPP__DISPL, true); + /* + * Display baseline/delta/ratio + * formula/periods columns. + */ + perf_hpp__column_enable(PERF_HPP__BASELINE); + + switch (compute) { + case COMPUTE_DELTA: + perf_hpp__column_enable(PERF_HPP__DELTA); + break; + case COMPUTE_RATIO: + perf_hpp__column_enable(PERF_HPP__RATIO); + break; + case COMPUTE_WEIGHTED_DIFF: + perf_hpp__column_enable(PERF_HPP__WEIGHTED_DIFF); + break; + default: + BUG_ON(1); + }; + + if (show_formula) + perf_hpp__column_enable(PERF_HPP__FORMULA); + + if (show_period) { + perf_hpp__column_enable(PERF_HPP__PERIOD); + perf_hpp__column_enable(PERF_HPP__PERIOD_BASELINE); + } } int cmd_diff(int argc, const char **argv, const char *prefix __maybe_unused) @@ -295,7 +605,9 @@ int cmd_diff(int argc, const char **argv, const char *prefix __maybe_unused) ui_init(); - setup_sorting(diff_usage, options); + if (setup_sorting() < 0) + usage_with_options(diff_usage, options); + setup_pager(); sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "dso", NULL); |