perf hists: Basic support of hierarchical report view
authorNamhyung Kim <namhyung@kernel.org>
Wed, 24 Feb 2016 15:13:34 +0000 (00:13 +0900)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Wed, 24 Feb 2016 16:35:44 +0000 (13:35 -0300)
In the hierarchical view, entries will be grouped and sorted on the
first key, and then on the second key, and so on.  Add the
he->hroot_{in,out} fields to keep the lower level entries. Actually this
can share space, in a union, with callchain's 'sorted_root' since the
hroots are only used by non-leaf entries and callchain is only used by
leaf entries.

It also adds the 'parent_he' and 'depth' fields which can be used by browsers.

This patch only implements collapsing part which creates internal
entries for each sort key.  These need to be sorted by output_sort stage
and to be displayed properly in the later patch(es).

Signed-off-by: Namhyung Kim <namhyung@kernel.org>
Acked-by: Pekka Enberg <penberg@kernel.org>
Cc: Andi Kleen <andi@firstfloor.org>
Cc: David Ahern <dsahern@gmail.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Stephane Eranian <eranian@google.com>
Cc: Wang Nan <wangnan0@huawei.com>
Link: http://lkml.kernel.org/r/1456326830-30456-3-git-send-email-namhyung@kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
tools/perf/util/hist.c
tools/perf/util/sort.h
tools/perf/util/symbol.h

index 017eb5c42c37edffc0fb128207c21d40d9ba1587..88145245095908e3b619ac0779e131e4437cf91a 100644 (file)
@@ -396,6 +396,9 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template,
                }
                INIT_LIST_HEAD(&he->pairs.node);
                thread__get(he->thread);
+
+               if (!symbol_conf.report_hierarchy)
+                       he->leaf = true;
        }
 
        return he;
@@ -1049,6 +1052,114 @@ int hist_entry__snprintf_alignment(struct hist_entry *he, struct perf_hpp *hpp,
  * collapse the histogram
  */
 
+static void hists__apply_filters(struct hists *hists, struct hist_entry *he);
+
+static struct hist_entry *hierarchy_insert_entry(struct hists *hists,
+                                                struct rb_root *root,
+                                                struct hist_entry *he,
+                                                struct perf_hpp_fmt *fmt)
+{
+       struct rb_node **p = &root->rb_node;
+       struct rb_node *parent = NULL;
+       struct hist_entry *iter, *new;
+       int64_t cmp;
+
+       while (*p != NULL) {
+               parent = *p;
+               iter = rb_entry(parent, struct hist_entry, rb_node_in);
+
+               cmp = fmt->collapse(fmt, iter, he);
+               if (!cmp) {
+                       he_stat__add_stat(&iter->stat, &he->stat);
+                       return iter;
+               }
+
+               if (cmp < 0)
+                       p = &parent->rb_left;
+               else
+                       p = &parent->rb_right;
+       }
+
+       new = hist_entry__new(he, true);
+       if (new == NULL)
+               return NULL;
+
+       hists__apply_filters(hists, new);
+       hists->nr_entries++;
+
+       /* save related format for output */
+       new->fmt = fmt;
+
+       /* some fields are now passed to 'new' */
+       if (perf_hpp__is_trace_entry(fmt))
+               he->trace_output = NULL;
+       else
+               new->trace_output = NULL;
+
+       if (perf_hpp__is_srcline_entry(fmt))
+               he->srcline = NULL;
+       else
+               new->srcline = NULL;
+
+       if (perf_hpp__is_srcfile_entry(fmt))
+               he->srcfile = NULL;
+       else
+               new->srcfile = NULL;
+
+       rb_link_node(&new->rb_node_in, parent, p);
+       rb_insert_color(&new->rb_node_in, root);
+       return new;
+}
+
+static int hists__hierarchy_insert_entry(struct hists *hists,
+                                        struct rb_root *root,
+                                        struct hist_entry *he)
+{
+       struct perf_hpp_fmt *fmt;
+       struct hist_entry *new_he = NULL;
+       struct hist_entry *parent = NULL;
+       int depth = 0;
+       int ret = 0;
+
+       hists__for_each_sort_list(hists, fmt) {
+               if (!perf_hpp__is_sort_entry(fmt) &&
+                   !perf_hpp__is_dynamic_entry(fmt))
+                       continue;
+               if (perf_hpp__should_skip(fmt, hists))
+                       continue;
+
+               /* insert copy of 'he' for each fmt into the hierarchy */
+               new_he = hierarchy_insert_entry(hists, root, he, fmt);
+               if (new_he == NULL) {
+                       ret = -1;
+                       break;
+               }
+
+               root = &new_he->hroot_in;
+               new_he->parent_he = parent;
+               new_he->depth = depth++;
+               parent = new_he;
+       }
+
+       if (new_he) {
+               new_he->leaf = true;
+
+               if (symbol_conf.use_callchain) {
+                       callchain_cursor_reset(&callchain_cursor);
+                       if (callchain_merge(&callchain_cursor,
+                                           new_he->callchain,
+                                           he->callchain) < 0)
+                               ret = -1;
+               }
+       }
+
+       /* 'he' is no longer used */
+       hist_entry__delete(he);
+
+       /* return 0 (or -1) since it already applied filters */
+       return ret;
+}
+
 int hists__collapse_insert_entry(struct hists *hists, struct rb_root *root,
                                 struct hist_entry *he)
 {
@@ -1057,6 +1168,9 @@ int hists__collapse_insert_entry(struct hists *hists, struct rb_root *root,
        struct hist_entry *iter;
        int64_t cmp;
 
+       if (symbol_conf.report_hierarchy)
+               return hists__hierarchy_insert_entry(hists, root, he);
+
        while (*p != NULL) {
                parent = *p;
                iter = rb_entry(parent, struct hist_entry, rb_node_in);
index 5b9c6246de6d95c5a7d9e74b274000e59351e5ec..10315e02adf574a59ab0c0e703611aa7e310afbc 100644 (file)
@@ -96,9 +96,11 @@ struct hist_entry {
        s32                     socket;
        s32                     cpu;
        u8                      cpumode;
+       u8                      depth;
 
        /* We are added by hists__add_dummy_entry. */
        bool                    dummy;
+       bool                    leaf;
 
        char                    level;
        u8                      filtered;
@@ -120,13 +122,22 @@ struct hist_entry {
        char                    *srcline;
        char                    *srcfile;
        struct symbol           *parent;
-       struct rb_root          sorted_chain;
        struct branch_info      *branch_info;
        struct hists            *hists;
        struct mem_info         *mem_info;
        void                    *raw_data;
        u32                     raw_size;
        void                    *trace_output;
+       struct perf_hpp_fmt     *fmt;
+       struct hist_entry       *parent_he;
+       union {
+               /* this is for hierarchical entry structure */
+               struct {
+                       struct rb_root  hroot_in;
+                       struct rb_root  hroot_out;
+               };                              /* non-leaf entries */
+               struct rb_root  sorted_chain;   /* leaf entry has callchains */
+       };
        struct callchain_root   callchain[0]; /* must be last member */
 };
 
index ccd1caa40e116645be37025665388886d7c97546..a937053a0ae07a6214fb03753faa66819ed40884 100644 (file)
@@ -110,7 +110,8 @@ struct symbol_conf {
                        has_filter,
                        show_ref_callgraph,
                        hide_unresolved,
-                       raw_trace;
+                       raw_trace,
+                       report_hierarchy;
        const char      *vmlinux_name,
                        *kallsyms_name,
                        *source_prefix,