builtin-report.c 14.7 KB
Newer Older
1 2 3 4 5 6 7
/*
 * builtin-report.c
 *
 * Builtin report command: Analyze the perf.data input file,
 * look up and read DSOs and symbol information and display
 * a histogram of results, along various sorting keys.
 */
8
#include "builtin.h"
9

10 11
#include "util/util.h"

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 27 28

#include "util/parse-options.h"
#include "util/parse-events.h"

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 41
static struct perf_read_values	show_threads_values;

42 43
static const char	default_pretty_printing_style[] = "normal";
static const char	*pretty_printing_style = default_pretty_printing_style;
44

45 46
static char		callchain_default_opt[] = "fractal,0.5";

47 48 49
static struct hists *perf_session__hists_findnew(struct perf_session *self,
						 u64 event_stream, u32 type,
						 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 56

	while (*p != NULL) {
		parent = *p;
57
		iter = rb_entry(parent, struct hists, rb_node);
58 59 60 61 62 63 64 65 66 67
		if (iter->config == config)
			return iter;


		if (config > iter->config)
			p = &(*p)->rb_right;
		else
			p = &(*p)->rb_left;
	}

68
	new = malloc(sizeof(struct hists));
69 70
	if (new == NULL)
		return NULL;
71
	memset(new, 0, sizeof(struct hists));
72 73 74 75
	new->event_stream = event_stream;
	new->config = config;
	new->type = type;
	rb_link_node(&new->rb_node, parent, p);
76
	rb_insert_color(&new->rb_node, &self->hists_tree);
77 78 79
	return new;
}

80
static int perf_session__add_hist_entry(struct perf_session *session,
81
					struct addr_location *al,
82
					struct perf_sample *sample)
83
{
84
	struct symbol *parent = NULL;
85
	int err = 0;
86
	struct hist_entry *he;
87
	struct hists *hists;
88
	struct perf_event_attr *attr;
89

90 91 92
	if ((sort__has_parent || symbol_conf.use_callchain) && sample->callchain) {
		err = perf_session__resolve_callchain(session, al->thread,
						      sample->callchain, &parent);
93 94
		if (err)
			return err;
95
	}
96

97
	attr = perf_header__find_attr(sample->id, &session->header);
98
	if (attr)
99
		hists = perf_session__hists_findnew(session, sample->id, attr->type, attr->config);
100
	else
101
		hists = perf_session__hists_findnew(session, sample->id, 0, 0);
102
	if (hists == NULL)
103 104
		return -ENOMEM;

105
	he = __hists__add_entry(hists, al, parent, sample->period);
106
	if (he == NULL)
107 108
		return -ENOMEM;

109
	if (symbol_conf.use_callchain) {
110 111
		err = callchain_append(he->callchain, &session->callchain_cursor,
				       sample->period);
112
		if (err)
113
			return err;
114 115 116 117 118 119
	}
	/*
	 * Only in the newt browser we are doing integrated annotation,
	 * so we don't allocated the extra space needed because the stdio
	 * code will not use it.
	 */
120
	if (use_browser > 0)
121
		err = hist_entry__inc_addr_samples(he, al->addr);
122

123
	return err;
124 125
}

126
static int add_event_total(struct perf_session *session,
127
			   struct perf_sample *sample,
128 129
			   struct perf_event_attr *attr)
{
130
	struct hists *hists;
131 132

	if (attr)
133
		hists = perf_session__hists_findnew(session, sample->id,
134
						    attr->type, attr->config);
135
	else
136
		hists = perf_session__hists_findnew(session, sample->id, 0, 0);
137

138
	if (!hists)
139 140
		return -ENOMEM;

141
	hists->stats.total_period += sample->period;
142 143 144 145 146 147
	/*
	 * FIXME: add_event_total should be moved from here to
	 * perf_session__process_event so that the proper hist is passed to
	 * the event_op methods.
	 */
	hists__inc_nr_events(hists, PERF_RECORD_SAMPLE);
148
	session->hists.stats.total_period += sample->period;
149 150 151
	return 0;
}

152 153
static int process_sample_event(union perf_event *event,
				struct perf_sample *sample,
154
				struct perf_session *session)
155
{
156
	struct addr_location al;
157
	struct perf_event_attr *attr;
158

159
	if (perf_event__preprocess_sample(event, session, &al, sample, NULL) < 0) {
160
		fprintf(stderr, "problem processing %d event, skipping it.\n",
161 162 163
			event->header.type);
		return -1;
	}
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 178 179
		return -1;
	}

180 181
	return 0;
}
I
Ingo Molnar 已提交
182

183 184
static int process_read_event(union perf_event *event,
			      struct perf_sample *sample __used,
185
			      struct perf_session *session __used)
186
{
187
	struct perf_event_attr *attr;
188

189
	attr = perf_header__find_attr(event->read.id, &session->header);
190

191
	if (show_threads) {
192
		const char *name = attr ? __event_name(attr->type, attr->config)
193 194 195 196 197 198 199 200
				   : "unknown";
		perf_read_values_add_value(&show_threads_values,
					   event->read.pid, event->read.tid,
					   event->read.id,
					   name,
					   event->read.value);
	}

201
	dump_printf(": %d %d %s %" PRIu64 "\n", event->read.pid, event->read.tid,
202 203
		    attr ? __event_name(attr->type, attr->config) : "FAIL",
		    event->read.value);
204 205 206 207

	return 0;
}

208
static int perf_session__setup_sample_type(struct perf_session *self)
209
{
210
	if (!(self->sample_type & PERF_SAMPLE_CALLCHAIN)) {
211 212 213 214
		if (sort__has_parent) {
			fprintf(stderr, "selected --sort parent, but no"
					" callchain data. Did you call"
					" perf record without -g?\n");
215
			return -EINVAL;
216
		}
217
		if (symbol_conf.use_callchain) {
218
			fprintf(stderr, "selected -g but no callchain data."
219 220
					" Did you call perf record without"
					" -g?\n");
221
			return -1;
222
		}
223 224
	} else if (!dont_use_callchains && callchain_param.mode != CHAIN_NONE &&
		   !symbol_conf.use_callchain) {
225
			symbol_conf.use_callchain = true;
226
			if (callchain_register_param(&callchain_param) < 0) {
227 228
				fprintf(stderr, "Can't register callchain"
						" params\n");
229
				return -EINVAL;
230
			}
231 232
	}

233 234
	return 0;
}
235

236
static struct perf_event_ops event_ops = {
237 238 239 240 241 242 243 244 245 246 247
	.sample		 = process_sample_event,
	.mmap		 = perf_event__process_mmap,
	.comm		 = perf_event__process_comm,
	.exit		 = perf_event__process_task,
	.fork		 = perf_event__process_task,
	.lost		 = perf_event__process_lost,
	.read		 = process_read_event,
	.attr		 = perf_event__process_attr,
	.event_type	 = perf_event__process_event_type,
	.tracing_data	 = perf_event__process_tracing_data,
	.build_id	 = perf_event__process_build_id,
248 249
	.ordered_samples = true,
	.ordering_requires_timestamps = true,
250
};
251

252 253
extern volatile int session_done;

254
static void sig_handler(int sig __used)
255 256 257 258
{
	session_done = 1;
}

259 260 261 262 263 264 265 266 267 268 269 270 271 272
static size_t hists__fprintf_nr_sample_events(struct hists *self,
					      const char *evname, FILE *fp)
{
	size_t ret;
	char unit;
	unsigned long nr_events = self->stats.nr_events[PERF_RECORD_SAMPLE];

	nr_events = convert_unit(nr_events, &unit);
	ret = fprintf(fp, "# Events: %lu%c", nr_events, unit);
	if (evname != NULL)
		ret += fprintf(fp, " %s", evname);
	return ret + fprintf(fp, "\n#\n");
}

273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304
static int hists__tty_browse_tree(struct rb_root *tree, const char *help)
{
	struct rb_node *next = rb_first(tree);

	while (next) {
		struct hists *hists = rb_entry(next, struct hists, rb_node);
		const char *evname = NULL;

		if (rb_first(&hists->entries) != rb_last(&hists->entries))
			evname = __event_name(hists->type, hists->config);

		hists__fprintf_nr_sample_events(hists, evname, stdout);
		hists__fprintf(hists, NULL, false, stdout);
		fprintf(stdout, "\n\n");
		next = rb_next(&hists->rb_node);
	}

	if (sort_order == default_sort_order &&
	    parent_pattern == default_parent_pattern) {
		fprintf(stdout, "#\n# (%s)\n#\n", help);

		if (show_threads) {
			bool style = !strcmp(pretty_printing_style, "raw");
			perf_read_values_display(stdout, &show_threads_values,
						 style);
			perf_read_values_destroy(&show_threads_values);
		}
	}

	return 0;
}

305 306
static int __cmd_report(void)
{
307
	int ret = -EINVAL;
308
	struct perf_session *session;
309
	struct rb_node *next;
310
	const char *help = "For a higher level overview, try: perf report --sort comm,dso";
311

312 313
	signal(SIGINT, sig_handler);

314
	session = perf_session__new(input_name, O_RDONLY, force, false, &event_ops);
315 316 317
	if (session == NULL)
		return -ENOMEM;

318 319
	if (show_threads)
		perf_read_values_init(&show_threads_values);
320

321 322 323 324
	ret = perf_session__setup_sample_type(session);
	if (ret)
		goto out_delete;

325
	ret = perf_session__process_events(session, &event_ops);
326
	if (ret)
327
		goto out_delete;
328

329
	if (dump_trace) {
330
		perf_session__fprintf_nr_events(session, stdout);
331
		goto out_delete;
332
	}
333

334
	if (verbose > 3)
335
		perf_session__fprintf(session, stdout);
336

337
	if (verbose > 2)
338
		perf_session__fprintf_dsos(session, stdout);
339

340
	next = rb_first(&session->hists_tree);
341
	while (next) {
342
		struct hists *hists;
343

344 345
		hists = rb_entry(next, struct hists, rb_node);
		hists__collapse_resort(hists);
346
		hists__output_resort(hists);
347
		next = rb_next(&hists->rb_node);
348 349
	}

350 351 352 353
	if (use_browser > 0)
		hists__tui_browse_tree(&session->hists_tree, help);
	else
		hists__tty_browse_tree(&session->hists_tree, help);
354

355
out_delete:
356 357 358 359 360 361 362 363 364 365 366 367
	/*
	 * 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);
 	 */
368
	return ret;
369 370
}

371 372
static int
parse_callchain_opt(const struct option *opt __used, const char *arg,
373
		    int unset)
374
{
375
	char *tok, *tok2;
376 377
	char *endptr;

378 379 380 381 382 383 384 385
	/*
	 * --no-call-graph
	 */
	if (unset) {
		dont_use_callchains = true;
		return 0;
	}

386
	symbol_conf.use_callchain = true;
387 388 389 390

	if (!arg)
		return 0;

391 392 393 394 395 396
	tok = strtok((char *)arg, ",");
	if (!tok)
		return -1;

	/* get the output mode */
	if (!strncmp(tok, "graph", strlen(arg)))
397
		callchain_param.mode = CHAIN_GRAPH_ABS;
398

399
	else if (!strncmp(tok, "flat", strlen(arg)))
400 401 402 403 404
		callchain_param.mode = CHAIN_FLAT;

	else if (!strncmp(tok, "fractal", strlen(arg)))
		callchain_param.mode = CHAIN_GRAPH_REL;

405 406
	else if (!strncmp(tok, "none", strlen(arg))) {
		callchain_param.mode = CHAIN_NONE;
407
		symbol_conf.use_callchain = false;
408 409 410 411

		return 0;
	}

412 413 414
	else
		return -1;

415 416 417
	/* get the min percentage */
	tok = strtok(NULL, ",");
	if (!tok)
418
		goto setup;
419

420
	tok2 = strtok(NULL, ",");
421
	callchain_param.min_percent = strtod(tok, &endptr);
422 423 424
	if (tok == endptr)
		return -1;

425 426
	if (tok2)
		callchain_param.print_limit = strtod(tok2, &endptr);
427
setup:
428
	if (callchain_register_param(&callchain_param) < 0) {
429 430 431
		fprintf(stderr, "Can't register callchain params\n");
		return -1;
	}
432 433 434
	return 0;
}

435
static const char * const report_usage[] = {
436 437 438 439 440 441 442
	"perf report [<options>] <command>",
	NULL
};

static const struct option options[] = {
	OPT_STRING('i', "input", &input_name, "file",
		    "input file name"),
443
	OPT_INCR('v', "verbose", &verbose,
444
		    "be more verbose (show symbol address, etc)"),
445 446
	OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
		    "dump raw trace in ASCII"),
447 448
	OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
		   "file", "vmlinux pathname"),
449 450
	OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name,
		   "file", "kallsyms pathname"),
451
	OPT_BOOLEAN('f', "force", &force, "don't complain, do it"),
452
	OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules,
453
		    "load module symbols - WARNING: use only with -k and LIVE kernel"),
454
	OPT_BOOLEAN('n', "show-nr-samples", &symbol_conf.show_nr_samples,
455
		    "Show a column with the number of samples"),
456 457
	OPT_BOOLEAN('T', "threads", &show_threads,
		    "Show per-thread event counters"),
458 459
	OPT_STRING(0, "pretty", &pretty_printing_style, "key",
		   "pretty printing style key: normal raw"),
460 461
	OPT_BOOLEAN(0, "tui", &use_tui, "Use the TUI interface"),
	OPT_BOOLEAN(0, "stdio", &use_stdio, "Use the stdio interface"),
462
	OPT_STRING('s', "sort", &sort_order, "key[,key2...]",
463
		   "sort by key(s): pid, comm, dso, symbol, parent"),
464 465
	OPT_BOOLEAN(0, "showcpuutilization", &symbol_conf.show_cpu_utilization,
		    "Show sample percentage for different cpu modes"),
466 467
	OPT_STRING('p', "parent", &parent_pattern, "regex",
		   "regex filter to identify parent, see: '--sort parent'"),
468
	OPT_BOOLEAN('x', "exclude-other", &symbol_conf.exclude_other,
469
		    "Only display entries with parent-match"),
470
	OPT_CALLBACK_DEFAULT('g', "call-graph", NULL, "output_type,min_percent",
471
		     "Display callchains using output_type (graph, flat, fractal, or none) and min percent threshold. "
472
		     "Default: fractal,0.5", &parse_callchain_opt, callchain_default_opt),
473
	OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]",
474
		   "only consider symbols in these dsos"),
475
	OPT_STRING('C', "comms", &symbol_conf.comm_list_str, "comm[,comm...]",
476
		   "only consider symbols in these comms"),
477
	OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]",
478
		   "only consider these symbols"),
479
	OPT_STRING('w', "column-widths", &symbol_conf.col_width_list_str,
480 481
		   "width[,width...]",
		   "don't try to adjust column width, use these fixed values"),
482
	OPT_STRING('t', "field-separator", &symbol_conf.field_sep, "separator",
483 484
		   "separator for columns, no spaces will be added between "
		   "columns '.' is reserved."),
485 486
	OPT_BOOLEAN('U', "hide-unresolved", &hide_unresolved,
		    "Only display entries resolved to a symbol"),
487 488
	OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
		    "Look for files with symbols relative to this directory"),
489 490 491
	OPT_END()
};

492
int cmd_report(int argc, const char **argv, const char *prefix __used)
493
{
494 495
	argc = parse_options(argc, argv, options, report_usage, 0);

496 497 498 499 500
	if (use_stdio)
		use_browser = 0;
	else if (use_tui)
		use_browser = 1;

501 502
	if (strcmp(input_name, "-") != 0)
		setup_browser();
503 504
	else
		use_browser = 0;
505 506 507 508 509
	/*
	 * Only in the newt browser we are doing integrated annotation,
	 * so don't allocate extra space that won't be used in the stdio
	 * implementation.
	 */
510
	if (use_browser > 0) {
511
		symbol_conf.priv_size = sizeof(struct sym_priv);
512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527
		/*
 		 * For searching by name on the "Browse map details".
 		 * providing it only in verbose mode not to bloat too
 		 * much struct symbol.
 		 */
		if (verbose) {
			/*
			 * XXX: Need to provide a less kludgy way to ask for
			 * more space per symbol, the u32 is for the index on
			 * the ui browser.
			 * See symbol__browser_index.
			 */
			symbol_conf.priv_size += sizeof(u32);
			symbol_conf.sort_by_name = true;
		}
	}
528

529
	if (symbol__init() < 0)
530
		return -1;
531

532
	setup_sorting(report_usage, options);
533

534
	if (parent_pattern != default_parent_pattern) {
535 536
		if (sort_dimension__add("parent") < 0)
			return -1;
537 538
		sort_parent.elide = 1;
	} else
539
		symbol_conf.exclude_other = false;
540

541 542 543 544 545 546
	/*
	 * Any (unrecognized) arguments left?
	 */
	if (argc)
		usage_with_options(report_usage, options);

547 548 549
	sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "dso", stdout);
	sort_entry__setup_elide(&sort_comm, symbol_conf.comm_list, "comm", stdout);
	sort_entry__setup_elide(&sort_sym, symbol_conf.sym_list, "symbol", stdout);
550

551 552
	return __cmd_report();
}