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 81
static int perf_session__add_hist_entry(struct perf_session *self,
					struct addr_location *al,
82
					struct sample_data *data)
83
{
84 85
	struct map_symbol *syms = NULL;
	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 95 96
		if (syms == NULL)
			return -ENOMEM;
	}
97 98 99

	attr = perf_header__find_attr(data->id, &self->header);
	if (attr)
100
		hists = perf_session__hists_findnew(self, data->id, attr->type, attr->config);
101
	else
102 103
		hists = perf_session__hists_findnew(self, data->id, 0, 0);
	if (hists == NULL)
104
		goto out_free_syms;
105
	he = __hists__add_entry(hists, al, parent, data->period);
106
	if (he == NULL)
107 108
		goto out_free_syms;
	err = 0;
109
	if (symbol_conf.use_callchain) {
110 111
		err = callchain_append(he->callchain, data->callchain, syms,
				       data->period);
112 113 114 115 116 117 118 119
		if (err)
			goto out_free_syms;
	}
	/*
	 * 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 124
out_free_syms:
	free(syms);
	return err;
125 126
}

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

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

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

142
	hists->stats.total_period += data->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 += data->period;
150 151 152
	return 0;
}

153 154
static int process_sample_event(event_t *event, struct sample_data *sample,
				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 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(event_t *event, struct sample_data *sample __used,
			      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 193 194 195 196 197 198 199
				   : "unknown";
		perf_read_values_add_value(&show_threads_values,
					   event->read.pid, event->read.tid,
					   event->read.id,
					   name,
					   event->read.value);
	}

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

	return 0;
}

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

232 233
	return 0;
}
234

235
static struct perf_event_ops event_ops = {
236 237 238 239 240 241 242
	.sample	= process_sample_event,
	.mmap	= event__process_mmap,
	.comm	= event__process_comm,
	.exit	= event__process_task,
	.fork	= event__process_task,
	.lost	= event__process_lost,
	.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 248
	.ordered_samples = true,
	.ordering_requires_timestamps = true,
249
};
250

251 252
extern volatile int session_done;

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

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

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

304 305
static int __cmd_report(void)
{
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 312
	signal(SIGINT, sig_handler);

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

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

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

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 344
		hists = rb_entry(next, struct hists, rb_node);
		hists__collapse_resort(hists);
345
		hists__output_resort(hists);
346
		next = rb_next(&hists->rb_node);
347 348
	}

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

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

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

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

385
	symbol_conf.use_callchain = true;
386 387 388 389

	if (!arg)
		return 0;

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

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

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

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

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

		return 0;
	}

411 412 413
	else
		return -1;

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

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

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

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

static const struct option options[] = {
	OPT_STRING('i', "input", &input_name, "file",
		    "input file name"),
442
	OPT_INCR('v', "verbose", &verbose,
443
		    "be more verbose (show symbol address, etc)"),
444 445
	OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
		    "dump raw trace in ASCII"),
446 447
	OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
		   "file", "vmlinux pathname"),
448 449
	OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name,
		   "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 456
	OPT_BOOLEAN('T', "threads", &show_threads,
		    "Show per-thread event counters"),
457 458
	OPT_STRING(0, "pretty", &pretty_printing_style, "key",
		   "pretty printing style key: normal raw"),
459 460
	OPT_BOOLEAN(0, "tui", &use_tui, "Use the TUI interface"),
	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 464
	OPT_BOOLEAN(0, "showcpuutilization", &symbol_conf.show_cpu_utilization,
		    "Show sample percentage for different cpu modes"),
465 466
	OPT_STRING('p', "parent", &parent_pattern, "regex",
		   "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 480
		   "width[,width...]",
		   "don't try to adjust column width, use these fixed values"),
481
	OPT_STRING('t', "field-separator", &symbol_conf.field_sep, "separator",
482 483
		   "separator for columns, no spaces will be added between "
		   "columns '.' is reserved."),
484 485
	OPT_BOOLEAN('U', "hide-unresolved", &hide_unresolved,
		    "Only display entries resolved to a symbol"),
486 487
	OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
		    "Look for files with symbols relative to this directory"),
488 489 490
	OPT_END()
};

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

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

500 501
	if (strcmp(input_name, "-") != 0)
		setup_browser();
502 503
	else
		use_browser = 0;
504 505 506 507 508
	/*
	 * 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.
	 */
509
	if (use_browser > 0) {
510
		symbol_conf.priv_size = sizeof(struct sym_priv);
511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526
		/*
 		 * 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;
		}
	}
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 535
		if (sort_dimension__add("parent") < 0)
			return -1;
536 537
		sort_parent.elide = 1;
	} else
538
		symbol_conf.exclude_other = false;
539

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

546 547 548
	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);
549

550 551
	return __cmd_report();
}