1 /* 2 * builtin-report.c 3 * 4 * Builtin report 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/callchain.h" 18 #include "util/strlist.h" 19 #include "util/values.h" 20 21 #include "perf.h" 22 #include "util/debug.h" 23 #include "util/header.h" 24 #include "util/session.h" 25 26 #include "util/parse-options.h" 27 #include "util/parse-events.h" 28 29 #include "util/thread.h" 30 #include "util/sort.h" 31 #include "util/hist.h" 32 33 static char const *input_name = "perf.data"; 34 35 static bool force, use_tui, use_stdio; 36 static bool hide_unresolved; 37 static bool dont_use_callchains; 38 39 static bool show_threads; 40 static struct perf_read_values show_threads_values; 41 42 static const char default_pretty_printing_style[] = "normal"; 43 static const char *pretty_printing_style = default_pretty_printing_style; 44 45 static char callchain_default_opt[] = "fractal,0.5"; 46 47 static struct hists *perf_session__hists_findnew(struct perf_session *self, 48 u64 event_stream, u32 type, 49 u64 config) 50 { 51 struct rb_node **p = &self->hists_tree.rb_node; 52 struct rb_node *parent = NULL; 53 struct hists *iter, *new; 54 55 while (*p != NULL) { 56 parent = *p; 57 iter = rb_entry(parent, struct hists, rb_node); 58 if (iter->config == config) 59 return iter; 60 61 62 if (config > iter->config) 63 p = &(*p)->rb_right; 64 else 65 p = &(*p)->rb_left; 66 } 67 68 new = malloc(sizeof(struct hists)); 69 if (new == NULL) 70 return NULL; 71 memset(new, 0, sizeof(struct hists)); 72 new->event_stream = event_stream; 73 new->config = config; 74 new->type = type; 75 rb_link_node(&new->rb_node, parent, p); 76 rb_insert_color(&new->rb_node, &self->hists_tree); 77 return new; 78 } 79 80 static int perf_session__add_hist_entry(struct perf_session *self, 81 struct addr_location *al, 82 struct sample_data *data) 83 { 84 struct map_symbol *syms = NULL; 85 struct symbol *parent = NULL; 86 int err = -ENOMEM; 87 struct hist_entry *he; 88 struct hists *hists; 89 struct perf_event_attr *attr; 90 91 if ((sort__has_parent || symbol_conf.use_callchain) && data->callchain) { 92 syms = perf_session__resolve_callchain(self, al->thread, 93 data->callchain, &parent); 94 if (syms == NULL) 95 return -ENOMEM; 96 } 97 98 attr = perf_header__find_attr(data->id, &self->header); 99 if (attr) 100 hists = perf_session__hists_findnew(self, data->id, attr->type, attr->config); 101 else 102 hists = perf_session__hists_findnew(self, data->id, 0, 0); 103 if (hists == NULL) 104 goto out_free_syms; 105 he = __hists__add_entry(hists, al, parent, data->period); 106 if (he == NULL) 107 goto out_free_syms; 108 err = 0; 109 if (symbol_conf.use_callchain) { 110 err = callchain_append(he->callchain, data->callchain, syms, 111 data->period); 112 if (err) 113 goto out_free_syms; 114 } 115 /* 116 * Only in the newt browser we are doing integrated annotation, 117 * so we don't allocated the extra space needed because the stdio 118 * code will not use it. 119 */ 120 if (use_browser > 0) 121 err = hist_entry__inc_addr_samples(he, al->addr); 122 out_free_syms: 123 free(syms); 124 return err; 125 } 126 127 static int add_event_total(struct perf_session *session, 128 struct sample_data *data, 129 struct perf_event_attr *attr) 130 { 131 struct hists *hists; 132 133 if (attr) 134 hists = perf_session__hists_findnew(session, data->id, 135 attr->type, attr->config); 136 else 137 hists = perf_session__hists_findnew(session, data->id, 0, 0); 138 139 if (!hists) 140 return -ENOMEM; 141 142 hists->stats.total_period += data->period; 143 /* 144 * FIXME: add_event_total should be moved from here to 145 * perf_session__process_event so that the proper hist is passed to 146 * the event_op methods. 147 */ 148 hists__inc_nr_events(hists, PERF_RECORD_SAMPLE); 149 session->hists.stats.total_period += data->period; 150 return 0; 151 } 152 153 static int process_sample_event(event_t *event, struct sample_data *sample, 154 struct perf_session *session) 155 { 156 struct addr_location al; 157 struct perf_event_attr *attr; 158 159 if (event__preprocess_sample(event, session, &al, sample, NULL) < 0) { 160 fprintf(stderr, "problem processing %d event, skipping it.\n", 161 event->header.type); 162 return -1; 163 } 164 165 if (al.filtered || (hide_unresolved && al.sym == NULL)) 166 return 0; 167 168 if (perf_session__add_hist_entry(session, &al, sample)) { 169 pr_debug("problem incrementing symbol period, skipping event\n"); 170 return -1; 171 } 172 173 attr = perf_header__find_attr(sample->id, &session->header); 174 175 if (add_event_total(session, sample, attr)) { 176 pr_debug("problem adding event period\n"); 177 return -1; 178 } 179 180 return 0; 181 } 182 183 static int process_read_event(event_t *event, struct sample_data *sample __used, 184 struct perf_session *session __used) 185 { 186 struct perf_event_attr *attr; 187 188 attr = perf_header__find_attr(event->read.id, &session->header); 189 190 if (show_threads) { 191 const char *name = attr ? __event_name(attr->type, attr->config) 192 : "unknown"; 193 perf_read_values_add_value(&show_threads_values, 194 event->read.pid, event->read.tid, 195 event->read.id, 196 name, 197 event->read.value); 198 } 199 200 dump_printf(": %d %d %s %" PRIu64 "\n", event->read.pid, event->read.tid, 201 attr ? __event_name(attr->type, attr->config) : "FAIL", 202 event->read.value); 203 204 return 0; 205 } 206 207 static int perf_session__setup_sample_type(struct perf_session *self) 208 { 209 if (!(self->sample_type & PERF_SAMPLE_CALLCHAIN)) { 210 if (sort__has_parent) { 211 fprintf(stderr, "selected --sort parent, but no" 212 " callchain data. Did you call" 213 " perf record without -g?\n"); 214 return -EINVAL; 215 } 216 if (symbol_conf.use_callchain) { 217 fprintf(stderr, "selected -g but no callchain data." 218 " Did you call perf record without" 219 " -g?\n"); 220 return -1; 221 } 222 } else if (!dont_use_callchains && callchain_param.mode != CHAIN_NONE && 223 !symbol_conf.use_callchain) { 224 symbol_conf.use_callchain = true; 225 if (register_callchain_param(&callchain_param) < 0) { 226 fprintf(stderr, "Can't register callchain" 227 " params\n"); 228 return -EINVAL; 229 } 230 } 231 232 return 0; 233 } 234 235 static struct perf_event_ops event_ops = { 236 .sample = process_sample_event, 237 .mmap = event__process_mmap, 238 .comm = event__process_comm, 239 .exit = event__process_task, 240 .fork = event__process_task, 241 .lost = event__process_lost, 242 .read = process_read_event, 243 .attr = event__process_attr, 244 .event_type = event__process_event_type, 245 .tracing_data = event__process_tracing_data, 246 .build_id = event__process_build_id, 247 .ordered_samples = true, 248 .ordering_requires_timestamps = true, 249 }; 250 251 extern volatile int session_done; 252 253 static void sig_handler(int sig __used) 254 { 255 session_done = 1; 256 } 257 258 static size_t hists__fprintf_nr_sample_events(struct hists *self, 259 const char *evname, FILE *fp) 260 { 261 size_t ret; 262 char unit; 263 unsigned long nr_events = self->stats.nr_events[PERF_RECORD_SAMPLE]; 264 265 nr_events = convert_unit(nr_events, &unit); 266 ret = fprintf(fp, "# Events: %lu%c", nr_events, unit); 267 if (evname != NULL) 268 ret += fprintf(fp, " %s", evname); 269 return ret + fprintf(fp, "\n#\n"); 270 } 271 272 static int hists__tty_browse_tree(struct rb_root *tree, const char *help) 273 { 274 struct rb_node *next = rb_first(tree); 275 276 while (next) { 277 struct hists *hists = rb_entry(next, struct hists, rb_node); 278 const char *evname = NULL; 279 280 if (rb_first(&hists->entries) != rb_last(&hists->entries)) 281 evname = __event_name(hists->type, hists->config); 282 283 hists__fprintf_nr_sample_events(hists, evname, stdout); 284 hists__fprintf(hists, NULL, false, stdout); 285 fprintf(stdout, "\n\n"); 286 next = rb_next(&hists->rb_node); 287 } 288 289 if (sort_order == default_sort_order && 290 parent_pattern == default_parent_pattern) { 291 fprintf(stdout, "#\n# (%s)\n#\n", help); 292 293 if (show_threads) { 294 bool style = !strcmp(pretty_printing_style, "raw"); 295 perf_read_values_display(stdout, &show_threads_values, 296 style); 297 perf_read_values_destroy(&show_threads_values); 298 } 299 } 300 301 return 0; 302 } 303 304 static int __cmd_report(void) 305 { 306 int ret = -EINVAL; 307 struct perf_session *session; 308 struct rb_node *next; 309 const char *help = "For a higher level overview, try: perf report --sort comm,dso"; 310 311 signal(SIGINT, sig_handler); 312 313 session = perf_session__new(input_name, O_RDONLY, force, false, &event_ops); 314 if (session == NULL) 315 return -ENOMEM; 316 317 if (show_threads) 318 perf_read_values_init(&show_threads_values); 319 320 ret = perf_session__setup_sample_type(session); 321 if (ret) 322 goto out_delete; 323 324 ret = perf_session__process_events(session, &event_ops); 325 if (ret) 326 goto out_delete; 327 328 if (dump_trace) { 329 perf_session__fprintf_nr_events(session, stdout); 330 goto out_delete; 331 } 332 333 if (verbose > 3) 334 perf_session__fprintf(session, stdout); 335 336 if (verbose > 2) 337 perf_session__fprintf_dsos(session, stdout); 338 339 next = rb_first(&session->hists_tree); 340 while (next) { 341 struct hists *hists; 342 343 hists = rb_entry(next, struct hists, rb_node); 344 hists__collapse_resort(hists); 345 hists__output_resort(hists); 346 next = rb_next(&hists->rb_node); 347 } 348 349 if (use_browser > 0) 350 hists__tui_browse_tree(&session->hists_tree, help); 351 else 352 hists__tty_browse_tree(&session->hists_tree, help); 353 354 out_delete: 355 /* 356 * Speed up the exit process, for large files this can 357 * take quite a while. 358 * 359 * XXX Enable this when using valgrind or if we ever 360 * librarize this command. 361 * 362 * Also experiment with obstacks to see how much speed 363 * up we'll get here. 364 * 365 * perf_session__delete(session); 366 */ 367 return ret; 368 } 369 370 static int 371 parse_callchain_opt(const struct option *opt __used, const char *arg, 372 int unset) 373 { 374 char *tok, *tok2; 375 char *endptr; 376 377 /* 378 * --no-call-graph 379 */ 380 if (unset) { 381 dont_use_callchains = true; 382 return 0; 383 } 384 385 symbol_conf.use_callchain = true; 386 387 if (!arg) 388 return 0; 389 390 tok = strtok((char *)arg, ","); 391 if (!tok) 392 return -1; 393 394 /* get the output mode */ 395 if (!strncmp(tok, "graph", strlen(arg))) 396 callchain_param.mode = CHAIN_GRAPH_ABS; 397 398 else if (!strncmp(tok, "flat", strlen(arg))) 399 callchain_param.mode = CHAIN_FLAT; 400 401 else if (!strncmp(tok, "fractal", strlen(arg))) 402 callchain_param.mode = CHAIN_GRAPH_REL; 403 404 else if (!strncmp(tok, "none", strlen(arg))) { 405 callchain_param.mode = CHAIN_NONE; 406 symbol_conf.use_callchain = false; 407 408 return 0; 409 } 410 411 else 412 return -1; 413 414 /* get the min percentage */ 415 tok = strtok(NULL, ","); 416 if (!tok) 417 goto setup; 418 419 tok2 = strtok(NULL, ","); 420 callchain_param.min_percent = strtod(tok, &endptr); 421 if (tok == endptr) 422 return -1; 423 424 if (tok2) 425 callchain_param.print_limit = strtod(tok2, &endptr); 426 setup: 427 if (register_callchain_param(&callchain_param) < 0) { 428 fprintf(stderr, "Can't register callchain params\n"); 429 return -1; 430 } 431 return 0; 432 } 433 434 static const char * const report_usage[] = { 435 "perf report [<options>] <command>", 436 NULL 437 }; 438 439 static const struct option options[] = { 440 OPT_STRING('i', "input", &input_name, "file", 441 "input file name"), 442 OPT_INCR('v', "verbose", &verbose, 443 "be more verbose (show symbol address, etc)"), 444 OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, 445 "dump raw trace in ASCII"), 446 OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, 447 "file", "vmlinux pathname"), 448 OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name, 449 "file", "kallsyms pathname"), 450 OPT_BOOLEAN('f', "force", &force, "don't complain, do it"), 451 OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules, 452 "load module symbols - WARNING: use only with -k and LIVE kernel"), 453 OPT_BOOLEAN('n', "show-nr-samples", &symbol_conf.show_nr_samples, 454 "Show a column with the number of samples"), 455 OPT_BOOLEAN('T', "threads", &show_threads, 456 "Show per-thread event counters"), 457 OPT_STRING(0, "pretty", &pretty_printing_style, "key", 458 "pretty printing style key: normal raw"), 459 OPT_BOOLEAN(0, "tui", &use_tui, "Use the TUI interface"), 460 OPT_BOOLEAN(0, "stdio", &use_stdio, "Use the stdio interface"), 461 OPT_STRING('s', "sort", &sort_order, "key[,key2...]", 462 "sort by key(s): pid, comm, dso, symbol, parent"), 463 OPT_BOOLEAN(0, "showcpuutilization", &symbol_conf.show_cpu_utilization, 464 "Show sample percentage for different cpu modes"), 465 OPT_STRING('p', "parent", &parent_pattern, "regex", 466 "regex filter to identify parent, see: '--sort parent'"), 467 OPT_BOOLEAN('x', "exclude-other", &symbol_conf.exclude_other, 468 "Only display entries with parent-match"), 469 OPT_CALLBACK_DEFAULT('g', "call-graph", NULL, "output_type,min_percent", 470 "Display callchains using output_type (graph, flat, fractal, or none) and min percent threshold. " 471 "Default: fractal,0.5", &parse_callchain_opt, callchain_default_opt), 472 OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]", 473 "only consider symbols in these dsos"), 474 OPT_STRING('C', "comms", &symbol_conf.comm_list_str, "comm[,comm...]", 475 "only consider symbols in these comms"), 476 OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]", 477 "only consider these symbols"), 478 OPT_STRING('w', "column-widths", &symbol_conf.col_width_list_str, 479 "width[,width...]", 480 "don't try to adjust column width, use these fixed values"), 481 OPT_STRING('t', "field-separator", &symbol_conf.field_sep, "separator", 482 "separator for columns, no spaces will be added between " 483 "columns '.' is reserved."), 484 OPT_BOOLEAN('U', "hide-unresolved", &hide_unresolved, 485 "Only display entries resolved to a symbol"), 486 OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory", 487 "Look for files with symbols relative to this directory"), 488 OPT_END() 489 }; 490 491 int cmd_report(int argc, const char **argv, const char *prefix __used) 492 { 493 argc = parse_options(argc, argv, options, report_usage, 0); 494 495 if (use_stdio) 496 use_browser = 0; 497 else if (use_tui) 498 use_browser = 1; 499 500 if (strcmp(input_name, "-") != 0) 501 setup_browser(); 502 else 503 use_browser = 0; 504 /* 505 * Only in the newt browser we are doing integrated annotation, 506 * so don't allocate extra space that won't be used in the stdio 507 * implementation. 508 */ 509 if (use_browser > 0) { 510 symbol_conf.priv_size = sizeof(struct sym_priv); 511 /* 512 * For searching by name on the "Browse map details". 513 * providing it only in verbose mode not to bloat too 514 * much struct symbol. 515 */ 516 if (verbose) { 517 /* 518 * XXX: Need to provide a less kludgy way to ask for 519 * more space per symbol, the u32 is for the index on 520 * the ui browser. 521 * See symbol__browser_index. 522 */ 523 symbol_conf.priv_size += sizeof(u32); 524 symbol_conf.sort_by_name = true; 525 } 526 } 527 528 if (symbol__init() < 0) 529 return -1; 530 531 setup_sorting(report_usage, options); 532 533 if (parent_pattern != default_parent_pattern) { 534 if (sort_dimension__add("parent") < 0) 535 return -1; 536 sort_parent.elide = 1; 537 } else 538 symbol_conf.exclude_other = false; 539 540 /* 541 * Any (unrecognized) arguments left? 542 */ 543 if (argc) 544 usage_with_options(report_usage, options); 545 546 sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "dso", stdout); 547 sort_entry__setup_elide(&sort_comm, symbol_conf.comm_list, "comm", stdout); 548 sort_entry__setup_elide(&sort_sym, symbol_conf.sym_list, "symbol", stdout); 549 550 return __cmd_report(); 551 } 552