diff options
author | Peter Zijlstra <a.p.zijlstra@chello.nl> | 2009-05-27 14:20:24 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2009-05-27 15:44:13 -0400 |
commit | e7fb08b1d06a6b37263c765205de5614a2273aeb (patch) | |
tree | c8548e6b58a386325f11f9c069792136d2f625a8 /Documentation/perf_counter/builtin-report.c | |
parent | 450aaa2b2a1b006870ba68251fbb40b2387caade (diff) |
perf_counter: tools: report: Rework histogram code
In preparation for configurable sorting, rework the histgram code a bit.
Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Corey Ashford <cjashfor@linux.vnet.ibm.com>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: John Kacur <jkacur@redhat.com>
Cc: Mike Galbraith <efault@gmx.de>
LKML-Reference: <20090527182100.796410098@chello.nl>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'Documentation/perf_counter/builtin-report.c')
-rw-r--r-- | Documentation/perf_counter/builtin-report.c | 294 |
1 files changed, 143 insertions, 151 deletions
diff --git a/Documentation/perf_counter/builtin-report.c b/Documentation/perf_counter/builtin-report.c index 3e87cbd3045a..276256439b78 100644 --- a/Documentation/perf_counter/builtin-report.c +++ b/Documentation/perf_counter/builtin-report.c | |||
@@ -597,71 +597,9 @@ struct thread; | |||
597 | 597 | ||
598 | static const char *thread__name(struct thread *self, char *bf, size_t size); | 598 | static const char *thread__name(struct thread *self, char *bf, size_t size); |
599 | 599 | ||
600 | struct symhist { | ||
601 | struct rb_node rb_node; | ||
602 | struct dso *dso; | ||
603 | struct symbol *sym; | ||
604 | struct thread *thread; | ||
605 | uint64_t ip; | ||
606 | uint32_t count; | ||
607 | char level; | ||
608 | }; | ||
609 | |||
610 | static struct symhist *symhist__new(struct symbol *sym, uint64_t ip, | ||
611 | struct thread *thread, struct dso *dso, | ||
612 | char level) | ||
613 | { | ||
614 | struct symhist *self = malloc(sizeof(*self)); | ||
615 | |||
616 | if (self != NULL) { | ||
617 | self->sym = sym; | ||
618 | self->thread = thread; | ||
619 | self->ip = ip; | ||
620 | self->dso = dso; | ||
621 | self->level = level; | ||
622 | self->count = 1; | ||
623 | } | ||
624 | |||
625 | return self; | ||
626 | } | ||
627 | |||
628 | static void symhist__inc(struct symhist *self) | ||
629 | { | ||
630 | ++self->count; | ||
631 | } | ||
632 | |||
633 | static size_t | ||
634 | symhist__fprintf(struct symhist *self, uint64_t total_samples, FILE *fp) | ||
635 | { | ||
636 | char bf[32]; | ||
637 | size_t ret; | ||
638 | |||
639 | if (total_samples) | ||
640 | ret = fprintf(fp, "%5.2f%% ", (self->count * 100.0) / total_samples); | ||
641 | else | ||
642 | ret = fprintf(fp, "%12d ", self->count); | ||
643 | |||
644 | ret += fprintf(fp, "%14s [%c] ", | ||
645 | thread__name(self->thread, bf, sizeof(bf)), | ||
646 | self->level); | ||
647 | |||
648 | if (verbose) | ||
649 | ret += fprintf(fp, "%#018llx ", (unsigned long long)self->ip); | ||
650 | |||
651 | if (self->level != '.') | ||
652 | ret += fprintf(fp, "%s\n", | ||
653 | self->sym ? self->sym->name : "<unknown>"); | ||
654 | else | ||
655 | ret += fprintf(fp, "%s: %s\n", | ||
656 | self->dso ? self->dso->name : "<unknown>", | ||
657 | self->sym ? self->sym->name : "<unknown>"); | ||
658 | return ret; | ||
659 | } | ||
660 | |||
661 | struct thread { | 600 | struct thread { |
662 | struct rb_node rb_node; | 601 | struct rb_node rb_node; |
663 | struct list_head maps; | 602 | struct list_head maps; |
664 | struct rb_root symhists; | ||
665 | pid_t pid; | 603 | pid_t pid; |
666 | char *comm; | 604 | char *comm; |
667 | }; | 605 | }; |
@@ -683,67 +621,17 @@ static struct thread *thread__new(pid_t pid) | |||
683 | self->pid = pid; | 621 | self->pid = pid; |
684 | self->comm = NULL; | 622 | self->comm = NULL; |
685 | INIT_LIST_HEAD(&self->maps); | 623 | INIT_LIST_HEAD(&self->maps); |
686 | self->symhists = RB_ROOT; | ||
687 | } | 624 | } |
688 | 625 | ||
689 | return self; | 626 | return self; |
690 | } | 627 | } |
691 | 628 | ||
692 | static int thread__symbol_incnew(struct thread *self, struct symbol *sym, | ||
693 | uint64_t ip, struct dso *dso, char level) | ||
694 | { | ||
695 | struct rb_node **p = &self->symhists.rb_node; | ||
696 | struct rb_node *parent = NULL; | ||
697 | struct symhist *sh; | ||
698 | |||
699 | while (*p != NULL) { | ||
700 | uint64_t start; | ||
701 | |||
702 | parent = *p; | ||
703 | sh = rb_entry(parent, struct symhist, rb_node); | ||
704 | |||
705 | if (sh->sym == sym || ip == sh->ip) { | ||
706 | symhist__inc(sh); | ||
707 | return 0; | ||
708 | } | ||
709 | |||
710 | /* Handle unresolved symbols too */ | ||
711 | start = !sh->sym ? sh->ip : sh->sym->start; | ||
712 | |||
713 | if (ip < start) | ||
714 | p = &(*p)->rb_left; | ||
715 | else | ||
716 | p = &(*p)->rb_right; | ||
717 | } | ||
718 | |||
719 | sh = symhist__new(sym, ip, self, dso, level); | ||
720 | if (sh == NULL) | ||
721 | return -ENOMEM; | ||
722 | rb_link_node(&sh->rb_node, parent, p); | ||
723 | rb_insert_color(&sh->rb_node, &self->symhists); | ||
724 | return 0; | ||
725 | } | ||
726 | |||
727 | static int thread__set_comm(struct thread *self, const char *comm) | 629 | static int thread__set_comm(struct thread *self, const char *comm) |
728 | { | 630 | { |
729 | self->comm = strdup(comm); | 631 | self->comm = strdup(comm); |
730 | return self->comm ? 0 : -ENOMEM; | 632 | return self->comm ? 0 : -ENOMEM; |
731 | } | 633 | } |
732 | 634 | ||
733 | static size_t thread__fprintf(struct thread *self, FILE *fp) | ||
734 | { | ||
735 | int ret = fprintf(fp, "thread: %d %s\n", self->pid, self->comm); | ||
736 | struct rb_node *nd; | ||
737 | |||
738 | for (nd = rb_first(&self->symhists); nd; nd = rb_next(nd)) { | ||
739 | struct symhist *pos = rb_entry(nd, struct symhist, rb_node); | ||
740 | |||
741 | ret += symhist__fprintf(pos, 0, fp); | ||
742 | } | ||
743 | |||
744 | return ret; | ||
745 | } | ||
746 | |||
747 | static struct rb_root threads; | 635 | static struct rb_root threads; |
748 | 636 | ||
749 | static struct thread *threads__findnew(pid_t pid) | 637 | static struct thread *threads__findnew(pid_t pid) |
@@ -792,70 +680,172 @@ static struct map *thread__find_map(struct thread *self, uint64_t ip) | |||
792 | return NULL; | 680 | return NULL; |
793 | } | 681 | } |
794 | 682 | ||
795 | static void threads__fprintf(FILE *fp) | 683 | /* |
684 | * histogram, sorted on item, collects counts | ||
685 | */ | ||
686 | |||
687 | static struct rb_root hist; | ||
688 | |||
689 | struct hist_entry { | ||
690 | struct rb_node rb_node; | ||
691 | |||
692 | struct thread *thread; | ||
693 | struct map *map; | ||
694 | struct dso *dso; | ||
695 | struct symbol *sym; | ||
696 | uint64_t ip; | ||
697 | char level; | ||
698 | |||
699 | uint32_t count; | ||
700 | }; | ||
701 | |||
702 | static int64_t | ||
703 | hist_entry__cmp(struct hist_entry *left, struct hist_entry *right) | ||
704 | { | ||
705 | uint64_t ip_l, ip_r; | ||
706 | int cmp = right->thread->pid - left->thread->pid; | ||
707 | |||
708 | if (cmp) | ||
709 | return cmp; | ||
710 | |||
711 | if (left->sym == right->sym) | ||
712 | return 0; | ||
713 | |||
714 | ip_l = left->sym ? left->sym->start : left->ip; | ||
715 | ip_r = right->sym ? right->sym->start : right->ip; | ||
716 | |||
717 | return (int64_t)(ip_r - ip_l); | ||
718 | } | ||
719 | |||
720 | static int | ||
721 | hist_entry__add(struct thread *thread, struct map *map, struct dso *dso, | ||
722 | struct symbol *sym, uint64_t ip, char level) | ||
796 | { | 723 | { |
797 | struct rb_node *nd; | 724 | struct rb_node **p = &hist.rb_node; |
798 | for (nd = rb_first(&threads); nd; nd = rb_next(nd)) { | 725 | struct rb_node *parent = NULL; |
799 | struct thread *pos = rb_entry(nd, struct thread, rb_node); | 726 | struct hist_entry *he; |
800 | thread__fprintf(pos, fp); | 727 | struct hist_entry entry = { |
728 | .thread = thread, | ||
729 | .map = map, | ||
730 | .dso = dso, | ||
731 | .sym = sym, | ||
732 | .ip = ip, | ||
733 | .level = level, | ||
734 | .count = 1, | ||
735 | }; | ||
736 | int cmp; | ||
737 | |||
738 | while (*p != NULL) { | ||
739 | parent = *p; | ||
740 | he = rb_entry(parent, struct hist_entry, rb_node); | ||
741 | |||
742 | cmp = hist_entry__cmp(&entry, he); | ||
743 | |||
744 | if (!cmp) { | ||
745 | he->count++; | ||
746 | return 0; | ||
747 | } | ||
748 | |||
749 | if (cmp < 0) | ||
750 | p = &(*p)->rb_left; | ||
751 | else | ||
752 | p = &(*p)->rb_right; | ||
801 | } | 753 | } |
754 | |||
755 | he = malloc(sizeof(*he)); | ||
756 | if (!he) | ||
757 | return -ENOMEM; | ||
758 | *he = entry; | ||
759 | rb_link_node(&he->rb_node, parent, p); | ||
760 | rb_insert_color(&he->rb_node, &hist); | ||
761 | |||
762 | return 0; | ||
802 | } | 763 | } |
803 | 764 | ||
804 | static struct rb_root global_symhists; | 765 | static size_t |
766 | hist_entry__fprintf(FILE *fp, struct hist_entry *self, uint64_t total_samples) | ||
767 | { | ||
768 | char bf[32]; | ||
769 | size_t ret; | ||
770 | |||
771 | if (total_samples) { | ||
772 | ret = fprintf(fp, "%5.2f%% ", | ||
773 | (self->count * 100.0) / total_samples); | ||
774 | } else | ||
775 | ret = fprintf(fp, "%12d ", self->count); | ||
805 | 776 | ||
806 | static void threads__insert_symhist(struct symhist *sh) | 777 | ret += fprintf(fp, "%14s [%c] ", |
778 | thread__name(self->thread, bf, sizeof(bf)), | ||
779 | self->level); | ||
780 | |||
781 | if (verbose) | ||
782 | ret += fprintf(fp, "%#018llx ", (unsigned long long)self->ip); | ||
783 | |||
784 | if (self->level != '.') | ||
785 | ret += fprintf(fp, "%s\n", | ||
786 | self->sym ? self->sym->name : "<unknown>"); | ||
787 | else | ||
788 | ret += fprintf(fp, "%s: %s\n", | ||
789 | self->dso ? self->dso->name : "<unknown>", | ||
790 | self->sym ? self->sym->name : "<unknown>"); | ||
791 | return ret; | ||
792 | } | ||
793 | |||
794 | /* | ||
795 | * reverse the map, sort on count. | ||
796 | */ | ||
797 | |||
798 | static struct rb_root output_hists; | ||
799 | |||
800 | static void output__insert_entry(struct hist_entry *he) | ||
807 | { | 801 | { |
808 | struct rb_node **p = &global_symhists.rb_node; | 802 | struct rb_node **p = &output_hists.rb_node; |
809 | struct rb_node *parent = NULL; | 803 | struct rb_node *parent = NULL; |
810 | struct symhist *iter; | 804 | struct hist_entry *iter; |
811 | 805 | ||
812 | while (*p != NULL) { | 806 | while (*p != NULL) { |
813 | parent = *p; | 807 | parent = *p; |
814 | iter = rb_entry(parent, struct symhist, rb_node); | 808 | iter = rb_entry(parent, struct hist_entry, rb_node); |
815 | 809 | ||
816 | /* Reverse order */ | 810 | if (he->count > iter->count) |
817 | if (sh->count > iter->count) | ||
818 | p = &(*p)->rb_left; | 811 | p = &(*p)->rb_left; |
819 | else | 812 | else |
820 | p = &(*p)->rb_right; | 813 | p = &(*p)->rb_right; |
821 | } | 814 | } |
822 | 815 | ||
823 | rb_link_node(&sh->rb_node, parent, p); | 816 | rb_link_node(&he->rb_node, parent, p); |
824 | rb_insert_color(&sh->rb_node, &global_symhists); | 817 | rb_insert_color(&he->rb_node, &output_hists); |
825 | } | 818 | } |
826 | 819 | ||
827 | static void threads__sort_symhists(void) | 820 | static void output__resort(void) |
828 | { | 821 | { |
829 | struct rb_node *nd; | 822 | struct rb_node *next = rb_first(&hist); |
830 | 823 | struct hist_entry *n; | |
831 | for (nd = rb_first(&threads); nd; nd = rb_next(nd)) { | ||
832 | struct thread *thread = rb_entry(nd, struct thread, rb_node); | ||
833 | struct rb_node *next = rb_first(&thread->symhists); | ||
834 | 824 | ||
835 | while (next) { | 825 | while (next) { |
836 | struct symhist *n = rb_entry(next, struct symhist, | 826 | n = rb_entry(next, struct hist_entry, rb_node); |
837 | rb_node); | 827 | next = rb_next(&n->rb_node); |
838 | next = rb_next(&n->rb_node); | ||
839 | rb_erase(&n->rb_node, &thread->symhists); | ||
840 | threads__insert_symhist(n); | ||
841 | } | ||
842 | 828 | ||
829 | rb_erase(&n->rb_node, &hist); | ||
830 | output__insert_entry(n); | ||
843 | } | 831 | } |
844 | } | 832 | } |
845 | 833 | ||
846 | static size_t threads__symhists_fprintf(uint64_t total_samples, FILE *fp) | 834 | static size_t output__fprintf(FILE *fp, uint64_t total_samples) |
847 | { | 835 | { |
836 | struct hist_entry *pos; | ||
848 | struct rb_node *nd; | 837 | struct rb_node *nd; |
849 | size_t ret = 0; | 838 | size_t ret = 0; |
850 | 839 | ||
851 | for (nd = rb_first(&global_symhists); nd; nd = rb_next(nd)) { | 840 | for (nd = rb_first(&output_hists); nd; nd = rb_next(nd)) { |
852 | struct symhist *pos = rb_entry(nd, struct symhist, rb_node); | 841 | pos = rb_entry(nd, struct hist_entry, rb_node); |
853 | ret += symhist__fprintf(pos, total_samples, fp); | 842 | ret += hist_entry__fprintf(fp, pos, total_samples); |
854 | } | 843 | } |
855 | 844 | ||
856 | return ret; | 845 | return ret; |
857 | } | 846 | } |
858 | 847 | ||
848 | |||
859 | static int __cmd_report(void) | 849 | static int __cmd_report(void) |
860 | { | 850 | { |
861 | unsigned long offset = 0; | 851 | unsigned long offset = 0; |
@@ -926,6 +916,7 @@ more: | |||
926 | struct dso *dso = NULL; | 916 | struct dso *dso = NULL; |
927 | struct thread *thread = threads__findnew(event->ip.pid); | 917 | struct thread *thread = threads__findnew(event->ip.pid); |
928 | uint64_t ip = event->ip.ip; | 918 | uint64_t ip = event->ip.ip; |
919 | struct map *map = NULL; | ||
929 | 920 | ||
930 | if (dump_trace) { | 921 | if (dump_trace) { |
931 | fprintf(stderr, "%p [%p]: PERF_EVENT (IP, %d): %d: %p\n", | 922 | fprintf(stderr, "%p [%p]: PERF_EVENT (IP, %d): %d: %p\n", |
@@ -945,9 +936,10 @@ more: | |||
945 | if (event->header.misc & PERF_EVENT_MISC_KERNEL) { | 936 | if (event->header.misc & PERF_EVENT_MISC_KERNEL) { |
946 | show = SHOW_KERNEL; | 937 | show = SHOW_KERNEL; |
947 | level = 'k'; | 938 | level = 'k'; |
939 | |||
948 | dso = kernel_dso; | 940 | dso = kernel_dso; |
941 | |||
949 | } else if (event->header.misc & PERF_EVENT_MISC_USER) { | 942 | } else if (event->header.misc & PERF_EVENT_MISC_USER) { |
950 | struct map *map; | ||
951 | 943 | ||
952 | show = SHOW_USER; | 944 | show = SHOW_USER; |
953 | level = '.'; | 945 | level = '.'; |
@@ -957,6 +949,7 @@ more: | |||
957 | dso = map->dso; | 949 | dso = map->dso; |
958 | ip -= map->start + map->pgoff; | 950 | ip -= map->start + map->pgoff; |
959 | } | 951 | } |
952 | |||
960 | } else { | 953 | } else { |
961 | show = SHOW_HV; | 954 | show = SHOW_HV; |
962 | level = 'H'; | 955 | level = 'H'; |
@@ -965,8 +958,9 @@ more: | |||
965 | if (show & show_mask) { | 958 | if (show & show_mask) { |
966 | struct symbol *sym = dso__find_symbol(dso, ip); | 959 | struct symbol *sym = dso__find_symbol(dso, ip); |
967 | 960 | ||
968 | if (thread__symbol_incnew(thread, sym, ip, dso, level)) { | 961 | if (hist_entry__add(thread, map, dso, sym, ip, level)) { |
969 | fprintf(stderr, "problem incrementing symbol count, bailing out\n"); | 962 | fprintf(stderr, |
963 | "problem incrementing symbol count, bailing out\n"); | ||
970 | goto done; | 964 | goto done; |
971 | } | 965 | } |
972 | } | 966 | } |
@@ -1050,13 +1044,11 @@ done: | |||
1050 | return 0; | 1044 | return 0; |
1051 | } | 1045 | } |
1052 | 1046 | ||
1053 | if (verbose >= 2) { | 1047 | if (verbose >= 2) |
1054 | dsos__fprintf(stdout); | 1048 | dsos__fprintf(stdout); |
1055 | threads__fprintf(stdout); | ||
1056 | } | ||
1057 | 1049 | ||
1058 | threads__sort_symhists(); | 1050 | output__resort(); |
1059 | threads__symhists_fprintf(total, stdout); | 1051 | output__fprintf(stdout, total); |
1060 | 1052 | ||
1061 | return rc; | 1053 | return rc; |
1062 | } | 1054 | } |