summaryrefslogtreecommitdiff
path: root/tools/perf/builtin-annotate.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf/builtin-annotate.c')
-rw-r--r--tools/perf/builtin-annotate.c662
1 files changed, 236 insertions, 426 deletions
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c
index 593ff25006de..5ebd0c3b71b6 100644
--- a/tools/perf/builtin-annotate.c
+++ b/tools/perf/builtin-annotate.c
@@ -8,17 +8,18 @@
#include "builtin.h"
#include "util/util.h"
-
#include "util/color.h"
#include <linux/list.h>
#include "util/cache.h"
#include <linux/rbtree.h>
#include "util/symbol.h"
-#include "util/string.h"
#include "perf.h"
#include "util/debug.h"
+#include "util/evlist.h"
+#include "util/evsel.h"
+#include "util/annotate.h"
#include "util/event.h"
#include "util/parse-options.h"
#include "util/parse-events.h"
@@ -26,507 +27,324 @@
#include "util/sort.h"
#include "util/hist.h"
#include "util/session.h"
-
-static char const *input_name = "perf.data";
-
-static int force;
-
-static int full_paths;
-
-static int print_line;
-
-struct sym_hist {
- u64 sum;
- u64 ip[0];
-};
-
-struct sym_ext {
- struct rb_node node;
- double percent;
- char *path;
-};
-
-struct sym_priv {
- struct sym_hist *hist;
- struct sym_ext *ext;
+#include "util/tool.h"
+#include "arch/common.h"
+
+#include <linux/bitmap.h>
+
+struct perf_annotate {
+ struct perf_tool tool;
+ bool force, use_tui, use_stdio, use_gtk;
+ bool full_paths;
+ bool print_line;
+ bool skip_missing;
+ const char *sym_hist_filter;
+ const char *cpu_list;
+ DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
};
-static const char *sym_hist_filter;
-
-static int symbol_filter(struct map *map __used, struct symbol *sym)
+static int perf_evsel__add_sample(struct perf_evsel *evsel,
+ struct perf_sample *sample,
+ struct addr_location *al,
+ struct perf_annotate *ann)
{
- if (sym_hist_filter == NULL ||
- strcmp(sym->name, sym_hist_filter) == 0) {
- struct sym_priv *priv = symbol__priv(sym);
- const int size = (sizeof(*priv->hist) +
- (sym->end - sym->start) * sizeof(u64));
-
- priv->hist = malloc(size);
- if (priv->hist)
- memset(priv->hist, 0, size);
+ struct hist_entry *he;
+ int ret;
+
+ if (ann->sym_hist_filter != NULL &&
+ (al->sym == NULL ||
+ strcmp(ann->sym_hist_filter, al->sym->name) != 0)) {
+ /* We're only interested in a symbol named sym_hist_filter */
+ if (al->sym != NULL) {
+ rb_erase(&al->sym->rb_node,
+ &al->map->dso->symbols[al->map->type]);
+ symbol__delete(al->sym);
+ }
return 0;
}
- /*
- * FIXME: We should really filter it out, as we don't want to go thru symbols
- * we're not interested, and if a DSO ends up with no symbols, delete it too,
- * but right now the kernel loading routines in symbol.c bail out if no symbols
- * are found, fix it later.
- */
- return 0;
-}
-/*
- * collect histogram counts
- */
-static void hist_hit(struct hist_entry *he, u64 ip)
-{
- unsigned int sym_size, offset;
- struct symbol *sym = he->sym;
- struct sym_priv *priv;
- struct sym_hist *h;
-
- he->count++;
-
- if (!sym || !he->map)
- return;
-
- priv = symbol__priv(sym);
- if (!priv->hist)
- return;
-
- sym_size = sym->end - sym->start;
- offset = ip - sym->start;
-
- if (verbose)
- fprintf(stderr, "%s: ip=%Lx\n", __func__,
- he->map->unmap_ip(he->map, ip));
-
- if (offset >= sym_size)
- return;
-
- h = priv->hist;
- h->sum++;
- h->ip[offset]++;
-
- if (verbose >= 3)
- printf("%p %s: count++ [ip: %p, %08Lx] => %Ld\n",
- (void *)(unsigned long)he->sym->start,
- he->sym->name,
- (void *)(unsigned long)ip, ip - he->sym->start,
- h->ip[offset]);
-}
-
-static int perf_session__add_hist_entry(struct perf_session *self,
- struct addr_location *al, u64 count)
-{
- bool hit;
- struct hist_entry *he = __perf_session__add_hist_entry(self, al, NULL,
- count, &hit);
+ he = __hists__add_entry(&evsel->hists, al, NULL, 1, 1);
if (he == NULL)
return -ENOMEM;
- hist_hit(he, al->addr);
- return 0;
-}
-
-static int process_sample_event(event_t *event, struct perf_session *session)
-{
- struct addr_location al;
- dump_printf("(IP, %d): %d: %p\n", event->header.misc,
- event->ip.pid, (void *)(long)event->ip.ip);
+ ret = 0;
+ if (he->ms.sym != NULL) {
+ struct annotation *notes = symbol__annotation(he->ms.sym);
+ if (notes->src == NULL && symbol__alloc_hist(he->ms.sym) < 0)
+ return -ENOMEM;
- if (event__preprocess_sample(event, session, &al, symbol_filter) < 0) {
- fprintf(stderr, "problem processing %d event, skipping it.\n",
- event->header.type);
- return -1;
+ ret = hist_entry__inc_addr_samples(he, evsel->idx, al->addr);
}
- if (!al.filtered && perf_session__add_hist_entry(session, &al, 1)) {
- fprintf(stderr, "problem incrementing symbol count, "
- "skipping event\n");
- return -1;
- }
-
- return 0;
+ evsel->hists.stats.total_period += sample->period;
+ hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE);
+ return ret;
}
-static int parse_line(FILE *file, struct hist_entry *he, u64 len)
+static int process_sample_event(struct perf_tool *tool,
+ union perf_event *event,
+ struct perf_sample *sample,
+ struct perf_evsel *evsel,
+ struct machine *machine)
{
- struct symbol *sym = he->sym;
- char *line = NULL, *tmp, *tmp2;
- static const char *prev_line;
- static const char *prev_color;
- unsigned int offset;
- size_t line_len;
- u64 start;
- s64 line_ip;
- int ret;
- char *c;
+ struct perf_annotate *ann = container_of(tool, struct perf_annotate, tool);
+ struct addr_location al;
- if (getline(&line, &line_len, file) < 0)
- return -1;
- if (!line)
+ if (perf_event__preprocess_sample(event, machine, &al, sample) < 0) {
+ pr_warning("problem processing %d event, skipping it.\n",
+ event->header.type);
return -1;
-
- c = strchr(line, '\n');
- if (c)
- *c = 0;
-
- line_ip = -1;
- offset = 0;
- ret = -2;
-
- /*
- * Strip leading spaces:
- */
- tmp = line;
- while (*tmp) {
- if (*tmp != ' ')
- break;
- tmp++;
}
- if (*tmp) {
- /*
- * Parse hexa addresses followed by ':'
- */
- line_ip = strtoull(tmp, &tmp2, 16);
- if (*tmp2 != ':')
- line_ip = -1;
- }
-
- start = he->map->unmap_ip(he->map, sym->start);
-
- if (line_ip != -1) {
- const char *path = NULL;
- unsigned int hits = 0;
- double percent = 0.0;
- const char *color;
- struct sym_priv *priv = symbol__priv(sym);
- struct sym_ext *sym_ext = priv->ext;
- struct sym_hist *h = priv->hist;
-
- offset = line_ip - start;
- if (offset < len)
- hits = h->ip[offset];
-
- if (offset < len && sym_ext) {
- path = sym_ext[offset].path;
- percent = sym_ext[offset].percent;
- } else if (h->sum)
- percent = 100.0 * hits / h->sum;
-
- color = get_percent_color(percent);
-
- /*
- * Also color the filename and line if needed, with
- * the same color than the percentage. Don't print it
- * twice for close colored ip with the same filename:line
- */
- if (path) {
- if (!prev_line || strcmp(prev_line, path)
- || color != prev_color) {
- color_fprintf(stdout, color, " %s", path);
- prev_line = path;
- prev_color = color;
- }
- }
+ if (ann->cpu_list && !test_bit(sample->cpu, ann->cpu_bitmap))
+ return 0;
- color_fprintf(stdout, color, " %7.2f", percent);
- printf(" : ");
- color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", line);
- } else {
- if (!*line)
- printf(" :\n");
- else
- printf(" : %s\n", line);
+ if (!al.filtered && perf_evsel__add_sample(evsel, sample, &al, ann)) {
+ pr_warning("problem incrementing symbol count, "
+ "skipping event\n");
+ return -1;
}
return 0;
}
-static struct rb_root root_sym_ext;
-
-static void insert_source_line(struct sym_ext *sym_ext)
+static int hist_entry__tty_annotate(struct hist_entry *he,
+ struct perf_evsel *evsel,
+ struct perf_annotate *ann)
{
- struct sym_ext *iter;
- struct rb_node **p = &root_sym_ext.rb_node;
- struct rb_node *parent = NULL;
-
- while (*p != NULL) {
- parent = *p;
- iter = rb_entry(parent, struct sym_ext, node);
-
- if (sym_ext->percent > iter->percent)
- p = &(*p)->rb_left;
- else
- p = &(*p)->rb_right;
- }
-
- rb_link_node(&sym_ext->node, parent, p);
- rb_insert_color(&sym_ext->node, &root_sym_ext);
+ return symbol__tty_annotate(he->ms.sym, he->ms.map, evsel,
+ ann->print_line, ann->full_paths, 0, 0);
}
-static void free_source_line(struct hist_entry *he, int len)
+static void hists__find_annotations(struct hists *self,
+ struct perf_evsel *evsel,
+ struct perf_annotate *ann)
{
- struct sym_priv *priv = symbol__priv(he->sym);
- struct sym_ext *sym_ext = priv->ext;
- int i;
-
- if (!sym_ext)
- return;
+ struct rb_node *nd = rb_first(&self->entries), *next;
+ int key = K_RIGHT;
- for (i = 0; i < len; i++)
- free(sym_ext[i].path);
- free(sym_ext);
-
- priv->ext = NULL;
- root_sym_ext = RB_ROOT;
-}
-
-/* Get the filename:line for the colored entries */
-static void
-get_source_line(struct hist_entry *he, int len, const char *filename)
-{
- struct symbol *sym = he->sym;
- u64 start;
- int i;
- char cmd[PATH_MAX * 2];
- struct sym_ext *sym_ext;
- struct sym_priv *priv = symbol__priv(sym);
- struct sym_hist *h = priv->hist;
-
- if (!h->sum)
- return;
-
- sym_ext = priv->ext = calloc(len, sizeof(struct sym_ext));
- if (!priv->ext)
- return;
-
- start = he->map->unmap_ip(he->map, sym->start);
-
- for (i = 0; i < len; i++) {
- char *path = NULL;
- size_t line_len;
- u64 offset;
- FILE *fp;
-
- sym_ext[i].percent = 100.0 * h->ip[i] / h->sum;
- if (sym_ext[i].percent <= 0.5)
- continue;
-
- offset = start + i;
- sprintf(cmd, "addr2line -e %s %016llx", filename, offset);
- fp = popen(cmd, "r");
- if (!fp)
+ while (nd) {
+ struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
+ struct annotation *notes;
+
+ if (he->ms.sym == NULL || he->ms.map->dso->annotate_warned)
+ goto find_next;
+
+ notes = symbol__annotation(he->ms.sym);
+ if (notes->src == NULL) {
+find_next:
+ if (key == K_LEFT)
+ nd = rb_prev(nd);
+ else
+ nd = rb_next(nd);
continue;
+ }
- if (getline(&path, &line_len, fp) < 0 || !line_len)
- goto next;
-
- sym_ext[i].path = malloc(sizeof(char) * line_len + 1);
- if (!sym_ext[i].path)
- goto next;
-
- strcpy(sym_ext[i].path, path);
- insert_source_line(&sym_ext[i]);
+ if (use_browser == 2) {
+ int ret;
+
+ ret = hist_entry__gtk_annotate(he, evsel, NULL);
+ if (!ret || !ann->skip_missing)
+ return;
+
+ /* skip missing symbols */
+ nd = rb_next(nd);
+ } else if (use_browser == 1) {
+ key = hist_entry__tui_annotate(he, evsel, NULL);
+ switch (key) {
+ case -1:
+ if (!ann->skip_missing)
+ return;
+ /* fall through */
+ case K_RIGHT:
+ next = rb_next(nd);
+ break;
+ case K_LEFT:
+ next = rb_prev(nd);
+ break;
+ default:
+ return;
+ }
- next:
- pclose(fp);
+ if (next != NULL)
+ nd = next;
+ } else {
+ hist_entry__tty_annotate(he, evsel, ann);
+ nd = rb_next(nd);
+ /*
+ * Since we have a hist_entry per IP for the same
+ * symbol, free he->ms.sym->src to signal we already
+ * processed this symbol.
+ */
+ free(notes->src);
+ notes->src = NULL;
+ }
}
}
-static void print_summary(const char *filename)
+static int __cmd_annotate(struct perf_annotate *ann)
{
- struct sym_ext *sym_ext;
- struct rb_node *node;
-
- printf("\nSorted summary for file %s\n", filename);
- printf("----------------------------------------------\n\n");
-
- if (RB_EMPTY_ROOT(&root_sym_ext)) {
- printf(" Nothing higher than %1.1f%%\n", MIN_GREEN);
- return;
- }
+ int ret;
+ struct perf_session *session;
+ struct perf_evsel *pos;
+ u64 total_nr_samples;
- node = rb_first(&root_sym_ext);
- while (node) {
- double percent;
- const char *color;
- char *path;
+ session = perf_session__new(input_name, O_RDONLY,
+ ann->force, false, &ann->tool);
+ if (session == NULL)
+ return -ENOMEM;
- sym_ext = rb_entry(node, struct sym_ext, node);
- percent = sym_ext->percent;
- color = get_percent_color(percent);
- path = sym_ext->path;
+ machines__set_symbol_filter(&session->machines, symbol__annotate_init);
- color_fprintf(stdout, color, " %7.2f %s", percent, path);
- node = rb_next(node);
+ if (ann->cpu_list) {
+ ret = perf_session__cpu_bitmap(session, ann->cpu_list,
+ ann->cpu_bitmap);
+ if (ret)
+ goto out_delete;
}
-}
-static void annotate_sym(struct hist_entry *he)
-{
- struct map *map = he->map;
- struct dso *dso = map->dso;
- struct symbol *sym = he->sym;
- const char *filename = dso->long_name, *d_filename;
- u64 len;
- char command[PATH_MAX*2];
- FILE *file;
-
- if (!filename)
- return;
-
- if (verbose)
- fprintf(stderr, "%s: filename=%s, sym=%s, start=%Lx, end=%Lx\n",
- __func__, filename, sym->name,
- map->unmap_ip(map, sym->start),
- map->unmap_ip(map, sym->end));
-
- if (full_paths)
- d_filename = filename;
- else
- d_filename = basename(filename);
-
- len = sym->end - sym->start;
-
- if (print_line) {
- get_source_line(he, len, filename);
- print_summary(filename);
+ if (!objdump_path) {
+ ret = perf_session_env__lookup_objdump(&session->header.env);
+ if (ret)
+ goto out_delete;
}
- printf("\n\n------------------------------------------------\n");
- printf(" Percent | Source code & Disassembly of %s\n", d_filename);
- printf("------------------------------------------------\n");
-
- if (verbose >= 2)
- printf("annotating [%p] %30s : [%p] %30s\n",
- dso, dso->long_name, sym, sym->name);
-
- sprintf(command, "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s|grep -v %s",
- map->unmap_ip(map, sym->start), map->unmap_ip(map, sym->end),
- filename, filename);
-
- if (verbose >= 3)
- printf("doing: %s\n", command);
-
- file = popen(command, "r");
- if (!file)
- return;
+ ret = perf_session__process_events(session, &ann->tool);
+ if (ret)
+ goto out_delete;
- while (!feof(file)) {
- if (parse_line(file, he, len) < 0)
- break;
+ if (dump_trace) {
+ perf_session__fprintf_nr_events(session, stdout);
+ goto out_delete;
}
- pclose(file);
- if (print_line)
- free_source_line(he, len);
-}
+ if (verbose > 3)
+ perf_session__fprintf(session, stdout);
-static void perf_session__find_annotations(struct perf_session *self)
-{
- struct rb_node *nd;
+ if (verbose > 2)
+ perf_session__fprintf_dsos(session, stdout);
- for (nd = rb_first(&self->hists); nd; nd = rb_next(nd)) {
- struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
- struct sym_priv *priv;
+ total_nr_samples = 0;
+ list_for_each_entry(pos, &session->evlist->entries, node) {
+ struct hists *hists = &pos->hists;
+ u32 nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
- if (he->sym == NULL)
- continue;
+ if (nr_samples > 0) {
+ total_nr_samples += nr_samples;
+ hists__collapse_resort(hists);
+ hists__output_resort(hists);
- priv = symbol__priv(he->sym);
- if (priv->hist == NULL)
- continue;
+ if (symbol_conf.event_group &&
+ !perf_evsel__is_group_leader(pos))
+ continue;
- annotate_sym(he);
- /*
- * Since we have a hist_entry per IP for the same symbol, free
- * he->sym->hist to signal we already processed this symbol.
- */
- free(priv->hist);
- priv->hist = NULL;
+ hists__find_annotations(hists, pos, ann);
+ }
}
-}
-
-static struct perf_event_ops event_ops = {
- .process_sample_event = process_sample_event,
- .process_mmap_event = event__process_mmap,
- .process_comm_event = event__process_comm,
- .process_fork_event = event__process_task,
-};
-
-static int __cmd_annotate(void)
-{
- int ret;
- struct perf_session *session;
-
- session = perf_session__new(input_name, O_RDONLY, force);
- if (session == NULL)
- return -ENOMEM;
-
- ret = perf_session__process_events(session, &event_ops);
- if (ret)
- goto out_delete;
- if (dump_trace) {
- event__print_totals();
+ if (total_nr_samples == 0) {
+ ui__error("The %s file has no samples!\n", session->filename);
goto out_delete;
}
- if (verbose > 3)
- perf_session__fprintf(session, stdout);
+ if (use_browser == 2)
+ perf_gtk__show_annotations();
- if (verbose > 2)
- dsos__fprintf(stdout);
-
- perf_session__collapse_resort(session);
- perf_session__output_resort(session, session->event_total[0]);
- perf_session__find_annotations(session);
out_delete:
- perf_session__delete(session);
-
+ /*
+ * Speed up the exit process, for large files this can
+ * take quite a while.
+ *
+ * XXX Enable this when using valgrind or if we ever
+ * librarize this command.
+ *
+ * Also experiment with obstacks to see how much speed
+ * up we'll get here.
+ *
+ * perf_session__delete(session);
+ */
return ret;
}
static const char * const annotate_usage[] = {
- "perf annotate [<options>] <command>",
+ "perf annotate [<options>]",
NULL
};
-static const struct option options[] = {
+int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused)
+{
+ struct perf_annotate annotate = {
+ .tool = {
+ .sample = process_sample_event,
+ .mmap = perf_event__process_mmap,
+ .mmap2 = perf_event__process_mmap2,
+ .comm = perf_event__process_comm,
+ .exit = perf_event__process_exit,
+ .fork = perf_event__process_fork,
+ .ordered_samples = true,
+ .ordering_requires_timestamps = true,
+ },
+ };
+ const struct option options[] = {
OPT_STRING('i', "input", &input_name, "file",
"input file name"),
- OPT_STRING('s', "symbol", &sym_hist_filter, "symbol",
+ OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]",
+ "only consider symbols in these dsos"),
+ OPT_STRING('s', "symbol", &annotate.sym_hist_filter, "symbol",
"symbol to annotate"),
- OPT_BOOLEAN('f', "force", &force, "don't complain, do it"),
- OPT_BOOLEAN('v', "verbose", &verbose,
+ OPT_BOOLEAN('f', "force", &annotate.force, "don't complain, do it"),
+ OPT_INCR('v', "verbose", &verbose,
"be more verbose (show symbol address, etc)"),
OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
"dump raw trace in ASCII"),
+ OPT_BOOLEAN(0, "gtk", &annotate.use_gtk, "Use the GTK interface"),
+ OPT_BOOLEAN(0, "tui", &annotate.use_tui, "Use the TUI interface"),
+ OPT_BOOLEAN(0, "stdio", &annotate.use_stdio, "Use the stdio interface"),
OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
"file", "vmlinux pathname"),
OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules,
"load module symbols - WARNING: use only with -k and LIVE kernel"),
- OPT_BOOLEAN('l', "print-line", &print_line,
+ OPT_BOOLEAN('l', "print-line", &annotate.print_line,
"print matching source lines (may be slow)"),
- OPT_BOOLEAN('P', "full-paths", &full_paths,
+ OPT_BOOLEAN('P', "full-paths", &annotate.full_paths,
"Don't shorten the displayed pathnames"),
+ OPT_BOOLEAN(0, "skip-missing", &annotate.skip_missing,
+ "Skip symbols that cannot be annotated"),
+ OPT_STRING('C', "cpu", &annotate.cpu_list, "cpu", "list of cpus to profile"),
+ OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
+ "Look for files with symbols relative to this directory"),
+ OPT_BOOLEAN(0, "source", &symbol_conf.annotate_src,
+ "Interleave source code with assembly code (default)"),
+ OPT_BOOLEAN(0, "asm-raw", &symbol_conf.annotate_asm_raw,
+ "Display raw encoding of assembly instructions (default)"),
+ OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style",
+ "Specify disassembler style (e.g. -M intel for intel syntax)"),
+ OPT_STRING(0, "objdump", &objdump_path, "path",
+ "objdump binary to use for disassembly and annotations"),
+ OPT_BOOLEAN(0, "group", &symbol_conf.event_group,
+ "Show event group information together"),
OPT_END()
-};
+ };
-int cmd_annotate(int argc, const char **argv, const char *prefix __used)
-{
argc = parse_options(argc, argv, options, annotate_usage, 0);
- symbol_conf.priv_size = sizeof(struct sym_priv);
+ if (annotate.use_stdio)
+ use_browser = 0;
+ else if (annotate.use_tui)
+ use_browser = 1;
+ else if (annotate.use_gtk)
+ use_browser = 2;
+
+ setup_browser(true);
+
+ symbol_conf.priv_size = sizeof(struct annotation);
symbol_conf.try_vmlinux_path = true;
if (symbol__init() < 0)
return -1;
- setup_sorting(annotate_usage, options);
+ if (setup_sorting() < 0)
+ usage_with_options(annotate_usage, options);
if (argc) {
/*
@@ -536,16 +354,8 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __used)
if (argc > 1)
usage_with_options(annotate_usage, options);
- sym_hist_filter = argv[0];
- }
-
- setup_pager();
-
- if (field_sep && *field_sep == '.') {
- fputs("'.' is the only non valid --field-separator argument\n",
- stderr);
- exit(129);
+ annotate.sym_hist_filter = argv[0];
}
- return __cmd_annotate();
+ return __cmd_annotate(&annotate);
}