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 18 #include "perf.h" 19 #include "util/debug.h" 20 21 #include "util/event.h" 22 #include "util/parse-options.h" 23 #include "util/parse-events.h" 24 #include "util/thread.h" 25 #include "util/sort.h" 26 #include "util/hist.h" 27 #include "util/session.h" 28 29 static char const *input_name = "perf.data"; 30 31 static bool force; 32 33 static bool full_paths; 34 35 static bool print_line; 36 37 static const char *sym_hist_filter; 38 39 static int hists__add_entry(struct hists *self, struct addr_location *al) 40 { 41 struct hist_entry *he; 42 43 if (sym_hist_filter != NULL && 44 (al->sym == NULL || strcmp(sym_hist_filter, al->sym->name) != 0)) { 45 /* We're only interested in a symbol named sym_hist_filter */ 46 if (al->sym != NULL) { 47 rb_erase(&al->sym->rb_node, 48 &al->map->dso->symbols[al->map->type]); 49 symbol__delete(al->sym); 50 } 51 return 0; 52 } 53 54 he = __hists__add_entry(self, al, NULL, 1); 55 if (he == NULL) 56 return -ENOMEM; 57 58 return hist_entry__inc_addr_samples(he, al->addr); 59 } 60 61 static int process_sample_event(event_t *event, struct perf_session *session) 62 { 63 struct addr_location al; 64 65 dump_printf("(IP, %d): %d: %#Lx\n", event->header.misc, 66 event->ip.pid, event->ip.ip); 67 68 if (event__preprocess_sample(event, session, &al, NULL) < 0) { 69 pr_warning("problem processing %d event, skipping it.\n", 70 event->header.type); 71 return -1; 72 } 73 74 if (!al.filtered && hists__add_entry(&session->hists, &al)) { 75 pr_warning("problem incrementing symbol count, " 76 "skipping event\n"); 77 return -1; 78 } 79 80 return 0; 81 } 82 83 static int objdump_line__print(struct objdump_line *self, 84 struct list_head *head, 85 struct hist_entry *he, u64 len) 86 { 87 struct symbol *sym = he->ms.sym; 88 static const char *prev_line; 89 static const char *prev_color; 90 91 if (self->offset != -1) { 92 const char *path = NULL; 93 unsigned int hits = 0; 94 double percent = 0.0; 95 const char *color; 96 struct sym_priv *priv = symbol__priv(sym); 97 struct sym_ext *sym_ext = priv->ext; 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 } 113 114 if (sym_ext == NULL && h->sum) 115 percent = 100.0 * hits / h->sum; 116 117 color = get_percent_color(percent); 118 119 /* 120 * Also color the filename and line if needed, with 121 * the same color than the percentage. Don't print it 122 * twice for close colored ip with the same filename:line 123 */ 124 if (path) { 125 if (!prev_line || strcmp(prev_line, path) 126 || color != prev_color) { 127 color_fprintf(stdout, color, " %s", path); 128 prev_line = path; 129 prev_color = color; 130 } 131 } 132 133 color_fprintf(stdout, color, " %7.2f", percent); 134 printf(" : "); 135 color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", self->line); 136 } else { 137 if (!*self->line) 138 printf(" :\n"); 139 else 140 printf(" : %s\n", self->line); 141 } 142 143 return 0; 144 } 145 146 static struct rb_root root_sym_ext; 147 148 static void insert_source_line(struct sym_ext *sym_ext) 149 { 150 struct sym_ext *iter; 151 struct rb_node **p = &root_sym_ext.rb_node; 152 struct rb_node *parent = NULL; 153 154 while (*p != NULL) { 155 parent = *p; 156 iter = rb_entry(parent, struct sym_ext, node); 157 158 if (sym_ext->percent > iter->percent) 159 p = &(*p)->rb_left; 160 else 161 p = &(*p)->rb_right; 162 } 163 164 rb_link_node(&sym_ext->node, parent, p); 165 rb_insert_color(&sym_ext->node, &root_sym_ext); 166 } 167 168 static void free_source_line(struct hist_entry *he, int len) 169 { 170 struct sym_priv *priv = symbol__priv(he->ms.sym); 171 struct sym_ext *sym_ext = priv->ext; 172 int i; 173 174 if (!sym_ext) 175 return; 176 177 for (i = 0; i < len; i++) 178 free(sym_ext[i].path); 179 free(sym_ext); 180 181 priv->ext = NULL; 182 root_sym_ext = RB_ROOT; 183 } 184 185 /* Get the filename:line for the colored entries */ 186 static void 187 get_source_line(struct hist_entry *he, int len, const char *filename) 188 { 189 struct symbol *sym = he->ms.sym; 190 u64 start; 191 int i; 192 char cmd[PATH_MAX * 2]; 193 struct sym_ext *sym_ext; 194 struct sym_priv *priv = symbol__priv(sym); 195 struct sym_hist *h = priv->hist; 196 197 if (!h->sum) 198 return; 199 200 sym_ext = priv->ext = calloc(len, sizeof(struct sym_ext)); 201 if (!priv->ext) 202 return; 203 204 start = he->ms.map->unmap_ip(he->ms.map, sym->start); 205 206 for (i = 0; i < len; i++) { 207 char *path = NULL; 208 size_t line_len; 209 u64 offset; 210 FILE *fp; 211 212 sym_ext[i].percent = 100.0 * h->ip[i] / h->sum; 213 if (sym_ext[i].percent <= 0.5) 214 continue; 215 216 offset = start + i; 217 sprintf(cmd, "addr2line -e %s %016llx", filename, offset); 218 fp = popen(cmd, "r"); 219 if (!fp) 220 continue; 221 222 if (getline(&path, &line_len, fp) < 0 || !line_len) 223 goto next; 224 225 sym_ext[i].path = malloc(sizeof(char) * line_len + 1); 226 if (!sym_ext[i].path) 227 goto next; 228 229 strcpy(sym_ext[i].path, path); 230 insert_source_line(&sym_ext[i]); 231 232 next: 233 pclose(fp); 234 } 235 } 236 237 static void print_summary(const char *filename) 238 { 239 struct sym_ext *sym_ext; 240 struct rb_node *node; 241 242 printf("\nSorted summary for file %s\n", filename); 243 printf("----------------------------------------------\n\n"); 244 245 if (RB_EMPTY_ROOT(&root_sym_ext)) { 246 printf(" Nothing higher than %1.1f%%\n", MIN_GREEN); 247 return; 248 } 249 250 node = rb_first(&root_sym_ext); 251 while (node) { 252 double percent; 253 const char *color; 254 char *path; 255 256 sym_ext = rb_entry(node, struct sym_ext, node); 257 percent = sym_ext->percent; 258 color = get_percent_color(percent); 259 path = sym_ext->path; 260 261 color_fprintf(stdout, color, " %7.2f %s", percent, path); 262 node = rb_next(node); 263 } 264 } 265 266 static void hist_entry__print_hits(struct hist_entry *self) 267 { 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 280 static void annotate_sym(struct hist_entry *he) 281 { 282 struct map *map = he->ms.map; 283 struct dso *dso = map->dso; 284 struct symbol *sym = he->ms.sym; 285 const char *filename = dso->long_name, *d_filename; 286 u64 len; 287 LIST_HEAD(head); 288 struct objdump_line *pos, *n; 289 290 if (hist_entry__annotate(he, &head) < 0) 291 return; 292 293 if (full_paths) 294 d_filename = filename; 295 else 296 d_filename = basename(filename); 297 298 len = sym->end - sym->start; 299 300 if (print_line) { 301 get_source_line(he, len, filename); 302 print_summary(filename); 303 } 304 305 printf("\n\n------------------------------------------------\n"); 306 printf(" Percent | Source code & Disassembly of %s\n", d_filename); 307 printf("------------------------------------------------\n"); 308 309 if (verbose) 310 hist_entry__print_hits(he); 311 312 list_for_each_entry_safe(pos, n, &head, node) { 313 objdump_line__print(pos, &head, he, len); 314 list_del(&pos->node); 315 objdump_line__free(pos); 316 } 317 318 if (print_line) 319 free_source_line(he, len); 320 } 321 322 static void hists__find_annotations(struct hists *self) 323 { 324 struct rb_node *nd; 325 326 for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { 327 struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node); 328 struct sym_priv *priv; 329 330 if (he->ms.sym == NULL) 331 continue; 332 333 priv = symbol__priv(he->ms.sym); 334 if (priv->hist == NULL) 335 continue; 336 337 annotate_sym(he); 338 /* 339 * Since we have a hist_entry per IP for the same symbol, free 340 * he->ms.sym->hist to signal we already processed this symbol. 341 */ 342 free(priv->hist); 343 priv->hist = NULL; 344 } 345 } 346 347 static struct perf_event_ops event_ops = { 348 .sample = process_sample_event, 349 .mmap = event__process_mmap, 350 .comm = event__process_comm, 351 .fork = event__process_task, 352 }; 353 354 static int __cmd_annotate(void) 355 { 356 int ret; 357 struct perf_session *session; 358 359 session = perf_session__new(input_name, O_RDONLY, force, false); 360 if (session == NULL) 361 return -ENOMEM; 362 363 ret = perf_session__process_events(session, &event_ops); 364 if (ret) 365 goto out_delete; 366 367 if (dump_trace) { 368 event__print_totals(); 369 goto out_delete; 370 } 371 372 if (verbose > 3) 373 perf_session__fprintf(session, stdout); 374 375 if (verbose > 2) 376 perf_session__fprintf_dsos(session, stdout); 377 378 hists__collapse_resort(&session->hists); 379 hists__output_resort(&session->hists); 380 hists__find_annotations(&session->hists); 381 out_delete: 382 perf_session__delete(session); 383 384 return ret; 385 } 386 387 static const char * const annotate_usage[] = { 388 "perf annotate [<options>] <command>", 389 NULL 390 }; 391 392 static const struct option options[] = { 393 OPT_STRING('i', "input", &input_name, "file", 394 "input file name"), 395 OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]", 396 "only consider symbols in these dsos"), 397 OPT_STRING('s', "symbol", &sym_hist_filter, "symbol", 398 "symbol to annotate"), 399 OPT_BOOLEAN('f', "force", &force, "don't complain, do it"), 400 OPT_INCR('v', "verbose", &verbose, 401 "be more verbose (show symbol address, etc)"), 402 OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, 403 "dump raw trace in ASCII"), 404 OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, 405 "file", "vmlinux pathname"), 406 OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules, 407 "load module symbols - WARNING: use only with -k and LIVE kernel"), 408 OPT_BOOLEAN('l', "print-line", &print_line, 409 "print matching source lines (may be slow)"), 410 OPT_BOOLEAN('P', "full-paths", &full_paths, 411 "Don't shorten the displayed pathnames"), 412 OPT_END() 413 }; 414 415 int cmd_annotate(int argc, const char **argv, const char *prefix __used) 416 { 417 argc = parse_options(argc, argv, options, annotate_usage, 0); 418 419 symbol_conf.priv_size = sizeof(struct sym_priv); 420 symbol_conf.try_vmlinux_path = true; 421 422 if (symbol__init() < 0) 423 return -1; 424 425 setup_sorting(annotate_usage, options); 426 427 if (argc) { 428 /* 429 * Special case: if there's an argument left then assume tha 430 * it's a symbol filter: 431 */ 432 if (argc > 1) 433 usage_with_options(annotate_usage, options); 434 435 sym_hist_filter = argv[0]; 436 } 437 438 setup_pager(); 439 440 if (field_sep && *field_sep == '.') { 441 pr_err("'.' is the only non valid --field-separator argument\n"); 442 return -1; 443 } 444 445 return __cmd_annotate(); 446 } 447