builtin-report.c 14.1 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;
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
		err = append_chain(he->callchain, data->callchain, syms, data->period);
111 112 113 114 115 116 117 118
		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.
	 */
119
	if (use_browser > 0)
120
		err = hist_entry__inc_addr_samples(he, al->addr);
121 122 123
out_free_syms:
	free(syms);
	return err;
124 125
}

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

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

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

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

152
static int process_sample_event(event_t *event, struct perf_session *session)
153
{
154
	struct sample_data data = { .period = 1, };
155
	struct addr_location al;
156
	struct perf_event_attr *attr;
157

158
	event__parse_sample(event, session->sample_type, &data);
159

160 161
	dump_printf("(IP, %d): %d/%d: %#Lx period: %Ld\n", event->header.misc,
		    data.pid, data.tid, data.ip, data.period);
162

163
	if (session->sample_type & PERF_SAMPLE_CALLCHAIN) {
164
		unsigned int i;
165

166
		dump_printf("... chain: nr:%Lu\n", data.callchain->nr);
167

168
		if (!ip_callchain__valid(data.callchain, event)) {
169 170
			pr_debug("call-chain problem with event, "
				 "skipping it.\n");
171 172 173 174
			return 0;
		}

		if (dump_trace) {
175 176 177
			for (i = 0; i < data.callchain->nr; i++)
				dump_printf("..... %2d: %016Lx\n",
					    i, data.callchain->ips[i]);
178 179 180
		}
	}

181 182
	if (event__preprocess_sample(event, session, &al, NULL) < 0) {
		fprintf(stderr, "problem processing %d event, skipping it.\n",
183 184 185
			event->header.type);
		return -1;
	}
186

187
	if (al.filtered || (hide_unresolved && al.sym == NULL))
188
		return 0;
189

190
	if (perf_session__add_hist_entry(session, &al, &data)) {
191
		pr_debug("problem incrementing symbol period, skipping event\n");
192
		return -1;
193
	}
194

195 196 197
	attr = perf_header__find_attr(data.id, &session->header);

	if (add_event_total(session, &data, attr)) {
198
		pr_debug("problem adding event period\n");
199 200 201
		return -1;
	}

202 203
	return 0;
}
I
Ingo Molnar 已提交
204

205
static int process_read_event(event_t *event, struct perf_session *session __used)
206
{
207
	struct perf_event_attr *attr;
208

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

211
	if (show_threads) {
212
		const char *name = attr ? __event_name(attr->type, attr->config)
213 214 215 216 217 218 219 220
				   : "unknown";
		perf_read_values_add_value(&show_threads_values,
					   event->read.pid, event->read.tid,
					   event->read.id,
					   name,
					   event->read.value);
	}

221 222 223
	dump_printf(": %d %d %s %Lu\n", event->read.pid, event->read.tid,
		    attr ? __event_name(attr->type, attr->config) : "FAIL",
		    event->read.value);
224 225 226 227

	return 0;
}

228
static int perf_session__setup_sample_type(struct perf_session *self)
229
{
230
	if (!(self->sample_type & PERF_SAMPLE_CALLCHAIN)) {
231 232 233 234
		if (sort__has_parent) {
			fprintf(stderr, "selected --sort parent, but no"
					" callchain data. Did you call"
					" perf record without -g?\n");
235
			return -EINVAL;
236
		}
237
		if (symbol_conf.use_callchain) {
238
			fprintf(stderr, "selected -g but no callchain data."
239 240
					" Did you call perf record without"
					" -g?\n");
241
			return -1;
242
		}
243 244
	} else if (!dont_use_callchains && callchain_param.mode != CHAIN_NONE &&
		   !symbol_conf.use_callchain) {
245
			symbol_conf.use_callchain = true;
246 247 248
			if (register_callchain_param(&callchain_param) < 0) {
				fprintf(stderr, "Can't register callchain"
						" params\n");
249
				return -EINVAL;
250
			}
251 252
	}

253 254
	return 0;
}
255

256
static struct perf_event_ops event_ops = {
257 258 259 260 261 262 263
	.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,
264
	.attr	= event__process_attr,
265
	.event_type = event__process_event_type,
266
	.tracing_data = event__process_tracing_data,
267
	.build_id = event__process_build_id,
268
};
269

270 271
extern volatile int session_done;

272
static void sig_handler(int sig __used)
273 274 275 276
{
	session_done = 1;
}

277 278 279 280 281 282 283 284 285 286 287 288 289 290
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");
}

291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322
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;
}

323 324
static int __cmd_report(void)
{
325
	int ret = -EINVAL;
326
	struct perf_session *session;
327
	struct rb_node *next;
328
	const char *help = "For a higher level overview, try: perf report --sort comm,dso";
329

330 331
	signal(SIGINT, sig_handler);

T
Tom Zanussi 已提交
332
	session = perf_session__new(input_name, O_RDONLY, force, false);
333 334 335
	if (session == NULL)
		return -ENOMEM;

336 337
	if (show_threads)
		perf_read_values_init(&show_threads_values);
338

339 340 341 342
	ret = perf_session__setup_sample_type(session);
	if (ret)
		goto out_delete;

343
	ret = perf_session__process_events(session, &event_ops);
344
	if (ret)
345
		goto out_delete;
346

347
	if (dump_trace) {
348
		perf_session__fprintf_nr_events(session, stdout);
349
		goto out_delete;
350
	}
351

352
	if (verbose > 3)
353
		perf_session__fprintf(session, stdout);
354

355
	if (verbose > 2)
356
		perf_session__fprintf_dsos(session, stdout);
357

358
	next = rb_first(&session->hists_tree);
359
	while (next) {
360
		struct hists *hists;
361

362 363
		hists = rb_entry(next, struct hists, rb_node);
		hists__collapse_resort(hists);
364
		hists__output_resort(hists);
365
		next = rb_next(&hists->rb_node);
366 367
	}

368 369 370 371
	if (use_browser > 0)
		hists__tui_browse_tree(&session->hists_tree, help);
	else
		hists__tty_browse_tree(&session->hists_tree, help);
372

373 374
out_delete:
	perf_session__delete(session);
375
	return ret;
376 377
}

378 379
static int
parse_callchain_opt(const struct option *opt __used, const char *arg,
380
		    int unset)
381
{
382
	char *tok, *tok2;
383 384
	char *endptr;

385 386 387 388 389 390 391 392
	/*
	 * --no-call-graph
	 */
	if (unset) {
		dont_use_callchains = true;
		return 0;
	}

393
	symbol_conf.use_callchain = true;
394 395 396 397

	if (!arg)
		return 0;

398 399 400 401 402 403
	tok = strtok((char *)arg, ",");
	if (!tok)
		return -1;

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

406
	else if (!strncmp(tok, "flat", strlen(arg)))
407 408 409 410 411
		callchain_param.mode = CHAIN_FLAT;

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

412 413
	else if (!strncmp(tok, "none", strlen(arg))) {
		callchain_param.mode = CHAIN_NONE;
414
		symbol_conf.use_callchain = false;
415 416 417 418

		return 0;
	}

419 420 421
	else
		return -1;

422 423 424
	/* get the min percentage */
	tok = strtok(NULL, ",");
	if (!tok)
425
		goto setup;
426

427
	tok2 = strtok(NULL, ",");
428
	callchain_param.min_percent = strtod(tok, &endptr);
429 430 431
	if (tok == endptr)
		return -1;

432 433
	if (tok2)
		callchain_param.print_limit = strtod(tok2, &endptr);
434 435 436 437 438
setup:
	if (register_callchain_param(&callchain_param) < 0) {
		fprintf(stderr, "Can't register callchain params\n");
		return -1;
	}
439 440 441
	return 0;
}

442
static const char * const report_usage[] = {
443 444 445 446 447 448 449
	"perf report [<options>] <command>",
	NULL
};

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

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

499 500
	if (strcmp(input_name, "-") != 0)
		setup_browser();
501 502 503 504 505
	/*
	 * 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.
	 */
506
	if (use_browser > 0)
507
		symbol_conf.priv_size = sizeof(struct sym_priv);
508

509
	if (symbol__init() < 0)
510
		return -1;
511

512
	setup_sorting(report_usage, options);
513

514
	if (parent_pattern != default_parent_pattern) {
515 516
		if (sort_dimension__add("parent") < 0)
			return -1;
517 518
		sort_parent.elide = 1;
	} else
519
		symbol_conf.exclude_other = false;
520

521 522 523 524 525 526
	/*
	 * Any (unrecognized) arguments left?
	 */
	if (argc)
		usage_with_options(report_usage, options);

527 528 529
	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);
530

531 532
	return __cmd_report();
}