builtin-report.c 37.3 KB
Newer Older
1
// SPDX-License-Identifier: GPL-2.0
2 3 4 5 6 7 8
/*
 * 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.
 */
9
#include "builtin.h"
10

11
#include "util/util.h"
12
#include "util/config.h"
13

14
#include "util/annotate.h"
15
#include "util/color.h"
16
#include <linux/list.h>
17
#include <linux/rbtree.h>
18
#include <linux/err.h>
19
#include "util/symbol.h"
20
#include "util/callchain.h"
21
#include "util/values.h"
22

23
#include "perf.h"
24
#include "util/debug.h"
25 26
#include "util/evlist.h"
#include "util/evsel.h"
27
#include "util/header.h"
28
#include "util/session.h"
29
#include "util/tool.h"
30

31
#include <subcmd/parse-options.h>
32
#include <subcmd/exec-cmd.h>
33 34
#include "util/parse-events.h"

35
#include "util/thread.h"
36
#include "util/sort.h"
37
#include "util/hist.h"
38
#include "util/data.h"
39
#include "arch/common.h"
40
#include "util/time-utils.h"
41
#include "util/auxtrace.h"
42
#include "util/units.h"
43
#include "util/branch.h"
44

45
#include <dlfcn.h>
46
#include <errno.h>
47
#include <inttypes.h>
48
#include <regex.h>
49
#include <signal.h>
50
#include <linux/bitmap.h>
51
#include <linux/stringify.h>
52 53 54
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
55
#include <linux/mman.h>
56

57 58
#define PTIME_RANGE_MAX	10

59
struct report {
60
	struct perf_tool	tool;
61
	struct perf_session	*session;
62
	bool			use_tui, use_gtk, use_stdio;
63 64 65
	bool			show_full_info;
	bool			show_threads;
	bool			inverted_callchain;
66
	bool			mem_mode;
67
	bool			stats_mode;
68
	bool			tasks_mode;
69
	bool			mmaps_mode;
70 71
	bool			header;
	bool			header_only;
72
	bool			nonany_branch_mode;
73
	int			max_stack;
74 75 76
	struct perf_read_values	show_threads_values;
	const char		*pretty_printing_style;
	const char		*cpu_list;
77
	const char		*symbol_filter_str;
78
	const char		*time_str;
79 80
	struct perf_time_interval ptime_range[PTIME_RANGE_MAX];
	int			range_num;
81
	float			min_percent;
82
	u64			nr_entries;
83
	u64			queue_size;
84
	int			socket_filter;
85
	DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
86
	struct branch_type_stat	brtype_stat;
87
};
88

89
static int report__config(const char *var, const char *value, void *cb)
90
{
91 92
	struct report *rep = cb;

93 94 95 96
	if (!strcmp(var, "report.group")) {
		symbol_conf.event_group = perf_config_bool(var, value);
		return 0;
	}
97
	if (!strcmp(var, "report.percent-limit")) {
98 99 100 101
		double pcnt = strtof(value, NULL);

		rep->min_percent = pcnt;
		callchain_param.min_percent = pcnt;
102 103
		return 0;
	}
104 105 106 107
	if (!strcmp(var, "report.children")) {
		symbol_conf.cumulate_callchain = perf_config_bool(var, value);
		return 0;
	}
108 109 110
	if (!strcmp(var, "report.queue-size"))
		return perf_config_u64(&rep->queue_size, var, value);

111 112 113 114
	if (!strcmp(var, "report.sort_order")) {
		default_sort_order = strdup(value);
		return 0;
	}
115

116
	return 0;
117 118
}

119 120 121 122 123 124 125 126
static int hist_iter__report_callback(struct hist_entry_iter *iter,
				      struct addr_location *al, bool single,
				      void *arg)
{
	int err = 0;
	struct report *rep = arg;
	struct hist_entry *he = iter->he;
	struct perf_evsel *evsel = iter->evsel;
127
	struct perf_sample *sample = iter->sample;
128 129 130 131 132 133
	struct mem_info *mi;
	struct branch_info *bi;

	if (!ui__has_annotation())
		return 0;

134
	hist__account_cycles(sample->branch_stack, al, sample,
135 136
			     rep->nonany_branch_mode);

137 138
	if (sort__mode == SORT_MODE__BRANCH) {
		bi = he->branch_info;
139
		err = addr_map_symbol__inc_samples(&bi->from, sample, evsel->idx);
140 141 142
		if (err)
			goto out;

143
		err = addr_map_symbol__inc_samples(&bi->to, sample, evsel->idx);
144 145 146

	} else if (rep->mem_mode) {
		mi = he->mem_info;
147
		err = addr_map_symbol__inc_samples(&mi->daddr, sample, evsel->idx);
148 149 150
		if (err)
			goto out;

151
		err = hist_entry__inc_addr_samples(he, sample, evsel->idx, al->addr);
152 153 154

	} else if (symbol_conf.cumulate_callchain) {
		if (single)
155
			err = hist_entry__inc_addr_samples(he, sample, evsel->idx,
156 157
							   al->addr);
	} else {
158
		err = hist_entry__inc_addr_samples(he, sample, evsel->idx, al->addr);
159 160 161 162
	}

out:
	return err;
163 164
}

165 166 167 168 169 170 171 172
static int hist_iter__branch_callback(struct hist_entry_iter *iter,
				      struct addr_location *al __maybe_unused,
				      bool single __maybe_unused,
				      void *arg)
{
	struct hist_entry *he = iter->he;
	struct report *rep = arg;
	struct branch_info *bi;
173 174 175 176 177 178 179 180 181
	struct perf_sample *sample = iter->sample;
	struct perf_evsel *evsel = iter->evsel;
	int err;

	if (!ui__has_annotation())
		return 0;

	hist__account_cycles(sample->branch_stack, al, sample,
			     rep->nonany_branch_mode);
182 183

	bi = he->branch_info;
184 185 186 187 188 189
	err = addr_map_symbol__inc_samples(&bi->from, sample, evsel->idx);
	if (err)
		goto out;

	err = addr_map_symbol__inc_samples(&bi->to, sample, evsel->idx);

190 191 192
	branch_type_count(&rep->brtype_stat, &bi->flags,
			  bi->from.addr, bi->to.addr);

193 194
out:
	return err;
195 196
}

197
static int process_sample_event(struct perf_tool *tool,
198
				union perf_event *event,
199
				struct perf_sample *sample,
200
				struct perf_evsel *evsel,
201
				struct machine *machine)
202
{
203
	struct report *rep = container_of(tool, struct report, tool);
204
	struct addr_location al;
205
	struct hist_entry_iter iter = {
206 207
		.evsel 			= evsel,
		.sample 		= sample,
208
		.hide_unresolved 	= symbol_conf.hide_unresolved,
209
		.add_entry_cb 		= hist_iter__report_callback,
210
	};
211
	int ret = 0;
212

213 214
	if (perf_time__ranges_skip_sample(rep->ptime_range, rep->range_num,
					  sample->time)) {
215
		return 0;
216
	}
217

218
	if (machine__resolve(machine, &al, sample) < 0) {
219 220
		pr_debug("problem processing %d event, skipping it.\n",
			 event->header.type);
221 222
		return -1;
	}
223

224
	if (symbol_conf.hide_unresolved && al.sym == NULL)
225
		goto out_put;
226

227
	if (rep->cpu_list && !test_bit(sample->cpu, rep->cpu_bitmap))
228
		goto out_put;
229

230 231 232 233 234 235 236
	if (sort__mode == SORT_MODE__BRANCH) {
		/*
		 * A non-synthesized event might not have a branch stack if
		 * branch stacks have been synthesized (using itrace options).
		 */
		if (!sample->branch_stack)
			goto out_put;
237 238

		iter.add_entry_cb = hist_iter__branch_callback;
239
		iter.ops = &hist_iter_branch;
240
	} else if (rep->mem_mode) {
241
		iter.ops = &hist_iter_mem;
242
	} else if (symbol_conf.cumulate_callchain) {
243
		iter.ops = &hist_iter_cumulative;
244
	} else {
245
		iter.ops = &hist_iter_normal;
246
	}
247 248 249 250

	if (al.map != NULL)
		al.map->dso->hit = 1;

251
	ret = hist_entry_iter__add(&iter, &al, rep->max_stack, rep);
252 253
	if (ret < 0)
		pr_debug("problem adding hist entry, skipping event\n");
254 255
out_put:
	addr_location__put(&al);
256
	return ret;
257
}
I
Ingo Molnar 已提交
258

259
static int process_read_event(struct perf_tool *tool,
260
			      union perf_event *event,
261
			      struct perf_sample *sample __maybe_unused,
262
			      struct perf_evsel *evsel,
263
			      struct machine *machine __maybe_unused)
264
{
265
	struct report *rep = container_of(tool, struct report, tool);
266

267
	if (rep->show_threads) {
268
		const char *name = evsel ? perf_evsel__name(evsel) : "unknown";
269
		int err = perf_read_values_add_value(&rep->show_threads_values,
270
					   event->read.pid, event->read.tid,
271
					   evsel->idx,
272 273
					   name,
					   event->read.value);
274 275 276

		if (err)
			return err;
277 278
	}

279 280 281
	return 0;
}

282
/* For pipe mode, sample_type is not currently set */
283
static int report__setup_sample_type(struct report *rep)
284
{
285 286
	struct perf_session *session = rep->session;
	u64 sample_type = perf_evlist__combined_sample_type(session->evlist);
287
	bool is_pipe = perf_data__is_pipe(session->data);
288

289 290 291 292 293 294
	if (session->itrace_synth_opts->callchain ||
	    (!is_pipe &&
	     perf_header__has_feat(&session->header, HEADER_AUXTRACE) &&
	     !session->itrace_synth_opts->set))
		sample_type |= PERF_SAMPLE_CALLCHAIN;

295 296 297
	if (session->itrace_synth_opts->last_branch)
		sample_type |= PERF_SAMPLE_BRANCH_STACK;

298
	if (!is_pipe && !(sample_type & PERF_SAMPLE_CALLCHAIN)) {
299
		if (perf_hpp_list.parent) {
300
			ui__error("Selected --sort parent, but no "
301 302
				    "callchain data. Did you call "
				    "'perf record' without -g?\n");
303
			return -EINVAL;
304
		}
305 306 307 308 309
		if (symbol_conf.use_callchain &&
			!symbol_conf.show_branchflag_count) {
			ui__error("Selected -g or --branch-history.\n"
				  "But no callchain or branch data.\n"
				  "Did you call 'perf record' without -g or -b?\n");
310
			return -1;
311
		}
312
	} else if (!callchain_param.enabled &&
313
		   callchain_param.mode != CHAIN_NONE &&
314
		   !symbol_conf.use_callchain) {
315
			symbol_conf.use_callchain = true;
316
			if (callchain_register_param(&callchain_param) < 0) {
317
				ui__error("Can't register callchain params.\n");
318
				return -EINVAL;
319
			}
320 321
	}

322 323 324 325 326 327 328 329
	if (symbol_conf.cumulate_callchain) {
		/* Silently ignore if callchain is missing */
		if (!(sample_type & PERF_SAMPLE_CALLCHAIN)) {
			symbol_conf.cumulate_callchain = false;
			perf_hpp__cancel_cumulate();
		}
	}

330
	if (sort__mode == SORT_MODE__BRANCH) {
331
		if (!is_pipe &&
332
		    !(sample_type & PERF_SAMPLE_BRANCH_STACK)) {
333 334
			ui__error("Selected -b but no branch data. "
				  "Did you call perf record without -b?\n");
335 336 337 338
			return -1;
		}
	}

339 340
	if (symbol_conf.use_callchain || symbol_conf.cumulate_callchain) {
		if ((sample_type & PERF_SAMPLE_REGS_USER) &&
341
		    (sample_type & PERF_SAMPLE_STACK_USER)) {
342
			callchain_param.record_mode = CALLCHAIN_DWARF;
343 344
			dwarf_callchain_users = true;
		} else if (sample_type & PERF_SAMPLE_BRANCH_STACK)
345
			callchain_param.record_mode = CALLCHAIN_LBR;
346 347 348
		else
			callchain_param.record_mode = CALLCHAIN_FP;
	}
349 350 351 352 353 354

	/* ??? handle more cases than just ANY? */
	if (!(perf_evlist__combined_branch_type(session->evlist) &
				PERF_SAMPLE_BRANCH_ANY))
		rep->nonany_branch_mode = true;

355 356
	return 0;
}
357

358
static void sig_handler(int sig __maybe_unused)
359 360 361 362
{
	session_done = 1;
}

363
static size_t hists__fprintf_nr_sample_events(struct hists *hists, struct report *rep,
364 365 366 367
					      const char *evname, FILE *fp)
{
	size_t ret;
	char unit;
368 369 370
	unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
	u64 nr_events = hists->stats.total_period;
	struct perf_evsel *evsel = hists_to_evsel(hists);
371 372
	char buf[512];
	size_t size = sizeof(buf);
373
	int socked_id = hists->socket_filter;
374

375 376 377
	if (quiet)
		return 0;

378 379 380 381 382
	if (symbol_conf.filter_relative) {
		nr_samples = hists->stats.nr_non_filtered_samples;
		nr_events = hists->stats.total_non_filtered_period;
	}

383
	if (perf_evsel__is_group_event(evsel)) {
384 385 386 387 388 389
		struct perf_evsel *pos;

		perf_evsel__group_desc(evsel, buf, size);
		evname = buf;

		for_each_group_member(pos, evsel) {
390 391
			const struct hists *pos_hists = evsel__hists(pos);

392
			if (symbol_conf.filter_relative) {
393 394
				nr_samples += pos_hists->stats.nr_non_filtered_samples;
				nr_events += pos_hists->stats.total_non_filtered_period;
395
			} else {
396 397
				nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
				nr_events += pos_hists->stats.total_period;
398
			}
399 400
		}
	}
401

402 403
	nr_samples = convert_unit(nr_samples, &unit);
	ret = fprintf(fp, "# Samples: %lu%c", nr_samples, unit);
404
	if (evname != NULL)
405 406
		ret += fprintf(fp, " of event '%s'", evname);

407 408 409 410 411
	if (symbol_conf.show_ref_callgraph &&
	    strstr(evname, "call-graph=no")) {
		ret += fprintf(fp, ", show reference callgraph");
	}

412 413
	if (rep->mem_mode) {
		ret += fprintf(fp, "\n# Total weight : %" PRIu64, nr_events);
414
		ret += fprintf(fp, "\n# Sort order   : %s", sort_order ? : default_mem_sort_order);
415 416
	} else
		ret += fprintf(fp, "\n# Event count (approx.): %" PRIu64, nr_events);
417

418 419
	if (socked_id > -1)
		ret += fprintf(fp, "\n# Processor Socket: %d", socked_id);
420

421 422 423
	return ret + fprintf(fp, "\n#\n");
}

424
static int perf_evlist__tty_browse_hists(struct perf_evlist *evlist,
425
					 struct report *rep,
426
					 const char *help)
427
{
428
	struct perf_evsel *pos;
429

430 431 432 433 434
	if (!quiet) {
		fprintf(stdout, "#\n# Total Lost Samples: %" PRIu64 "\n#\n",
			evlist->stats.total_lost_samples);
	}

435
	evlist__for_each_entry(evlist, pos) {
436
		struct hists *hists = evsel__hists(pos);
437
		const char *evname = perf_evsel__name(pos);
438

439 440 441 442
		if (symbol_conf.event_group &&
		    !perf_evsel__is_group_leader(pos))
			continue;

443
		hists__fprintf_nr_sample_events(hists, rep, evname, stdout);
444
		hists__fprintf(hists, !quiet, 0, 0, rep->min_percent, stdout,
445 446
			       symbol_conf.use_callchain ||
			       symbol_conf.show_branchflag_count);
447 448 449
		fprintf(stdout, "\n\n");
	}

450
	if (!quiet)
451 452
		fprintf(stdout, "#\n# (%s)\n#\n", help);

453 454 455 456 457
	if (rep->show_threads) {
		bool style = !strcmp(rep->pretty_printing_style, "raw");
		perf_read_values_display(stdout, &rep->show_threads_values,
					 style);
		perf_read_values_destroy(&rep->show_threads_values);
458 459
	}

460 461 462
	if (sort__mode == SORT_MODE__BRANCH)
		branch_type_stat_display(stdout, &rep->brtype_stat);

463 464 465
	return 0;
}

466 467
static void report__warn_kptr_restrict(const struct report *rep)
{
468
	struct map *kernel_map = machine__kernel_map(&rep->session->machines.host);
469
	struct kmap *kernel_kmap = kernel_map ? map__kmap(kernel_map) : NULL;
470

471 472 473
	if (perf_evlist__exclude_kernel(rep->session->evlist))
		return;

474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497
	if (kernel_map == NULL ||
	    (kernel_map->dso->hit &&
	     (kernel_kmap->ref_reloc_sym == NULL ||
	      kernel_kmap->ref_reloc_sym->addr == 0))) {
		const char *desc =
		    "As no suitable kallsyms nor vmlinux was found, kernel samples\n"
		    "can't be resolved.";

		if (kernel_map) {
			const struct dso *kdso = kernel_map->dso;
			if (!RB_EMPTY_ROOT(&kdso->symbols[MAP__FUNCTION])) {
				desc = "If some relocation was applied (e.g. "
				       "kexec) symbols may be misresolved.";
			}
		}

		ui__warning(
"Kernel address maps (/proc/{kallsyms,modules}) were restricted.\n\n"
"Check /proc/sys/kernel/kptr_restrict before running 'perf record'.\n\n%s\n\n"
"Samples in kernel modules can't be resolved as well.\n\n",
		desc);
	}
}

498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517
static int report__gtk_browse_hists(struct report *rep, const char *help)
{
	int (*hist_browser)(struct perf_evlist *evlist, const char *help,
			    struct hist_browser_timer *timer, float min_pcnt);

	hist_browser = dlsym(perf_gtk_handle, "perf_evlist__gtk_browse_hists");

	if (hist_browser == NULL) {
		ui__error("GTK browser not found!\n");
		return -1;
	}

	return hist_browser(rep->session->evlist, help, NULL, rep->min_percent);
}

static int report__browse_hists(struct report *rep)
{
	int ret;
	struct perf_session *session = rep->session;
	struct perf_evlist *evlist = session->evlist;
518 519 520 521 522 523 524 525
	const char *help = perf_tip(system_path(TIPDIR));

	if (help == NULL) {
		/* fallback for people who don't install perf ;-) */
		help = perf_tip(DOCDIR);
		if (help == NULL)
			help = "Cannot load tips.txt file, please install perf!";
	}
526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549

	switch (use_browser) {
	case 1:
		ret = perf_evlist__tui_browse_hists(evlist, help, NULL,
						    rep->min_percent,
						    &session->header.env);
		/*
		 * Usually "ret" is the last pressed key, and we only
		 * care if the key notifies us to switch data file.
		 */
		if (ret != K_SWITCH_INPUT_DATA)
			ret = 0;
		break;
	case 2:
		ret = report__gtk_browse_hists(rep, help);
		break;
	default:
		ret = perf_evlist__tty_browse_hists(evlist, rep, help);
		break;
	}

	return ret;
}

550
static int report__collapse_hists(struct report *rep)
551 552 553
{
	struct ui_progress prog;
	struct perf_evsel *pos;
554
	int ret = 0;
555

556
	ui_progress__init(&prog, rep->nr_entries, "Merging related events...");
557

558
	evlist__for_each_entry(rep->session->evlist, pos) {
559
		struct hists *hists = evsel__hists(pos);
560 561 562 563

		if (pos->idx == 0)
			hists->symbol_filter_str = rep->symbol_filter_str;

564 565
		hists->socket_filter = rep->socket_filter;

566 567 568
		ret = hists__collapse_resort(hists, &prog);
		if (ret < 0)
			break;
569 570 571 572

		/* Non-group events are considered as leader */
		if (symbol_conf.event_group &&
		    !perf_evsel__is_group_leader(pos)) {
573
			struct hists *leader_hists = evsel__hists(pos->leader);
574 575 576 577 578 579 580

			hists__match(leader_hists, hists);
			hists__link(leader_hists, hists);
		}
	}

	ui_progress__finish();
581
	return ret;
582 583
}

584 585 586 587 588 589 590
static void report__output_resort(struct report *rep)
{
	struct ui_progress prog;
	struct perf_evsel *pos;

	ui_progress__init(&prog, rep->nr_entries, "Sorting events for output...");

591
	evlist__for_each_entry(rep->session->evlist, pos)
592
		perf_evsel__output_resort(pos, &prog);
593 594 595 596

	ui_progress__finish();
}

597 598 599 600 601 602 603 604 605 606 607 608 609 610
static void stats_setup(struct report *rep)
{
	memset(&rep->tool, 0, sizeof(rep->tool));
	rep->tool.no_warn = true;
}

static int stats_print(struct report *rep)
{
	struct perf_session *session = rep->session;

	perf_session__fprintf_nr_events(session, stdout);
	return 0;
}

611 612 613
static void tasks_setup(struct report *rep)
{
	memset(&rep->tool, 0, sizeof(rep->tool));
614 615 616 617
	if (rep->mmaps_mode) {
		rep->tool.mmap = perf_event__process_mmap;
		rep->tool.mmap2 = perf_event__process_mmap2;
	}
618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651
	rep->tool.comm = perf_event__process_comm;
	rep->tool.exit = perf_event__process_exit;
	rep->tool.fork = perf_event__process_fork;
	rep->tool.no_warn = true;
}

struct task {
	struct thread		*thread;
	struct list_head	 list;
	struct list_head	 children;
};

static struct task *tasks_list(struct task *task, struct machine *machine)
{
	struct thread *parent_thread, *thread = task->thread;
	struct task   *parent_task;

	/* Already listed. */
	if (!list_empty(&task->list))
		return NULL;

	/* Last one in the chain. */
	if (thread->ppid == -1)
		return task;

	parent_thread = machine__find_thread(machine, -1, thread->ppid);
	if (!parent_thread)
		return ERR_PTR(-ENOENT);

	parent_task = thread__priv(parent_thread);
	list_add_tail(&task->list, &parent_task->children);
	return tasks_list(parent_task, machine);
}

652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680
static size_t maps__fprintf_task(struct maps *maps, int indent, FILE *fp)
{
	size_t printed = 0;
	struct rb_node *nd;

	for (nd = rb_first(&maps->entries); nd; nd = rb_next(nd)) {
		struct map *map = rb_entry(nd, struct map, rb_node);

		printed += fprintf(fp, "%*s  %" PRIx64 "-%" PRIx64 " %c%c%c%c %08" PRIx64 " %" PRIu64 " %s\n",
				   indent, "", map->start, map->end,
				   map->prot & PROT_READ ? 'r' : '-',
				   map->prot & PROT_WRITE ? 'w' : '-',
				   map->prot & PROT_EXEC ? 'x' : '-',
				   map->flags & MAP_SHARED ? 's' : 'p',
				   map->pgoff,
				   map->ino, map->dso->name);
	}

	return printed;
}

static int map_groups__fprintf_task(struct map_groups *mg, int indent, FILE *fp)
{
	int printed = 0, i;
	for (i = 0; i < MAP__NR_TYPES; ++i)
		printed += maps__fprintf_task(&mg->maps[i], indent, fp);
	return printed;
}

681 682 683 684
static void task__print_level(struct task *task, FILE *fp, int level)
{
	struct thread *thread = task->thread;
	struct task *child;
685 686 687 688 689
	int comm_indent = fprintf(fp, "  %8d %8d %8d |%*s",
				  thread->pid_, thread->tid, thread->ppid,
				  level, "");

	fprintf(fp, "%s\n", thread__comm_str(thread));
690

691
	map_groups__fprintf_task(thread->mg, comm_indent, fp);
692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764

	if (!list_empty(&task->children)) {
		list_for_each_entry(child, &task->children, list)
			task__print_level(child, fp, level + 1);
	}
}

static int tasks_print(struct report *rep, FILE *fp)
{
	struct perf_session *session = rep->session;
	struct machine      *machine = &session->machines.host;
	struct task *tasks, *task;
	unsigned int nr = 0, itask = 0, i;
	struct rb_node *nd;
	LIST_HEAD(list);

	/*
	 * No locking needed while accessing machine->threads,
	 * because --tasks is single threaded command.
	 */

	/* Count all the threads. */
	for (i = 0; i < THREADS__TABLE_SIZE; i++)
		nr += machine->threads[i].nr;

	tasks = malloc(sizeof(*tasks) * nr);
	if (!tasks)
		return -ENOMEM;

	for (i = 0; i < THREADS__TABLE_SIZE; i++) {
		struct threads *threads = &machine->threads[i];

		for (nd = rb_first(&threads->entries); nd; nd = rb_next(nd)) {
			task = tasks + itask++;

			task->thread = rb_entry(nd, struct thread, rb_node);
			INIT_LIST_HEAD(&task->children);
			INIT_LIST_HEAD(&task->list);
			thread__set_priv(task->thread, task);
		}
	}

	/*
	 * Iterate every task down to the unprocessed parent
	 * and link all in task children list. Task with no
	 * parent is added into 'list'.
	 */
	for (itask = 0; itask < nr; itask++) {
		task = tasks + itask;

		if (!list_empty(&task->list))
			continue;

		task = tasks_list(task, machine);
		if (IS_ERR(task)) {
			pr_err("Error: failed to process tasks\n");
			free(tasks);
			return PTR_ERR(task);
		}

		if (task)
			list_add_tail(&task->list, &list);
	}

	fprintf(fp, "# %8s %8s %8s  %s\n", "pid", "tid", "ppid", "comm");

	list_for_each_entry(task, &list, list)
		task__print_level(task, fp, 0);

	free(tasks);
	return 0;
}

765
static int __cmd_report(struct report *rep)
766
{
767
	int ret;
768
	struct perf_session *session = rep->session;
769
	struct perf_evsel *pos;
770
	struct perf_data *data = session->data;
771

772 773
	signal(SIGINT, sig_handler);

774 775 776
	if (rep->cpu_list) {
		ret = perf_session__cpu_bitmap(session, rep->cpu_list,
					       rep->cpu_bitmap);
777 778
		if (ret) {
			ui__error("failed to set cpu bitmap\n");
779
			return ret;
780
		}
781
		session->itrace_synth_opts->cpu_bitmap = rep->cpu_bitmap;
782 783
	}

784 785 786 787 788
	if (rep->show_threads) {
		ret = perf_read_values_init(&rep->show_threads_values);
		if (ret)
			return ret;
	}
789

790
	ret = report__setup_sample_type(rep);
791 792
	if (ret) {
		/* report__setup_sample_type() already showed error message */
793
		return ret;
794
	}
795

796 797 798
	if (rep->stats_mode)
		stats_setup(rep);

799 800 801
	if (rep->tasks_mode)
		tasks_setup(rep);

802
	ret = perf_session__process_events(session);
803 804
	if (ret) {
		ui__error("failed to process sample\n");
805
		return ret;
806
	}
807

808 809 810
	if (rep->stats_mode)
		return stats_print(rep);

811 812 813
	if (rep->tasks_mode)
		return tasks_print(rep, stdout);

814
	report__warn_kptr_restrict(rep);
815

816
	evlist__for_each_entry(session->evlist, pos)
817 818
		rep->nr_entries += evsel__hists(pos)->nr_entries;

819 820 821
	if (use_browser == 0) {
		if (verbose > 3)
			perf_session__fprintf(session, stdout);
822

823 824
		if (verbose > 2)
			perf_session__fprintf_dsos(session, stdout);
825

826 827
		if (dump_trace) {
			perf_session__fprintf_nr_events(session, stdout);
828
			perf_evlist__fprintf_nr_events(session->evlist, stdout);
829 830
			return 0;
		}
831 832
	}

833 834 835 836 837
	ret = report__collapse_hists(rep);
	if (ret) {
		ui__error("failed to process hist entry\n");
		return ret;
	}
838

839 840 841
	if (session_done())
		return 0;

842 843 844 845 846
	/*
	 * recalculate number of entries after collapsing since it
	 * might be changed during the collapse phase.
	 */
	rep->nr_entries = 0;
847
	evlist__for_each_entry(session->evlist, pos)
848 849
		rep->nr_entries += evsel__hists(pos)->nr_entries;

850
	if (rep->nr_entries == 0) {
J
Jiri Olsa 已提交
851
		ui__error("The %s file has no samples!\n", data->file.path);
852
		return 0;
853 854
	}

855
	report__output_resort(rep);
856

857
	return report__browse_hists(rep);
858 859
}

860
static int
861
report_parse_callchain_opt(const struct option *opt, const char *arg, int unset)
862
{
863
	struct callchain_param *callchain = opt->value;
864

865
	callchain->enabled = !unset;
866 867 868 869
	/*
	 * --no-call-graph
	 */
	if (unset) {
870 871
		symbol_conf.use_callchain = false;
		callchain->mode = CHAIN_NONE;
872 873 874
		return 0;
	}

875
	return parse_callchain_report_opt(arg);
876 877
}

878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895
int
report_parse_ignore_callees_opt(const struct option *opt __maybe_unused,
				const char *arg, int unset __maybe_unused)
{
	if (arg) {
		int err = regcomp(&ignore_callees_regex, arg, REG_EXTENDED);
		if (err) {
			char buf[BUFSIZ];
			regerror(err, &ignore_callees_regex, buf, sizeof(buf));
			pr_err("Invalid --ignore-callees regex: %s\n%s", arg, buf);
			return -1;
		}
		have_ignore_callees = 1;
	}

	return 0;
}

896
static int
897
parse_branch_mode(const struct option *opt,
898
		  const char *str __maybe_unused, int unset)
899
{
900 901 902
	int *branch_mode = opt->value;

	*branch_mode = !unset;
903 904 905
	return 0;
}

906 907 908 909
static int
parse_percent_limit(const struct option *opt, const char *str,
		    int unset __maybe_unused)
{
910
	struct report *rep = opt->value;
911
	double pcnt = strtof(str, NULL);
912

913 914
	rep->min_percent = pcnt;
	callchain_param.min_percent = pcnt;
915 916 917
	return 0;
}

918
#define CALLCHAIN_DEFAULT_OPT  "graph,0.5,caller,function,percent"
919 920 921 922

const char report_callchain_help[] = "Display call graph (stack chain/backtrace):\n\n"
				     CALLCHAIN_REPORT_HELP
				     "\n\t\t\t\tDefault: " CALLCHAIN_DEFAULT_OPT;
923

924
int cmd_report(int argc, const char **argv)
925
{
926
	struct perf_session *session;
927
	struct itrace_synth_opts itrace_synth_opts = { .set = 0, };
928
	struct stat st;
929
	bool has_br_stack = false;
930
	int branch_mode = -1;
931
	bool branch_call_mode = false;
932
	char callchain_default_opt[] = CALLCHAIN_DEFAULT_OPT;
933
	const char * const report_usage[] = {
N
Namhyung Kim 已提交
934
		"perf report [<options>]",
935 936
		NULL
	};
937
	struct report report = {
938
		.tool = {
939 940
			.sample		 = process_sample_event,
			.mmap		 = perf_event__process_mmap,
941
			.mmap2		 = perf_event__process_mmap2,
942
			.comm		 = perf_event__process_comm,
943
			.namespaces	 = perf_event__process_namespaces,
944 945
			.exit		 = perf_event__process_exit,
			.fork		 = perf_event__process_fork,
946 947 948 949 950
			.lost		 = perf_event__process_lost,
			.read		 = process_read_event,
			.attr		 = perf_event__process_attr,
			.tracing_data	 = perf_event__process_tracing_data,
			.build_id	 = perf_event__process_build_id,
951 952 953
			.id_index	 = perf_event__process_id_index,
			.auxtrace_info	 = perf_event__process_auxtrace_info,
			.auxtrace	 = perf_event__process_auxtrace,
954
			.feature	 = perf_event__process_feature,
955
			.ordered_events	 = true,
956 957
			.ordering_requires_timestamps = true,
		},
958
		.max_stack		 = PERF_MAX_STACK_DEPTH,
959
		.pretty_printing_style	 = "normal",
960
		.socket_filter		 = -1,
961 962
	};
	const struct option options[] = {
963
	OPT_STRING('i', "input", &input_name, "file",
964
		    "input file name"),
965
	OPT_INCR('v', "verbose", &verbose,
966
		    "be more verbose (show symbol address, etc)"),
967
	OPT_BOOLEAN('q', "quiet", &quiet, "Do not show any message"),
968 969
	OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
		    "dump raw trace in ASCII"),
970
	OPT_BOOLEAN(0, "stats", &report.stats_mode, "Display event stats"),
971
	OPT_BOOLEAN(0, "tasks", &report.tasks_mode, "Display recorded tasks"),
972
	OPT_BOOLEAN(0, "mmaps", &report.mmaps_mode, "Display recorded tasks memory maps"),
973 974
	OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
		   "file", "vmlinux pathname"),
975 976
	OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name,
		   "file", "kallsyms pathname"),
977
	OPT_BOOLEAN('f', "force", &symbol_conf.force, "don't complain, do it"),
978
	OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules,
979
		    "load module symbols - WARNING: use only with -k and LIVE kernel"),
980
	OPT_BOOLEAN('n', "show-nr-samples", &symbol_conf.show_nr_samples,
981
		    "Show a column with the number of samples"),
982
	OPT_BOOLEAN('T', "threads", &report.show_threads,
983
		    "Show per-thread event counters"),
984
	OPT_STRING(0, "pretty", &report.pretty_printing_style, "key",
985
		   "pretty printing style key: normal raw"),
986
	OPT_BOOLEAN(0, "tui", &report.use_tui, "Use the TUI interface"),
987
	OPT_BOOLEAN(0, "gtk", &report.use_gtk, "Use the GTK2 interface"),
988 989
	OPT_BOOLEAN(0, "stdio", &report.use_stdio,
		    "Use the stdio interface"),
990 991 992
	OPT_BOOLEAN(0, "header", &report.header, "Show data header."),
	OPT_BOOLEAN(0, "header-only", &report.header_only,
		    "Show only data header."),
993
	OPT_STRING('s', "sort", &sort_order, "key[,key2...]",
994 995
		   "sort by key(s): pid, comm, dso, symbol, parent, cpu, srcline, ..."
		   " Please refer the man page for the complete list."),
996 997
	OPT_STRING('F', "fields", &field_order, "key[,keys...]",
		   "output field(s): overhead, period, sample plus all of sort keys"),
998
	OPT_BOOLEAN(0, "show-cpu-utilization", &symbol_conf.show_cpu_utilization,
999
		    "Show sample percentage for different cpu modes"),
1000 1001
	OPT_BOOLEAN_FLAG(0, "showcpuutilization", &symbol_conf.show_cpu_utilization,
		    "Show sample percentage for different cpu modes", PARSE_OPT_HIDDEN),
1002 1003
	OPT_STRING('p', "parent", &parent_pattern, "regex",
		   "regex filter to identify parent, see: '--sort parent'"),
1004
	OPT_BOOLEAN('x', "exclude-other", &symbol_conf.exclude_other,
1005
		    "Only display entries with parent-match"),
1006
	OPT_CALLBACK_DEFAULT('g', "call-graph", &callchain_param,
1007
			     "print_type,threshold[,print_limit],order,sort_key[,branch],value",
1008 1009
			     report_callchain_help, &report_parse_callchain_opt,
			     callchain_default_opt),
1010 1011
	OPT_BOOLEAN(0, "children", &symbol_conf.cumulate_callchain,
		    "Accumulate callchains of children and show total overhead as well"),
1012 1013 1014
	OPT_INTEGER(0, "max-stack", &report.max_stack,
		    "Set the maximum stack depth when parsing the callchain, "
		    "anything beyond the specified depth will be ignored. "
1015
		    "Default: kernel.perf_event_max_stack or " __stringify(PERF_MAX_STACK_DEPTH)),
1016 1017
	OPT_BOOLEAN('G', "inverted", &report.inverted_callchain,
		    "alias for inverted call graph"),
1018 1019 1020
	OPT_CALLBACK(0, "ignore-callees", NULL, "regex",
		   "ignore callees of these functions in call graphs",
		   report_parse_ignore_callees_opt),
1021
	OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]",
1022
		   "only consider symbols in these dsos"),
1023
	OPT_STRING('c', "comms", &symbol_conf.comm_list_str, "comm[,comm...]",
1024
		   "only consider symbols in these comms"),
1025 1026 1027 1028
	OPT_STRING(0, "pid", &symbol_conf.pid_list_str, "pid[,pid...]",
		   "only consider symbols in these pids"),
	OPT_STRING(0, "tid", &symbol_conf.tid_list_str, "tid[,tid...]",
		   "only consider symbols in these tids"),
1029
	OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]",
1030
		   "only consider these symbols"),
1031 1032
	OPT_STRING(0, "symbol-filter", &report.symbol_filter_str, "filter",
		   "only show symbols that (partially) match with this filter"),
1033
	OPT_STRING('w', "column-widths", &symbol_conf.col_width_list_str,
1034 1035
		   "width[,width...]",
		   "don't try to adjust column width, use these fixed values"),
1036
	OPT_STRING_NOEMPTY('t', "field-separator", &symbol_conf.field_sep, "separator",
1037 1038
		   "separator for columns, no spaces will be added between "
		   "columns '.' is reserved."),
1039
	OPT_BOOLEAN('U', "hide-unresolved", &symbol_conf.hide_unresolved,
1040
		    "Only display entries resolved to a symbol"),
1041 1042 1043
	OPT_CALLBACK(0, "symfs", NULL, "directory",
		     "Look for files with symbols relative to this directory",
		     symbol__config_symfs),
1044
	OPT_STRING('C', "cpu", &report.cpu_list, "cpu",
1045 1046
		   "list of cpus to profile"),
	OPT_BOOLEAN('I', "show-info", &report.show_full_info,
1047
		    "Display extended information about perf.data file"),
1048 1049 1050 1051
	OPT_BOOLEAN(0, "source", &symbol_conf.annotate_src,
		    "Interleave source code with assembly code (default)"),
	OPT_BOOLEAN(0, "asm-raw", &symbol_conf.annotate_asm_raw,
		    "Display raw encoding of assembly instructions (default)"),
1052 1053
	OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style",
		   "Specify disassembler style (e.g. -M intel for intel syntax)"),
1054 1055
	OPT_BOOLEAN(0, "show-total-period", &symbol_conf.show_total_period,
		    "Show a column with the sum of periods"),
N
Namhyung Kim 已提交
1056 1057
	OPT_BOOLEAN(0, "group", &symbol_conf.event_group,
		    "Show event group information together"),
1058
	OPT_CALLBACK_NOOPT('b', "branch-stack", &branch_mode, "",
1059 1060 1061 1062
		    "use branch records for per branch histogram filling",
		    parse_branch_mode),
	OPT_BOOLEAN(0, "branch-history", &branch_call_mode,
		    "add last branch records to call history"),
1063 1064
	OPT_STRING(0, "objdump", &objdump_path, "path",
		   "objdump binary to use for disassembly and annotations"),
1065 1066
	OPT_BOOLEAN(0, "demangle", &symbol_conf.demangle,
		    "Disable symbol demangling"),
1067 1068
	OPT_BOOLEAN(0, "demangle-kernel", &symbol_conf.demangle_kernel,
		    "Enable kernel symbol demangling"),
1069
	OPT_BOOLEAN(0, "mem-mode", &report.mem_mode, "mem access profile"),
1070 1071
	OPT_CALLBACK(0, "percent-limit", &report, "percent",
		     "Don't show entries under that percent", parse_percent_limit),
1072
	OPT_CALLBACK(0, "percentage", NULL, "relative|absolute",
N
Namhyung Kim 已提交
1073
		     "how to display percentage of filtered entries", parse_filter_percentage),
1074 1075 1076
	OPT_CALLBACK_OPTARG(0, "itrace", &itrace_synth_opts, NULL, "opts",
			    "Instruction Tracing options",
			    itrace_parse_synth_opts),
1077 1078
	OPT_BOOLEAN(0, "full-source-path", &srcline_full_filename,
			"Show full source file name path for source lines"),
1079 1080
	OPT_BOOLEAN(0, "show-ref-call-graph", &symbol_conf.show_ref_callgraph,
		    "Show callgraph from reference event"),
1081 1082
	OPT_INTEGER(0, "socket-filter", &report.socket_filter,
		    "only show processor socket that match with this filter"),
1083 1084
	OPT_BOOLEAN(0, "raw-trace", &symbol_conf.raw_trace,
		    "Show raw trace event output (do not use print fmt or plugins)"),
1085 1086
	OPT_BOOLEAN(0, "hierarchy", &symbol_conf.report_hierarchy,
		    "Show entries in a hierarchy"),
1087 1088 1089
	OPT_CALLBACK_DEFAULT(0, "stdio-color", NULL, "mode",
			     "'always' (default), 'never' or 'auto' only applicable to --stdio mode",
			     stdio__config_color, "always"),
1090 1091
	OPT_STRING(0, "time", &report.time_str, "str",
		   "Time span of interest (start,stop)"),
J
Jin Yao 已提交
1092 1093
	OPT_BOOLEAN(0, "inline", &symbol_conf.inline_name,
		    "Show inline function"),
1094
	OPT_END()
1095
	};
1096
	struct perf_data data = {
1097 1098
		.mode  = PERF_DATA_MODE_READ,
	};
1099 1100 1101 1102
	int ret = hists__init();

	if (ret < 0)
		return ret;
1103

1104 1105 1106
	ret = perf_config(report__config, &report);
	if (ret)
		return ret;
1107

1108
	argc = parse_options(argc, argv, options, report_usage, 0);
1109 1110 1111 1112 1113 1114 1115 1116 1117 1118
	if (argc) {
		/*
		 * Special case: if there's an argument left then assume that
		 * it's a symbol filter:
		 */
		if (argc > 1)
			usage_with_options(report_usage, options);

		report.symbol_filter_str = argv[0];
	}
1119

1120 1121 1122
	if (report.mmaps_mode)
		report.tasks_mode = true;

1123 1124 1125
	if (quiet)
		perf_quiet_option();

1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136
	if (symbol_conf.vmlinux_name &&
	    access(symbol_conf.vmlinux_name, R_OK)) {
		pr_err("Invalid file: %s\n", symbol_conf.vmlinux_name);
		return -EINVAL;
	}
	if (symbol_conf.kallsyms_name &&
	    access(symbol_conf.kallsyms_name, R_OK)) {
		pr_err("Invalid file: %s\n", symbol_conf.kallsyms_name);
		return -EINVAL;
	}

1137
	if (report.inverted_callchain)
1138
		callchain_param.order = ORDER_CALLER;
1139 1140
	if (symbol_conf.cumulate_callchain && !callchain_param.order_set)
		callchain_param.order = ORDER_CALLER;
1141

1142 1143 1144 1145
	if (itrace_synth_opts.callchain &&
	    (int)itrace_synth_opts.callchain_sz > report.max_stack)
		report.max_stack = itrace_synth_opts.callchain_sz;

1146
	if (!input_name || !strlen(input_name)) {
1147
		if (!fstat(STDIN_FILENO, &st) && S_ISFIFO(st.st_mode))
1148
			input_name = "-";
1149
		else
1150
			input_name = "perf.data";
1151
	}
1152

J
Jiri Olsa 已提交
1153 1154
	data.file.path = input_name;
	data.force     = symbol_conf.force;
1155

1156
repeat:
1157
	session = perf_session__new(&data, false, &report.tool);
1158
	if (session == NULL)
1159
		return -1;
1160

1161 1162 1163 1164 1165
	if (report.queue_size) {
		ordered_events__set_alloc_size(&session->ordered_events,
					       report.queue_size);
	}

1166 1167
	session->itrace_synth_opts = &itrace_synth_opts;

1168 1169 1170 1171
	report.session = session;

	has_br_stack = perf_header__has_feat(&session->header,
					     HEADER_BRANCH_STACK);
1172

1173 1174 1175
	if (itrace_synth_opts.last_branch)
		has_br_stack = true;

1176 1177 1178
	if (has_br_stack && branch_call_mode)
		symbol_conf.show_branchflag_count = true;

1179 1180
	memset(&report.brtype_stat, 0, sizeof(struct branch_type_stat));

1181 1182 1183 1184 1185 1186
	/*
	 * Branch mode is a tristate:
	 * -1 means default, so decide based on the file having branch data.
	 * 0/1 means the user chose a mode.
	 */
	if (((branch_mode == -1 && has_br_stack) || branch_mode == 1) &&
1187
	    !branch_call_mode) {
1188
		sort__mode = SORT_MODE__BRANCH;
1189 1190
		symbol_conf.cumulate_callchain = false;
	}
1191
	if (branch_call_mode) {
1192
		callchain_param.key = CCKEY_ADDRESS;
1193 1194 1195 1196 1197 1198
		callchain_param.branch_callstack = 1;
		symbol_conf.use_callchain = true;
		callchain_register_param(&callchain_param);
		if (sort_order == NULL)
			sort_order = "srcline,symbol,dso";
	}
1199

1200
	if (report.mem_mode) {
1201
		if (sort__mode == SORT_MODE__BRANCH) {
1202
			pr_err("branch and mem mode incompatible\n");
1203 1204
			goto error;
		}
1205
		sort__mode = SORT_MODE__MEMORY;
1206
		symbol_conf.cumulate_callchain = false;
1207
	}
1208

1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219
	if (symbol_conf.report_hierarchy) {
		/* disable incompatible options */
		symbol_conf.cumulate_callchain = false;

		if (field_order) {
			pr_err("Error: --hierarchy and --fields options cannot be used together\n");
			parse_options_usage(report_usage, options, "F", 1);
			parse_options_usage(NULL, options, "hierarchy", 0);
			goto error;
		}

1220
		perf_hpp_list.need_collapse = true;
1221 1222
	}

1223 1224 1225 1226 1227 1228 1229
	if (report.use_stdio)
		use_browser = 0;
	else if (report.use_tui)
		use_browser = 1;
	else if (report.use_gtk)
		use_browser = 2;

1230 1231
	/* Force tty output for header output and per-thread stat. */
	if (report.header || report.header_only || report.show_threads)
1232
		use_browser = 0;
1233 1234 1235 1236
	if (report.header || report.header_only)
		report.tool.show_feat_hdr = SHOW_FEAT_HEADER;
	if (report.show_full_info)
		report.tool.show_feat_hdr = SHOW_FEAT_HEADER_FULL_INFO;
1237
	if (report.stats_mode || report.tasks_mode)
1238
		use_browser = 0;
1239
	if (report.stats_mode && report.tasks_mode) {
1240
		pr_err("Error: --tasks and --mmaps can't be used together with --stats\n");
1241 1242
		goto error;
	}
1243

1244 1245
	if (strcmp(input_name, "-") != 0)
		setup_browser(true);
1246
	else
1247 1248
		use_browser = 0;

1249 1250 1251 1252 1253 1254 1255 1256 1257
	if (setup_sorting(session->evlist) < 0) {
		if (sort_order)
			parse_options_usage(report_usage, options, "s", 1);
		if (field_order)
			parse_options_usage(sort_order ? NULL : report_usage,
					    options, "F", 1);
		goto error;
	}

1258
	if ((report.header || report.header_only) && !quiet) {
1259 1260
		perf_session__fprintf_info(session, stdout,
					   report.show_full_info);
1261 1262 1263 1264
		if (report.header_only) {
			ret = 0;
			goto error;
		}
1265 1266
	} else if (use_browser == 0 && !quiet &&
		   !report.stats_mode && !report.tasks_mode) {
1267 1268 1269 1270
		fputs("# To display the perf.data header info, please use --header/--header-only options.\n#\n",
		      stdout);
	}

1271
	/*
1272
	 * Only in the TUI browser we are doing integrated annotation,
1273 1274 1275
	 * so don't allocate extra space that won't be used in the stdio
	 * implementation.
	 */
1276
	if (ui__has_annotation()) {
1277 1278 1279
		ret = symbol__annotation_init();
		if (ret < 0)
			goto error;
1280 1281 1282 1283 1284
		/*
 		 * For searching by name on the "Browse map details".
 		 * providing it only in verbose mode not to bloat too
 		 * much struct symbol.
 		 */
1285
		if (verbose > 0) {
1286 1287 1288 1289 1290 1291 1292 1293 1294 1295
			/*
			 * 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;
		}
	}
1296

1297
	if (symbol__init(&session->header.env) < 0)
1298
		goto error;
1299

1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318
	if (perf_time__parse_str(report.ptime_range, report.time_str) != 0) {
		if (session->evlist->first_sample_time == 0 &&
		    session->evlist->last_sample_time == 0) {
			pr_err("No first/last sample time in perf data\n");
			return -EINVAL;
		}

		report.range_num = perf_time__percent_parse_str(
					report.ptime_range, PTIME_RANGE_MAX,
					report.time_str,
					session->evlist->first_sample_time,
					session->evlist->last_sample_time);

		if (report.range_num < 0) {
			pr_err("Invalid time string\n");
			return -EINVAL;
		}
	} else {
		report.range_num = 1;
1319 1320
	}

1321
	sort__setup_elide(stdout);
1322

1323
	ret = __cmd_report(&report);
1324 1325 1326 1327 1328 1329
	if (ret == K_SWITCH_INPUT_DATA) {
		perf_session__delete(session);
		goto repeat;
	} else
		ret = 0;

1330 1331 1332
error:
	perf_session__delete(session);
	return ret;
1333
}