perf report: Enable sorting by srcline as key
authorMilian Wolff <milian.wolff@kdab.com>
Sat, 18 Mar 2017 21:49:28 +0000 (22:49 +0100)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Mon, 27 Mar 2017 15:13:28 +0000 (12:13 -0300)
Often it is interesting to know how costly a given source line is in
total. Previously, one had to build these sums manually based on all
addresses that pointed to the same source line. This patch introduces
srcline as a sort key, which will do the aggregation for us.

Paired with the recent addition of showing inline frames, this makes
perf report much more useful for many C++ work loads.

The following shows the new feature in action. First, let's show the
status quo output when we sort by address. The result contains many hist
entries that generate the same output:

  ~~~~~~~~~~~~~~~~
  $ perf report --stdio --inline -g address
  # Children      Self  Command       Shared Object        Symbol
  # ........  ........  ............  ...................  .........................................
  #
      99.89%    35.34%  cpp-inlining  cpp-inlining         [.] main
            |
            |--64.55%--main complex:655
            |          /home/milian/projects/kdab/rnd/hotspot/tests/test-clients/cpp-inlining/main.cpp:39 (inline)
            |          /usr/include/c++/6.3.1/complex:664 (inline)
            |          |
            |          |--60.31%--hypot +20
            |          |          |
            |          |          |--8.52%--__hypot_finite +273
            |          |          |
            |          |          |--7.32%--__hypot_finite +411
...
             --35.34%--_start +4194346
                       __libc_start_main +241
                       |
                       |--6.65%--main random.tcc:3326
                       |          /home/milian/projects/kdab/rnd/hotspot/tests/test-clients/cpp-inlining/main.cpp:39 (inline)
                       |          /usr/include/c++/6.3.1/bits/random.h:1809 (inline)
                       |          /usr/include/c++/6.3.1/bits/random.h:1818 (inline)
                       |          /usr/include/c++/6.3.1/bits/random.h:185 (inline)
                       |
                       |--2.70%--main random.tcc:3326
                       |          /home/milian/projects/kdab/rnd/hotspot/tests/test-clients/cpp-inlining/main.cpp:39 (inline)
                       |          /usr/include/c++/6.3.1/bits/random.h:1809 (inline)
                       |          /usr/include/c++/6.3.1/bits/random.h:1818 (inline)
                       |          /usr/include/c++/6.3.1/bits/random.h:185 (inline)
                       |
                       |--1.69%--main random.tcc:3326
                       |          /home/milian/projects/kdab/rnd/hotspot/tests/test-clients/cpp-inlining/main.cpp:39 (inline)
                       |          /usr/include/c++/6.3.1/bits/random.h:1809 (inline)
                       |          /usr/include/c++/6.3.1/bits/random.h:1818 (inline)
                       |          /usr/include/c++/6.3.1/bits/random.h:185 (inline)
  ...
  ~~~~~~~~~~~~~~~~

With this patch and `-g srcline` we instead get the following output:

  ~~~~~~~~~~~~~~~~
  $ perf report --stdio --inline -g srcline
  # Children      Self  Command       Shared Object        Symbol
  # ........  ........  ............  ...................  .........................................
  #
      99.89%    35.34%  cpp-inlining  cpp-inlining         [.] main
            |
            |--64.55%--main complex:655
            |          /home/milian/projects/kdab/rnd/hotspot/tests/test-clients/cpp-inlining/main.cpp:39 (inline)
            |          /usr/include/c++/6.3.1/complex:664 (inline)
            |          |
            |          |--64.02%--hypot
            |          |          |
            |          |           --59.81%--__hypot_finite
            |          |
            |           --0.53%--cabs
            |
             --35.34%--_start
                       __libc_start_main
                       |
                       |--12.48%--main random.tcc:3326
                       |          /home/milian/projects/kdab/rnd/hotspot/tests/test-clients/cpp-inlining/main.cpp:39 (inline)
                       |          /usr/include/c++/6.3.1/bits/random.h:1809 (inline)
                       |          /usr/include/c++/6.3.1/bits/random.h:1818 (inline)
                       |          /usr/include/c++/6.3.1/bits/random.h:185 (inline)
  ...
  ~~~~~~~~~~~~~~~~

Signed-off-by: Milian Wolff <milian.wolff@kdab.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Yao Jin <yao.jin@linux.intel.com>
Link: http://lkml.kernel.org/r/20170318214928.9047-1-milian.wolff@kdab.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
tools/perf/Documentation/perf-report.txt
tools/perf/ui/browsers/hists.c
tools/perf/ui/stdio/hist.c
tools/perf/util/annotate.c
tools/perf/util/callchain.c
tools/perf/util/callchain.h
tools/perf/util/map.c
tools/perf/util/sort.c
tools/perf/util/srcline.c
tools/perf/util/util.h

index 248bba434b530b2db22b0a2ecc1c5884e01627b4..37a1759141579b6fdf35ee1ebcb996060ef01c71 100644 (file)
@@ -235,6 +235,7 @@ OPTIONS
        sort_key can be:
        - function: compare on functions (default)
        - address: compare on individual code addresses
+       - srcline: compare on source filename and line number
 
        branch can be:
        - branch: include last branch information in callgraph when available.
index 62ecaebf25208313a7cf3fb4d78026f5b04616b0..da24072bb76ec29ca481c1badfad9b9a6e1954ba 100644 (file)
@@ -851,7 +851,8 @@ static int hist_browser__show_inline(struct hist_browser *browser,
                        if (ui_browser__is_current_entry(&browser->b, row))
                                color = HE_COLORSET_SELECTED;
 
-                       if (callchain_param.key == CCKEY_ADDRESS) {
+                       if (callchain_param.key == CCKEY_ADDRESS ||
+                           callchain_param.key == CCKEY_SRCLINE) {
                                if (ilist->filename != NULL)
                                        scnprintf(buf, sizeof(buf),
                                                  "%s:%d (inline)",
index 6128f485a3c502927d991b222913723578360580..d52d5f64ea89119064e3bc57479b6df82fde2a92 100644 (file)
@@ -52,7 +52,8 @@ static size_t inline__fprintf(struct map *map, u64 ip, int left_margin,
                                ret += fprintf(fp, "          ");
                        }
 
-                       if (callchain_param.key == CCKEY_ADDRESS) {
+                       if (callchain_param.key == CCKEY_ADDRESS ||
+                           callchain_param.key == CCKEY_SRCLINE) {
                                if (ilist->filename != NULL)
                                        ret += fprintf(fp, "%s:%d (inline)",
                                                       ilist->filename,
index 22cd1dbe724bf7db50c937b215924fd41ce9a99f..3d0263e5d1db662147487c13d1eab35a4738b9f5 100644 (file)
@@ -1674,7 +1674,8 @@ static int symbol__get_source_line(struct symbol *sym, struct map *map,
                        goto next;
 
                offset = start + i;
-               src_line->path = get_srcline(map->dso, offset, NULL, false);
+               src_line->path = get_srcline(map->dso, offset, NULL,
+                                            false, true);
                insert_source_line(&tmp_root, src_line);
 
        next:
index aba953421a0329d0de0f2adc5ce1a4599d0fe320..d78776a20e8002da76ff0195e243769df2444e49 100644 (file)
@@ -80,6 +80,10 @@ static int parse_callchain_sort_key(const char *value)
                callchain_param.key = CCKEY_ADDRESS;
                return 0;
        }
+       if (!strncmp(value, "srcline", strlen(value))) {
+               callchain_param.key = CCKEY_SRCLINE;
+               return 0;
+       }
        if (!strncmp(value, "branch", strlen(value))) {
                callchain_param.branch_callstack = 1;
                return 0;
@@ -510,14 +514,51 @@ enum match_result {
        MATCH_GT,
 };
 
+static enum match_result match_chain_srcline(struct callchain_cursor_node *node,
+                                            struct callchain_list *cnode)
+{
+       char *left = get_srcline(cnode->ms.map->dso,
+                                map__rip_2objdump(cnode->ms.map, cnode->ip),
+                                cnode->ms.sym, true, false);
+       char *right = get_srcline(node->map->dso,
+                                 map__rip_2objdump(node->map, node->ip),
+                                 node->sym, true, false);
+       enum match_result ret = MATCH_EQ;
+       int cmp;
+
+       if (left && right)
+               cmp = strcmp(left, right);
+       else if (!left && right)
+               cmp = 1;
+       else if (left && !right)
+               cmp = -1;
+       else if (cnode->ip == node->ip)
+               cmp = 0;
+       else
+               cmp = (cnode->ip < node->ip) ? -1 : 1;
+
+       if (cmp != 0)
+               ret = cmp < 0 ? MATCH_LT : MATCH_GT;
+
+       free_srcline(left);
+       free_srcline(right);
+       return ret;
+}
+
 static enum match_result match_chain(struct callchain_cursor_node *node,
                                     struct callchain_list *cnode)
 {
        struct symbol *sym = node->sym;
        u64 left, right;
 
-       if (cnode->ms.sym && sym &&
-           callchain_param.key == CCKEY_FUNCTION) {
+       if (callchain_param.key == CCKEY_SRCLINE) {
+               enum match_result match = match_chain_srcline(node, cnode);
+
+               if (match != MATCH_ERROR)
+                       return match;
+       }
+
+       if (cnode->ms.sym && sym && callchain_param.key == CCKEY_FUNCTION) {
                left = cnode->ms.sym->start;
                right = sym->start;
        } else {
@@ -911,15 +952,16 @@ out:
 char *callchain_list__sym_name(struct callchain_list *cl,
                               char *bf, size_t bfsize, bool show_dso)
 {
+       bool show_addr = callchain_param.key == CCKEY_ADDRESS;
+       bool show_srcline = show_addr || callchain_param.key == CCKEY_SRCLINE;
        int printed;
 
        if (cl->ms.sym) {
-               if (callchain_param.key == CCKEY_ADDRESS &&
-                   cl->ms.map && !cl->srcline)
+               if (show_srcline && cl->ms.map && !cl->srcline)
                        cl->srcline = get_srcline(cl->ms.map->dso,
                                                  map__rip_2objdump(cl->ms.map,
                                                                    cl->ip),
-                                                 cl->ms.sym, false);
+                                                 cl->ms.sym, false, show_addr);
                if (cl->srcline)
                        printed = scnprintf(bf, bfsize, "%s %s",
                                        cl->ms.sym->name, cl->srcline);
index 4f4b60f1558a827cbe34d9d387e6a91a917b58dd..c56c23dbbf72838b8758ec703759a6ad09f229a6 100644 (file)
@@ -77,7 +77,8 @@ typedef void (*sort_chain_func_t)(struct rb_root *, struct callchain_root *,
 
 enum chain_key {
        CCKEY_FUNCTION,
-       CCKEY_ADDRESS
+       CCKEY_ADDRESS,
+       CCKEY_SRCLINE
 };
 
 enum chain_value {
index 1d9ebcf9e38e867d3626069865dfb61f2385f599..c1870ac365a3547c7dacbf7d9ee24cf8a089c07f 100644 (file)
@@ -405,7 +405,8 @@ int map__fprintf_srcline(struct map *map, u64 addr, const char *prefix,
 
        if (map && map->dso) {
                srcline = get_srcline(map->dso,
-                                     map__rip_2objdump(map, addr), NULL, true);
+                                     map__rip_2objdump(map, addr), NULL,
+                                     true, true);
                if (srcline != SRCLINE_UNKNOWN)
                        ret = fprintf(fp, "%s%s", prefix, srcline);
                free_srcline(srcline);
index 8b0d4e39f6402fa6f836821d43ee9a52115aea36..73f3ec1cf2a0f8bb4bb3b7d8bfe2cb39780251f1 100644 (file)
@@ -323,7 +323,7 @@ char *hist_entry__get_srcline(struct hist_entry *he)
                return SRCLINE_UNKNOWN;
 
        return get_srcline(map->dso, map__rip_2objdump(map, he->ip),
-                          he->ms.sym, true);
+                          he->ms.sym, true, true);
 }
 
 static int64_t
@@ -366,7 +366,8 @@ sort__srcline_from_cmp(struct hist_entry *left, struct hist_entry *right)
                        left->branch_info->srcline_from = get_srcline(map->dso,
                                           map__rip_2objdump(map,
                                                             left->branch_info->from.al_addr),
-                                                        left->branch_info->from.sym, true);
+                                                        left->branch_info->from.sym,
+                                                        true, true);
        }
        if (!right->branch_info->srcline_from) {
                struct map *map = right->branch_info->from.map;
@@ -376,7 +377,8 @@ sort__srcline_from_cmp(struct hist_entry *left, struct hist_entry *right)
                        right->branch_info->srcline_from = get_srcline(map->dso,
                                             map__rip_2objdump(map,
                                                               right->branch_info->from.al_addr),
-                                                    right->branch_info->from.sym, true);
+                                                    right->branch_info->from.sym,
+                                                    true, true);
        }
        return strcmp(right->branch_info->srcline_from, left->branch_info->srcline_from);
 }
@@ -407,7 +409,8 @@ sort__srcline_to_cmp(struct hist_entry *left, struct hist_entry *right)
                        left->branch_info->srcline_to = get_srcline(map->dso,
                                           map__rip_2objdump(map,
                                                             left->branch_info->to.al_addr),
-                                                        left->branch_info->from.sym, true);
+                                                        left->branch_info->from.sym,
+                                                        true, true);
        }
        if (!right->branch_info->srcline_to) {
                struct map *map = right->branch_info->to.map;
@@ -417,7 +420,8 @@ sort__srcline_to_cmp(struct hist_entry *left, struct hist_entry *right)
                        right->branch_info->srcline_to = get_srcline(map->dso,
                                             map__rip_2objdump(map,
                                                               right->branch_info->to.al_addr),
-                                                    right->branch_info->to.sym, true);
+                                                    right->branch_info->to.sym,
+                                                    true, true);
        }
        return strcmp(right->branch_info->srcline_to, left->branch_info->srcline_to);
 }
@@ -448,7 +452,7 @@ static char *hist_entry__get_srcfile(struct hist_entry *e)
                return no_srcfile;
 
        sf = __get_srcline(map->dso, map__rip_2objdump(map, e->ip),
-                        e->ms.sym, false, true);
+                        e->ms.sym, false, true, true);
        if (!strcmp(sf, SRCLINE_UNKNOWN))
                return no_srcfile;
        p = strchr(sf, ':');
index 3ce28f702b36bbb03387a163848f235f9f03b616..778ccb5d99d14c3bcc1d742c3feefc6fe34531b2 100644 (file)
@@ -429,7 +429,7 @@ out:
 #define A2L_FAIL_LIMIT 123
 
 char *__get_srcline(struct dso *dso, u64 addr, struct symbol *sym,
-                 bool show_sym, bool unwind_inlines)
+                 bool show_sym, bool show_addr, bool unwind_inlines)
 {
        char *file = NULL;
        unsigned line = 0;
@@ -463,6 +463,11 @@ out:
                dso->has_srcline = 0;
                dso__free_a2l(dso);
        }
+
+       if (!show_addr)
+               return (show_sym && sym) ?
+                           strndup(sym->name, sym->namelen) : NULL;
+
        if (sym) {
                if (asprintf(&srcline, "%s+%" PRIu64, show_sym ? sym->name : "",
                                        addr - sym->start) < 0)
@@ -479,9 +484,9 @@ void free_srcline(char *srcline)
 }
 
 char *get_srcline(struct dso *dso, u64 addr, struct symbol *sym,
-                 bool show_sym)
+                 bool show_sym, bool show_addr)
 {
-       return __get_srcline(dso, addr, sym, show_sym, false);
+       return __get_srcline(dso, addr, sym, show_sym, show_addr, false);
 }
 
 struct inline_node *dso__parse_addr_inlines(struct dso *dso, u64 addr)
index cc0700d6fef07bd497861f2675961b9bec9885b4..7cf5752b38fdbbdf380ba2e4dd9403bfd0f81ea9 100644 (file)
@@ -287,9 +287,9 @@ struct symbol;
 
 extern bool srcline_full_filename;
 char *get_srcline(struct dso *dso, u64 addr, struct symbol *sym,
-                 bool show_sym);
+                 bool show_sym, bool show_addr);
 char *__get_srcline(struct dso *dso, u64 addr, struct symbol *sym,
-                 bool show_sym, bool unwind_inlines);
+                 bool show_sym, bool show_addr, bool unwind_inlines);
 void free_srcline(char *srcline);
 
 int perf_event_paranoid(void);