xref: /openbmc/linux/tools/perf/util/callchain.c (revision 8ab12a20)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
28cb76d99SFrederic Weisbecker /*
31b3a0e95SFrederic Weisbecker  * Copyright (C) 2009-2011, Frederic Weisbecker <fweisbec@gmail.com>
48cb76d99SFrederic Weisbecker  *
58cb76d99SFrederic Weisbecker  * Handle the callchains from the stream in an ad-hoc radix tree and then
68cb76d99SFrederic Weisbecker  * sort them in an rbtree.
78cb76d99SFrederic Weisbecker  *
8deac911cSFrederic Weisbecker  * Using a radix for code path provides a fast retrieval and factorizes
9deac911cSFrederic Weisbecker  * memory use. Also that lets us use the paths in a hierarchical graph view.
10deac911cSFrederic Weisbecker  *
118cb76d99SFrederic Weisbecker  */
128cb76d99SFrederic Weisbecker 
13fd20e811SArnaldo Carvalho de Melo #include <inttypes.h>
148cb76d99SFrederic Weisbecker #include <stdlib.h>
158cb76d99SFrederic Weisbecker #include <stdio.h>
168cb76d99SFrederic Weisbecker #include <stdbool.h>
178cb76d99SFrederic Weisbecker #include <errno.h>
18c0a8865eSFrederic Weisbecker #include <math.h>
198520a98dSArnaldo Carvalho de Melo #include <linux/string.h>
207f7c536fSArnaldo Carvalho de Melo #include <linux/zalloc.h>
218cb76d99SFrederic Weisbecker 
22b965bb41SFrederic Weisbecker #include "asm/bug.h"
23b965bb41SFrederic Weisbecker 
24185bcb92SArnaldo Carvalho de Melo #include "debug.h"
254a3cec84SArnaldo Carvalho de Melo #include "dso.h"
26ea49e01cSArnaldo Carvalho de Melo #include "event.h"
2799571ab3SAndi Kleen #include "hist.h"
282dc9fb1aSNamhyung Kim #include "sort.h"
292dc9fb1aSNamhyung Kim #include "machine.h"
301101f69aSArnaldo Carvalho de Melo #include "map.h"
318cb76d99SFrederic Weisbecker #include "callchain.h"
32b851dd49SJin Yao #include "branch.h"
337cadca8eSArnaldo Carvalho de Melo #include "symbol.h"
347cb2a53fSNamhyung Kim #include "util.h"
35c1a604dfSArnaldo Carvalho de Melo #include "../perf.h"
368cb76d99SFrederic Weisbecker 
3756e2e056SArnaldo Carvalho de Melo #define CALLCHAIN_PARAM_DEFAULT			\
3856e2e056SArnaldo Carvalho de Melo 	.mode		= CHAIN_GRAPH_ABS,	\
3956e2e056SArnaldo Carvalho de Melo 	.min_percent	= 0.5,			\
4056e2e056SArnaldo Carvalho de Melo 	.order		= ORDER_CALLEE,		\
4156e2e056SArnaldo Carvalho de Melo 	.key		= CCKEY_FUNCTION,	\
4256e2e056SArnaldo Carvalho de Melo 	.value		= CCVAL_PERCENT,	\
4356e2e056SArnaldo Carvalho de Melo 
4456e2e056SArnaldo Carvalho de Melo struct callchain_param callchain_param = {
4556e2e056SArnaldo Carvalho de Melo 	CALLCHAIN_PARAM_DEFAULT
4656e2e056SArnaldo Carvalho de Melo };
4756e2e056SArnaldo Carvalho de Melo 
48eabad8c6SArnaldo Carvalho de Melo /*
49eabad8c6SArnaldo Carvalho de Melo  * Are there any events usind DWARF callchains?
50eabad8c6SArnaldo Carvalho de Melo  *
51eabad8c6SArnaldo Carvalho de Melo  * I.e.
52eabad8c6SArnaldo Carvalho de Melo  *
53eabad8c6SArnaldo Carvalho de Melo  * -e cycles/call-graph=dwarf/
54eabad8c6SArnaldo Carvalho de Melo  */
55eabad8c6SArnaldo Carvalho de Melo bool dwarf_callchain_users;
56eabad8c6SArnaldo Carvalho de Melo 
5756e2e056SArnaldo Carvalho de Melo struct callchain_param callchain_param_default = {
5856e2e056SArnaldo Carvalho de Melo 	CALLCHAIN_PARAM_DEFAULT
5956e2e056SArnaldo Carvalho de Melo };
6056e2e056SArnaldo Carvalho de Melo 
61*8ab12a20SIan Rogers /* Used for thread-local struct callchain_cursor. */
62*8ab12a20SIan Rogers static pthread_key_t callchain_cursor;
6347260645SNamhyung Kim 
parse_callchain_record_opt(const char * arg,struct callchain_param * param)64c3a6a8c4SKan Liang int parse_callchain_record_opt(const char *arg, struct callchain_param *param)
65f7f084f4SNamhyung Kim {
66076a30c4SKan Liang 	return parse_callchain_record(arg, param);
67f7f084f4SNamhyung Kim }
68f7f084f4SNamhyung Kim 
parse_callchain_mode(const char * value)692b9240caSNamhyung Kim static int parse_callchain_mode(const char *value)
702b9240caSNamhyung Kim {
712b9240caSNamhyung Kim 	if (!strncmp(value, "graph", strlen(value))) {
722b9240caSNamhyung Kim 		callchain_param.mode = CHAIN_GRAPH_ABS;
732b9240caSNamhyung Kim 		return 0;
742b9240caSNamhyung Kim 	}
752b9240caSNamhyung Kim 	if (!strncmp(value, "flat", strlen(value))) {
762b9240caSNamhyung Kim 		callchain_param.mode = CHAIN_FLAT;
772b9240caSNamhyung Kim 		return 0;
782b9240caSNamhyung Kim 	}
792b9240caSNamhyung Kim 	if (!strncmp(value, "fractal", strlen(value))) {
802b9240caSNamhyung Kim 		callchain_param.mode = CHAIN_GRAPH_REL;
812b9240caSNamhyung Kim 		return 0;
822b9240caSNamhyung Kim 	}
8326e77924SNamhyung Kim 	if (!strncmp(value, "folded", strlen(value))) {
8426e77924SNamhyung Kim 		callchain_param.mode = CHAIN_FOLDED;
8526e77924SNamhyung Kim 		return 0;
8626e77924SNamhyung Kim 	}
872b9240caSNamhyung Kim 	return -1;
882b9240caSNamhyung Kim }
892b9240caSNamhyung Kim 
parse_callchain_order(const char * value)902b9240caSNamhyung Kim static int parse_callchain_order(const char *value)
912b9240caSNamhyung Kim {
922b9240caSNamhyung Kim 	if (!strncmp(value, "caller", strlen(value))) {
932b9240caSNamhyung Kim 		callchain_param.order = ORDER_CALLER;
94792aeafaSNamhyung Kim 		callchain_param.order_set = true;
952b9240caSNamhyung Kim 		return 0;
962b9240caSNamhyung Kim 	}
972b9240caSNamhyung Kim 	if (!strncmp(value, "callee", strlen(value))) {
982b9240caSNamhyung Kim 		callchain_param.order = ORDER_CALLEE;
99792aeafaSNamhyung Kim 		callchain_param.order_set = true;
1002b9240caSNamhyung Kim 		return 0;
1012b9240caSNamhyung Kim 	}
1022b9240caSNamhyung Kim 	return -1;
1032b9240caSNamhyung Kim }
1042b9240caSNamhyung Kim 
parse_callchain_sort_key(const char * value)1052b9240caSNamhyung Kim static int parse_callchain_sort_key(const char *value)
1062b9240caSNamhyung Kim {
1072b9240caSNamhyung Kim 	if (!strncmp(value, "function", strlen(value))) {
1082b9240caSNamhyung Kim 		callchain_param.key = CCKEY_FUNCTION;
1092b9240caSNamhyung Kim 		return 0;
1102b9240caSNamhyung Kim 	}
1112b9240caSNamhyung Kim 	if (!strncmp(value, "address", strlen(value))) {
1122b9240caSNamhyung Kim 		callchain_param.key = CCKEY_ADDRESS;
1132b9240caSNamhyung Kim 		return 0;
1142b9240caSNamhyung Kim 	}
1155dfa210eSMilian Wolff 	if (!strncmp(value, "srcline", strlen(value))) {
1165dfa210eSMilian Wolff 		callchain_param.key = CCKEY_SRCLINE;
1175dfa210eSMilian Wolff 		return 0;
1185dfa210eSMilian Wolff 	}
1198b7bad58SAndi Kleen 	if (!strncmp(value, "branch", strlen(value))) {
1208b7bad58SAndi Kleen 		callchain_param.branch_callstack = 1;
1218b7bad58SAndi Kleen 		return 0;
1228b7bad58SAndi Kleen 	}
1232b9240caSNamhyung Kim 	return -1;
1242b9240caSNamhyung Kim }
1252b9240caSNamhyung Kim 
parse_callchain_value(const char * value)126f2af0086SNamhyung Kim static int parse_callchain_value(const char *value)
127f2af0086SNamhyung Kim {
128f2af0086SNamhyung Kim 	if (!strncmp(value, "percent", strlen(value))) {
129f2af0086SNamhyung Kim 		callchain_param.value = CCVAL_PERCENT;
130f2af0086SNamhyung Kim 		return 0;
131f2af0086SNamhyung Kim 	}
132f2af0086SNamhyung Kim 	if (!strncmp(value, "period", strlen(value))) {
133f2af0086SNamhyung Kim 		callchain_param.value = CCVAL_PERIOD;
134f2af0086SNamhyung Kim 		return 0;
135f2af0086SNamhyung Kim 	}
136f2af0086SNamhyung Kim 	if (!strncmp(value, "count", strlen(value))) {
137f2af0086SNamhyung Kim 		callchain_param.value = CCVAL_COUNT;
138f2af0086SNamhyung Kim 		return 0;
139f2af0086SNamhyung Kim 	}
140f2af0086SNamhyung Kim 	return -1;
141f2af0086SNamhyung Kim }
142f2af0086SNamhyung Kim 
get_stack_size(const char * str,unsigned long * _size)14356e2e056SArnaldo Carvalho de Melo static int get_stack_size(const char *str, unsigned long *_size)
14456e2e056SArnaldo Carvalho de Melo {
14556e2e056SArnaldo Carvalho de Melo 	char *endptr;
14656e2e056SArnaldo Carvalho de Melo 	unsigned long size;
14756e2e056SArnaldo Carvalho de Melo 	unsigned long max_size = round_down(USHRT_MAX, sizeof(u64));
14856e2e056SArnaldo Carvalho de Melo 
14956e2e056SArnaldo Carvalho de Melo 	size = strtoul(str, &endptr, 0);
15056e2e056SArnaldo Carvalho de Melo 
15156e2e056SArnaldo Carvalho de Melo 	do {
15256e2e056SArnaldo Carvalho de Melo 		if (*endptr)
15356e2e056SArnaldo Carvalho de Melo 			break;
15456e2e056SArnaldo Carvalho de Melo 
15556e2e056SArnaldo Carvalho de Melo 		size = round_up(size, sizeof(u64));
15656e2e056SArnaldo Carvalho de Melo 		if (!size || size > max_size)
15756e2e056SArnaldo Carvalho de Melo 			break;
15856e2e056SArnaldo Carvalho de Melo 
15956e2e056SArnaldo Carvalho de Melo 		*_size = size;
16056e2e056SArnaldo Carvalho de Melo 		return 0;
16156e2e056SArnaldo Carvalho de Melo 
16256e2e056SArnaldo Carvalho de Melo 	} while (0);
16356e2e056SArnaldo Carvalho de Melo 
16456e2e056SArnaldo Carvalho de Melo 	pr_err("callchain: Incorrect stack dump size (max %ld): %s\n",
16556e2e056SArnaldo Carvalho de Melo 	       max_size, str);
16656e2e056SArnaldo Carvalho de Melo 	return -1;
16756e2e056SArnaldo Carvalho de Melo }
16856e2e056SArnaldo Carvalho de Melo 
169a2c10d39SNamhyung Kim static int
__parse_callchain_report_opt(const char * arg,bool allow_record_opt)170a2c10d39SNamhyung Kim __parse_callchain_report_opt(const char *arg, bool allow_record_opt)
171cff6bb46SDon Zickus {
172e8232f1aSNamhyung Kim 	char *tok;
173dadafc31SArnaldo Carvalho de Melo 	char *endptr, *saveptr = NULL;
174e8232f1aSNamhyung Kim 	bool minpcnt_set = false;
175a2c10d39SNamhyung Kim 	bool record_opt_set = false;
176a2c10d39SNamhyung Kim 	bool try_stack_size = false;
177cff6bb46SDon Zickus 
17830234f09SArnaldo Carvalho de Melo 	callchain_param.enabled = true;
179cff6bb46SDon Zickus 	symbol_conf.use_callchain = true;
180cff6bb46SDon Zickus 
181cff6bb46SDon Zickus 	if (!arg)
182cff6bb46SDon Zickus 		return 0;
183cff6bb46SDon Zickus 
184dadafc31SArnaldo Carvalho de Melo 	while ((tok = strtok_r((char *)arg, ",", &saveptr)) != NULL) {
185e8232f1aSNamhyung Kim 		if (!strncmp(tok, "none", strlen(tok))) {
186cff6bb46SDon Zickus 			callchain_param.mode = CHAIN_NONE;
18730234f09SArnaldo Carvalho de Melo 			callchain_param.enabled = false;
188cff6bb46SDon Zickus 			symbol_conf.use_callchain = false;
189cff6bb46SDon Zickus 			return 0;
190cff6bb46SDon Zickus 		}
191cff6bb46SDon Zickus 
1922b9240caSNamhyung Kim 		if (!parse_callchain_mode(tok) ||
1932b9240caSNamhyung Kim 		    !parse_callchain_order(tok) ||
194f2af0086SNamhyung Kim 		    !parse_callchain_sort_key(tok) ||
195f2af0086SNamhyung Kim 		    !parse_callchain_value(tok)) {
1962b9240caSNamhyung Kim 			/* parsing ok - move on to the next */
197a2c10d39SNamhyung Kim 			try_stack_size = false;
198a2c10d39SNamhyung Kim 			goto next;
199a2c10d39SNamhyung Kim 		} else if (allow_record_opt && !record_opt_set) {
200a2c10d39SNamhyung Kim 			if (parse_callchain_record(tok, &callchain_param))
201a2c10d39SNamhyung Kim 				goto try_numbers;
202a2c10d39SNamhyung Kim 
203a2c10d39SNamhyung Kim 			/* assume that number followed by 'dwarf' is stack size */
204a2c10d39SNamhyung Kim 			if (callchain_param.record_mode == CALLCHAIN_DWARF)
205a2c10d39SNamhyung Kim 				try_stack_size = true;
206a2c10d39SNamhyung Kim 
207a2c10d39SNamhyung Kim 			record_opt_set = true;
208a2c10d39SNamhyung Kim 			goto next;
209a2c10d39SNamhyung Kim 		}
210a2c10d39SNamhyung Kim 
211a2c10d39SNamhyung Kim try_numbers:
212a2c10d39SNamhyung Kim 		if (try_stack_size) {
213a2c10d39SNamhyung Kim 			unsigned long size = 0;
214a2c10d39SNamhyung Kim 
215a2c10d39SNamhyung Kim 			if (get_stack_size(tok, &size) < 0)
216a2c10d39SNamhyung Kim 				return -1;
217a2c10d39SNamhyung Kim 			callchain_param.dump_size = size;
218a2c10d39SNamhyung Kim 			try_stack_size = false;
2192b9240caSNamhyung Kim 		} else if (!minpcnt_set) {
220e8232f1aSNamhyung Kim 			/* try to get the min percent */
221cff6bb46SDon Zickus 			callchain_param.min_percent = strtod(tok, &endptr);
222cff6bb46SDon Zickus 			if (tok == endptr)
223cff6bb46SDon Zickus 				return -1;
224e8232f1aSNamhyung Kim 			minpcnt_set = true;
225e8232f1aSNamhyung Kim 		} else {
226e8232f1aSNamhyung Kim 			/* try print limit at last */
227e8232f1aSNamhyung Kim 			callchain_param.print_limit = strtoul(tok, &endptr, 0);
228e8232f1aSNamhyung Kim 			if (tok == endptr)
229e8232f1aSNamhyung Kim 				return -1;
230cff6bb46SDon Zickus 		}
231a2c10d39SNamhyung Kim next:
232e8232f1aSNamhyung Kim 		arg = NULL;
233e8232f1aSNamhyung Kim 	}
234cff6bb46SDon Zickus 
235cff6bb46SDon Zickus 	if (callchain_register_param(&callchain_param) < 0) {
236cff6bb46SDon Zickus 		pr_err("Can't register callchain params\n");
237cff6bb46SDon Zickus 		return -1;
238cff6bb46SDon Zickus 	}
239cff6bb46SDon Zickus 	return 0;
240cff6bb46SDon Zickus }
241cff6bb46SDon Zickus 
parse_callchain_report_opt(const char * arg)242a2c10d39SNamhyung Kim int parse_callchain_report_opt(const char *arg)
243a2c10d39SNamhyung Kim {
244a2c10d39SNamhyung Kim 	return __parse_callchain_report_opt(arg, false);
245a2c10d39SNamhyung Kim }
246a2c10d39SNamhyung Kim 
parse_callchain_top_opt(const char * arg)247a2c10d39SNamhyung Kim int parse_callchain_top_opt(const char *arg)
248a2c10d39SNamhyung Kim {
249a2c10d39SNamhyung Kim 	return __parse_callchain_report_opt(arg, true);
250a2c10d39SNamhyung Kim }
251a2c10d39SNamhyung Kim 
parse_callchain_record(const char * arg,struct callchain_param * param)25256e2e056SArnaldo Carvalho de Melo int parse_callchain_record(const char *arg, struct callchain_param *param)
25356e2e056SArnaldo Carvalho de Melo {
25456e2e056SArnaldo Carvalho de Melo 	char *tok, *name, *saveptr = NULL;
25556e2e056SArnaldo Carvalho de Melo 	char *buf;
25656e2e056SArnaldo Carvalho de Melo 	int ret = -1;
25756e2e056SArnaldo Carvalho de Melo 
25856e2e056SArnaldo Carvalho de Melo 	/* We need buffer that we know we can write to. */
25956e2e056SArnaldo Carvalho de Melo 	buf = malloc(strlen(arg) + 1);
26056e2e056SArnaldo Carvalho de Melo 	if (!buf)
26156e2e056SArnaldo Carvalho de Melo 		return -ENOMEM;
26256e2e056SArnaldo Carvalho de Melo 
26356e2e056SArnaldo Carvalho de Melo 	strcpy(buf, arg);
26456e2e056SArnaldo Carvalho de Melo 
26556e2e056SArnaldo Carvalho de Melo 	tok = strtok_r((char *)buf, ",", &saveptr);
26656e2e056SArnaldo Carvalho de Melo 	name = tok ? : (char *)buf;
26756e2e056SArnaldo Carvalho de Melo 
26856e2e056SArnaldo Carvalho de Melo 	do {
26956e2e056SArnaldo Carvalho de Melo 		/* Framepointer style */
27056e2e056SArnaldo Carvalho de Melo 		if (!strncmp(name, "fp", sizeof("fp"))) {
27156e2e056SArnaldo Carvalho de Melo 			ret = 0;
2727cb2a53fSNamhyung Kim 			param->record_mode = CALLCHAIN_FP;
2737cb2a53fSNamhyung Kim 
2747cb2a53fSNamhyung Kim 			tok = strtok_r(NULL, ",", &saveptr);
2757cb2a53fSNamhyung Kim 			if (tok) {
2767cb2a53fSNamhyung Kim 				unsigned long size;
2777cb2a53fSNamhyung Kim 
2787cb2a53fSNamhyung Kim 				size = strtoul(tok, &name, 0);
2797cb2a53fSNamhyung Kim 				if (size < (unsigned) sysctl__max_stack())
2807cb2a53fSNamhyung Kim 					param->max_stack = size;
2817cb2a53fSNamhyung Kim 			}
28256e2e056SArnaldo Carvalho de Melo 			break;
28356e2e056SArnaldo Carvalho de Melo 
28456e2e056SArnaldo Carvalho de Melo 		/* Dwarf style */
28556e2e056SArnaldo Carvalho de Melo 		} else if (!strncmp(name, "dwarf", sizeof("dwarf"))) {
28656e2e056SArnaldo Carvalho de Melo 			const unsigned long default_stack_dump_size = 8192;
28756e2e056SArnaldo Carvalho de Melo 
28856e2e056SArnaldo Carvalho de Melo 			ret = 0;
28956e2e056SArnaldo Carvalho de Melo 			param->record_mode = CALLCHAIN_DWARF;
29056e2e056SArnaldo Carvalho de Melo 			param->dump_size = default_stack_dump_size;
291eabad8c6SArnaldo Carvalho de Melo 			dwarf_callchain_users = true;
29256e2e056SArnaldo Carvalho de Melo 
29356e2e056SArnaldo Carvalho de Melo 			tok = strtok_r(NULL, ",", &saveptr);
29456e2e056SArnaldo Carvalho de Melo 			if (tok) {
29556e2e056SArnaldo Carvalho de Melo 				unsigned long size = 0;
29656e2e056SArnaldo Carvalho de Melo 
29756e2e056SArnaldo Carvalho de Melo 				ret = get_stack_size(tok, &size);
29856e2e056SArnaldo Carvalho de Melo 				param->dump_size = size;
29956e2e056SArnaldo Carvalho de Melo 			}
30056e2e056SArnaldo Carvalho de Melo 		} else if (!strncmp(name, "lbr", sizeof("lbr"))) {
30156e2e056SArnaldo Carvalho de Melo 			if (!strtok_r(NULL, ",", &saveptr)) {
30256e2e056SArnaldo Carvalho de Melo 				param->record_mode = CALLCHAIN_LBR;
30356e2e056SArnaldo Carvalho de Melo 				ret = 0;
30456e2e056SArnaldo Carvalho de Melo 			} else
30556e2e056SArnaldo Carvalho de Melo 				pr_err("callchain: No more arguments "
30656e2e056SArnaldo Carvalho de Melo 					"needed for --call-graph lbr\n");
30756e2e056SArnaldo Carvalho de Melo 			break;
30856e2e056SArnaldo Carvalho de Melo 		} else {
30956e2e056SArnaldo Carvalho de Melo 			pr_err("callchain: Unknown --call-graph option "
31056e2e056SArnaldo Carvalho de Melo 			       "value: %s\n", arg);
31156e2e056SArnaldo Carvalho de Melo 			break;
31256e2e056SArnaldo Carvalho de Melo 		}
31356e2e056SArnaldo Carvalho de Melo 
31456e2e056SArnaldo Carvalho de Melo 	} while (0);
31556e2e056SArnaldo Carvalho de Melo 
31656e2e056SArnaldo Carvalho de Melo 	free(buf);
31756e2e056SArnaldo Carvalho de Melo 	return ret;
31856e2e056SArnaldo Carvalho de Melo }
31956e2e056SArnaldo Carvalho de Melo 
perf_callchain_config(const char * var,const char * value)3202b9240caSNamhyung Kim int perf_callchain_config(const char *var, const char *value)
3212b9240caSNamhyung Kim {
3222b9240caSNamhyung Kim 	char *endptr;
3232b9240caSNamhyung Kim 
3248e99b6d4SArnaldo Carvalho de Melo 	if (!strstarts(var, "call-graph."))
3252b9240caSNamhyung Kim 		return 0;
3262b9240caSNamhyung Kim 	var += sizeof("call-graph.") - 1;
3272b9240caSNamhyung Kim 
3282b9240caSNamhyung Kim 	if (!strcmp(var, "record-mode"))
329c3a6a8c4SKan Liang 		return parse_callchain_record_opt(value, &callchain_param);
3302b9240caSNamhyung Kim 	if (!strcmp(var, "dump-size")) {
3312b9240caSNamhyung Kim 		unsigned long size = 0;
3322b9240caSNamhyung Kim 		int ret;
3332b9240caSNamhyung Kim 
3342b9240caSNamhyung Kim 		ret = get_stack_size(value, &size);
3352b9240caSNamhyung Kim 		callchain_param.dump_size = size;
3362b9240caSNamhyung Kim 
3372b9240caSNamhyung Kim 		return ret;
3382b9240caSNamhyung Kim 	}
3399789e7e9SMengting Zhang 	if (!strcmp(var, "print-type")){
3409789e7e9SMengting Zhang 		int ret;
3419789e7e9SMengting Zhang 		ret = parse_callchain_mode(value);
3429789e7e9SMengting Zhang 		if (ret == -1)
3439789e7e9SMengting Zhang 			pr_err("Invalid callchain mode: %s\n", value);
3449789e7e9SMengting Zhang 		return ret;
3459789e7e9SMengting Zhang 	}
3469789e7e9SMengting Zhang 	if (!strcmp(var, "order")){
3479789e7e9SMengting Zhang 		int ret;
3489789e7e9SMengting Zhang 		ret = parse_callchain_order(value);
3499789e7e9SMengting Zhang 		if (ret == -1)
3509789e7e9SMengting Zhang 			pr_err("Invalid callchain order: %s\n", value);
3519789e7e9SMengting Zhang 		return ret;
3529789e7e9SMengting Zhang 	}
3539789e7e9SMengting Zhang 	if (!strcmp(var, "sort-key")){
3549789e7e9SMengting Zhang 		int ret;
3559789e7e9SMengting Zhang 		ret = parse_callchain_sort_key(value);
3569789e7e9SMengting Zhang 		if (ret == -1)
3579789e7e9SMengting Zhang 			pr_err("Invalid callchain sort key: %s\n", value);
3589789e7e9SMengting Zhang 		return ret;
3599789e7e9SMengting Zhang 	}
3602b9240caSNamhyung Kim 	if (!strcmp(var, "threshold")) {
3612b9240caSNamhyung Kim 		callchain_param.min_percent = strtod(value, &endptr);
362ecc4c561SArnaldo Carvalho de Melo 		if (value == endptr) {
363ecc4c561SArnaldo Carvalho de Melo 			pr_err("Invalid callchain threshold: %s\n", value);
3642b9240caSNamhyung Kim 			return -1;
3652b9240caSNamhyung Kim 		}
366ecc4c561SArnaldo Carvalho de Melo 	}
3672b9240caSNamhyung Kim 	if (!strcmp(var, "print-limit")) {
3682b9240caSNamhyung Kim 		callchain_param.print_limit = strtod(value, &endptr);
369ecc4c561SArnaldo Carvalho de Melo 		if (value == endptr) {
370ecc4c561SArnaldo Carvalho de Melo 			pr_err("Invalid callchain print limit: %s\n", value);
3712b9240caSNamhyung Kim 			return -1;
3722b9240caSNamhyung Kim 		}
373ecc4c561SArnaldo Carvalho de Melo 	}
3742b9240caSNamhyung Kim 
3752b9240caSNamhyung Kim 	return 0;
3762b9240caSNamhyung Kim }
3772b9240caSNamhyung Kim 
378deac911cSFrederic Weisbecker static void
rb_insert_callchain(struct rb_root * root,struct callchain_node * chain,enum chain_mode mode)3794eb3e478SFrederic Weisbecker rb_insert_callchain(struct rb_root *root, struct callchain_node *chain,
3804eb3e478SFrederic Weisbecker 		    enum chain_mode mode)
3818cb76d99SFrederic Weisbecker {
3828cb76d99SFrederic Weisbecker 	struct rb_node **p = &root->rb_node;
3838cb76d99SFrederic Weisbecker 	struct rb_node *parent = NULL;
3848cb76d99SFrederic Weisbecker 	struct callchain_node *rnode;
385f08c3154SFrederic Weisbecker 	u64 chain_cumul = callchain_cumul_hits(chain);
3868cb76d99SFrederic Weisbecker 
3878cb76d99SFrederic Weisbecker 	while (*p) {
3881953287bSFrederic Weisbecker 		u64 rnode_cumul;
3891953287bSFrederic Weisbecker 
3908cb76d99SFrederic Weisbecker 		parent = *p;
3918cb76d99SFrederic Weisbecker 		rnode = rb_entry(parent, struct callchain_node, rb_node);
392f08c3154SFrederic Weisbecker 		rnode_cumul = callchain_cumul_hits(rnode);
3938cb76d99SFrederic Weisbecker 
3944eb3e478SFrederic Weisbecker 		switch (mode) {
395805d127dSFrederic Weisbecker 		case CHAIN_FLAT:
39626e77924SNamhyung Kim 		case CHAIN_FOLDED:
3978cb76d99SFrederic Weisbecker 			if (rnode->hit < chain->hit)
3988cb76d99SFrederic Weisbecker 				p = &(*p)->rb_left;
3998cb76d99SFrederic Weisbecker 			else
4008cb76d99SFrederic Weisbecker 				p = &(*p)->rb_right;
4014eb3e478SFrederic Weisbecker 			break;
402805d127dSFrederic Weisbecker 		case CHAIN_GRAPH_ABS: /* Falldown */
403805d127dSFrederic Weisbecker 		case CHAIN_GRAPH_REL:
4041953287bSFrederic Weisbecker 			if (rnode_cumul < chain_cumul)
4054eb3e478SFrederic Weisbecker 				p = &(*p)->rb_left;
4064eb3e478SFrederic Weisbecker 			else
4074eb3e478SFrederic Weisbecker 				p = &(*p)->rb_right;
4084eb3e478SFrederic Weisbecker 			break;
40983a0944fSIngo Molnar 		case CHAIN_NONE:
4104eb3e478SFrederic Weisbecker 		default:
4114eb3e478SFrederic Weisbecker 			break;
4124eb3e478SFrederic Weisbecker 		}
4138cb76d99SFrederic Weisbecker 	}
4148cb76d99SFrederic Weisbecker 
4158cb76d99SFrederic Weisbecker 	rb_link_node(&chain->rb_node, parent, p);
4168cb76d99SFrederic Weisbecker 	rb_insert_color(&chain->rb_node, root);
4178cb76d99SFrederic Weisbecker }
4188cb76d99SFrederic Weisbecker 
419805d127dSFrederic Weisbecker static void
__sort_chain_flat(struct rb_root * rb_root,struct callchain_node * node,u64 min_hit)420805d127dSFrederic Weisbecker __sort_chain_flat(struct rb_root *rb_root, struct callchain_node *node,
421c20ab37eSFrederic Weisbecker 		  u64 min_hit)
4228cb76d99SFrederic Weisbecker {
423e369517cSNamhyung Kim 	struct rb_node *n;
4248cb76d99SFrederic Weisbecker 	struct callchain_node *child;
4258cb76d99SFrederic Weisbecker 
426e369517cSNamhyung Kim 	n = rb_first(&node->rb_root_in);
427e369517cSNamhyung Kim 	while (n) {
428e369517cSNamhyung Kim 		child = rb_entry(n, struct callchain_node, rb_node_in);
429e369517cSNamhyung Kim 		n = rb_next(n);
430e369517cSNamhyung Kim 
431805d127dSFrederic Weisbecker 		__sort_chain_flat(rb_root, child, min_hit);
432e369517cSNamhyung Kim 	}
4338cb76d99SFrederic Weisbecker 
434c20ab37eSFrederic Weisbecker 	if (node->hit && node->hit >= min_hit)
435805d127dSFrederic Weisbecker 		rb_insert_callchain(rb_root, node, CHAIN_FLAT);
4364eb3e478SFrederic Weisbecker }
4374eb3e478SFrederic Weisbecker 
438805d127dSFrederic Weisbecker /*
439805d127dSFrederic Weisbecker  * Once we get every callchains from the stream, we can now
440805d127dSFrederic Weisbecker  * sort them by hit
441805d127dSFrederic Weisbecker  */
442805d127dSFrederic Weisbecker static void
sort_chain_flat(struct rb_root * rb_root,struct callchain_root * root,u64 min_hit,struct callchain_param * param __maybe_unused)443d2009c51SFrederic Weisbecker sort_chain_flat(struct rb_root *rb_root, struct callchain_root *root,
4441d037ca1SIrina Tirdea 		u64 min_hit, struct callchain_param *param __maybe_unused)
445805d127dSFrederic Weisbecker {
4460356218aSNamhyung Kim 	*rb_root = RB_ROOT;
447d2009c51SFrederic Weisbecker 	__sort_chain_flat(rb_root, &root->node, min_hit);
448805d127dSFrederic Weisbecker }
449805d127dSFrederic Weisbecker 
__sort_chain_graph_abs(struct callchain_node * node,u64 min_hit)450805d127dSFrederic Weisbecker static void __sort_chain_graph_abs(struct callchain_node *node,
451805d127dSFrederic Weisbecker 				   u64 min_hit)
4524eb3e478SFrederic Weisbecker {
453e369517cSNamhyung Kim 	struct rb_node *n;
4544eb3e478SFrederic Weisbecker 	struct callchain_node *child;
4554eb3e478SFrederic Weisbecker 
4564eb3e478SFrederic Weisbecker 	node->rb_root = RB_ROOT;
457e369517cSNamhyung Kim 	n = rb_first(&node->rb_root_in);
4584eb3e478SFrederic Weisbecker 
459e369517cSNamhyung Kim 	while (n) {
460e369517cSNamhyung Kim 		child = rb_entry(n, struct callchain_node, rb_node_in);
461e369517cSNamhyung Kim 		n = rb_next(n);
462e369517cSNamhyung Kim 
463805d127dSFrederic Weisbecker 		__sort_chain_graph_abs(child, min_hit);
464f08c3154SFrederic Weisbecker 		if (callchain_cumul_hits(child) >= min_hit)
465805d127dSFrederic Weisbecker 			rb_insert_callchain(&node->rb_root, child,
466805d127dSFrederic Weisbecker 					    CHAIN_GRAPH_ABS);
4674eb3e478SFrederic Weisbecker 	}
4684eb3e478SFrederic Weisbecker }
4694eb3e478SFrederic Weisbecker 
470805d127dSFrederic Weisbecker static void
sort_chain_graph_abs(struct rb_root * rb_root,struct callchain_root * chain_root,u64 min_hit,struct callchain_param * param __maybe_unused)471d2009c51SFrederic Weisbecker sort_chain_graph_abs(struct rb_root *rb_root, struct callchain_root *chain_root,
4721d037ca1SIrina Tirdea 		     u64 min_hit, struct callchain_param *param __maybe_unused)
4734eb3e478SFrederic Weisbecker {
474d2009c51SFrederic Weisbecker 	__sort_chain_graph_abs(&chain_root->node, min_hit);
475d2009c51SFrederic Weisbecker 	rb_root->rb_node = chain_root->node.rb_root.rb_node;
4768cb76d99SFrederic Weisbecker }
4778cb76d99SFrederic Weisbecker 
__sort_chain_graph_rel(struct callchain_node * node,double min_percent)478805d127dSFrederic Weisbecker static void __sort_chain_graph_rel(struct callchain_node *node,
479805d127dSFrederic Weisbecker 				   double min_percent)
480805d127dSFrederic Weisbecker {
481e369517cSNamhyung Kim 	struct rb_node *n;
482805d127dSFrederic Weisbecker 	struct callchain_node *child;
483805d127dSFrederic Weisbecker 	u64 min_hit;
484805d127dSFrederic Weisbecker 
485805d127dSFrederic Weisbecker 	node->rb_root = RB_ROOT;
486c0a8865eSFrederic Weisbecker 	min_hit = ceil(node->children_hit * min_percent);
487805d127dSFrederic Weisbecker 
488e369517cSNamhyung Kim 	n = rb_first(&node->rb_root_in);
489e369517cSNamhyung Kim 	while (n) {
490e369517cSNamhyung Kim 		child = rb_entry(n, struct callchain_node, rb_node_in);
491e369517cSNamhyung Kim 		n = rb_next(n);
492e369517cSNamhyung Kim 
493805d127dSFrederic Weisbecker 		__sort_chain_graph_rel(child, min_percent);
494f08c3154SFrederic Weisbecker 		if (callchain_cumul_hits(child) >= min_hit)
495805d127dSFrederic Weisbecker 			rb_insert_callchain(&node->rb_root, child,
496805d127dSFrederic Weisbecker 					    CHAIN_GRAPH_REL);
497805d127dSFrederic Weisbecker 	}
498805d127dSFrederic Weisbecker }
499805d127dSFrederic Weisbecker 
500805d127dSFrederic Weisbecker static void
sort_chain_graph_rel(struct rb_root * rb_root,struct callchain_root * chain_root,u64 min_hit __maybe_unused,struct callchain_param * param)501d2009c51SFrederic Weisbecker sort_chain_graph_rel(struct rb_root *rb_root, struct callchain_root *chain_root,
5021d037ca1SIrina Tirdea 		     u64 min_hit __maybe_unused, struct callchain_param *param)
503805d127dSFrederic Weisbecker {
504d2009c51SFrederic Weisbecker 	__sort_chain_graph_rel(&chain_root->node, param->min_percent / 100.0);
505d2009c51SFrederic Weisbecker 	rb_root->rb_node = chain_root->node.rb_root.rb_node;
506805d127dSFrederic Weisbecker }
507805d127dSFrederic Weisbecker 
callchain_register_param(struct callchain_param * param)50816537f13SFrederic Weisbecker int callchain_register_param(struct callchain_param *param)
509805d127dSFrederic Weisbecker {
510805d127dSFrederic Weisbecker 	switch (param->mode) {
511805d127dSFrederic Weisbecker 	case CHAIN_GRAPH_ABS:
512805d127dSFrederic Weisbecker 		param->sort = sort_chain_graph_abs;
513805d127dSFrederic Weisbecker 		break;
514805d127dSFrederic Weisbecker 	case CHAIN_GRAPH_REL:
515805d127dSFrederic Weisbecker 		param->sort = sort_chain_graph_rel;
516805d127dSFrederic Weisbecker 		break;
517805d127dSFrederic Weisbecker 	case CHAIN_FLAT:
51826e77924SNamhyung Kim 	case CHAIN_FOLDED:
519805d127dSFrederic Weisbecker 		param->sort = sort_chain_flat;
520805d127dSFrederic Weisbecker 		break;
52183a0944fSIngo Molnar 	case CHAIN_NONE:
522805d127dSFrederic Weisbecker 	default:
523805d127dSFrederic Weisbecker 		return -1;
524805d127dSFrederic Weisbecker 	}
525805d127dSFrederic Weisbecker 	return 0;
526805d127dSFrederic Weisbecker }
527805d127dSFrederic Weisbecker 
528deac911cSFrederic Weisbecker /*
529deac911cSFrederic Weisbecker  * Create a child for a parent. If inherit_children, then the new child
530deac911cSFrederic Weisbecker  * will become the new parent of it's parent children
531deac911cSFrederic Weisbecker  */
532deac911cSFrederic Weisbecker static struct callchain_node *
create_child(struct callchain_node * parent,bool inherit_children)533deac911cSFrederic Weisbecker create_child(struct callchain_node *parent, bool inherit_children)
5348cb76d99SFrederic Weisbecker {
5358cb76d99SFrederic Weisbecker 	struct callchain_node *new;
5368cb76d99SFrederic Weisbecker 
537cdd5b75bSArnaldo Carvalho de Melo 	new = zalloc(sizeof(*new));
5388cb76d99SFrederic Weisbecker 	if (!new) {
5398cb76d99SFrederic Weisbecker 		perror("not enough memory to create child for code path tree");
5408cb76d99SFrederic Weisbecker 		return NULL;
5418cb76d99SFrederic Weisbecker 	}
5428cb76d99SFrederic Weisbecker 	new->parent = parent;
5438cb76d99SFrederic Weisbecker 	INIT_LIST_HEAD(&new->val);
5444b3a3212SNamhyung Kim 	INIT_LIST_HEAD(&new->parent_val);
545deac911cSFrederic Weisbecker 
546deac911cSFrederic Weisbecker 	if (inherit_children) {
547e369517cSNamhyung Kim 		struct rb_node *n;
548e369517cSNamhyung Kim 		struct callchain_node *child;
549deac911cSFrederic Weisbecker 
550e369517cSNamhyung Kim 		new->rb_root_in = parent->rb_root_in;
551e369517cSNamhyung Kim 		parent->rb_root_in = RB_ROOT;
552deac911cSFrederic Weisbecker 
553e369517cSNamhyung Kim 		n = rb_first(&new->rb_root_in);
554e369517cSNamhyung Kim 		while (n) {
555e369517cSNamhyung Kim 			child = rb_entry(n, struct callchain_node, rb_node_in);
556e369517cSNamhyung Kim 			child->parent = new;
557e369517cSNamhyung Kim 			n = rb_next(n);
558deac911cSFrederic Weisbecker 		}
559e369517cSNamhyung Kim 
560e369517cSNamhyung Kim 		/* make it the first child */
561e369517cSNamhyung Kim 		rb_link_node(&new->rb_node_in, NULL, &parent->rb_root_in.rb_node);
562e369517cSNamhyung Kim 		rb_insert_color(&new->rb_node_in, &parent->rb_root_in);
563e369517cSNamhyung Kim 	}
5648cb76d99SFrederic Weisbecker 
5658cb76d99SFrederic Weisbecker 	return new;
5668cb76d99SFrederic Weisbecker }
5678cb76d99SFrederic Weisbecker 
568301fde27SFrederic Weisbecker 
569deac911cSFrederic Weisbecker /*
570deac911cSFrederic Weisbecker  * Fill the node with callchain values
571deac911cSFrederic Weisbecker  */
5728451cbb9SNamhyung Kim static int
fill_node(struct callchain_node * node,struct callchain_cursor * cursor)5731b3a0e95SFrederic Weisbecker fill_node(struct callchain_node *node, struct callchain_cursor *cursor)
5748cb76d99SFrederic Weisbecker {
5751b3a0e95SFrederic Weisbecker 	struct callchain_cursor_node *cursor_node;
5768cb76d99SFrederic Weisbecker 
5771b3a0e95SFrederic Weisbecker 	node->val_nr = cursor->nr - cursor->pos;
5781b3a0e95SFrederic Weisbecker 	if (!node->val_nr)
5791b3a0e95SFrederic Weisbecker 		pr_warning("Warning: empty node in callchain tree\n");
5801b3a0e95SFrederic Weisbecker 
5811b3a0e95SFrederic Weisbecker 	cursor_node = callchain_cursor_current(cursor);
5821b3a0e95SFrederic Weisbecker 
5831b3a0e95SFrederic Weisbecker 	while (cursor_node) {
5848cb76d99SFrederic Weisbecker 		struct callchain_list *call;
5858cb76d99SFrederic Weisbecker 
586cdd5b75bSArnaldo Carvalho de Melo 		call = zalloc(sizeof(*call));
5878cb76d99SFrederic Weisbecker 		if (!call) {
5888cb76d99SFrederic Weisbecker 			perror("not enough memory for the code path tree");
5898451cbb9SNamhyung Kim 			return -1;
5908cb76d99SFrederic Weisbecker 		}
5911b3a0e95SFrederic Weisbecker 		call->ip = cursor_node->ip;
5925f0fef8aSArnaldo Carvalho de Melo 		call->ms = cursor_node->ms;
593ec417ad4SIan Rogers 		call->ms.map = map__get(call->ms.map);
594bffb5b0cSIan Rogers 		call->ms.maps = maps__get(call->ms.maps);
59540a342cdSMilian Wolff 		call->srcline = cursor_node->srcline;
5963dd029efSJin Yao 
5973dd029efSJin Yao 		if (cursor_node->branch) {
5983dd029efSJin Yao 			call->branch_count = 1;
5993dd029efSJin Yao 
600a1a8bed3SJin Yao 			if (cursor_node->branch_from) {
601a1a8bed3SJin Yao 				/*
602a1a8bed3SJin Yao 				 * branch_from is set with value somewhere else
603a1a8bed3SJin Yao 				 * to imply it's "to" of a branch.
604a1a8bed3SJin Yao 				 */
605a1a8bed3SJin Yao 				call->brtype_stat.branch_to = true;
606a1a8bed3SJin Yao 
6073dd029efSJin Yao 				if (cursor_node->branch_flags.predicted)
6083dd029efSJin Yao 					call->predicted_count = 1;
6093dd029efSJin Yao 
6103dd029efSJin Yao 				if (cursor_node->branch_flags.abort)
6113dd029efSJin Yao 					call->abort_count = 1;
6123dd029efSJin Yao 
613b851dd49SJin Yao 				branch_type_count(&call->brtype_stat,
614b851dd49SJin Yao 						  &cursor_node->branch_flags,
615b851dd49SJin Yao 						  cursor_node->branch_from,
616b851dd49SJin Yao 						  cursor_node->ip);
617a1a8bed3SJin Yao 			} else {
618a1a8bed3SJin Yao 				/*
619a1a8bed3SJin Yao 				 * It's "from" of a branch
620a1a8bed3SJin Yao 				 */
621a1a8bed3SJin Yao 				call->brtype_stat.branch_to = false;
622a1a8bed3SJin Yao 				call->cycles_count =
623a1a8bed3SJin Yao 					cursor_node->branch_flags.cycles;
624a1a8bed3SJin Yao 				call->iter_count = cursor_node->nr_loop_iter;
625c4ee0625SJin Yao 				call->iter_cycles = cursor_node->iter_cycles;
626a1a8bed3SJin Yao 			}
6273dd029efSJin Yao 		}
6283dd029efSJin Yao 
6298cb76d99SFrederic Weisbecker 		list_add_tail(&call->list, &node->val);
6301b3a0e95SFrederic Weisbecker 
6311b3a0e95SFrederic Weisbecker 		callchain_cursor_advance(cursor);
6321b3a0e95SFrederic Weisbecker 		cursor_node = callchain_cursor_current(cursor);
6338cb76d99SFrederic Weisbecker 	}
6348451cbb9SNamhyung Kim 	return 0;
6358cb76d99SFrederic Weisbecker }
6368cb76d99SFrederic Weisbecker 
637e369517cSNamhyung Kim static struct callchain_node *
add_child(struct callchain_node * parent,struct callchain_cursor * cursor,u64 period)6381b3a0e95SFrederic Weisbecker add_child(struct callchain_node *parent,
6391b3a0e95SFrederic Weisbecker 	  struct callchain_cursor *cursor,
6401b3a0e95SFrederic Weisbecker 	  u64 period)
6418cb76d99SFrederic Weisbecker {
6428cb76d99SFrederic Weisbecker 	struct callchain_node *new;
6438cb76d99SFrederic Weisbecker 
644deac911cSFrederic Weisbecker 	new = create_child(parent, false);
6457565bd39SNamhyung Kim 	if (new == NULL)
6467565bd39SNamhyung Kim 		return NULL;
6477565bd39SNamhyung Kim 
6488451cbb9SNamhyung Kim 	if (fill_node(new, cursor) < 0) {
6498451cbb9SNamhyung Kim 		struct callchain_list *call, *tmp;
6508451cbb9SNamhyung Kim 
6518451cbb9SNamhyung Kim 		list_for_each_entry_safe(call, tmp, &new->val, list) {
652e56fbc9dSArnaldo Carvalho de Melo 			list_del_init(&call->list);
6539c68ae98SKrister Johansen 			map__zput(call->ms.map);
654bffb5b0cSIan Rogers 			maps__zput(call->ms.maps);
6558451cbb9SNamhyung Kim 			free(call);
6568451cbb9SNamhyung Kim 		}
6578451cbb9SNamhyung Kim 		free(new);
6588451cbb9SNamhyung Kim 		return NULL;
6598451cbb9SNamhyung Kim 	}
6608cb76d99SFrederic Weisbecker 
6611953287bSFrederic Weisbecker 	new->children_hit = 0;
662108553e1SFrederic Weisbecker 	new->hit = period;
6635e47f8ffSNamhyung Kim 	new->children_count = 0;
6645e47f8ffSNamhyung Kim 	new->count = 1;
665e369517cSNamhyung Kim 	return new;
666e369517cSNamhyung Kim }
667e369517cSNamhyung Kim 
6682d713b80SNamhyung Kim enum match_result {
6692d713b80SNamhyung Kim 	MATCH_ERROR  = -1,
6702d713b80SNamhyung Kim 	MATCH_EQ,
6712d713b80SNamhyung Kim 	MATCH_LT,
6722d713b80SNamhyung Kim 	MATCH_GT,
6732d713b80SNamhyung Kim };
6742d713b80SNamhyung Kim 
match_chain_strings(const char * left,const char * right)675cbe50f61SMilian Wolff static enum match_result match_chain_strings(const char *left,
676cbe50f61SMilian Wolff 					     const char *right)
6775dfa210eSMilian Wolff {
6785dfa210eSMilian Wolff 	enum match_result ret = MATCH_EQ;
6795dfa210eSMilian Wolff 	int cmp;
6805dfa210eSMilian Wolff 
6815dfa210eSMilian Wolff 	if (left && right)
6825dfa210eSMilian Wolff 		cmp = strcmp(left, right);
6835dfa210eSMilian Wolff 	else if (!left && right)
6845dfa210eSMilian Wolff 		cmp = 1;
6855dfa210eSMilian Wolff 	else if (left && !right)
6865dfa210eSMilian Wolff 		cmp = -1;
6875dfa210eSMilian Wolff 	else
688cbe50f61SMilian Wolff 		return MATCH_ERROR;
6895dfa210eSMilian Wolff 
6905dfa210eSMilian Wolff 	if (cmp != 0)
6915dfa210eSMilian Wolff 		ret = cmp < 0 ? MATCH_LT : MATCH_GT;
6925dfa210eSMilian Wolff 
6935dfa210eSMilian Wolff 	return ret;
6945dfa210eSMilian Wolff }
6955dfa210eSMilian Wolff 
696bf36eb5cSMilian Wolff /*
697bf36eb5cSMilian Wolff  * We need to always use relative addresses because we're aggregating
698bf36eb5cSMilian Wolff  * callchains from multiple threads, i.e. different address spaces, so
699bf36eb5cSMilian Wolff  * comparing absolute addresses make no sense as a symbol in a DSO may end up
700bf36eb5cSMilian Wolff  * in a different address when used in a different binary or even the same
701bf36eb5cSMilian Wolff  * binary but with some sort of address randomization technique, thus we need
702bf36eb5cSMilian Wolff  * to compare just relative addresses. -acme
703bf36eb5cSMilian Wolff  */
match_chain_dso_addresses(struct map * left_map,u64 left_ip,struct map * right_map,u64 right_ip)704bf36eb5cSMilian Wolff static enum match_result match_chain_dso_addresses(struct map *left_map, u64 left_ip,
705bf36eb5cSMilian Wolff 						   struct map *right_map, u64 right_ip)
706bf36eb5cSMilian Wolff {
70763df0e4bSIan Rogers 	struct dso *left_dso = left_map ? map__dso(left_map) : NULL;
70863df0e4bSIan Rogers 	struct dso *right_dso = right_map ? map__dso(right_map) : NULL;
709bf36eb5cSMilian Wolff 
710bf36eb5cSMilian Wolff 	if (left_dso != right_dso)
711bf36eb5cSMilian Wolff 		return left_dso < right_dso ? MATCH_LT : MATCH_GT;
712bf36eb5cSMilian Wolff 
713bf36eb5cSMilian Wolff 	if (left_ip != right_ip)
714bf36eb5cSMilian Wolff  		return left_ip < right_ip ? MATCH_LT : MATCH_GT;
715bf36eb5cSMilian Wolff 
716bf36eb5cSMilian Wolff 	return MATCH_EQ;
717bf36eb5cSMilian Wolff }
718bf36eb5cSMilian Wolff 
match_chain(struct callchain_cursor_node * node,struct callchain_list * cnode)7192d713b80SNamhyung Kim static enum match_result match_chain(struct callchain_cursor_node *node,
720e369517cSNamhyung Kim 				     struct callchain_list *cnode)
721e369517cSNamhyung Kim {
722bf36eb5cSMilian Wolff 	enum match_result match = MATCH_ERROR;
723e369517cSNamhyung Kim 
724bf36eb5cSMilian Wolff 	switch (callchain_param.key) {
725bf36eb5cSMilian Wolff 	case CCKEY_SRCLINE:
726bf36eb5cSMilian Wolff 		match = match_chain_strings(cnode->srcline, node->srcline);
727bf36eb5cSMilian Wolff 		if (match != MATCH_ERROR)
728bf36eb5cSMilian Wolff 			break;
729bf36eb5cSMilian Wolff 		/* otherwise fall-back to symbol-based comparison below */
730f7a858bfSLiam Howlett 		fallthrough;
731bf36eb5cSMilian Wolff 	case CCKEY_FUNCTION:
7325f0fef8aSArnaldo Carvalho de Melo 		if (node->ms.sym && cnode->ms.sym) {
733bf36eb5cSMilian Wolff 			/*
734bf36eb5cSMilian Wolff 			 * Compare inlined frames based on their symbol name
735bf36eb5cSMilian Wolff 			 * because different inlined frames will have the same
736bf36eb5cSMilian Wolff 			 * symbol start. Otherwise do a faster comparison based
737bf36eb5cSMilian Wolff 			 * on the symbol start address.
738bf36eb5cSMilian Wolff 			 */
7395f0fef8aSArnaldo Carvalho de Melo 			if (cnode->ms.sym->inlined || node->ms.sym->inlined) {
740cbe50f61SMilian Wolff 				match = match_chain_strings(cnode->ms.sym->name,
7415f0fef8aSArnaldo Carvalho de Melo 							    node->ms.sym->name);
7425dfa210eSMilian Wolff 				if (match != MATCH_ERROR)
743bf36eb5cSMilian Wolff 					break;
7442d713b80SNamhyung Kim 			} else {
745bf36eb5cSMilian Wolff 				match = match_chain_dso_addresses(cnode->ms.map, cnode->ms.sym->start,
7465f0fef8aSArnaldo Carvalho de Melo 								  node->ms.map, node->ms.sym->start);
747bf36eb5cSMilian Wolff 				break;
748bf36eb5cSMilian Wolff 			}
749bf36eb5cSMilian Wolff 		}
750bf36eb5cSMilian Wolff 		/* otherwise fall-back to IP-based comparison below */
751f7a858bfSLiam Howlett 		fallthrough;
752bf36eb5cSMilian Wolff 	case CCKEY_ADDRESS:
753bf36eb5cSMilian Wolff 	default:
7545f0fef8aSArnaldo Carvalho de Melo 		match = match_chain_dso_addresses(cnode->ms.map, cnode->ip, node->ms.map, node->ip);
755bf36eb5cSMilian Wolff 		break;
7562d713b80SNamhyung Kim 	}
7572d713b80SNamhyung Kim 
758bf36eb5cSMilian Wolff 	if (match == MATCH_EQ && node->branch) {
7593dd029efSJin Yao 		cnode->branch_count++;
7603dd029efSJin Yao 
761a1a8bed3SJin Yao 		if (node->branch_from) {
762a1a8bed3SJin Yao 			/*
763a1a8bed3SJin Yao 			 * It's "to" of a branch
764a1a8bed3SJin Yao 			 */
765a1a8bed3SJin Yao 			cnode->brtype_stat.branch_to = true;
766a1a8bed3SJin Yao 
7673dd029efSJin Yao 			if (node->branch_flags.predicted)
7683dd029efSJin Yao 				cnode->predicted_count++;
7693dd029efSJin Yao 
7703dd029efSJin Yao 			if (node->branch_flags.abort)
7713dd029efSJin Yao 				cnode->abort_count++;
7723dd029efSJin Yao 
773b851dd49SJin Yao 			branch_type_count(&cnode->brtype_stat,
774b851dd49SJin Yao 					  &node->branch_flags,
775b851dd49SJin Yao 					  node->branch_from,
776b851dd49SJin Yao 					  node->ip);
777a1a8bed3SJin Yao 		} else {
778a1a8bed3SJin Yao 			/*
779a1a8bed3SJin Yao 			 * It's "from" of a branch
780a1a8bed3SJin Yao 			 */
781a1a8bed3SJin Yao 			cnode->brtype_stat.branch_to = false;
782bf36eb5cSMilian Wolff 			cnode->cycles_count += node->branch_flags.cycles;
783a1a8bed3SJin Yao 			cnode->iter_count += node->nr_loop_iter;
784c4ee0625SJin Yao 			cnode->iter_cycles += node->iter_cycles;
785a3366db0SJin Yao 			cnode->from_count++;
786a1a8bed3SJin Yao 		}
7873dd029efSJin Yao 	}
7883dd029efSJin Yao 
789bf36eb5cSMilian Wolff 	return match;
7908cb76d99SFrederic Weisbecker }
7918cb76d99SFrederic Weisbecker 
792deac911cSFrederic Weisbecker /*
793deac911cSFrederic Weisbecker  * Split the parent in two parts (a new child is created) and
794deac911cSFrederic Weisbecker  * give a part of its callchain to the created child.
795deac911cSFrederic Weisbecker  * Then create another child to host the given callchain of new branch
796deac911cSFrederic Weisbecker  */
797f2bb4c5aSNamhyung Kim static int
split_add_child(struct callchain_node * parent,struct callchain_cursor * cursor,struct callchain_list * to_split,u64 idx_parents,u64 idx_local,u64 period)7981b3a0e95SFrederic Weisbecker split_add_child(struct callchain_node *parent,
7991b3a0e95SFrederic Weisbecker 		struct callchain_cursor *cursor,
8001b3a0e95SFrederic Weisbecker 		struct callchain_list *to_split,
8011b3a0e95SFrederic Weisbecker 		u64 idx_parents, u64 idx_local, u64 period)
8028cb76d99SFrederic Weisbecker {
8038cb76d99SFrederic Weisbecker 	struct callchain_node *new;
804deac911cSFrederic Weisbecker 	struct list_head *old_tail;
805f37a291cSIngo Molnar 	unsigned int idx_total = idx_parents + idx_local;
8068cb76d99SFrederic Weisbecker 
8078cb76d99SFrederic Weisbecker 	/* split */
808deac911cSFrederic Weisbecker 	new = create_child(parent, true);
809f2bb4c5aSNamhyung Kim 	if (new == NULL)
810f2bb4c5aSNamhyung Kim 		return -1;
8118cb76d99SFrederic Weisbecker 
812deac911cSFrederic Weisbecker 	/* split the callchain and move a part to the new child */
813deac911cSFrederic Weisbecker 	old_tail = parent->val.prev;
814deac911cSFrederic Weisbecker 	list_del_range(&to_split->list, old_tail);
815deac911cSFrederic Weisbecker 	new->val.next = &to_split->list;
816deac911cSFrederic Weisbecker 	new->val.prev = old_tail;
817deac911cSFrederic Weisbecker 	to_split->list.prev = &new->val;
818deac911cSFrederic Weisbecker 	old_tail->next = &new->val;
819deac911cSFrederic Weisbecker 
820deac911cSFrederic Weisbecker 	/* split the hits */
821deac911cSFrederic Weisbecker 	new->hit = parent->hit;
8221953287bSFrederic Weisbecker 	new->children_hit = parent->children_hit;
823f08c3154SFrederic Weisbecker 	parent->children_hit = callchain_cumul_hits(new);
824deac911cSFrederic Weisbecker 	new->val_nr = parent->val_nr - idx_local;
825deac911cSFrederic Weisbecker 	parent->val_nr = idx_local;
8265e47f8ffSNamhyung Kim 	new->count = parent->count;
8275e47f8ffSNamhyung Kim 	new->children_count = parent->children_count;
8285e47f8ffSNamhyung Kim 	parent->children_count = callchain_cumul_counts(new);
829deac911cSFrederic Weisbecker 
830deac911cSFrederic Weisbecker 	/* create a new child for the new branch if any */
8311b3a0e95SFrederic Weisbecker 	if (idx_total < cursor->nr) {
832e369517cSNamhyung Kim 		struct callchain_node *first;
833e369517cSNamhyung Kim 		struct callchain_list *cnode;
834e369517cSNamhyung Kim 		struct callchain_cursor_node *node;
835e369517cSNamhyung Kim 		struct rb_node *p, **pp;
836e369517cSNamhyung Kim 
837deac911cSFrederic Weisbecker 		parent->hit = 0;
838108553e1SFrederic Weisbecker 		parent->children_hit += period;
8395e47f8ffSNamhyung Kim 		parent->count = 0;
8405e47f8ffSNamhyung Kim 		parent->children_count += 1;
841e369517cSNamhyung Kim 
842e369517cSNamhyung Kim 		node = callchain_cursor_current(cursor);
843e369517cSNamhyung Kim 		new = add_child(parent, cursor, period);
8447565bd39SNamhyung Kim 		if (new == NULL)
845f2bb4c5aSNamhyung Kim 			return -1;
846e369517cSNamhyung Kim 
847e369517cSNamhyung Kim 		/*
848e369517cSNamhyung Kim 		 * This is second child since we moved parent's children
849e369517cSNamhyung Kim 		 * to new (first) child above.
850e369517cSNamhyung Kim 		 */
851e369517cSNamhyung Kim 		p = parent->rb_root_in.rb_node;
852e369517cSNamhyung Kim 		first = rb_entry(p, struct callchain_node, rb_node_in);
853e369517cSNamhyung Kim 		cnode = list_first_entry(&first->val, struct callchain_list,
854e369517cSNamhyung Kim 					 list);
855e369517cSNamhyung Kim 
8562d713b80SNamhyung Kim 		if (match_chain(node, cnode) == MATCH_LT)
857e369517cSNamhyung Kim 			pp = &p->rb_left;
858e369517cSNamhyung Kim 		else
859e369517cSNamhyung Kim 			pp = &p->rb_right;
860e369517cSNamhyung Kim 
861e369517cSNamhyung Kim 		rb_link_node(&new->rb_node_in, p, pp);
862e369517cSNamhyung Kim 		rb_insert_color(&new->rb_node_in, &parent->rb_root_in);
863deac911cSFrederic Weisbecker 	} else {
864108553e1SFrederic Weisbecker 		parent->hit = period;
8655e47f8ffSNamhyung Kim 		parent->count = 1;
866deac911cSFrederic Weisbecker 	}
867f2bb4c5aSNamhyung Kim 	return 0;
8688cb76d99SFrederic Weisbecker }
8698cb76d99SFrederic Weisbecker 
8702d713b80SNamhyung Kim static enum match_result
8711b3a0e95SFrederic Weisbecker append_chain(struct callchain_node *root,
8721b3a0e95SFrederic Weisbecker 	     struct callchain_cursor *cursor,
8731b3a0e95SFrederic Weisbecker 	     u64 period);
8748cb76d99SFrederic Weisbecker 
875dca0d122SNamhyung Kim static int
append_chain_children(struct callchain_node * root,struct callchain_cursor * cursor,u64 period)8761b3a0e95SFrederic Weisbecker append_chain_children(struct callchain_node *root,
8771b3a0e95SFrederic Weisbecker 		      struct callchain_cursor *cursor,
8781b3a0e95SFrederic Weisbecker 		      u64 period)
8798cb76d99SFrederic Weisbecker {
8808cb76d99SFrederic Weisbecker 	struct callchain_node *rnode;
881e369517cSNamhyung Kim 	struct callchain_cursor_node *node;
882e369517cSNamhyung Kim 	struct rb_node **p = &root->rb_root_in.rb_node;
883e369517cSNamhyung Kim 	struct rb_node *parent = NULL;
884e369517cSNamhyung Kim 
885e369517cSNamhyung Kim 	node = callchain_cursor_current(cursor);
886e369517cSNamhyung Kim 	if (!node)
887dca0d122SNamhyung Kim 		return -1;
8888cb76d99SFrederic Weisbecker 
8894d39c89fSIngo Molnar 	/* lookup in children */
890e369517cSNamhyung Kim 	while (*p) {
8912d713b80SNamhyung Kim 		enum match_result ret;
892f37a291cSIngo Molnar 
893e369517cSNamhyung Kim 		parent = *p;
894e369517cSNamhyung Kim 		rnode = rb_entry(parent, struct callchain_node, rb_node_in);
895e369517cSNamhyung Kim 
896b965bb41SFrederic Weisbecker 		/* If at least first entry matches, rely to children */
897b965bb41SFrederic Weisbecker 		ret = append_chain(rnode, cursor, period);
8982d713b80SNamhyung Kim 		if (ret == MATCH_EQ)
8991953287bSFrederic Weisbecker 			goto inc_children_hit;
900dca0d122SNamhyung Kim 		if (ret == MATCH_ERROR)
901dca0d122SNamhyung Kim 			return -1;
902e369517cSNamhyung Kim 
9032d713b80SNamhyung Kim 		if (ret == MATCH_LT)
904e369517cSNamhyung Kim 			p = &parent->rb_left;
905e369517cSNamhyung Kim 		else
906e369517cSNamhyung Kim 			p = &parent->rb_right;
907e369517cSNamhyung Kim 	}
908deac911cSFrederic Weisbecker 	/* nothing in children, add to the current node */
909e369517cSNamhyung Kim 	rnode = add_child(root, cursor, period);
9107565bd39SNamhyung Kim 	if (rnode == NULL)
911dca0d122SNamhyung Kim 		return -1;
9127565bd39SNamhyung Kim 
913e369517cSNamhyung Kim 	rb_link_node(&rnode->rb_node_in, parent, p);
914e369517cSNamhyung Kim 	rb_insert_color(&rnode->rb_node_in, &root->rb_root_in);
915e05b876cSFrederic Weisbecker 
9161953287bSFrederic Weisbecker inc_children_hit:
917108553e1SFrederic Weisbecker 	root->children_hit += period;
9185e47f8ffSNamhyung Kim 	root->children_count++;
919dca0d122SNamhyung Kim 	return 0;
9208cb76d99SFrederic Weisbecker }
9218cb76d99SFrederic Weisbecker 
9222d713b80SNamhyung Kim static enum match_result
append_chain(struct callchain_node * root,struct callchain_cursor * cursor,u64 period)9231b3a0e95SFrederic Weisbecker append_chain(struct callchain_node *root,
9241b3a0e95SFrederic Weisbecker 	     struct callchain_cursor *cursor,
9251b3a0e95SFrederic Weisbecker 	     u64 period)
9268cb76d99SFrederic Weisbecker {
9278cb76d99SFrederic Weisbecker 	struct callchain_list *cnode;
9281b3a0e95SFrederic Weisbecker 	u64 start = cursor->pos;
9298cb76d99SFrederic Weisbecker 	bool found = false;
9301b3a0e95SFrederic Weisbecker 	u64 matches;
9312d713b80SNamhyung Kim 	enum match_result cmp = MATCH_ERROR;
9328cb76d99SFrederic Weisbecker 
933deac911cSFrederic Weisbecker 	/*
934deac911cSFrederic Weisbecker 	 * Lookup in the current node
935deac911cSFrederic Weisbecker 	 * If we have a symbol, then compare the start to match
93699571ab3SAndi Kleen 	 * anywhere inside a function, unless function
93799571ab3SAndi Kleen 	 * mode is disabled.
938deac911cSFrederic Weisbecker 	 */
9398cb76d99SFrederic Weisbecker 	list_for_each_entry(cnode, &root->val, list) {
9401b3a0e95SFrederic Weisbecker 		struct callchain_cursor_node *node;
941301fde27SFrederic Weisbecker 
9421b3a0e95SFrederic Weisbecker 		node = callchain_cursor_current(cursor);
9431b3a0e95SFrederic Weisbecker 		if (!node)
944deac911cSFrederic Weisbecker 			break;
945301fde27SFrederic Weisbecker 
946b965bb41SFrederic Weisbecker 		cmp = match_chain(node, cnode);
9472d713b80SNamhyung Kim 		if (cmp != MATCH_EQ)
9488cb76d99SFrederic Weisbecker 			break;
949301fde27SFrederic Weisbecker 
9508cb76d99SFrederic Weisbecker 		found = true;
9511b3a0e95SFrederic Weisbecker 
9521b3a0e95SFrederic Weisbecker 		callchain_cursor_advance(cursor);
9538cb76d99SFrederic Weisbecker 	}
9548cb76d99SFrederic Weisbecker 
955e369517cSNamhyung Kim 	/* matches not, relay no the parent */
9561b3a0e95SFrederic Weisbecker 	if (!found) {
9572d713b80SNamhyung Kim 		WARN_ONCE(cmp == MATCH_ERROR, "Chain comparison error\n");
958b965bb41SFrederic Weisbecker 		return cmp;
9591b3a0e95SFrederic Weisbecker 	}
9601b3a0e95SFrederic Weisbecker 
9611b3a0e95SFrederic Weisbecker 	matches = cursor->pos - start;
9628cb76d99SFrederic Weisbecker 
9638cb76d99SFrederic Weisbecker 	/* we match only a part of the node. Split it and add the new chain */
9641b3a0e95SFrederic Weisbecker 	if (matches < root->val_nr) {
965f2bb4c5aSNamhyung Kim 		if (split_add_child(root, cursor, cnode, start, matches,
966f2bb4c5aSNamhyung Kim 				    period) < 0)
967f2bb4c5aSNamhyung Kim 			return MATCH_ERROR;
968f2bb4c5aSNamhyung Kim 
9692d713b80SNamhyung Kim 		return MATCH_EQ;
9708cb76d99SFrederic Weisbecker 	}
9718cb76d99SFrederic Weisbecker 
9728cb76d99SFrederic Weisbecker 	/* we match 100% of the path, increment the hit */
9731b3a0e95SFrederic Weisbecker 	if (matches == root->val_nr && cursor->pos == cursor->nr) {
974108553e1SFrederic Weisbecker 		root->hit += period;
9755e47f8ffSNamhyung Kim 		root->count++;
9762d713b80SNamhyung Kim 		return MATCH_EQ;
9778cb76d99SFrederic Weisbecker 	}
9788cb76d99SFrederic Weisbecker 
979deac911cSFrederic Weisbecker 	/* We match the node and still have a part remaining */
980dca0d122SNamhyung Kim 	if (append_chain_children(root, cursor, period) < 0)
981dca0d122SNamhyung Kim 		return MATCH_ERROR;
982deac911cSFrederic Weisbecker 
9832d713b80SNamhyung Kim 	return MATCH_EQ;
9848cb76d99SFrederic Weisbecker }
9858cb76d99SFrederic Weisbecker 
callchain_append(struct callchain_root * root,struct callchain_cursor * cursor,u64 period)9861b3a0e95SFrederic Weisbecker int callchain_append(struct callchain_root *root,
9871b3a0e95SFrederic Weisbecker 		     struct callchain_cursor *cursor,
9881b3a0e95SFrederic Weisbecker 		     u64 period)
9898cb76d99SFrederic Weisbecker {
990*8ab12a20SIan Rogers 	if (cursor == NULL)
991*8ab12a20SIan Rogers 		return -1;
992*8ab12a20SIan Rogers 
9931b3a0e95SFrederic Weisbecker 	if (!cursor->nr)
994301fde27SFrederic Weisbecker 		return 0;
995301fde27SFrederic Weisbecker 
9961b3a0e95SFrederic Weisbecker 	callchain_cursor_commit(cursor);
997301fde27SFrederic Weisbecker 
998dca0d122SNamhyung Kim 	if (append_chain_children(&root->node, cursor, period) < 0)
999dca0d122SNamhyung Kim 		return -1;
1000301fde27SFrederic Weisbecker 
10011b3a0e95SFrederic Weisbecker 	if (cursor->nr > root->max_depth)
10021b3a0e95SFrederic Weisbecker 		root->max_depth = cursor->nr;
1003301fde27SFrederic Weisbecker 
1004301fde27SFrederic Weisbecker 	return 0;
10058cb76d99SFrederic Weisbecker }
1006612d4fd7SFrederic Weisbecker 
1007612d4fd7SFrederic Weisbecker static int
merge_chain_branch(struct callchain_cursor * cursor,struct callchain_node * dst,struct callchain_node * src)10081b3a0e95SFrederic Weisbecker merge_chain_branch(struct callchain_cursor *cursor,
10091b3a0e95SFrederic Weisbecker 		   struct callchain_node *dst, struct callchain_node *src)
1010612d4fd7SFrederic Weisbecker {
10111b3a0e95SFrederic Weisbecker 	struct callchain_cursor_node **old_last = cursor->last;
1012e369517cSNamhyung Kim 	struct callchain_node *child;
1013612d4fd7SFrederic Weisbecker 	struct callchain_list *list, *next_list;
1014e369517cSNamhyung Kim 	struct rb_node *n;
10151b3a0e95SFrederic Weisbecker 	int old_pos = cursor->nr;
1016612d4fd7SFrederic Weisbecker 	int err = 0;
1017612d4fd7SFrederic Weisbecker 
1018612d4fd7SFrederic Weisbecker 	list_for_each_entry_safe(list, next_list, &src->val, list) {
1019bffb5b0cSIan Rogers 		struct map_symbol ms = {
1020bffb5b0cSIan Rogers 			.maps = maps__get(list->ms.maps),
1021bffb5b0cSIan Rogers 			.map = map__get(list->ms.map),
1022bffb5b0cSIan Rogers 		};
1023bffb5b0cSIan Rogers 		callchain_cursor_append(cursor, list->ip, &ms, false, NULL, 0, 0, 0, list->srcline);
1024e56fbc9dSArnaldo Carvalho de Melo 		list_del_init(&list->list);
1025bffb5b0cSIan Rogers 		map__zput(ms.map);
1026bffb5b0cSIan Rogers 		maps__zput(ms.maps);
10279c68ae98SKrister Johansen 		map__zput(list->ms.map);
1028bffb5b0cSIan Rogers 		maps__zput(list->ms.maps);
1029612d4fd7SFrederic Weisbecker 		free(list);
1030612d4fd7SFrederic Weisbecker 	}
1031612d4fd7SFrederic Weisbecker 
10321b3a0e95SFrederic Weisbecker 	if (src->hit) {
10331b3a0e95SFrederic Weisbecker 		callchain_cursor_commit(cursor);
1034dca0d122SNamhyung Kim 		if (append_chain_children(dst, cursor, src->hit) < 0)
1035dca0d122SNamhyung Kim 			return -1;
10361b3a0e95SFrederic Weisbecker 	}
1037612d4fd7SFrederic Weisbecker 
1038e369517cSNamhyung Kim 	n = rb_first(&src->rb_root_in);
1039e369517cSNamhyung Kim 	while (n) {
1040e369517cSNamhyung Kim 		child = container_of(n, struct callchain_node, rb_node_in);
1041e369517cSNamhyung Kim 		n = rb_next(n);
1042e369517cSNamhyung Kim 		rb_erase(&child->rb_node_in, &src->rb_root_in);
1043e369517cSNamhyung Kim 
10441b3a0e95SFrederic Weisbecker 		err = merge_chain_branch(cursor, dst, child);
1045612d4fd7SFrederic Weisbecker 		if (err)
1046612d4fd7SFrederic Weisbecker 			break;
1047612d4fd7SFrederic Weisbecker 
1048612d4fd7SFrederic Weisbecker 		free(child);
1049612d4fd7SFrederic Weisbecker 	}
1050612d4fd7SFrederic Weisbecker 
10511b3a0e95SFrederic Weisbecker 	cursor->nr = old_pos;
10521b3a0e95SFrederic Weisbecker 	cursor->last = old_last;
1053612d4fd7SFrederic Weisbecker 
1054612d4fd7SFrederic Weisbecker 	return err;
1055612d4fd7SFrederic Weisbecker }
1056612d4fd7SFrederic Weisbecker 
callchain_merge(struct callchain_cursor * cursor,struct callchain_root * dst,struct callchain_root * src)10571b3a0e95SFrederic Weisbecker int callchain_merge(struct callchain_cursor *cursor,
10581b3a0e95SFrederic Weisbecker 		    struct callchain_root *dst, struct callchain_root *src)
1059612d4fd7SFrederic Weisbecker {
10601b3a0e95SFrederic Weisbecker 	return merge_chain_branch(cursor, &dst->node, &src->node);
10611b3a0e95SFrederic Weisbecker }
1062612d4fd7SFrederic Weisbecker 
callchain_cursor_append(struct callchain_cursor * cursor,u64 ip,struct map_symbol * ms,bool branch,struct branch_flags * flags,int nr_loop_iter,u64 iter_cycles,u64 branch_from,const char * srcline)10631b3a0e95SFrederic Weisbecker int callchain_cursor_append(struct callchain_cursor *cursor,
10645f0fef8aSArnaldo Carvalho de Melo 			    u64 ip, struct map_symbol *ms,
1065410024dbSJin Yao 			    bool branch, struct branch_flags *flags,
106640a342cdSMilian Wolff 			    int nr_loop_iter, u64 iter_cycles, u64 branch_from,
106740a342cdSMilian Wolff 			    const char *srcline)
10681b3a0e95SFrederic Weisbecker {
10691b3a0e95SFrederic Weisbecker 	struct callchain_cursor_node *node = *cursor->last;
10701b3a0e95SFrederic Weisbecker 
10711b3a0e95SFrederic Weisbecker 	if (!node) {
107291b98804SPaul Gortmaker 		node = calloc(1, sizeof(*node));
10731b3a0e95SFrederic Weisbecker 		if (!node)
1074612d4fd7SFrederic Weisbecker 			return -ENOMEM;
1075612d4fd7SFrederic Weisbecker 
10761b3a0e95SFrederic Weisbecker 		*cursor->last = node;
10771b3a0e95SFrederic Weisbecker 	}
1078612d4fd7SFrederic Weisbecker 
10791b3a0e95SFrederic Weisbecker 	node->ip = ip;
1080bffb5b0cSIan Rogers 	maps__zput(node->ms.maps);
10815f0fef8aSArnaldo Carvalho de Melo 	map__zput(node->ms.map);
10825f0fef8aSArnaldo Carvalho de Melo 	node->ms = *ms;
1083bffb5b0cSIan Rogers 	node->ms.maps = maps__get(ms->maps);
1084bffb5b0cSIan Rogers 	node->ms.map = map__get(ms->map);
1085410024dbSJin Yao 	node->branch = branch;
1086410024dbSJin Yao 	node->nr_loop_iter = nr_loop_iter;
1087c4ee0625SJin Yao 	node->iter_cycles = iter_cycles;
108840a342cdSMilian Wolff 	node->srcline = srcline;
1089410024dbSJin Yao 
1090410024dbSJin Yao 	if (flags)
1091410024dbSJin Yao 		memcpy(&node->branch_flags, flags,
1092410024dbSJin Yao 			sizeof(struct branch_flags));
1093612d4fd7SFrederic Weisbecker 
1094b851dd49SJin Yao 	node->branch_from = branch_from;
10951b3a0e95SFrederic Weisbecker 	cursor->nr++;
1096612d4fd7SFrederic Weisbecker 
10971b3a0e95SFrederic Weisbecker 	cursor->last = &node->next;
10981b3a0e95SFrederic Weisbecker 
10991b3a0e95SFrederic Weisbecker 	return 0;
1100612d4fd7SFrederic Weisbecker }
11012dc9fb1aSNamhyung Kim 
sample__resolve_callchain(struct perf_sample * sample,struct callchain_cursor * cursor,struct symbol ** parent,struct evsel * evsel,struct addr_location * al,int max_stack)110291d7b2deSArnaldo Carvalho de Melo int sample__resolve_callchain(struct perf_sample *sample,
110391d7b2deSArnaldo Carvalho de Melo 			      struct callchain_cursor *cursor, struct symbol **parent,
110432dcd021SJiri Olsa 			      struct evsel *evsel, struct addr_location *al,
11052dc9fb1aSNamhyung Kim 			      int max_stack)
11062dc9fb1aSNamhyung Kim {
1107b49a821eSJin Yao 	if (sample->callchain == NULL && !symbol_conf.show_branchflag_count)
11082dc9fb1aSNamhyung Kim 		return 0;
11092dc9fb1aSNamhyung Kim 
11107a13aa28SNamhyung Kim 	if (symbol_conf.use_callchain || symbol_conf.cumulate_callchain ||
1111b49a821eSJin Yao 	    perf_hpp_list.parent || symbol_conf.show_branchflag_count) {
111291d7b2deSArnaldo Carvalho de Melo 		return thread__resolve_callchain(al->thread, cursor, evsel, sample,
1113cc8b7c2bSArnaldo Carvalho de Melo 						 parent, al, max_stack);
11142dc9fb1aSNamhyung Kim 	}
11152dc9fb1aSNamhyung Kim 	return 0;
11162dc9fb1aSNamhyung Kim }
11172dc9fb1aSNamhyung Kim 
hist_entry__append_callchain(struct hist_entry * he,struct perf_sample * sample)11182dc9fb1aSNamhyung Kim int hist_entry__append_callchain(struct hist_entry *he, struct perf_sample *sample)
11192dc9fb1aSNamhyung Kim {
1120b49a821eSJin Yao 	if ((!symbol_conf.use_callchain || sample->callchain == NULL) &&
1121b49a821eSJin Yao 		!symbol_conf.show_branchflag_count)
11222dc9fb1aSNamhyung Kim 		return 0;
1123*8ab12a20SIan Rogers 	return callchain_append(he->callchain, get_tls_callchain_cursor(), sample->period);
11242dc9fb1aSNamhyung Kim }
1125c7405d85SNamhyung Kim 
fill_callchain_info(struct addr_location * al,struct callchain_cursor_node * node,bool hide_unresolved)1126c7405d85SNamhyung Kim int fill_callchain_info(struct addr_location *al, struct callchain_cursor_node *node,
1127c7405d85SNamhyung Kim 			bool hide_unresolved)
1128c7405d85SNamhyung Kim {
11295ab6d715SIan Rogers 	struct machine *machine = maps__machine(node->ms.maps);
11305ab6d715SIan Rogers 
1131bffb5b0cSIan Rogers 	maps__put(al->maps);
1132bffb5b0cSIan Rogers 	al->maps = maps__get(node->ms.maps);
1133ec417ad4SIan Rogers 	map__put(al->map);
1134ec417ad4SIan Rogers 	al->map = map__get(node->ms.map);
11355f0fef8aSArnaldo Carvalho de Melo 	al->sym = node->ms.sym;
11361fb7d06aSMilian Wolff 	al->srcline = node->srcline;
1137c7405d85SNamhyung Kim 	al->addr = node->ip;
1138c7405d85SNamhyung Kim 
1139c7405d85SNamhyung Kim 	if (al->sym == NULL) {
1140c7405d85SNamhyung Kim 		if (hide_unresolved)
1141c7405d85SNamhyung Kim 			return 0;
1142c7405d85SNamhyung Kim 		if (al->map == NULL)
1143c7405d85SNamhyung Kim 			goto out;
1144c7405d85SNamhyung Kim 	}
1145bffb5b0cSIan Rogers 	if (RC_CHK_ACCESS(al->maps) == RC_CHK_ACCESS(machine__kernel_maps(machine))) {
11465ab6d715SIan Rogers 		if (machine__is_host(machine)) {
1147c7405d85SNamhyung Kim 			al->cpumode = PERF_RECORD_MISC_KERNEL;
1148c7405d85SNamhyung Kim 			al->level = 'k';
1149c7405d85SNamhyung Kim 		} else {
1150c7405d85SNamhyung Kim 			al->cpumode = PERF_RECORD_MISC_GUEST_KERNEL;
1151c7405d85SNamhyung Kim 			al->level = 'g';
1152c7405d85SNamhyung Kim 		}
1153c7405d85SNamhyung Kim 	} else {
11545ab6d715SIan Rogers 		if (machine__is_host(machine)) {
1155c7405d85SNamhyung Kim 			al->cpumode = PERF_RECORD_MISC_USER;
1156c7405d85SNamhyung Kim 			al->level = '.';
1157c7405d85SNamhyung Kim 		} else if (perf_guest) {
1158c7405d85SNamhyung Kim 			al->cpumode = PERF_RECORD_MISC_GUEST_USER;
1159c7405d85SNamhyung Kim 			al->level = 'u';
1160c7405d85SNamhyung Kim 		} else {
1161c7405d85SNamhyung Kim 			al->cpumode = PERF_RECORD_MISC_HYPERVISOR;
1162c7405d85SNamhyung Kim 			al->level = 'H';
1163c7405d85SNamhyung Kim 		}
1164c7405d85SNamhyung Kim 	}
1165c7405d85SNamhyung Kim 
1166c7405d85SNamhyung Kim out:
1167c7405d85SNamhyung Kim 	return 1;
1168c7405d85SNamhyung Kim }
11692989ccaaSAndi Kleen 
callchain_list__sym_name(struct callchain_list * cl,char * bf,size_t bfsize,bool show_dso)11702989ccaaSAndi Kleen char *callchain_list__sym_name(struct callchain_list *cl,
11712989ccaaSAndi Kleen 			       char *bf, size_t bfsize, bool show_dso)
11722989ccaaSAndi Kleen {
11735dfa210eSMilian Wolff 	bool show_addr = callchain_param.key == CCKEY_ADDRESS;
11745dfa210eSMilian Wolff 	bool show_srcline = show_addr || callchain_param.key == CCKEY_SRCLINE;
11752989ccaaSAndi Kleen 	int printed;
11762989ccaaSAndi Kleen 
11772989ccaaSAndi Kleen 	if (cl->ms.sym) {
11788932f807SMilian Wolff 		const char *inlined = cl->ms.sym->inlined ? " (inlined)" : "";
11798932f807SMilian Wolff 
118040a342cdSMilian Wolff 		if (show_srcline && cl->srcline)
11818932f807SMilian Wolff 			printed = scnprintf(bf, bfsize, "%s %s%s",
11828932f807SMilian Wolff 					    cl->ms.sym->name, cl->srcline,
11838932f807SMilian Wolff 					    inlined);
118423f0981bSAndi Kleen 		else
11858932f807SMilian Wolff 			printed = scnprintf(bf, bfsize, "%s%s",
11868932f807SMilian Wolff 					    cl->ms.sym->name, inlined);
11872989ccaaSAndi Kleen 	} else
11882989ccaaSAndi Kleen 		printed = scnprintf(bf, bfsize, "%#" PRIx64, cl->ip);
11892989ccaaSAndi Kleen 
11902989ccaaSAndi Kleen 	if (show_dso)
11912989ccaaSAndi Kleen 		scnprintf(bf + printed, bfsize - printed, " %s",
11922989ccaaSAndi Kleen 			  cl->ms.map ?
119363df0e4bSIan Rogers 			  map__dso(cl->ms.map)->short_name :
11942989ccaaSAndi Kleen 			  "unknown");
11952989ccaaSAndi Kleen 
11962989ccaaSAndi Kleen 	return bf;
11972989ccaaSAndi Kleen }
1198d114960cSNamhyung Kim 
callchain_node__scnprintf_value(struct callchain_node * node,char * bf,size_t bfsize,u64 total)11995ab250caSNamhyung Kim char *callchain_node__scnprintf_value(struct callchain_node *node,
12005ab250caSNamhyung Kim 				      char *bf, size_t bfsize, u64 total)
12015ab250caSNamhyung Kim {
12025ab250caSNamhyung Kim 	double percent = 0.0;
12035ab250caSNamhyung Kim 	u64 period = callchain_cumul_hits(node);
1204f2af0086SNamhyung Kim 	unsigned count = callchain_cumul_counts(node);
12055ab250caSNamhyung Kim 
1206f2af0086SNamhyung Kim 	if (callchain_param.mode == CHAIN_FOLDED) {
12075ab250caSNamhyung Kim 		period = node->hit;
1208f2af0086SNamhyung Kim 		count = node->count;
1209f2af0086SNamhyung Kim 	}
1210f2af0086SNamhyung Kim 
1211f2af0086SNamhyung Kim 	switch (callchain_param.value) {
1212f2af0086SNamhyung Kim 	case CCVAL_PERIOD:
1213f2af0086SNamhyung Kim 		scnprintf(bf, bfsize, "%"PRIu64, period);
1214f2af0086SNamhyung Kim 		break;
1215f2af0086SNamhyung Kim 	case CCVAL_COUNT:
1216f2af0086SNamhyung Kim 		scnprintf(bf, bfsize, "%u", count);
1217f2af0086SNamhyung Kim 		break;
1218f2af0086SNamhyung Kim 	case CCVAL_PERCENT:
1219f2af0086SNamhyung Kim 	default:
12205ab250caSNamhyung Kim 		if (total)
12215ab250caSNamhyung Kim 			percent = period * 100.0 / total;
12225ab250caSNamhyung Kim 		scnprintf(bf, bfsize, "%.2f%%", percent);
1223f2af0086SNamhyung Kim 		break;
1224f2af0086SNamhyung Kim 	}
12255ab250caSNamhyung Kim 	return bf;
12265ab250caSNamhyung Kim }
12275ab250caSNamhyung Kim 
callchain_node__fprintf_value(struct callchain_node * node,FILE * fp,u64 total)12285ab250caSNamhyung Kim int callchain_node__fprintf_value(struct callchain_node *node,
12295ab250caSNamhyung Kim 				 FILE *fp, u64 total)
12305ab250caSNamhyung Kim {
12315ab250caSNamhyung Kim 	double percent = 0.0;
12325ab250caSNamhyung Kim 	u64 period = callchain_cumul_hits(node);
1233f2af0086SNamhyung Kim 	unsigned count = callchain_cumul_counts(node);
12345ab250caSNamhyung Kim 
1235f2af0086SNamhyung Kim 	if (callchain_param.mode == CHAIN_FOLDED) {
12365ab250caSNamhyung Kim 		period = node->hit;
1237f2af0086SNamhyung Kim 		count = node->count;
1238f2af0086SNamhyung Kim 	}
1239f2af0086SNamhyung Kim 
1240f2af0086SNamhyung Kim 	switch (callchain_param.value) {
1241f2af0086SNamhyung Kim 	case CCVAL_PERIOD:
1242f2af0086SNamhyung Kim 		return fprintf(fp, "%"PRIu64, period);
1243f2af0086SNamhyung Kim 	case CCVAL_COUNT:
1244f2af0086SNamhyung Kim 		return fprintf(fp, "%u", count);
1245f2af0086SNamhyung Kim 	case CCVAL_PERCENT:
1246f2af0086SNamhyung Kim 	default:
12475ab250caSNamhyung Kim 		if (total)
12485ab250caSNamhyung Kim 			percent = period * 100.0 / total;
12495ab250caSNamhyung Kim 		return percent_color_fprintf(fp, "%.2f%%", percent);
12505ab250caSNamhyung Kim 	}
1251f2af0086SNamhyung Kim 	return 0;
1252f2af0086SNamhyung Kim }
12535ab250caSNamhyung Kim 
callchain_counts_value(struct callchain_node * node,u64 * branch_count,u64 * predicted_count,u64 * abort_count,u64 * cycles_count)12543dd029efSJin Yao static void callchain_counts_value(struct callchain_node *node,
12553dd029efSJin Yao 				   u64 *branch_count, u64 *predicted_count,
12563dd029efSJin Yao 				   u64 *abort_count, u64 *cycles_count)
12573dd029efSJin Yao {
12583dd029efSJin Yao 	struct callchain_list *clist;
12593dd029efSJin Yao 
12603dd029efSJin Yao 	list_for_each_entry(clist, &node->val, list) {
12613dd029efSJin Yao 		if (branch_count)
12623dd029efSJin Yao 			*branch_count += clist->branch_count;
12633dd029efSJin Yao 
12643dd029efSJin Yao 		if (predicted_count)
12653dd029efSJin Yao 			*predicted_count += clist->predicted_count;
12663dd029efSJin Yao 
12673dd029efSJin Yao 		if (abort_count)
12683dd029efSJin Yao 			*abort_count += clist->abort_count;
12693dd029efSJin Yao 
12703dd029efSJin Yao 		if (cycles_count)
12713dd029efSJin Yao 			*cycles_count += clist->cycles_count;
12723dd029efSJin Yao 	}
12733dd029efSJin Yao }
12743dd029efSJin Yao 
callchain_node_branch_counts_cumul(struct callchain_node * node,u64 * branch_count,u64 * predicted_count,u64 * abort_count,u64 * cycles_count)12753dd029efSJin Yao static int callchain_node_branch_counts_cumul(struct callchain_node *node,
12763dd029efSJin Yao 					      u64 *branch_count,
12773dd029efSJin Yao 					      u64 *predicted_count,
12783dd029efSJin Yao 					      u64 *abort_count,
12793dd029efSJin Yao 					      u64 *cycles_count)
12803dd029efSJin Yao {
12813dd029efSJin Yao 	struct callchain_node *child;
12823dd029efSJin Yao 	struct rb_node *n;
12833dd029efSJin Yao 
12843dd029efSJin Yao 	n = rb_first(&node->rb_root_in);
12853dd029efSJin Yao 	while (n) {
12863dd029efSJin Yao 		child = rb_entry(n, struct callchain_node, rb_node_in);
12873dd029efSJin Yao 		n = rb_next(n);
12883dd029efSJin Yao 
12893dd029efSJin Yao 		callchain_node_branch_counts_cumul(child, branch_count,
12903dd029efSJin Yao 						   predicted_count,
12913dd029efSJin Yao 						   abort_count,
12923dd029efSJin Yao 						   cycles_count);
12933dd029efSJin Yao 
12943dd029efSJin Yao 		callchain_counts_value(child, branch_count,
12953dd029efSJin Yao 				       predicted_count, abort_count,
12963dd029efSJin Yao 				       cycles_count);
12973dd029efSJin Yao 	}
12983dd029efSJin Yao 
12993dd029efSJin Yao 	return 0;
13003dd029efSJin Yao }
13013dd029efSJin Yao 
callchain_branch_counts(struct callchain_root * root,u64 * branch_count,u64 * predicted_count,u64 * abort_count,u64 * cycles_count)13023dd029efSJin Yao int callchain_branch_counts(struct callchain_root *root,
13033dd029efSJin Yao 			    u64 *branch_count, u64 *predicted_count,
13043dd029efSJin Yao 			    u64 *abort_count, u64 *cycles_count)
13053dd029efSJin Yao {
13063dd029efSJin Yao 	if (branch_count)
13073dd029efSJin Yao 		*branch_count = 0;
13083dd029efSJin Yao 
13093dd029efSJin Yao 	if (predicted_count)
13103dd029efSJin Yao 		*predicted_count = 0;
13113dd029efSJin Yao 
13123dd029efSJin Yao 	if (abort_count)
13133dd029efSJin Yao 		*abort_count = 0;
13143dd029efSJin Yao 
13153dd029efSJin Yao 	if (cycles_count)
13163dd029efSJin Yao 		*cycles_count = 0;
13173dd029efSJin Yao 
13183dd029efSJin Yao 	return callchain_node_branch_counts_cumul(&root->node,
13193dd029efSJin Yao 						  branch_count,
13203dd029efSJin Yao 						  predicted_count,
13213dd029efSJin Yao 						  abort_count,
13223dd029efSJin Yao 						  cycles_count);
13233dd029efSJin Yao }
13243dd029efSJin Yao 
count_pri64_printf(int idx,const char * str,u64 value,char * bf,int bfsize)13258d51735fSJin Yao static int count_pri64_printf(int idx, const char *str, u64 value, char *bf, int bfsize)
13268d51735fSJin Yao {
1327016f2f98Sye xingchen 	return scnprintf(bf, bfsize, "%s%s:%" PRId64 "", (idx) ? " " : " (", str, value);
13288d51735fSJin Yao }
13298d51735fSJin Yao 
count_float_printf(int idx,const char * str,float value,char * bf,int bfsize,float threshold)1330a1a8bed3SJin Yao static int count_float_printf(int idx, const char *str, float value,
1331a1a8bed3SJin Yao 			      char *bf, int bfsize, float threshold)
13328d51735fSJin Yao {
1333a1a8bed3SJin Yao 	if (threshold != 0.0 && value < threshold)
1334a1a8bed3SJin Yao 		return 0;
1335a1a8bed3SJin Yao 
1336016f2f98Sye xingchen 	return scnprintf(bf, bfsize, "%s%s:%.1f%%", (idx) ? " " : " (", str, value);
13378d51735fSJin Yao }
13388d51735fSJin Yao 
branch_to_str(char * bf,int bfsize,u64 branch_count,u64 predicted_count,u64 abort_count,struct branch_type_stat * brtype_stat)1339a1a8bed3SJin Yao static int branch_to_str(char *bf, int bfsize,
13403dd029efSJin Yao 			 u64 branch_count, u64 predicted_count,
1341a1a8bed3SJin Yao 			 u64 abort_count,
1342b851dd49SJin Yao 			 struct branch_type_stat *brtype_stat)
13433dd029efSJin Yao {
1344b851dd49SJin Yao 	int printed, i = 0;
13453dd029efSJin Yao 
1346b851dd49SJin Yao 	printed = branch_type_str(brtype_stat, bf, bfsize);
1347b851dd49SJin Yao 	if (printed)
1348b851dd49SJin Yao 		i++;
1349b851dd49SJin Yao 
13508d51735fSJin Yao 	if (predicted_count < branch_count) {
13518d51735fSJin Yao 		printed += count_float_printf(i++, "predicted",
13528d51735fSJin Yao 				predicted_count * 100.0 / branch_count,
1353a1a8bed3SJin Yao 				bf + printed, bfsize - printed, 0.0);
13548d51735fSJin Yao 	}
13558d51735fSJin Yao 
13568d51735fSJin Yao 	if (abort_count) {
13578d51735fSJin Yao 		printed += count_float_printf(i++, "abort",
13588d51735fSJin Yao 				abort_count * 100.0 / branch_count,
1359a1a8bed3SJin Yao 				bf + printed, bfsize - printed, 0.1);
13608d51735fSJin Yao 	}
13618d51735fSJin Yao 
1362a1a8bed3SJin Yao 	if (i)
1363a1a8bed3SJin Yao 		printed += scnprintf(bf + printed, bfsize - printed, ")");
1364a1a8bed3SJin Yao 
1365a1a8bed3SJin Yao 	return printed;
1366a1a8bed3SJin Yao }
1367a1a8bed3SJin Yao 
branch_from_str(char * bf,int bfsize,u64 branch_count,u64 cycles_count,u64 iter_count,u64 iter_cycles,u64 from_count)1368a1a8bed3SJin Yao static int branch_from_str(char *bf, int bfsize,
1369a1a8bed3SJin Yao 			   u64 branch_count,
1370a1a8bed3SJin Yao 			   u64 cycles_count, u64 iter_count,
1371a3366db0SJin Yao 			   u64 iter_cycles, u64 from_count)
1372a1a8bed3SJin Yao {
1373a1a8bed3SJin Yao 	int printed = 0, i = 0;
1374a3366db0SJin Yao 	u64 cycles, v = 0;
1375a1a8bed3SJin Yao 
13763dd029efSJin Yao 	cycles = cycles_count / branch_count;
13778d51735fSJin Yao 	if (cycles) {
13788d51735fSJin Yao 		printed += count_pri64_printf(i++, "cycles",
13798d51735fSJin Yao 				cycles,
13808d51735fSJin Yao 				bf + printed, bfsize - printed);
13818d51735fSJin Yao 	}
13823dd029efSJin Yao 
1383a3366db0SJin Yao 	if (iter_count && from_count) {
1384a3366db0SJin Yao 		v = iter_count / from_count;
1385a3366db0SJin Yao 		if (v) {
1386c4ee0625SJin Yao 			printed += count_pri64_printf(i++, "iter",
1387a3366db0SJin Yao 					v, bf + printed, bfsize - printed);
1388c4ee0625SJin Yao 
1389c4ee0625SJin Yao 			printed += count_pri64_printf(i++, "avg_cycles",
1390c4ee0625SJin Yao 					iter_cycles / iter_count,
13918d51735fSJin Yao 					bf + printed, bfsize - printed);
13923dd029efSJin Yao 		}
1393a3366db0SJin Yao 	}
13943dd029efSJin Yao 
13958d51735fSJin Yao 	if (i)
1396a1a8bed3SJin Yao 		printed += scnprintf(bf + printed, bfsize - printed, ")");
13973dd029efSJin Yao 
1398a1a8bed3SJin Yao 	return printed;
1399a1a8bed3SJin Yao }
1400a1a8bed3SJin Yao 
counts_str_build(char * bf,int bfsize,u64 branch_count,u64 predicted_count,u64 abort_count,u64 cycles_count,u64 iter_count,u64 iter_cycles,u64 from_count,struct branch_type_stat * brtype_stat)1401a1a8bed3SJin Yao static int counts_str_build(char *bf, int bfsize,
1402a1a8bed3SJin Yao 			     u64 branch_count, u64 predicted_count,
1403a1a8bed3SJin Yao 			     u64 abort_count, u64 cycles_count,
1404c4ee0625SJin Yao 			     u64 iter_count, u64 iter_cycles,
1405a3366db0SJin Yao 			     u64 from_count,
1406a1a8bed3SJin Yao 			     struct branch_type_stat *brtype_stat)
1407a1a8bed3SJin Yao {
1408a1a8bed3SJin Yao 	int printed;
1409a1a8bed3SJin Yao 
1410a1a8bed3SJin Yao 	if (branch_count == 0)
1411a1a8bed3SJin Yao 		return scnprintf(bf, bfsize, " (calltrace)");
1412a1a8bed3SJin Yao 
1413a1a8bed3SJin Yao 	if (brtype_stat->branch_to) {
1414a1a8bed3SJin Yao 		printed = branch_to_str(bf, bfsize, branch_count,
1415a1a8bed3SJin Yao 				predicted_count, abort_count, brtype_stat);
1416a1a8bed3SJin Yao 	} else {
1417a1a8bed3SJin Yao 		printed = branch_from_str(bf, bfsize, branch_count,
1418a3366db0SJin Yao 				cycles_count, iter_count, iter_cycles,
1419a3366db0SJin Yao 				from_count);
1420a1a8bed3SJin Yao 	}
1421a1a8bed3SJin Yao 
1422a1a8bed3SJin Yao 	if (!printed)
14238d51735fSJin Yao 		bf[0] = 0;
1424a1a8bed3SJin Yao 
1425a1a8bed3SJin Yao 	return printed;
1426c1dfcfadSJin Yao }
1427c1dfcfadSJin Yao 
callchain_counts_printf(FILE * fp,char * bf,int bfsize,u64 branch_count,u64 predicted_count,u64 abort_count,u64 cycles_count,u64 iter_count,u64 iter_cycles,u64 from_count,struct branch_type_stat * brtype_stat)1428c1dfcfadSJin Yao static int callchain_counts_printf(FILE *fp, char *bf, int bfsize,
1429c1dfcfadSJin Yao 				   u64 branch_count, u64 predicted_count,
1430c1dfcfadSJin Yao 				   u64 abort_count, u64 cycles_count,
1431c4ee0625SJin Yao 				   u64 iter_count, u64 iter_cycles,
1432a3366db0SJin Yao 				   u64 from_count,
1433b851dd49SJin Yao 				   struct branch_type_stat *brtype_stat)
1434c1dfcfadSJin Yao {
1435b851dd49SJin Yao 	char str[256];
1436c1dfcfadSJin Yao 
1437c1dfcfadSJin Yao 	counts_str_build(str, sizeof(str), branch_count,
1438c1dfcfadSJin Yao 			 predicted_count, abort_count, cycles_count,
1439a3366db0SJin Yao 			 iter_count, iter_cycles, from_count, brtype_stat);
1440c1dfcfadSJin Yao 
1441c1dfcfadSJin Yao 	if (fp)
1442c1dfcfadSJin Yao 		return fprintf(fp, "%s", str);
1443c1dfcfadSJin Yao 
1444c1dfcfadSJin Yao 	return scnprintf(bf, bfsize, "%s", str);
14453dd029efSJin Yao }
14463dd029efSJin Yao 
callchain_list_counts__printf_value(struct callchain_list * clist,FILE * fp,char * bf,int bfsize)1447c4ee0625SJin Yao int callchain_list_counts__printf_value(struct callchain_list *clist,
14483dd029efSJin Yao 					FILE *fp, char *bf, int bfsize)
14493dd029efSJin Yao {
14503dd029efSJin Yao 	u64 branch_count, predicted_count;
14513dd029efSJin Yao 	u64 abort_count, cycles_count;
1452c4ee0625SJin Yao 	u64 iter_count, iter_cycles;
1453a3366db0SJin Yao 	u64 from_count;
14543dd029efSJin Yao 
14553dd029efSJin Yao 	branch_count = clist->branch_count;
14563dd029efSJin Yao 	predicted_count = clist->predicted_count;
14573dd029efSJin Yao 	abort_count = clist->abort_count;
14583dd029efSJin Yao 	cycles_count = clist->cycles_count;
1459c4ee0625SJin Yao 	iter_count = clist->iter_count;
1460c4ee0625SJin Yao 	iter_cycles = clist->iter_cycles;
1461a3366db0SJin Yao 	from_count = clist->from_count;
14623dd029efSJin Yao 
14633dd029efSJin Yao 	return callchain_counts_printf(fp, bf, bfsize, branch_count,
14643dd029efSJin Yao 				       predicted_count, abort_count,
1465c4ee0625SJin Yao 				       cycles_count, iter_count, iter_cycles,
1466a3366db0SJin Yao 				       from_count, &clist->brtype_stat);
14673dd029efSJin Yao }
14683dd029efSJin Yao 
free_callchain_node(struct callchain_node * node)1469d114960cSNamhyung Kim static void free_callchain_node(struct callchain_node *node)
1470d114960cSNamhyung Kim {
1471d114960cSNamhyung Kim 	struct callchain_list *list, *tmp;
1472d114960cSNamhyung Kim 	struct callchain_node *child;
1473d114960cSNamhyung Kim 	struct rb_node *n;
1474d114960cSNamhyung Kim 
14754b3a3212SNamhyung Kim 	list_for_each_entry_safe(list, tmp, &node->parent_val, list) {
1476e56fbc9dSArnaldo Carvalho de Melo 		list_del_init(&list->list);
14779c68ae98SKrister Johansen 		map__zput(list->ms.map);
1478bffb5b0cSIan Rogers 		maps__zput(list->ms.maps);
14794b3a3212SNamhyung Kim 		free(list);
14804b3a3212SNamhyung Kim 	}
14814b3a3212SNamhyung Kim 
1482d114960cSNamhyung Kim 	list_for_each_entry_safe(list, tmp, &node->val, list) {
1483e56fbc9dSArnaldo Carvalho de Melo 		list_del_init(&list->list);
14849c68ae98SKrister Johansen 		map__zput(list->ms.map);
1485bffb5b0cSIan Rogers 		maps__zput(list->ms.maps);
1486d114960cSNamhyung Kim 		free(list);
1487d114960cSNamhyung Kim 	}
1488d114960cSNamhyung Kim 
1489d114960cSNamhyung Kim 	n = rb_first(&node->rb_root_in);
1490d114960cSNamhyung Kim 	while (n) {
1491d114960cSNamhyung Kim 		child = container_of(n, struct callchain_node, rb_node_in);
1492d114960cSNamhyung Kim 		n = rb_next(n);
1493d114960cSNamhyung Kim 		rb_erase(&child->rb_node_in, &node->rb_root_in);
1494d114960cSNamhyung Kim 
1495d114960cSNamhyung Kim 		free_callchain_node(child);
1496d114960cSNamhyung Kim 		free(child);
1497d114960cSNamhyung Kim 	}
1498d114960cSNamhyung Kim }
1499d114960cSNamhyung Kim 
free_callchain(struct callchain_root * root)1500d114960cSNamhyung Kim void free_callchain(struct callchain_root *root)
1501d114960cSNamhyung Kim {
1502d114960cSNamhyung Kim 	if (!symbol_conf.use_callchain)
1503d114960cSNamhyung Kim 		return;
1504d114960cSNamhyung Kim 
1505d114960cSNamhyung Kim 	free_callchain_node(&root->node);
1506d114960cSNamhyung Kim }
15074b3a3212SNamhyung Kim 
decay_callchain_node(struct callchain_node * node)150842b276a2SNamhyung Kim static u64 decay_callchain_node(struct callchain_node *node)
150942b276a2SNamhyung Kim {
151042b276a2SNamhyung Kim 	struct callchain_node *child;
151142b276a2SNamhyung Kim 	struct rb_node *n;
151242b276a2SNamhyung Kim 	u64 child_hits = 0;
151342b276a2SNamhyung Kim 
151442b276a2SNamhyung Kim 	n = rb_first(&node->rb_root_in);
151542b276a2SNamhyung Kim 	while (n) {
151642b276a2SNamhyung Kim 		child = container_of(n, struct callchain_node, rb_node_in);
151742b276a2SNamhyung Kim 
151842b276a2SNamhyung Kim 		child_hits += decay_callchain_node(child);
151942b276a2SNamhyung Kim 		n = rb_next(n);
152042b276a2SNamhyung Kim 	}
152142b276a2SNamhyung Kim 
152242b276a2SNamhyung Kim 	node->hit = (node->hit * 7) / 8;
152342b276a2SNamhyung Kim 	node->children_hit = child_hits;
152442b276a2SNamhyung Kim 
152542b276a2SNamhyung Kim 	return node->hit;
152642b276a2SNamhyung Kim }
152742b276a2SNamhyung Kim 
decay_callchain(struct callchain_root * root)152842b276a2SNamhyung Kim void decay_callchain(struct callchain_root *root)
152942b276a2SNamhyung Kim {
153042b276a2SNamhyung Kim 	if (!symbol_conf.use_callchain)
153142b276a2SNamhyung Kim 		return;
153242b276a2SNamhyung Kim 
153342b276a2SNamhyung Kim 	decay_callchain_node(&root->node);
153442b276a2SNamhyung Kim }
153542b276a2SNamhyung Kim 
callchain_node__make_parent_list(struct callchain_node * node)15364b3a3212SNamhyung Kim int callchain_node__make_parent_list(struct callchain_node *node)
15374b3a3212SNamhyung Kim {
15384b3a3212SNamhyung Kim 	struct callchain_node *parent = node->parent;
15394b3a3212SNamhyung Kim 	struct callchain_list *chain, *new;
15404b3a3212SNamhyung Kim 	LIST_HEAD(head);
15414b3a3212SNamhyung Kim 
15424b3a3212SNamhyung Kim 	while (parent) {
15434b3a3212SNamhyung Kim 		list_for_each_entry_reverse(chain, &parent->val, list) {
15444b3a3212SNamhyung Kim 			new = malloc(sizeof(*new));
15454b3a3212SNamhyung Kim 			if (new == NULL)
15464b3a3212SNamhyung Kim 				goto out;
15474b3a3212SNamhyung Kim 			*new = *chain;
15484b3a3212SNamhyung Kim 			new->has_children = false;
1549ec417ad4SIan Rogers 			new->ms.map = map__get(new->ms.map);
15504b3a3212SNamhyung Kim 			list_add_tail(&new->list, &head);
15514b3a3212SNamhyung Kim 		}
15524b3a3212SNamhyung Kim 		parent = parent->parent;
15534b3a3212SNamhyung Kim 	}
15544b3a3212SNamhyung Kim 
15554b3a3212SNamhyung Kim 	list_for_each_entry_safe_reverse(chain, new, &head, list)
15564b3a3212SNamhyung Kim 		list_move_tail(&chain->list, &node->parent_val);
15574b3a3212SNamhyung Kim 
15584b3a3212SNamhyung Kim 	if (!list_empty(&node->parent_val)) {
15594b3a3212SNamhyung Kim 		chain = list_first_entry(&node->parent_val, struct callchain_list, list);
15604b3a3212SNamhyung Kim 		chain->has_children = rb_prev(&node->rb_node) || rb_next(&node->rb_node);
15614b3a3212SNamhyung Kim 
15624b3a3212SNamhyung Kim 		chain = list_first_entry(&node->val, struct callchain_list, list);
15634b3a3212SNamhyung Kim 		chain->has_children = false;
15644b3a3212SNamhyung Kim 	}
15654b3a3212SNamhyung Kim 	return 0;
15664b3a3212SNamhyung Kim 
15674b3a3212SNamhyung Kim out:
15684b3a3212SNamhyung Kim 	list_for_each_entry_safe(chain, new, &head, list) {
1569e56fbc9dSArnaldo Carvalho de Melo 		list_del_init(&chain->list);
15709c68ae98SKrister Johansen 		map__zput(chain->ms.map);
1571bffb5b0cSIan Rogers 		maps__zput(chain->ms.maps);
15724b3a3212SNamhyung Kim 		free(chain);
15734b3a3212SNamhyung Kim 	}
15744b3a3212SNamhyung Kim 	return -ENOMEM;
15754b3a3212SNamhyung Kim }
1576571f1eb9SNamhyung Kim 
callchain_cursor__delete(void * vcursor)1577*8ab12a20SIan Rogers static void callchain_cursor__delete(void *vcursor)
1578*8ab12a20SIan Rogers {
1579*8ab12a20SIan Rogers 	struct callchain_cursor *cursor = vcursor;
1580*8ab12a20SIan Rogers 	struct callchain_cursor_node *node, *next;
1581*8ab12a20SIan Rogers 
1582*8ab12a20SIan Rogers 	callchain_cursor_reset(cursor);
1583*8ab12a20SIan Rogers 	for (node = cursor->first; node != NULL; node = next) {
1584*8ab12a20SIan Rogers 		next = node->next;
1585*8ab12a20SIan Rogers 		free(node);
1586*8ab12a20SIan Rogers 	}
1587*8ab12a20SIan Rogers 	free(cursor);
1588*8ab12a20SIan Rogers }
1589*8ab12a20SIan Rogers 
init_callchain_cursor_key(void)1590*8ab12a20SIan Rogers static void init_callchain_cursor_key(void)
1591*8ab12a20SIan Rogers {
1592*8ab12a20SIan Rogers 	if (pthread_key_create(&callchain_cursor, callchain_cursor__delete)) {
1593*8ab12a20SIan Rogers 		pr_err("callchain cursor creation failed");
1594*8ab12a20SIan Rogers 		abort();
1595*8ab12a20SIan Rogers 	}
1596*8ab12a20SIan Rogers }
1597*8ab12a20SIan Rogers 
get_tls_callchain_cursor(void)1598*8ab12a20SIan Rogers struct callchain_cursor *get_tls_callchain_cursor(void)
1599*8ab12a20SIan Rogers {
1600*8ab12a20SIan Rogers 	static pthread_once_t once_control = PTHREAD_ONCE_INIT;
1601*8ab12a20SIan Rogers 	struct callchain_cursor *cursor;
1602*8ab12a20SIan Rogers 
1603*8ab12a20SIan Rogers 	pthread_once(&once_control, init_callchain_cursor_key);
1604*8ab12a20SIan Rogers 	cursor = pthread_getspecific(callchain_cursor);
1605*8ab12a20SIan Rogers 	if (!cursor) {
1606*8ab12a20SIan Rogers 		cursor = zalloc(sizeof(*cursor));
1607*8ab12a20SIan Rogers 		if (!cursor)
1608*8ab12a20SIan Rogers 			pr_debug3("%s: not enough memory\n", __func__);
1609*8ab12a20SIan Rogers 		pthread_setspecific(callchain_cursor, cursor);
1610*8ab12a20SIan Rogers 	}
1611*8ab12a20SIan Rogers 	return cursor;
1612*8ab12a20SIan Rogers }
1613*8ab12a20SIan Rogers 
callchain_cursor__copy(struct callchain_cursor * dst,struct callchain_cursor * src)1614571f1eb9SNamhyung Kim int callchain_cursor__copy(struct callchain_cursor *dst,
1615571f1eb9SNamhyung Kim 			   struct callchain_cursor *src)
1616571f1eb9SNamhyung Kim {
1617571f1eb9SNamhyung Kim 	int rc = 0;
1618571f1eb9SNamhyung Kim 
1619571f1eb9SNamhyung Kim 	callchain_cursor_reset(dst);
1620571f1eb9SNamhyung Kim 	callchain_cursor_commit(src);
1621571f1eb9SNamhyung Kim 
1622571f1eb9SNamhyung Kim 	while (true) {
1623571f1eb9SNamhyung Kim 		struct callchain_cursor_node *node;
1624571f1eb9SNamhyung Kim 
1625571f1eb9SNamhyung Kim 		node = callchain_cursor_current(src);
1626571f1eb9SNamhyung Kim 		if (node == NULL)
1627571f1eb9SNamhyung Kim 			break;
1628571f1eb9SNamhyung Kim 
16295f0fef8aSArnaldo Carvalho de Melo 		rc = callchain_cursor_append(dst, node->ip, &node->ms,
1630571f1eb9SNamhyung Kim 					     node->branch, &node->branch_flags,
1631c4ee0625SJin Yao 					     node->nr_loop_iter,
1632c4ee0625SJin Yao 					     node->iter_cycles,
163340a342cdSMilian Wolff 					     node->branch_from, node->srcline);
1634571f1eb9SNamhyung Kim 		if (rc)
1635571f1eb9SNamhyung Kim 			break;
1636571f1eb9SNamhyung Kim 
1637571f1eb9SNamhyung Kim 		callchain_cursor_advance(src);
1638571f1eb9SNamhyung Kim 	}
1639571f1eb9SNamhyung Kim 
1640571f1eb9SNamhyung Kim 	return rc;
1641571f1eb9SNamhyung Kim }
16427b644f9aSArnaldo Carvalho de Melo 
16437b644f9aSArnaldo Carvalho de Melo /*
16447b644f9aSArnaldo Carvalho de Melo  * Initialize a cursor before adding entries inside, but keep
16457b644f9aSArnaldo Carvalho de Melo  * the previously allocated entries as a cache.
16467b644f9aSArnaldo Carvalho de Melo  */
callchain_cursor_reset(struct callchain_cursor * cursor)16477b644f9aSArnaldo Carvalho de Melo void callchain_cursor_reset(struct callchain_cursor *cursor)
16487b644f9aSArnaldo Carvalho de Melo {
16497b644f9aSArnaldo Carvalho de Melo 	struct callchain_cursor_node *node;
16507b644f9aSArnaldo Carvalho de Melo 
16517b644f9aSArnaldo Carvalho de Melo 	cursor->nr = 0;
16527b644f9aSArnaldo Carvalho de Melo 	cursor->last = &cursor->first;
16537b644f9aSArnaldo Carvalho de Melo 
1654bffb5b0cSIan Rogers 	for (node = cursor->first; node != NULL; node = node->next) {
16555f0fef8aSArnaldo Carvalho de Melo 		map__zput(node->ms.map);
1656bffb5b0cSIan Rogers 		maps__zput(node->ms.maps);
1657bffb5b0cSIan Rogers 	}
16587b644f9aSArnaldo Carvalho de Melo }
16590d71a2b2SJiri Olsa 
callchain_param_setup(u64 sample_type,const char * arch)1660aa8db3e4SAlexandre Truong void callchain_param_setup(u64 sample_type, const char *arch)
16610d71a2b2SJiri Olsa {
16620d71a2b2SJiri Olsa 	if (symbol_conf.use_callchain || symbol_conf.cumulate_callchain) {
16630d71a2b2SJiri Olsa 		if ((sample_type & PERF_SAMPLE_REGS_USER) &&
16640d71a2b2SJiri Olsa 		    (sample_type & PERF_SAMPLE_STACK_USER)) {
16650d71a2b2SJiri Olsa 			callchain_param.record_mode = CALLCHAIN_DWARF;
16660d71a2b2SJiri Olsa 			dwarf_callchain_users = true;
16670d71a2b2SJiri Olsa 		} else if (sample_type & PERF_SAMPLE_BRANCH_STACK)
16680d71a2b2SJiri Olsa 			callchain_param.record_mode = CALLCHAIN_LBR;
16690d71a2b2SJiri Olsa 		else
16700d71a2b2SJiri Olsa 			callchain_param.record_mode = CALLCHAIN_FP;
16710d71a2b2SJiri Olsa 	}
1672aa8db3e4SAlexandre Truong 
1673aa8db3e4SAlexandre Truong 	/*
1674aa8db3e4SAlexandre Truong 	 * It's necessary to use libunwind to reliably determine the caller of
1675aa8db3e4SAlexandre Truong 	 * a leaf function on aarch64, as otherwise we cannot know whether to
1676aa8db3e4SAlexandre Truong 	 * start from the LR or FP.
1677aa8db3e4SAlexandre Truong 	 *
1678aa8db3e4SAlexandre Truong 	 * Always starting from the LR can result in duplicate or entirely
1679aa8db3e4SAlexandre Truong 	 * erroneous entries. Always skipping the LR and starting from the FP
1680aa8db3e4SAlexandre Truong 	 * can result in missing entries.
1681aa8db3e4SAlexandre Truong 	 */
1682aa8db3e4SAlexandre Truong 	if (callchain_param.record_mode == CALLCHAIN_FP && !strcmp(arch, "arm64"))
1683aa8db3e4SAlexandre Truong 		dwarf_callchain_users = true;
16840d71a2b2SJiri Olsa }
168547ef8398SJin Yao 
chain_match(struct callchain_list * base_chain,struct callchain_list * pair_chain)168647ef8398SJin Yao static bool chain_match(struct callchain_list *base_chain,
168747ef8398SJin Yao 			struct callchain_list *pair_chain)
168847ef8398SJin Yao {
168947ef8398SJin Yao 	enum match_result match;
169047ef8398SJin Yao 
169147ef8398SJin Yao 	match = match_chain_strings(base_chain->srcline,
169247ef8398SJin Yao 				    pair_chain->srcline);
169347ef8398SJin Yao 	if (match != MATCH_ERROR)
169447ef8398SJin Yao 		return match == MATCH_EQ;
169547ef8398SJin Yao 
169647ef8398SJin Yao 	match = match_chain_dso_addresses(base_chain->ms.map,
169747ef8398SJin Yao 					  base_chain->ip,
169847ef8398SJin Yao 					  pair_chain->ms.map,
169947ef8398SJin Yao 					  pair_chain->ip);
170047ef8398SJin Yao 
170147ef8398SJin Yao 	return match == MATCH_EQ;
170247ef8398SJin Yao }
170347ef8398SJin Yao 
callchain_cnode_matched(struct callchain_node * base_cnode,struct callchain_node * pair_cnode)170447ef8398SJin Yao bool callchain_cnode_matched(struct callchain_node *base_cnode,
170547ef8398SJin Yao 			     struct callchain_node *pair_cnode)
170647ef8398SJin Yao {
170747ef8398SJin Yao 	struct callchain_list *base_chain, *pair_chain;
170847ef8398SJin Yao 	bool match = false;
170947ef8398SJin Yao 
171047ef8398SJin Yao 	pair_chain = list_first_entry(&pair_cnode->val,
171147ef8398SJin Yao 				      struct callchain_list,
171247ef8398SJin Yao 				      list);
171347ef8398SJin Yao 
171447ef8398SJin Yao 	list_for_each_entry(base_chain, &base_cnode->val, list) {
171547ef8398SJin Yao 		if (&pair_chain->list == &pair_cnode->val)
171647ef8398SJin Yao 			return false;
171747ef8398SJin Yao 
171847ef8398SJin Yao 		if (!base_chain->srcline || !pair_chain->srcline) {
171947ef8398SJin Yao 			pair_chain = list_next_entry(pair_chain, list);
172047ef8398SJin Yao 			continue;
172147ef8398SJin Yao 		}
172247ef8398SJin Yao 
172347ef8398SJin Yao 		match = chain_match(base_chain, pair_chain);
172447ef8398SJin Yao 		if (!match)
172547ef8398SJin Yao 			return false;
172647ef8398SJin Yao 
172747ef8398SJin Yao 		pair_chain = list_next_entry(pair_chain, list);
172847ef8398SJin Yao 	}
172947ef8398SJin Yao 
173047ef8398SJin Yao 	/*
173147ef8398SJin Yao 	 * Say chain1 is ABC, chain2 is ABCD, we consider they are
173247ef8398SJin Yao 	 * not fully matched.
173347ef8398SJin Yao 	 */
173447ef8398SJin Yao 	if (pair_chain && (&pair_chain->list != &pair_cnode->val))
173547ef8398SJin Yao 		return false;
173647ef8398SJin Yao 
173747ef8398SJin Yao 	return match;
173847ef8398SJin Yao }
173928904f4dSJin Yao 
count_callchain_hits(struct hist_entry * he)174028904f4dSJin Yao static u64 count_callchain_hits(struct hist_entry *he)
174128904f4dSJin Yao {
174228904f4dSJin Yao 	struct rb_root *root = &he->sorted_chain;
174328904f4dSJin Yao 	struct rb_node *rb_node = rb_first(root);
174428904f4dSJin Yao 	struct callchain_node *node;
174528904f4dSJin Yao 	u64 chain_hits = 0;
174628904f4dSJin Yao 
174728904f4dSJin Yao 	while (rb_node) {
174828904f4dSJin Yao 		node = rb_entry(rb_node, struct callchain_node, rb_node);
174928904f4dSJin Yao 		chain_hits += node->hit;
175028904f4dSJin Yao 		rb_node = rb_next(rb_node);
175128904f4dSJin Yao 	}
175228904f4dSJin Yao 
175328904f4dSJin Yao 	return chain_hits;
175428904f4dSJin Yao }
175528904f4dSJin Yao 
callchain_total_hits(struct hists * hists)175628904f4dSJin Yao u64 callchain_total_hits(struct hists *hists)
175728904f4dSJin Yao {
175828904f4dSJin Yao 	struct rb_node *next = rb_first_cached(&hists->entries);
175928904f4dSJin Yao 	u64 chain_hits = 0;
176028904f4dSJin Yao 
176128904f4dSJin Yao 	while (next) {
176228904f4dSJin Yao 		struct hist_entry *he = rb_entry(next, struct hist_entry,
176328904f4dSJin Yao 						 rb_node);
176428904f4dSJin Yao 
176528904f4dSJin Yao 		chain_hits += count_callchain_hits(he);
176628904f4dSJin Yao 		next = rb_next(&he->rb_node);
176728904f4dSJin Yao 	}
176828904f4dSJin Yao 
176928904f4dSJin Yao 	return chain_hits;
177028904f4dSJin Yao }
17715bbd6badSJin Yao 
callchain_avg_cycles(struct callchain_node * cnode)17725bbd6badSJin Yao s64 callchain_avg_cycles(struct callchain_node *cnode)
17735bbd6badSJin Yao {
17745bbd6badSJin Yao 	struct callchain_list *chain;
17755bbd6badSJin Yao 	s64 cycles = 0;
17765bbd6badSJin Yao 
17775bbd6badSJin Yao 	list_for_each_entry(chain, &cnode->val, list) {
17785bbd6badSJin Yao 		if (chain->srcline && chain->branch_count)
17795bbd6badSJin Yao 			cycles += chain->cycles_count / chain->branch_count;
17805bbd6badSJin Yao 	}
17815bbd6badSJin Yao 
17825bbd6badSJin Yao 	return cycles;
17835bbd6badSJin Yao }
1784