xref: /linux/tools/perf/builtin-report.c (revision bcefe12eff5dca6fdfa94ed85e5bee66380d5cd9)
1 /*
2  * builtin-report.c
3  *
4  * Builtin report command: Analyze the perf.data input file,
5  * look up and read DSOs and symbol information and display
6  * a histogram of results, along various sorting keys.
7  */
8 #include "builtin.h"
9 
10 #include "util/util.h"
11 
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/string.h"
18 #include "util/callchain.h"
19 #include "util/strlist.h"
20 #include "util/values.h"
21 
22 #include "perf.h"
23 #include "util/debug.h"
24 #include "util/header.h"
25 
26 #include "util/parse-options.h"
27 #include "util/parse-events.h"
28 
29 #include "util/data_map.h"
30 #include "util/thread.h"
31 #include "util/sort.h"
32 #include "util/hist.h"
33 
34 static char		const *input_name = "perf.data";
35 
36 static char		*dso_list_str, *comm_list_str, *sym_list_str,
37 			*col_width_list_str;
38 static struct strlist	*dso_list, *comm_list, *sym_list;
39 
40 static int		force;
41 
42 static int		full_paths;
43 static int		show_nr_samples;
44 
45 static int		show_threads;
46 static struct perf_read_values	show_threads_values;
47 
48 static char		default_pretty_printing_style[] = "normal";
49 static char		*pretty_printing_style = default_pretty_printing_style;
50 
51 static int		exclude_other = 1;
52 
53 static char		callchain_default_opt[] = "fractal,0.5";
54 
55 static struct perf_header *header;
56 
57 static u64		sample_type;
58 
59 struct symbol_conf	symbol_conf;
60 
61 
62 static size_t
63 callchain__fprintf_left_margin(FILE *fp, int left_margin)
64 {
65 	int i;
66 	int ret;
67 
68 	ret = fprintf(fp, "            ");
69 
70 	for (i = 0; i < left_margin; i++)
71 		ret += fprintf(fp, " ");
72 
73 	return ret;
74 }
75 
76 static size_t ipchain__fprintf_graph_line(FILE *fp, int depth, int depth_mask,
77 					  int left_margin)
78 {
79 	int i;
80 	size_t ret = 0;
81 
82 	ret += callchain__fprintf_left_margin(fp, left_margin);
83 
84 	for (i = 0; i < depth; i++)
85 		if (depth_mask & (1 << i))
86 			ret += fprintf(fp, "|          ");
87 		else
88 			ret += fprintf(fp, "           ");
89 
90 	ret += fprintf(fp, "\n");
91 
92 	return ret;
93 }
94 static size_t
95 ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain, int depth,
96 		       int depth_mask, int count, u64 total_samples,
97 		       int hits, int left_margin)
98 {
99 	int i;
100 	size_t ret = 0;
101 
102 	ret += callchain__fprintf_left_margin(fp, left_margin);
103 	for (i = 0; i < depth; i++) {
104 		if (depth_mask & (1 << i))
105 			ret += fprintf(fp, "|");
106 		else
107 			ret += fprintf(fp, " ");
108 		if (!count && i == depth - 1) {
109 			double percent;
110 
111 			percent = hits * 100.0 / total_samples;
112 			ret += percent_color_fprintf(fp, "--%2.2f%%-- ", percent);
113 		} else
114 			ret += fprintf(fp, "%s", "          ");
115 	}
116 	if (chain->sym)
117 		ret += fprintf(fp, "%s\n", chain->sym->name);
118 	else
119 		ret += fprintf(fp, "%p\n", (void *)(long)chain->ip);
120 
121 	return ret;
122 }
123 
124 static struct symbol *rem_sq_bracket;
125 static struct callchain_list rem_hits;
126 
127 static void init_rem_hits(void)
128 {
129 	rem_sq_bracket = malloc(sizeof(*rem_sq_bracket) + 6);
130 	if (!rem_sq_bracket) {
131 		fprintf(stderr, "Not enough memory to display remaining hits\n");
132 		return;
133 	}
134 
135 	strcpy(rem_sq_bracket->name, "[...]");
136 	rem_hits.sym = rem_sq_bracket;
137 }
138 
139 static size_t
140 __callchain__fprintf_graph(FILE *fp, struct callchain_node *self,
141 			   u64 total_samples, int depth, int depth_mask,
142 			   int left_margin)
143 {
144 	struct rb_node *node, *next;
145 	struct callchain_node *child;
146 	struct callchain_list *chain;
147 	int new_depth_mask = depth_mask;
148 	u64 new_total;
149 	u64 remaining;
150 	size_t ret = 0;
151 	int i;
152 
153 	if (callchain_param.mode == CHAIN_GRAPH_REL)
154 		new_total = self->children_hit;
155 	else
156 		new_total = total_samples;
157 
158 	remaining = new_total;
159 
160 	node = rb_first(&self->rb_root);
161 	while (node) {
162 		u64 cumul;
163 
164 		child = rb_entry(node, struct callchain_node, rb_node);
165 		cumul = cumul_hits(child);
166 		remaining -= cumul;
167 
168 		/*
169 		 * The depth mask manages the output of pipes that show
170 		 * the depth. We don't want to keep the pipes of the current
171 		 * level for the last child of this depth.
172 		 * Except if we have remaining filtered hits. They will
173 		 * supersede the last child
174 		 */
175 		next = rb_next(node);
176 		if (!next && (callchain_param.mode != CHAIN_GRAPH_REL || !remaining))
177 			new_depth_mask &= ~(1 << (depth - 1));
178 
179 		/*
180 		 * But we keep the older depth mask for the line seperator
181 		 * to keep the level link until we reach the last child
182 		 */
183 		ret += ipchain__fprintf_graph_line(fp, depth, depth_mask,
184 						   left_margin);
185 		i = 0;
186 		list_for_each_entry(chain, &child->val, list) {
187 			if (chain->ip >= PERF_CONTEXT_MAX)
188 				continue;
189 			ret += ipchain__fprintf_graph(fp, chain, depth,
190 						      new_depth_mask, i++,
191 						      new_total,
192 						      cumul,
193 						      left_margin);
194 		}
195 		ret += __callchain__fprintf_graph(fp, child, new_total,
196 						  depth + 1,
197 						  new_depth_mask | (1 << depth),
198 						  left_margin);
199 		node = next;
200 	}
201 
202 	if (callchain_param.mode == CHAIN_GRAPH_REL &&
203 		remaining && remaining != new_total) {
204 
205 		if (!rem_sq_bracket)
206 			return ret;
207 
208 		new_depth_mask &= ~(1 << (depth - 1));
209 
210 		ret += ipchain__fprintf_graph(fp, &rem_hits, depth,
211 					      new_depth_mask, 0, new_total,
212 					      remaining, left_margin);
213 	}
214 
215 	return ret;
216 }
217 
218 
219 static size_t
220 callchain__fprintf_graph(FILE *fp, struct callchain_node *self,
221 			 u64 total_samples, int left_margin)
222 {
223 	struct callchain_list *chain;
224 	bool printed = false;
225 	int i = 0;
226 	int ret = 0;
227 
228 	list_for_each_entry(chain, &self->val, list) {
229 		if (chain->ip >= PERF_CONTEXT_MAX)
230 			continue;
231 
232 		if (!i++ && sort__first_dimension == SORT_SYM)
233 			continue;
234 
235 		if (!printed) {
236 			ret += callchain__fprintf_left_margin(fp, left_margin);
237 			ret += fprintf(fp, "|\n");
238 			ret += callchain__fprintf_left_margin(fp, left_margin);
239 			ret += fprintf(fp, "---");
240 
241 			left_margin += 3;
242 			printed = true;
243 		} else
244 			ret += callchain__fprintf_left_margin(fp, left_margin);
245 
246 		if (chain->sym)
247 			ret += fprintf(fp, " %s\n", chain->sym->name);
248 		else
249 			ret += fprintf(fp, " %p\n", (void *)(long)chain->ip);
250 	}
251 
252 	ret += __callchain__fprintf_graph(fp, self, total_samples, 1, 1, left_margin);
253 
254 	return ret;
255 }
256 
257 static size_t
258 callchain__fprintf_flat(FILE *fp, struct callchain_node *self,
259 			u64 total_samples)
260 {
261 	struct callchain_list *chain;
262 	size_t ret = 0;
263 
264 	if (!self)
265 		return 0;
266 
267 	ret += callchain__fprintf_flat(fp, self->parent, total_samples);
268 
269 
270 	list_for_each_entry(chain, &self->val, list) {
271 		if (chain->ip >= PERF_CONTEXT_MAX)
272 			continue;
273 		if (chain->sym)
274 			ret += fprintf(fp, "                %s\n", chain->sym->name);
275 		else
276 			ret += fprintf(fp, "                %p\n",
277 					(void *)(long)chain->ip);
278 	}
279 
280 	return ret;
281 }
282 
283 static size_t
284 hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self,
285 			      u64 total_samples, int left_margin)
286 {
287 	struct rb_node *rb_node;
288 	struct callchain_node *chain;
289 	size_t ret = 0;
290 
291 	rb_node = rb_first(&self->sorted_chain);
292 	while (rb_node) {
293 		double percent;
294 
295 		chain = rb_entry(rb_node, struct callchain_node, rb_node);
296 		percent = chain->hit * 100.0 / total_samples;
297 		switch (callchain_param.mode) {
298 		case CHAIN_FLAT:
299 			ret += percent_color_fprintf(fp, "           %6.2f%%\n",
300 						     percent);
301 			ret += callchain__fprintf_flat(fp, chain, total_samples);
302 			break;
303 		case CHAIN_GRAPH_ABS: /* Falldown */
304 		case CHAIN_GRAPH_REL:
305 			ret += callchain__fprintf_graph(fp, chain, total_samples,
306 							left_margin);
307 		case CHAIN_NONE:
308 		default:
309 			break;
310 		}
311 		ret += fprintf(fp, "\n");
312 		rb_node = rb_next(rb_node);
313 	}
314 
315 	return ret;
316 }
317 
318 static size_t
319 hist_entry__fprintf(FILE *fp, struct hist_entry *self, u64 total_samples)
320 {
321 	struct sort_entry *se;
322 	size_t ret;
323 
324 	if (exclude_other && !self->parent)
325 		return 0;
326 
327 	if (total_samples)
328 		ret = percent_color_fprintf(fp,
329 					    field_sep ? "%.2f" : "   %6.2f%%",
330 					(self->count * 100.0) / total_samples);
331 	else
332 		ret = fprintf(fp, field_sep ? "%lld" : "%12lld ", self->count);
333 
334 	if (show_nr_samples) {
335 		if (field_sep)
336 			fprintf(fp, "%c%lld", *field_sep, self->count);
337 		else
338 			fprintf(fp, "%11lld", self->count);
339 	}
340 
341 	list_for_each_entry(se, &hist_entry__sort_list, list) {
342 		if (se->elide)
343 			continue;
344 
345 		fprintf(fp, "%s", field_sep ?: "  ");
346 		ret += se->print(fp, self, se->width ? *se->width : 0);
347 	}
348 
349 	ret += fprintf(fp, "\n");
350 
351 	if (callchain) {
352 		int left_margin = 0;
353 
354 		if (sort__first_dimension == SORT_COMM) {
355 			se = list_first_entry(&hist_entry__sort_list, typeof(*se),
356 						list);
357 			left_margin = se->width ? *se->width : 0;
358 			left_margin -= thread__comm_len(self->thread);
359 		}
360 
361 		hist_entry_callchain__fprintf(fp, self, total_samples,
362 					      left_margin);
363 	}
364 
365 	return ret;
366 }
367 
368 /*
369  *
370  */
371 
372 static void dso__calc_col_width(struct dso *self)
373 {
374 	if (!col_width_list_str && !field_sep &&
375 	    (!dso_list || strlist__has_entry(dso_list, self->name))) {
376 		unsigned int slen = strlen(self->name);
377 		if (slen > dsos__col_width)
378 			dsos__col_width = slen;
379 	}
380 
381 	self->slen_calculated = 1;
382 }
383 
384 static void thread__comm_adjust(struct thread *self)
385 {
386 	char *comm = self->comm;
387 
388 	if (!col_width_list_str && !field_sep &&
389 	    (!comm_list || strlist__has_entry(comm_list, comm))) {
390 		unsigned int slen = strlen(comm);
391 
392 		if (slen > comms__col_width) {
393 			comms__col_width = slen;
394 			threads__col_width = slen + 6;
395 		}
396 	}
397 }
398 
399 static int thread__set_comm_adjust(struct thread *self, const char *comm)
400 {
401 	int ret = thread__set_comm(self, comm);
402 
403 	if (ret)
404 		return ret;
405 
406 	thread__comm_adjust(self);
407 
408 	return 0;
409 }
410 
411 static int call__match(struct symbol *sym)
412 {
413 	if (sym->name && !regexec(&parent_regex, sym->name, 0, NULL, 0))
414 		return 1;
415 
416 	return 0;
417 }
418 
419 static struct symbol **resolve_callchain(struct thread *thread,
420 					 struct ip_callchain *chain,
421 					 struct symbol **parent)
422 {
423 	u8 cpumode = PERF_RECORD_MISC_USER;
424 	struct symbol **syms = NULL;
425 	unsigned int i;
426 
427 	if (callchain) {
428 		syms = calloc(chain->nr, sizeof(*syms));
429 		if (!syms) {
430 			fprintf(stderr, "Can't allocate memory for symbols\n");
431 			exit(-1);
432 		}
433 	}
434 
435 	for (i = 0; i < chain->nr; i++) {
436 		u64 ip = chain->ips[i];
437 		struct addr_location al;
438 
439 		if (ip >= PERF_CONTEXT_MAX) {
440 			switch (ip) {
441 			case PERF_CONTEXT_HV:
442 				cpumode = PERF_RECORD_MISC_HYPERVISOR;	break;
443 			case PERF_CONTEXT_KERNEL:
444 				cpumode = PERF_RECORD_MISC_KERNEL;	break;
445 			case PERF_CONTEXT_USER:
446 				cpumode = PERF_RECORD_MISC_USER;	break;
447 			default:
448 				break;
449 			}
450 			continue;
451 		}
452 
453 		thread__find_addr_location(thread, cpumode, MAP__FUNCTION,
454 					   ip, &al, NULL);
455 		if (al.sym != NULL) {
456 			if (sort__has_parent && !*parent &&
457 			    call__match(al.sym))
458 				*parent = al.sym;
459 			if (!callchain)
460 				break;
461 			syms[i] = al.sym;
462 		}
463 	}
464 
465 	return syms;
466 }
467 
468 /*
469  * collect histogram counts
470  */
471 
472 static int hist_entry__add(struct addr_location *al,
473 			   struct ip_callchain *chain, u64 count)
474 {
475 	struct symbol **syms = NULL, *parent = NULL;
476 	bool hit;
477 	struct hist_entry *he;
478 
479 	if ((sort__has_parent || callchain) && chain)
480 		syms = resolve_callchain(al->thread, chain, &parent);
481 
482 	he = __hist_entry__add(al, parent, count, &hit);
483 	if (he == NULL)
484 		return -ENOMEM;
485 
486 	if (hit)
487 		he->count += count;
488 
489 	if (callchain) {
490 		if (!hit)
491 			callchain_init(&he->callchain);
492 		append_chain(&he->callchain, chain, syms);
493 		free(syms);
494 	}
495 
496 	return 0;
497 }
498 
499 static size_t output__fprintf(FILE *fp, u64 total_samples)
500 {
501 	struct hist_entry *pos;
502 	struct sort_entry *se;
503 	struct rb_node *nd;
504 	size_t ret = 0;
505 	unsigned int width;
506 	char *col_width = col_width_list_str;
507 	int raw_printing_style;
508 
509 	raw_printing_style = !strcmp(pretty_printing_style, "raw");
510 
511 	init_rem_hits();
512 
513 	fprintf(fp, "# Samples: %Ld\n", (u64)total_samples);
514 	fprintf(fp, "#\n");
515 
516 	fprintf(fp, "# Overhead");
517 	if (show_nr_samples) {
518 		if (field_sep)
519 			fprintf(fp, "%cSamples", *field_sep);
520 		else
521 			fputs("  Samples  ", fp);
522 	}
523 	list_for_each_entry(se, &hist_entry__sort_list, list) {
524 		if (se->elide)
525 			continue;
526 		if (field_sep) {
527 			fprintf(fp, "%c%s", *field_sep, se->header);
528 			continue;
529 		}
530 		width = strlen(se->header);
531 		if (se->width) {
532 			if (col_width_list_str) {
533 				if (col_width) {
534 					*se->width = atoi(col_width);
535 					col_width = strchr(col_width, ',');
536 					if (col_width)
537 						++col_width;
538 				}
539 			}
540 			width = *se->width = max(*se->width, width);
541 		}
542 		fprintf(fp, "  %*s", width, se->header);
543 	}
544 	fprintf(fp, "\n");
545 
546 	if (field_sep)
547 		goto print_entries;
548 
549 	fprintf(fp, "# ........");
550 	if (show_nr_samples)
551 		fprintf(fp, " ..........");
552 	list_for_each_entry(se, &hist_entry__sort_list, list) {
553 		unsigned int i;
554 
555 		if (se->elide)
556 			continue;
557 
558 		fprintf(fp, "  ");
559 		if (se->width)
560 			width = *se->width;
561 		else
562 			width = strlen(se->header);
563 		for (i = 0; i < width; i++)
564 			fprintf(fp, ".");
565 	}
566 	fprintf(fp, "\n");
567 
568 	fprintf(fp, "#\n");
569 
570 print_entries:
571 	for (nd = rb_first(&output_hists); nd; nd = rb_next(nd)) {
572 		pos = rb_entry(nd, struct hist_entry, rb_node);
573 		ret += hist_entry__fprintf(fp, pos, total_samples);
574 	}
575 
576 	if (sort_order == default_sort_order &&
577 			parent_pattern == default_parent_pattern) {
578 		fprintf(fp, "#\n");
579 		fprintf(fp, "# (For a higher level overview, try: perf report --sort comm,dso)\n");
580 		fprintf(fp, "#\n");
581 	}
582 	fprintf(fp, "\n");
583 
584 	free(rem_sq_bracket);
585 
586 	if (show_threads)
587 		perf_read_values_display(fp, &show_threads_values,
588 					 raw_printing_style);
589 
590 	return ret;
591 }
592 
593 static int validate_chain(struct ip_callchain *chain, event_t *event)
594 {
595 	unsigned int chain_size;
596 
597 	chain_size = event->header.size;
598 	chain_size -= (unsigned long)&event->ip.__more_data - (unsigned long)event;
599 
600 	if (chain->nr*sizeof(u64) > chain_size)
601 		return -1;
602 
603 	return 0;
604 }
605 
606 static int process_sample_event(event_t *event)
607 {
608 	u64 ip = event->ip.ip;
609 	u64 period = 1;
610 	void *more_data = event->ip.__more_data;
611 	struct ip_callchain *chain = NULL;
612 	int cpumode;
613 	struct addr_location al;
614 	struct thread *thread = threads__findnew(event->ip.pid);
615 
616 	if (sample_type & PERF_SAMPLE_PERIOD) {
617 		period = *(u64 *)more_data;
618 		more_data += sizeof(u64);
619 	}
620 
621 	dump_printf("(IP, %d): %d/%d: %p period: %Ld\n",
622 		event->header.misc,
623 		event->ip.pid, event->ip.tid,
624 		(void *)(long)ip,
625 		(long long)period);
626 
627 	if (sample_type & PERF_SAMPLE_CALLCHAIN) {
628 		unsigned int i;
629 
630 		chain = (void *)more_data;
631 
632 		dump_printf("... chain: nr:%Lu\n", chain->nr);
633 
634 		if (validate_chain(chain, event) < 0) {
635 			pr_debug("call-chain problem with event, "
636 				 "skipping it.\n");
637 			return 0;
638 		}
639 
640 		if (dump_trace) {
641 			for (i = 0; i < chain->nr; i++)
642 				dump_printf("..... %2d: %016Lx\n", i, chain->ips[i]);
643 		}
644 	}
645 
646 	if (thread == NULL) {
647 		pr_debug("problem processing %d event, skipping it.\n",
648 			event->header.type);
649 		return -1;
650 	}
651 
652 	dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid);
653 
654 	if (comm_list && !strlist__has_entry(comm_list, thread->comm))
655 		return 0;
656 
657 	cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
658 
659 	thread__find_addr_location(thread, cpumode,
660 				   MAP__FUNCTION, ip, &al, NULL);
661 	/*
662 	 * We have to do this here as we may have a dso with no symbol hit that
663 	 * has a name longer than the ones with symbols sampled.
664 	 */
665 	if (al.map && !sort_dso.elide && !al.map->dso->slen_calculated)
666 		dso__calc_col_width(al.map->dso);
667 
668 	if (dso_list &&
669 	    (!al.map || !al.map->dso ||
670 	     !(strlist__has_entry(dso_list, al.map->dso->short_name) ||
671 	       (al.map->dso->short_name != al.map->dso->long_name &&
672 		strlist__has_entry(dso_list, al.map->dso->long_name)))))
673 		return 0;
674 
675 	if (sym_list && al.sym && !strlist__has_entry(sym_list, al.sym->name))
676 		return 0;
677 
678 	if (hist_entry__add(&al, chain, period)) {
679 		pr_debug("problem incrementing symbol count, skipping event\n");
680 		return -1;
681 	}
682 
683 	event__stats.total += period;
684 
685 	return 0;
686 }
687 
688 static int process_comm_event(event_t *event)
689 {
690 	struct thread *thread = threads__findnew(event->comm.pid);
691 
692 	dump_printf(": %s:%d\n", event->comm.comm, event->comm.pid);
693 
694 	if (thread == NULL ||
695 	    thread__set_comm_adjust(thread, event->comm.comm)) {
696 		dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n");
697 		return -1;
698 	}
699 
700 	return 0;
701 }
702 
703 static int process_read_event(event_t *event)
704 {
705 	struct perf_event_attr *attr;
706 
707 	attr = perf_header__find_attr(event->read.id, header);
708 
709 	if (show_threads) {
710 		const char *name = attr ? __event_name(attr->type, attr->config)
711 				   : "unknown";
712 		perf_read_values_add_value(&show_threads_values,
713 					   event->read.pid, event->read.tid,
714 					   event->read.id,
715 					   name,
716 					   event->read.value);
717 	}
718 
719 	dump_printf(": %d %d %s %Lu\n", event->read.pid, event->read.tid,
720 		    attr ? __event_name(attr->type, attr->config) : "FAIL",
721 		    event->read.value);
722 
723 	return 0;
724 }
725 
726 static int sample_type_check(u64 type)
727 {
728 	sample_type = type;
729 
730 	if (!(sample_type & PERF_SAMPLE_CALLCHAIN)) {
731 		if (sort__has_parent) {
732 			fprintf(stderr, "selected --sort parent, but no"
733 					" callchain data. Did you call"
734 					" perf record without -g?\n");
735 			return -1;
736 		}
737 		if (callchain) {
738 			fprintf(stderr, "selected -g but no callchain data."
739 					" Did you call perf record without"
740 					" -g?\n");
741 			return -1;
742 		}
743 	} else if (callchain_param.mode != CHAIN_NONE && !callchain) {
744 			callchain = 1;
745 			if (register_callchain_param(&callchain_param) < 0) {
746 				fprintf(stderr, "Can't register callchain"
747 						" params\n");
748 				return -1;
749 			}
750 	}
751 
752 	return 0;
753 }
754 
755 static struct perf_file_handler file_handler = {
756 	.process_sample_event	= process_sample_event,
757 	.process_mmap_event	= event__process_mmap,
758 	.process_comm_event	= process_comm_event,
759 	.process_exit_event	= event__process_task,
760 	.process_fork_event	= event__process_task,
761 	.process_lost_event	= event__process_lost,
762 	.process_read_event	= process_read_event,
763 	.sample_type_check	= sample_type_check,
764 };
765 
766 
767 static int __cmd_report(void)
768 {
769 	struct thread *idle;
770 	int ret;
771 
772 	idle = register_idle_thread();
773 	thread__comm_adjust(idle);
774 
775 	if (show_threads)
776 		perf_read_values_init(&show_threads_values);
777 
778 	register_perf_file_handler(&file_handler);
779 
780 	ret = mmap_dispatch_perf_file(&header, input_name, force,
781 				      full_paths, &event__cwdlen, &event__cwd);
782 	if (ret)
783 		return ret;
784 
785 	if (dump_trace) {
786 		event__print_totals();
787 		return 0;
788 	}
789 
790 	if (verbose > 3)
791 		threads__fprintf(stdout);
792 
793 	if (verbose > 2)
794 		dsos__fprintf(stdout);
795 
796 	collapse__resort();
797 	output__resort(event__stats.total);
798 	output__fprintf(stdout, event__stats.total);
799 
800 	if (show_threads)
801 		perf_read_values_destroy(&show_threads_values);
802 
803 	return ret;
804 }
805 
806 static int
807 parse_callchain_opt(const struct option *opt __used, const char *arg,
808 		    int unset __used)
809 {
810 	char *tok;
811 	char *endptr;
812 
813 	callchain = 1;
814 
815 	if (!arg)
816 		return 0;
817 
818 	tok = strtok((char *)arg, ",");
819 	if (!tok)
820 		return -1;
821 
822 	/* get the output mode */
823 	if (!strncmp(tok, "graph", strlen(arg)))
824 		callchain_param.mode = CHAIN_GRAPH_ABS;
825 
826 	else if (!strncmp(tok, "flat", strlen(arg)))
827 		callchain_param.mode = CHAIN_FLAT;
828 
829 	else if (!strncmp(tok, "fractal", strlen(arg)))
830 		callchain_param.mode = CHAIN_GRAPH_REL;
831 
832 	else if (!strncmp(tok, "none", strlen(arg))) {
833 		callchain_param.mode = CHAIN_NONE;
834 		callchain = 0;
835 
836 		return 0;
837 	}
838 
839 	else
840 		return -1;
841 
842 	/* get the min percentage */
843 	tok = strtok(NULL, ",");
844 	if (!tok)
845 		goto setup;
846 
847 	callchain_param.min_percent = strtod(tok, &endptr);
848 	if (tok == endptr)
849 		return -1;
850 
851 setup:
852 	if (register_callchain_param(&callchain_param) < 0) {
853 		fprintf(stderr, "Can't register callchain params\n");
854 		return -1;
855 	}
856 	return 0;
857 }
858 
859 //static const char * const report_usage[] = {
860 const char * const report_usage[] = {
861 	"perf report [<options>] <command>",
862 	NULL
863 };
864 
865 static const struct option options[] = {
866 	OPT_STRING('i', "input", &input_name, "file",
867 		    "input file name"),
868 	OPT_BOOLEAN('v', "verbose", &verbose,
869 		    "be more verbose (show symbol address, etc)"),
870 	OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
871 		    "dump raw trace in ASCII"),
872 	OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
873 		   "file", "vmlinux pathname"),
874 	OPT_BOOLEAN('f', "force", &force, "don't complain, do it"),
875 	OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules,
876 		    "load module symbols - WARNING: use only with -k and LIVE kernel"),
877 	OPT_BOOLEAN('n', "show-nr-samples", &show_nr_samples,
878 		    "Show a column with the number of samples"),
879 	OPT_BOOLEAN('T', "threads", &show_threads,
880 		    "Show per-thread event counters"),
881 	OPT_STRING(0, "pretty", &pretty_printing_style, "key",
882 		   "pretty printing style key: normal raw"),
883 	OPT_STRING('s', "sort", &sort_order, "key[,key2...]",
884 		   "sort by key(s): pid, comm, dso, symbol, parent"),
885 	OPT_BOOLEAN('P', "full-paths", &full_paths,
886 		    "Don't shorten the pathnames taking into account the cwd"),
887 	OPT_STRING('p', "parent", &parent_pattern, "regex",
888 		   "regex filter to identify parent, see: '--sort parent'"),
889 	OPT_BOOLEAN('x', "exclude-other", &exclude_other,
890 		    "Only display entries with parent-match"),
891 	OPT_CALLBACK_DEFAULT('g', "call-graph", NULL, "output_type,min_percent",
892 		     "Display callchains using output_type and min percent threshold. "
893 		     "Default: fractal,0.5", &parse_callchain_opt, callchain_default_opt),
894 	OPT_STRING('d', "dsos", &dso_list_str, "dso[,dso...]",
895 		   "only consider symbols in these dsos"),
896 	OPT_STRING('C', "comms", &comm_list_str, "comm[,comm...]",
897 		   "only consider symbols in these comms"),
898 	OPT_STRING('S', "symbols", &sym_list_str, "symbol[,symbol...]",
899 		   "only consider these symbols"),
900 	OPT_STRING('w', "column-widths", &col_width_list_str,
901 		   "width[,width...]",
902 		   "don't try to adjust column width, use these fixed values"),
903 	OPT_STRING('t', "field-separator", &field_sep, "separator",
904 		   "separator for columns, no spaces will be added between "
905 		   "columns '.' is reserved."),
906 	OPT_END()
907 };
908 
909 static void setup_sorting(void)
910 {
911 	char *tmp, *tok, *str = strdup(sort_order);
912 
913 	for (tok = strtok_r(str, ", ", &tmp);
914 			tok; tok = strtok_r(NULL, ", ", &tmp)) {
915 		if (sort_dimension__add(tok) < 0) {
916 			error("Unknown --sort key: `%s'", tok);
917 			usage_with_options(report_usage, options);
918 		}
919 	}
920 
921 	free(str);
922 }
923 
924 static void setup_list(struct strlist **list, const char *list_str,
925 		       struct sort_entry *se, const char *list_name,
926 		       FILE *fp)
927 {
928 	if (list_str) {
929 		*list = strlist__new(true, list_str);
930 		if (!*list) {
931 			fprintf(stderr, "problems parsing %s list\n",
932 				list_name);
933 			exit(129);
934 		}
935 		if (strlist__nr_entries(*list) == 1) {
936 			fprintf(fp, "# %s: %s\n", list_name,
937 				strlist__entry(*list, 0)->s);
938 			se->elide = true;
939 		}
940 	}
941 }
942 
943 int cmd_report(int argc, const char **argv, const char *prefix __used)
944 {
945 	if (symbol__init(&symbol_conf) < 0)
946 		return -1;
947 
948 	argc = parse_options(argc, argv, options, report_usage, 0);
949 
950 	setup_sorting();
951 
952 	if (parent_pattern != default_parent_pattern) {
953 		sort_dimension__add("parent");
954 		sort_parent.elide = 1;
955 	} else
956 		exclude_other = 0;
957 
958 	/*
959 	 * Any (unrecognized) arguments left?
960 	 */
961 	if (argc)
962 		usage_with_options(report_usage, options);
963 
964 	setup_pager();
965 
966 	setup_list(&dso_list, dso_list_str, &sort_dso, "dso", stdout);
967 	setup_list(&comm_list, comm_list_str, &sort_comm, "comm", stdout);
968 	setup_list(&sym_list, sym_list_str, &sort_sym, "symbol", stdout);
969 
970 	if (field_sep && *field_sep == '.') {
971 		fputs("'.' is the only non valid --field-separator argument\n",
972 		      stderr);
973 		exit(129);
974 	}
975 
976 	return __cmd_report();
977 }
978