summaryrefslogtreecommitdiff
path: root/tools/perf/builtin-diff.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2012-12-11 18:14:31 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2012-12-11 18:14:31 -0800
commit090f8ccba37034cec5a5972a70abeaae7eb0222b (patch)
tree0b7cf8a4bb94601816399acfb336835fbf309a2a /tools/perf/builtin-diff.c
parentaefb058b0c27dafb15072406fbfd92d2ac2c8790 (diff)
parentcc1b39dbf9f55a438e8a21a694394c20e6a17129 (diff)
downloadlwn-090f8ccba37034cec5a5972a70abeaae7eb0222b.tar.gz
lwn-090f8ccba37034cec5a5972a70abeaae7eb0222b.zip
Merge branch 'perf-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull perf updates from Ingo Molnar: "Lots of activity: 211 files changed, 8328 insertions(+), 4116 deletions(-) most of it on the tooling side. Main changes: * ftrace enhancements and fixes from Steve Rostedt. * uprobes fixes, cleanups and preparation for the ARM port from Oleg Nesterov. * UAPI fixes, from David Howels - prepares the arch/x86 UAPI transition * Separate perf tests into multiple objects, one per test, from Jiri Olsa. * Make hardware event translations available in sysfs, from Jiri Olsa. * Fixes to /proc/pid/maps parsing, preparatory to supporting data maps, from Namhyung Kim * Implement ui_progress for GTK, from Namhyung Kim * Add framework for automated perf_event_attr tests, where tools with different command line options will be run from a 'perf test', via python glue, and the perf syscall will be intercepted to verify that the perf_event_attr fields set by the tool are those expected, from Jiri Olsa * Add a 'link' method for hists, so that we can have the leader with buckets for all the entries in all the hists. This new method is now used in the default 'diff' output, making the sum of the 'baseline' column be 100%, eliminating blind spots. * libtraceevent fixes for compiler warnings trying to make perf it build on some distros, like fedora 14, 32-bit, some of the warnings really pointed to real bugs. * Add a browser for 'perf script' and make it available from the report and annotate browsers. It does filtering to find the scripts that handle events found in the perf.data file used. From Feng Tang * perf inject changes to allow showing where a task sleeps, from Andrew Vagin. * Makefile improvements from Namhyung Kim. * Add --pre and --post command hooks in 'stat', from Peter Zijlstra. * Don't stop synthesizing threads when one vanishes, this is for the existing threads when we start a tool like trace. * Use sched:sched_stat_runtime to provide a thread summary, this produces the same output as the 'trace summary' subcommand of tglx's original "trace" tool. * Support interrupted syscalls in 'trace' * Add an event duration column and filter in 'trace'. * There are references to the man pages in some tools, so try to build Documentation when installing, warning the user if that is not possible, from Borislav Petkov. * Give user better message if precise is not supported, from David Ahern. * Try to find cross-built objdump path by using the session environment information in the perf.data file header, from Irina Tirdea, original patch and idea by Namhyung Kim. * Diplays more output on features check for make V=1, so that one can figure out what is happening by looking at gcc output, etc. From Jiri Olsa. * Add on_exit implementation for systems without one, e.g. Android, from Bernhard Rosenkraenzer. * Only process events for vcpus of interest, helps handling large number of events, from David Ahern. * Cross compilation fixes for Android, from Irina Tirdea. * Add documentation on compiling for Android, from Irina Tirdea. * perf diff improvements from Jiri Olsa. * Target (task/user/cpu/syswide) handling improvements, from Namhyung Kim. * Add support in 'trace' for tracing workload given by command line, from Namhyung Kim. * ... and much more." * 'perf-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (194 commits) uprobes: Use percpu_rw_semaphore to fix register/unregister vs dup_mmap() race perf evsel: Introduce is_group_member method perf powerpc: Use uapi/unistd.h to fix build error tools: Pass the target in descend tools: Honour the O= flag when tool build called from a higher Makefile tools: Define a Makefile function to do subdir processing perf ui: Always compile browser setup code perf ui: Add ui_progress__finish() perf ui gtk: Implement ui_progress functions perf ui: Introduce generic ui_progress helper perf ui tui: Move progress.c under ui/tui directory perf tools: Add basic event modifier sanity check perf tools: Omit group members from perf_evlist__disable/enable perf tools: Ensure single disable call per event in record comand perf tools: Fix 'disabled' attribute config for record command perf tools: Fix attributes for '{}' defined event groups perf tools: Use sscanf for parsing /proc/pid/maps perf tools: Add gtk.<command> config option for launching GTK browser perf tools: Fix compile error on NO_NEWT=1 build perf hists: Initialize all of he->stat with zeroes ...
Diffstat (limited to 'tools/perf/builtin-diff.c')
-rw-r--r--tools/perf/builtin-diff.c437
1 files changed, 400 insertions, 37 deletions
diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c
index a0b531c14b97..93b852f8a5d5 100644
--- a/tools/perf/builtin-diff.c
+++ b/tools/perf/builtin-diff.c
@@ -24,6 +24,228 @@ static char const *input_old = "perf.data.old",
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;
+}
+
+static double get_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 = hist_entry__next_pair(he);
+ double new_percent = get_period_percent(he, he->stat.period);
+ double old_percent = pair ? get_period_percent(pair, pair->stat.period) : 0.0;
+
+ 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 = hist_entry__next_pair(he);
+ double new_period = he->stat.period;
+ double old_period = pair ? pair->stat.period : 0;
+
+ he->diff.computed = true;
+ he->diff.period_ratio = pair ? (new_period / old_period) : 0;
+ return he->diff.period_ratio;
+}
+
+s64 perf_diff__compute_wdiff(struct hist_entry *he)
+{
+ struct hist_entry *pair = hist_entry__next_pair(he);
+ u64 new_period = he->stat.period;
+ u64 old_period = pair ? pair->stat.period : 0;
+
+ he->diff.computed = true;
+
+ if (!pair)
+ he->diff.wdiff = 0;
+ else
+ 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, char *buf, size_t size)
+{
+ struct hist_entry *pair = hist_entry__next_pair(he);
+
+ if (!pair)
+ return -1;
+
+ 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, char *buf, size_t size)
+{
+ struct hist_entry *pair = hist_entry__next_pair(he);
+ double new_period = he->stat.period;
+ double old_period = pair ? pair->stat.period : 0;
+
+ if (!pair)
+ return -1;
+
+ return scnprintf(buf, size, "%.0F / %.0F", new_period, old_period);
+}
+
+static int formula_wdiff(struct hist_entry *he, char *buf, size_t size)
+{
+ struct hist_entry *pair = hist_entry__next_pair(he);
+ u64 new_period = he->stat.period;
+ u64 old_period = pair ? pair->stat.period : 0;
+
+ if (!pair)
+ return -1;
+
+ return scnprintf(buf, size,
+ "(%" PRIu64 " * " "%" PRId64 ") - (%" PRIu64 " * " "%" PRId64 ")",
+ new_period, compute_wdiff_w2, old_period, compute_wdiff_w1);
+}
+
+int perf_diff__formula(char *buf, size_t size, struct hist_entry *he)
+{
+ switch (compute) {
+ case COMPUTE_DELTA:
+ return formula_delta(he, buf, size);
+ case COMPUTE_RATIO:
+ return formula_ratio(he, buf, size);
+ case COMPUTE_WEIGHTED_DIFF:
+ return formula_wdiff(he, buf, size);
+ default:
+ BUG_ON(1);
+ }
+
+ return -1;
+}
static int hists__add_entry(struct hists *self,
struct addr_location *al, u64 period)
@@ -47,7 +269,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,8 +285,8 @@ 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,
@@ -112,36 +334,6 @@ static void hists__name_resort(struct hists *self, bool sort)
self->entries = tmp;
}
-static struct hist_entry *hists__find_entry(struct hists *self,
- struct hist_entry *he)
-{
- struct rb_node *n = self->entries.rb_node;
-
- while (n) {
- struct hist_entry *iter = rb_entry(n, struct hist_entry, rb_node);
- int64_t cmp = hist_entry__cmp(he, iter);
-
- if (cmp < 0)
- n = n->rb_left;
- else if (cmp > 0)
- n = n->rb_right;
- else
- return iter;
- }
-
- return NULL;
-}
-
-static void hists__match(struct hists *older, struct hists *newer)
-{
- struct rb_node *nd;
-
- 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);
- }
-}
-
static struct perf_evsel *evsel_match(struct perf_evsel *evsel,
struct perf_evlist *evlist)
{
@@ -172,6 +364,144 @@ static void perf_evlist__resort_hists(struct perf_evlist *evlist, bool name)
}
}
+static void hists__baseline_only(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 (!hist_entry__next_pair(he)) {
+ rb_erase(&he->rb_node, &hists->entries);
+ hist_entry__free(he);
+ }
+ }
+}
+
+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);
+
+ switch (compute) {
+ case COMPUTE_DELTA:
+ perf_diff__compute_delta(he);
+ break;
+ case COMPUTE_RATIO:
+ perf_diff__compute_ratio(he);
+ break;
+ case COMPUTE_WEIGHTED_DIFF:
+ perf_diff__compute_wdiff(he);
+ break;
+ default:
+ BUG_ON(1);
+ }
+ }
+}
+
+static int64_t cmp_doubles(double l, double r)
+{
+ if (l > r)
+ return -1;
+ else if (l < r)
+ return 1;
+ else
+ return 0;
+}
+
+static int64_t
+hist_entry__cmp_compute(struct hist_entry *left, struct hist_entry *right,
+ int c)
+{
+ switch (c) {
+ case COMPUTE_DELTA:
+ {
+ double l = left->diff.period_ratio_delta;
+ double r = right->diff.period_ratio_delta;
+
+ return cmp_doubles(l, r);
+ }
+ case COMPUTE_RATIO:
+ {
+ double l = left->diff.period_ratio;
+ double r = right->diff.period_ratio;
+
+ return cmp_doubles(l, r);
+ }
+ case COMPUTE_WEIGHTED_DIFF:
+ {
+ s64 l = left->diff.wdiff;
+ s64 r = right->diff.wdiff;
+
+ return r - l;
+ }
+ default:
+ BUG_ON(1);
+ }
+
+ return 0;
+}
+
+static void insert_hist_entry_by_compute(struct rb_root *root,
+ struct hist_entry *he,
+ int c)
+{
+ struct rb_node **p = &root->rb_node;
+ struct rb_node *parent = NULL;
+ struct hist_entry *iter;
+
+ 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 void hists__compute_resort(struct hists *hists)
+{
+ struct rb_root tmp = RB_ROOT;
+ 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);
+
+ rb_erase(&he->rb_node, &hists->entries);
+ insert_hist_entry_by_compute(&tmp, he, compute);
+ }
+
+ hists->entries = tmp;
+}
+
+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);
+ }
+
+ hists__fprintf(new, true, 0, 0, stdout);
+}
+
static int __cmd_diff(void)
{
int ret, i;
@@ -213,8 +543,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:
@@ -235,6 +564,16 @@ static const struct option options[] = {
"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"),
@@ -263,12 +602,36 @@ static void ui_init(void)
/* No overhead column. */
perf_hpp__column_enable(PERF_HPP__OVERHEAD, false);
- /* Display baseline/delta/displacement columns. */
+ /*
+ * Display baseline/delta/ratio/displacement/
+ * formula/periods columns.
+ */
perf_hpp__column_enable(PERF_HPP__BASELINE, true);
- perf_hpp__column_enable(PERF_HPP__DELTA, true);
+
+ switch (compute) {
+ case COMPUTE_DELTA:
+ perf_hpp__column_enable(PERF_HPP__DELTA, true);
+ break;
+ case COMPUTE_RATIO:
+ perf_hpp__column_enable(PERF_HPP__RATIO, true);
+ break;
+ case COMPUTE_WEIGHTED_DIFF:
+ perf_hpp__column_enable(PERF_HPP__WEIGHTED_DIFF, true);
+ break;
+ default:
+ BUG_ON(1);
+ };
if (show_displacement)
perf_hpp__column_enable(PERF_HPP__DISPL, true);
+
+ if (show_formula)
+ perf_hpp__column_enable(PERF_HPP__FORMULA, true);
+
+ if (show_period) {
+ perf_hpp__column_enable(PERF_HPP__PERIOD, true);
+ perf_hpp__column_enable(PERF_HPP__PERIOD_BASELINE, true);
+ }
}
int cmd_diff(int argc, const char **argv, const char *prefix __maybe_unused)