perf hists browser: Support flat callchains
authorNamhyung Kim <namhyung@kernel.org>
Mon, 9 Nov 2015 05:45:43 +0000 (14:45 +0900)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Thu, 19 Nov 2015 16:19:24 +0000 (13:19 -0300)
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>
tools/perf/ui/browsers/hists.c
tools/perf/util/callchain.c
tools/perf/util/callchain.h

index 0746d41d9efed7d2a730c087af9597d0ee404518..c44af461a68f29f470655440087291c3b2c58597 100644 (file)
@@ -178,12 +178,44 @@ static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
        return n;
 }
 
+static int callchain_node__count_flat_rows(struct callchain_node *node)
+{
+       struct callchain_list *chain;
+       char folded_sign = 0;
+       int n = 0;
+
+       list_for_each_entry(chain, &node->parent_val, list) {
+               if (!folded_sign) {
+                       /* only check first chain list entry */
+                       folded_sign = callchain_list__folded(chain);
+                       if (folded_sign == '+')
+                               return 1;
+               }
+               n++;
+       }
+
+       list_for_each_entry(chain, &node->val, list) {
+               if (!folded_sign) {
+                       /* node->parent_val list might be empty */
+                       folded_sign = callchain_list__folded(chain);
+                       if (folded_sign == '+')
+                               return 1;
+               }
+               n++;
+       }
+
+       return n;
+}
+
 static int callchain_node__count_rows(struct callchain_node *node)
 {
        struct callchain_list *chain;
        bool unfolded = false;
        int n = 0;
 
+       if (callchain_param.mode == CHAIN_FLAT)
+               return callchain_node__count_flat_rows(node);
+
        list_for_each_entry(chain, &node->val, list) {
                ++n;
                unfolded = chain->unfolded;
@@ -263,7 +295,7 @@ static void callchain_node__init_have_children(struct callchain_node *node,
        chain = list_entry(node->val.next, struct callchain_list, list);
        chain->has_children = has_sibling;
 
-       if (!list_empty(&node->val)) {
+       if (node->val.next != node->val.prev) {
                chain = list_entry(node->val.prev, struct callchain_list, list);
                chain->has_children = !RB_EMPTY_ROOT(&node->rb_root);
        }
@@ -279,6 +311,8 @@ static void callchain__init_have_children(struct rb_root *root)
        for (nd = rb_first(root); nd; nd = rb_next(nd)) {
                struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
                callchain_node__init_have_children(node, has_sibling);
+               if (callchain_param.mode == CHAIN_FLAT)
+                       callchain_node__make_parent_list(node);
        }
 }
 
@@ -612,6 +646,83 @@ static int hist_browser__show_callchain_list(struct hist_browser *browser,
        return 1;
 }
 
+static int hist_browser__show_callchain_flat(struct hist_browser *browser,
+                                            struct rb_root *root,
+                                            unsigned short row, u64 total,
+                                            print_callchain_entry_fn print,
+                                            struct callchain_print_arg *arg,
+                                            check_output_full_fn is_output_full)
+{
+       struct rb_node *node;
+       int first_row = row, offset = LEVEL_OFFSET_STEP;
+       bool need_percent;
+
+       node = rb_first(root);
+       need_percent = node && rb_next(node);
+
+       while (node) {
+               struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
+               struct rb_node *next = rb_next(node);
+               struct callchain_list *chain;
+               char folded_sign = ' ';
+               int first = true;
+               int extra_offset = 0;
+
+               list_for_each_entry(chain, &child->parent_val, list) {
+                       bool was_first = first;
+
+                       if (first)
+                               first = false;
+                       else if (need_percent)
+                               extra_offset = LEVEL_OFFSET_STEP;
+
+                       folded_sign = callchain_list__folded(chain);
+
+                       row += hist_browser__show_callchain_list(browser, child,
+                                                       chain, row, total,
+                                                       was_first && need_percent,
+                                                       offset + extra_offset,
+                                                       print, arg);
+
+                       if (is_output_full(browser, row))
+                               goto out;
+
+                       if (folded_sign == '+')
+                               goto next;
+               }
+
+               list_for_each_entry(chain, &child->val, list) {
+                       bool was_first = first;
+
+                       if (first)
+                               first = false;
+                       else if (need_percent)
+                               extra_offset = LEVEL_OFFSET_STEP;
+
+                       folded_sign = callchain_list__folded(chain);
+
+                       row += hist_browser__show_callchain_list(browser, child,
+                                                       chain, row, total,
+                                                       was_first && need_percent,
+                                                       offset + extra_offset,
+                                                       print, arg);
+
+                       if (is_output_full(browser, row))
+                               goto out;
+
+                       if (folded_sign == '+')
+                               break;
+               }
+
+next:
+               if (is_output_full(browser, row))
+                       break;
+               node = next;
+       }
+out:
+       return row - first_row;
+}
+
 static int hist_browser__show_callchain(struct hist_browser *browser,
                                        struct rb_root *root, int level,
                                        unsigned short row, u64 total,
@@ -864,10 +975,17 @@ static int hist_browser__show_entry(struct hist_browser *browser,
                                total = entry->stat.period;
                }
 
-               printed += hist_browser__show_callchain(browser,
+               if (callchain_param.mode == CHAIN_FLAT) {
+                       printed += hist_browser__show_callchain_flat(browser,
+                                       &entry->sorted_chain, row, total,
+                                       hist_browser__show_callchain_entry, &arg,
+                                       hist_browser__check_output_full);
+               } else {
+                       printed += hist_browser__show_callchain(browser,
                                        &entry->sorted_chain, 1, row, total,
                                        hist_browser__show_callchain_entry, &arg,
                                        hist_browser__check_output_full);
+               }
 
                if (arg.is_current_entry)
                        browser->he_selection = entry;
index 717c58c1da58f51315885d9c4fb98a4e02d6df45..fc3b1e0d09eec2b318cfabaa84e952d1f9843355 100644 (file)
@@ -387,6 +387,7 @@ create_child(struct callchain_node *parent, bool inherit_children)
        }
        new->parent = parent;
        INIT_LIST_HEAD(&new->val);
+       INIT_LIST_HEAD(&new->parent_val);
 
        if (inherit_children) {
                struct rb_node *n;
@@ -894,6 +895,11 @@ static void free_callchain_node(struct callchain_node *node)
        struct callchain_node *child;
        struct rb_node *n;
 
+       list_for_each_entry_safe(list, tmp, &node->parent_val, list) {
+               list_del(&list->list);
+               free(list);
+       }
+
        list_for_each_entry_safe(list, tmp, &node->val, list) {
                list_del(&list->list);
                free(list);
@@ -917,3 +923,41 @@ void free_callchain(struct callchain_root *root)
 
        free_callchain_node(&root->node);
 }
+
+int callchain_node__make_parent_list(struct callchain_node *node)
+{
+       struct callchain_node *parent = node->parent;
+       struct callchain_list *chain, *new;
+       LIST_HEAD(head);
+
+       while (parent) {
+               list_for_each_entry_reverse(chain, &parent->val, list) {
+                       new = malloc(sizeof(*new));
+                       if (new == NULL)
+                               goto out;
+                       *new = *chain;
+                       new->has_children = false;
+                       list_add_tail(&new->list, &head);
+               }
+               parent = parent->parent;
+       }
+
+       list_for_each_entry_safe_reverse(chain, new, &head, list)
+               list_move_tail(&chain->list, &node->parent_val);
+
+       if (!list_empty(&node->parent_val)) {
+               chain = list_first_entry(&node->parent_val, struct callchain_list, list);
+               chain->has_children = rb_prev(&node->rb_node) || rb_next(&node->rb_node);
+
+               chain = list_first_entry(&node->val, struct callchain_list, list);
+               chain->has_children = false;
+       }
+       return 0;
+
+out:
+       list_for_each_entry_safe(chain, new, &head, list) {
+               list_del(&chain->list);
+               free(chain);
+       }
+       return -ENOMEM;
+}
index 47bc0c57f764f097fb8e9e0c4e9252835412dafb..6e9b5f2099e191d2ace7697559920d2a377cf9d8 100644 (file)
@@ -56,6 +56,7 @@ enum chain_order {
 struct callchain_node {
        struct callchain_node   *parent;
        struct list_head        val;
+       struct list_head        parent_val;
        struct rb_node          rb_node_in; /* to insert nodes in an rbtree */
        struct rb_node          rb_node;    /* to sort nodes in an output tree */
        struct rb_root          rb_root_in; /* input tree of children */
@@ -251,5 +252,6 @@ int callchain_node__fprintf_value(struct callchain_node *node,
                                  FILE *fp, u64 total);
 
 void free_callchain(struct callchain_root *root);
+int callchain_node__make_parent_list(struct callchain_node *node);
 
 #endif /* __PERF_CALLCHAIN_H */