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 | |
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')
-rw-r--r-- | tools/perf/Documentation/perf-diff.txt | 2 | ||||
-rw-r--r-- | tools/perf/builtin-diff.c | 137 | ||||
-rw-r--r-- | tools/perf/ui/hist.c | 29 | ||||
-rw-r--r-- | tools/perf/util/hist.h | 2 | ||||
-rw-r--r-- | tools/perf/util/sort.h | 15 |
5 files changed, 167 insertions, 18 deletions
diff --git a/tools/perf/Documentation/perf-diff.txt b/tools/perf/Documentation/perf-diff.txt index 8fff0618c597..cff3d9b6e4a3 100644 --- a/tools/perf/Documentation/perf-diff.txt +++ b/tools/perf/Documentation/perf-diff.txt | |||
@@ -79,6 +79,8 @@ OPTIONS | |||
79 | -c:: | 79 | -c:: |
80 | --compute:: | 80 | --compute:: |
81 | Differential computation selection - delta,ratio (default is delta). | 81 | Differential computation selection - delta,ratio (default is delta). |
82 | If '+' is specified as a first character, the output is sorted based | ||
83 | on the computation results. | ||
82 | See COMPARISON METHODS section for more info. | 84 | See COMPARISON METHODS section for more info. |
83 | 85 | ||
84 | COMPARISON METHODS | 86 | COMPARISON METHODS |
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 | ||
diff --git a/tools/perf/ui/hist.c b/tools/perf/ui/hist.c index 1b633a4b5c45..659f2a25e997 100644 --- a/tools/perf/ui/hist.c +++ b/tools/perf/ui/hist.c | |||
@@ -242,24 +242,15 @@ static int hpp__width_delta(struct perf_hpp *hpp __maybe_unused) | |||
242 | 242 | ||
243 | static int hpp__entry_delta(struct perf_hpp *hpp, struct hist_entry *he) | 243 | static int hpp__entry_delta(struct perf_hpp *hpp, struct hist_entry *he) |
244 | { | 244 | { |
245 | struct hist_entry *pair = he->pair; | ||
246 | struct hists *pair_hists = pair ? pair->hists : NULL; | ||
247 | struct hists *hists = he->hists; | ||
248 | u64 old_total, new_total; | ||
249 | double old_percent = 0, new_percent = 0; | ||
250 | double diff; | ||
251 | const char *fmt = symbol_conf.field_sep ? "%s" : "%7.7s"; | 245 | const char *fmt = symbol_conf.field_sep ? "%s" : "%7.7s"; |
252 | char buf[32] = " "; | 246 | char buf[32] = " "; |
247 | double diff; | ||
253 | 248 | ||
254 | old_total = pair_hists ? pair_hists->stats.total_period : 0; | 249 | if (he->diff.computed) |
255 | if (old_total > 0 && pair) | 250 | diff = he->diff.period_ratio_delta; |
256 | old_percent = 100.0 * pair->stat.period / old_total; | 251 | else |
257 | 252 | diff = perf_diff__compute_delta(he); | |
258 | new_total = hists->stats.total_period; | ||
259 | if (new_total > 0) | ||
260 | new_percent = 100.0 * he->stat.period / new_total; | ||
261 | 253 | ||
262 | diff = new_percent - old_percent; | ||
263 | if (fabs(diff) >= 0.01) | 254 | if (fabs(diff) >= 0.01) |
264 | scnprintf(buf, sizeof(buf), "%+4.2F%%", diff); | 255 | scnprintf(buf, sizeof(buf), "%+4.2F%%", diff); |
265 | 256 | ||
@@ -280,12 +271,14 @@ static int hpp__width_ratio(struct perf_hpp *hpp __maybe_unused) | |||
280 | 271 | ||
281 | static int hpp__entry_ratio(struct perf_hpp *hpp, struct hist_entry *he) | 272 | static int hpp__entry_ratio(struct perf_hpp *hpp, struct hist_entry *he) |
282 | { | 273 | { |
283 | struct hist_entry *pair = he->pair; | ||
284 | double new_period = he->stat.period; | ||
285 | double old_period = pair ? pair->stat.period : 0; | ||
286 | double ratio = pair ? new_period / old_period : 0; | ||
287 | const char *fmt = symbol_conf.field_sep ? "%s" : "%14s"; | 274 | const char *fmt = symbol_conf.field_sep ? "%s" : "%14s"; |
288 | char buf[32] = " "; | 275 | char buf[32] = " "; |
276 | double ratio; | ||
277 | |||
278 | if (he->diff.computed) | ||
279 | ratio = he->diff.period_ratio; | ||
280 | else | ||
281 | ratio = perf_diff__compute_ratio(he); | ||
289 | 282 | ||
290 | if (ratio > 0.0) | 283 | if (ratio > 0.0) |
291 | scnprintf(buf, sizeof(buf), "%+14.6F", ratio); | 284 | scnprintf(buf, sizeof(buf), "%+14.6F", ratio); |
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 7e4d4c262213..a7ea28a1d502 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h | |||
@@ -205,4 +205,6 @@ int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist __maybe_unused, | |||
205 | 205 | ||
206 | unsigned int hists__sort_list_width(struct hists *self); | 206 | unsigned int hists__sort_list_width(struct hists *self); |
207 | 207 | ||
208 | double perf_diff__compute_delta(struct hist_entry *he); | ||
209 | double perf_diff__compute_ratio(struct hist_entry *he); | ||
208 | #endif /* __PERF_HIST_H */ | 210 | #endif /* __PERF_HIST_H */ |
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 5786f323b597..337aeefa2451 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h | |||
@@ -52,6 +52,19 @@ struct he_stat { | |||
52 | u32 nr_events; | 52 | u32 nr_events; |
53 | }; | 53 | }; |
54 | 54 | ||
55 | struct hist_entry_diff { | ||
56 | bool computed; | ||
57 | |||
58 | /* PERF_HPP__DISPL */ | ||
59 | int displacement; | ||
60 | |||
61 | /* PERF_HPP__DELTA */ | ||
62 | double period_ratio_delta; | ||
63 | |||
64 | /* PERF_HPP__RATIO */ | ||
65 | double period_ratio; | ||
66 | }; | ||
67 | |||
55 | /** | 68 | /** |
56 | * struct hist_entry - histogram entry | 69 | * struct hist_entry - histogram entry |
57 | * | 70 | * |
@@ -67,6 +80,8 @@ struct hist_entry { | |||
67 | u64 ip; | 80 | u64 ip; |
68 | s32 cpu; | 81 | s32 cpu; |
69 | 82 | ||
83 | struct hist_entry_diff diff; | ||
84 | |||
70 | /* XXX These two should move to some tree widget lib */ | 85 | /* XXX These two should move to some tree widget lib */ |
71 | u16 row_offset; | 86 | u16 row_offset; |
72 | u16 nr_rows; | 87 | u16 nr_rows; |