diff options
author | Jiri Olsa <jolsa@redhat.com> | 2012-10-05 10:44:42 -0400 |
---|---|---|
committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2012-10-05 13:08:54 -0400 |
commit | 96c47f19846742bdfa3c153c8d26ccca5945586b (patch) | |
tree | 209bbd497e4ddc814718fd8963abd615850e412d /tools/perf/builtin-diff.c | |
parent | 7aaf6b35512382329c5b2dd46b42f2bf12a5fff0 (diff) |
perf diff: Add option to sort entries based on diff computation
Adding support to sort hist entries based on the outcome of selected
computation. It's now possible to specify '+' as a first character of
'-c' option value to make such sort.
Example:
$ perf diff -c ratio -b
# Event 'cache-misses'
#
# Baseline Ratio Shared Object Symbol
# ........ .............. ................. ................................
#
19.64% 0.69 [kernel.kallsyms] [k] clear_page
0.30% 0.17 [kernel.kallsyms] [k] mm_alloc
0.04% 0.20 [kernel.kallsyms] [k] kmem_cache_alloc
$ perf diff -c +ratio -b
# Event 'cache-misses'
#
# Baseline Ratio Shared Object Symbol
# ........ .............. ................. ................................
#
19.64% 0.69 [kernel.kallsyms] [k] clear_page
0.04% 0.20 [kernel.kallsyms] [k] kmem_cache_alloc
0.30% 0.17 [kernel.kallsyms] [k] mm_alloc
Signed-off-by: Jiri Olsa <jolsa@redhat.com>
Cc: Andi Kleen <andi@firstfloor.org>
Cc: Corey Ashford <cjashfor@linux.vnet.ibm.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Link: http://lkml.kernel.org/r/1349448287-18919-4-git-send-email-jolsa@redhat.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Diffstat (limited to 'tools/perf/builtin-diff.c')
-rw-r--r-- | tools/perf/builtin-diff.c | 137 |
1 files changed, 137 insertions, 0 deletions
diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index e90c06aea4d4..e13cfac0b063 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c | |||
@@ -25,6 +25,7 @@ static char diff__default_sort_order[] = "dso,symbol"; | |||
25 | static bool force; | 25 | static bool force; |
26 | static bool show_displacement; | 26 | static bool show_displacement; |
27 | static bool show_baseline_only; | 27 | static bool show_baseline_only; |
28 | static bool sort_compute; | ||
28 | 29 | ||
29 | enum { | 30 | enum { |
30 | COMPUTE_DELTA, | 31 | COMPUTE_DELTA, |
@@ -50,6 +51,13 @@ static int setup_compute(const struct option *opt, const char *str, | |||
50 | return 0; | 51 | return 0; |
51 | } | 52 | } |
52 | 53 | ||
54 | if (*str == '+') { | ||
55 | sort_compute = true; | ||
56 | str++; | ||
57 | if (!*str) | ||
58 | return 0; | ||
59 | } | ||
60 | |||
53 | for (i = 0; i < COMPUTE_MAX; i++) | 61 | for (i = 0; i < COMPUTE_MAX; i++) |
54 | if (!strcmp(str, compute_names[i])) { | 62 | if (!strcmp(str, compute_names[i])) { |
55 | *cp = i; | 63 | *cp = i; |
@@ -61,6 +69,34 @@ static int setup_compute(const struct option *opt, const char *str, | |||
61 | return -EINVAL; | 69 | return -EINVAL; |
62 | } | 70 | } |
63 | 71 | ||
72 | static double get_period_percent(struct hist_entry *he, u64 period) | ||
73 | { | ||
74 | u64 total = he->hists->stats.total_period; | ||
75 | return (period * 100.0) / total; | ||
76 | } | ||
77 | |||
78 | double perf_diff__compute_delta(struct hist_entry *he) | ||
79 | { | ||
80 | struct hist_entry *pair = he->pair; | ||
81 | double new_percent = get_period_percent(he, he->stat.period); | ||
82 | double old_percent = pair ? get_period_percent(pair, pair->stat.period) : 0.0; | ||
83 | |||
84 | he->diff.period_ratio_delta = new_percent - old_percent; | ||
85 | he->diff.computed = true; | ||
86 | return he->diff.period_ratio_delta; | ||
87 | } | ||
88 | |||
89 | double perf_diff__compute_ratio(struct hist_entry *he) | ||
90 | { | ||
91 | struct hist_entry *pair = he->pair; | ||
92 | double new_period = he->stat.period; | ||
93 | double old_period = pair ? pair->stat.period : 0; | ||
94 | |||
95 | he->diff.computed = true; | ||
96 | he->diff.period_ratio = pair ? (new_period / old_period) : 0; | ||
97 | return he->diff.period_ratio; | ||
98 | } | ||
99 | |||
64 | static int hists__add_entry(struct hists *self, | 100 | static int hists__add_entry(struct hists *self, |
65 | struct addr_location *al, u64 period) | 101 | struct addr_location *al, u64 period) |
66 | { | 102 | { |
@@ -223,6 +259,102 @@ static void hists__baseline_only(struct hists *hists) | |||
223 | } | 259 | } |
224 | } | 260 | } |
225 | 261 | ||
262 | static void hists__precompute(struct hists *hists) | ||
263 | { | ||
264 | struct rb_node *next = rb_first(&hists->entries); | ||
265 | |||
266 | while (next != NULL) { | ||
267 | struct hist_entry *he = rb_entry(next, struct hist_entry, rb_node); | ||
268 | |||
269 | next = rb_next(&he->rb_node); | ||
270 | |||
271 | switch (compute) { | ||
272 | case COMPUTE_DELTA: | ||
273 | perf_diff__compute_delta(he); | ||
274 | break; | ||
275 | case COMPUTE_RATIO: | ||
276 | perf_diff__compute_ratio(he); | ||
277 | break; | ||
278 | default: | ||
279 | BUG_ON(1); | ||
280 | } | ||
281 | } | ||
282 | } | ||
283 | |||
284 | static int64_t cmp_doubles(double l, double r) | ||
285 | { | ||
286 | if (l > r) | ||
287 | return -1; | ||
288 | else if (l < r) | ||
289 | return 1; | ||
290 | else | ||
291 | return 0; | ||
292 | } | ||
293 | |||
294 | static int64_t | ||
295 | hist_entry__cmp_compute(struct hist_entry *left, struct hist_entry *right, | ||
296 | int c) | ||
297 | { | ||
298 | switch (c) { | ||
299 | case COMPUTE_DELTA: | ||
300 | { | ||
301 | double l = left->diff.period_ratio_delta; | ||
302 | double r = right->diff.period_ratio_delta; | ||
303 | |||
304 | return cmp_doubles(l, r); | ||
305 | } | ||
306 | case COMPUTE_RATIO: | ||
307 | { | ||
308 | double l = left->diff.period_ratio; | ||
309 | double r = right->diff.period_ratio; | ||
310 | |||
311 | return cmp_doubles(l, r); | ||
312 | } | ||
313 | default: | ||
314 | BUG_ON(1); | ||
315 | } | ||
316 | |||
317 | return 0; | ||
318 | } | ||
319 | |||
320 | static void insert_hist_entry_by_compute(struct rb_root *root, | ||
321 | struct hist_entry *he, | ||
322 | int c) | ||
323 | { | ||
324 | struct rb_node **p = &root->rb_node; | ||
325 | struct rb_node *parent = NULL; | ||
326 | struct hist_entry *iter; | ||
327 | |||
328 | while (*p != NULL) { | ||
329 | parent = *p; | ||
330 | iter = rb_entry(parent, struct hist_entry, rb_node); | ||
331 | if (hist_entry__cmp_compute(he, iter, c) < 0) | ||
332 | p = &(*p)->rb_left; | ||
333 | else | ||
334 | p = &(*p)->rb_right; | ||
335 | } | ||
336 | |||
337 | rb_link_node(&he->rb_node, parent, p); | ||
338 | rb_insert_color(&he->rb_node, root); | ||
339 | } | ||
340 | |||
341 | static void hists__compute_resort(struct hists *hists) | ||
342 | { | ||
343 | struct rb_root tmp = RB_ROOT; | ||
344 | struct rb_node *next = rb_first(&hists->entries); | ||
345 | |||
346 | while (next != NULL) { | ||
347 | struct hist_entry *he = rb_entry(next, struct hist_entry, rb_node); | ||
348 | |||
349 | next = rb_next(&he->rb_node); | ||
350 | |||
351 | rb_erase(&he->rb_node, &hists->entries); | ||
352 | insert_hist_entry_by_compute(&tmp, he, compute); | ||
353 | } | ||
354 | |||
355 | hists->entries = tmp; | ||
356 | } | ||
357 | |||
226 | static void hists__process(struct hists *old, struct hists *new) | 358 | static void hists__process(struct hists *old, struct hists *new) |
227 | { | 359 | { |
228 | hists__match(old, new); | 360 | hists__match(old, new); |
@@ -230,6 +362,11 @@ static void hists__process(struct hists *old, struct hists *new) | |||
230 | if (show_baseline_only) | 362 | if (show_baseline_only) |
231 | hists__baseline_only(new); | 363 | hists__baseline_only(new); |
232 | 364 | ||
365 | if (sort_compute) { | ||
366 | hists__precompute(new); | ||
367 | hists__compute_resort(new); | ||
368 | } | ||
369 | |||
233 | hists__fprintf(new, true, 0, 0, stdout); | 370 | hists__fprintf(new, true, 0, 0, stdout); |
234 | } | 371 | } |
235 | 372 | ||