diff options
Diffstat (limited to 'tools/perf/builtin-diff.c')
-rw-r--r-- | tools/perf/builtin-diff.c | 205 |
1 files changed, 77 insertions, 128 deletions
diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 93b852f8a5d5..d207a97a2db1 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c | |||
@@ -23,7 +23,6 @@ static char const *input_old = "perf.data.old", | |||
23 | *input_new = "perf.data"; | 23 | *input_new = "perf.data"; |
24 | static char diff__default_sort_order[] = "dso,symbol"; | 24 | static char diff__default_sort_order[] = "dso,symbol"; |
25 | static bool force; | 25 | static bool force; |
26 | static bool show_displacement; | ||
27 | static bool show_period; | 26 | static bool show_period; |
28 | static bool show_formula; | 27 | static bool show_formula; |
29 | static bool show_baseline_only; | 28 | static bool show_baseline_only; |
@@ -146,58 +145,47 @@ static int setup_compute(const struct option *opt, const char *str, | |||
146 | return -EINVAL; | 145 | return -EINVAL; |
147 | } | 146 | } |
148 | 147 | ||
149 | static double get_period_percent(struct hist_entry *he, u64 period) | 148 | double perf_diff__period_percent(struct hist_entry *he, u64 period) |
150 | { | 149 | { |
151 | u64 total = he->hists->stats.total_period; | 150 | u64 total = he->hists->stats.total_period; |
152 | return (period * 100.0) / total; | 151 | return (period * 100.0) / total; |
153 | } | 152 | } |
154 | 153 | ||
155 | double perf_diff__compute_delta(struct hist_entry *he) | 154 | double perf_diff__compute_delta(struct hist_entry *he, struct hist_entry *pair) |
156 | { | 155 | { |
157 | struct hist_entry *pair = hist_entry__next_pair(he); | 156 | double new_percent = perf_diff__period_percent(he, he->stat.period); |
158 | double new_percent = get_period_percent(he, he->stat.period); | 157 | double old_percent = perf_diff__period_percent(pair, pair->stat.period); |
159 | double old_percent = pair ? get_period_percent(pair, pair->stat.period) : 0.0; | ||
160 | 158 | ||
161 | he->diff.period_ratio_delta = new_percent - old_percent; | 159 | he->diff.period_ratio_delta = new_percent - old_percent; |
162 | he->diff.computed = true; | 160 | he->diff.computed = true; |
163 | return he->diff.period_ratio_delta; | 161 | return he->diff.period_ratio_delta; |
164 | } | 162 | } |
165 | 163 | ||
166 | double perf_diff__compute_ratio(struct hist_entry *he) | 164 | double perf_diff__compute_ratio(struct hist_entry *he, struct hist_entry *pair) |
167 | { | 165 | { |
168 | struct hist_entry *pair = hist_entry__next_pair(he); | ||
169 | double new_period = he->stat.period; | 166 | double new_period = he->stat.period; |
170 | double old_period = pair ? pair->stat.period : 0; | 167 | double old_period = pair->stat.period; |
171 | 168 | ||
172 | he->diff.computed = true; | 169 | he->diff.computed = true; |
173 | he->diff.period_ratio = pair ? (new_period / old_period) : 0; | 170 | he->diff.period_ratio = new_period / old_period; |
174 | return he->diff.period_ratio; | 171 | return he->diff.period_ratio; |
175 | } | 172 | } |
176 | 173 | ||
177 | s64 perf_diff__compute_wdiff(struct hist_entry *he) | 174 | s64 perf_diff__compute_wdiff(struct hist_entry *he, struct hist_entry *pair) |
178 | { | 175 | { |
179 | struct hist_entry *pair = hist_entry__next_pair(he); | ||
180 | u64 new_period = he->stat.period; | 176 | u64 new_period = he->stat.period; |
181 | u64 old_period = pair ? pair->stat.period : 0; | 177 | u64 old_period = pair->stat.period; |
182 | 178 | ||
183 | he->diff.computed = true; | 179 | he->diff.computed = true; |
184 | 180 | he->diff.wdiff = new_period * compute_wdiff_w2 - | |
185 | if (!pair) | 181 | old_period * compute_wdiff_w1; |
186 | he->diff.wdiff = 0; | ||
187 | else | ||
188 | he->diff.wdiff = new_period * compute_wdiff_w2 - | ||
189 | old_period * compute_wdiff_w1; | ||
190 | 182 | ||
191 | return he->diff.wdiff; | 183 | return he->diff.wdiff; |
192 | } | 184 | } |
193 | 185 | ||
194 | static int formula_delta(struct hist_entry *he, char *buf, size_t size) | 186 | static int formula_delta(struct hist_entry *he, struct hist_entry *pair, |
187 | char *buf, size_t size) | ||
195 | { | 188 | { |
196 | struct hist_entry *pair = hist_entry__next_pair(he); | ||
197 | |||
198 | if (!pair) | ||
199 | return -1; | ||
200 | |||
201 | return scnprintf(buf, size, | 189 | return scnprintf(buf, size, |
202 | "(%" PRIu64 " * 100 / %" PRIu64 ") - " | 190 | "(%" PRIu64 " * 100 / %" PRIu64 ") - " |
203 | "(%" PRIu64 " * 100 / %" PRIu64 ")", | 191 | "(%" PRIu64 " * 100 / %" PRIu64 ")", |
@@ -205,41 +193,36 @@ static int formula_delta(struct hist_entry *he, char *buf, size_t size) | |||
205 | pair->stat.period, pair->hists->stats.total_period); | 193 | pair->stat.period, pair->hists->stats.total_period); |
206 | } | 194 | } |
207 | 195 | ||
208 | static int formula_ratio(struct hist_entry *he, char *buf, size_t size) | 196 | static int formula_ratio(struct hist_entry *he, struct hist_entry *pair, |
197 | char *buf, size_t size) | ||
209 | { | 198 | { |
210 | struct hist_entry *pair = hist_entry__next_pair(he); | ||
211 | double new_period = he->stat.period; | 199 | double new_period = he->stat.period; |
212 | double old_period = pair ? pair->stat.period : 0; | 200 | double old_period = pair->stat.period; |
213 | |||
214 | if (!pair) | ||
215 | return -1; | ||
216 | 201 | ||
217 | return scnprintf(buf, size, "%.0F / %.0F", new_period, old_period); | 202 | return scnprintf(buf, size, "%.0F / %.0F", new_period, old_period); |
218 | } | 203 | } |
219 | 204 | ||
220 | static int formula_wdiff(struct hist_entry *he, char *buf, size_t size) | 205 | static int formula_wdiff(struct hist_entry *he, struct hist_entry *pair, |
206 | char *buf, size_t size) | ||
221 | { | 207 | { |
222 | struct hist_entry *pair = hist_entry__next_pair(he); | ||
223 | u64 new_period = he->stat.period; | 208 | u64 new_period = he->stat.period; |
224 | u64 old_period = pair ? pair->stat.period : 0; | 209 | u64 old_period = pair->stat.period; |
225 | |||
226 | if (!pair) | ||
227 | return -1; | ||
228 | 210 | ||
229 | return scnprintf(buf, size, | 211 | return scnprintf(buf, size, |
230 | "(%" PRIu64 " * " "%" PRId64 ") - (%" PRIu64 " * " "%" PRId64 ")", | 212 | "(%" PRIu64 " * " "%" PRId64 ") - (%" PRIu64 " * " "%" PRId64 ")", |
231 | new_period, compute_wdiff_w2, old_period, compute_wdiff_w1); | 213 | new_period, compute_wdiff_w2, old_period, compute_wdiff_w1); |
232 | } | 214 | } |
233 | 215 | ||
234 | int perf_diff__formula(char *buf, size_t size, struct hist_entry *he) | 216 | int perf_diff__formula(struct hist_entry *he, struct hist_entry *pair, |
217 | char *buf, size_t size) | ||
235 | { | 218 | { |
236 | switch (compute) { | 219 | switch (compute) { |
237 | case COMPUTE_DELTA: | 220 | case COMPUTE_DELTA: |
238 | return formula_delta(he, buf, size); | 221 | return formula_delta(he, pair, buf, size); |
239 | case COMPUTE_RATIO: | 222 | case COMPUTE_RATIO: |
240 | return formula_ratio(he, buf, size); | 223 | return formula_ratio(he, pair, buf, size); |
241 | case COMPUTE_WEIGHTED_DIFF: | 224 | case COMPUTE_WEIGHTED_DIFF: |
242 | return formula_wdiff(he, buf, size); | 225 | return formula_wdiff(he, pair, buf, size); |
243 | default: | 226 | default: |
244 | BUG_ON(1); | 227 | BUG_ON(1); |
245 | } | 228 | } |
@@ -292,48 +275,6 @@ static struct perf_tool tool = { | |||
292 | .ordering_requires_timestamps = true, | 275 | .ordering_requires_timestamps = true, |
293 | }; | 276 | }; |
294 | 277 | ||
295 | static void insert_hist_entry_by_name(struct rb_root *root, | ||
296 | struct hist_entry *he) | ||
297 | { | ||
298 | struct rb_node **p = &root->rb_node; | ||
299 | struct rb_node *parent = NULL; | ||
300 | struct hist_entry *iter; | ||
301 | |||
302 | while (*p != NULL) { | ||
303 | parent = *p; | ||
304 | iter = rb_entry(parent, struct hist_entry, rb_node); | ||
305 | if (hist_entry__cmp(he, iter) < 0) | ||
306 | p = &(*p)->rb_left; | ||
307 | else | ||
308 | p = &(*p)->rb_right; | ||
309 | } | ||
310 | |||
311 | rb_link_node(&he->rb_node, parent, p); | ||
312 | rb_insert_color(&he->rb_node, root); | ||
313 | } | ||
314 | |||
315 | static void hists__name_resort(struct hists *self, bool sort) | ||
316 | { | ||
317 | unsigned long position = 1; | ||
318 | struct rb_root tmp = RB_ROOT; | ||
319 | struct rb_node *next = rb_first(&self->entries); | ||
320 | |||
321 | while (next != NULL) { | ||
322 | struct hist_entry *n = rb_entry(next, struct hist_entry, rb_node); | ||
323 | |||
324 | next = rb_next(&n->rb_node); | ||
325 | n->position = position++; | ||
326 | |||
327 | if (sort) { | ||
328 | rb_erase(&n->rb_node, &self->entries); | ||
329 | insert_hist_entry_by_name(&tmp, n); | ||
330 | } | ||
331 | } | ||
332 | |||
333 | if (sort) | ||
334 | self->entries = tmp; | ||
335 | } | ||
336 | |||
337 | static struct perf_evsel *evsel_match(struct perf_evsel *evsel, | 278 | static struct perf_evsel *evsel_match(struct perf_evsel *evsel, |
338 | struct perf_evlist *evlist) | 279 | struct perf_evlist *evlist) |
339 | { | 280 | { |
@@ -346,34 +287,34 @@ static struct perf_evsel *evsel_match(struct perf_evsel *evsel, | |||
346 | return NULL; | 287 | return NULL; |
347 | } | 288 | } |
348 | 289 | ||
349 | static void perf_evlist__resort_hists(struct perf_evlist *evlist, bool name) | 290 | static void perf_evlist__collapse_resort(struct perf_evlist *evlist) |
350 | { | 291 | { |
351 | struct perf_evsel *evsel; | 292 | struct perf_evsel *evsel; |
352 | 293 | ||
353 | list_for_each_entry(evsel, &evlist->entries, node) { | 294 | list_for_each_entry(evsel, &evlist->entries, node) { |
354 | struct hists *hists = &evsel->hists; | 295 | struct hists *hists = &evsel->hists; |
355 | 296 | ||
356 | hists__output_resort(hists); | 297 | hists__collapse_resort(hists); |
357 | |||
358 | /* | ||
359 | * The hists__name_resort only sets possition | ||
360 | * if name is false. | ||
361 | */ | ||
362 | if (name || ((!name) && show_displacement)) | ||
363 | hists__name_resort(hists, name); | ||
364 | } | 298 | } |
365 | } | 299 | } |
366 | 300 | ||
367 | static void hists__baseline_only(struct hists *hists) | 301 | static void hists__baseline_only(struct hists *hists) |
368 | { | 302 | { |
369 | struct rb_node *next = rb_first(&hists->entries); | 303 | struct rb_root *root; |
304 | struct rb_node *next; | ||
370 | 305 | ||
306 | if (sort__need_collapse) | ||
307 | root = &hists->entries_collapsed; | ||
308 | else | ||
309 | root = hists->entries_in; | ||
310 | |||
311 | next = rb_first(root); | ||
371 | while (next != NULL) { | 312 | while (next != NULL) { |
372 | struct hist_entry *he = rb_entry(next, struct hist_entry, rb_node); | 313 | struct hist_entry *he = rb_entry(next, struct hist_entry, rb_node_in); |
373 | 314 | ||
374 | next = rb_next(&he->rb_node); | 315 | next = rb_next(&he->rb_node_in); |
375 | if (!hist_entry__next_pair(he)) { | 316 | if (!hist_entry__next_pair(he)) { |
376 | rb_erase(&he->rb_node, &hists->entries); | 317 | rb_erase(&he->rb_node_in, root); |
377 | hist_entry__free(he); | 318 | hist_entry__free(he); |
378 | } | 319 | } |
379 | } | 320 | } |
@@ -385,18 +326,21 @@ static void hists__precompute(struct hists *hists) | |||
385 | 326 | ||
386 | while (next != NULL) { | 327 | while (next != NULL) { |
387 | struct hist_entry *he = rb_entry(next, struct hist_entry, rb_node); | 328 | struct hist_entry *he = rb_entry(next, struct hist_entry, rb_node); |
329 | struct hist_entry *pair = hist_entry__next_pair(he); | ||
388 | 330 | ||
389 | next = rb_next(&he->rb_node); | 331 | next = rb_next(&he->rb_node); |
332 | if (!pair) | ||
333 | continue; | ||
390 | 334 | ||
391 | switch (compute) { | 335 | switch (compute) { |
392 | case COMPUTE_DELTA: | 336 | case COMPUTE_DELTA: |
393 | perf_diff__compute_delta(he); | 337 | perf_diff__compute_delta(he, pair); |
394 | break; | 338 | break; |
395 | case COMPUTE_RATIO: | 339 | case COMPUTE_RATIO: |
396 | perf_diff__compute_ratio(he); | 340 | perf_diff__compute_ratio(he, pair); |
397 | break; | 341 | break; |
398 | case COMPUTE_WEIGHTED_DIFF: | 342 | case COMPUTE_WEIGHTED_DIFF: |
399 | perf_diff__compute_wdiff(he); | 343 | perf_diff__compute_wdiff(he, pair); |
400 | break; | 344 | break; |
401 | default: | 345 | default: |
402 | BUG_ON(1); | 346 | BUG_ON(1); |
@@ -470,19 +414,30 @@ static void insert_hist_entry_by_compute(struct rb_root *root, | |||
470 | 414 | ||
471 | static void hists__compute_resort(struct hists *hists) | 415 | static void hists__compute_resort(struct hists *hists) |
472 | { | 416 | { |
473 | struct rb_root tmp = RB_ROOT; | 417 | struct rb_root *root; |
474 | struct rb_node *next = rb_first(&hists->entries); | 418 | struct rb_node *next; |
419 | |||
420 | if (sort__need_collapse) | ||
421 | root = &hists->entries_collapsed; | ||
422 | else | ||
423 | root = hists->entries_in; | ||
424 | |||
425 | hists->entries = RB_ROOT; | ||
426 | next = rb_first(root); | ||
427 | |||
428 | hists->nr_entries = 0; | ||
429 | hists->stats.total_period = 0; | ||
430 | hists__reset_col_len(hists); | ||
475 | 431 | ||
476 | while (next != NULL) { | 432 | while (next != NULL) { |
477 | struct hist_entry *he = rb_entry(next, struct hist_entry, rb_node); | 433 | struct hist_entry *he; |
478 | 434 | ||
479 | next = rb_next(&he->rb_node); | 435 | he = rb_entry(next, struct hist_entry, rb_node_in); |
436 | next = rb_next(&he->rb_node_in); | ||
480 | 437 | ||
481 | rb_erase(&he->rb_node, &hists->entries); | 438 | insert_hist_entry_by_compute(&hists->entries, he, compute); |
482 | insert_hist_entry_by_compute(&tmp, he, compute); | 439 | hists__inc_nr_entries(hists, he); |
483 | } | 440 | } |
484 | |||
485 | hists->entries = tmp; | ||
486 | } | 441 | } |
487 | 442 | ||
488 | static void hists__process(struct hists *old, struct hists *new) | 443 | static void hists__process(struct hists *old, struct hists *new) |
@@ -497,6 +452,8 @@ static void hists__process(struct hists *old, struct hists *new) | |||
497 | if (sort_compute) { | 452 | if (sort_compute) { |
498 | hists__precompute(new); | 453 | hists__precompute(new); |
499 | hists__compute_resort(new); | 454 | hists__compute_resort(new); |
455 | } else { | ||
456 | hists__output_resort(new); | ||
500 | } | 457 | } |
501 | 458 | ||
502 | hists__fprintf(new, true, 0, 0, stdout); | 459 | hists__fprintf(new, true, 0, 0, stdout); |
@@ -528,8 +485,8 @@ static int __cmd_diff(void) | |||
528 | evlist_old = older->evlist; | 485 | evlist_old = older->evlist; |
529 | evlist_new = newer->evlist; | 486 | evlist_new = newer->evlist; |
530 | 487 | ||
531 | perf_evlist__resort_hists(evlist_old, true); | 488 | perf_evlist__collapse_resort(evlist_old); |
532 | perf_evlist__resort_hists(evlist_new, false); | 489 | perf_evlist__collapse_resort(evlist_new); |
533 | 490 | ||
534 | list_for_each_entry(evsel, &evlist_new->entries, node) { | 491 | list_for_each_entry(evsel, &evlist_new->entries, node) { |
535 | struct perf_evsel *evsel_old; | 492 | struct perf_evsel *evsel_old; |
@@ -562,8 +519,6 @@ static const char * const diff_usage[] = { | |||
562 | static const struct option options[] = { | 519 | static const struct option options[] = { |
563 | OPT_INCR('v', "verbose", &verbose, | 520 | OPT_INCR('v', "verbose", &verbose, |
564 | "be more verbose (show symbol address, etc)"), | 521 | "be more verbose (show symbol address, etc)"), |
565 | OPT_BOOLEAN('M', "displacement", &show_displacement, | ||
566 | "Show position displacement relative to baseline"), | ||
567 | OPT_BOOLEAN('b', "baseline-only", &show_baseline_only, | 522 | OPT_BOOLEAN('b', "baseline-only", &show_baseline_only, |
568 | "Show only items with match in baseline"), | 523 | "Show only items with match in baseline"), |
569 | OPT_CALLBACK('c', "compute", &compute, | 524 | OPT_CALLBACK('c', "compute", &compute, |
@@ -597,40 +552,32 @@ static const struct option options[] = { | |||
597 | 552 | ||
598 | static void ui_init(void) | 553 | static void ui_init(void) |
599 | { | 554 | { |
600 | perf_hpp__init(); | ||
601 | |||
602 | /* No overhead column. */ | ||
603 | perf_hpp__column_enable(PERF_HPP__OVERHEAD, false); | ||
604 | |||
605 | /* | 555 | /* |
606 | * Display baseline/delta/ratio/displacement/ | 556 | * Display baseline/delta/ratio |
607 | * formula/periods columns. | 557 | * formula/periods columns. |
608 | */ | 558 | */ |
609 | perf_hpp__column_enable(PERF_HPP__BASELINE, true); | 559 | perf_hpp__column_enable(PERF_HPP__BASELINE); |
610 | 560 | ||
611 | switch (compute) { | 561 | switch (compute) { |
612 | case COMPUTE_DELTA: | 562 | case COMPUTE_DELTA: |
613 | perf_hpp__column_enable(PERF_HPP__DELTA, true); | 563 | perf_hpp__column_enable(PERF_HPP__DELTA); |
614 | break; | 564 | break; |
615 | case COMPUTE_RATIO: | 565 | case COMPUTE_RATIO: |
616 | perf_hpp__column_enable(PERF_HPP__RATIO, true); | 566 | perf_hpp__column_enable(PERF_HPP__RATIO); |
617 | break; | 567 | break; |
618 | case COMPUTE_WEIGHTED_DIFF: | 568 | case COMPUTE_WEIGHTED_DIFF: |
619 | perf_hpp__column_enable(PERF_HPP__WEIGHTED_DIFF, true); | 569 | perf_hpp__column_enable(PERF_HPP__WEIGHTED_DIFF); |
620 | break; | 570 | break; |
621 | default: | 571 | default: |
622 | BUG_ON(1); | 572 | BUG_ON(1); |
623 | }; | 573 | }; |
624 | 574 | ||
625 | if (show_displacement) | ||
626 | perf_hpp__column_enable(PERF_HPP__DISPL, true); | ||
627 | |||
628 | if (show_formula) | 575 | if (show_formula) |
629 | perf_hpp__column_enable(PERF_HPP__FORMULA, true); | 576 | perf_hpp__column_enable(PERF_HPP__FORMULA); |
630 | 577 | ||
631 | if (show_period) { | 578 | if (show_period) { |
632 | perf_hpp__column_enable(PERF_HPP__PERIOD, true); | 579 | perf_hpp__column_enable(PERF_HPP__PERIOD); |
633 | perf_hpp__column_enable(PERF_HPP__PERIOD_BASELINE, true); | 580 | perf_hpp__column_enable(PERF_HPP__PERIOD_BASELINE); |
634 | } | 581 | } |
635 | } | 582 | } |
636 | 583 | ||
@@ -658,7 +605,9 @@ int cmd_diff(int argc, const char **argv, const char *prefix __maybe_unused) | |||
658 | 605 | ||
659 | ui_init(); | 606 | ui_init(); |
660 | 607 | ||
661 | setup_sorting(diff_usage, options); | 608 | if (setup_sorting() < 0) |
609 | usage_with_options(diff_usage, options); | ||
610 | |||
662 | setup_pager(); | 611 | setup_pager(); |
663 | 612 | ||
664 | sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "dso", NULL); | 613 | sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "dso", NULL); |