aboutsummaryrefslogtreecommitdiffstats
path: root/tools/perf/builtin-diff.c
diff options
context:
space:
mode:
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",
24static char diff__default_sort_order[] = "dso,symbol"; 24static char diff__default_sort_order[] = "dso,symbol";
25static bool force; 25static bool force;
26static bool show_displacement; 26static bool show_displacement;
27static bool show_period;
28static bool show_formula;
29static bool show_baseline_only;
30static bool sort_compute;
31
32static s64 compute_wdiff_w1;
33static s64 compute_wdiff_w2;
34
35enum {
36 COMPUTE_DELTA,
37 COMPUTE_RATIO,
38 COMPUTE_WEIGHTED_DIFF,
39 COMPUTE_MAX,
40};
41
42const char *compute_names[COMPUTE_MAX] = {
43 [COMPUTE_DELTA] = "delta",
44 [COMPUTE_RATIO] = "ratio",
45 [COMPUTE_WEIGHTED_DIFF] = "wdiff",
46};
47
48static int compute;
49
50static int setup_compute_opt_wdiff(char *opt)
51{
52 char *w1_str = opt;
53 char *w2_str;
54
55 int ret = -EINVAL;
56
57 if (!opt)
58 goto out;
59
60 w2_str = strchr(opt, ',');
61 if (!w2_str)
62 goto out;
63
64 *w2_str++ = 0x0;
65 if (!*w2_str)
66 goto out;
67
68 compute_wdiff_w1 = strtol(w1_str, NULL, 10);
69 compute_wdiff_w2 = strtol(w2_str, NULL, 10);
70
71 if (!compute_wdiff_w1 || !compute_wdiff_w2)
72 goto out;
73
74 pr_debug("compute wdiff w1(%" PRId64 ") w2(%" PRId64 ")\n",
75 compute_wdiff_w1, compute_wdiff_w2);
76
77 ret = 0;
78
79 out:
80 if (ret)
81 pr_err("Failed: wrong weight data, use 'wdiff:w1,w2'\n");
82
83 return ret;
84}
85
86static int setup_compute_opt(char *opt)
87{
88 if (compute == COMPUTE_WEIGHTED_DIFF)
89 return setup_compute_opt_wdiff(opt);
90
91 if (opt) {
92 pr_err("Failed: extra option specified '%s'", opt);
93 return -EINVAL;
94 }
95
96 return 0;
97}
98
99static int setup_compute(const struct option *opt, const char *str,
100 int unset __maybe_unused)
101{
102 int *cp = (int *) opt->value;
103 char *cstr = (char *) str;
104 char buf[50];
105 unsigned i;
106 char *option;
107
108 if (!str) {
109 *cp = COMPUTE_DELTA;
110 return 0;
111 }
112
113 if (*str == '+') {
114 sort_compute = true;
115 cstr = (char *) ++str;
116 if (!*str)
117 return 0;
118 }
119
120 option = strchr(str, ':');
121 if (option) {
122 unsigned len = option++ - str;
123
124 /*
125 * The str data are not writeable, so we need
126 * to use another buffer.
127 */
128
129 /* No option value is longer. */
130 if (len >= sizeof(buf))
131 return -EINVAL;
132
133 strncpy(buf, str, len);
134 buf[len] = 0x0;
135 cstr = buf;
136 }
137
138 for (i = 0; i < COMPUTE_MAX; i++)
139 if (!strcmp(cstr, compute_names[i])) {
140 *cp = i;
141 return setup_compute_opt(option);
142 }
143
144 pr_err("Failed: '%s' is not computation method "
145 "(use 'delta','ratio' or 'wdiff')\n", str);
146 return -EINVAL;
147}
148
149static double get_period_percent(struct hist_entry *he, u64 period)
150{
151 u64 total = he->hists->stats.total_period;
152 return (period * 100.0) / total;
153}
154
155double perf_diff__compute_delta(struct hist_entry *he)
156{
157 struct hist_entry *pair = hist_entry__next_pair(he);
158 double new_percent = get_period_percent(he, he->stat.period);
159 double old_percent = pair ? get_period_percent(pair, pair->stat.period) : 0.0;
160
161 he->diff.period_ratio_delta = new_percent - old_percent;
162 he->diff.computed = true;
163 return he->diff.period_ratio_delta;
164}
165
166double perf_diff__compute_ratio(struct hist_entry *he)
167{
168 struct hist_entry *pair = hist_entry__next_pair(he);
169 double new_period = he->stat.period;
170 double old_period = pair ? pair->stat.period : 0;
171
172 he->diff.computed = true;
173 he->diff.period_ratio = pair ? (new_period / old_period) : 0;
174 return he->diff.period_ratio;
175}
176
177s64 perf_diff__compute_wdiff(struct hist_entry *he)
178{
179 struct hist_entry *pair = hist_entry__next_pair(he);
180 u64 new_period = he->stat.period;
181 u64 old_period = pair ? pair->stat.period : 0;
182
183 he->diff.computed = true;
184
185 if (!pair)
186 he->diff.wdiff = 0;
187 else
188 he->diff.wdiff = new_period * compute_wdiff_w2 -
189 old_period * compute_wdiff_w1;
190
191 return he->diff.wdiff;
192}
193
194static int formula_delta(struct hist_entry *he, char *buf, size_t size)
195{
196 struct hist_entry *pair = hist_entry__next_pair(he);
197
198 if (!pair)
199 return -1;
200
201 return scnprintf(buf, size,
202 "(%" PRIu64 " * 100 / %" PRIu64 ") - "
203 "(%" PRIu64 " * 100 / %" PRIu64 ")",
204 he->stat.period, he->hists->stats.total_period,
205 pair->stat.period, pair->hists->stats.total_period);
206}
207
208static int formula_ratio(struct hist_entry *he, char *buf, size_t size)
209{
210 struct hist_entry *pair = hist_entry__next_pair(he);
211 double new_period = he->stat.period;
212 double old_period = pair ? pair->stat.period : 0;
213
214 if (!pair)
215 return -1;
216
217 return scnprintf(buf, size, "%.0F / %.0F", new_period, old_period);
218}
219
220static int formula_wdiff(struct hist_entry *he, char *buf, size_t size)
221{
222 struct hist_entry *pair = hist_entry__next_pair(he);
223 u64 new_period = he->stat.period;
224 u64 old_period = pair ? pair->stat.period : 0;
225
226 if (!pair)
227 return -1;
228
229 return scnprintf(buf, size,
230 "(%" PRIu64 " * " "%" PRId64 ") - (%" PRIu64 " * " "%" PRId64 ")",
231 new_period, compute_wdiff_w2, old_period, compute_wdiff_w1);
232}
233
234int perf_diff__formula(char *buf, size_t size, struct hist_entry *he)
235{
236 switch (compute) {
237 case COMPUTE_DELTA:
238 return formula_delta(he, buf, size);
239 case COMPUTE_RATIO:
240 return formula_ratio(he, buf, size);
241 case COMPUTE_WEIGHTED_DIFF:
242 return formula_wdiff(he, buf, size);
243 default:
244 BUG_ON(1);
245 }
246
247 return -1;
248}
27 249
28static int hists__add_entry(struct hists *self, 250static int hists__add_entry(struct hists *self,
29 struct addr_location *al, u64 period) 251 struct addr_location *al, u64 period)
@@ -47,7 +269,7 @@ static int diff__process_sample_event(struct perf_tool *tool __maybe_unused,
47 return -1; 269 return -1;
48 } 270 }
49 271
50 if (al.filtered || al.sym == NULL) 272 if (al.filtered)
51 return 0; 273 return 0;
52 274
53 if (hists__add_entry(&evsel->hists, &al, sample->period)) { 275 if (hists__add_entry(&evsel->hists, &al, sample->period)) {
@@ -63,8 +285,8 @@ static struct perf_tool tool = {
63 .sample = diff__process_sample_event, 285 .sample = diff__process_sample_event,
64 .mmap = perf_event__process_mmap, 286 .mmap = perf_event__process_mmap,
65 .comm = perf_event__process_comm, 287 .comm = perf_event__process_comm,
66 .exit = perf_event__process_task, 288 .exit = perf_event__process_exit,
67 .fork = perf_event__process_task, 289 .fork = perf_event__process_fork,
68 .lost = perf_event__process_lost, 290 .lost = perf_event__process_lost,
69 .ordered_samples = true, 291 .ordered_samples = true,
70 .ordering_requires_timestamps = true, 292 .ordering_requires_timestamps = true,
@@ -112,36 +334,6 @@ static void hists__name_resort(struct hists *self, bool sort)
112 self->entries = tmp; 334 self->entries = tmp;
113} 335}
114 336
115static struct hist_entry *hists__find_entry(struct hists *self,
116 struct hist_entry *he)
117{
118 struct rb_node *n = self->entries.rb_node;
119
120 while (n) {
121 struct hist_entry *iter = rb_entry(n, struct hist_entry, rb_node);
122 int64_t cmp = hist_entry__cmp(he, iter);
123
124 if (cmp < 0)
125 n = n->rb_left;
126 else if (cmp > 0)
127 n = n->rb_right;
128 else
129 return iter;
130 }
131
132 return NULL;
133}
134
135static void hists__match(struct hists *older, struct hists *newer)
136{
137 struct rb_node *nd;
138
139 for (nd = rb_first(&newer->entries); nd; nd = rb_next(nd)) {
140 struct hist_entry *pos = rb_entry(nd, struct hist_entry, rb_node);
141 pos->pair = hists__find_entry(older, pos);
142 }
143}
144
145static struct perf_evsel *evsel_match(struct perf_evsel *evsel, 337static struct perf_evsel *evsel_match(struct perf_evsel *evsel,
146 struct perf_evlist *evlist) 338 struct perf_evlist *evlist)
147{ 339{
@@ -172,6 +364,144 @@ static void perf_evlist__resort_hists(struct perf_evlist *evlist, bool name)
172 } 364 }
173} 365}
174 366
367static void hists__baseline_only(struct hists *hists)
368{
369 struct rb_node *next = rb_first(&hists->entries);
370
371 while (next != NULL) {
372 struct hist_entry *he = rb_entry(next, struct hist_entry, rb_node);
373
374 next = rb_next(&he->rb_node);
375 if (!hist_entry__next_pair(he)) {
376 rb_erase(&he->rb_node, &hists->entries);
377 hist_entry__free(he);
378 }
379 }
380}
381
382static void hists__precompute(struct hists *hists)
383{
384 struct rb_node *next = rb_first(&hists->entries);
385
386 while (next != NULL) {
387 struct hist_entry *he = rb_entry(next, struct hist_entry, rb_node);
388
389 next = rb_next(&he->rb_node);
390
391 switch (compute) {
392 case COMPUTE_DELTA:
393 perf_diff__compute_delta(he);
394 break;
395 case COMPUTE_RATIO:
396 perf_diff__compute_ratio(he);
397 break;
398 case COMPUTE_WEIGHTED_DIFF:
399 perf_diff__compute_wdiff(he);
400 break;
401 default:
402 BUG_ON(1);
403 }
404 }
405}
406
407static int64_t cmp_doubles(double l, double r)
408{
409 if (l > r)
410 return -1;
411 else if (l < r)
412 return 1;
413 else
414 return 0;
415}
416
417static int64_t
418hist_entry__cmp_compute(struct hist_entry *left, struct hist_entry *right,
419 int c)
420{
421 switch (c) {
422 case COMPUTE_DELTA:
423 {
424 double l = left->diff.period_ratio_delta;
425 double r = right->diff.period_ratio_delta;
426
427 return cmp_doubles(l, r);
428 }
429 case COMPUTE_RATIO:
430 {
431 double l = left->diff.period_ratio;
432 double r = right->diff.period_ratio;
433
434 return cmp_doubles(l, r);
435 }
436 case COMPUTE_WEIGHTED_DIFF:
437 {
438 s64 l = left->diff.wdiff;
439 s64 r = right->diff.wdiff;
440
441 return r - l;
442 }
443 default:
444 BUG_ON(1);
445 }
446
447 return 0;
448}
449
450static void insert_hist_entry_by_compute(struct rb_root *root,
451 struct hist_entry *he,
452 int c)
453{
454 struct rb_node **p = &root->rb_node;
455 struct rb_node *parent = NULL;
456 struct hist_entry *iter;
457
458 while (*p != NULL) {
459 parent = *p;
460 iter = rb_entry(parent, struct hist_entry, rb_node);
461 if (hist_entry__cmp_compute(he, iter, c) < 0)
462 p = &(*p)->rb_left;
463 else
464 p = &(*p)->rb_right;
465 }
466
467 rb_link_node(&he->rb_node, parent, p);
468 rb_insert_color(&he->rb_node, root);
469}
470
471static void hists__compute_resort(struct hists *hists)
472{
473 struct rb_root tmp = RB_ROOT;
474 struct rb_node *next = rb_first(&hists->entries);
475
476 while (next != NULL) {
477 struct hist_entry *he = rb_entry(next, struct hist_entry, rb_node);
478
479 next = rb_next(&he->rb_node);
480
481 rb_erase(&he->rb_node, &hists->entries);
482 insert_hist_entry_by_compute(&tmp, he, compute);
483 }
484
485 hists->entries = tmp;
486}
487
488static void hists__process(struct hists *old, struct hists *new)
489{
490 hists__match(new, old);
491
492 if (show_baseline_only)
493 hists__baseline_only(new);
494 else
495 hists__link(new, old);
496
497 if (sort_compute) {
498 hists__precompute(new);
499 hists__compute_resort(new);
500 }
501
502 hists__fprintf(new, true, 0, 0, stdout);
503}
504
175static int __cmd_diff(void) 505static int __cmd_diff(void)
176{ 506{
177 int ret, i; 507 int ret, i;
@@ -213,8 +543,7 @@ static int __cmd_diff(void)
213 543
214 first = false; 544 first = false;
215 545
216 hists__match(&evsel_old->hists, &evsel->hists); 546 hists__process(&evsel_old->hists, &evsel->hists);
217 hists__fprintf(&evsel->hists, true, 0, 0, stdout);
218 } 547 }
219 548
220out_delete: 549out_delete:
@@ -235,6 +564,16 @@ static const struct option options[] = {
235 "be more verbose (show symbol address, etc)"), 564 "be more verbose (show symbol address, etc)"),
236 OPT_BOOLEAN('M', "displacement", &show_displacement, 565 OPT_BOOLEAN('M', "displacement", &show_displacement,
237 "Show position displacement relative to baseline"), 566 "Show position displacement relative to baseline"),
567 OPT_BOOLEAN('b', "baseline-only", &show_baseline_only,
568 "Show only items with match in baseline"),
569 OPT_CALLBACK('c', "compute", &compute,
570 "delta,ratio,wdiff:w1,w2 (default delta)",
571 "Entries differential computation selection",
572 setup_compute),
573 OPT_BOOLEAN('p', "period", &show_period,
574 "Show period values."),
575 OPT_BOOLEAN('F', "formula", &show_formula,
576 "Show formula."),
238 OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, 577 OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
239 "dump raw trace in ASCII"), 578 "dump raw trace in ASCII"),
240 OPT_BOOLEAN('f', "force", &force, "don't complain, do it"), 579 OPT_BOOLEAN('f', "force", &force, "don't complain, do it"),
@@ -263,12 +602,36 @@ static void ui_init(void)
263 /* No overhead column. */ 602 /* No overhead column. */
264 perf_hpp__column_enable(PERF_HPP__OVERHEAD, false); 603 perf_hpp__column_enable(PERF_HPP__OVERHEAD, false);
265 604
266 /* Display baseline/delta/displacement columns. */ 605 /*
606 * Display baseline/delta/ratio/displacement/
607 * formula/periods columns.
608 */
267 perf_hpp__column_enable(PERF_HPP__BASELINE, true); 609 perf_hpp__column_enable(PERF_HPP__BASELINE, true);
268 perf_hpp__column_enable(PERF_HPP__DELTA, true); 610
611 switch (compute) {
612 case COMPUTE_DELTA:
613 perf_hpp__column_enable(PERF_HPP__DELTA, true);
614 break;
615 case COMPUTE_RATIO:
616 perf_hpp__column_enable(PERF_HPP__RATIO, true);
617 break;
618 case COMPUTE_WEIGHTED_DIFF:
619 perf_hpp__column_enable(PERF_HPP__WEIGHTED_DIFF, true);
620 break;
621 default:
622 BUG_ON(1);
623 };
269 624
270 if (show_displacement) 625 if (show_displacement)
271 perf_hpp__column_enable(PERF_HPP__DISPL, true); 626 perf_hpp__column_enable(PERF_HPP__DISPL, true);
627
628 if (show_formula)
629 perf_hpp__column_enable(PERF_HPP__FORMULA, true);
630
631 if (show_period) {
632 perf_hpp__column_enable(PERF_HPP__PERIOD, true);
633 perf_hpp__column_enable(PERF_HPP__PERIOD_BASELINE, true);
634 }
272} 635}
273 636
274int cmd_diff(int argc, const char **argv, const char *prefix __maybe_unused) 637int cmd_diff(int argc, const char **argv, const char *prefix __maybe_unused)