xref: /kernel/linux/linux-6.6/tools/perf/ui/stdio/hist.c (revision 62306a36)
162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#include <stdio.h>
362306a36Sopenharmony_ci#include <stdlib.h>
462306a36Sopenharmony_ci#include <linux/string.h>
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include "../../util/callchain.h"
762306a36Sopenharmony_ci#include "../../util/debug.h"
862306a36Sopenharmony_ci#include "../../util/event.h"
962306a36Sopenharmony_ci#include "../../util/hist.h"
1062306a36Sopenharmony_ci#include "../../util/map.h"
1162306a36Sopenharmony_ci#include "../../util/maps.h"
1262306a36Sopenharmony_ci#include "../../util/symbol.h"
1362306a36Sopenharmony_ci#include "../../util/sort.h"
1462306a36Sopenharmony_ci#include "../../util/evsel.h"
1562306a36Sopenharmony_ci#include "../../util/srcline.h"
1662306a36Sopenharmony_ci#include "../../util/string2.h"
1762306a36Sopenharmony_ci#include "../../util/thread.h"
1862306a36Sopenharmony_ci#include "../../util/block-info.h"
1962306a36Sopenharmony_ci#include <linux/ctype.h>
2062306a36Sopenharmony_ci#include <linux/zalloc.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_cistatic size_t callchain__fprintf_left_margin(FILE *fp, int left_margin)
2362306a36Sopenharmony_ci{
2462306a36Sopenharmony_ci	int i;
2562306a36Sopenharmony_ci	int ret = fprintf(fp, "            ");
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci	for (i = 0; i < left_margin; i++)
2862306a36Sopenharmony_ci		ret += fprintf(fp, " ");
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	return ret;
3162306a36Sopenharmony_ci}
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistatic size_t ipchain__fprintf_graph_line(FILE *fp, int depth, int depth_mask,
3462306a36Sopenharmony_ci					  int left_margin)
3562306a36Sopenharmony_ci{
3662306a36Sopenharmony_ci	int i;
3762306a36Sopenharmony_ci	size_t ret = callchain__fprintf_left_margin(fp, left_margin);
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	for (i = 0; i < depth; i++)
4062306a36Sopenharmony_ci		if (depth_mask & (1 << i))
4162306a36Sopenharmony_ci			ret += fprintf(fp, "|          ");
4262306a36Sopenharmony_ci		else
4362306a36Sopenharmony_ci			ret += fprintf(fp, "           ");
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	ret += fprintf(fp, "\n");
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	return ret;
4862306a36Sopenharmony_ci}
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistatic size_t ipchain__fprintf_graph(FILE *fp, struct callchain_node *node,
5162306a36Sopenharmony_ci				     struct callchain_list *chain,
5262306a36Sopenharmony_ci				     int depth, int depth_mask, int period,
5362306a36Sopenharmony_ci				     u64 total_samples, int left_margin)
5462306a36Sopenharmony_ci{
5562306a36Sopenharmony_ci	int i;
5662306a36Sopenharmony_ci	size_t ret = 0;
5762306a36Sopenharmony_ci	char bf[1024], *alloc_str = NULL;
5862306a36Sopenharmony_ci	char buf[64];
5962306a36Sopenharmony_ci	const char *str;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	ret += callchain__fprintf_left_margin(fp, left_margin);
6262306a36Sopenharmony_ci	for (i = 0; i < depth; i++) {
6362306a36Sopenharmony_ci		if (depth_mask & (1 << i))
6462306a36Sopenharmony_ci			ret += fprintf(fp, "|");
6562306a36Sopenharmony_ci		else
6662306a36Sopenharmony_ci			ret += fprintf(fp, " ");
6762306a36Sopenharmony_ci		if (!period && i == depth - 1) {
6862306a36Sopenharmony_ci			ret += fprintf(fp, "--");
6962306a36Sopenharmony_ci			ret += callchain_node__fprintf_value(node, fp, total_samples);
7062306a36Sopenharmony_ci			ret += fprintf(fp, "--");
7162306a36Sopenharmony_ci		} else
7262306a36Sopenharmony_ci			ret += fprintf(fp, "%s", "          ");
7362306a36Sopenharmony_ci	}
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	str = callchain_list__sym_name(chain, bf, sizeof(bf), false);
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	if (symbol_conf.show_branchflag_count) {
7862306a36Sopenharmony_ci		callchain_list_counts__printf_value(chain, NULL,
7962306a36Sopenharmony_ci						    buf, sizeof(buf));
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci		if (asprintf(&alloc_str, "%s%s", str, buf) < 0)
8262306a36Sopenharmony_ci			str = "Not enough memory!";
8362306a36Sopenharmony_ci		else
8462306a36Sopenharmony_ci			str = alloc_str;
8562306a36Sopenharmony_ci	}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	fputs(str, fp);
8862306a36Sopenharmony_ci	fputc('\n', fp);
8962306a36Sopenharmony_ci	free(alloc_str);
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	return ret;
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cistatic struct symbol *rem_sq_bracket;
9562306a36Sopenharmony_cistatic struct callchain_list rem_hits;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_cistatic void init_rem_hits(void)
9862306a36Sopenharmony_ci{
9962306a36Sopenharmony_ci	rem_sq_bracket = malloc(sizeof(*rem_sq_bracket) + 6);
10062306a36Sopenharmony_ci	if (!rem_sq_bracket) {
10162306a36Sopenharmony_ci		fprintf(stderr, "Not enough memory to display remaining hits\n");
10262306a36Sopenharmony_ci		return;
10362306a36Sopenharmony_ci	}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	strcpy(rem_sq_bracket->name, "[...]");
10662306a36Sopenharmony_ci	rem_hits.ms.sym = rem_sq_bracket;
10762306a36Sopenharmony_ci}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_cistatic size_t __callchain__fprintf_graph(FILE *fp, struct rb_root *root,
11062306a36Sopenharmony_ci					 u64 total_samples, int depth,
11162306a36Sopenharmony_ci					 int depth_mask, int left_margin)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	struct rb_node *node, *next;
11462306a36Sopenharmony_ci	struct callchain_node *child = NULL;
11562306a36Sopenharmony_ci	struct callchain_list *chain;
11662306a36Sopenharmony_ci	int new_depth_mask = depth_mask;
11762306a36Sopenharmony_ci	u64 remaining;
11862306a36Sopenharmony_ci	size_t ret = 0;
11962306a36Sopenharmony_ci	int i;
12062306a36Sopenharmony_ci	uint entries_printed = 0;
12162306a36Sopenharmony_ci	int cumul_count = 0;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	remaining = total_samples;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	node = rb_first(root);
12662306a36Sopenharmony_ci	while (node) {
12762306a36Sopenharmony_ci		u64 new_total;
12862306a36Sopenharmony_ci		u64 cumul;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci		child = rb_entry(node, struct callchain_node, rb_node);
13162306a36Sopenharmony_ci		cumul = callchain_cumul_hits(child);
13262306a36Sopenharmony_ci		remaining -= cumul;
13362306a36Sopenharmony_ci		cumul_count += callchain_cumul_counts(child);
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci		/*
13662306a36Sopenharmony_ci		 * The depth mask manages the output of pipes that show
13762306a36Sopenharmony_ci		 * the depth. We don't want to keep the pipes of the current
13862306a36Sopenharmony_ci		 * level for the last child of this depth.
13962306a36Sopenharmony_ci		 * Except if we have remaining filtered hits. They will
14062306a36Sopenharmony_ci		 * supersede the last child
14162306a36Sopenharmony_ci		 */
14262306a36Sopenharmony_ci		next = rb_next(node);
14362306a36Sopenharmony_ci		if (!next && (callchain_param.mode != CHAIN_GRAPH_REL || !remaining))
14462306a36Sopenharmony_ci			new_depth_mask &= ~(1 << (depth - 1));
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci		/*
14762306a36Sopenharmony_ci		 * But we keep the older depth mask for the line separator
14862306a36Sopenharmony_ci		 * to keep the level link until we reach the last child
14962306a36Sopenharmony_ci		 */
15062306a36Sopenharmony_ci		ret += ipchain__fprintf_graph_line(fp, depth, depth_mask,
15162306a36Sopenharmony_ci						   left_margin);
15262306a36Sopenharmony_ci		i = 0;
15362306a36Sopenharmony_ci		list_for_each_entry(chain, &child->val, list) {
15462306a36Sopenharmony_ci			ret += ipchain__fprintf_graph(fp, child, chain, depth,
15562306a36Sopenharmony_ci						      new_depth_mask, i++,
15662306a36Sopenharmony_ci						      total_samples,
15762306a36Sopenharmony_ci						      left_margin);
15862306a36Sopenharmony_ci		}
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci		if (callchain_param.mode == CHAIN_GRAPH_REL)
16162306a36Sopenharmony_ci			new_total = child->children_hit;
16262306a36Sopenharmony_ci		else
16362306a36Sopenharmony_ci			new_total = total_samples;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci		ret += __callchain__fprintf_graph(fp, &child->rb_root, new_total,
16662306a36Sopenharmony_ci						  depth + 1,
16762306a36Sopenharmony_ci						  new_depth_mask | (1 << depth),
16862306a36Sopenharmony_ci						  left_margin);
16962306a36Sopenharmony_ci		node = next;
17062306a36Sopenharmony_ci		if (++entries_printed == callchain_param.print_limit)
17162306a36Sopenharmony_ci			break;
17262306a36Sopenharmony_ci	}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	if (callchain_param.mode == CHAIN_GRAPH_REL &&
17562306a36Sopenharmony_ci		remaining && remaining != total_samples) {
17662306a36Sopenharmony_ci		struct callchain_node rem_node = {
17762306a36Sopenharmony_ci			.hit = remaining,
17862306a36Sopenharmony_ci		};
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci		if (!rem_sq_bracket)
18162306a36Sopenharmony_ci			return ret;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci		if (callchain_param.value == CCVAL_COUNT && child && child->parent) {
18462306a36Sopenharmony_ci			rem_node.count = child->parent->children_count - cumul_count;
18562306a36Sopenharmony_ci			if (rem_node.count <= 0)
18662306a36Sopenharmony_ci				return ret;
18762306a36Sopenharmony_ci		}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci		new_depth_mask &= ~(1 << (depth - 1));
19062306a36Sopenharmony_ci		ret += ipchain__fprintf_graph(fp, &rem_node, &rem_hits, depth,
19162306a36Sopenharmony_ci					      new_depth_mask, 0, total_samples,
19262306a36Sopenharmony_ci					      left_margin);
19362306a36Sopenharmony_ci	}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	return ret;
19662306a36Sopenharmony_ci}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci/*
19962306a36Sopenharmony_ci * If have one single callchain root, don't bother printing
20062306a36Sopenharmony_ci * its percentage (100 % in fractal mode and the same percentage
20162306a36Sopenharmony_ci * than the hist in graph mode). This also avoid one level of column.
20262306a36Sopenharmony_ci *
20362306a36Sopenharmony_ci * However when percent-limit applied, it's possible that single callchain
20462306a36Sopenharmony_ci * node have different (non-100% in fractal mode) percentage.
20562306a36Sopenharmony_ci */
20662306a36Sopenharmony_cistatic bool need_percent_display(struct rb_node *node, u64 parent_samples)
20762306a36Sopenharmony_ci{
20862306a36Sopenharmony_ci	struct callchain_node *cnode;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	if (rb_next(node))
21162306a36Sopenharmony_ci		return true;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	cnode = rb_entry(node, struct callchain_node, rb_node);
21462306a36Sopenharmony_ci	return callchain_cumul_hits(cnode) != parent_samples;
21562306a36Sopenharmony_ci}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_cistatic size_t callchain__fprintf_graph(FILE *fp, struct rb_root *root,
21862306a36Sopenharmony_ci				       u64 total_samples, u64 parent_samples,
21962306a36Sopenharmony_ci				       int left_margin)
22062306a36Sopenharmony_ci{
22162306a36Sopenharmony_ci	struct callchain_node *cnode;
22262306a36Sopenharmony_ci	struct callchain_list *chain;
22362306a36Sopenharmony_ci	u32 entries_printed = 0;
22462306a36Sopenharmony_ci	bool printed = false;
22562306a36Sopenharmony_ci	struct rb_node *node;
22662306a36Sopenharmony_ci	int i = 0;
22762306a36Sopenharmony_ci	int ret = 0;
22862306a36Sopenharmony_ci	char bf[1024];
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	node = rb_first(root);
23162306a36Sopenharmony_ci	if (node && !need_percent_display(node, parent_samples)) {
23262306a36Sopenharmony_ci		cnode = rb_entry(node, struct callchain_node, rb_node);
23362306a36Sopenharmony_ci		list_for_each_entry(chain, &cnode->val, list) {
23462306a36Sopenharmony_ci			/*
23562306a36Sopenharmony_ci			 * If we sort by symbol, the first entry is the same than
23662306a36Sopenharmony_ci			 * the symbol. No need to print it otherwise it appears as
23762306a36Sopenharmony_ci			 * displayed twice.
23862306a36Sopenharmony_ci			 */
23962306a36Sopenharmony_ci			if (!i++ && field_order == NULL &&
24062306a36Sopenharmony_ci			    sort_order && strstarts(sort_order, "sym"))
24162306a36Sopenharmony_ci				continue;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci			if (!printed) {
24462306a36Sopenharmony_ci				ret += callchain__fprintf_left_margin(fp, left_margin);
24562306a36Sopenharmony_ci				ret += fprintf(fp, "|\n");
24662306a36Sopenharmony_ci				ret += callchain__fprintf_left_margin(fp, left_margin);
24762306a36Sopenharmony_ci				ret += fprintf(fp, "---");
24862306a36Sopenharmony_ci				left_margin += 3;
24962306a36Sopenharmony_ci				printed = true;
25062306a36Sopenharmony_ci			} else
25162306a36Sopenharmony_ci				ret += callchain__fprintf_left_margin(fp, left_margin);
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci			ret += fprintf(fp, "%s",
25462306a36Sopenharmony_ci				       callchain_list__sym_name(chain, bf,
25562306a36Sopenharmony_ci								sizeof(bf),
25662306a36Sopenharmony_ci								false));
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci			if (symbol_conf.show_branchflag_count)
25962306a36Sopenharmony_ci				ret += callchain_list_counts__printf_value(
26062306a36Sopenharmony_ci						chain, fp, NULL, 0);
26162306a36Sopenharmony_ci			ret += fprintf(fp, "\n");
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci			if (++entries_printed == callchain_param.print_limit)
26462306a36Sopenharmony_ci				break;
26562306a36Sopenharmony_ci		}
26662306a36Sopenharmony_ci		root = &cnode->rb_root;
26762306a36Sopenharmony_ci	}
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	if (callchain_param.mode == CHAIN_GRAPH_REL)
27062306a36Sopenharmony_ci		total_samples = parent_samples;
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	ret += __callchain__fprintf_graph(fp, root, total_samples,
27362306a36Sopenharmony_ci					  1, 1, left_margin);
27462306a36Sopenharmony_ci	if (ret) {
27562306a36Sopenharmony_ci		/* do not add a blank line if it printed nothing */
27662306a36Sopenharmony_ci		ret += fprintf(fp, "\n");
27762306a36Sopenharmony_ci	}
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	return ret;
28062306a36Sopenharmony_ci}
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_cistatic size_t __callchain__fprintf_flat(FILE *fp, struct callchain_node *node,
28362306a36Sopenharmony_ci					u64 total_samples)
28462306a36Sopenharmony_ci{
28562306a36Sopenharmony_ci	struct callchain_list *chain;
28662306a36Sopenharmony_ci	size_t ret = 0;
28762306a36Sopenharmony_ci	char bf[1024];
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	if (!node)
29062306a36Sopenharmony_ci		return 0;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	ret += __callchain__fprintf_flat(fp, node->parent, total_samples);
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	list_for_each_entry(chain, &node->val, list) {
29662306a36Sopenharmony_ci		if (chain->ip >= PERF_CONTEXT_MAX)
29762306a36Sopenharmony_ci			continue;
29862306a36Sopenharmony_ci		ret += fprintf(fp, "                %s\n", callchain_list__sym_name(chain,
29962306a36Sopenharmony_ci					bf, sizeof(bf), false));
30062306a36Sopenharmony_ci	}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	return ret;
30362306a36Sopenharmony_ci}
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_cistatic size_t callchain__fprintf_flat(FILE *fp, struct rb_root *tree,
30662306a36Sopenharmony_ci				      u64 total_samples)
30762306a36Sopenharmony_ci{
30862306a36Sopenharmony_ci	size_t ret = 0;
30962306a36Sopenharmony_ci	u32 entries_printed = 0;
31062306a36Sopenharmony_ci	struct callchain_node *chain;
31162306a36Sopenharmony_ci	struct rb_node *rb_node = rb_first(tree);
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	while (rb_node) {
31462306a36Sopenharmony_ci		chain = rb_entry(rb_node, struct callchain_node, rb_node);
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci		ret += fprintf(fp, "           ");
31762306a36Sopenharmony_ci		ret += callchain_node__fprintf_value(chain, fp, total_samples);
31862306a36Sopenharmony_ci		ret += fprintf(fp, "\n");
31962306a36Sopenharmony_ci		ret += __callchain__fprintf_flat(fp, chain, total_samples);
32062306a36Sopenharmony_ci		ret += fprintf(fp, "\n");
32162306a36Sopenharmony_ci		if (++entries_printed == callchain_param.print_limit)
32262306a36Sopenharmony_ci			break;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci		rb_node = rb_next(rb_node);
32562306a36Sopenharmony_ci	}
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	return ret;
32862306a36Sopenharmony_ci}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_cistatic size_t __callchain__fprintf_folded(FILE *fp, struct callchain_node *node)
33162306a36Sopenharmony_ci{
33262306a36Sopenharmony_ci	const char *sep = symbol_conf.field_sep ?: ";";
33362306a36Sopenharmony_ci	struct callchain_list *chain;
33462306a36Sopenharmony_ci	size_t ret = 0;
33562306a36Sopenharmony_ci	char bf[1024];
33662306a36Sopenharmony_ci	bool first;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	if (!node)
33962306a36Sopenharmony_ci		return 0;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	ret += __callchain__fprintf_folded(fp, node->parent);
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	first = (ret == 0);
34462306a36Sopenharmony_ci	list_for_each_entry(chain, &node->val, list) {
34562306a36Sopenharmony_ci		if (chain->ip >= PERF_CONTEXT_MAX)
34662306a36Sopenharmony_ci			continue;
34762306a36Sopenharmony_ci		ret += fprintf(fp, "%s%s", first ? "" : sep,
34862306a36Sopenharmony_ci			       callchain_list__sym_name(chain,
34962306a36Sopenharmony_ci						bf, sizeof(bf), false));
35062306a36Sopenharmony_ci		first = false;
35162306a36Sopenharmony_ci	}
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	return ret;
35462306a36Sopenharmony_ci}
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_cistatic size_t callchain__fprintf_folded(FILE *fp, struct rb_root *tree,
35762306a36Sopenharmony_ci					u64 total_samples)
35862306a36Sopenharmony_ci{
35962306a36Sopenharmony_ci	size_t ret = 0;
36062306a36Sopenharmony_ci	u32 entries_printed = 0;
36162306a36Sopenharmony_ci	struct callchain_node *chain;
36262306a36Sopenharmony_ci	struct rb_node *rb_node = rb_first(tree);
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	while (rb_node) {
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci		chain = rb_entry(rb_node, struct callchain_node, rb_node);
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci		ret += callchain_node__fprintf_value(chain, fp, total_samples);
36962306a36Sopenharmony_ci		ret += fprintf(fp, " ");
37062306a36Sopenharmony_ci		ret += __callchain__fprintf_folded(fp, chain);
37162306a36Sopenharmony_ci		ret += fprintf(fp, "\n");
37262306a36Sopenharmony_ci		if (++entries_printed == callchain_param.print_limit)
37362306a36Sopenharmony_ci			break;
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci		rb_node = rb_next(rb_node);
37662306a36Sopenharmony_ci	}
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	return ret;
37962306a36Sopenharmony_ci}
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_cistatic size_t hist_entry_callchain__fprintf(struct hist_entry *he,
38262306a36Sopenharmony_ci					    u64 total_samples, int left_margin,
38362306a36Sopenharmony_ci					    FILE *fp)
38462306a36Sopenharmony_ci{
38562306a36Sopenharmony_ci	u64 parent_samples = he->stat.period;
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	if (symbol_conf.cumulate_callchain)
38862306a36Sopenharmony_ci		parent_samples = he->stat_acc->period;
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	switch (callchain_param.mode) {
39162306a36Sopenharmony_ci	case CHAIN_GRAPH_REL:
39262306a36Sopenharmony_ci		return callchain__fprintf_graph(fp, &he->sorted_chain, total_samples,
39362306a36Sopenharmony_ci						parent_samples, left_margin);
39462306a36Sopenharmony_ci		break;
39562306a36Sopenharmony_ci	case CHAIN_GRAPH_ABS:
39662306a36Sopenharmony_ci		return callchain__fprintf_graph(fp, &he->sorted_chain, total_samples,
39762306a36Sopenharmony_ci						parent_samples, left_margin);
39862306a36Sopenharmony_ci		break;
39962306a36Sopenharmony_ci	case CHAIN_FLAT:
40062306a36Sopenharmony_ci		return callchain__fprintf_flat(fp, &he->sorted_chain, total_samples);
40162306a36Sopenharmony_ci		break;
40262306a36Sopenharmony_ci	case CHAIN_FOLDED:
40362306a36Sopenharmony_ci		return callchain__fprintf_folded(fp, &he->sorted_chain, total_samples);
40462306a36Sopenharmony_ci		break;
40562306a36Sopenharmony_ci	case CHAIN_NONE:
40662306a36Sopenharmony_ci		break;
40762306a36Sopenharmony_ci	default:
40862306a36Sopenharmony_ci		pr_err("Bad callchain mode\n");
40962306a36Sopenharmony_ci	}
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	return 0;
41262306a36Sopenharmony_ci}
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ciint __hist_entry__snprintf(struct hist_entry *he, struct perf_hpp *hpp,
41562306a36Sopenharmony_ci			   struct perf_hpp_list *hpp_list)
41662306a36Sopenharmony_ci{
41762306a36Sopenharmony_ci	const char *sep = symbol_conf.field_sep;
41862306a36Sopenharmony_ci	struct perf_hpp_fmt *fmt;
41962306a36Sopenharmony_ci	char *start = hpp->buf;
42062306a36Sopenharmony_ci	int ret;
42162306a36Sopenharmony_ci	bool first = true;
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	if (symbol_conf.exclude_other && !he->parent)
42462306a36Sopenharmony_ci		return 0;
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	perf_hpp_list__for_each_format(hpp_list, fmt) {
42762306a36Sopenharmony_ci		if (perf_hpp__should_skip(fmt, he->hists))
42862306a36Sopenharmony_ci			continue;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci		/*
43162306a36Sopenharmony_ci		 * If there's no field_sep, we still need
43262306a36Sopenharmony_ci		 * to display initial '  '.
43362306a36Sopenharmony_ci		 */
43462306a36Sopenharmony_ci		if (!sep || !first) {
43562306a36Sopenharmony_ci			ret = scnprintf(hpp->buf, hpp->size, "%s", sep ?: "  ");
43662306a36Sopenharmony_ci			advance_hpp(hpp, ret);
43762306a36Sopenharmony_ci		} else
43862306a36Sopenharmony_ci			first = false;
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci		if (perf_hpp__use_color() && fmt->color)
44162306a36Sopenharmony_ci			ret = fmt->color(fmt, hpp, he);
44262306a36Sopenharmony_ci		else
44362306a36Sopenharmony_ci			ret = fmt->entry(fmt, hpp, he);
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci		ret = hist_entry__snprintf_alignment(he, hpp, fmt, ret);
44662306a36Sopenharmony_ci		advance_hpp(hpp, ret);
44762306a36Sopenharmony_ci	}
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	return hpp->buf - start;
45062306a36Sopenharmony_ci}
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_cistatic int hist_entry__snprintf(struct hist_entry *he, struct perf_hpp *hpp)
45362306a36Sopenharmony_ci{
45462306a36Sopenharmony_ci	return __hist_entry__snprintf(he, hpp, he->hists->hpp_list);
45562306a36Sopenharmony_ci}
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_cistatic int hist_entry__hierarchy_fprintf(struct hist_entry *he,
45862306a36Sopenharmony_ci					 struct perf_hpp *hpp,
45962306a36Sopenharmony_ci					 struct hists *hists,
46062306a36Sopenharmony_ci					 FILE *fp)
46162306a36Sopenharmony_ci{
46262306a36Sopenharmony_ci	const char *sep = symbol_conf.field_sep;
46362306a36Sopenharmony_ci	struct perf_hpp_fmt *fmt;
46462306a36Sopenharmony_ci	struct perf_hpp_list_node *fmt_node;
46562306a36Sopenharmony_ci	char *buf = hpp->buf;
46662306a36Sopenharmony_ci	size_t size = hpp->size;
46762306a36Sopenharmony_ci	int ret, printed = 0;
46862306a36Sopenharmony_ci	bool first = true;
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	if (symbol_conf.exclude_other && !he->parent)
47162306a36Sopenharmony_ci		return 0;
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	ret = scnprintf(hpp->buf, hpp->size, "%*s", he->depth * HIERARCHY_INDENT, "");
47462306a36Sopenharmony_ci	advance_hpp(hpp, ret);
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	/* the first hpp_list_node is for overhead columns */
47762306a36Sopenharmony_ci	fmt_node = list_first_entry(&hists->hpp_formats,
47862306a36Sopenharmony_ci				    struct perf_hpp_list_node, list);
47962306a36Sopenharmony_ci	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
48062306a36Sopenharmony_ci		/*
48162306a36Sopenharmony_ci		 * If there's no field_sep, we still need
48262306a36Sopenharmony_ci		 * to display initial '  '.
48362306a36Sopenharmony_ci		 */
48462306a36Sopenharmony_ci		if (!sep || !first) {
48562306a36Sopenharmony_ci			ret = scnprintf(hpp->buf, hpp->size, "%s", sep ?: "  ");
48662306a36Sopenharmony_ci			advance_hpp(hpp, ret);
48762306a36Sopenharmony_ci		} else
48862306a36Sopenharmony_ci			first = false;
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci		if (perf_hpp__use_color() && fmt->color)
49162306a36Sopenharmony_ci			ret = fmt->color(fmt, hpp, he);
49262306a36Sopenharmony_ci		else
49362306a36Sopenharmony_ci			ret = fmt->entry(fmt, hpp, he);
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci		ret = hist_entry__snprintf_alignment(he, hpp, fmt, ret);
49662306a36Sopenharmony_ci		advance_hpp(hpp, ret);
49762306a36Sopenharmony_ci	}
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	if (!sep)
50062306a36Sopenharmony_ci		ret = scnprintf(hpp->buf, hpp->size, "%*s",
50162306a36Sopenharmony_ci				(hists->nr_hpp_node - 2) * HIERARCHY_INDENT, "");
50262306a36Sopenharmony_ci	advance_hpp(hpp, ret);
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	printed += fprintf(fp, "%s", buf);
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	perf_hpp_list__for_each_format(he->hpp_list, fmt) {
50762306a36Sopenharmony_ci		hpp->buf  = buf;
50862306a36Sopenharmony_ci		hpp->size = size;
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci		/*
51162306a36Sopenharmony_ci		 * No need to call hist_entry__snprintf_alignment() since this
51262306a36Sopenharmony_ci		 * fmt is always the last column in the hierarchy mode.
51362306a36Sopenharmony_ci		 */
51462306a36Sopenharmony_ci		if (perf_hpp__use_color() && fmt->color)
51562306a36Sopenharmony_ci			fmt->color(fmt, hpp, he);
51662306a36Sopenharmony_ci		else
51762306a36Sopenharmony_ci			fmt->entry(fmt, hpp, he);
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci		/*
52062306a36Sopenharmony_ci		 * dynamic entries are right-aligned but we want left-aligned
52162306a36Sopenharmony_ci		 * in the hierarchy mode
52262306a36Sopenharmony_ci		 */
52362306a36Sopenharmony_ci		printed += fprintf(fp, "%s%s", sep ?: "  ", skip_spaces(buf));
52462306a36Sopenharmony_ci	}
52562306a36Sopenharmony_ci	printed += putc('\n', fp);
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	if (he->leaf && hist_entry__has_callchains(he) && symbol_conf.use_callchain) {
52862306a36Sopenharmony_ci		u64 total = hists__total_period(hists);
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci		printed += hist_entry_callchain__fprintf(he, total, 0, fp);
53162306a36Sopenharmony_ci		goto out;
53262306a36Sopenharmony_ci	}
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ciout:
53562306a36Sopenharmony_ci	return printed;
53662306a36Sopenharmony_ci}
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_cistatic int hist_entry__block_fprintf(struct hist_entry *he,
53962306a36Sopenharmony_ci				     char *bf, size_t size,
54062306a36Sopenharmony_ci				     FILE *fp)
54162306a36Sopenharmony_ci{
54262306a36Sopenharmony_ci	struct block_hist *bh = container_of(he, struct block_hist, he);
54362306a36Sopenharmony_ci	int ret = 0;
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	for (unsigned int i = 0; i < bh->block_hists.nr_entries; i++) {
54662306a36Sopenharmony_ci		struct perf_hpp hpp = {
54762306a36Sopenharmony_ci			.buf		= bf,
54862306a36Sopenharmony_ci			.size		= size,
54962306a36Sopenharmony_ci			.skip		= false,
55062306a36Sopenharmony_ci		};
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci		bh->block_idx = i;
55362306a36Sopenharmony_ci		hist_entry__snprintf(he, &hpp);
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci		if (!hpp.skip)
55662306a36Sopenharmony_ci			ret += fprintf(fp, "%s\n", bf);
55762306a36Sopenharmony_ci	}
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	return ret;
56062306a36Sopenharmony_ci}
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_cistatic int hist_entry__individual_block_fprintf(struct hist_entry *he,
56362306a36Sopenharmony_ci						char *bf, size_t size,
56462306a36Sopenharmony_ci						FILE *fp)
56562306a36Sopenharmony_ci{
56662306a36Sopenharmony_ci	int ret = 0;
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	struct perf_hpp hpp = {
56962306a36Sopenharmony_ci		.buf		= bf,
57062306a36Sopenharmony_ci		.size		= size,
57162306a36Sopenharmony_ci		.skip		= false,
57262306a36Sopenharmony_ci	};
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	hist_entry__snprintf(he, &hpp);
57562306a36Sopenharmony_ci	if (!hpp.skip)
57662306a36Sopenharmony_ci		ret += fprintf(fp, "%s\n", bf);
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	return ret;
57962306a36Sopenharmony_ci}
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_cistatic int hist_entry__fprintf(struct hist_entry *he, size_t size,
58262306a36Sopenharmony_ci			       char *bf, size_t bfsz, FILE *fp,
58362306a36Sopenharmony_ci			       bool ignore_callchains)
58462306a36Sopenharmony_ci{
58562306a36Sopenharmony_ci	int ret;
58662306a36Sopenharmony_ci	int callchain_ret = 0;
58762306a36Sopenharmony_ci	struct perf_hpp hpp = {
58862306a36Sopenharmony_ci		.buf		= bf,
58962306a36Sopenharmony_ci		.size		= size,
59062306a36Sopenharmony_ci	};
59162306a36Sopenharmony_ci	struct hists *hists = he->hists;
59262306a36Sopenharmony_ci	u64 total_period = hists->stats.total_period;
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	if (size == 0 || size > bfsz)
59562306a36Sopenharmony_ci		size = hpp.size = bfsz;
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	if (symbol_conf.report_hierarchy)
59862306a36Sopenharmony_ci		return hist_entry__hierarchy_fprintf(he, &hpp, hists, fp);
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	if (symbol_conf.report_block)
60162306a36Sopenharmony_ci		return hist_entry__block_fprintf(he, bf, size, fp);
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	if (symbol_conf.report_individual_block)
60462306a36Sopenharmony_ci		return hist_entry__individual_block_fprintf(he, bf, size, fp);
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	hist_entry__snprintf(he, &hpp);
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci	ret = fprintf(fp, "%s\n", bf);
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	if (hist_entry__has_callchains(he) && !ignore_callchains)
61162306a36Sopenharmony_ci		callchain_ret = hist_entry_callchain__fprintf(he, total_period,
61262306a36Sopenharmony_ci							      0, fp);
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	ret += callchain_ret;
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	return ret;
61762306a36Sopenharmony_ci}
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_cistatic int print_hierarchy_indent(const char *sep, int indent,
62062306a36Sopenharmony_ci				  const char *line, FILE *fp)
62162306a36Sopenharmony_ci{
62262306a36Sopenharmony_ci	int width;
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	if (sep != NULL || indent < 2)
62562306a36Sopenharmony_ci		return 0;
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	width = (indent - 2) * HIERARCHY_INDENT;
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci	return fprintf(fp, "%-*.*s", width, width, line);
63062306a36Sopenharmony_ci}
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_cistatic int hists__fprintf_hierarchy_headers(struct hists *hists,
63362306a36Sopenharmony_ci					    struct perf_hpp *hpp, FILE *fp)
63462306a36Sopenharmony_ci{
63562306a36Sopenharmony_ci	bool first_node, first_col;
63662306a36Sopenharmony_ci	int indent;
63762306a36Sopenharmony_ci	int depth;
63862306a36Sopenharmony_ci	unsigned width = 0;
63962306a36Sopenharmony_ci	unsigned header_width = 0;
64062306a36Sopenharmony_ci	struct perf_hpp_fmt *fmt;
64162306a36Sopenharmony_ci	struct perf_hpp_list_node *fmt_node;
64262306a36Sopenharmony_ci	const char *sep = symbol_conf.field_sep;
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	indent = hists->nr_hpp_node;
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci	/* preserve max indent depth for column headers */
64762306a36Sopenharmony_ci	print_hierarchy_indent(sep, indent, " ", fp);
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	/* the first hpp_list_node is for overhead columns */
65062306a36Sopenharmony_ci	fmt_node = list_first_entry(&hists->hpp_formats,
65162306a36Sopenharmony_ci				    struct perf_hpp_list_node, list);
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
65462306a36Sopenharmony_ci		fmt->header(fmt, hpp, hists, 0, NULL);
65562306a36Sopenharmony_ci		fprintf(fp, "%s%s", hpp->buf, sep ?: "  ");
65662306a36Sopenharmony_ci	}
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	/* combine sort headers with ' / ' */
65962306a36Sopenharmony_ci	first_node = true;
66062306a36Sopenharmony_ci	list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) {
66162306a36Sopenharmony_ci		if (!first_node)
66262306a36Sopenharmony_ci			header_width += fprintf(fp, " / ");
66362306a36Sopenharmony_ci		first_node = false;
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci		first_col = true;
66662306a36Sopenharmony_ci		perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
66762306a36Sopenharmony_ci			if (perf_hpp__should_skip(fmt, hists))
66862306a36Sopenharmony_ci				continue;
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci			if (!first_col)
67162306a36Sopenharmony_ci				header_width += fprintf(fp, "+");
67262306a36Sopenharmony_ci			first_col = false;
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci			fmt->header(fmt, hpp, hists, 0, NULL);
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci			header_width += fprintf(fp, "%s", strim(hpp->buf));
67762306a36Sopenharmony_ci		}
67862306a36Sopenharmony_ci	}
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	fprintf(fp, "\n# ");
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	/* preserve max indent depth for initial dots */
68362306a36Sopenharmony_ci	print_hierarchy_indent(sep, indent, dots, fp);
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	/* the first hpp_list_node is for overhead columns */
68662306a36Sopenharmony_ci	fmt_node = list_first_entry(&hists->hpp_formats,
68762306a36Sopenharmony_ci				    struct perf_hpp_list_node, list);
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	first_col = true;
69062306a36Sopenharmony_ci	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
69162306a36Sopenharmony_ci		if (!first_col)
69262306a36Sopenharmony_ci			fprintf(fp, "%s", sep ?: "..");
69362306a36Sopenharmony_ci		first_col = false;
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci		width = fmt->width(fmt, hpp, hists);
69662306a36Sopenharmony_ci		fprintf(fp, "%.*s", width, dots);
69762306a36Sopenharmony_ci	}
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	depth = 0;
70062306a36Sopenharmony_ci	list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) {
70162306a36Sopenharmony_ci		first_col = true;
70262306a36Sopenharmony_ci		width = depth * HIERARCHY_INDENT;
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci		perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
70562306a36Sopenharmony_ci			if (perf_hpp__should_skip(fmt, hists))
70662306a36Sopenharmony_ci				continue;
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci			if (!first_col)
70962306a36Sopenharmony_ci				width++;  /* for '+' sign between column header */
71062306a36Sopenharmony_ci			first_col = false;
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci			width += fmt->width(fmt, hpp, hists);
71362306a36Sopenharmony_ci		}
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci		if (width > header_width)
71662306a36Sopenharmony_ci			header_width = width;
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci		depth++;
71962306a36Sopenharmony_ci	}
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci	fprintf(fp, "%s%-.*s", sep ?: "  ", header_width, dots);
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	fprintf(fp, "\n#\n");
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	return 2;
72662306a36Sopenharmony_ci}
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_cistatic void fprintf_line(struct hists *hists, struct perf_hpp *hpp,
72962306a36Sopenharmony_ci			 int line, FILE *fp)
73062306a36Sopenharmony_ci{
73162306a36Sopenharmony_ci	struct perf_hpp_fmt *fmt;
73262306a36Sopenharmony_ci	const char *sep = symbol_conf.field_sep;
73362306a36Sopenharmony_ci	bool first = true;
73462306a36Sopenharmony_ci	int span = 0;
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	hists__for_each_format(hists, fmt) {
73762306a36Sopenharmony_ci		if (perf_hpp__should_skip(fmt, hists))
73862306a36Sopenharmony_ci			continue;
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci		if (!first && !span)
74162306a36Sopenharmony_ci			fprintf(fp, "%s", sep ?: "  ");
74262306a36Sopenharmony_ci		else
74362306a36Sopenharmony_ci			first = false;
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci		fmt->header(fmt, hpp, hists, line, &span);
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci		if (!span)
74862306a36Sopenharmony_ci			fprintf(fp, "%s", hpp->buf);
74962306a36Sopenharmony_ci	}
75062306a36Sopenharmony_ci}
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_cistatic int
75362306a36Sopenharmony_cihists__fprintf_standard_headers(struct hists *hists,
75462306a36Sopenharmony_ci				struct perf_hpp *hpp,
75562306a36Sopenharmony_ci				FILE *fp)
75662306a36Sopenharmony_ci{
75762306a36Sopenharmony_ci	struct perf_hpp_list *hpp_list = hists->hpp_list;
75862306a36Sopenharmony_ci	struct perf_hpp_fmt *fmt;
75962306a36Sopenharmony_ci	unsigned int width;
76062306a36Sopenharmony_ci	const char *sep = symbol_conf.field_sep;
76162306a36Sopenharmony_ci	bool first = true;
76262306a36Sopenharmony_ci	int line;
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci	for (line = 0; line < hpp_list->nr_header_lines; line++) {
76562306a36Sopenharmony_ci		/* first # is displayed one level up */
76662306a36Sopenharmony_ci		if (line)
76762306a36Sopenharmony_ci			fprintf(fp, "# ");
76862306a36Sopenharmony_ci		fprintf_line(hists, hpp, line, fp);
76962306a36Sopenharmony_ci		fprintf(fp, "\n");
77062306a36Sopenharmony_ci	}
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci	if (sep)
77362306a36Sopenharmony_ci		return hpp_list->nr_header_lines;
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci	first = true;
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci	fprintf(fp, "# ");
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci	hists__for_each_format(hists, fmt) {
78062306a36Sopenharmony_ci		unsigned int i;
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci		if (perf_hpp__should_skip(fmt, hists))
78362306a36Sopenharmony_ci			continue;
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci		if (!first)
78662306a36Sopenharmony_ci			fprintf(fp, "%s", sep ?: "  ");
78762306a36Sopenharmony_ci		else
78862306a36Sopenharmony_ci			first = false;
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci		width = fmt->width(fmt, hpp, hists);
79162306a36Sopenharmony_ci		for (i = 0; i < width; i++)
79262306a36Sopenharmony_ci			fprintf(fp, ".");
79362306a36Sopenharmony_ci	}
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci	fprintf(fp, "\n");
79662306a36Sopenharmony_ci	fprintf(fp, "#\n");
79762306a36Sopenharmony_ci	return hpp_list->nr_header_lines + 2;
79862306a36Sopenharmony_ci}
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ciint hists__fprintf_headers(struct hists *hists, FILE *fp)
80162306a36Sopenharmony_ci{
80262306a36Sopenharmony_ci	char bf[1024];
80362306a36Sopenharmony_ci	struct perf_hpp dummy_hpp = {
80462306a36Sopenharmony_ci		.buf	= bf,
80562306a36Sopenharmony_ci		.size	= sizeof(bf),
80662306a36Sopenharmony_ci	};
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci	fprintf(fp, "# ");
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci	if (symbol_conf.report_hierarchy)
81162306a36Sopenharmony_ci		return hists__fprintf_hierarchy_headers(hists, &dummy_hpp, fp);
81262306a36Sopenharmony_ci	else
81362306a36Sopenharmony_ci		return hists__fprintf_standard_headers(hists, &dummy_hpp, fp);
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci}
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_cisize_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,
81862306a36Sopenharmony_ci		      int max_cols, float min_pcnt, FILE *fp,
81962306a36Sopenharmony_ci		      bool ignore_callchains)
82062306a36Sopenharmony_ci{
82162306a36Sopenharmony_ci	struct rb_node *nd;
82262306a36Sopenharmony_ci	size_t ret = 0;
82362306a36Sopenharmony_ci	const char *sep = symbol_conf.field_sep;
82462306a36Sopenharmony_ci	int nr_rows = 0;
82562306a36Sopenharmony_ci	size_t linesz;
82662306a36Sopenharmony_ci	char *line = NULL;
82762306a36Sopenharmony_ci	unsigned indent;
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci	init_rem_hits();
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci	hists__reset_column_width(hists);
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci	if (symbol_conf.col_width_list_str)
83462306a36Sopenharmony_ci		perf_hpp__set_user_width(symbol_conf.col_width_list_str);
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci	if (show_header)
83762306a36Sopenharmony_ci		nr_rows += hists__fprintf_headers(hists, fp);
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci	if (max_rows && nr_rows >= max_rows)
84062306a36Sopenharmony_ci		goto out;
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci	linesz = hists__sort_list_width(hists) + 3 + 1;
84362306a36Sopenharmony_ci	linesz += perf_hpp__color_overhead();
84462306a36Sopenharmony_ci	line = malloc(linesz);
84562306a36Sopenharmony_ci	if (line == NULL) {
84662306a36Sopenharmony_ci		ret = -1;
84762306a36Sopenharmony_ci		goto out;
84862306a36Sopenharmony_ci	}
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci	indent = hists__overhead_width(hists) + 4;
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci	for (nd = rb_first_cached(&hists->entries); nd;
85362306a36Sopenharmony_ci	     nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD)) {
85462306a36Sopenharmony_ci		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
85562306a36Sopenharmony_ci		float percent;
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci		if (h->filtered)
85862306a36Sopenharmony_ci			continue;
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci		if (symbol_conf.report_individual_block)
86162306a36Sopenharmony_ci			percent = block_info__total_cycles_percent(h);
86262306a36Sopenharmony_ci		else
86362306a36Sopenharmony_ci			percent = hist_entry__get_percent_limit(h);
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci		if (percent < min_pcnt)
86662306a36Sopenharmony_ci			continue;
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci		ret += hist_entry__fprintf(h, max_cols, line, linesz, fp, ignore_callchains);
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci		if (max_rows && ++nr_rows >= max_rows)
87162306a36Sopenharmony_ci			break;
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ci		/*
87462306a36Sopenharmony_ci		 * If all children are filtered out or percent-limited,
87562306a36Sopenharmony_ci		 * display "no entry >= x.xx%" message.
87662306a36Sopenharmony_ci		 */
87762306a36Sopenharmony_ci		if (!h->leaf && !hist_entry__has_hierarchy_children(h, min_pcnt)) {
87862306a36Sopenharmony_ci			int depth = hists->nr_hpp_node + h->depth + 1;
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ci			print_hierarchy_indent(sep, depth, " ", fp);
88162306a36Sopenharmony_ci			fprintf(fp, "%*sno entry >= %.2f%%\n", indent, "", min_pcnt);
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci			if (max_rows && ++nr_rows >= max_rows)
88462306a36Sopenharmony_ci				break;
88562306a36Sopenharmony_ci		}
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_ci		if (h->ms.map == NULL && verbose > 1) {
88862306a36Sopenharmony_ci			maps__fprintf(thread__maps(h->thread), fp);
88962306a36Sopenharmony_ci			fprintf(fp, "%.10s end\n", graph_dotted_line);
89062306a36Sopenharmony_ci		}
89162306a36Sopenharmony_ci	}
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci	free(line);
89462306a36Sopenharmony_ciout:
89562306a36Sopenharmony_ci	zfree(&rem_sq_bracket);
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	return ret;
89862306a36Sopenharmony_ci}
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_cisize_t events_stats__fprintf(struct events_stats *stats, FILE *fp,
90162306a36Sopenharmony_ci			     bool skip_empty)
90262306a36Sopenharmony_ci{
90362306a36Sopenharmony_ci	int i;
90462306a36Sopenharmony_ci	size_t ret = 0;
90562306a36Sopenharmony_ci	u32 total = stats->nr_events[0];
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci	for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) {
90862306a36Sopenharmony_ci		const char *name;
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci		name = perf_event__name(i);
91162306a36Sopenharmony_ci		if (!strcmp(name, "UNKNOWN"))
91262306a36Sopenharmony_ci			continue;
91362306a36Sopenharmony_ci		if (skip_empty && !stats->nr_events[i])
91462306a36Sopenharmony_ci			continue;
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci		if (i && total) {
91762306a36Sopenharmony_ci			ret += fprintf(fp, "%16s events: %10d  (%4.1f%%)\n",
91862306a36Sopenharmony_ci				       name, stats->nr_events[i],
91962306a36Sopenharmony_ci				       100.0 * stats->nr_events[i] / total);
92062306a36Sopenharmony_ci		} else {
92162306a36Sopenharmony_ci			ret += fprintf(fp, "%16s events: %10d\n",
92262306a36Sopenharmony_ci				       name, stats->nr_events[i]);
92362306a36Sopenharmony_ci		}
92462306a36Sopenharmony_ci	}
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci	return ret;
92762306a36Sopenharmony_ci}
928