aboutsummaryrefslogtreecommitdiffstats
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.c352
1 files changed, 138 insertions, 214 deletions
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c
index 593ff25006de..96db5248e995 100644
--- a/tools/perf/builtin-annotate.c
+++ b/tools/perf/builtin-annotate.c
@@ -14,7 +14,6 @@
14#include "util/cache.h" 14#include "util/cache.h"
15#include <linux/rbtree.h> 15#include <linux/rbtree.h>
16#include "util/symbol.h" 16#include "util/symbol.h"
17#include "util/string.h"
18 17
19#include "perf.h" 18#include "perf.h"
20#include "util/debug.h" 19#include "util/debug.h"
@@ -29,175 +28,67 @@
29 28
30static char const *input_name = "perf.data"; 29static char const *input_name = "perf.data";
31 30
32static int force; 31static bool force;
33 32
34static int full_paths; 33static bool full_paths;
35 34
36static int print_line; 35static bool print_line;
37
38struct sym_hist {
39 u64 sum;
40 u64 ip[0];
41};
42
43struct sym_ext {
44 struct rb_node node;
45 double percent;
46 char *path;
47};
48
49struct sym_priv {
50 struct sym_hist *hist;
51 struct sym_ext *ext;
52};
53 36
54static const char *sym_hist_filter; 37static const char *sym_hist_filter;
55 38
56static int symbol_filter(struct map *map __used, struct symbol *sym) 39static int hists__add_entry(struct hists *self, struct addr_location *al)
57{ 40{
58 if (sym_hist_filter == NULL || 41 struct hist_entry *he;
59 strcmp(sym->name, sym_hist_filter) == 0) { 42
60 struct sym_priv *priv = symbol__priv(sym); 43 if (sym_hist_filter != NULL &&
61 const int size = (sizeof(*priv->hist) + 44 (al->sym == NULL || strcmp(sym_hist_filter, al->sym->name) != 0)) {
62 (sym->end - sym->start) * sizeof(u64)); 45 /* We're only interested in a symbol named sym_hist_filter */
63 46 if (al->sym != NULL) {
64 priv->hist = malloc(size); 47 rb_erase(&al->sym->rb_node,
65 if (priv->hist) 48 &al->map->dso->symbols[al->map->type]);
66 memset(priv->hist, 0, size); 49 symbol__delete(al->sym);
50 }
67 return 0; 51 return 0;
68 } 52 }
69 /*
70 * FIXME: We should really filter it out, as we don't want to go thru symbols
71 * we're not interested, and if a DSO ends up with no symbols, delete it too,
72 * but right now the kernel loading routines in symbol.c bail out if no symbols
73 * are found, fix it later.
74 */
75 return 0;
76}
77
78/*
79 * collect histogram counts
80 */
81static void hist_hit(struct hist_entry *he, u64 ip)
82{
83 unsigned int sym_size, offset;
84 struct symbol *sym = he->sym;
85 struct sym_priv *priv;
86 struct sym_hist *h;
87
88 he->count++;
89
90 if (!sym || !he->map)
91 return;
92 53
93 priv = symbol__priv(sym); 54 he = __hists__add_entry(self, al, NULL, 1);
94 if (!priv->hist)
95 return;
96
97 sym_size = sym->end - sym->start;
98 offset = ip - sym->start;
99
100 if (verbose)
101 fprintf(stderr, "%s: ip=%Lx\n", __func__,
102 he->map->unmap_ip(he->map, ip));
103
104 if (offset >= sym_size)
105 return;
106
107 h = priv->hist;
108 h->sum++;
109 h->ip[offset]++;
110
111 if (verbose >= 3)
112 printf("%p %s: count++ [ip: %p, %08Lx] => %Ld\n",
113 (void *)(unsigned long)he->sym->start,
114 he->sym->name,
115 (void *)(unsigned long)ip, ip - he->sym->start,
116 h->ip[offset]);
117}
118
119static int perf_session__add_hist_entry(struct perf_session *self,
120 struct addr_location *al, u64 count)
121{
122 bool hit;
123 struct hist_entry *he = __perf_session__add_hist_entry(self, al, NULL,
124 count, &hit);
125 if (he == NULL) 55 if (he == NULL)
126 return -ENOMEM; 56 return -ENOMEM;
127 hist_hit(he, al->addr); 57
128 return 0; 58 return hist_entry__inc_addr_samples(he, al->addr);
129} 59}
130 60
131static int process_sample_event(event_t *event, struct perf_session *session) 61static int process_sample_event(event_t *event, struct perf_session *session)
132{ 62{
133 struct addr_location al; 63 struct addr_location al;
134 64
135 dump_printf("(IP, %d): %d: %p\n", event->header.misc, 65 dump_printf("(IP, %d): %d: %#Lx\n", event->header.misc,
136 event->ip.pid, (void *)(long)event->ip.ip); 66 event->ip.pid, event->ip.ip);
137 67
138 if (event__preprocess_sample(event, session, &al, symbol_filter) < 0) { 68 if (event__preprocess_sample(event, session, &al, NULL) < 0) {
139 fprintf(stderr, "problem processing %d event, skipping it.\n", 69 pr_warning("problem processing %d event, skipping it.\n",
140 event->header.type); 70 event->header.type);
141 return -1; 71 return -1;
142 } 72 }
143 73
144 if (!al.filtered && perf_session__add_hist_entry(session, &al, 1)) { 74 if (!al.filtered && hists__add_entry(&session->hists, &al)) {
145 fprintf(stderr, "problem incrementing symbol count, " 75 pr_warning("problem incrementing symbol count, "
146 "skipping event\n"); 76 "skipping event\n");
147 return -1; 77 return -1;
148 } 78 }
149 79
150 return 0; 80 return 0;
151} 81}
152 82
153static int parse_line(FILE *file, struct hist_entry *he, u64 len) 83static int objdump_line__print(struct objdump_line *self,
84 struct list_head *head,
85 struct hist_entry *he, u64 len)
154{ 86{
155 struct symbol *sym = he->sym; 87 struct symbol *sym = he->ms.sym;
156 char *line = NULL, *tmp, *tmp2;
157 static const char *prev_line; 88 static const char *prev_line;
158 static const char *prev_color; 89 static const char *prev_color;
159 unsigned int offset;
160 size_t line_len;
161 u64 start;
162 s64 line_ip;
163 int ret;
164 char *c;
165
166 if (getline(&line, &line_len, file) < 0)
167 return -1;
168 if (!line)
169 return -1;
170 90
171 c = strchr(line, '\n'); 91 if (self->offset != -1) {
172 if (c)
173 *c = 0;
174
175 line_ip = -1;
176 offset = 0;
177 ret = -2;
178
179 /*
180 * Strip leading spaces:
181 */
182 tmp = line;
183 while (*tmp) {
184 if (*tmp != ' ')
185 break;
186 tmp++;
187 }
188
189 if (*tmp) {
190 /*
191 * Parse hexa addresses followed by ':'
192 */
193 line_ip = strtoull(tmp, &tmp2, 16);
194 if (*tmp2 != ':')
195 line_ip = -1;
196 }
197
198 start = he->map->unmap_ip(he->map, sym->start);
199
200 if (line_ip != -1) {
201 const char *path = NULL; 92 const char *path = NULL;
202 unsigned int hits = 0; 93 unsigned int hits = 0;
203 double percent = 0.0; 94 double percent = 0.0;
@@ -205,15 +96,22 @@ static int parse_line(FILE *file, struct hist_entry *he, u64 len)
205 struct sym_priv *priv = symbol__priv(sym); 96 struct sym_priv *priv = symbol__priv(sym);
206 struct sym_ext *sym_ext = priv->ext; 97 struct sym_ext *sym_ext = priv->ext;
207 struct sym_hist *h = priv->hist; 98 struct sym_hist *h = priv->hist;
99 s64 offset = self->offset;
100 struct objdump_line *next = objdump__get_next_ip_line(head, self);
101
102 while (offset < (s64)len &&
103 (next == NULL || offset < next->offset)) {
104 if (sym_ext) {
105 if (path == NULL)
106 path = sym_ext[offset].path;
107 percent += sym_ext[offset].percent;
108 } else
109 hits += h->ip[offset];
110
111 ++offset;
112 }
208 113
209 offset = line_ip - start; 114 if (sym_ext == NULL && h->sum)
210 if (offset < len)
211 hits = h->ip[offset];
212
213 if (offset < len && sym_ext) {
214 path = sym_ext[offset].path;
215 percent = sym_ext[offset].percent;
216 } else if (h->sum)
217 percent = 100.0 * hits / h->sum; 115 percent = 100.0 * hits / h->sum;
218 116
219 color = get_percent_color(percent); 117 color = get_percent_color(percent);
@@ -234,12 +132,12 @@ static int parse_line(FILE *file, struct hist_entry *he, u64 len)
234 132
235 color_fprintf(stdout, color, " %7.2f", percent); 133 color_fprintf(stdout, color, " %7.2f", percent);
236 printf(" : "); 134 printf(" : ");
237 color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", line); 135 color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", self->line);
238 } else { 136 } else {
239 if (!*line) 137 if (!*self->line)
240 printf(" :\n"); 138 printf(" :\n");
241 else 139 else
242 printf(" : %s\n", line); 140 printf(" : %s\n", self->line);
243 } 141 }
244 142
245 return 0; 143 return 0;
@@ -269,7 +167,7 @@ static void insert_source_line(struct sym_ext *sym_ext)
269 167
270static void free_source_line(struct hist_entry *he, int len) 168static void free_source_line(struct hist_entry *he, int len)
271{ 169{
272 struct sym_priv *priv = symbol__priv(he->sym); 170 struct sym_priv *priv = symbol__priv(he->ms.sym);
273 struct sym_ext *sym_ext = priv->ext; 171 struct sym_ext *sym_ext = priv->ext;
274 int i; 172 int i;
275 173
@@ -288,7 +186,7 @@ static void free_source_line(struct hist_entry *he, int len)
288static void 186static void
289get_source_line(struct hist_entry *he, int len, const char *filename) 187get_source_line(struct hist_entry *he, int len, const char *filename)
290{ 188{
291 struct symbol *sym = he->sym; 189 struct symbol *sym = he->ms.sym;
292 u64 start; 190 u64 start;
293 int i; 191 int i;
294 char cmd[PATH_MAX * 2]; 192 char cmd[PATH_MAX * 2];
@@ -303,7 +201,7 @@ get_source_line(struct hist_entry *he, int len, const char *filename)
303 if (!priv->ext) 201 if (!priv->ext)
304 return; 202 return;
305 203
306 start = he->map->unmap_ip(he->map, sym->start); 204 start = he->ms.map->unmap_ip(he->ms.map, sym->start);
307 205
308 for (i = 0; i < len; i++) { 206 for (i = 0; i < len; i++) {
309 char *path = NULL; 207 char *path = NULL;
@@ -365,24 +263,32 @@ static void print_summary(const char *filename)
365 } 263 }
366} 264}
367 265
368static void annotate_sym(struct hist_entry *he) 266static void hist_entry__print_hits(struct hist_entry *self)
369{ 267{
370 struct map *map = he->map; 268 struct symbol *sym = self->ms.sym;
269 struct sym_priv *priv = symbol__priv(sym);
270 struct sym_hist *h = priv->hist;
271 u64 len = sym->end - sym->start, offset;
272
273 for (offset = 0; offset < len; ++offset)
274 if (h->ip[offset] != 0)
275 printf("%*Lx: %Lu\n", BITS_PER_LONG / 2,
276 sym->start + offset, h->ip[offset]);
277 printf("%*s: %Lu\n", BITS_PER_LONG / 2, "h->sum", h->sum);
278}
279
280static int hist_entry__tty_annotate(struct hist_entry *he)
281{
282 struct map *map = he->ms.map;
371 struct dso *dso = map->dso; 283 struct dso *dso = map->dso;
372 struct symbol *sym = he->sym; 284 struct symbol *sym = he->ms.sym;
373 const char *filename = dso->long_name, *d_filename; 285 const char *filename = dso->long_name, *d_filename;
374 u64 len; 286 u64 len;
375 char command[PATH_MAX*2]; 287 LIST_HEAD(head);
376 FILE *file; 288 struct objdump_line *pos, *n;
377
378 if (!filename)
379 return;
380 289
381 if (verbose) 290 if (hist_entry__annotate(he, &head) < 0)
382 fprintf(stderr, "%s: filename=%s, sym=%s, start=%Lx, end=%Lx\n", 291 return -1;
383 __func__, filename, sym->name,
384 map->unmap_ip(map, sym->start),
385 map->unmap_ip(map, sym->end));
386 292
387 if (full_paths) 293 if (full_paths)
388 d_filename = filename; 294 d_filename = filename;
@@ -400,61 +306,78 @@ static void annotate_sym(struct hist_entry *he)
400 printf(" Percent | Source code & Disassembly of %s\n", d_filename); 306 printf(" Percent | Source code & Disassembly of %s\n", d_filename);
401 printf("------------------------------------------------\n"); 307 printf("------------------------------------------------\n");
402 308
403 if (verbose >= 2) 309 if (verbose)
404 printf("annotating [%p] %30s : [%p] %30s\n", 310 hist_entry__print_hits(he);
405 dso, dso->long_name, sym, sym->name);
406
407 sprintf(command, "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s|grep -v %s",
408 map->unmap_ip(map, sym->start), map->unmap_ip(map, sym->end),
409 filename, filename);
410
411 if (verbose >= 3)
412 printf("doing: %s\n", command);
413
414 file = popen(command, "r");
415 if (!file)
416 return;
417 311
418 while (!feof(file)) { 312 list_for_each_entry_safe(pos, n, &head, node) {
419 if (parse_line(file, he, len) < 0) 313 objdump_line__print(pos, &head, he, len);
420 break; 314 list_del(&pos->node);
315 objdump_line__free(pos);
421 } 316 }
422 317
423 pclose(file);
424 if (print_line) 318 if (print_line)
425 free_source_line(he, len); 319 free_source_line(he, len);
320
321 return 0;
426} 322}
427 323
428static void perf_session__find_annotations(struct perf_session *self) 324static void hists__find_annotations(struct hists *self)
429{ 325{
430 struct rb_node *nd; 326 struct rb_node *first = rb_first(&self->entries), *nd = first;
327 int key = KEY_RIGHT;
431 328
432 for (nd = rb_first(&self->hists); nd; nd = rb_next(nd)) { 329 while (nd) {
433 struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node); 330 struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
434 struct sym_priv *priv; 331 struct sym_priv *priv;
435 332
436 if (he->sym == NULL) 333 if (he->ms.sym == NULL || he->ms.map->dso->annotate_warned)
437 continue; 334 goto find_next;
438 335
439 priv = symbol__priv(he->sym); 336 priv = symbol__priv(he->ms.sym);
440 if (priv->hist == NULL) 337 if (priv->hist == NULL) {
338find_next:
339 if (key == KEY_LEFT)
340 nd = rb_prev(nd);
341 else
342 nd = rb_next(nd);
441 continue; 343 continue;
344 }
442 345
443 annotate_sym(he); 346 if (use_browser > 0) {
444 /* 347 key = hist_entry__tui_annotate(he);
445 * Since we have a hist_entry per IP for the same symbol, free 348 if (is_exit_key(key))
446 * he->sym->hist to signal we already processed this symbol. 349 break;
447 */ 350 switch (key) {
448 free(priv->hist); 351 case KEY_RIGHT:
449 priv->hist = NULL; 352 case '\t':
353 nd = rb_next(nd);
354 break;
355 case KEY_LEFT:
356 if (nd == first)
357 continue;
358 nd = rb_prev(nd);
359 default:
360 break;
361 }
362 } else {
363 hist_entry__tty_annotate(he);
364 nd = rb_next(nd);
365 /*
366 * Since we have a hist_entry per IP for the same
367 * symbol, free he->ms.sym->hist to signal we already
368 * processed this symbol.
369 */
370 free(priv->hist);
371 priv->hist = NULL;
372 }
450 } 373 }
451} 374}
452 375
453static struct perf_event_ops event_ops = { 376static struct perf_event_ops event_ops = {
454 .process_sample_event = process_sample_event, 377 .sample = process_sample_event,
455 .process_mmap_event = event__process_mmap, 378 .mmap = event__process_mmap,
456 .process_comm_event = event__process_comm, 379 .comm = event__process_comm,
457 .process_fork_event = event__process_task, 380 .fork = event__process_task,
458}; 381};
459 382
460static int __cmd_annotate(void) 383static int __cmd_annotate(void)
@@ -462,7 +385,7 @@ static int __cmd_annotate(void)
462 int ret; 385 int ret;
463 struct perf_session *session; 386 struct perf_session *session;
464 387
465 session = perf_session__new(input_name, O_RDONLY, force); 388 session = perf_session__new(input_name, O_RDONLY, force, false);
466 if (session == NULL) 389 if (session == NULL)
467 return -ENOMEM; 390 return -ENOMEM;
468 391
@@ -471,7 +394,7 @@ static int __cmd_annotate(void)
471 goto out_delete; 394 goto out_delete;
472 395
473 if (dump_trace) { 396 if (dump_trace) {
474 event__print_totals(); 397 perf_session__fprintf_nr_events(session, stdout);
475 goto out_delete; 398 goto out_delete;
476 } 399 }
477 400
@@ -479,11 +402,11 @@ static int __cmd_annotate(void)
479 perf_session__fprintf(session, stdout); 402 perf_session__fprintf(session, stdout);
480 403
481 if (verbose > 2) 404 if (verbose > 2)
482 dsos__fprintf(stdout); 405 perf_session__fprintf_dsos(session, stdout);
483 406
484 perf_session__collapse_resort(session); 407 hists__collapse_resort(&session->hists);
485 perf_session__output_resort(session, session->event_total[0]); 408 hists__output_resort(&session->hists);
486 perf_session__find_annotations(session); 409 hists__find_annotations(&session->hists);
487out_delete: 410out_delete:
488 perf_session__delete(session); 411 perf_session__delete(session);
489 412
@@ -498,10 +421,12 @@ static const char * const annotate_usage[] = {
498static const struct option options[] = { 421static const struct option options[] = {
499 OPT_STRING('i', "input", &input_name, "file", 422 OPT_STRING('i', "input", &input_name, "file",
500 "input file name"), 423 "input file name"),
424 OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]",
425 "only consider symbols in these dsos"),
501 OPT_STRING('s', "symbol", &sym_hist_filter, "symbol", 426 OPT_STRING('s', "symbol", &sym_hist_filter, "symbol",
502 "symbol to annotate"), 427 "symbol to annotate"),
503 OPT_BOOLEAN('f', "force", &force, "don't complain, do it"), 428 OPT_BOOLEAN('f', "force", &force, "don't complain, do it"),
504 OPT_BOOLEAN('v', "verbose", &verbose, 429 OPT_INCR('v', "verbose", &verbose,
505 "be more verbose (show symbol address, etc)"), 430 "be more verbose (show symbol address, etc)"),
506 OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, 431 OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
507 "dump raw trace in ASCII"), 432 "dump raw trace in ASCII"),
@@ -520,6 +445,8 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __used)
520{ 445{
521 argc = parse_options(argc, argv, options, annotate_usage, 0); 446 argc = parse_options(argc, argv, options, annotate_usage, 0);
522 447
448 setup_browser();
449
523 symbol_conf.priv_size = sizeof(struct sym_priv); 450 symbol_conf.priv_size = sizeof(struct sym_priv);
524 symbol_conf.try_vmlinux_path = true; 451 symbol_conf.try_vmlinux_path = true;
525 452
@@ -539,12 +466,9 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __used)
539 sym_hist_filter = argv[0]; 466 sym_hist_filter = argv[0];
540 } 467 }
541 468
542 setup_pager();
543
544 if (field_sep && *field_sep == '.') { 469 if (field_sep && *field_sep == '.') {
545 fputs("'.' is the only non valid --field-separator argument\n", 470 pr_err("'.' is the only non valid --field-separator argument\n");
546 stderr); 471 return -1;
547 exit(129);
548 } 472 }
549 473
550 return __cmd_annotate(); 474 return __cmd_annotate();