Merge tag 'perf-core-for-mingo-20160920' of git://git.kernel.org/pub/scm/linux/kernel...
[linux-2.6-block.git] / tools / perf / util / annotate.c
index e9825fe825fd65088ef807a3b54ca4a4a198ead7..aeb5a441bd7416744598f7dfa6d18809cf4fa883 100644 (file)
@@ -17,6 +17,7 @@
 #include "debug.h"
 #include "annotate.h"
 #include "evsel.h"
+#include "block-range.h"
 #include <regex.h>
 #include <pthread.h>
 #include <linux/bitops.h>
@@ -53,7 +54,7 @@ int ins__scnprintf(struct ins *ins, char *bf, size_t size,
        return ins__raw_scnprintf(ins, bf, size, ops);
 }
 
-static int call__parse(struct ins_operands *ops)
+static int call__parse(struct ins_operands *ops, struct map *map)
 {
        char *endptr, *tok, *name;
 
@@ -81,16 +82,16 @@ static int call__parse(struct ins_operands *ops)
        return ops->target.name == NULL ? -1 : 0;
 
 indirect_call:
-       tok = strchr(endptr, '(');
-       if (tok != NULL) {
-               ops->target.addr = 0;
+       tok = strchr(endptr, '*');
+       if (tok == NULL) {
+               struct symbol *sym = map__find_symbol(map, map->map_ip(map, ops->target.addr));
+               if (sym != NULL)
+                       ops->target.name = strdup(sym->name);
+               else
+                       ops->target.addr = 0;
                return 0;
        }
 
-       tok = strchr(endptr, '*');
-       if (tok == NULL)
-               return -1;
-
        ops->target.addr = strtoull(tok + 1, NULL, 16);
        return 0;
 }
@@ -117,7 +118,7 @@ bool ins__is_call(const struct ins *ins)
        return ins->ops == &call_ops;
 }
 
-static int jump__parse(struct ins_operands *ops)
+static int jump__parse(struct ins_operands *ops, struct map *map __maybe_unused)
 {
        const char *s = strchr(ops->raw, '+');
 
@@ -172,7 +173,7 @@ static int comment__symbol(char *raw, char *comment, u64 *addrp, char **namep)
        return 0;
 }
 
-static int lock__parse(struct ins_operands *ops)
+static int lock__parse(struct ins_operands *ops, struct map *map)
 {
        char *name;
 
@@ -193,7 +194,7 @@ static int lock__parse(struct ins_operands *ops)
                return 0;
 
        if (ops->locked.ins->ops->parse &&
-           ops->locked.ins->ops->parse(ops->locked.ops) < 0)
+           ops->locked.ins->ops->parse(ops->locked.ops, map) < 0)
                goto out_free_ops;
 
        return 0;
@@ -236,7 +237,7 @@ static struct ins_ops lock_ops = {
        .scnprintf = lock__scnprintf,
 };
 
-static int mov__parse(struct ins_operands *ops)
+static int mov__parse(struct ins_operands *ops, struct map *map __maybe_unused)
 {
        char *s = strchr(ops->raw, ','), *target, *comment, prev;
 
@@ -303,7 +304,7 @@ static struct ins_ops mov_ops = {
        .scnprintf = mov__scnprintf,
 };
 
-static int dec__parse(struct ins_operands *ops)
+static int dec__parse(struct ins_operands *ops, struct map *map __maybe_unused)
 {
        char *target, *comment, *s, prev;
 
@@ -491,13 +492,6 @@ static struct ins *ins__find(const char *name)
        return bsearch(name, instructions, nmemb, sizeof(struct ins), ins__key_cmp);
 }
 
-int symbol__annotate_init(struct map *map __maybe_unused, struct symbol *sym)
-{
-       struct annotation *notes = symbol__annotation(sym);
-       pthread_mutex_init(&notes->lock, NULL);
-       return 0;
-}
-
 int symbol__alloc_hist(struct symbol *sym)
 {
        struct annotation *notes = symbol__annotation(sym);
@@ -715,7 +709,7 @@ int hist_entry__inc_addr_samples(struct hist_entry *he, int evidx, u64 ip)
        return symbol__inc_addr_samples(he->ms.sym, he->ms.map, evidx, ip);
 }
 
-static void disasm_line__init_ins(struct disasm_line *dl)
+static void disasm_line__init_ins(struct disasm_line *dl, struct map *map)
 {
        dl->ins = ins__find(dl->name);
 
@@ -725,7 +719,7 @@ static void disasm_line__init_ins(struct disasm_line *dl)
        if (!dl->ins->ops)
                return;
 
-       if (dl->ins->ops->parse && dl->ins->ops->parse(&dl->ops) < 0)
+       if (dl->ins->ops->parse && dl->ins->ops->parse(&dl->ops, map) < 0)
                dl->ins = NULL;
 }
 
@@ -767,7 +761,8 @@ out_free_name:
 }
 
 static struct disasm_line *disasm_line__new(s64 offset, char *line,
-                                       size_t privsize, int line_nr)
+                                           size_t privsize, int line_nr,
+                                           struct map *map)
 {
        struct disasm_line *dl = zalloc(sizeof(*dl) + privsize);
 
@@ -782,7 +777,7 @@ static struct disasm_line *disasm_line__new(s64 offset, char *line,
                        if (disasm_line__parse(dl->line, &dl->name, &dl->ops.raw) < 0)
                                goto out_free_line;
 
-                       disasm_line__init_ins(dl);
+                       disasm_line__init_ins(dl, map);
                }
        }
 
@@ -866,6 +861,89 @@ double disasm__calc_percent(struct annotation *notes, int evidx, s64 offset,
        return percent;
 }
 
+static const char *annotate__address_color(struct block_range *br)
+{
+       double cov = block_range__coverage(br);
+
+       if (cov >= 0) {
+               /* mark red for >75% coverage */
+               if (cov > 0.75)
+                       return PERF_COLOR_RED;
+
+               /* mark dull for <1% coverage */
+               if (cov < 0.01)
+                       return PERF_COLOR_NORMAL;
+       }
+
+       return PERF_COLOR_MAGENTA;
+}
+
+static const char *annotate__asm_color(struct block_range *br)
+{
+       double cov = block_range__coverage(br);
+
+       if (cov >= 0) {
+               /* mark dull for <1% coverage */
+               if (cov < 0.01)
+                       return PERF_COLOR_NORMAL;
+       }
+
+       return PERF_COLOR_BLUE;
+}
+
+static void annotate__branch_printf(struct block_range *br, u64 addr)
+{
+       bool emit_comment = true;
+
+       if (!br)
+               return;
+
+#if 1
+       if (br->is_target && br->start == addr) {
+               struct block_range *branch = br;
+               double p;
+
+               /*
+                * Find matching branch to our target.
+                */
+               while (!branch->is_branch)
+                       branch = block_range__next(branch);
+
+               p = 100 *(double)br->entry / branch->coverage;
+
+               if (p > 0.1) {
+                       if (emit_comment) {
+                               emit_comment = false;
+                               printf("\t#");
+                       }
+
+                       /*
+                        * The percentage of coverage joined at this target in relation
+                        * to the next branch.
+                        */
+                       printf(" +%.2f%%", p);
+               }
+       }
+#endif
+       if (br->is_branch && br->end == addr) {
+               double p = 100*(double)br->taken / br->coverage;
+
+               if (p > 0.1) {
+                       if (emit_comment) {
+                               emit_comment = false;
+                               printf("\t#");
+                       }
+
+                       /*
+                        * The percentage of coverage leaving at this branch, and
+                        * its prediction ratio.
+                        */
+                       printf(" -%.2f%% (p:%.2f%%)", p, 100*(double)br->pred  / br->taken);
+               }
+       }
+}
+
+
 static int disasm_line__print(struct disasm_line *dl, struct symbol *sym, u64 start,
                      struct perf_evsel *evsel, u64 len, int min_pcnt, int printed,
                      int max_lines, struct disasm_line *queue)
@@ -885,6 +963,7 @@ static int disasm_line__print(struct disasm_line *dl, struct symbol *sym, u64 st
                s64 offset = dl->offset;
                const u64 addr = start + offset;
                struct disasm_line *next;
+               struct block_range *br;
 
                next = disasm__get_next_ip_line(&notes->src->source, dl);
 
@@ -954,8 +1033,12 @@ static int disasm_line__print(struct disasm_line *dl, struct symbol *sym, u64 st
                }
 
                printf(" :      ");
-               color_fprintf(stdout, PERF_COLOR_MAGENTA, "  %" PRIx64 ":", addr);
-               color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", dl->line);
+
+               br = block_range__find(addr);
+               color_fprintf(stdout, annotate__address_color(br), "  %" PRIx64 ":", addr);
+               color_fprintf(stdout, annotate__asm_color(br), "%s", dl->line);
+               annotate__branch_printf(br, addr);
+               printf("\n");
 
                if (ppercents != &percent)
                        free(ppercents);
@@ -1066,7 +1149,7 @@ static int symbol__parse_objdump_line(struct symbol *sym, struct map *map,
                        parsed_line = tmp2 + 1;
        }
 
-       dl = disasm_line__new(offset, parsed_line, privsize, *line_nr);
+       dl = disasm_line__new(offset, parsed_line, privsize, *line_nr, map);
        free(line);
        (*line_nr)++;
 
@@ -1084,7 +1167,7 @@ static int symbol__parse_objdump_line(struct symbol *sym, struct map *map,
                        .addr = dl->ops.target.addr,
                };
 
-               if (!map_groups__find_ams(&target, NULL) &&
+               if (!map_groups__find_ams(&target) &&
                    target.sym->start == target.al_addr)
                        dl->ops.target.name = strdup(target.sym->name);
        }
@@ -1123,75 +1206,99 @@ static void delete_last_nop(struct symbol *sym)
        }
 }
 
-int symbol__annotate(struct symbol *sym, struct map *map, size_t privsize)
+int symbol__strerror_disassemble(struct symbol *sym __maybe_unused, struct map *map,
+                             int errnum, char *buf, size_t buflen)
 {
        struct dso *dso = map->dso;
-       char *filename = dso__build_id_filename(dso, NULL, 0);
-       bool free_filename = true;
-       char command[PATH_MAX * 2];
-       FILE *file;
-       int err = 0;
-       char symfs_filename[PATH_MAX];
-       struct kcore_extract kce;
-       bool delete_extract = false;
-       int lineno = 0;
-       int nline;
 
-       if (filename)
-               symbol__join_symfs(symfs_filename, filename);
+       BUG_ON(buflen == 0);
+
+       if (errnum >= 0) {
+               str_error_r(errnum, buf, buflen);
+               return 0;
+       }
+
+       switch (errnum) {
+       case SYMBOL_ANNOTATE_ERRNO__NO_VMLINUX: {
+               char bf[SBUILD_ID_SIZE + 15] = " with build id ";
+               char *build_id_msg = NULL;
 
-       if (filename == NULL) {
                if (dso->has_build_id) {
-                       pr_err("Can't annotate %s: not enough memory\n",
-                              sym->name);
-                       return -ENOMEM;
+                       build_id__sprintf(dso->build_id,
+                                         sizeof(dso->build_id), bf + 15);
+                       build_id_msg = bf;
                }
+               scnprintf(buf, buflen,
+                         "No vmlinux file%s\nwas found in the path.\n\n"
+                         "Note that annotation using /proc/kcore requires CAP_SYS_RAWIO capability.\n\n"
+                         "Please use:\n\n"
+                         "  perf buildid-cache -vu vmlinux\n\n"
+                         "or:\n\n"
+                         "  --vmlinux vmlinux\n", build_id_msg ?: "");
+       }
+               break;
+       default:
+               scnprintf(buf, buflen, "Internal error: Invalid %d error code\n", errnum);
+               break;
+       }
+
+       return 0;
+}
+
+static int dso__disassemble_filename(struct dso *dso, char *filename, size_t filename_size)
+{
+       char linkname[PATH_MAX];
+       char *build_id_filename;
+
+       if (dso->symtab_type == DSO_BINARY_TYPE__KALLSYMS &&
+           !dso__is_kcore(dso))
+               return SYMBOL_ANNOTATE_ERRNO__NO_VMLINUX;
+
+       build_id_filename = dso__build_id_filename(dso, NULL, 0);
+       if (build_id_filename) {
+               __symbol__join_symfs(filename, filename_size, build_id_filename);
+               free(build_id_filename);
+       } else {
+               if (dso->has_build_id)
+                       return ENOMEM;
                goto fallback;
-       } else if (dso__is_kcore(dso)) {
-               goto fallback;
-       } else if (readlink(symfs_filename, command, sizeof(command)) < 0 ||
-                  strstr(command, DSO__NAME_KALLSYMS) ||
-                  access(symfs_filename, R_OK)) {
-               free(filename);
+       }
+
+       if (dso__is_kcore(dso) ||
+           readlink(filename, linkname, sizeof(linkname)) < 0 ||
+           strstr(linkname, DSO__NAME_KALLSYMS) ||
+           access(filename, R_OK)) {
 fallback:
                /*
                 * If we don't have build-ids or the build-id file isn't in the
                 * cache, or is just a kallsyms file, well, lets hope that this
                 * DSO is the same as when 'perf record' ran.
                 */
-               filename = (char *)dso->long_name;
-               symbol__join_symfs(symfs_filename, filename);
-               free_filename = false;
+               __symbol__join_symfs(filename, filename_size, dso->long_name);
        }
 
-       if (dso->symtab_type == DSO_BINARY_TYPE__KALLSYMS &&
-           !dso__is_kcore(dso)) {
-               char bf[SBUILD_ID_SIZE + 15] = " with build id ";
-               char *build_id_msg = NULL;
+       return 0;
+}
 
-               if (dso->annotate_warned)
-                       goto out_free_filename;
+int symbol__disassemble(struct symbol *sym, struct map *map, size_t privsize)
+{
+       struct dso *dso = map->dso;
+       char command[PATH_MAX * 2];
+       FILE *file;
+       char symfs_filename[PATH_MAX];
+       struct kcore_extract kce;
+       bool delete_extract = false;
+       int stdout_fd[2];
+       int lineno = 0;
+       int nline;
+       pid_t pid;
+       int err = dso__disassemble_filename(dso, symfs_filename, sizeof(symfs_filename));
 
-               if (dso->has_build_id) {
-                       build_id__sprintf(dso->build_id,
-                                         sizeof(dso->build_id), bf + 15);
-                       build_id_msg = bf;
-               }
-               err = -ENOENT;
-               dso->annotate_warned = 1;
-               pr_err("Can't annotate %s:\n\n"
-                      "No vmlinux file%s\nwas found in the path.\n\n"
-                      "Note that annotation using /proc/kcore requires CAP_SYS_RAWIO capability.\n\n"
-                      "Please use:\n\n"
-                      "  perf buildid-cache -vu vmlinux\n\n"
-                      "or:\n\n"
-                      "  --vmlinux vmlinux\n",
-                      sym->name, build_id_msg ?: "");
-               goto out_free_filename;
-       }
+       if (err)
+               return err;
 
        pr_debug("%s: filename=%s, sym=%s, start=%#" PRIx64 ", end=%#" PRIx64 "\n", __func__,
-                filename, sym->name, map->unmap_ip(map, sym->start),
+                symfs_filename, sym->name, map->unmap_ip(map, sym->start),
                 map->unmap_ip(map, sym->end));
 
        pr_debug("annotating [%p] %30s : [%p] %30s\n",
@@ -1206,11 +1313,6 @@ fallback:
                        delete_extract = true;
                        strlcpy(symfs_filename, kce.extract_filename,
                                sizeof(symfs_filename));
-                       if (free_filename) {
-                               free(filename);
-                               free_filename = false;
-                       }
-                       filename = symfs_filename;
                }
        } else if (dso__needs_decompress(dso)) {
                char tmp[PATH_MAX];
@@ -1219,14 +1321,14 @@ fallback:
                bool ret;
 
                if (kmod_path__parse_ext(&m, symfs_filename))
-                       goto out_free_filename;
+                       goto out;
 
                snprintf(tmp, PATH_MAX, "/tmp/perf-kmod-XXXXXX");
 
                fd = mkstemp(tmp);
                if (fd < 0) {
                        free(m.ext);
-                       goto out_free_filename;
+                       goto out;
                }
 
                ret = decompress_to_file(m.ext, symfs_filename, fd);
@@ -1238,7 +1340,7 @@ fallback:
                close(fd);
 
                if (!ret)
-                       goto out_free_filename;
+                       goto out;
 
                strcpy(symfs_filename, tmp);
        }
@@ -1254,13 +1356,36 @@ fallback:
                 map__rip_2objdump(map, sym->end),
                 symbol_conf.annotate_asm_raw ? "" : "--no-show-raw",
                 symbol_conf.annotate_src ? "-S" : "",
-                symfs_filename, filename);
+                symfs_filename, symfs_filename);
 
        pr_debug("Executing: %s\n", command);
 
-       file = popen(command, "r");
+       err = -1;
+       if (pipe(stdout_fd) < 0) {
+               pr_err("Failure creating the pipe to run %s\n", command);
+               goto out_remove_tmp;
+       }
+
+       pid = fork();
+       if (pid < 0) {
+               pr_err("Failure forking to run %s\n", command);
+               goto out_close_stdout;
+       }
+
+       if (pid == 0) {
+               close(stdout_fd[0]);
+               dup2(stdout_fd[1], 1);
+               close(stdout_fd[1]);
+               execl("/bin/sh", "sh", "-c", command, NULL);
+               perror(command);
+               exit(-1);
+       }
+
+       close(stdout_fd[1]);
+
+       file = fdopen(stdout_fd[0], "r");
        if (!file) {
-               pr_err("Failure running %s\n", command);
+               pr_err("Failure creating FILE stream for %s\n", command);
                /*
                 * If we were using debug info should retry with
                 * original binary.
@@ -1286,17 +1411,22 @@ fallback:
        if (dso__is_kcore(dso))
                delete_last_nop(sym);
 
-       pclose(file);
-
+       fclose(file);
+       err = 0;
 out_remove_tmp:
+       close(stdout_fd[0]);
+
        if (dso__needs_decompress(dso))
                unlink(symfs_filename);
-out_free_filename:
+
        if (delete_extract)
                kcore_extract__delete(&kce);
-       if (free_filename)
-               free(filename);
+out:
        return err;
+
+out_close_stdout:
+       close(stdout_fd[1]);
+       goto out_remove_tmp;
 }
 
 static void insert_source_line(struct rb_root *root, struct source_line *src_line)
@@ -1663,7 +1793,7 @@ int symbol__tty_annotate(struct symbol *sym, struct map *map,
        struct rb_root source_line = RB_ROOT;
        u64 len;
 
-       if (symbol__annotate(sym, map, 0) < 0)
+       if (symbol__disassemble(sym, map, 0) < 0)
                return -1;
 
        len = symbol__size(sym);