diff options
author | Arnaldo Carvalho de Melo <acme@redhat.com> | 2011-10-05 18:30:22 -0400 |
---|---|---|
committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2011-10-07 15:59:59 -0400 |
commit | 19d4ac3c1039fb952f4c073fd0898e9295a836c8 (patch) | |
tree | 56acd6c60a518297713a5392acd8813cf4ef768a /tools/perf/builtin-top.c | |
parent | ab81f3fd350c510730adb1ca40ef55c2b2952121 (diff) |
perf top: Add callgraph support
Just like in 'perf report', but live.
Still needs to decay the callchains, but already somewhat useful as-is.
Cc: David Ahern <dsahern@gmail.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Stephane Eranian <eranian@google.com>
Link: http://lkml.kernel.org/n/tip-cj3rmaf5jpsvi3v0tf7t4uvp@git.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Diffstat (limited to 'tools/perf/builtin-top.c')
-rw-r--r-- | tools/perf/builtin-top.c | 146 |
1 files changed, 144 insertions, 2 deletions
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 2cf5e50a6997..b9b7fe085895 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c | |||
@@ -76,6 +76,12 @@ static bool system_wide = false; | |||
76 | 76 | ||
77 | static bool use_tui, use_stdio; | 77 | static bool use_tui, use_stdio; |
78 | 78 | ||
79 | static bool sort_has_symbols; | ||
80 | |||
81 | static bool dont_use_callchains; | ||
82 | static char callchain_default_opt[] = "fractal,0.5,callee"; | ||
83 | |||
84 | |||
79 | static int default_interval = 0; | 85 | static int default_interval = 0; |
80 | 86 | ||
81 | static bool kptr_restrict_warned; | 87 | static bool kptr_restrict_warned; |
@@ -648,9 +654,11 @@ static void perf_event__process_sample(const union perf_event *event, | |||
648 | struct perf_sample *sample, | 654 | struct perf_sample *sample, |
649 | struct perf_session *session) | 655 | struct perf_session *session) |
650 | { | 656 | { |
657 | struct symbol *parent = NULL; | ||
651 | u64 ip = event->ip.ip; | 658 | u64 ip = event->ip.ip; |
652 | struct addr_location al; | 659 | struct addr_location al; |
653 | struct machine *machine; | 660 | struct machine *machine; |
661 | int err; | ||
654 | u8 origin = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | 662 | u8 origin = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; |
655 | 663 | ||
656 | ++top.samples; | 664 | ++top.samples; |
@@ -748,13 +756,29 @@ static void perf_event__process_sample(const union perf_event *event, | |||
748 | evsel = perf_evlist__id2evsel(top.evlist, sample->id); | 756 | evsel = perf_evlist__id2evsel(top.evlist, sample->id); |
749 | assert(evsel != NULL); | 757 | assert(evsel != NULL); |
750 | 758 | ||
759 | if ((sort__has_parent || symbol_conf.use_callchain) && | ||
760 | sample->callchain) { | ||
761 | err = perf_session__resolve_callchain(session, al.thread, | ||
762 | sample->callchain, &parent); | ||
763 | if (err) | ||
764 | return; | ||
765 | } | ||
766 | |||
751 | he = perf_session__add_hist_entry(session, &al, sample, evsel); | 767 | he = perf_session__add_hist_entry(session, &al, sample, evsel); |
752 | if (he == NULL) { | 768 | if (he == NULL) { |
753 | pr_err("Problem incrementing symbol period, skipping event\n"); | 769 | pr_err("Problem incrementing symbol period, skipping event\n"); |
754 | return; | 770 | return; |
755 | } | 771 | } |
756 | 772 | ||
757 | record_precise_ip(he, evsel->idx, ip); | 773 | if (symbol_conf.use_callchain) { |
774 | err = callchain_append(he->callchain, &session->callchain_cursor, | ||
775 | sample->period); | ||
776 | if (err) | ||
777 | return; | ||
778 | } | ||
779 | |||
780 | if (sort_has_symbols) | ||
781 | record_precise_ip(he, evsel->idx, ip); | ||
758 | } | 782 | } |
759 | 783 | ||
760 | return; | 784 | return; |
@@ -808,6 +832,9 @@ static void start_counters(struct perf_evlist *evlist) | |||
808 | attr->read_format |= PERF_FORMAT_ID; | 832 | attr->read_format |= PERF_FORMAT_ID; |
809 | } | 833 | } |
810 | 834 | ||
835 | if (symbol_conf.use_callchain) | ||
836 | attr->sample_type |= PERF_SAMPLE_CALLCHAIN; | ||
837 | |||
811 | attr->mmap = 1; | 838 | attr->mmap = 1; |
812 | attr->comm = 1; | 839 | attr->comm = 1; |
813 | attr->inherit = inherit; | 840 | attr->inherit = inherit; |
@@ -864,10 +891,27 @@ out_err: | |||
864 | exit(0); | 891 | exit(0); |
865 | } | 892 | } |
866 | 893 | ||
894 | static int setup_sample_type(void) | ||
895 | { | ||
896 | if (!sort_has_symbols) { | ||
897 | if (symbol_conf.use_callchain) { | ||
898 | ui__warning("Selected -g but \"sym\" not present in --sort/-s."); | ||
899 | return -EINVAL; | ||
900 | } | ||
901 | } else if (!dont_use_callchains && callchain_param.mode != CHAIN_NONE) { | ||
902 | if (callchain_register_param(&callchain_param) < 0) { | ||
903 | ui__warning("Can't register callchain params.\n"); | ||
904 | return -EINVAL; | ||
905 | } | ||
906 | } | ||
907 | |||
908 | return 0; | ||
909 | } | ||
910 | |||
867 | static int __cmd_top(void) | 911 | static int __cmd_top(void) |
868 | { | 912 | { |
869 | pthread_t thread; | 913 | pthread_t thread; |
870 | int ret __used; | 914 | int ret; |
871 | /* | 915 | /* |
872 | * FIXME: perf_session__new should allow passing a O_MMAP, so that all this | 916 | * FIXME: perf_session__new should allow passing a O_MMAP, so that all this |
873 | * mmap reading, etc is encapsulated in it. Use O_WRONLY for now. | 917 | * mmap reading, etc is encapsulated in it. Use O_WRONLY for now. |
@@ -876,6 +920,10 @@ static int __cmd_top(void) | |||
876 | if (top.session == NULL) | 920 | if (top.session == NULL) |
877 | return -ENOMEM; | 921 | return -ENOMEM; |
878 | 922 | ||
923 | ret = setup_sample_type(); | ||
924 | if (ret) | ||
925 | goto out_delete; | ||
926 | |||
879 | if (top.target_tid != -1) | 927 | if (top.target_tid != -1) |
880 | perf_event__synthesize_thread_map(top.evlist->threads, | 928 | perf_event__synthesize_thread_map(top.evlist->threads, |
881 | perf_event__process, top.session); | 929 | perf_event__process, top.session); |
@@ -916,6 +964,90 @@ static int __cmd_top(void) | |||
916 | ret = poll(top.evlist->pollfd, top.evlist->nr_fds, 100); | 964 | ret = poll(top.evlist->pollfd, top.evlist->nr_fds, 100); |
917 | } | 965 | } |
918 | 966 | ||
967 | out_delete: | ||
968 | perf_session__delete(top.session); | ||
969 | top.session = NULL; | ||
970 | |||
971 | return 0; | ||
972 | } | ||
973 | |||
974 | static int | ||
975 | parse_callchain_opt(const struct option *opt __used, const char *arg, | ||
976 | int unset) | ||
977 | { | ||
978 | char *tok, *tok2; | ||
979 | char *endptr; | ||
980 | |||
981 | /* | ||
982 | * --no-call-graph | ||
983 | */ | ||
984 | if (unset) { | ||
985 | dont_use_callchains = true; | ||
986 | return 0; | ||
987 | } | ||
988 | |||
989 | symbol_conf.use_callchain = true; | ||
990 | |||
991 | if (!arg) | ||
992 | return 0; | ||
993 | |||
994 | tok = strtok((char *)arg, ","); | ||
995 | if (!tok) | ||
996 | return -1; | ||
997 | |||
998 | /* get the output mode */ | ||
999 | if (!strncmp(tok, "graph", strlen(arg))) | ||
1000 | callchain_param.mode = CHAIN_GRAPH_ABS; | ||
1001 | |||
1002 | else if (!strncmp(tok, "flat", strlen(arg))) | ||
1003 | callchain_param.mode = CHAIN_FLAT; | ||
1004 | |||
1005 | else if (!strncmp(tok, "fractal", strlen(arg))) | ||
1006 | callchain_param.mode = CHAIN_GRAPH_REL; | ||
1007 | |||
1008 | else if (!strncmp(tok, "none", strlen(arg))) { | ||
1009 | callchain_param.mode = CHAIN_NONE; | ||
1010 | symbol_conf.use_callchain = false; | ||
1011 | |||
1012 | return 0; | ||
1013 | } | ||
1014 | |||
1015 | else | ||
1016 | return -1; | ||
1017 | |||
1018 | /* get the min percentage */ | ||
1019 | tok = strtok(NULL, ","); | ||
1020 | if (!tok) | ||
1021 | goto setup; | ||
1022 | |||
1023 | callchain_param.min_percent = strtod(tok, &endptr); | ||
1024 | if (tok == endptr) | ||
1025 | return -1; | ||
1026 | |||
1027 | /* get the print limit */ | ||
1028 | tok2 = strtok(NULL, ","); | ||
1029 | if (!tok2) | ||
1030 | goto setup; | ||
1031 | |||
1032 | if (tok2[0] != 'c') { | ||
1033 | callchain_param.print_limit = strtod(tok2, &endptr); | ||
1034 | tok2 = strtok(NULL, ","); | ||
1035 | if (!tok2) | ||
1036 | goto setup; | ||
1037 | } | ||
1038 | |||
1039 | /* get the call chain order */ | ||
1040 | if (!strcmp(tok2, "caller")) | ||
1041 | callchain_param.order = ORDER_CALLER; | ||
1042 | else if (!strcmp(tok2, "callee")) | ||
1043 | callchain_param.order = ORDER_CALLEE; | ||
1044 | else | ||
1045 | return -1; | ||
1046 | setup: | ||
1047 | if (callchain_register_param(&callchain_param) < 0) { | ||
1048 | fprintf(stderr, "Can't register callchain params\n"); | ||
1049 | return -1; | ||
1050 | } | ||
919 | return 0; | 1051 | return 0; |
920 | } | 1052 | } |
921 | 1053 | ||
@@ -973,6 +1105,10 @@ static const struct option options[] = { | |||
973 | "sort by key(s): pid, comm, dso, symbol, parent"), | 1105 | "sort by key(s): pid, comm, dso, symbol, parent"), |
974 | OPT_BOOLEAN('n', "show-nr-samples", &symbol_conf.show_nr_samples, | 1106 | OPT_BOOLEAN('n', "show-nr-samples", &symbol_conf.show_nr_samples, |
975 | "Show a column with the number of samples"), | 1107 | "Show a column with the number of samples"), |
1108 | OPT_CALLBACK_DEFAULT('G', "call-graph", NULL, "output_type,min_percent, call_order", | ||
1109 | "Display callchains using output_type (graph, flat, fractal, or none), min percent threshold and callchain order. " | ||
1110 | "Default: fractal,0.5,callee", &parse_callchain_opt, | ||
1111 | callchain_default_opt), | ||
976 | OPT_BOOLEAN(0, "show-total-period", &symbol_conf.show_total_period, | 1112 | OPT_BOOLEAN(0, "show-total-period", &symbol_conf.show_total_period, |
977 | "Show a column with the sum of periods"), | 1113 | "Show a column with the sum of periods"), |
978 | OPT_STRING(0, "dsos", &symbol_conf.dso_list_str, "dso[,dso...]", | 1114 | OPT_STRING(0, "dsos", &symbol_conf.dso_list_str, "dso[,dso...]", |
@@ -1082,6 +1218,12 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) | |||
1082 | sort_entry__setup_elide(&sort_comm, symbol_conf.comm_list, "comm", stdout); | 1218 | sort_entry__setup_elide(&sort_comm, symbol_conf.comm_list, "comm", stdout); |
1083 | sort_entry__setup_elide(&sort_sym, symbol_conf.sym_list, "symbol", stdout); | 1219 | sort_entry__setup_elide(&sort_sym, symbol_conf.sym_list, "symbol", stdout); |
1084 | 1220 | ||
1221 | /* | ||
1222 | * Avoid annotation data structures overhead when symbols aren't on the | ||
1223 | * sort list. | ||
1224 | */ | ||
1225 | sort_has_symbols = sort_sym.list.next != NULL; | ||
1226 | |||
1085 | get_term_dimensions(&winsize); | 1227 | get_term_dimensions(&winsize); |
1086 | if (top.print_entries == 0) { | 1228 | if (top.print_entries == 0) { |
1087 | update_print_entries(&winsize); | 1229 | update_print_entries(&winsize); |