xref: /openbmc/linux/tools/perf/ui/stdio/hist.c (revision 6355592e)
1 // SPDX-License-Identifier: GPL-2.0
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <linux/string.h>
5 
6 #include "../../util/callchain.h"
7 #include "../../util/debug.h"
8 #include "../../util/hist.h"
9 #include "../../util/map.h"
10 #include "../../util/map_groups.h"
11 #include "../../util/symbol.h"
12 #include "../../util/sort.h"
13 #include "../../util/evsel.h"
14 #include "../../util/srcline.h"
15 #include "../../util/string2.h"
16 #include "../../util/thread.h"
17 #include <linux/ctype.h>
18 #include <linux/zalloc.h>
19 
20 static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin)
21 {
22 	int i;
23 	int ret = fprintf(fp, "            ");
24 
25 	for (i = 0; i < left_margin; i++)
26 		ret += fprintf(fp, " ");
27 
28 	return ret;
29 }
30 
31 static size_t ipchain__fprintf_graph_line(FILE *fp, int depth, int depth_mask,
32 					  int left_margin)
33 {
34 	int i;
35 	size_t ret = callchain__fprintf_left_margin(fp, left_margin);
36 
37 	for (i = 0; i < depth; i++)
38 		if (depth_mask & (1 << i))
39 			ret += fprintf(fp, "|          ");
40 		else
41 			ret += fprintf(fp, "           ");
42 
43 	ret += fprintf(fp, "\n");
44 
45 	return ret;
46 }
47 
48 static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_node *node,
49 				     struct callchain_list *chain,
50 				     int depth, int depth_mask, int period,
51 				     u64 total_samples, int left_margin)
52 {
53 	int i;
54 	size_t ret = 0;
55 	char bf[1024], *alloc_str = NULL;
56 	char buf[64];
57 	const char *str;
58 
59 	ret += callchain__fprintf_left_margin(fp, left_margin);
60 	for (i = 0; i < depth; i++) {
61 		if (depth_mask & (1 << i))
62 			ret += fprintf(fp, "|");
63 		else
64 			ret += fprintf(fp, " ");
65 		if (!period && i == depth - 1) {
66 			ret += fprintf(fp, "--");
67 			ret += callchain_node__fprintf_value(node, fp, total_samples);
68 			ret += fprintf(fp, "--");
69 		} else
70 			ret += fprintf(fp, "%s", "          ");
71 	}
72 
73 	str = callchain_list__sym_name(chain, bf, sizeof(bf), false);
74 
75 	if (symbol_conf.show_branchflag_count) {
76 		callchain_list_counts__printf_value(chain, NULL,
77 						    buf, sizeof(buf));
78 
79 		if (asprintf(&alloc_str, "%s%s", str, buf) < 0)
80 			str = "Not enough memory!";
81 		else
82 			str = alloc_str;
83 	}
84 
85 	fputs(str, fp);
86 	fputc('\n', fp);
87 	free(alloc_str);
88 
89 	return ret;
90 }
91 
92 static struct symbol *rem_sq_bracket;
93 static struct callchain_list rem_hits;
94 
95 static void init_rem_hits(void)
96 {
97 	rem_sq_bracket = malloc(sizeof(*rem_sq_bracket) + 6);
98 	if (!rem_sq_bracket) {
99 		fprintf(stderr, "Not enough memory to display remaining hits\n");
100 		return;
101 	}
102 
103 	strcpy(rem_sq_bracket->name, "[...]");
104 	rem_hits.ms.sym = rem_sq_bracket;
105 }
106 
107 static size_t __callchain__fprintf_graph(FILE *fp, struct rb_root *root,
108 					 u64 total_samples, int depth,
109 					 int depth_mask, int left_margin)
110 {
111 	struct rb_node *node, *next;
112 	struct callchain_node *child = NULL;
113 	struct callchain_list *chain;
114 	int new_depth_mask = depth_mask;
115 	u64 remaining;
116 	size_t ret = 0;
117 	int i;
118 	uint entries_printed = 0;
119 	int cumul_count = 0;
120 
121 	remaining = total_samples;
122 
123 	node = rb_first(root);
124 	while (node) {
125 		u64 new_total;
126 		u64 cumul;
127 
128 		child = rb_entry(node, struct callchain_node, rb_node);
129 		cumul = callchain_cumul_hits(child);
130 		remaining -= cumul;
131 		cumul_count += callchain_cumul_counts(child);
132 
133 		/*
134 		 * The depth mask manages the output of pipes that show
135 		 * the depth. We don't want to keep the pipes of the current
136 		 * level for the last child of this depth.
137 		 * Except if we have remaining filtered hits. They will
138 		 * supersede the last child
139 		 */
140 		next = rb_next(node);
141 		if (!next && (callchain_param.mode != CHAIN_GRAPH_REL || !remaining))
142 			new_depth_mask &= ~(1 << (depth - 1));
143 
144 		/*
145 		 * But we keep the older depth mask for the line separator
146 		 * to keep the level link until we reach the last child
147 		 */
148 		ret += ipchain__fprintf_graph_line(fp, depth, depth_mask,
149 						   left_margin);
150 		i = 0;
151 		list_for_each_entry(chain, &child->val, list) {
152 			ret += ipchain__fprintf_graph(fp, child, chain, depth,
153 						      new_depth_mask, i++,
154 						      total_samples,
155 						      left_margin);
156 		}
157 
158 		if (callchain_param.mode == CHAIN_GRAPH_REL)
159 			new_total = child->children_hit;
160 		else
161 			new_total = total_samples;
162 
163 		ret += __callchain__fprintf_graph(fp, &child->rb_root, new_total,
164 						  depth + 1,
165 						  new_depth_mask | (1 << depth),
166 						  left_margin);
167 		node = next;
168 		if (++entries_printed == callchain_param.print_limit)
169 			break;
170 	}
171 
172 	if (callchain_param.mode == CHAIN_GRAPH_REL &&
173 		remaining && remaining != total_samples) {
174 		struct callchain_node rem_node = {
175 			.hit = remaining,
176 		};
177 
178 		if (!rem_sq_bracket)
179 			return ret;
180 
181 		if (callchain_param.value == CCVAL_COUNT && child && child->parent) {
182 			rem_node.count = child->parent->children_count - cumul_count;
183 			if (rem_node.count <= 0)
184 				return ret;
185 		}
186 
187 		new_depth_mask &= ~(1 << (depth - 1));
188 		ret += ipchain__fprintf_graph(fp, &rem_node, &rem_hits, depth,
189 					      new_depth_mask, 0, total_samples,
190 					      left_margin);
191 	}
192 
193 	return ret;
194 }
195 
196 /*
197  * If have one single callchain root, don't bother printing
198  * its percentage (100 % in fractal mode and the same percentage
199  * than the hist in graph mode). This also avoid one level of column.
200  *
201  * However when percent-limit applied, it's possible that single callchain
202  * node have different (non-100% in fractal mode) percentage.
203  */
204 static bool need_percent_display(struct rb_node *node, u64 parent_samples)
205 {
206 	struct callchain_node *cnode;
207 
208 	if (rb_next(node))
209 		return true;
210 
211 	cnode = rb_entry(node, struct callchain_node, rb_node);
212 	return callchain_cumul_hits(cnode) != parent_samples;
213 }
214 
215 static size_t callchain__fprintf_graph(FILE *fp, struct rb_root *root,
216 				       u64 total_samples, u64 parent_samples,
217 				       int left_margin)
218 {
219 	struct callchain_node *cnode;
220 	struct callchain_list *chain;
221 	u32 entries_printed = 0;
222 	bool printed = false;
223 	struct rb_node *node;
224 	int i = 0;
225 	int ret = 0;
226 	char bf[1024];
227 
228 	node = rb_first(root);
229 	if (node && !need_percent_display(node, parent_samples)) {
230 		cnode = rb_entry(node, struct callchain_node, rb_node);
231 		list_for_each_entry(chain, &cnode->val, list) {
232 			/*
233 			 * If we sort by symbol, the first entry is the same than
234 			 * the symbol. No need to print it otherwise it appears as
235 			 * displayed twice.
236 			 */
237 			if (!i++ && field_order == NULL &&
238 			    sort_order && strstarts(sort_order, "sym"))
239 				continue;
240 
241 			if (!printed) {
242 				ret += callchain__fprintf_left_margin(fp, left_margin);
243 				ret += fprintf(fp, "|\n");
244 				ret += callchain__fprintf_left_margin(fp, left_margin);
245 				ret += fprintf(fp, "---");
246 				left_margin += 3;
247 				printed = true;
248 			} else
249 				ret += callchain__fprintf_left_margin(fp, left_margin);
250 
251 			ret += fprintf(fp, "%s",
252 				       callchain_list__sym_name(chain, bf,
253 								sizeof(bf),
254 								false));
255 
256 			if (symbol_conf.show_branchflag_count)
257 				ret += callchain_list_counts__printf_value(
258 						chain, fp, NULL, 0);
259 			ret += fprintf(fp, "\n");
260 
261 			if (++entries_printed == callchain_param.print_limit)
262 				break;
263 		}
264 		root = &cnode->rb_root;
265 	}
266 
267 	if (callchain_param.mode == CHAIN_GRAPH_REL)
268 		total_samples = parent_samples;
269 
270 	ret += __callchain__fprintf_graph(fp, root, total_samples,
271 					  1, 1, left_margin);
272 	if (ret) {
273 		/* do not add a blank line if it printed nothing */
274 		ret += fprintf(fp, "\n");
275 	}
276 
277 	return ret;
278 }
279 
280 static size_t __callchain__fprintf_flat(FILE *fp, struct callchain_node *node,
281 					u64 total_samples)
282 {
283 	struct callchain_list *chain;
284 	size_t ret = 0;
285 	char bf[1024];
286 
287 	if (!node)
288 		return 0;
289 
290 	ret += __callchain__fprintf_flat(fp, node->parent, total_samples);
291 
292 
293 	list_for_each_entry(chain, &node->val, list) {
294 		if (chain->ip >= PERF_CONTEXT_MAX)
295 			continue;
296 		ret += fprintf(fp, "                %s\n", callchain_list__sym_name(chain,
297 					bf, sizeof(bf), false));
298 	}
299 
300 	return ret;
301 }
302 
303 static size_t callchain__fprintf_flat(FILE *fp, struct rb_root *tree,
304 				      u64 total_samples)
305 {
306 	size_t ret = 0;
307 	u32 entries_printed = 0;
308 	struct callchain_node *chain;
309 	struct rb_node *rb_node = rb_first(tree);
310 
311 	while (rb_node) {
312 		chain = rb_entry(rb_node, struct callchain_node, rb_node);
313 
314 		ret += fprintf(fp, "           ");
315 		ret += callchain_node__fprintf_value(chain, fp, total_samples);
316 		ret += fprintf(fp, "\n");
317 		ret += __callchain__fprintf_flat(fp, chain, total_samples);
318 		ret += fprintf(fp, "\n");
319 		if (++entries_printed == callchain_param.print_limit)
320 			break;
321 
322 		rb_node = rb_next(rb_node);
323 	}
324 
325 	return ret;
326 }
327 
328 static size_t __callchain__fprintf_folded(FILE *fp, struct callchain_node *node)
329 {
330 	const char *sep = symbol_conf.field_sep ?: ";";
331 	struct callchain_list *chain;
332 	size_t ret = 0;
333 	char bf[1024];
334 	bool first;
335 
336 	if (!node)
337 		return 0;
338 
339 	ret += __callchain__fprintf_folded(fp, node->parent);
340 
341 	first = (ret == 0);
342 	list_for_each_entry(chain, &node->val, list) {
343 		if (chain->ip >= PERF_CONTEXT_MAX)
344 			continue;
345 		ret += fprintf(fp, "%s%s", first ? "" : sep,
346 			       callchain_list__sym_name(chain,
347 						bf, sizeof(bf), false));
348 		first = false;
349 	}
350 
351 	return ret;
352 }
353 
354 static size_t callchain__fprintf_folded(FILE *fp, struct rb_root *tree,
355 					u64 total_samples)
356 {
357 	size_t ret = 0;
358 	u32 entries_printed = 0;
359 	struct callchain_node *chain;
360 	struct rb_node *rb_node = rb_first(tree);
361 
362 	while (rb_node) {
363 
364 		chain = rb_entry(rb_node, struct callchain_node, rb_node);
365 
366 		ret += callchain_node__fprintf_value(chain, fp, total_samples);
367 		ret += fprintf(fp, " ");
368 		ret += __callchain__fprintf_folded(fp, chain);
369 		ret += fprintf(fp, "\n");
370 		if (++entries_printed == callchain_param.print_limit)
371 			break;
372 
373 		rb_node = rb_next(rb_node);
374 	}
375 
376 	return ret;
377 }
378 
379 static size_t hist_entry_callchain__fprintf(struct hist_entry *he,
380 					    u64 total_samples, int left_margin,
381 					    FILE *fp)
382 {
383 	u64 parent_samples = he->stat.period;
384 
385 	if (symbol_conf.cumulate_callchain)
386 		parent_samples = he->stat_acc->period;
387 
388 	switch (callchain_param.mode) {
389 	case CHAIN_GRAPH_REL:
390 		return callchain__fprintf_graph(fp, &he->sorted_chain, total_samples,
391 						parent_samples, left_margin);
392 		break;
393 	case CHAIN_GRAPH_ABS:
394 		return callchain__fprintf_graph(fp, &he->sorted_chain, total_samples,
395 						parent_samples, left_margin);
396 		break;
397 	case CHAIN_FLAT:
398 		return callchain__fprintf_flat(fp, &he->sorted_chain, total_samples);
399 		break;
400 	case CHAIN_FOLDED:
401 		return callchain__fprintf_folded(fp, &he->sorted_chain, total_samples);
402 		break;
403 	case CHAIN_NONE:
404 		break;
405 	default:
406 		pr_err("Bad callchain mode\n");
407 	}
408 
409 	return 0;
410 }
411 
412 int __hist_entry__snprintf(struct hist_entry *he, struct perf_hpp *hpp,
413 			   struct perf_hpp_list *hpp_list)
414 {
415 	const char *sep = symbol_conf.field_sep;
416 	struct perf_hpp_fmt *fmt;
417 	char *start = hpp->buf;
418 	int ret;
419 	bool first = true;
420 
421 	if (symbol_conf.exclude_other && !he->parent)
422 		return 0;
423 
424 	perf_hpp_list__for_each_format(hpp_list, fmt) {
425 		if (perf_hpp__should_skip(fmt, he->hists))
426 			continue;
427 
428 		/*
429 		 * If there's no field_sep, we still need
430 		 * to display initial '  '.
431 		 */
432 		if (!sep || !first) {
433 			ret = scnprintf(hpp->buf, hpp->size, "%s", sep ?: "  ");
434 			advance_hpp(hpp, ret);
435 		} else
436 			first = false;
437 
438 		if (perf_hpp__use_color() && fmt->color)
439 			ret = fmt->color(fmt, hpp, he);
440 		else
441 			ret = fmt->entry(fmt, hpp, he);
442 
443 		ret = hist_entry__snprintf_alignment(he, hpp, fmt, ret);
444 		advance_hpp(hpp, ret);
445 	}
446 
447 	return hpp->buf - start;
448 }
449 
450 static int hist_entry__snprintf(struct hist_entry *he, struct perf_hpp *hpp)
451 {
452 	return __hist_entry__snprintf(he, hpp, he->hists->hpp_list);
453 }
454 
455 static int hist_entry__hierarchy_fprintf(struct hist_entry *he,
456 					 struct perf_hpp *hpp,
457 					 struct hists *hists,
458 					 FILE *fp)
459 {
460 	const char *sep = symbol_conf.field_sep;
461 	struct perf_hpp_fmt *fmt;
462 	struct perf_hpp_list_node *fmt_node;
463 	char *buf = hpp->buf;
464 	size_t size = hpp->size;
465 	int ret, printed = 0;
466 	bool first = true;
467 
468 	if (symbol_conf.exclude_other && !he->parent)
469 		return 0;
470 
471 	ret = scnprintf(hpp->buf, hpp->size, "%*s", he->depth * HIERARCHY_INDENT, "");
472 	advance_hpp(hpp, ret);
473 
474 	/* the first hpp_list_node is for overhead columns */
475 	fmt_node = list_first_entry(&hists->hpp_formats,
476 				    struct perf_hpp_list_node, list);
477 	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
478 		/*
479 		 * If there's no field_sep, we still need
480 		 * to display initial '  '.
481 		 */
482 		if (!sep || !first) {
483 			ret = scnprintf(hpp->buf, hpp->size, "%s", sep ?: "  ");
484 			advance_hpp(hpp, ret);
485 		} else
486 			first = false;
487 
488 		if (perf_hpp__use_color() && fmt->color)
489 			ret = fmt->color(fmt, hpp, he);
490 		else
491 			ret = fmt->entry(fmt, hpp, he);
492 
493 		ret = hist_entry__snprintf_alignment(he, hpp, fmt, ret);
494 		advance_hpp(hpp, ret);
495 	}
496 
497 	if (!sep)
498 		ret = scnprintf(hpp->buf, hpp->size, "%*s",
499 				(hists->nr_hpp_node - 2) * HIERARCHY_INDENT, "");
500 	advance_hpp(hpp, ret);
501 
502 	printed += fprintf(fp, "%s", buf);
503 
504 	perf_hpp_list__for_each_format(he->hpp_list, fmt) {
505 		hpp->buf  = buf;
506 		hpp->size = size;
507 
508 		/*
509 		 * No need to call hist_entry__snprintf_alignment() since this
510 		 * fmt is always the last column in the hierarchy mode.
511 		 */
512 		if (perf_hpp__use_color() && fmt->color)
513 			fmt->color(fmt, hpp, he);
514 		else
515 			fmt->entry(fmt, hpp, he);
516 
517 		/*
518 		 * dynamic entries are right-aligned but we want left-aligned
519 		 * in the hierarchy mode
520 		 */
521 		printed += fprintf(fp, "%s%s", sep ?: "  ", skip_spaces(buf));
522 	}
523 	printed += putc('\n', fp);
524 
525 	if (he->leaf && hist_entry__has_callchains(he) && symbol_conf.use_callchain) {
526 		u64 total = hists__total_period(hists);
527 
528 		printed += hist_entry_callchain__fprintf(he, total, 0, fp);
529 		goto out;
530 	}
531 
532 out:
533 	return printed;
534 }
535 
536 static int hist_entry__block_fprintf(struct hist_entry *he,
537 				     char *bf, size_t size,
538 				     FILE *fp)
539 {
540 	struct block_hist *bh = container_of(he, struct block_hist, he);
541 	int ret = 0;
542 
543 	for (unsigned int i = 0; i < bh->block_hists.nr_entries; i++) {
544 		struct perf_hpp hpp = {
545 			.buf		= bf,
546 			.size		= size,
547 			.skip		= false,
548 		};
549 
550 		bh->block_idx = i;
551 		hist_entry__snprintf(he, &hpp);
552 
553 		if (!hpp.skip)
554 			ret += fprintf(fp, "%s\n", bf);
555 	}
556 
557 	return ret;
558 }
559 
560 static int hist_entry__fprintf(struct hist_entry *he, size_t size,
561 			       char *bf, size_t bfsz, FILE *fp,
562 			       bool ignore_callchains)
563 {
564 	int ret;
565 	int callchain_ret = 0;
566 	struct perf_hpp hpp = {
567 		.buf		= bf,
568 		.size		= size,
569 	};
570 	struct hists *hists = he->hists;
571 	u64 total_period = hists->stats.total_period;
572 
573 	if (size == 0 || size > bfsz)
574 		size = hpp.size = bfsz;
575 
576 	if (symbol_conf.report_hierarchy)
577 		return hist_entry__hierarchy_fprintf(he, &hpp, hists, fp);
578 
579 	if (symbol_conf.report_block)
580 		return hist_entry__block_fprintf(he, bf, size, fp);
581 
582 	hist_entry__snprintf(he, &hpp);
583 
584 	ret = fprintf(fp, "%s\n", bf);
585 
586 	if (hist_entry__has_callchains(he) && !ignore_callchains)
587 		callchain_ret = hist_entry_callchain__fprintf(he, total_period,
588 							      0, fp);
589 
590 	ret += callchain_ret;
591 
592 	return ret;
593 }
594 
595 static int print_hierarchy_indent(const char *sep, int indent,
596 				  const char *line, FILE *fp)
597 {
598 	int width;
599 
600 	if (sep != NULL || indent < 2)
601 		return 0;
602 
603 	width = (indent - 2) * HIERARCHY_INDENT;
604 
605 	return fprintf(fp, "%-*.*s", width, width, line);
606 }
607 
608 static int hists__fprintf_hierarchy_headers(struct hists *hists,
609 					    struct perf_hpp *hpp, FILE *fp)
610 {
611 	bool first_node, first_col;
612 	int indent;
613 	int depth;
614 	unsigned width = 0;
615 	unsigned header_width = 0;
616 	struct perf_hpp_fmt *fmt;
617 	struct perf_hpp_list_node *fmt_node;
618 	const char *sep = symbol_conf.field_sep;
619 
620 	indent = hists->nr_hpp_node;
621 
622 	/* preserve max indent depth for column headers */
623 	print_hierarchy_indent(sep, indent, " ", fp);
624 
625 	/* the first hpp_list_node is for overhead columns */
626 	fmt_node = list_first_entry(&hists->hpp_formats,
627 				    struct perf_hpp_list_node, list);
628 
629 	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
630 		fmt->header(fmt, hpp, hists, 0, NULL);
631 		fprintf(fp, "%s%s", hpp->buf, sep ?: "  ");
632 	}
633 
634 	/* combine sort headers with ' / ' */
635 	first_node = true;
636 	list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) {
637 		if (!first_node)
638 			header_width += fprintf(fp, " / ");
639 		first_node = false;
640 
641 		first_col = true;
642 		perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
643 			if (perf_hpp__should_skip(fmt, hists))
644 				continue;
645 
646 			if (!first_col)
647 				header_width += fprintf(fp, "+");
648 			first_col = false;
649 
650 			fmt->header(fmt, hpp, hists, 0, NULL);
651 
652 			header_width += fprintf(fp, "%s", strim(hpp->buf));
653 		}
654 	}
655 
656 	fprintf(fp, "\n# ");
657 
658 	/* preserve max indent depth for initial dots */
659 	print_hierarchy_indent(sep, indent, dots, fp);
660 
661 	/* the first hpp_list_node is for overhead columns */
662 	fmt_node = list_first_entry(&hists->hpp_formats,
663 				    struct perf_hpp_list_node, list);
664 
665 	first_col = true;
666 	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
667 		if (!first_col)
668 			fprintf(fp, "%s", sep ?: "..");
669 		first_col = false;
670 
671 		width = fmt->width(fmt, hpp, hists);
672 		fprintf(fp, "%.*s", width, dots);
673 	}
674 
675 	depth = 0;
676 	list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) {
677 		first_col = true;
678 		width = depth * HIERARCHY_INDENT;
679 
680 		perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
681 			if (perf_hpp__should_skip(fmt, hists))
682 				continue;
683 
684 			if (!first_col)
685 				width++;  /* for '+' sign between column header */
686 			first_col = false;
687 
688 			width += fmt->width(fmt, hpp, hists);
689 		}
690 
691 		if (width > header_width)
692 			header_width = width;
693 
694 		depth++;
695 	}
696 
697 	fprintf(fp, "%s%-.*s", sep ?: "  ", header_width, dots);
698 
699 	fprintf(fp, "\n#\n");
700 
701 	return 2;
702 }
703 
704 static void fprintf_line(struct hists *hists, struct perf_hpp *hpp,
705 			 int line, FILE *fp)
706 {
707 	struct perf_hpp_fmt *fmt;
708 	const char *sep = symbol_conf.field_sep;
709 	bool first = true;
710 	int span = 0;
711 
712 	hists__for_each_format(hists, fmt) {
713 		if (perf_hpp__should_skip(fmt, hists))
714 			continue;
715 
716 		if (!first && !span)
717 			fprintf(fp, "%s", sep ?: "  ");
718 		else
719 			first = false;
720 
721 		fmt->header(fmt, hpp, hists, line, &span);
722 
723 		if (!span)
724 			fprintf(fp, "%s", hpp->buf);
725 	}
726 }
727 
728 static int
729 hists__fprintf_standard_headers(struct hists *hists,
730 				struct perf_hpp *hpp,
731 				FILE *fp)
732 {
733 	struct perf_hpp_list *hpp_list = hists->hpp_list;
734 	struct perf_hpp_fmt *fmt;
735 	unsigned int width;
736 	const char *sep = symbol_conf.field_sep;
737 	bool first = true;
738 	int line;
739 
740 	for (line = 0; line < hpp_list->nr_header_lines; line++) {
741 		/* first # is displayed one level up */
742 		if (line)
743 			fprintf(fp, "# ");
744 		fprintf_line(hists, hpp, line, fp);
745 		fprintf(fp, "\n");
746 	}
747 
748 	if (sep)
749 		return hpp_list->nr_header_lines;
750 
751 	first = true;
752 
753 	fprintf(fp, "# ");
754 
755 	hists__for_each_format(hists, fmt) {
756 		unsigned int i;
757 
758 		if (perf_hpp__should_skip(fmt, hists))
759 			continue;
760 
761 		if (!first)
762 			fprintf(fp, "%s", sep ?: "  ");
763 		else
764 			first = false;
765 
766 		width = fmt->width(fmt, hpp, hists);
767 		for (i = 0; i < width; i++)
768 			fprintf(fp, ".");
769 	}
770 
771 	fprintf(fp, "\n");
772 	fprintf(fp, "#\n");
773 	return hpp_list->nr_header_lines + 2;
774 }
775 
776 int hists__fprintf_headers(struct hists *hists, FILE *fp)
777 {
778 	char bf[1024];
779 	struct perf_hpp dummy_hpp = {
780 		.buf	= bf,
781 		.size	= sizeof(bf),
782 	};
783 
784 	fprintf(fp, "# ");
785 
786 	if (symbol_conf.report_hierarchy)
787 		return hists__fprintf_hierarchy_headers(hists, &dummy_hpp, fp);
788 	else
789 		return hists__fprintf_standard_headers(hists, &dummy_hpp, fp);
790 
791 }
792 
793 size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,
794 		      int max_cols, float min_pcnt, FILE *fp,
795 		      bool ignore_callchains)
796 {
797 	struct rb_node *nd;
798 	size_t ret = 0;
799 	const char *sep = symbol_conf.field_sep;
800 	int nr_rows = 0;
801 	size_t linesz;
802 	char *line = NULL;
803 	unsigned indent;
804 
805 	init_rem_hits();
806 
807 	hists__reset_column_width(hists);
808 
809 	if (symbol_conf.col_width_list_str)
810 		perf_hpp__set_user_width(symbol_conf.col_width_list_str);
811 
812 	if (show_header)
813 		nr_rows += hists__fprintf_headers(hists, fp);
814 
815 	if (max_rows && nr_rows >= max_rows)
816 		goto out;
817 
818 	linesz = hists__sort_list_width(hists) + 3 + 1;
819 	linesz += perf_hpp__color_overhead();
820 	line = malloc(linesz);
821 	if (line == NULL) {
822 		ret = -1;
823 		goto out;
824 	}
825 
826 	indent = hists__overhead_width(hists) + 4;
827 
828 	for (nd = rb_first_cached(&hists->entries); nd;
829 	     nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD)) {
830 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
831 		float percent;
832 
833 		if (h->filtered)
834 			continue;
835 
836 		percent = hist_entry__get_percent_limit(h);
837 		if (percent < min_pcnt)
838 			continue;
839 
840 		ret += hist_entry__fprintf(h, max_cols, line, linesz, fp, ignore_callchains);
841 
842 		if (max_rows && ++nr_rows >= max_rows)
843 			break;
844 
845 		/*
846 		 * If all children are filtered out or percent-limited,
847 		 * display "no entry >= x.xx%" message.
848 		 */
849 		if (!h->leaf && !hist_entry__has_hierarchy_children(h, min_pcnt)) {
850 			int depth = hists->nr_hpp_node + h->depth + 1;
851 
852 			print_hierarchy_indent(sep, depth, " ", fp);
853 			fprintf(fp, "%*sno entry >= %.2f%%\n", indent, "", min_pcnt);
854 
855 			if (max_rows && ++nr_rows >= max_rows)
856 				break;
857 		}
858 
859 		if (h->ms.map == NULL && verbose > 1) {
860 			map_groups__fprintf(h->thread->mg, fp);
861 			fprintf(fp, "%.10s end\n", graph_dotted_line);
862 		}
863 	}
864 
865 	free(line);
866 out:
867 	zfree(&rem_sq_bracket);
868 
869 	return ret;
870 }
871 
872 size_t events_stats__fprintf(struct events_stats *stats, FILE *fp)
873 {
874 	int i;
875 	size_t ret = 0;
876 
877 	for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) {
878 		const char *name;
879 
880 		name = perf_event__name(i);
881 		if (!strcmp(name, "UNKNOWN"))
882 			continue;
883 
884 		ret += fprintf(fp, "%16s events: %10d\n", name, stats->nr_events[i]);
885 	}
886 
887 	return ret;
888 }
889