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/annotate.h"
13
#include "util/color.h"
14
#include <linux/list.h>
15
#include "util/cache.h"
16
#include <linux/rbtree.h>
17
#include "util/symbol.h"
18
#include "util/callchain.h"
19
#include "util/strlist.h"
20
#include "util/values.h"
21

22
#include "perf.h"
23
#include "util/debug.h"
24
#include "util/header.h"
25
#include "util/session.h"
26 27 28 29

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

30
#include "util/thread.h"
31
#include "util/sort.h"
32
#include "util/hist.h"
33

34
static char		const *input_name = "perf.data";
35

36
static bool		force, use_tui, use_stdio;
37
static bool		hide_unresolved;
38
static bool		dont_use_callchains;
39

40
static bool		show_threads;
41 42
static struct perf_read_values	show_threads_values;

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

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

48 49 50
static struct hists *perf_session__hists_findnew(struct perf_session *self,
						 u64 event_stream, u32 type,
						 u64 config)
51
{
52
	struct rb_node **p = &self->hists_tree.rb_node;
53
	struct rb_node *parent = NULL;
54
	struct hists *iter, *new;
55 56 57

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


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

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

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

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

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

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

110
	if (symbol_conf.use_callchain) {
111 112
		err = callchain_append(he->callchain, &session->callchain_cursor,
				       sample->period);
113
		if (err)
114
			return err;
115 116 117 118 119 120
	}
	/*
	 * 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.
	 */
121
	if (use_browser > 0)
122
		err = hist_entry__inc_addr_samples(he, al->addr);
123

124
	return err;
125 126
}

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

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

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

142
	hists->stats.total_period += sample->period;
143 144 145 146 147 148
	/*
	 * 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);
149
	session->hists.stats.total_period += sample->period;
150 151 152
	return 0;
}

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

160
	if (perf_event__preprocess_sample(event, session, &al, sample, NULL) < 0) {
161
		fprintf(stderr, "problem processing %d event, skipping it.\n",
162 163 164
			event->header.type);
		return -1;
	}
165

166
	if (al.filtered || (hide_unresolved && al.sym == NULL))
167
		return 0;
168

169
	if (perf_session__add_hist_entry(session, &al, sample)) {
170
		pr_debug("problem incrementing symbol period, skipping event\n");
171
		return -1;
172
	}
173

174
	attr = perf_header__find_attr(sample->id, &session->header);
175

176
	if (add_event_total(session, sample, attr)) {
177
		pr_debug("problem adding event period\n");
178 179 180
		return -1;
	}

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

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

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

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

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

	return 0;
}

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

234 235
	return 0;
}
236

237
static struct perf_event_ops event_ops = {
238 239 240 241 242 243 244 245 246 247 248
	.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,
249 250
	.ordered_samples = true,
	.ordering_requires_timestamps = true,
251
};
252

253 254
extern volatile int session_done;

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

260 261 262 263 264 265 266 267 268 269 270 271 272 273
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");
}

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 305
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;
}

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

313 314
	signal(SIGINT, sig_handler);

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

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

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

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

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

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

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

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

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

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

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

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

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

387
	symbol_conf.use_callchain = true;
388 389 390 391

	if (!arg)
		return 0;

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

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

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

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

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

		return 0;
	}

413 414 415
	else
		return -1;

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

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

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

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

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

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

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

502
	if (strcmp(input_name, "-") != 0)
503
		setup_browser(true);
504 505
	else
		use_browser = 0;
506 507 508 509 510
	/*
	 * 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.
	 */
511
	if (use_browser > 0) {
512
		symbol_conf.priv_size = sizeof(struct annotation);
513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528
		/*
 		 * 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;
		}
	}
529

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

533
	setup_sorting(report_usage, options);
534

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

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

548 549 550
	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);
551

552 553
	return __cmd_report();
}