aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPeter Zijlstra <a.p.zijlstra@chello.nl>2009-06-17 09:51:44 -0400
committerIngo Molnar <mingo@elte.hu>2009-06-17 13:23:52 -0400
commit6e7d6fdcbeefa9434653b5e5da12909636ea1d52 (patch)
treef224c67b032841da2d58130c1d36beed8e5598aa
parenta3d06cc6aa3e765dc2bf98626f87272dcf641dca (diff)
perf report: Add --sort <call> --call <$regex>
Implement sorting by callchain symbols, --sort <call>. It will create a new column which will show a match to --call $regex or "[unmatched]". Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl> Cc: Mike Galbraith <efault@gmx.de> Cc: Paul Mackerras <paulus@samba.org> Cc: Arnaldo Carvalho de Melo <acme@redhat.com> LKML-Reference: <new-submission> Signed-off-by: Ingo Molnar <mingo@elte.hu>
-rw-r--r--tools/perf/builtin-report.c209
1 files changed, 158 insertions, 51 deletions
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index f86bb07c0e84..cd74b2e58adb 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -40,11 +40,13 @@ static int dump_trace = 0;
40 40
41static int verbose; 41static int verbose;
42static int full_paths; 42static int full_paths;
43static int collapse_syscalls;
44 43
45static unsigned long page_size; 44static unsigned long page_size;
46static unsigned long mmap_window = 32; 45static unsigned long mmap_window = 32;
47 46
47static char *call = "^sys_";
48static regex_t call_regex;
49
48struct ip_chain_event { 50struct ip_chain_event {
49 __u16 nr; 51 __u16 nr;
50 __u16 hv; 52 __u16 hv;
@@ -463,6 +465,7 @@ struct hist_entry {
463 struct map *map; 465 struct map *map;
464 struct dso *dso; 466 struct dso *dso;
465 struct symbol *sym; 467 struct symbol *sym;
468 struct symbol *call;
466 __u64 ip; 469 __u64 ip;
467 char level; 470 char level;
468 471
@@ -483,6 +486,16 @@ struct sort_entry {
483 size_t (*print)(FILE *fp, struct hist_entry *); 486 size_t (*print)(FILE *fp, struct hist_entry *);
484}; 487};
485 488
489static int64_t cmp_null(void *l, void *r)
490{
491 if (!l && !r)
492 return 0;
493 else if (!l)
494 return -1;
495 else
496 return 1;
497}
498
486/* --sort pid */ 499/* --sort pid */
487 500
488static int64_t 501static int64_t
@@ -517,14 +530,8 @@ sort__comm_collapse(struct hist_entry *left, struct hist_entry *right)
517 char *comm_l = left->thread->comm; 530 char *comm_l = left->thread->comm;
518 char *comm_r = right->thread->comm; 531 char *comm_r = right->thread->comm;
519 532
520 if (!comm_l || !comm_r) { 533 if (!comm_l || !comm_r)
521 if (!comm_l && !comm_r) 534 return cmp_null(comm_l, comm_r);
522 return 0;
523 else if (!comm_l)
524 return -1;
525 else
526 return 1;
527 }
528 535
529 return strcmp(comm_l, comm_r); 536 return strcmp(comm_l, comm_r);
530} 537}
@@ -550,14 +557,8 @@ sort__dso_cmp(struct hist_entry *left, struct hist_entry *right)
550 struct dso *dso_l = left->dso; 557 struct dso *dso_l = left->dso;
551 struct dso *dso_r = right->dso; 558 struct dso *dso_r = right->dso;
552 559
553 if (!dso_l || !dso_r) { 560 if (!dso_l || !dso_r)
554 if (!dso_l && !dso_r) 561 return cmp_null(dso_l, dso_r);
555 return 0;
556 else if (!dso_l)
557 return -1;
558 else
559 return 1;
560 }
561 562
562 return strcmp(dso_l->name, dso_r->name); 563 return strcmp(dso_l->name, dso_r->name);
563} 564}
@@ -617,7 +618,38 @@ static struct sort_entry sort_sym = {
617 .print = sort__sym_print, 618 .print = sort__sym_print,
618}; 619};
619 620
621/* --sort call */
622
623static int64_t
624sort__call_cmp(struct hist_entry *left, struct hist_entry *right)
625{
626 struct symbol *sym_l = left->call;
627 struct symbol *sym_r = right->call;
628
629 if (!sym_l || !sym_r)
630 return cmp_null(sym_l, sym_r);
631
632 return strcmp(sym_l->name, sym_r->name);
633}
634
635static size_t
636sort__call_print(FILE *fp, struct hist_entry *self)
637{
638 size_t ret = 0;
639
640 ret += fprintf(fp, "%-20s", self->call ? self->call->name : "[unmatched]");
641
642 return ret;
643}
644
645static struct sort_entry sort_call = {
646 .header = "Callchain symbol ",
647 .cmp = sort__call_cmp,
648 .print = sort__call_print,
649};
650
620static int sort__need_collapse = 0; 651static int sort__need_collapse = 0;
652static int sort__has_call = 0;
621 653
622struct sort_dimension { 654struct sort_dimension {
623 char *name; 655 char *name;
@@ -630,6 +662,7 @@ static struct sort_dimension sort_dimensions[] = {
630 { .name = "comm", .entry = &sort_comm, }, 662 { .name = "comm", .entry = &sort_comm, },
631 { .name = "dso", .entry = &sort_dso, }, 663 { .name = "dso", .entry = &sort_dso, },
632 { .name = "symbol", .entry = &sort_sym, }, 664 { .name = "symbol", .entry = &sort_sym, },
665 { .name = "call", .entry = &sort_call, },
633}; 666};
634 667
635static LIST_HEAD(hist_entry__sort_list); 668static LIST_HEAD(hist_entry__sort_list);
@@ -650,6 +683,18 @@ static int sort_dimension__add(char *tok)
650 if (sd->entry->collapse) 683 if (sd->entry->collapse)
651 sort__need_collapse = 1; 684 sort__need_collapse = 1;
652 685
686 if (sd->entry == &sort_call) {
687 int ret = regcomp(&call_regex, call, REG_EXTENDED);
688 if (ret) {
689 char err[BUFSIZ];
690
691 regerror(ret, &call_regex, err, sizeof(err));
692 fprintf(stderr, "Invalid regex: %s\n%s", call, err);
693 exit(-1);
694 }
695 sort__has_call = 1;
696 }
697
653 list_add_tail(&sd->entry->list, &hist_entry__sort_list); 698 list_add_tail(&sd->entry->list, &hist_entry__sort_list);
654 sd->taken = 1; 699 sd->taken = 1;
655 700
@@ -731,12 +776,75 @@ hist_entry__fprintf(FILE *fp, struct hist_entry *self, __u64 total_samples)
731} 776}
732 777
733/* 778/*
779 *
780 */
781
782static struct symbol *
783resolve_symbol(struct thread *thread, struct map **mapp,
784 struct dso **dsop, __u64 *ipp)
785{
786 struct dso *dso = dsop ? *dsop : NULL;
787 struct map *map = mapp ? *mapp : NULL;
788 uint64_t ip = *ipp;
789
790 if (!thread)
791 return NULL;
792
793 if (dso)
794 goto got_dso;
795
796 if (map)
797 goto got_map;
798
799 map = thread__find_map(thread, ip);
800 if (map != NULL) {
801 if (mapp)
802 *mapp = map;
803got_map:
804 ip = map->map_ip(map, ip);
805 *ipp = ip;
806
807 dso = map->dso;
808 } else {
809 /*
810 * If this is outside of all known maps,
811 * and is a negative address, try to look it
812 * up in the kernel dso, as it might be a
813 * vsyscall (which executes in user-mode):
814 */
815 if ((long long)ip < 0)
816 dso = kernel_dso;
817 }
818 dprintf(" ...... dso: %s\n", dso ? dso->name : "<not found>");
819
820 if (dsop)
821 *dsop = dso;
822
823 if (!dso)
824 return NULL;
825got_dso:
826 return dso->find_symbol(dso, ip);
827}
828
829static struct symbol *call__match(struct symbol *sym)
830{
831 if (!sym)
832 return NULL;
833
834 if (sym->name && !regexec(&call_regex, sym->name, 0, NULL, 0))
835 return sym;
836
837 return NULL;
838}
839
840/*
734 * collect histogram counts 841 * collect histogram counts
735 */ 842 */
736 843
737static int 844static int
738hist_entry__add(struct thread *thread, struct map *map, struct dso *dso, 845hist_entry__add(struct thread *thread, struct map *map, struct dso *dso,
739 struct symbol *sym, __u64 ip, char level, __u64 count) 846 struct symbol *sym, __u64 ip, struct ip_chain_event *chain,
847 char level, __u64 count)
740{ 848{
741 struct rb_node **p = &hist.rb_node; 849 struct rb_node **p = &hist.rb_node;
742 struct rb_node *parent = NULL; 850 struct rb_node *parent = NULL;
@@ -752,6 +860,33 @@ hist_entry__add(struct thread *thread, struct map *map, struct dso *dso,
752 }; 860 };
753 int cmp; 861 int cmp;
754 862
863 if (sort__has_call && chain) {
864 int i, nr = chain->hv;
865 struct symbol *sym;
866 struct dso *dso;
867 __u64 ip;
868
869 for (i = 0; i < chain->kernel; i++) {
870 ip = chain->ips[nr + i];
871 dso = kernel_dso;
872 sym = resolve_symbol(thread, NULL, &dso, &ip);
873 entry.call = call__match(sym);
874 if (entry.call)
875 goto got_call;
876 }
877 nr += i;
878
879 for (i = 0; i < chain->user; i++) {
880 ip = chain->ips[nr + i];
881 sym = resolve_symbol(thread, NULL, NULL, &ip);
882 entry.call = call__match(sym);
883 if (entry.call)
884 goto got_call;
885 }
886 nr += i;
887 }
888got_call:
889
755 while (*p != NULL) { 890 while (*p != NULL) {
756 parent = *p; 891 parent = *p;
757 he = rb_entry(parent, struct hist_entry, rb_node); 892 he = rb_entry(parent, struct hist_entry, rb_node);
@@ -955,7 +1090,7 @@ process_overflow_event(event_t *event, unsigned long offset, unsigned long head)
955 __u64 period = 1; 1090 __u64 period = 1;
956 struct map *map = NULL; 1091 struct map *map = NULL;
957 void *more_data = event->ip.__more_data; 1092 void *more_data = event->ip.__more_data;
958 struct ip_chain_event *chain; 1093 struct ip_chain_event *chain = NULL;
959 1094
960 if (event->header.type & PERF_SAMPLE_PERIOD) { 1095 if (event->header.type & PERF_SAMPLE_PERIOD) {
961 period = *(__u64 *)more_data; 1096 period = *(__u64 *)more_data;
@@ -984,15 +1119,6 @@ process_overflow_event(event_t *event, unsigned long offset, unsigned long head)
984 for (i = 0; i < chain->nr; i++) 1119 for (i = 0; i < chain->nr; i++)
985 dprintf("..... %2d: %016Lx\n", i, chain->ips[i]); 1120 dprintf("..... %2d: %016Lx\n", i, chain->ips[i]);
986 } 1121 }
987 if (collapse_syscalls) {
988 /*
989 * Find the all-but-last kernel entry
990 * amongst the call-chains - to get
991 * to the level of system calls:
992 */
993 if (chain->kernel >= 2)
994 ip = chain->ips[chain->kernel-2];
995 }
996 } 1122 }
997 1123
998 dprintf(" ... thread: %s:%d\n", thread->comm, thread->pid); 1124 dprintf(" ... thread: %s:%d\n", thread->comm, thread->pid);
@@ -1016,22 +1142,6 @@ process_overflow_event(event_t *event, unsigned long offset, unsigned long head)
1016 show = SHOW_USER; 1142 show = SHOW_USER;
1017 level = '.'; 1143 level = '.';
1018 1144
1019 map = thread__find_map(thread, ip);
1020 if (map != NULL) {
1021 ip = map->map_ip(map, ip);
1022 dso = map->dso;
1023 } else {
1024 /*
1025 * If this is outside of all known maps,
1026 * and is a negative address, try to look it
1027 * up in the kernel dso, as it might be a
1028 * vsyscall (which executes in user-mode):
1029 */
1030 if ((long long)ip < 0)
1031 dso = kernel_dso;
1032 }
1033 dprintf(" ...... dso: %s\n", dso ? dso->name : "<not found>");
1034
1035 } else { 1145 } else {
1036 show = SHOW_HV; 1146 show = SHOW_HV;
1037 level = 'H'; 1147 level = 'H';
@@ -1039,12 +1149,9 @@ process_overflow_event(event_t *event, unsigned long offset, unsigned long head)
1039 } 1149 }
1040 1150
1041 if (show & show_mask) { 1151 if (show & show_mask) {
1042 struct symbol *sym = NULL; 1152 struct symbol *sym = resolve_symbol(thread, &map, &dso, &ip);
1043
1044 if (dso)
1045 sym = dso->find_symbol(dso, ip);
1046 1153
1047 if (hist_entry__add(thread, map, dso, sym, ip, level, period)) { 1154 if (hist_entry__add(thread, map, dso, sym, ip, chain, level, period)) {
1048 fprintf(stderr, 1155 fprintf(stderr,
1049 "problem incrementing symbol count, skipping event\n"); 1156 "problem incrementing symbol count, skipping event\n");
1050 return -1; 1157 return -1;
@@ -1353,8 +1460,8 @@ static const struct option options[] = {
1353 "sort by key(s): pid, comm, dso, symbol. Default: pid,symbol"), 1460 "sort by key(s): pid, comm, dso, symbol. Default: pid,symbol"),
1354 OPT_BOOLEAN('P', "full-paths", &full_paths, 1461 OPT_BOOLEAN('P', "full-paths", &full_paths,
1355 "Don't shorten the pathnames taking into account the cwd"), 1462 "Don't shorten the pathnames taking into account the cwd"),
1356 OPT_BOOLEAN('S', "syscalls", &collapse_syscalls, 1463 OPT_STRING('c', "call", &call, "regex",
1357 "show per syscall summary overhead, using call graph"), 1464 "regex to use for --sort call"),
1358 OPT_END() 1465 OPT_END()
1359}; 1466};
1360 1467