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);
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 162
	dump_printf("(IP, %d): %d/%d: %#Lx period: %Ld cpu:%d\n",
		    event->header.misc, data.pid, data.tid, data.ip,
		    data.period, data.cpu);
163

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	return 0;
}

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

254 255
	return 0;
}
256

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

271 272
extern volatile int session_done;

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

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

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

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

331 332
	signal(SIGINT, sig_handler);

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

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

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

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

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

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

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

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

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

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

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

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

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

394
	symbol_conf.use_callchain = true;
395 396 397 398

	if (!arg)
		return 0;

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

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

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

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

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

		return 0;
	}

420 421 422
	else
		return -1;

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

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

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

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

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

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

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

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

513
	setup_sorting(report_usage, options);
514

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

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

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

532 533
	return __cmd_report();
}