1 /* 2 * builtin-annotate.c 3 * 4 * Builtin annotate command: Analyze the perf.data input file, 5 * look up and read DSOs and symbol information and display 6 * a histogram of results, along various sorting keys. 7 */ 8 #include "builtin.h" 9 10 #include "util/util.h" 11 12 #include "util/color.h" 13 #include <linux/list.h> 14 #include "util/cache.h" 15 #include <linux/rbtree.h> 16 #include "util/symbol.h" 17 #include "util/string.h" 18 19 #include "perf.h" 20 #include "util/debug.h" 21 22 #include "util/event.h" 23 #include "util/parse-options.h" 24 #include "util/parse-events.h" 25 #include "util/thread.h" 26 #include "util/sort.h" 27 #include "util/hist.h" 28 #include "util/session.h" 29 30 static char const *input_name = "perf.data"; 31 32 static int force; 33 34 static int full_paths; 35 36 static int print_line; 37 38 struct sym_hist { 39 u64 sum; 40 u64 ip[0]; 41 }; 42 43 struct sym_ext { 44 struct rb_node node; 45 double percent; 46 char *path; 47 }; 48 49 struct sym_priv { 50 struct sym_hist *hist; 51 struct sym_ext *ext; 52 }; 53 54 static const char *sym_hist_filter; 55 56 static int symbol_filter(struct map *map __used, struct symbol *sym) 57 { 58 if (sym_hist_filter == NULL || 59 strcmp(sym->name, sym_hist_filter) == 0) { 60 struct sym_priv *priv = symbol__priv(sym); 61 const int size = (sizeof(*priv->hist) + 62 (sym->end - sym->start) * sizeof(u64)); 63 64 priv->hist = malloc(size); 65 if (priv->hist) 66 memset(priv->hist, 0, size); 67 return 0; 68 } 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 */ 81 static 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 93 priv = symbol__priv(sym); 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 119 static 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) 126 return -ENOMEM; 127 hist_hit(he, al->addr); 128 return 0; 129 } 130 131 static int process_sample_event(event_t *event, struct perf_session *session) 132 { 133 struct addr_location al; 134 135 dump_printf("(IP, %d): %d: %p\n", event->header.misc, 136 event->ip.pid, (void *)(long)event->ip.ip); 137 138 if (event__preprocess_sample(event, session, &al, symbol_filter) < 0) { 139 fprintf(stderr, "problem processing %d event, skipping it.\n", 140 event->header.type); 141 return -1; 142 } 143 144 if (!al.filtered && perf_session__add_hist_entry(session, &al, 1)) { 145 fprintf(stderr, "problem incrementing symbol count, " 146 "skipping event\n"); 147 return -1; 148 } 149 150 return 0; 151 } 152 153 static int parse_line(FILE *file, struct hist_entry *he, u64 len) 154 { 155 struct symbol *sym = he->sym; 156 char *line = NULL, *tmp, *tmp2; 157 static const char *prev_line; 158 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 171 c = strchr(line, '\n'); 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; 202 unsigned int hits = 0; 203 double percent = 0.0; 204 const char *color; 205 struct sym_priv *priv = symbol__priv(sym); 206 struct sym_ext *sym_ext = priv->ext; 207 struct sym_hist *h = priv->hist; 208 209 offset = line_ip - start; 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; 218 219 color = get_percent_color(percent); 220 221 /* 222 * Also color the filename and line if needed, with 223 * the same color than the percentage. Don't print it 224 * twice for close colored ip with the same filename:line 225 */ 226 if (path) { 227 if (!prev_line || strcmp(prev_line, path) 228 || color != prev_color) { 229 color_fprintf(stdout, color, " %s", path); 230 prev_line = path; 231 prev_color = color; 232 } 233 } 234 235 color_fprintf(stdout, color, " %7.2f", percent); 236 printf(" : "); 237 color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", line); 238 } else { 239 if (!*line) 240 printf(" :\n"); 241 else 242 printf(" : %s\n", line); 243 } 244 245 return 0; 246 } 247 248 static struct rb_root root_sym_ext; 249 250 static void insert_source_line(struct sym_ext *sym_ext) 251 { 252 struct sym_ext *iter; 253 struct rb_node **p = &root_sym_ext.rb_node; 254 struct rb_node *parent = NULL; 255 256 while (*p != NULL) { 257 parent = *p; 258 iter = rb_entry(parent, struct sym_ext, node); 259 260 if (sym_ext->percent > iter->percent) 261 p = &(*p)->rb_left; 262 else 263 p = &(*p)->rb_right; 264 } 265 266 rb_link_node(&sym_ext->node, parent, p); 267 rb_insert_color(&sym_ext->node, &root_sym_ext); 268 } 269 270 static void free_source_line(struct hist_entry *he, int len) 271 { 272 struct sym_priv *priv = symbol__priv(he->sym); 273 struct sym_ext *sym_ext = priv->ext; 274 int i; 275 276 if (!sym_ext) 277 return; 278 279 for (i = 0; i < len; i++) 280 free(sym_ext[i].path); 281 free(sym_ext); 282 283 priv->ext = NULL; 284 root_sym_ext = RB_ROOT; 285 } 286 287 /* Get the filename:line for the colored entries */ 288 static void 289 get_source_line(struct hist_entry *he, int len, const char *filename) 290 { 291 struct symbol *sym = he->sym; 292 u64 start; 293 int i; 294 char cmd[PATH_MAX * 2]; 295 struct sym_ext *sym_ext; 296 struct sym_priv *priv = symbol__priv(sym); 297 struct sym_hist *h = priv->hist; 298 299 if (!h->sum) 300 return; 301 302 sym_ext = priv->ext = calloc(len, sizeof(struct sym_ext)); 303 if (!priv->ext) 304 return; 305 306 start = he->map->unmap_ip(he->map, sym->start); 307 308 for (i = 0; i < len; i++) { 309 char *path = NULL; 310 size_t line_len; 311 u64 offset; 312 FILE *fp; 313 314 sym_ext[i].percent = 100.0 * h->ip[i] / h->sum; 315 if (sym_ext[i].percent <= 0.5) 316 continue; 317 318 offset = start + i; 319 sprintf(cmd, "addr2line -e %s %016llx", filename, offset); 320 fp = popen(cmd, "r"); 321 if (!fp) 322 continue; 323 324 if (getline(&path, &line_len, fp) < 0 || !line_len) 325 goto next; 326 327 sym_ext[i].path = malloc(sizeof(char) * line_len + 1); 328 if (!sym_ext[i].path) 329 goto next; 330 331 strcpy(sym_ext[i].path, path); 332 insert_source_line(&sym_ext[i]); 333 334 next: 335 pclose(fp); 336 } 337 } 338 339 static void print_summary(const char *filename) 340 { 341 struct sym_ext *sym_ext; 342 struct rb_node *node; 343 344 printf("\nSorted summary for file %s\n", filename); 345 printf("----------------------------------------------\n\n"); 346 347 if (RB_EMPTY_ROOT(&root_sym_ext)) { 348 printf(" Nothing higher than %1.1f%%\n", MIN_GREEN); 349 return; 350 } 351 352 node = rb_first(&root_sym_ext); 353 while (node) { 354 double percent; 355 const char *color; 356 char *path; 357 358 sym_ext = rb_entry(node, struct sym_ext, node); 359 percent = sym_ext->percent; 360 color = get_percent_color(percent); 361 path = sym_ext->path; 362 363 color_fprintf(stdout, color, " %7.2f %s", percent, path); 364 node = rb_next(node); 365 } 366 } 367 368 static void annotate_sym(struct hist_entry *he) 369 { 370 struct map *map = he->map; 371 struct dso *dso = map->dso; 372 struct symbol *sym = he->sym; 373 const char *filename = dso->long_name, *d_filename; 374 u64 len; 375 char command[PATH_MAX*2]; 376 FILE *file; 377 378 if (!filename) 379 return; 380 381 if (verbose) 382 fprintf(stderr, "%s: filename=%s, sym=%s, start=%Lx, end=%Lx\n", 383 __func__, filename, sym->name, 384 map->unmap_ip(map, sym->start), 385 map->unmap_ip(map, sym->end)); 386 387 if (full_paths) 388 d_filename = filename; 389 else 390 d_filename = basename(filename); 391 392 len = sym->end - sym->start; 393 394 if (print_line) { 395 get_source_line(he, len, filename); 396 print_summary(filename); 397 } 398 399 printf("\n\n------------------------------------------------\n"); 400 printf(" Percent | Source code & Disassembly of %s\n", d_filename); 401 printf("------------------------------------------------\n"); 402 403 if (verbose >= 2) 404 printf("annotating [%p] %30s : [%p] %30s\n", 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 418 while (!feof(file)) { 419 if (parse_line(file, he, len) < 0) 420 break; 421 } 422 423 pclose(file); 424 if (print_line) 425 free_source_line(he, len); 426 } 427 428 static void perf_session__find_annotations(struct perf_session *self) 429 { 430 struct rb_node *nd; 431 432 for (nd = rb_first(&self->hists); nd; nd = rb_next(nd)) { 433 struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node); 434 struct sym_priv *priv; 435 436 if (he->sym == NULL) 437 continue; 438 439 priv = symbol__priv(he->sym); 440 if (priv->hist == NULL) 441 continue; 442 443 annotate_sym(he); 444 /* 445 * Since we have a hist_entry per IP for the same symbol, free 446 * he->sym->hist to signal we already processed this symbol. 447 */ 448 free(priv->hist); 449 priv->hist = NULL; 450 } 451 } 452 453 static struct perf_event_ops event_ops = { 454 .process_sample_event = process_sample_event, 455 .process_mmap_event = event__process_mmap, 456 .process_comm_event = event__process_comm, 457 .process_fork_event = event__process_task, 458 }; 459 460 static int __cmd_annotate(void) 461 { 462 int ret; 463 struct perf_session *session; 464 465 session = perf_session__new(input_name, O_RDONLY, force); 466 if (session == NULL) 467 return -ENOMEM; 468 469 ret = perf_session__process_events(session, &event_ops); 470 if (ret) 471 goto out_delete; 472 473 if (dump_trace) { 474 event__print_totals(); 475 goto out_delete; 476 } 477 478 if (verbose > 3) 479 perf_session__fprintf(session, stdout); 480 481 if (verbose > 2) 482 dsos__fprintf(stdout); 483 484 perf_session__collapse_resort(session); 485 perf_session__output_resort(session, session->event_total[0]); 486 perf_session__find_annotations(session); 487 out_delete: 488 perf_session__delete(session); 489 490 return ret; 491 } 492 493 static const char * const annotate_usage[] = { 494 "perf annotate [<options>] <command>", 495 NULL 496 }; 497 498 static const struct option options[] = { 499 OPT_STRING('i', "input", &input_name, "file", 500 "input file name"), 501 OPT_STRING('s', "symbol", &sym_hist_filter, "symbol", 502 "symbol to annotate"), 503 OPT_BOOLEAN('f', "force", &force, "don't complain, do it"), 504 OPT_BOOLEAN('v', "verbose", &verbose, 505 "be more verbose (show symbol address, etc)"), 506 OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, 507 "dump raw trace in ASCII"), 508 OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, 509 "file", "vmlinux pathname"), 510 OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules, 511 "load module symbols - WARNING: use only with -k and LIVE kernel"), 512 OPT_BOOLEAN('l', "print-line", &print_line, 513 "print matching source lines (may be slow)"), 514 OPT_BOOLEAN('P', "full-paths", &full_paths, 515 "Don't shorten the displayed pathnames"), 516 OPT_END() 517 }; 518 519 int cmd_annotate(int argc, const char **argv, const char *prefix __used) 520 { 521 argc = parse_options(argc, argv, options, annotate_usage, 0); 522 523 symbol_conf.priv_size = sizeof(struct sym_priv); 524 symbol_conf.try_vmlinux_path = true; 525 526 if (symbol__init() < 0) 527 return -1; 528 529 setup_sorting(annotate_usage, options); 530 531 if (argc) { 532 /* 533 * Special case: if there's an argument left then assume tha 534 * it's a symbol filter: 535 */ 536 if (argc > 1) 537 usage_with_options(annotate_usage, options); 538 539 sym_hist_filter = argv[0]; 540 } 541 542 setup_pager(); 543 544 if (field_sep && *field_sep == '.') { 545 fputs("'.' is the only non valid --field-separator argument\n", 546 stderr); 547 exit(129); 548 } 549 550 return __cmd_annotate(); 551 } 552