diff options
Diffstat (limited to 'tools/perf/ui/hist.c')
-rw-r--r-- | tools/perf/ui/hist.c | 262 |
1 files changed, 182 insertions, 80 deletions
diff --git a/tools/perf/ui/hist.c b/tools/perf/ui/hist.c index bf2a66e254ea..3baeaa6e71b5 100644 --- a/tools/perf/ui/hist.c +++ b/tools/perf/ui/hist.c | |||
@@ -5,6 +5,7 @@ | |||
5 | #include "../util/util.h" | 5 | #include "../util/util.h" |
6 | #include "../util/sort.h" | 6 | #include "../util/sort.h" |
7 | #include "../util/evsel.h" | 7 | #include "../util/evsel.h" |
8 | #include "../util/evlist.h" | ||
8 | 9 | ||
9 | /* hist period print (hpp) functions */ | 10 | /* hist period print (hpp) functions */ |
10 | 11 | ||
@@ -371,7 +372,20 @@ static int64_t hpp__nop_cmp(struct perf_hpp_fmt *fmt __maybe_unused, | |||
371 | return 0; | 372 | return 0; |
372 | } | 373 | } |
373 | 374 | ||
374 | #define HPP__COLOR_PRINT_FNS(_name, _fn) \ | 375 | static bool perf_hpp__is_hpp_entry(struct perf_hpp_fmt *a) |
376 | { | ||
377 | return a->header == hpp__header_fn; | ||
378 | } | ||
379 | |||
380 | static bool hpp__equal(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b) | ||
381 | { | ||
382 | if (!perf_hpp__is_hpp_entry(a) || !perf_hpp__is_hpp_entry(b)) | ||
383 | return false; | ||
384 | |||
385 | return a->idx == b->idx; | ||
386 | } | ||
387 | |||
388 | #define HPP__COLOR_PRINT_FNS(_name, _fn, _idx) \ | ||
375 | { \ | 389 | { \ |
376 | .name = _name, \ | 390 | .name = _name, \ |
377 | .header = hpp__header_fn, \ | 391 | .header = hpp__header_fn, \ |
@@ -381,9 +395,11 @@ static int64_t hpp__nop_cmp(struct perf_hpp_fmt *fmt __maybe_unused, | |||
381 | .cmp = hpp__nop_cmp, \ | 395 | .cmp = hpp__nop_cmp, \ |
382 | .collapse = hpp__nop_cmp, \ | 396 | .collapse = hpp__nop_cmp, \ |
383 | .sort = hpp__sort_ ## _fn, \ | 397 | .sort = hpp__sort_ ## _fn, \ |
398 | .idx = PERF_HPP__ ## _idx, \ | ||
399 | .equal = hpp__equal, \ | ||
384 | } | 400 | } |
385 | 401 | ||
386 | #define HPP__COLOR_ACC_PRINT_FNS(_name, _fn) \ | 402 | #define HPP__COLOR_ACC_PRINT_FNS(_name, _fn, _idx) \ |
387 | { \ | 403 | { \ |
388 | .name = _name, \ | 404 | .name = _name, \ |
389 | .header = hpp__header_fn, \ | 405 | .header = hpp__header_fn, \ |
@@ -393,9 +409,11 @@ static int64_t hpp__nop_cmp(struct perf_hpp_fmt *fmt __maybe_unused, | |||
393 | .cmp = hpp__nop_cmp, \ | 409 | .cmp = hpp__nop_cmp, \ |
394 | .collapse = hpp__nop_cmp, \ | 410 | .collapse = hpp__nop_cmp, \ |
395 | .sort = hpp__sort_ ## _fn, \ | 411 | .sort = hpp__sort_ ## _fn, \ |
412 | .idx = PERF_HPP__ ## _idx, \ | ||
413 | .equal = hpp__equal, \ | ||
396 | } | 414 | } |
397 | 415 | ||
398 | #define HPP__PRINT_FNS(_name, _fn) \ | 416 | #define HPP__PRINT_FNS(_name, _fn, _idx) \ |
399 | { \ | 417 | { \ |
400 | .name = _name, \ | 418 | .name = _name, \ |
401 | .header = hpp__header_fn, \ | 419 | .header = hpp__header_fn, \ |
@@ -404,22 +422,25 @@ static int64_t hpp__nop_cmp(struct perf_hpp_fmt *fmt __maybe_unused, | |||
404 | .cmp = hpp__nop_cmp, \ | 422 | .cmp = hpp__nop_cmp, \ |
405 | .collapse = hpp__nop_cmp, \ | 423 | .collapse = hpp__nop_cmp, \ |
406 | .sort = hpp__sort_ ## _fn, \ | 424 | .sort = hpp__sort_ ## _fn, \ |
425 | .idx = PERF_HPP__ ## _idx, \ | ||
426 | .equal = hpp__equal, \ | ||
407 | } | 427 | } |
408 | 428 | ||
409 | struct perf_hpp_fmt perf_hpp__format[] = { | 429 | struct perf_hpp_fmt perf_hpp__format[] = { |
410 | HPP__COLOR_PRINT_FNS("Overhead", overhead), | 430 | HPP__COLOR_PRINT_FNS("Overhead", overhead, OVERHEAD), |
411 | HPP__COLOR_PRINT_FNS("sys", overhead_sys), | 431 | HPP__COLOR_PRINT_FNS("sys", overhead_sys, OVERHEAD_SYS), |
412 | HPP__COLOR_PRINT_FNS("usr", overhead_us), | 432 | HPP__COLOR_PRINT_FNS("usr", overhead_us, OVERHEAD_US), |
413 | HPP__COLOR_PRINT_FNS("guest sys", overhead_guest_sys), | 433 | HPP__COLOR_PRINT_FNS("guest sys", overhead_guest_sys, OVERHEAD_GUEST_SYS), |
414 | HPP__COLOR_PRINT_FNS("guest usr", overhead_guest_us), | 434 | HPP__COLOR_PRINT_FNS("guest usr", overhead_guest_us, OVERHEAD_GUEST_US), |
415 | HPP__COLOR_ACC_PRINT_FNS("Children", overhead_acc), | 435 | HPP__COLOR_ACC_PRINT_FNS("Children", overhead_acc, OVERHEAD_ACC), |
416 | HPP__PRINT_FNS("Samples", samples), | 436 | HPP__PRINT_FNS("Samples", samples, SAMPLES), |
417 | HPP__PRINT_FNS("Period", period) | 437 | HPP__PRINT_FNS("Period", period, PERIOD) |
418 | }; | 438 | }; |
419 | 439 | ||
420 | LIST_HEAD(perf_hpp__list); | 440 | struct perf_hpp_list perf_hpp_list = { |
421 | LIST_HEAD(perf_hpp__sort_list); | 441 | .fields = LIST_HEAD_INIT(perf_hpp_list.fields), |
422 | 442 | .sorts = LIST_HEAD_INIT(perf_hpp_list.sorts), | |
443 | }; | ||
423 | 444 | ||
424 | #undef HPP__COLOR_PRINT_FNS | 445 | #undef HPP__COLOR_PRINT_FNS |
425 | #undef HPP__COLOR_ACC_PRINT_FNS | 446 | #undef HPP__COLOR_ACC_PRINT_FNS |
@@ -485,63 +506,60 @@ void perf_hpp__init(void) | |||
485 | hpp_dimension__add_output(PERF_HPP__PERIOD); | 506 | hpp_dimension__add_output(PERF_HPP__PERIOD); |
486 | } | 507 | } |
487 | 508 | ||
488 | void perf_hpp__column_register(struct perf_hpp_fmt *format) | 509 | void perf_hpp_list__column_register(struct perf_hpp_list *list, |
510 | struct perf_hpp_fmt *format) | ||
489 | { | 511 | { |
490 | list_add_tail(&format->list, &perf_hpp__list); | 512 | list_add_tail(&format->list, &list->fields); |
491 | } | 513 | } |
492 | 514 | ||
493 | void perf_hpp__column_unregister(struct perf_hpp_fmt *format) | 515 | void perf_hpp_list__register_sort_field(struct perf_hpp_list *list, |
516 | struct perf_hpp_fmt *format) | ||
494 | { | 517 | { |
495 | list_del(&format->list); | 518 | list_add_tail(&format->sort_list, &list->sorts); |
496 | } | 519 | } |
497 | 520 | ||
498 | void perf_hpp__register_sort_field(struct perf_hpp_fmt *format) | 521 | void perf_hpp__column_unregister(struct perf_hpp_fmt *format) |
499 | { | ||
500 | list_add_tail(&format->sort_list, &perf_hpp__sort_list); | ||
501 | } | ||
502 | |||
503 | void perf_hpp__column_enable(unsigned col) | ||
504 | { | ||
505 | BUG_ON(col >= PERF_HPP__MAX_INDEX); | ||
506 | perf_hpp__column_register(&perf_hpp__format[col]); | ||
507 | } | ||
508 | |||
509 | void perf_hpp__column_disable(unsigned col) | ||
510 | { | 522 | { |
511 | BUG_ON(col >= PERF_HPP__MAX_INDEX); | 523 | list_del(&format->list); |
512 | perf_hpp__column_unregister(&perf_hpp__format[col]); | ||
513 | } | 524 | } |
514 | 525 | ||
515 | void perf_hpp__cancel_cumulate(void) | 526 | void perf_hpp__cancel_cumulate(void) |
516 | { | 527 | { |
528 | struct perf_hpp_fmt *fmt, *acc, *ovh, *tmp; | ||
529 | |||
517 | if (is_strict_order(field_order)) | 530 | if (is_strict_order(field_order)) |
518 | return; | 531 | return; |
519 | 532 | ||
520 | perf_hpp__column_disable(PERF_HPP__OVERHEAD_ACC); | 533 | ovh = &perf_hpp__format[PERF_HPP__OVERHEAD]; |
521 | perf_hpp__format[PERF_HPP__OVERHEAD].name = "Overhead"; | 534 | acc = &perf_hpp__format[PERF_HPP__OVERHEAD_ACC]; |
535 | |||
536 | perf_hpp_list__for_each_format_safe(&perf_hpp_list, fmt, tmp) { | ||
537 | if (acc->equal(acc, fmt)) { | ||
538 | perf_hpp__column_unregister(fmt); | ||
539 | continue; | ||
540 | } | ||
541 | |||
542 | if (ovh->equal(ovh, fmt)) | ||
543 | fmt->name = "Overhead"; | ||
544 | } | ||
522 | } | 545 | } |
523 | 546 | ||
524 | void perf_hpp__setup_output_field(void) | 547 | static bool fmt_equal(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b) |
548 | { | ||
549 | return a->equal && a->equal(a, b); | ||
550 | } | ||
551 | |||
552 | void perf_hpp__setup_output_field(struct perf_hpp_list *list) | ||
525 | { | 553 | { |
526 | struct perf_hpp_fmt *fmt; | 554 | struct perf_hpp_fmt *fmt; |
527 | 555 | ||
528 | /* append sort keys to output field */ | 556 | /* append sort keys to output field */ |
529 | perf_hpp__for_each_sort_list(fmt) { | 557 | perf_hpp_list__for_each_sort_list(list, fmt) { |
530 | if (!list_empty(&fmt->list)) | 558 | struct perf_hpp_fmt *pos; |
531 | continue; | ||
532 | |||
533 | /* | ||
534 | * sort entry fields are dynamically created, | ||
535 | * so they can share a same sort key even though | ||
536 | * the list is empty. | ||
537 | */ | ||
538 | if (perf_hpp__is_sort_entry(fmt)) { | ||
539 | struct perf_hpp_fmt *pos; | ||
540 | 559 | ||
541 | perf_hpp__for_each_format(pos) { | 560 | perf_hpp_list__for_each_format(list, pos) { |
542 | if (perf_hpp__same_sort_entry(pos, fmt)) | 561 | if (fmt_equal(fmt, pos)) |
543 | goto next; | 562 | goto next; |
544 | } | ||
545 | } | 563 | } |
546 | 564 | ||
547 | perf_hpp__column_register(fmt); | 565 | perf_hpp__column_register(fmt); |
@@ -550,27 +568,17 @@ next: | |||
550 | } | 568 | } |
551 | } | 569 | } |
552 | 570 | ||
553 | void perf_hpp__append_sort_keys(void) | 571 | void perf_hpp__append_sort_keys(struct perf_hpp_list *list) |
554 | { | 572 | { |
555 | struct perf_hpp_fmt *fmt; | 573 | struct perf_hpp_fmt *fmt; |
556 | 574 | ||
557 | /* append output fields to sort keys */ | 575 | /* append output fields to sort keys */ |
558 | perf_hpp__for_each_format(fmt) { | 576 | perf_hpp_list__for_each_format(list, fmt) { |
559 | if (!list_empty(&fmt->sort_list)) | 577 | struct perf_hpp_fmt *pos; |
560 | continue; | ||
561 | |||
562 | /* | ||
563 | * sort entry fields are dynamically created, | ||
564 | * so they can share a same sort key even though | ||
565 | * the list is empty. | ||
566 | */ | ||
567 | if (perf_hpp__is_sort_entry(fmt)) { | ||
568 | struct perf_hpp_fmt *pos; | ||
569 | 578 | ||
570 | perf_hpp__for_each_sort_list(pos) { | 579 | perf_hpp_list__for_each_sort_list(list, pos) { |
571 | if (perf_hpp__same_sort_entry(pos, fmt)) | 580 | if (fmt_equal(fmt, pos)) |
572 | goto next; | 581 | goto next; |
573 | } | ||
574 | } | 582 | } |
575 | 583 | ||
576 | perf_hpp__register_sort_field(fmt); | 584 | perf_hpp__register_sort_field(fmt); |
@@ -579,20 +587,29 @@ next: | |||
579 | } | 587 | } |
580 | } | 588 | } |
581 | 589 | ||
582 | void perf_hpp__reset_output_field(void) | 590 | |
591 | static void fmt_free(struct perf_hpp_fmt *fmt) | ||
592 | { | ||
593 | if (fmt->free) | ||
594 | fmt->free(fmt); | ||
595 | } | ||
596 | |||
597 | void perf_hpp__reset_output_field(struct perf_hpp_list *list) | ||
583 | { | 598 | { |
584 | struct perf_hpp_fmt *fmt, *tmp; | 599 | struct perf_hpp_fmt *fmt, *tmp; |
585 | 600 | ||
586 | /* reset output fields */ | 601 | /* reset output fields */ |
587 | perf_hpp__for_each_format_safe(fmt, tmp) { | 602 | perf_hpp_list__for_each_format_safe(list, fmt, tmp) { |
588 | list_del_init(&fmt->list); | 603 | list_del_init(&fmt->list); |
589 | list_del_init(&fmt->sort_list); | 604 | list_del_init(&fmt->sort_list); |
605 | fmt_free(fmt); | ||
590 | } | 606 | } |
591 | 607 | ||
592 | /* reset sort keys */ | 608 | /* reset sort keys */ |
593 | perf_hpp__for_each_sort_list_safe(fmt, tmp) { | 609 | perf_hpp_list__for_each_sort_list_safe(list, fmt, tmp) { |
594 | list_del_init(&fmt->list); | 610 | list_del_init(&fmt->list); |
595 | list_del_init(&fmt->sort_list); | 611 | list_del_init(&fmt->sort_list); |
612 | fmt_free(fmt); | ||
596 | } | 613 | } |
597 | } | 614 | } |
598 | 615 | ||
@@ -606,7 +623,7 @@ unsigned int hists__sort_list_width(struct hists *hists) | |||
606 | bool first = true; | 623 | bool first = true; |
607 | struct perf_hpp dummy_hpp; | 624 | struct perf_hpp dummy_hpp; |
608 | 625 | ||
609 | perf_hpp__for_each_format(fmt) { | 626 | hists__for_each_format(hists, fmt) { |
610 | if (perf_hpp__should_skip(fmt, hists)) | 627 | if (perf_hpp__should_skip(fmt, hists)) |
611 | continue; | 628 | continue; |
612 | 629 | ||
@@ -624,22 +641,39 @@ unsigned int hists__sort_list_width(struct hists *hists) | |||
624 | return ret; | 641 | return ret; |
625 | } | 642 | } |
626 | 643 | ||
627 | void perf_hpp__reset_width(struct perf_hpp_fmt *fmt, struct hists *hists) | 644 | unsigned int hists__overhead_width(struct hists *hists) |
628 | { | 645 | { |
629 | int idx; | 646 | struct perf_hpp_fmt *fmt; |
630 | 647 | int ret = 0; | |
631 | if (perf_hpp__is_sort_entry(fmt)) | 648 | bool first = true; |
632 | return perf_hpp__reset_sort_width(fmt, hists); | 649 | struct perf_hpp dummy_hpp; |
633 | 650 | ||
634 | for (idx = 0; idx < PERF_HPP__MAX_INDEX; idx++) { | 651 | hists__for_each_format(hists, fmt) { |
635 | if (fmt == &perf_hpp__format[idx]) | 652 | if (perf_hpp__is_sort_entry(fmt) || perf_hpp__is_dynamic_entry(fmt)) |
636 | break; | 653 | break; |
654 | |||
655 | if (first) | ||
656 | first = false; | ||
657 | else | ||
658 | ret += 2; | ||
659 | |||
660 | ret += fmt->width(fmt, &dummy_hpp, hists_to_evsel(hists)); | ||
637 | } | 661 | } |
638 | 662 | ||
639 | if (idx == PERF_HPP__MAX_INDEX) | 663 | return ret; |
664 | } | ||
665 | |||
666 | void perf_hpp__reset_width(struct perf_hpp_fmt *fmt, struct hists *hists) | ||
667 | { | ||
668 | if (perf_hpp__is_sort_entry(fmt)) | ||
669 | return perf_hpp__reset_sort_width(fmt, hists); | ||
670 | |||
671 | if (perf_hpp__is_dynamic_entry(fmt)) | ||
640 | return; | 672 | return; |
641 | 673 | ||
642 | switch (idx) { | 674 | BUG_ON(fmt->idx >= PERF_HPP__MAX_INDEX); |
675 | |||
676 | switch (fmt->idx) { | ||
643 | case PERF_HPP__OVERHEAD: | 677 | case PERF_HPP__OVERHEAD: |
644 | case PERF_HPP__OVERHEAD_SYS: | 678 | case PERF_HPP__OVERHEAD_SYS: |
645 | case PERF_HPP__OVERHEAD_US: | 679 | case PERF_HPP__OVERHEAD_US: |
@@ -667,7 +701,7 @@ void perf_hpp__set_user_width(const char *width_list_str) | |||
667 | struct perf_hpp_fmt *fmt; | 701 | struct perf_hpp_fmt *fmt; |
668 | const char *ptr = width_list_str; | 702 | const char *ptr = width_list_str; |
669 | 703 | ||
670 | perf_hpp__for_each_format(fmt) { | 704 | perf_hpp_list__for_each_format(&perf_hpp_list, fmt) { |
671 | char *p; | 705 | char *p; |
672 | 706 | ||
673 | int len = strtol(ptr, &p, 10); | 707 | int len = strtol(ptr, &p, 10); |
@@ -679,3 +713,71 @@ void perf_hpp__set_user_width(const char *width_list_str) | |||
679 | break; | 713 | break; |
680 | } | 714 | } |
681 | } | 715 | } |
716 | |||
717 | static int add_hierarchy_fmt(struct hists *hists, struct perf_hpp_fmt *fmt) | ||
718 | { | ||
719 | struct perf_hpp_list_node *node = NULL; | ||
720 | struct perf_hpp_fmt *fmt_copy; | ||
721 | bool found = false; | ||
722 | bool skip = perf_hpp__should_skip(fmt, hists); | ||
723 | |||
724 | list_for_each_entry(node, &hists->hpp_formats, list) { | ||
725 | if (node->level == fmt->level) { | ||
726 | found = true; | ||
727 | break; | ||
728 | } | ||
729 | } | ||
730 | |||
731 | if (!found) { | ||
732 | node = malloc(sizeof(*node)); | ||
733 | if (node == NULL) | ||
734 | return -1; | ||
735 | |||
736 | node->skip = skip; | ||
737 | node->level = fmt->level; | ||
738 | perf_hpp_list__init(&node->hpp); | ||
739 | |||
740 | hists->nr_hpp_node++; | ||
741 | list_add_tail(&node->list, &hists->hpp_formats); | ||
742 | } | ||
743 | |||
744 | fmt_copy = perf_hpp_fmt__dup(fmt); | ||
745 | if (fmt_copy == NULL) | ||
746 | return -1; | ||
747 | |||
748 | if (!skip) | ||
749 | node->skip = false; | ||
750 | |||
751 | list_add_tail(&fmt_copy->list, &node->hpp.fields); | ||
752 | list_add_tail(&fmt_copy->sort_list, &node->hpp.sorts); | ||
753 | |||
754 | return 0; | ||
755 | } | ||
756 | |||
757 | int perf_hpp__setup_hists_formats(struct perf_hpp_list *list, | ||
758 | struct perf_evlist *evlist) | ||
759 | { | ||
760 | struct perf_evsel *evsel; | ||
761 | struct perf_hpp_fmt *fmt; | ||
762 | struct hists *hists; | ||
763 | int ret; | ||
764 | |||
765 | if (!symbol_conf.report_hierarchy) | ||
766 | return 0; | ||
767 | |||
768 | evlist__for_each(evlist, evsel) { | ||
769 | hists = evsel__hists(evsel); | ||
770 | |||
771 | perf_hpp_list__for_each_sort_list(list, fmt) { | ||
772 | if (perf_hpp__is_dynamic_entry(fmt) && | ||
773 | !perf_hpp__defined_dynamic_entry(fmt, hists)) | ||
774 | continue; | ||
775 | |||
776 | ret = add_hierarchy_fmt(hists, fmt); | ||
777 | if (ret < 0) | ||
778 | return ret; | ||
779 | } | ||
780 | } | ||
781 | |||
782 | return 0; | ||
783 | } | ||