builtin-report.c 14.5 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 201 202
	dump_printf(": %d %d %s %Lu\n", event->read.pid, event->read.tid,
		    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

249 250
extern volatile int session_done;

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

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

270 271 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
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;
}

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

309 310
	signal(SIGINT, sig_handler);

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

315 316
	if (show_threads)
		perf_read_values_init(&show_threads_values);
317

318 319 320 321
	ret = perf_session__setup_sample_type(session);
	if (ret)
		goto out_delete;

322
	ret = perf_session__process_events(session, &event_ops);
323
	if (ret)
324
		goto out_delete;
325

326
	if (dump_trace) {
327
		perf_session__fprintf_nr_events(session, stdout);
328
		goto out_delete;
329
	}
330

331
	if (verbose > 3)
332
		perf_session__fprintf(session, stdout);
333

334
	if (verbose > 2)
335
		perf_session__fprintf_dsos(session, stdout);
336

337
	next = rb_first(&session->hists_tree);
338
	while (next) {
339
		struct hists *hists;
340

341 342
		hists = rb_entry(next, struct hists, rb_node);
		hists__collapse_resort(hists);
343
		hists__output_resort(hists);
344
		next = rb_next(&hists->rb_node);
345 346
	}

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

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

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

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

383
	symbol_conf.use_callchain = true;
384 385 386 387

	if (!arg)
		return 0;

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

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

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

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

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

		return 0;
	}

409 410 411
	else
		return -1;

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

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

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

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

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

487
int cmd_report(int argc, const char **argv, const char *prefix __used)
488
{
489 490
	argc = parse_options(argc, argv, options, report_usage, 0);

491 492 493 494 495
	if (use_stdio)
		use_browser = 0;
	else if (use_tui)
		use_browser = 1;

496 497
	if (strcmp(input_name, "-") != 0)
		setup_browser();
498 499
	else
		use_browser = 0;
500 501 502 503 504
	/*
	 * 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.
	 */
505
	if (use_browser > 0) {
506
		symbol_conf.priv_size = sizeof(struct sym_priv);
507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522
		/*
 		 * 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;
		}
	}
523

524
	if (symbol__init() < 0)
525
		return -1;
526

527
	setup_sorting(report_usage, options);
528

529
	if (parent_pattern != default_parent_pattern) {
530 531
		if (sort_dimension__add("parent") < 0)
			return -1;
532 533
		sort_parent.elide = 1;
	} else
534
		symbol_conf.exclude_other = false;
535

536 537 538 539 540 541
	/*
	 * Any (unrecognized) arguments left?
	 */
	if (argc)
		usage_with_options(report_usage, options);

542 543 544
	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);
545

546 547
	return __cmd_report();
}