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
618ab12a20SIan Rogers /* Used for thread-local struct callchain_cursor. */
628ab12a20SIan 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 {
9908ab12a20SIan Rogers if (cursor == NULL)
9918ab12a20SIan Rogers return -1;
9928ab12a20SIan 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;
11238ab12a20SIan 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 {
1129*1b46b235SCasey Chen struct machine *machine = node->ms.maps ? maps__machine(node->ms.maps) : NULL;
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)15778ab12a20SIan Rogers static void callchain_cursor__delete(void *vcursor)
15788ab12a20SIan Rogers {
15798ab12a20SIan Rogers struct callchain_cursor *cursor = vcursor;
15808ab12a20SIan Rogers struct callchain_cursor_node *node, *next;
15818ab12a20SIan Rogers
15828ab12a20SIan Rogers callchain_cursor_reset(cursor);
15838ab12a20SIan Rogers for (node = cursor->first; node != NULL; node = next) {
15848ab12a20SIan Rogers next = node->next;
15858ab12a20SIan Rogers free(node);
15868ab12a20SIan Rogers }
15878ab12a20SIan Rogers free(cursor);
15888ab12a20SIan Rogers }
15898ab12a20SIan Rogers
init_callchain_cursor_key(void)15908ab12a20SIan Rogers static void init_callchain_cursor_key(void)
15918ab12a20SIan Rogers {
15928ab12a20SIan Rogers if (pthread_key_create(&callchain_cursor, callchain_cursor__delete)) {
15938ab12a20SIan Rogers pr_err("callchain cursor creation failed");
15948ab12a20SIan Rogers abort();
15958ab12a20SIan Rogers }
15968ab12a20SIan Rogers }
15978ab12a20SIan Rogers
get_tls_callchain_cursor(void)15988ab12a20SIan Rogers struct callchain_cursor *get_tls_callchain_cursor(void)
15998ab12a20SIan Rogers {
16008ab12a20SIan Rogers static pthread_once_t once_control = PTHREAD_ONCE_INIT;
16018ab12a20SIan Rogers struct callchain_cursor *cursor;
16028ab12a20SIan Rogers
16038ab12a20SIan Rogers pthread_once(&once_control, init_callchain_cursor_key);
16048ab12a20SIan Rogers cursor = pthread_getspecific(callchain_cursor);
16058ab12a20SIan Rogers if (!cursor) {
16068ab12a20SIan Rogers cursor = zalloc(sizeof(*cursor));
16078ab12a20SIan Rogers if (!cursor)
16088ab12a20SIan Rogers pr_debug3("%s: not enough memory\n", __func__);
16098ab12a20SIan Rogers pthread_setspecific(callchain_cursor, cursor);
16108ab12a20SIan Rogers }
16118ab12a20SIan Rogers return cursor;
16128ab12a20SIan Rogers }
16138ab12a20SIan 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