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