aboutsummaryrefslogtreecommitdiffstats
path: root/tools/perf
diff options
context:
space:
mode:
authorNamhyung Kim <namhyung@kernel.org>2015-11-09 00:45:43 -0500
committerArnaldo Carvalho de Melo <acme@redhat.com>2015-11-19 11:19:24 -0500
commit4b3a3212233a042f48b7b8fedc64933e1ccd8643 (patch)
treeca13bbb59f743db0a4a11520a5b7df3ca47d0daf /tools/perf
parent18bb838129b08fb0009b1ba1dc2f748a9537ee89 (diff)
perf hists browser: Support flat callchains
The flat callchain mode is to print all chains in a single, simple hierarchy so make it easy to see. Currently perf report --tui doesn't show flat callchains properly. With flat callchains, only leaf nodes are added to the final rbtree so it should show entries in parent nodes. To do that, add parent_val list to struct callchain_node and show them along with the (normal) val list. For example, consider following callchains with '-g graph'. $ perf report -g graph - 39.93% swapper [kernel.vmlinux] [k] intel_idle intel_idle cpuidle_enter_state cpuidle_enter call_cpuidle - cpu_startup_entry 28.63% start_secondary - 11.30% rest_init start_kernel x86_64_start_reservations x86_64_start_kernel Before: $ perf report -g flat - 39.93% swapper [kernel.vmlinux] [k] intel_idle 28.63% start_secondary - 11.30% rest_init start_kernel x86_64_start_reservations x86_64_start_kernel After: $ perf report -g flat - 39.93% swapper [kernel.vmlinux] [k] intel_idle - 28.63% intel_idle cpuidle_enter_state cpuidle_enter call_cpuidle cpu_startup_entry start_secondary - 11.30% intel_idle cpuidle_enter_state cpuidle_enter call_cpuidle cpu_startup_entry start_kernel x86_64_start_reservations x86_64_start_kernel Signed-off-by: Namhyung Kim <namhyung@kernel.org> Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com> Tested-by: Brendan Gregg <brendan.d.gregg@gmail.com> Cc: Andi Kleen <andi@firstfloor.org> Cc: David Ahern <dsahern@gmail.com> Cc: Frederic Weisbecker <fweisbec@gmail.com> Cc: Jiri Olsa <jolsa@redhat.com> Cc: Kan Liang <kan.liang@intel.com> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl> Link: http://lkml.kernel.org/r/1447047946-1691-8-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Diffstat (limited to 'tools/perf')
-rw-r--r--tools/perf/ui/browsers/hists.c122
-rw-r--r--tools/perf/util/callchain.c44
-rw-r--r--tools/perf/util/callchain.h2
3 files changed, 166 insertions, 2 deletions
diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c
index 0746d41d9efe..c44af461a68f 100644
--- a/tools/perf/ui/browsers/hists.c
+++ b/tools/perf/ui/browsers/hists.c
@@ -178,12 +178,44 @@ static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
178 return n; 178 return n;
179} 179}
180 180
181static int callchain_node__count_flat_rows(struct callchain_node *node)
182{
183 struct callchain_list *chain;
184 char folded_sign = 0;
185 int n = 0;
186
187 list_for_each_entry(chain, &node->parent_val, list) {
188 if (!folded_sign) {
189 /* only check first chain list entry */
190 folded_sign = callchain_list__folded(chain);
191 if (folded_sign == '+')
192 return 1;
193 }
194 n++;
195 }
196
197 list_for_each_entry(chain, &node->val, list) {
198 if (!folded_sign) {
199 /* node->parent_val list might be empty */
200 folded_sign = callchain_list__folded(chain);
201 if (folded_sign == '+')
202 return 1;
203 }
204 n++;
205 }
206
207 return n;
208}
209
181static int callchain_node__count_rows(struct callchain_node *node) 210static int callchain_node__count_rows(struct callchain_node *node)
182{ 211{
183 struct callchain_list *chain; 212 struct callchain_list *chain;
184 bool unfolded = false; 213 bool unfolded = false;
185 int n = 0; 214 int n = 0;
186 215
216 if (callchain_param.mode == CHAIN_FLAT)
217 return callchain_node__count_flat_rows(node);
218
187 list_for_each_entry(chain, &node->val, list) { 219 list_for_each_entry(chain, &node->val, list) {
188 ++n; 220 ++n;
189 unfolded = chain->unfolded; 221 unfolded = chain->unfolded;
@@ -263,7 +295,7 @@ static void callchain_node__init_have_children(struct callchain_node *node,
263 chain = list_entry(node->val.next, struct callchain_list, list); 295 chain = list_entry(node->val.next, struct callchain_list, list);
264 chain->has_children = has_sibling; 296 chain->has_children = has_sibling;
265 297
266 if (!list_empty(&node->val)) { 298 if (node->val.next != node->val.prev) {
267 chain = list_entry(node->val.prev, struct callchain_list, list); 299 chain = list_entry(node->val.prev, struct callchain_list, list);
268 chain->has_children = !RB_EMPTY_ROOT(&node->rb_root); 300 chain->has_children = !RB_EMPTY_ROOT(&node->rb_root);
269 } 301 }
@@ -279,6 +311,8 @@ static void callchain__init_have_children(struct rb_root *root)
279 for (nd = rb_first(root); nd; nd = rb_next(nd)) { 311 for (nd = rb_first(root); nd; nd = rb_next(nd)) {
280 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node); 312 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
281 callchain_node__init_have_children(node, has_sibling); 313 callchain_node__init_have_children(node, has_sibling);
314 if (callchain_param.mode == CHAIN_FLAT)
315 callchain_node__make_parent_list(node);
282 } 316 }
283} 317}
284 318
@@ -612,6 +646,83 @@ static int hist_browser__show_callchain_list(struct hist_browser *browser,
612 return 1; 646 return 1;
613} 647}
614 648
649static int hist_browser__show_callchain_flat(struct hist_browser *browser,
650 struct rb_root *root,
651 unsigned short row, u64 total,
652 print_callchain_entry_fn print,
653 struct callchain_print_arg *arg,
654 check_output_full_fn is_output_full)
655{
656 struct rb_node *node;
657 int first_row = row, offset = LEVEL_OFFSET_STEP;
658 bool need_percent;
659
660 node = rb_first(root);
661 need_percent = node && rb_next(node);
662
663 while (node) {
664 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
665 struct rb_node *next = rb_next(node);
666 struct callchain_list *chain;
667 char folded_sign = ' ';
668 int first = true;
669 int extra_offset = 0;
670
671 list_for_each_entry(chain, &child->parent_val, list) {
672 bool was_first = first;
673
674 if (first)
675 first = false;
676 else if (need_percent)
677 extra_offset = LEVEL_OFFSET_STEP;
678
679 folded_sign = callchain_list__folded(chain);
680
681 row += hist_browser__show_callchain_list(browser, child,
682 chain, row, total,
683 was_first && need_percent,
684 offset + extra_offset,
685 print, arg);
686
687 if (is_output_full(browser, row))
688 goto out;
689
690 if (folded_sign == '+')
691 goto next;
692 }
693
694 list_for_each_entry(chain, &child->val, list) {
695 bool was_first = first;
696
697 if (first)
698 first = false;
699 else if (need_percent)
700 extra_offset = LEVEL_OFFSET_STEP;
701
702 folded_sign = callchain_list__folded(chain);
703
704 row += hist_browser__show_callchain_list(browser, child,
705 chain, row, total,
706 was_first && need_percent,
707 offset + extra_offset,
708 print, arg);
709
710 if (is_output_full(browser, row))
711 goto out;
712
713 if (folded_sign == '+')
714 break;
715 }
716
717next:
718 if (is_output_full(browser, row))
719 break;
720 node = next;
721 }
722out:
723 return row - first_row;
724}
725
615static int hist_browser__show_callchain(struct hist_browser *browser, 726static int hist_browser__show_callchain(struct hist_browser *browser,
616 struct rb_root *root, int level, 727 struct rb_root *root, int level,
617 unsigned short row, u64 total, 728 unsigned short row, u64 total,
@@ -864,10 +975,17 @@ static int hist_browser__show_entry(struct hist_browser *browser,
864 total = entry->stat.period; 975 total = entry->stat.period;
865 } 976 }
866 977
867 printed += hist_browser__show_callchain(browser, 978 if (callchain_param.mode == CHAIN_FLAT) {
979 printed += hist_browser__show_callchain_flat(browser,
980 &entry->sorted_chain, row, total,
981 hist_browser__show_callchain_entry, &arg,
982 hist_browser__check_output_full);
983 } else {
984 printed += hist_browser__show_callchain(browser,
868 &entry->sorted_chain, 1, row, total, 985 &entry->sorted_chain, 1, row, total,
869 hist_browser__show_callchain_entry, &arg, 986 hist_browser__show_callchain_entry, &arg,
870 hist_browser__check_output_full); 987 hist_browser__check_output_full);
988 }
871 989
872 if (arg.is_current_entry) 990 if (arg.is_current_entry)
873 browser->he_selection = entry; 991 browser->he_selection = entry;
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c
index 717c58c1da58..fc3b1e0d09ee 100644
--- a/tools/perf/util/callchain.c
+++ b/tools/perf/util/callchain.c
@@ -387,6 +387,7 @@ create_child(struct callchain_node *parent, bool inherit_children)
387 } 387 }
388 new->parent = parent; 388 new->parent = parent;
389 INIT_LIST_HEAD(&new->val); 389 INIT_LIST_HEAD(&new->val);
390 INIT_LIST_HEAD(&new->parent_val);
390 391
391 if (inherit_children) { 392 if (inherit_children) {
392 struct rb_node *n; 393 struct rb_node *n;
@@ -894,6 +895,11 @@ static void free_callchain_node(struct callchain_node *node)
894 struct callchain_node *child; 895 struct callchain_node *child;
895 struct rb_node *n; 896 struct rb_node *n;
896 897
898 list_for_each_entry_safe(list, tmp, &node->parent_val, list) {
899 list_del(&list->list);
900 free(list);
901 }
902
897 list_for_each_entry_safe(list, tmp, &node->val, list) { 903 list_for_each_entry_safe(list, tmp, &node->val, list) {
898 list_del(&list->list); 904 list_del(&list->list);
899 free(list); 905 free(list);
@@ -917,3 +923,41 @@ void free_callchain(struct callchain_root *root)
917 923
918 free_callchain_node(&root->node); 924 free_callchain_node(&root->node);
919} 925}
926
927int callchain_node__make_parent_list(struct callchain_node *node)
928{
929 struct callchain_node *parent = node->parent;
930 struct callchain_list *chain, *new;
931 LIST_HEAD(head);
932
933 while (parent) {
934 list_for_each_entry_reverse(chain, &parent->val, list) {
935 new = malloc(sizeof(*new));
936 if (new == NULL)
937 goto out;
938 *new = *chain;
939 new->has_children = false;
940 list_add_tail(&new->list, &head);
941 }
942 parent = parent->parent;
943 }
944
945 list_for_each_entry_safe_reverse(chain, new, &head, list)
946 list_move_tail(&chain->list, &node->parent_val);
947
948 if (!list_empty(&node->parent_val)) {
949 chain = list_first_entry(&node->parent_val, struct callchain_list, list);
950 chain->has_children = rb_prev(&node->rb_node) || rb_next(&node->rb_node);
951
952 chain = list_first_entry(&node->val, struct callchain_list, list);
953 chain->has_children = false;
954 }
955 return 0;
956
957out:
958 list_for_each_entry_safe(chain, new, &head, list) {
959 list_del(&chain->list);
960 free(chain);
961 }
962 return -ENOMEM;
963}
diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h
index 47bc0c57f764..6e9b5f2099e1 100644
--- a/tools/perf/util/callchain.h
+++ b/tools/perf/util/callchain.h
@@ -56,6 +56,7 @@ enum chain_order {
56struct callchain_node { 56struct callchain_node {
57 struct callchain_node *parent; 57 struct callchain_node *parent;
58 struct list_head val; 58 struct list_head val;
59 struct list_head parent_val;
59 struct rb_node rb_node_in; /* to insert nodes in an rbtree */ 60 struct rb_node rb_node_in; /* to insert nodes in an rbtree */
60 struct rb_node rb_node; /* to sort nodes in an output tree */ 61 struct rb_node rb_node; /* to sort nodes in an output tree */
61 struct rb_root rb_root_in; /* input tree of children */ 62 struct rb_root rb_root_in; /* input tree of children */
@@ -251,5 +252,6 @@ int callchain_node__fprintf_value(struct callchain_node *node,
251 FILE *fp, u64 total); 252 FILE *fp, u64 total);
252 253
253void free_callchain(struct callchain_root *root); 254void free_callchain(struct callchain_root *root);
255int callchain_node__make_parent_list(struct callchain_node *node);
254 256
255#endif /* __PERF_CALLCHAIN_H */ 257#endif /* __PERF_CALLCHAIN_H */