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