162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2009-2011, Frederic Weisbecker <fweisbec@gmail.com> 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Handle the callchains from the stream in an ad-hoc radix tree and then 662306a36Sopenharmony_ci * sort them in an rbtree. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Using a radix for code path provides a fast retrieval and factorizes 962306a36Sopenharmony_ci * memory use. Also that lets us use the paths in a hierarchical graph view. 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <inttypes.h> 1462306a36Sopenharmony_ci#include <stdlib.h> 1562306a36Sopenharmony_ci#include <stdio.h> 1662306a36Sopenharmony_ci#include <stdbool.h> 1762306a36Sopenharmony_ci#include <errno.h> 1862306a36Sopenharmony_ci#include <math.h> 1962306a36Sopenharmony_ci#include <linux/string.h> 2062306a36Sopenharmony_ci#include <linux/zalloc.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include "asm/bug.h" 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include "debug.h" 2562306a36Sopenharmony_ci#include "dso.h" 2662306a36Sopenharmony_ci#include "event.h" 2762306a36Sopenharmony_ci#include "hist.h" 2862306a36Sopenharmony_ci#include "sort.h" 2962306a36Sopenharmony_ci#include "machine.h" 3062306a36Sopenharmony_ci#include "map.h" 3162306a36Sopenharmony_ci#include "callchain.h" 3262306a36Sopenharmony_ci#include "branch.h" 3362306a36Sopenharmony_ci#include "symbol.h" 3462306a36Sopenharmony_ci#include "util.h" 3562306a36Sopenharmony_ci#include "../perf.h" 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#define CALLCHAIN_PARAM_DEFAULT \ 3862306a36Sopenharmony_ci .mode = CHAIN_GRAPH_ABS, \ 3962306a36Sopenharmony_ci .min_percent = 0.5, \ 4062306a36Sopenharmony_ci .order = ORDER_CALLEE, \ 4162306a36Sopenharmony_ci .key = CCKEY_FUNCTION, \ 4262306a36Sopenharmony_ci .value = CCVAL_PERCENT, \ 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistruct callchain_param callchain_param = { 4562306a36Sopenharmony_ci CALLCHAIN_PARAM_DEFAULT 4662306a36Sopenharmony_ci}; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci/* 4962306a36Sopenharmony_ci * Are there any events usind DWARF callchains? 5062306a36Sopenharmony_ci * 5162306a36Sopenharmony_ci * I.e. 5262306a36Sopenharmony_ci * 5362306a36Sopenharmony_ci * -e cycles/call-graph=dwarf/ 5462306a36Sopenharmony_ci */ 5562306a36Sopenharmony_cibool dwarf_callchain_users; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistruct callchain_param callchain_param_default = { 5862306a36Sopenharmony_ci CALLCHAIN_PARAM_DEFAULT 5962306a36Sopenharmony_ci}; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci/* Used for thread-local struct callchain_cursor. */ 6262306a36Sopenharmony_cistatic pthread_key_t callchain_cursor; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ciint parse_callchain_record_opt(const char *arg, struct callchain_param *param) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci return parse_callchain_record(arg, param); 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic int parse_callchain_mode(const char *value) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci if (!strncmp(value, "graph", strlen(value))) { 7262306a36Sopenharmony_ci callchain_param.mode = CHAIN_GRAPH_ABS; 7362306a36Sopenharmony_ci return 0; 7462306a36Sopenharmony_ci } 7562306a36Sopenharmony_ci if (!strncmp(value, "flat", strlen(value))) { 7662306a36Sopenharmony_ci callchain_param.mode = CHAIN_FLAT; 7762306a36Sopenharmony_ci return 0; 7862306a36Sopenharmony_ci } 7962306a36Sopenharmony_ci if (!strncmp(value, "fractal", strlen(value))) { 8062306a36Sopenharmony_ci callchain_param.mode = CHAIN_GRAPH_REL; 8162306a36Sopenharmony_ci return 0; 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci if (!strncmp(value, "folded", strlen(value))) { 8462306a36Sopenharmony_ci callchain_param.mode = CHAIN_FOLDED; 8562306a36Sopenharmony_ci return 0; 8662306a36Sopenharmony_ci } 8762306a36Sopenharmony_ci return -1; 8862306a36Sopenharmony_ci} 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cistatic int parse_callchain_order(const char *value) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci if (!strncmp(value, "caller", strlen(value))) { 9362306a36Sopenharmony_ci callchain_param.order = ORDER_CALLER; 9462306a36Sopenharmony_ci callchain_param.order_set = true; 9562306a36Sopenharmony_ci return 0; 9662306a36Sopenharmony_ci } 9762306a36Sopenharmony_ci if (!strncmp(value, "callee", strlen(value))) { 9862306a36Sopenharmony_ci callchain_param.order = ORDER_CALLEE; 9962306a36Sopenharmony_ci callchain_param.order_set = true; 10062306a36Sopenharmony_ci return 0; 10162306a36Sopenharmony_ci } 10262306a36Sopenharmony_ci return -1; 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic int parse_callchain_sort_key(const char *value) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci if (!strncmp(value, "function", strlen(value))) { 10862306a36Sopenharmony_ci callchain_param.key = CCKEY_FUNCTION; 10962306a36Sopenharmony_ci return 0; 11062306a36Sopenharmony_ci } 11162306a36Sopenharmony_ci if (!strncmp(value, "address", strlen(value))) { 11262306a36Sopenharmony_ci callchain_param.key = CCKEY_ADDRESS; 11362306a36Sopenharmony_ci return 0; 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci if (!strncmp(value, "srcline", strlen(value))) { 11662306a36Sopenharmony_ci callchain_param.key = CCKEY_SRCLINE; 11762306a36Sopenharmony_ci return 0; 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci if (!strncmp(value, "branch", strlen(value))) { 12062306a36Sopenharmony_ci callchain_param.branch_callstack = 1; 12162306a36Sopenharmony_ci return 0; 12262306a36Sopenharmony_ci } 12362306a36Sopenharmony_ci return -1; 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistatic int parse_callchain_value(const char *value) 12762306a36Sopenharmony_ci{ 12862306a36Sopenharmony_ci if (!strncmp(value, "percent", strlen(value))) { 12962306a36Sopenharmony_ci callchain_param.value = CCVAL_PERCENT; 13062306a36Sopenharmony_ci return 0; 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci if (!strncmp(value, "period", strlen(value))) { 13362306a36Sopenharmony_ci callchain_param.value = CCVAL_PERIOD; 13462306a36Sopenharmony_ci return 0; 13562306a36Sopenharmony_ci } 13662306a36Sopenharmony_ci if (!strncmp(value, "count", strlen(value))) { 13762306a36Sopenharmony_ci callchain_param.value = CCVAL_COUNT; 13862306a36Sopenharmony_ci return 0; 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci return -1; 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic int get_stack_size(const char *str, unsigned long *_size) 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci char *endptr; 14662306a36Sopenharmony_ci unsigned long size; 14762306a36Sopenharmony_ci unsigned long max_size = round_down(USHRT_MAX, sizeof(u64)); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci size = strtoul(str, &endptr, 0); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci do { 15262306a36Sopenharmony_ci if (*endptr) 15362306a36Sopenharmony_ci break; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci size = round_up(size, sizeof(u64)); 15662306a36Sopenharmony_ci if (!size || size > max_size) 15762306a36Sopenharmony_ci break; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci *_size = size; 16062306a36Sopenharmony_ci return 0; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci } while (0); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci pr_err("callchain: Incorrect stack dump size (max %ld): %s\n", 16562306a36Sopenharmony_ci max_size, str); 16662306a36Sopenharmony_ci return -1; 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistatic int 17062306a36Sopenharmony_ci__parse_callchain_report_opt(const char *arg, bool allow_record_opt) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci char *tok; 17362306a36Sopenharmony_ci char *endptr, *saveptr = NULL; 17462306a36Sopenharmony_ci bool minpcnt_set = false; 17562306a36Sopenharmony_ci bool record_opt_set = false; 17662306a36Sopenharmony_ci bool try_stack_size = false; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci callchain_param.enabled = true; 17962306a36Sopenharmony_ci symbol_conf.use_callchain = true; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci if (!arg) 18262306a36Sopenharmony_ci return 0; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci while ((tok = strtok_r((char *)arg, ",", &saveptr)) != NULL) { 18562306a36Sopenharmony_ci if (!strncmp(tok, "none", strlen(tok))) { 18662306a36Sopenharmony_ci callchain_param.mode = CHAIN_NONE; 18762306a36Sopenharmony_ci callchain_param.enabled = false; 18862306a36Sopenharmony_ci symbol_conf.use_callchain = false; 18962306a36Sopenharmony_ci return 0; 19062306a36Sopenharmony_ci } 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci if (!parse_callchain_mode(tok) || 19362306a36Sopenharmony_ci !parse_callchain_order(tok) || 19462306a36Sopenharmony_ci !parse_callchain_sort_key(tok) || 19562306a36Sopenharmony_ci !parse_callchain_value(tok)) { 19662306a36Sopenharmony_ci /* parsing ok - move on to the next */ 19762306a36Sopenharmony_ci try_stack_size = false; 19862306a36Sopenharmony_ci goto next; 19962306a36Sopenharmony_ci } else if (allow_record_opt && !record_opt_set) { 20062306a36Sopenharmony_ci if (parse_callchain_record(tok, &callchain_param)) 20162306a36Sopenharmony_ci goto try_numbers; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci /* assume that number followed by 'dwarf' is stack size */ 20462306a36Sopenharmony_ci if (callchain_param.record_mode == CALLCHAIN_DWARF) 20562306a36Sopenharmony_ci try_stack_size = true; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci record_opt_set = true; 20862306a36Sopenharmony_ci goto next; 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_citry_numbers: 21262306a36Sopenharmony_ci if (try_stack_size) { 21362306a36Sopenharmony_ci unsigned long size = 0; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci if (get_stack_size(tok, &size) < 0) 21662306a36Sopenharmony_ci return -1; 21762306a36Sopenharmony_ci callchain_param.dump_size = size; 21862306a36Sopenharmony_ci try_stack_size = false; 21962306a36Sopenharmony_ci } else if (!minpcnt_set) { 22062306a36Sopenharmony_ci /* try to get the min percent */ 22162306a36Sopenharmony_ci callchain_param.min_percent = strtod(tok, &endptr); 22262306a36Sopenharmony_ci if (tok == endptr) 22362306a36Sopenharmony_ci return -1; 22462306a36Sopenharmony_ci minpcnt_set = true; 22562306a36Sopenharmony_ci } else { 22662306a36Sopenharmony_ci /* try print limit at last */ 22762306a36Sopenharmony_ci callchain_param.print_limit = strtoul(tok, &endptr, 0); 22862306a36Sopenharmony_ci if (tok == endptr) 22962306a36Sopenharmony_ci return -1; 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_cinext: 23262306a36Sopenharmony_ci arg = NULL; 23362306a36Sopenharmony_ci } 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci if (callchain_register_param(&callchain_param) < 0) { 23662306a36Sopenharmony_ci pr_err("Can't register callchain params\n"); 23762306a36Sopenharmony_ci return -1; 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci return 0; 24062306a36Sopenharmony_ci} 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ciint parse_callchain_report_opt(const char *arg) 24362306a36Sopenharmony_ci{ 24462306a36Sopenharmony_ci return __parse_callchain_report_opt(arg, false); 24562306a36Sopenharmony_ci} 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ciint parse_callchain_top_opt(const char *arg) 24862306a36Sopenharmony_ci{ 24962306a36Sopenharmony_ci return __parse_callchain_report_opt(arg, true); 25062306a36Sopenharmony_ci} 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ciint parse_callchain_record(const char *arg, struct callchain_param *param) 25362306a36Sopenharmony_ci{ 25462306a36Sopenharmony_ci char *tok, *name, *saveptr = NULL; 25562306a36Sopenharmony_ci char *buf; 25662306a36Sopenharmony_ci int ret = -1; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci /* We need buffer that we know we can write to. */ 25962306a36Sopenharmony_ci buf = malloc(strlen(arg) + 1); 26062306a36Sopenharmony_ci if (!buf) 26162306a36Sopenharmony_ci return -ENOMEM; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci strcpy(buf, arg); 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci tok = strtok_r((char *)buf, ",", &saveptr); 26662306a36Sopenharmony_ci name = tok ? : (char *)buf; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci do { 26962306a36Sopenharmony_ci /* Framepointer style */ 27062306a36Sopenharmony_ci if (!strncmp(name, "fp", sizeof("fp"))) { 27162306a36Sopenharmony_ci ret = 0; 27262306a36Sopenharmony_ci param->record_mode = CALLCHAIN_FP; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci tok = strtok_r(NULL, ",", &saveptr); 27562306a36Sopenharmony_ci if (tok) { 27662306a36Sopenharmony_ci unsigned long size; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci size = strtoul(tok, &name, 0); 27962306a36Sopenharmony_ci if (size < (unsigned) sysctl__max_stack()) 28062306a36Sopenharmony_ci param->max_stack = size; 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci break; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci /* Dwarf style */ 28562306a36Sopenharmony_ci } else if (!strncmp(name, "dwarf", sizeof("dwarf"))) { 28662306a36Sopenharmony_ci const unsigned long default_stack_dump_size = 8192; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci ret = 0; 28962306a36Sopenharmony_ci param->record_mode = CALLCHAIN_DWARF; 29062306a36Sopenharmony_ci param->dump_size = default_stack_dump_size; 29162306a36Sopenharmony_ci dwarf_callchain_users = true; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci tok = strtok_r(NULL, ",", &saveptr); 29462306a36Sopenharmony_ci if (tok) { 29562306a36Sopenharmony_ci unsigned long size = 0; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci ret = get_stack_size(tok, &size); 29862306a36Sopenharmony_ci param->dump_size = size; 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ci } else if (!strncmp(name, "lbr", sizeof("lbr"))) { 30162306a36Sopenharmony_ci if (!strtok_r(NULL, ",", &saveptr)) { 30262306a36Sopenharmony_ci param->record_mode = CALLCHAIN_LBR; 30362306a36Sopenharmony_ci ret = 0; 30462306a36Sopenharmony_ci } else 30562306a36Sopenharmony_ci pr_err("callchain: No more arguments " 30662306a36Sopenharmony_ci "needed for --call-graph lbr\n"); 30762306a36Sopenharmony_ci break; 30862306a36Sopenharmony_ci } else { 30962306a36Sopenharmony_ci pr_err("callchain: Unknown --call-graph option " 31062306a36Sopenharmony_ci "value: %s\n", arg); 31162306a36Sopenharmony_ci break; 31262306a36Sopenharmony_ci } 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci } while (0); 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci free(buf); 31762306a36Sopenharmony_ci return ret; 31862306a36Sopenharmony_ci} 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ciint perf_callchain_config(const char *var, const char *value) 32162306a36Sopenharmony_ci{ 32262306a36Sopenharmony_ci char *endptr; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci if (!strstarts(var, "call-graph.")) 32562306a36Sopenharmony_ci return 0; 32662306a36Sopenharmony_ci var += sizeof("call-graph.") - 1; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci if (!strcmp(var, "record-mode")) 32962306a36Sopenharmony_ci return parse_callchain_record_opt(value, &callchain_param); 33062306a36Sopenharmony_ci if (!strcmp(var, "dump-size")) { 33162306a36Sopenharmony_ci unsigned long size = 0; 33262306a36Sopenharmony_ci int ret; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci ret = get_stack_size(value, &size); 33562306a36Sopenharmony_ci callchain_param.dump_size = size; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci return ret; 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci if (!strcmp(var, "print-type")){ 34062306a36Sopenharmony_ci int ret; 34162306a36Sopenharmony_ci ret = parse_callchain_mode(value); 34262306a36Sopenharmony_ci if (ret == -1) 34362306a36Sopenharmony_ci pr_err("Invalid callchain mode: %s\n", value); 34462306a36Sopenharmony_ci return ret; 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci if (!strcmp(var, "order")){ 34762306a36Sopenharmony_ci int ret; 34862306a36Sopenharmony_ci ret = parse_callchain_order(value); 34962306a36Sopenharmony_ci if (ret == -1) 35062306a36Sopenharmony_ci pr_err("Invalid callchain order: %s\n", value); 35162306a36Sopenharmony_ci return ret; 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci if (!strcmp(var, "sort-key")){ 35462306a36Sopenharmony_ci int ret; 35562306a36Sopenharmony_ci ret = parse_callchain_sort_key(value); 35662306a36Sopenharmony_ci if (ret == -1) 35762306a36Sopenharmony_ci pr_err("Invalid callchain sort key: %s\n", value); 35862306a36Sopenharmony_ci return ret; 35962306a36Sopenharmony_ci } 36062306a36Sopenharmony_ci if (!strcmp(var, "threshold")) { 36162306a36Sopenharmony_ci callchain_param.min_percent = strtod(value, &endptr); 36262306a36Sopenharmony_ci if (value == endptr) { 36362306a36Sopenharmony_ci pr_err("Invalid callchain threshold: %s\n", value); 36462306a36Sopenharmony_ci return -1; 36562306a36Sopenharmony_ci } 36662306a36Sopenharmony_ci } 36762306a36Sopenharmony_ci if (!strcmp(var, "print-limit")) { 36862306a36Sopenharmony_ci callchain_param.print_limit = strtod(value, &endptr); 36962306a36Sopenharmony_ci if (value == endptr) { 37062306a36Sopenharmony_ci pr_err("Invalid callchain print limit: %s\n", value); 37162306a36Sopenharmony_ci return -1; 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci return 0; 37662306a36Sopenharmony_ci} 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_cistatic void 37962306a36Sopenharmony_cirb_insert_callchain(struct rb_root *root, struct callchain_node *chain, 38062306a36Sopenharmony_ci enum chain_mode mode) 38162306a36Sopenharmony_ci{ 38262306a36Sopenharmony_ci struct rb_node **p = &root->rb_node; 38362306a36Sopenharmony_ci struct rb_node *parent = NULL; 38462306a36Sopenharmony_ci struct callchain_node *rnode; 38562306a36Sopenharmony_ci u64 chain_cumul = callchain_cumul_hits(chain); 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci while (*p) { 38862306a36Sopenharmony_ci u64 rnode_cumul; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci parent = *p; 39162306a36Sopenharmony_ci rnode = rb_entry(parent, struct callchain_node, rb_node); 39262306a36Sopenharmony_ci rnode_cumul = callchain_cumul_hits(rnode); 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci switch (mode) { 39562306a36Sopenharmony_ci case CHAIN_FLAT: 39662306a36Sopenharmony_ci case CHAIN_FOLDED: 39762306a36Sopenharmony_ci if (rnode->hit < chain->hit) 39862306a36Sopenharmony_ci p = &(*p)->rb_left; 39962306a36Sopenharmony_ci else 40062306a36Sopenharmony_ci p = &(*p)->rb_right; 40162306a36Sopenharmony_ci break; 40262306a36Sopenharmony_ci case CHAIN_GRAPH_ABS: /* Falldown */ 40362306a36Sopenharmony_ci case CHAIN_GRAPH_REL: 40462306a36Sopenharmony_ci if (rnode_cumul < chain_cumul) 40562306a36Sopenharmony_ci p = &(*p)->rb_left; 40662306a36Sopenharmony_ci else 40762306a36Sopenharmony_ci p = &(*p)->rb_right; 40862306a36Sopenharmony_ci break; 40962306a36Sopenharmony_ci case CHAIN_NONE: 41062306a36Sopenharmony_ci default: 41162306a36Sopenharmony_ci break; 41262306a36Sopenharmony_ci } 41362306a36Sopenharmony_ci } 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci rb_link_node(&chain->rb_node, parent, p); 41662306a36Sopenharmony_ci rb_insert_color(&chain->rb_node, root); 41762306a36Sopenharmony_ci} 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_cistatic void 42062306a36Sopenharmony_ci__sort_chain_flat(struct rb_root *rb_root, struct callchain_node *node, 42162306a36Sopenharmony_ci u64 min_hit) 42262306a36Sopenharmony_ci{ 42362306a36Sopenharmony_ci struct rb_node *n; 42462306a36Sopenharmony_ci struct callchain_node *child; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci n = rb_first(&node->rb_root_in); 42762306a36Sopenharmony_ci while (n) { 42862306a36Sopenharmony_ci child = rb_entry(n, struct callchain_node, rb_node_in); 42962306a36Sopenharmony_ci n = rb_next(n); 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci __sort_chain_flat(rb_root, child, min_hit); 43262306a36Sopenharmony_ci } 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci if (node->hit && node->hit >= min_hit) 43562306a36Sopenharmony_ci rb_insert_callchain(rb_root, node, CHAIN_FLAT); 43662306a36Sopenharmony_ci} 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci/* 43962306a36Sopenharmony_ci * Once we get every callchains from the stream, we can now 44062306a36Sopenharmony_ci * sort them by hit 44162306a36Sopenharmony_ci */ 44262306a36Sopenharmony_cistatic void 44362306a36Sopenharmony_cisort_chain_flat(struct rb_root *rb_root, struct callchain_root *root, 44462306a36Sopenharmony_ci u64 min_hit, struct callchain_param *param __maybe_unused) 44562306a36Sopenharmony_ci{ 44662306a36Sopenharmony_ci *rb_root = RB_ROOT; 44762306a36Sopenharmony_ci __sort_chain_flat(rb_root, &root->node, min_hit); 44862306a36Sopenharmony_ci} 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_cistatic void __sort_chain_graph_abs(struct callchain_node *node, 45162306a36Sopenharmony_ci u64 min_hit) 45262306a36Sopenharmony_ci{ 45362306a36Sopenharmony_ci struct rb_node *n; 45462306a36Sopenharmony_ci struct callchain_node *child; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci node->rb_root = RB_ROOT; 45762306a36Sopenharmony_ci n = rb_first(&node->rb_root_in); 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci while (n) { 46062306a36Sopenharmony_ci child = rb_entry(n, struct callchain_node, rb_node_in); 46162306a36Sopenharmony_ci n = rb_next(n); 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci __sort_chain_graph_abs(child, min_hit); 46462306a36Sopenharmony_ci if (callchain_cumul_hits(child) >= min_hit) 46562306a36Sopenharmony_ci rb_insert_callchain(&node->rb_root, child, 46662306a36Sopenharmony_ci CHAIN_GRAPH_ABS); 46762306a36Sopenharmony_ci } 46862306a36Sopenharmony_ci} 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_cistatic void 47162306a36Sopenharmony_cisort_chain_graph_abs(struct rb_root *rb_root, struct callchain_root *chain_root, 47262306a36Sopenharmony_ci u64 min_hit, struct callchain_param *param __maybe_unused) 47362306a36Sopenharmony_ci{ 47462306a36Sopenharmony_ci __sort_chain_graph_abs(&chain_root->node, min_hit); 47562306a36Sopenharmony_ci rb_root->rb_node = chain_root->node.rb_root.rb_node; 47662306a36Sopenharmony_ci} 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_cistatic void __sort_chain_graph_rel(struct callchain_node *node, 47962306a36Sopenharmony_ci double min_percent) 48062306a36Sopenharmony_ci{ 48162306a36Sopenharmony_ci struct rb_node *n; 48262306a36Sopenharmony_ci struct callchain_node *child; 48362306a36Sopenharmony_ci u64 min_hit; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci node->rb_root = RB_ROOT; 48662306a36Sopenharmony_ci min_hit = ceil(node->children_hit * min_percent); 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci n = rb_first(&node->rb_root_in); 48962306a36Sopenharmony_ci while (n) { 49062306a36Sopenharmony_ci child = rb_entry(n, struct callchain_node, rb_node_in); 49162306a36Sopenharmony_ci n = rb_next(n); 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci __sort_chain_graph_rel(child, min_percent); 49462306a36Sopenharmony_ci if (callchain_cumul_hits(child) >= min_hit) 49562306a36Sopenharmony_ci rb_insert_callchain(&node->rb_root, child, 49662306a36Sopenharmony_ci CHAIN_GRAPH_REL); 49762306a36Sopenharmony_ci } 49862306a36Sopenharmony_ci} 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_cistatic void 50162306a36Sopenharmony_cisort_chain_graph_rel(struct rb_root *rb_root, struct callchain_root *chain_root, 50262306a36Sopenharmony_ci u64 min_hit __maybe_unused, struct callchain_param *param) 50362306a36Sopenharmony_ci{ 50462306a36Sopenharmony_ci __sort_chain_graph_rel(&chain_root->node, param->min_percent / 100.0); 50562306a36Sopenharmony_ci rb_root->rb_node = chain_root->node.rb_root.rb_node; 50662306a36Sopenharmony_ci} 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ciint callchain_register_param(struct callchain_param *param) 50962306a36Sopenharmony_ci{ 51062306a36Sopenharmony_ci switch (param->mode) { 51162306a36Sopenharmony_ci case CHAIN_GRAPH_ABS: 51262306a36Sopenharmony_ci param->sort = sort_chain_graph_abs; 51362306a36Sopenharmony_ci break; 51462306a36Sopenharmony_ci case CHAIN_GRAPH_REL: 51562306a36Sopenharmony_ci param->sort = sort_chain_graph_rel; 51662306a36Sopenharmony_ci break; 51762306a36Sopenharmony_ci case CHAIN_FLAT: 51862306a36Sopenharmony_ci case CHAIN_FOLDED: 51962306a36Sopenharmony_ci param->sort = sort_chain_flat; 52062306a36Sopenharmony_ci break; 52162306a36Sopenharmony_ci case CHAIN_NONE: 52262306a36Sopenharmony_ci default: 52362306a36Sopenharmony_ci return -1; 52462306a36Sopenharmony_ci } 52562306a36Sopenharmony_ci return 0; 52662306a36Sopenharmony_ci} 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci/* 52962306a36Sopenharmony_ci * Create a child for a parent. If inherit_children, then the new child 53062306a36Sopenharmony_ci * will become the new parent of it's parent children 53162306a36Sopenharmony_ci */ 53262306a36Sopenharmony_cistatic struct callchain_node * 53362306a36Sopenharmony_cicreate_child(struct callchain_node *parent, bool inherit_children) 53462306a36Sopenharmony_ci{ 53562306a36Sopenharmony_ci struct callchain_node *new; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci new = zalloc(sizeof(*new)); 53862306a36Sopenharmony_ci if (!new) { 53962306a36Sopenharmony_ci perror("not enough memory to create child for code path tree"); 54062306a36Sopenharmony_ci return NULL; 54162306a36Sopenharmony_ci } 54262306a36Sopenharmony_ci new->parent = parent; 54362306a36Sopenharmony_ci INIT_LIST_HEAD(&new->val); 54462306a36Sopenharmony_ci INIT_LIST_HEAD(&new->parent_val); 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci if (inherit_children) { 54762306a36Sopenharmony_ci struct rb_node *n; 54862306a36Sopenharmony_ci struct callchain_node *child; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci new->rb_root_in = parent->rb_root_in; 55162306a36Sopenharmony_ci parent->rb_root_in = RB_ROOT; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci n = rb_first(&new->rb_root_in); 55462306a36Sopenharmony_ci while (n) { 55562306a36Sopenharmony_ci child = rb_entry(n, struct callchain_node, rb_node_in); 55662306a36Sopenharmony_ci child->parent = new; 55762306a36Sopenharmony_ci n = rb_next(n); 55862306a36Sopenharmony_ci } 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci /* make it the first child */ 56162306a36Sopenharmony_ci rb_link_node(&new->rb_node_in, NULL, &parent->rb_root_in.rb_node); 56262306a36Sopenharmony_ci rb_insert_color(&new->rb_node_in, &parent->rb_root_in); 56362306a36Sopenharmony_ci } 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci return new; 56662306a36Sopenharmony_ci} 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci/* 57062306a36Sopenharmony_ci * Fill the node with callchain values 57162306a36Sopenharmony_ci */ 57262306a36Sopenharmony_cistatic int 57362306a36Sopenharmony_cifill_node(struct callchain_node *node, struct callchain_cursor *cursor) 57462306a36Sopenharmony_ci{ 57562306a36Sopenharmony_ci struct callchain_cursor_node *cursor_node; 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci node->val_nr = cursor->nr - cursor->pos; 57862306a36Sopenharmony_ci if (!node->val_nr) 57962306a36Sopenharmony_ci pr_warning("Warning: empty node in callchain tree\n"); 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci cursor_node = callchain_cursor_current(cursor); 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci while (cursor_node) { 58462306a36Sopenharmony_ci struct callchain_list *call; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci call = zalloc(sizeof(*call)); 58762306a36Sopenharmony_ci if (!call) { 58862306a36Sopenharmony_ci perror("not enough memory for the code path tree"); 58962306a36Sopenharmony_ci return -1; 59062306a36Sopenharmony_ci } 59162306a36Sopenharmony_ci call->ip = cursor_node->ip; 59262306a36Sopenharmony_ci call->ms = cursor_node->ms; 59362306a36Sopenharmony_ci call->ms.map = map__get(call->ms.map); 59462306a36Sopenharmony_ci call->ms.maps = maps__get(call->ms.maps); 59562306a36Sopenharmony_ci call->srcline = cursor_node->srcline; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci if (cursor_node->branch) { 59862306a36Sopenharmony_ci call->branch_count = 1; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci if (cursor_node->branch_from) { 60162306a36Sopenharmony_ci /* 60262306a36Sopenharmony_ci * branch_from is set with value somewhere else 60362306a36Sopenharmony_ci * to imply it's "to" of a branch. 60462306a36Sopenharmony_ci */ 60562306a36Sopenharmony_ci call->brtype_stat.branch_to = true; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci if (cursor_node->branch_flags.predicted) 60862306a36Sopenharmony_ci call->predicted_count = 1; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci if (cursor_node->branch_flags.abort) 61162306a36Sopenharmony_ci call->abort_count = 1; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci branch_type_count(&call->brtype_stat, 61462306a36Sopenharmony_ci &cursor_node->branch_flags, 61562306a36Sopenharmony_ci cursor_node->branch_from, 61662306a36Sopenharmony_ci cursor_node->ip); 61762306a36Sopenharmony_ci } else { 61862306a36Sopenharmony_ci /* 61962306a36Sopenharmony_ci * It's "from" of a branch 62062306a36Sopenharmony_ci */ 62162306a36Sopenharmony_ci call->brtype_stat.branch_to = false; 62262306a36Sopenharmony_ci call->cycles_count = 62362306a36Sopenharmony_ci cursor_node->branch_flags.cycles; 62462306a36Sopenharmony_ci call->iter_count = cursor_node->nr_loop_iter; 62562306a36Sopenharmony_ci call->iter_cycles = cursor_node->iter_cycles; 62662306a36Sopenharmony_ci } 62762306a36Sopenharmony_ci } 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci list_add_tail(&call->list, &node->val); 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci callchain_cursor_advance(cursor); 63262306a36Sopenharmony_ci cursor_node = callchain_cursor_current(cursor); 63362306a36Sopenharmony_ci } 63462306a36Sopenharmony_ci return 0; 63562306a36Sopenharmony_ci} 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_cistatic struct callchain_node * 63862306a36Sopenharmony_ciadd_child(struct callchain_node *parent, 63962306a36Sopenharmony_ci struct callchain_cursor *cursor, 64062306a36Sopenharmony_ci u64 period) 64162306a36Sopenharmony_ci{ 64262306a36Sopenharmony_ci struct callchain_node *new; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci new = create_child(parent, false); 64562306a36Sopenharmony_ci if (new == NULL) 64662306a36Sopenharmony_ci return NULL; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci if (fill_node(new, cursor) < 0) { 64962306a36Sopenharmony_ci struct callchain_list *call, *tmp; 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci list_for_each_entry_safe(call, tmp, &new->val, list) { 65262306a36Sopenharmony_ci list_del_init(&call->list); 65362306a36Sopenharmony_ci map__zput(call->ms.map); 65462306a36Sopenharmony_ci maps__zput(call->ms.maps); 65562306a36Sopenharmony_ci free(call); 65662306a36Sopenharmony_ci } 65762306a36Sopenharmony_ci free(new); 65862306a36Sopenharmony_ci return NULL; 65962306a36Sopenharmony_ci } 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci new->children_hit = 0; 66262306a36Sopenharmony_ci new->hit = period; 66362306a36Sopenharmony_ci new->children_count = 0; 66462306a36Sopenharmony_ci new->count = 1; 66562306a36Sopenharmony_ci return new; 66662306a36Sopenharmony_ci} 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_cienum match_result { 66962306a36Sopenharmony_ci MATCH_ERROR = -1, 67062306a36Sopenharmony_ci MATCH_EQ, 67162306a36Sopenharmony_ci MATCH_LT, 67262306a36Sopenharmony_ci MATCH_GT, 67362306a36Sopenharmony_ci}; 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_cistatic enum match_result match_chain_strings(const char *left, 67662306a36Sopenharmony_ci const char *right) 67762306a36Sopenharmony_ci{ 67862306a36Sopenharmony_ci enum match_result ret = MATCH_EQ; 67962306a36Sopenharmony_ci int cmp; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci if (left && right) 68262306a36Sopenharmony_ci cmp = strcmp(left, right); 68362306a36Sopenharmony_ci else if (!left && right) 68462306a36Sopenharmony_ci cmp = 1; 68562306a36Sopenharmony_ci else if (left && !right) 68662306a36Sopenharmony_ci cmp = -1; 68762306a36Sopenharmony_ci else 68862306a36Sopenharmony_ci return MATCH_ERROR; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci if (cmp != 0) 69162306a36Sopenharmony_ci ret = cmp < 0 ? MATCH_LT : MATCH_GT; 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci return ret; 69462306a36Sopenharmony_ci} 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci/* 69762306a36Sopenharmony_ci * We need to always use relative addresses because we're aggregating 69862306a36Sopenharmony_ci * callchains from multiple threads, i.e. different address spaces, so 69962306a36Sopenharmony_ci * comparing absolute addresses make no sense as a symbol in a DSO may end up 70062306a36Sopenharmony_ci * in a different address when used in a different binary or even the same 70162306a36Sopenharmony_ci * binary but with some sort of address randomization technique, thus we need 70262306a36Sopenharmony_ci * to compare just relative addresses. -acme 70362306a36Sopenharmony_ci */ 70462306a36Sopenharmony_cistatic enum match_result match_chain_dso_addresses(struct map *left_map, u64 left_ip, 70562306a36Sopenharmony_ci struct map *right_map, u64 right_ip) 70662306a36Sopenharmony_ci{ 70762306a36Sopenharmony_ci struct dso *left_dso = left_map ? map__dso(left_map) : NULL; 70862306a36Sopenharmony_ci struct dso *right_dso = right_map ? map__dso(right_map) : NULL; 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci if (left_dso != right_dso) 71162306a36Sopenharmony_ci return left_dso < right_dso ? MATCH_LT : MATCH_GT; 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci if (left_ip != right_ip) 71462306a36Sopenharmony_ci return left_ip < right_ip ? MATCH_LT : MATCH_GT; 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci return MATCH_EQ; 71762306a36Sopenharmony_ci} 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_cistatic enum match_result match_chain(struct callchain_cursor_node *node, 72062306a36Sopenharmony_ci struct callchain_list *cnode) 72162306a36Sopenharmony_ci{ 72262306a36Sopenharmony_ci enum match_result match = MATCH_ERROR; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci switch (callchain_param.key) { 72562306a36Sopenharmony_ci case CCKEY_SRCLINE: 72662306a36Sopenharmony_ci match = match_chain_strings(cnode->srcline, node->srcline); 72762306a36Sopenharmony_ci if (match != MATCH_ERROR) 72862306a36Sopenharmony_ci break; 72962306a36Sopenharmony_ci /* otherwise fall-back to symbol-based comparison below */ 73062306a36Sopenharmony_ci fallthrough; 73162306a36Sopenharmony_ci case CCKEY_FUNCTION: 73262306a36Sopenharmony_ci if (node->ms.sym && cnode->ms.sym) { 73362306a36Sopenharmony_ci /* 73462306a36Sopenharmony_ci * Compare inlined frames based on their symbol name 73562306a36Sopenharmony_ci * because different inlined frames will have the same 73662306a36Sopenharmony_ci * symbol start. Otherwise do a faster comparison based 73762306a36Sopenharmony_ci * on the symbol start address. 73862306a36Sopenharmony_ci */ 73962306a36Sopenharmony_ci if (cnode->ms.sym->inlined || node->ms.sym->inlined) { 74062306a36Sopenharmony_ci match = match_chain_strings(cnode->ms.sym->name, 74162306a36Sopenharmony_ci node->ms.sym->name); 74262306a36Sopenharmony_ci if (match != MATCH_ERROR) 74362306a36Sopenharmony_ci break; 74462306a36Sopenharmony_ci } else { 74562306a36Sopenharmony_ci match = match_chain_dso_addresses(cnode->ms.map, cnode->ms.sym->start, 74662306a36Sopenharmony_ci node->ms.map, node->ms.sym->start); 74762306a36Sopenharmony_ci break; 74862306a36Sopenharmony_ci } 74962306a36Sopenharmony_ci } 75062306a36Sopenharmony_ci /* otherwise fall-back to IP-based comparison below */ 75162306a36Sopenharmony_ci fallthrough; 75262306a36Sopenharmony_ci case CCKEY_ADDRESS: 75362306a36Sopenharmony_ci default: 75462306a36Sopenharmony_ci match = match_chain_dso_addresses(cnode->ms.map, cnode->ip, node->ms.map, node->ip); 75562306a36Sopenharmony_ci break; 75662306a36Sopenharmony_ci } 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci if (match == MATCH_EQ && node->branch) { 75962306a36Sopenharmony_ci cnode->branch_count++; 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci if (node->branch_from) { 76262306a36Sopenharmony_ci /* 76362306a36Sopenharmony_ci * It's "to" of a branch 76462306a36Sopenharmony_ci */ 76562306a36Sopenharmony_ci cnode->brtype_stat.branch_to = true; 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci if (node->branch_flags.predicted) 76862306a36Sopenharmony_ci cnode->predicted_count++; 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci if (node->branch_flags.abort) 77162306a36Sopenharmony_ci cnode->abort_count++; 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci branch_type_count(&cnode->brtype_stat, 77462306a36Sopenharmony_ci &node->branch_flags, 77562306a36Sopenharmony_ci node->branch_from, 77662306a36Sopenharmony_ci node->ip); 77762306a36Sopenharmony_ci } else { 77862306a36Sopenharmony_ci /* 77962306a36Sopenharmony_ci * It's "from" of a branch 78062306a36Sopenharmony_ci */ 78162306a36Sopenharmony_ci cnode->brtype_stat.branch_to = false; 78262306a36Sopenharmony_ci cnode->cycles_count += node->branch_flags.cycles; 78362306a36Sopenharmony_ci cnode->iter_count += node->nr_loop_iter; 78462306a36Sopenharmony_ci cnode->iter_cycles += node->iter_cycles; 78562306a36Sopenharmony_ci cnode->from_count++; 78662306a36Sopenharmony_ci } 78762306a36Sopenharmony_ci } 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci return match; 79062306a36Sopenharmony_ci} 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci/* 79362306a36Sopenharmony_ci * Split the parent in two parts (a new child is created) and 79462306a36Sopenharmony_ci * give a part of its callchain to the created child. 79562306a36Sopenharmony_ci * Then create another child to host the given callchain of new branch 79662306a36Sopenharmony_ci */ 79762306a36Sopenharmony_cistatic int 79862306a36Sopenharmony_cisplit_add_child(struct callchain_node *parent, 79962306a36Sopenharmony_ci struct callchain_cursor *cursor, 80062306a36Sopenharmony_ci struct callchain_list *to_split, 80162306a36Sopenharmony_ci u64 idx_parents, u64 idx_local, u64 period) 80262306a36Sopenharmony_ci{ 80362306a36Sopenharmony_ci struct callchain_node *new; 80462306a36Sopenharmony_ci struct list_head *old_tail; 80562306a36Sopenharmony_ci unsigned int idx_total = idx_parents + idx_local; 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci /* split */ 80862306a36Sopenharmony_ci new = create_child(parent, true); 80962306a36Sopenharmony_ci if (new == NULL) 81062306a36Sopenharmony_ci return -1; 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci /* split the callchain and move a part to the new child */ 81362306a36Sopenharmony_ci old_tail = parent->val.prev; 81462306a36Sopenharmony_ci list_del_range(&to_split->list, old_tail); 81562306a36Sopenharmony_ci new->val.next = &to_split->list; 81662306a36Sopenharmony_ci new->val.prev = old_tail; 81762306a36Sopenharmony_ci to_split->list.prev = &new->val; 81862306a36Sopenharmony_ci old_tail->next = &new->val; 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci /* split the hits */ 82162306a36Sopenharmony_ci new->hit = parent->hit; 82262306a36Sopenharmony_ci new->children_hit = parent->children_hit; 82362306a36Sopenharmony_ci parent->children_hit = callchain_cumul_hits(new); 82462306a36Sopenharmony_ci new->val_nr = parent->val_nr - idx_local; 82562306a36Sopenharmony_ci parent->val_nr = idx_local; 82662306a36Sopenharmony_ci new->count = parent->count; 82762306a36Sopenharmony_ci new->children_count = parent->children_count; 82862306a36Sopenharmony_ci parent->children_count = callchain_cumul_counts(new); 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci /* create a new child for the new branch if any */ 83162306a36Sopenharmony_ci if (idx_total < cursor->nr) { 83262306a36Sopenharmony_ci struct callchain_node *first; 83362306a36Sopenharmony_ci struct callchain_list *cnode; 83462306a36Sopenharmony_ci struct callchain_cursor_node *node; 83562306a36Sopenharmony_ci struct rb_node *p, **pp; 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci parent->hit = 0; 83862306a36Sopenharmony_ci parent->children_hit += period; 83962306a36Sopenharmony_ci parent->count = 0; 84062306a36Sopenharmony_ci parent->children_count += 1; 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci node = callchain_cursor_current(cursor); 84362306a36Sopenharmony_ci new = add_child(parent, cursor, period); 84462306a36Sopenharmony_ci if (new == NULL) 84562306a36Sopenharmony_ci return -1; 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci /* 84862306a36Sopenharmony_ci * This is second child since we moved parent's children 84962306a36Sopenharmony_ci * to new (first) child above. 85062306a36Sopenharmony_ci */ 85162306a36Sopenharmony_ci p = parent->rb_root_in.rb_node; 85262306a36Sopenharmony_ci first = rb_entry(p, struct callchain_node, rb_node_in); 85362306a36Sopenharmony_ci cnode = list_first_entry(&first->val, struct callchain_list, 85462306a36Sopenharmony_ci list); 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci if (match_chain(node, cnode) == MATCH_LT) 85762306a36Sopenharmony_ci pp = &p->rb_left; 85862306a36Sopenharmony_ci else 85962306a36Sopenharmony_ci pp = &p->rb_right; 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci rb_link_node(&new->rb_node_in, p, pp); 86262306a36Sopenharmony_ci rb_insert_color(&new->rb_node_in, &parent->rb_root_in); 86362306a36Sopenharmony_ci } else { 86462306a36Sopenharmony_ci parent->hit = period; 86562306a36Sopenharmony_ci parent->count = 1; 86662306a36Sopenharmony_ci } 86762306a36Sopenharmony_ci return 0; 86862306a36Sopenharmony_ci} 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_cistatic enum match_result 87162306a36Sopenharmony_ciappend_chain(struct callchain_node *root, 87262306a36Sopenharmony_ci struct callchain_cursor *cursor, 87362306a36Sopenharmony_ci u64 period); 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_cistatic int 87662306a36Sopenharmony_ciappend_chain_children(struct callchain_node *root, 87762306a36Sopenharmony_ci struct callchain_cursor *cursor, 87862306a36Sopenharmony_ci u64 period) 87962306a36Sopenharmony_ci{ 88062306a36Sopenharmony_ci struct callchain_node *rnode; 88162306a36Sopenharmony_ci struct callchain_cursor_node *node; 88262306a36Sopenharmony_ci struct rb_node **p = &root->rb_root_in.rb_node; 88362306a36Sopenharmony_ci struct rb_node *parent = NULL; 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci node = callchain_cursor_current(cursor); 88662306a36Sopenharmony_ci if (!node) 88762306a36Sopenharmony_ci return -1; 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci /* lookup in children */ 89062306a36Sopenharmony_ci while (*p) { 89162306a36Sopenharmony_ci enum match_result ret; 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci parent = *p; 89462306a36Sopenharmony_ci rnode = rb_entry(parent, struct callchain_node, rb_node_in); 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci /* If at least first entry matches, rely to children */ 89762306a36Sopenharmony_ci ret = append_chain(rnode, cursor, period); 89862306a36Sopenharmony_ci if (ret == MATCH_EQ) 89962306a36Sopenharmony_ci goto inc_children_hit; 90062306a36Sopenharmony_ci if (ret == MATCH_ERROR) 90162306a36Sopenharmony_ci return -1; 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci if (ret == MATCH_LT) 90462306a36Sopenharmony_ci p = &parent->rb_left; 90562306a36Sopenharmony_ci else 90662306a36Sopenharmony_ci p = &parent->rb_right; 90762306a36Sopenharmony_ci } 90862306a36Sopenharmony_ci /* nothing in children, add to the current node */ 90962306a36Sopenharmony_ci rnode = add_child(root, cursor, period); 91062306a36Sopenharmony_ci if (rnode == NULL) 91162306a36Sopenharmony_ci return -1; 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci rb_link_node(&rnode->rb_node_in, parent, p); 91462306a36Sopenharmony_ci rb_insert_color(&rnode->rb_node_in, &root->rb_root_in); 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ciinc_children_hit: 91762306a36Sopenharmony_ci root->children_hit += period; 91862306a36Sopenharmony_ci root->children_count++; 91962306a36Sopenharmony_ci return 0; 92062306a36Sopenharmony_ci} 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_cistatic enum match_result 92362306a36Sopenharmony_ciappend_chain(struct callchain_node *root, 92462306a36Sopenharmony_ci struct callchain_cursor *cursor, 92562306a36Sopenharmony_ci u64 period) 92662306a36Sopenharmony_ci{ 92762306a36Sopenharmony_ci struct callchain_list *cnode; 92862306a36Sopenharmony_ci u64 start = cursor->pos; 92962306a36Sopenharmony_ci bool found = false; 93062306a36Sopenharmony_ci u64 matches; 93162306a36Sopenharmony_ci enum match_result cmp = MATCH_ERROR; 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci /* 93462306a36Sopenharmony_ci * Lookup in the current node 93562306a36Sopenharmony_ci * If we have a symbol, then compare the start to match 93662306a36Sopenharmony_ci * anywhere inside a function, unless function 93762306a36Sopenharmony_ci * mode is disabled. 93862306a36Sopenharmony_ci */ 93962306a36Sopenharmony_ci list_for_each_entry(cnode, &root->val, list) { 94062306a36Sopenharmony_ci struct callchain_cursor_node *node; 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci node = callchain_cursor_current(cursor); 94362306a36Sopenharmony_ci if (!node) 94462306a36Sopenharmony_ci break; 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci cmp = match_chain(node, cnode); 94762306a36Sopenharmony_ci if (cmp != MATCH_EQ) 94862306a36Sopenharmony_ci break; 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci found = true; 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci callchain_cursor_advance(cursor); 95362306a36Sopenharmony_ci } 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci /* matches not, relay no the parent */ 95662306a36Sopenharmony_ci if (!found) { 95762306a36Sopenharmony_ci WARN_ONCE(cmp == MATCH_ERROR, "Chain comparison error\n"); 95862306a36Sopenharmony_ci return cmp; 95962306a36Sopenharmony_ci } 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci matches = cursor->pos - start; 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci /* we match only a part of the node. Split it and add the new chain */ 96462306a36Sopenharmony_ci if (matches < root->val_nr) { 96562306a36Sopenharmony_ci if (split_add_child(root, cursor, cnode, start, matches, 96662306a36Sopenharmony_ci period) < 0) 96762306a36Sopenharmony_ci return MATCH_ERROR; 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci return MATCH_EQ; 97062306a36Sopenharmony_ci } 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci /* we match 100% of the path, increment the hit */ 97362306a36Sopenharmony_ci if (matches == root->val_nr && cursor->pos == cursor->nr) { 97462306a36Sopenharmony_ci root->hit += period; 97562306a36Sopenharmony_ci root->count++; 97662306a36Sopenharmony_ci return MATCH_EQ; 97762306a36Sopenharmony_ci } 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci /* We match the node and still have a part remaining */ 98062306a36Sopenharmony_ci if (append_chain_children(root, cursor, period) < 0) 98162306a36Sopenharmony_ci return MATCH_ERROR; 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci return MATCH_EQ; 98462306a36Sopenharmony_ci} 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ciint callchain_append(struct callchain_root *root, 98762306a36Sopenharmony_ci struct callchain_cursor *cursor, 98862306a36Sopenharmony_ci u64 period) 98962306a36Sopenharmony_ci{ 99062306a36Sopenharmony_ci if (cursor == NULL) 99162306a36Sopenharmony_ci return -1; 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci if (!cursor->nr) 99462306a36Sopenharmony_ci return 0; 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_ci callchain_cursor_commit(cursor); 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci if (append_chain_children(&root->node, cursor, period) < 0) 99962306a36Sopenharmony_ci return -1; 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci if (cursor->nr > root->max_depth) 100262306a36Sopenharmony_ci root->max_depth = cursor->nr; 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci return 0; 100562306a36Sopenharmony_ci} 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_cistatic int 100862306a36Sopenharmony_cimerge_chain_branch(struct callchain_cursor *cursor, 100962306a36Sopenharmony_ci struct callchain_node *dst, struct callchain_node *src) 101062306a36Sopenharmony_ci{ 101162306a36Sopenharmony_ci struct callchain_cursor_node **old_last = cursor->last; 101262306a36Sopenharmony_ci struct callchain_node *child; 101362306a36Sopenharmony_ci struct callchain_list *list, *next_list; 101462306a36Sopenharmony_ci struct rb_node *n; 101562306a36Sopenharmony_ci int old_pos = cursor->nr; 101662306a36Sopenharmony_ci int err = 0; 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci list_for_each_entry_safe(list, next_list, &src->val, list) { 101962306a36Sopenharmony_ci struct map_symbol ms = { 102062306a36Sopenharmony_ci .maps = maps__get(list->ms.maps), 102162306a36Sopenharmony_ci .map = map__get(list->ms.map), 102262306a36Sopenharmony_ci }; 102362306a36Sopenharmony_ci callchain_cursor_append(cursor, list->ip, &ms, false, NULL, 0, 0, 0, list->srcline); 102462306a36Sopenharmony_ci list_del_init(&list->list); 102562306a36Sopenharmony_ci map__zput(ms.map); 102662306a36Sopenharmony_ci maps__zput(ms.maps); 102762306a36Sopenharmony_ci map__zput(list->ms.map); 102862306a36Sopenharmony_ci maps__zput(list->ms.maps); 102962306a36Sopenharmony_ci free(list); 103062306a36Sopenharmony_ci } 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_ci if (src->hit) { 103362306a36Sopenharmony_ci callchain_cursor_commit(cursor); 103462306a36Sopenharmony_ci if (append_chain_children(dst, cursor, src->hit) < 0) 103562306a36Sopenharmony_ci return -1; 103662306a36Sopenharmony_ci } 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci n = rb_first(&src->rb_root_in); 103962306a36Sopenharmony_ci while (n) { 104062306a36Sopenharmony_ci child = container_of(n, struct callchain_node, rb_node_in); 104162306a36Sopenharmony_ci n = rb_next(n); 104262306a36Sopenharmony_ci rb_erase(&child->rb_node_in, &src->rb_root_in); 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_ci err = merge_chain_branch(cursor, dst, child); 104562306a36Sopenharmony_ci if (err) 104662306a36Sopenharmony_ci break; 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ci free(child); 104962306a36Sopenharmony_ci } 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci cursor->nr = old_pos; 105262306a36Sopenharmony_ci cursor->last = old_last; 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci return err; 105562306a36Sopenharmony_ci} 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ciint callchain_merge(struct callchain_cursor *cursor, 105862306a36Sopenharmony_ci struct callchain_root *dst, struct callchain_root *src) 105962306a36Sopenharmony_ci{ 106062306a36Sopenharmony_ci return merge_chain_branch(cursor, &dst->node, &src->node); 106162306a36Sopenharmony_ci} 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ciint callchain_cursor_append(struct callchain_cursor *cursor, 106462306a36Sopenharmony_ci u64 ip, struct map_symbol *ms, 106562306a36Sopenharmony_ci bool branch, struct branch_flags *flags, 106662306a36Sopenharmony_ci int nr_loop_iter, u64 iter_cycles, u64 branch_from, 106762306a36Sopenharmony_ci const char *srcline) 106862306a36Sopenharmony_ci{ 106962306a36Sopenharmony_ci struct callchain_cursor_node *node = *cursor->last; 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci if (!node) { 107262306a36Sopenharmony_ci node = calloc(1, sizeof(*node)); 107362306a36Sopenharmony_ci if (!node) 107462306a36Sopenharmony_ci return -ENOMEM; 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_ci *cursor->last = node; 107762306a36Sopenharmony_ci } 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ci node->ip = ip; 108062306a36Sopenharmony_ci maps__zput(node->ms.maps); 108162306a36Sopenharmony_ci map__zput(node->ms.map); 108262306a36Sopenharmony_ci node->ms = *ms; 108362306a36Sopenharmony_ci node->ms.maps = maps__get(ms->maps); 108462306a36Sopenharmony_ci node->ms.map = map__get(ms->map); 108562306a36Sopenharmony_ci node->branch = branch; 108662306a36Sopenharmony_ci node->nr_loop_iter = nr_loop_iter; 108762306a36Sopenharmony_ci node->iter_cycles = iter_cycles; 108862306a36Sopenharmony_ci node->srcline = srcline; 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci if (flags) 109162306a36Sopenharmony_ci memcpy(&node->branch_flags, flags, 109262306a36Sopenharmony_ci sizeof(struct branch_flags)); 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_ci node->branch_from = branch_from; 109562306a36Sopenharmony_ci cursor->nr++; 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci cursor->last = &node->next; 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_ci return 0; 110062306a36Sopenharmony_ci} 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_ciint sample__resolve_callchain(struct perf_sample *sample, 110362306a36Sopenharmony_ci struct callchain_cursor *cursor, struct symbol **parent, 110462306a36Sopenharmony_ci struct evsel *evsel, struct addr_location *al, 110562306a36Sopenharmony_ci int max_stack) 110662306a36Sopenharmony_ci{ 110762306a36Sopenharmony_ci if (sample->callchain == NULL && !symbol_conf.show_branchflag_count) 110862306a36Sopenharmony_ci return 0; 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci if (symbol_conf.use_callchain || symbol_conf.cumulate_callchain || 111162306a36Sopenharmony_ci perf_hpp_list.parent || symbol_conf.show_branchflag_count) { 111262306a36Sopenharmony_ci return thread__resolve_callchain(al->thread, cursor, evsel, sample, 111362306a36Sopenharmony_ci parent, al, max_stack); 111462306a36Sopenharmony_ci } 111562306a36Sopenharmony_ci return 0; 111662306a36Sopenharmony_ci} 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_ciint hist_entry__append_callchain(struct hist_entry *he, struct perf_sample *sample) 111962306a36Sopenharmony_ci{ 112062306a36Sopenharmony_ci if ((!symbol_conf.use_callchain || sample->callchain == NULL) && 112162306a36Sopenharmony_ci !symbol_conf.show_branchflag_count) 112262306a36Sopenharmony_ci return 0; 112362306a36Sopenharmony_ci return callchain_append(he->callchain, get_tls_callchain_cursor(), sample->period); 112462306a36Sopenharmony_ci} 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ciint fill_callchain_info(struct addr_location *al, struct callchain_cursor_node *node, 112762306a36Sopenharmony_ci bool hide_unresolved) 112862306a36Sopenharmony_ci{ 112962306a36Sopenharmony_ci struct machine *machine = maps__machine(node->ms.maps); 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci maps__put(al->maps); 113262306a36Sopenharmony_ci al->maps = maps__get(node->ms.maps); 113362306a36Sopenharmony_ci map__put(al->map); 113462306a36Sopenharmony_ci al->map = map__get(node->ms.map); 113562306a36Sopenharmony_ci al->sym = node->ms.sym; 113662306a36Sopenharmony_ci al->srcline = node->srcline; 113762306a36Sopenharmony_ci al->addr = node->ip; 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_ci if (al->sym == NULL) { 114062306a36Sopenharmony_ci if (hide_unresolved) 114162306a36Sopenharmony_ci return 0; 114262306a36Sopenharmony_ci if (al->map == NULL) 114362306a36Sopenharmony_ci goto out; 114462306a36Sopenharmony_ci } 114562306a36Sopenharmony_ci if (RC_CHK_ACCESS(al->maps) == RC_CHK_ACCESS(machine__kernel_maps(machine))) { 114662306a36Sopenharmony_ci if (machine__is_host(machine)) { 114762306a36Sopenharmony_ci al->cpumode = PERF_RECORD_MISC_KERNEL; 114862306a36Sopenharmony_ci al->level = 'k'; 114962306a36Sopenharmony_ci } else { 115062306a36Sopenharmony_ci al->cpumode = PERF_RECORD_MISC_GUEST_KERNEL; 115162306a36Sopenharmony_ci al->level = 'g'; 115262306a36Sopenharmony_ci } 115362306a36Sopenharmony_ci } else { 115462306a36Sopenharmony_ci if (machine__is_host(machine)) { 115562306a36Sopenharmony_ci al->cpumode = PERF_RECORD_MISC_USER; 115662306a36Sopenharmony_ci al->level = '.'; 115762306a36Sopenharmony_ci } else if (perf_guest) { 115862306a36Sopenharmony_ci al->cpumode = PERF_RECORD_MISC_GUEST_USER; 115962306a36Sopenharmony_ci al->level = 'u'; 116062306a36Sopenharmony_ci } else { 116162306a36Sopenharmony_ci al->cpumode = PERF_RECORD_MISC_HYPERVISOR; 116262306a36Sopenharmony_ci al->level = 'H'; 116362306a36Sopenharmony_ci } 116462306a36Sopenharmony_ci } 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ciout: 116762306a36Sopenharmony_ci return 1; 116862306a36Sopenharmony_ci} 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_cichar *callchain_list__sym_name(struct callchain_list *cl, 117162306a36Sopenharmony_ci char *bf, size_t bfsize, bool show_dso) 117262306a36Sopenharmony_ci{ 117362306a36Sopenharmony_ci bool show_addr = callchain_param.key == CCKEY_ADDRESS; 117462306a36Sopenharmony_ci bool show_srcline = show_addr || callchain_param.key == CCKEY_SRCLINE; 117562306a36Sopenharmony_ci int printed; 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci if (cl->ms.sym) { 117862306a36Sopenharmony_ci const char *inlined = cl->ms.sym->inlined ? " (inlined)" : ""; 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_ci if (show_srcline && cl->srcline) 118162306a36Sopenharmony_ci printed = scnprintf(bf, bfsize, "%s %s%s", 118262306a36Sopenharmony_ci cl->ms.sym->name, cl->srcline, 118362306a36Sopenharmony_ci inlined); 118462306a36Sopenharmony_ci else 118562306a36Sopenharmony_ci printed = scnprintf(bf, bfsize, "%s%s", 118662306a36Sopenharmony_ci cl->ms.sym->name, inlined); 118762306a36Sopenharmony_ci } else 118862306a36Sopenharmony_ci printed = scnprintf(bf, bfsize, "%#" PRIx64, cl->ip); 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci if (show_dso) 119162306a36Sopenharmony_ci scnprintf(bf + printed, bfsize - printed, " %s", 119262306a36Sopenharmony_ci cl->ms.map ? 119362306a36Sopenharmony_ci map__dso(cl->ms.map)->short_name : 119462306a36Sopenharmony_ci "unknown"); 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ci return bf; 119762306a36Sopenharmony_ci} 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_cichar *callchain_node__scnprintf_value(struct callchain_node *node, 120062306a36Sopenharmony_ci char *bf, size_t bfsize, u64 total) 120162306a36Sopenharmony_ci{ 120262306a36Sopenharmony_ci double percent = 0.0; 120362306a36Sopenharmony_ci u64 period = callchain_cumul_hits(node); 120462306a36Sopenharmony_ci unsigned count = callchain_cumul_counts(node); 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_ci if (callchain_param.mode == CHAIN_FOLDED) { 120762306a36Sopenharmony_ci period = node->hit; 120862306a36Sopenharmony_ci count = node->count; 120962306a36Sopenharmony_ci } 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci switch (callchain_param.value) { 121262306a36Sopenharmony_ci case CCVAL_PERIOD: 121362306a36Sopenharmony_ci scnprintf(bf, bfsize, "%"PRIu64, period); 121462306a36Sopenharmony_ci break; 121562306a36Sopenharmony_ci case CCVAL_COUNT: 121662306a36Sopenharmony_ci scnprintf(bf, bfsize, "%u", count); 121762306a36Sopenharmony_ci break; 121862306a36Sopenharmony_ci case CCVAL_PERCENT: 121962306a36Sopenharmony_ci default: 122062306a36Sopenharmony_ci if (total) 122162306a36Sopenharmony_ci percent = period * 100.0 / total; 122262306a36Sopenharmony_ci scnprintf(bf, bfsize, "%.2f%%", percent); 122362306a36Sopenharmony_ci break; 122462306a36Sopenharmony_ci } 122562306a36Sopenharmony_ci return bf; 122662306a36Sopenharmony_ci} 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ciint callchain_node__fprintf_value(struct callchain_node *node, 122962306a36Sopenharmony_ci FILE *fp, u64 total) 123062306a36Sopenharmony_ci{ 123162306a36Sopenharmony_ci double percent = 0.0; 123262306a36Sopenharmony_ci u64 period = callchain_cumul_hits(node); 123362306a36Sopenharmony_ci unsigned count = callchain_cumul_counts(node); 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_ci if (callchain_param.mode == CHAIN_FOLDED) { 123662306a36Sopenharmony_ci period = node->hit; 123762306a36Sopenharmony_ci count = node->count; 123862306a36Sopenharmony_ci } 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_ci switch (callchain_param.value) { 124162306a36Sopenharmony_ci case CCVAL_PERIOD: 124262306a36Sopenharmony_ci return fprintf(fp, "%"PRIu64, period); 124362306a36Sopenharmony_ci case CCVAL_COUNT: 124462306a36Sopenharmony_ci return fprintf(fp, "%u", count); 124562306a36Sopenharmony_ci case CCVAL_PERCENT: 124662306a36Sopenharmony_ci default: 124762306a36Sopenharmony_ci if (total) 124862306a36Sopenharmony_ci percent = period * 100.0 / total; 124962306a36Sopenharmony_ci return percent_color_fprintf(fp, "%.2f%%", percent); 125062306a36Sopenharmony_ci } 125162306a36Sopenharmony_ci return 0; 125262306a36Sopenharmony_ci} 125362306a36Sopenharmony_ci 125462306a36Sopenharmony_cistatic void callchain_counts_value(struct callchain_node *node, 125562306a36Sopenharmony_ci u64 *branch_count, u64 *predicted_count, 125662306a36Sopenharmony_ci u64 *abort_count, u64 *cycles_count) 125762306a36Sopenharmony_ci{ 125862306a36Sopenharmony_ci struct callchain_list *clist; 125962306a36Sopenharmony_ci 126062306a36Sopenharmony_ci list_for_each_entry(clist, &node->val, list) { 126162306a36Sopenharmony_ci if (branch_count) 126262306a36Sopenharmony_ci *branch_count += clist->branch_count; 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_ci if (predicted_count) 126562306a36Sopenharmony_ci *predicted_count += clist->predicted_count; 126662306a36Sopenharmony_ci 126762306a36Sopenharmony_ci if (abort_count) 126862306a36Sopenharmony_ci *abort_count += clist->abort_count; 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_ci if (cycles_count) 127162306a36Sopenharmony_ci *cycles_count += clist->cycles_count; 127262306a36Sopenharmony_ci } 127362306a36Sopenharmony_ci} 127462306a36Sopenharmony_ci 127562306a36Sopenharmony_cistatic int callchain_node_branch_counts_cumul(struct callchain_node *node, 127662306a36Sopenharmony_ci u64 *branch_count, 127762306a36Sopenharmony_ci u64 *predicted_count, 127862306a36Sopenharmony_ci u64 *abort_count, 127962306a36Sopenharmony_ci u64 *cycles_count) 128062306a36Sopenharmony_ci{ 128162306a36Sopenharmony_ci struct callchain_node *child; 128262306a36Sopenharmony_ci struct rb_node *n; 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_ci n = rb_first(&node->rb_root_in); 128562306a36Sopenharmony_ci while (n) { 128662306a36Sopenharmony_ci child = rb_entry(n, struct callchain_node, rb_node_in); 128762306a36Sopenharmony_ci n = rb_next(n); 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_ci callchain_node_branch_counts_cumul(child, branch_count, 129062306a36Sopenharmony_ci predicted_count, 129162306a36Sopenharmony_ci abort_count, 129262306a36Sopenharmony_ci cycles_count); 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_ci callchain_counts_value(child, branch_count, 129562306a36Sopenharmony_ci predicted_count, abort_count, 129662306a36Sopenharmony_ci cycles_count); 129762306a36Sopenharmony_ci } 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_ci return 0; 130062306a36Sopenharmony_ci} 130162306a36Sopenharmony_ci 130262306a36Sopenharmony_ciint callchain_branch_counts(struct callchain_root *root, 130362306a36Sopenharmony_ci u64 *branch_count, u64 *predicted_count, 130462306a36Sopenharmony_ci u64 *abort_count, u64 *cycles_count) 130562306a36Sopenharmony_ci{ 130662306a36Sopenharmony_ci if (branch_count) 130762306a36Sopenharmony_ci *branch_count = 0; 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_ci if (predicted_count) 131062306a36Sopenharmony_ci *predicted_count = 0; 131162306a36Sopenharmony_ci 131262306a36Sopenharmony_ci if (abort_count) 131362306a36Sopenharmony_ci *abort_count = 0; 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_ci if (cycles_count) 131662306a36Sopenharmony_ci *cycles_count = 0; 131762306a36Sopenharmony_ci 131862306a36Sopenharmony_ci return callchain_node_branch_counts_cumul(&root->node, 131962306a36Sopenharmony_ci branch_count, 132062306a36Sopenharmony_ci predicted_count, 132162306a36Sopenharmony_ci abort_count, 132262306a36Sopenharmony_ci cycles_count); 132362306a36Sopenharmony_ci} 132462306a36Sopenharmony_ci 132562306a36Sopenharmony_cistatic int count_pri64_printf(int idx, const char *str, u64 value, char *bf, int bfsize) 132662306a36Sopenharmony_ci{ 132762306a36Sopenharmony_ci return scnprintf(bf, bfsize, "%s%s:%" PRId64 "", (idx) ? " " : " (", str, value); 132862306a36Sopenharmony_ci} 132962306a36Sopenharmony_ci 133062306a36Sopenharmony_cistatic int count_float_printf(int idx, const char *str, float value, 133162306a36Sopenharmony_ci char *bf, int bfsize, float threshold) 133262306a36Sopenharmony_ci{ 133362306a36Sopenharmony_ci if (threshold != 0.0 && value < threshold) 133462306a36Sopenharmony_ci return 0; 133562306a36Sopenharmony_ci 133662306a36Sopenharmony_ci return scnprintf(bf, bfsize, "%s%s:%.1f%%", (idx) ? " " : " (", str, value); 133762306a36Sopenharmony_ci} 133862306a36Sopenharmony_ci 133962306a36Sopenharmony_cistatic int branch_to_str(char *bf, int bfsize, 134062306a36Sopenharmony_ci u64 branch_count, u64 predicted_count, 134162306a36Sopenharmony_ci u64 abort_count, 134262306a36Sopenharmony_ci struct branch_type_stat *brtype_stat) 134362306a36Sopenharmony_ci{ 134462306a36Sopenharmony_ci int printed, i = 0; 134562306a36Sopenharmony_ci 134662306a36Sopenharmony_ci printed = branch_type_str(brtype_stat, bf, bfsize); 134762306a36Sopenharmony_ci if (printed) 134862306a36Sopenharmony_ci i++; 134962306a36Sopenharmony_ci 135062306a36Sopenharmony_ci if (predicted_count < branch_count) { 135162306a36Sopenharmony_ci printed += count_float_printf(i++, "predicted", 135262306a36Sopenharmony_ci predicted_count * 100.0 / branch_count, 135362306a36Sopenharmony_ci bf + printed, bfsize - printed, 0.0); 135462306a36Sopenharmony_ci } 135562306a36Sopenharmony_ci 135662306a36Sopenharmony_ci if (abort_count) { 135762306a36Sopenharmony_ci printed += count_float_printf(i++, "abort", 135862306a36Sopenharmony_ci abort_count * 100.0 / branch_count, 135962306a36Sopenharmony_ci bf + printed, bfsize - printed, 0.1); 136062306a36Sopenharmony_ci } 136162306a36Sopenharmony_ci 136262306a36Sopenharmony_ci if (i) 136362306a36Sopenharmony_ci printed += scnprintf(bf + printed, bfsize - printed, ")"); 136462306a36Sopenharmony_ci 136562306a36Sopenharmony_ci return printed; 136662306a36Sopenharmony_ci} 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_cistatic int branch_from_str(char *bf, int bfsize, 136962306a36Sopenharmony_ci u64 branch_count, 137062306a36Sopenharmony_ci u64 cycles_count, u64 iter_count, 137162306a36Sopenharmony_ci u64 iter_cycles, u64 from_count) 137262306a36Sopenharmony_ci{ 137362306a36Sopenharmony_ci int printed = 0, i = 0; 137462306a36Sopenharmony_ci u64 cycles, v = 0; 137562306a36Sopenharmony_ci 137662306a36Sopenharmony_ci cycles = cycles_count / branch_count; 137762306a36Sopenharmony_ci if (cycles) { 137862306a36Sopenharmony_ci printed += count_pri64_printf(i++, "cycles", 137962306a36Sopenharmony_ci cycles, 138062306a36Sopenharmony_ci bf + printed, bfsize - printed); 138162306a36Sopenharmony_ci } 138262306a36Sopenharmony_ci 138362306a36Sopenharmony_ci if (iter_count && from_count) { 138462306a36Sopenharmony_ci v = iter_count / from_count; 138562306a36Sopenharmony_ci if (v) { 138662306a36Sopenharmony_ci printed += count_pri64_printf(i++, "iter", 138762306a36Sopenharmony_ci v, bf + printed, bfsize - printed); 138862306a36Sopenharmony_ci 138962306a36Sopenharmony_ci printed += count_pri64_printf(i++, "avg_cycles", 139062306a36Sopenharmony_ci iter_cycles / iter_count, 139162306a36Sopenharmony_ci bf + printed, bfsize - printed); 139262306a36Sopenharmony_ci } 139362306a36Sopenharmony_ci } 139462306a36Sopenharmony_ci 139562306a36Sopenharmony_ci if (i) 139662306a36Sopenharmony_ci printed += scnprintf(bf + printed, bfsize - printed, ")"); 139762306a36Sopenharmony_ci 139862306a36Sopenharmony_ci return printed; 139962306a36Sopenharmony_ci} 140062306a36Sopenharmony_ci 140162306a36Sopenharmony_cistatic int counts_str_build(char *bf, int bfsize, 140262306a36Sopenharmony_ci u64 branch_count, u64 predicted_count, 140362306a36Sopenharmony_ci u64 abort_count, u64 cycles_count, 140462306a36Sopenharmony_ci u64 iter_count, u64 iter_cycles, 140562306a36Sopenharmony_ci u64 from_count, 140662306a36Sopenharmony_ci struct branch_type_stat *brtype_stat) 140762306a36Sopenharmony_ci{ 140862306a36Sopenharmony_ci int printed; 140962306a36Sopenharmony_ci 141062306a36Sopenharmony_ci if (branch_count == 0) 141162306a36Sopenharmony_ci return scnprintf(bf, bfsize, " (calltrace)"); 141262306a36Sopenharmony_ci 141362306a36Sopenharmony_ci if (brtype_stat->branch_to) { 141462306a36Sopenharmony_ci printed = branch_to_str(bf, bfsize, branch_count, 141562306a36Sopenharmony_ci predicted_count, abort_count, brtype_stat); 141662306a36Sopenharmony_ci } else { 141762306a36Sopenharmony_ci printed = branch_from_str(bf, bfsize, branch_count, 141862306a36Sopenharmony_ci cycles_count, iter_count, iter_cycles, 141962306a36Sopenharmony_ci from_count); 142062306a36Sopenharmony_ci } 142162306a36Sopenharmony_ci 142262306a36Sopenharmony_ci if (!printed) 142362306a36Sopenharmony_ci bf[0] = 0; 142462306a36Sopenharmony_ci 142562306a36Sopenharmony_ci return printed; 142662306a36Sopenharmony_ci} 142762306a36Sopenharmony_ci 142862306a36Sopenharmony_cistatic int callchain_counts_printf(FILE *fp, char *bf, int bfsize, 142962306a36Sopenharmony_ci u64 branch_count, u64 predicted_count, 143062306a36Sopenharmony_ci u64 abort_count, u64 cycles_count, 143162306a36Sopenharmony_ci u64 iter_count, u64 iter_cycles, 143262306a36Sopenharmony_ci u64 from_count, 143362306a36Sopenharmony_ci struct branch_type_stat *brtype_stat) 143462306a36Sopenharmony_ci{ 143562306a36Sopenharmony_ci char str[256]; 143662306a36Sopenharmony_ci 143762306a36Sopenharmony_ci counts_str_build(str, sizeof(str), branch_count, 143862306a36Sopenharmony_ci predicted_count, abort_count, cycles_count, 143962306a36Sopenharmony_ci iter_count, iter_cycles, from_count, brtype_stat); 144062306a36Sopenharmony_ci 144162306a36Sopenharmony_ci if (fp) 144262306a36Sopenharmony_ci return fprintf(fp, "%s", str); 144362306a36Sopenharmony_ci 144462306a36Sopenharmony_ci return scnprintf(bf, bfsize, "%s", str); 144562306a36Sopenharmony_ci} 144662306a36Sopenharmony_ci 144762306a36Sopenharmony_ciint callchain_list_counts__printf_value(struct callchain_list *clist, 144862306a36Sopenharmony_ci FILE *fp, char *bf, int bfsize) 144962306a36Sopenharmony_ci{ 145062306a36Sopenharmony_ci u64 branch_count, predicted_count; 145162306a36Sopenharmony_ci u64 abort_count, cycles_count; 145262306a36Sopenharmony_ci u64 iter_count, iter_cycles; 145362306a36Sopenharmony_ci u64 from_count; 145462306a36Sopenharmony_ci 145562306a36Sopenharmony_ci branch_count = clist->branch_count; 145662306a36Sopenharmony_ci predicted_count = clist->predicted_count; 145762306a36Sopenharmony_ci abort_count = clist->abort_count; 145862306a36Sopenharmony_ci cycles_count = clist->cycles_count; 145962306a36Sopenharmony_ci iter_count = clist->iter_count; 146062306a36Sopenharmony_ci iter_cycles = clist->iter_cycles; 146162306a36Sopenharmony_ci from_count = clist->from_count; 146262306a36Sopenharmony_ci 146362306a36Sopenharmony_ci return callchain_counts_printf(fp, bf, bfsize, branch_count, 146462306a36Sopenharmony_ci predicted_count, abort_count, 146562306a36Sopenharmony_ci cycles_count, iter_count, iter_cycles, 146662306a36Sopenharmony_ci from_count, &clist->brtype_stat); 146762306a36Sopenharmony_ci} 146862306a36Sopenharmony_ci 146962306a36Sopenharmony_cistatic void free_callchain_node(struct callchain_node *node) 147062306a36Sopenharmony_ci{ 147162306a36Sopenharmony_ci struct callchain_list *list, *tmp; 147262306a36Sopenharmony_ci struct callchain_node *child; 147362306a36Sopenharmony_ci struct rb_node *n; 147462306a36Sopenharmony_ci 147562306a36Sopenharmony_ci list_for_each_entry_safe(list, tmp, &node->parent_val, list) { 147662306a36Sopenharmony_ci list_del_init(&list->list); 147762306a36Sopenharmony_ci map__zput(list->ms.map); 147862306a36Sopenharmony_ci maps__zput(list->ms.maps); 147962306a36Sopenharmony_ci free(list); 148062306a36Sopenharmony_ci } 148162306a36Sopenharmony_ci 148262306a36Sopenharmony_ci list_for_each_entry_safe(list, tmp, &node->val, list) { 148362306a36Sopenharmony_ci list_del_init(&list->list); 148462306a36Sopenharmony_ci map__zput(list->ms.map); 148562306a36Sopenharmony_ci maps__zput(list->ms.maps); 148662306a36Sopenharmony_ci free(list); 148762306a36Sopenharmony_ci } 148862306a36Sopenharmony_ci 148962306a36Sopenharmony_ci n = rb_first(&node->rb_root_in); 149062306a36Sopenharmony_ci while (n) { 149162306a36Sopenharmony_ci child = container_of(n, struct callchain_node, rb_node_in); 149262306a36Sopenharmony_ci n = rb_next(n); 149362306a36Sopenharmony_ci rb_erase(&child->rb_node_in, &node->rb_root_in); 149462306a36Sopenharmony_ci 149562306a36Sopenharmony_ci free_callchain_node(child); 149662306a36Sopenharmony_ci free(child); 149762306a36Sopenharmony_ci } 149862306a36Sopenharmony_ci} 149962306a36Sopenharmony_ci 150062306a36Sopenharmony_civoid free_callchain(struct callchain_root *root) 150162306a36Sopenharmony_ci{ 150262306a36Sopenharmony_ci if (!symbol_conf.use_callchain) 150362306a36Sopenharmony_ci return; 150462306a36Sopenharmony_ci 150562306a36Sopenharmony_ci free_callchain_node(&root->node); 150662306a36Sopenharmony_ci} 150762306a36Sopenharmony_ci 150862306a36Sopenharmony_cistatic u64 decay_callchain_node(struct callchain_node *node) 150962306a36Sopenharmony_ci{ 151062306a36Sopenharmony_ci struct callchain_node *child; 151162306a36Sopenharmony_ci struct rb_node *n; 151262306a36Sopenharmony_ci u64 child_hits = 0; 151362306a36Sopenharmony_ci 151462306a36Sopenharmony_ci n = rb_first(&node->rb_root_in); 151562306a36Sopenharmony_ci while (n) { 151662306a36Sopenharmony_ci child = container_of(n, struct callchain_node, rb_node_in); 151762306a36Sopenharmony_ci 151862306a36Sopenharmony_ci child_hits += decay_callchain_node(child); 151962306a36Sopenharmony_ci n = rb_next(n); 152062306a36Sopenharmony_ci } 152162306a36Sopenharmony_ci 152262306a36Sopenharmony_ci node->hit = (node->hit * 7) / 8; 152362306a36Sopenharmony_ci node->children_hit = child_hits; 152462306a36Sopenharmony_ci 152562306a36Sopenharmony_ci return node->hit; 152662306a36Sopenharmony_ci} 152762306a36Sopenharmony_ci 152862306a36Sopenharmony_civoid decay_callchain(struct callchain_root *root) 152962306a36Sopenharmony_ci{ 153062306a36Sopenharmony_ci if (!symbol_conf.use_callchain) 153162306a36Sopenharmony_ci return; 153262306a36Sopenharmony_ci 153362306a36Sopenharmony_ci decay_callchain_node(&root->node); 153462306a36Sopenharmony_ci} 153562306a36Sopenharmony_ci 153662306a36Sopenharmony_ciint callchain_node__make_parent_list(struct callchain_node *node) 153762306a36Sopenharmony_ci{ 153862306a36Sopenharmony_ci struct callchain_node *parent = node->parent; 153962306a36Sopenharmony_ci struct callchain_list *chain, *new; 154062306a36Sopenharmony_ci LIST_HEAD(head); 154162306a36Sopenharmony_ci 154262306a36Sopenharmony_ci while (parent) { 154362306a36Sopenharmony_ci list_for_each_entry_reverse(chain, &parent->val, list) { 154462306a36Sopenharmony_ci new = malloc(sizeof(*new)); 154562306a36Sopenharmony_ci if (new == NULL) 154662306a36Sopenharmony_ci goto out; 154762306a36Sopenharmony_ci *new = *chain; 154862306a36Sopenharmony_ci new->has_children = false; 154962306a36Sopenharmony_ci new->ms.map = map__get(new->ms.map); 155062306a36Sopenharmony_ci list_add_tail(&new->list, &head); 155162306a36Sopenharmony_ci } 155262306a36Sopenharmony_ci parent = parent->parent; 155362306a36Sopenharmony_ci } 155462306a36Sopenharmony_ci 155562306a36Sopenharmony_ci list_for_each_entry_safe_reverse(chain, new, &head, list) 155662306a36Sopenharmony_ci list_move_tail(&chain->list, &node->parent_val); 155762306a36Sopenharmony_ci 155862306a36Sopenharmony_ci if (!list_empty(&node->parent_val)) { 155962306a36Sopenharmony_ci chain = list_first_entry(&node->parent_val, struct callchain_list, list); 156062306a36Sopenharmony_ci chain->has_children = rb_prev(&node->rb_node) || rb_next(&node->rb_node); 156162306a36Sopenharmony_ci 156262306a36Sopenharmony_ci chain = list_first_entry(&node->val, struct callchain_list, list); 156362306a36Sopenharmony_ci chain->has_children = false; 156462306a36Sopenharmony_ci } 156562306a36Sopenharmony_ci return 0; 156662306a36Sopenharmony_ci 156762306a36Sopenharmony_ciout: 156862306a36Sopenharmony_ci list_for_each_entry_safe(chain, new, &head, list) { 156962306a36Sopenharmony_ci list_del_init(&chain->list); 157062306a36Sopenharmony_ci map__zput(chain->ms.map); 157162306a36Sopenharmony_ci maps__zput(chain->ms.maps); 157262306a36Sopenharmony_ci free(chain); 157362306a36Sopenharmony_ci } 157462306a36Sopenharmony_ci return -ENOMEM; 157562306a36Sopenharmony_ci} 157662306a36Sopenharmony_ci 157762306a36Sopenharmony_cistatic void callchain_cursor__delete(void *vcursor) 157862306a36Sopenharmony_ci{ 157962306a36Sopenharmony_ci struct callchain_cursor *cursor = vcursor; 158062306a36Sopenharmony_ci struct callchain_cursor_node *node, *next; 158162306a36Sopenharmony_ci 158262306a36Sopenharmony_ci callchain_cursor_reset(cursor); 158362306a36Sopenharmony_ci for (node = cursor->first; node != NULL; node = next) { 158462306a36Sopenharmony_ci next = node->next; 158562306a36Sopenharmony_ci free(node); 158662306a36Sopenharmony_ci } 158762306a36Sopenharmony_ci free(cursor); 158862306a36Sopenharmony_ci} 158962306a36Sopenharmony_ci 159062306a36Sopenharmony_cistatic void init_callchain_cursor_key(void) 159162306a36Sopenharmony_ci{ 159262306a36Sopenharmony_ci if (pthread_key_create(&callchain_cursor, callchain_cursor__delete)) { 159362306a36Sopenharmony_ci pr_err("callchain cursor creation failed"); 159462306a36Sopenharmony_ci abort(); 159562306a36Sopenharmony_ci } 159662306a36Sopenharmony_ci} 159762306a36Sopenharmony_ci 159862306a36Sopenharmony_cistruct callchain_cursor *get_tls_callchain_cursor(void) 159962306a36Sopenharmony_ci{ 160062306a36Sopenharmony_ci static pthread_once_t once_control = PTHREAD_ONCE_INIT; 160162306a36Sopenharmony_ci struct callchain_cursor *cursor; 160262306a36Sopenharmony_ci 160362306a36Sopenharmony_ci pthread_once(&once_control, init_callchain_cursor_key); 160462306a36Sopenharmony_ci cursor = pthread_getspecific(callchain_cursor); 160562306a36Sopenharmony_ci if (!cursor) { 160662306a36Sopenharmony_ci cursor = zalloc(sizeof(*cursor)); 160762306a36Sopenharmony_ci if (!cursor) 160862306a36Sopenharmony_ci pr_debug3("%s: not enough memory\n", __func__); 160962306a36Sopenharmony_ci pthread_setspecific(callchain_cursor, cursor); 161062306a36Sopenharmony_ci } 161162306a36Sopenharmony_ci return cursor; 161262306a36Sopenharmony_ci} 161362306a36Sopenharmony_ci 161462306a36Sopenharmony_ciint callchain_cursor__copy(struct callchain_cursor *dst, 161562306a36Sopenharmony_ci struct callchain_cursor *src) 161662306a36Sopenharmony_ci{ 161762306a36Sopenharmony_ci int rc = 0; 161862306a36Sopenharmony_ci 161962306a36Sopenharmony_ci callchain_cursor_reset(dst); 162062306a36Sopenharmony_ci callchain_cursor_commit(src); 162162306a36Sopenharmony_ci 162262306a36Sopenharmony_ci while (true) { 162362306a36Sopenharmony_ci struct callchain_cursor_node *node; 162462306a36Sopenharmony_ci 162562306a36Sopenharmony_ci node = callchain_cursor_current(src); 162662306a36Sopenharmony_ci if (node == NULL) 162762306a36Sopenharmony_ci break; 162862306a36Sopenharmony_ci 162962306a36Sopenharmony_ci rc = callchain_cursor_append(dst, node->ip, &node->ms, 163062306a36Sopenharmony_ci node->branch, &node->branch_flags, 163162306a36Sopenharmony_ci node->nr_loop_iter, 163262306a36Sopenharmony_ci node->iter_cycles, 163362306a36Sopenharmony_ci node->branch_from, node->srcline); 163462306a36Sopenharmony_ci if (rc) 163562306a36Sopenharmony_ci break; 163662306a36Sopenharmony_ci 163762306a36Sopenharmony_ci callchain_cursor_advance(src); 163862306a36Sopenharmony_ci } 163962306a36Sopenharmony_ci 164062306a36Sopenharmony_ci return rc; 164162306a36Sopenharmony_ci} 164262306a36Sopenharmony_ci 164362306a36Sopenharmony_ci/* 164462306a36Sopenharmony_ci * Initialize a cursor before adding entries inside, but keep 164562306a36Sopenharmony_ci * the previously allocated entries as a cache. 164662306a36Sopenharmony_ci */ 164762306a36Sopenharmony_civoid callchain_cursor_reset(struct callchain_cursor *cursor) 164862306a36Sopenharmony_ci{ 164962306a36Sopenharmony_ci struct callchain_cursor_node *node; 165062306a36Sopenharmony_ci 165162306a36Sopenharmony_ci cursor->nr = 0; 165262306a36Sopenharmony_ci cursor->last = &cursor->first; 165362306a36Sopenharmony_ci 165462306a36Sopenharmony_ci for (node = cursor->first; node != NULL; node = node->next) { 165562306a36Sopenharmony_ci map__zput(node->ms.map); 165662306a36Sopenharmony_ci maps__zput(node->ms.maps); 165762306a36Sopenharmony_ci } 165862306a36Sopenharmony_ci} 165962306a36Sopenharmony_ci 166062306a36Sopenharmony_civoid callchain_param_setup(u64 sample_type, const char *arch) 166162306a36Sopenharmony_ci{ 166262306a36Sopenharmony_ci if (symbol_conf.use_callchain || symbol_conf.cumulate_callchain) { 166362306a36Sopenharmony_ci if ((sample_type & PERF_SAMPLE_REGS_USER) && 166462306a36Sopenharmony_ci (sample_type & PERF_SAMPLE_STACK_USER)) { 166562306a36Sopenharmony_ci callchain_param.record_mode = CALLCHAIN_DWARF; 166662306a36Sopenharmony_ci dwarf_callchain_users = true; 166762306a36Sopenharmony_ci } else if (sample_type & PERF_SAMPLE_BRANCH_STACK) 166862306a36Sopenharmony_ci callchain_param.record_mode = CALLCHAIN_LBR; 166962306a36Sopenharmony_ci else 167062306a36Sopenharmony_ci callchain_param.record_mode = CALLCHAIN_FP; 167162306a36Sopenharmony_ci } 167262306a36Sopenharmony_ci 167362306a36Sopenharmony_ci /* 167462306a36Sopenharmony_ci * It's necessary to use libunwind to reliably determine the caller of 167562306a36Sopenharmony_ci * a leaf function on aarch64, as otherwise we cannot know whether to 167662306a36Sopenharmony_ci * start from the LR or FP. 167762306a36Sopenharmony_ci * 167862306a36Sopenharmony_ci * Always starting from the LR can result in duplicate or entirely 167962306a36Sopenharmony_ci * erroneous entries. Always skipping the LR and starting from the FP 168062306a36Sopenharmony_ci * can result in missing entries. 168162306a36Sopenharmony_ci */ 168262306a36Sopenharmony_ci if (callchain_param.record_mode == CALLCHAIN_FP && !strcmp(arch, "arm64")) 168362306a36Sopenharmony_ci dwarf_callchain_users = true; 168462306a36Sopenharmony_ci} 168562306a36Sopenharmony_ci 168662306a36Sopenharmony_cistatic bool chain_match(struct callchain_list *base_chain, 168762306a36Sopenharmony_ci struct callchain_list *pair_chain) 168862306a36Sopenharmony_ci{ 168962306a36Sopenharmony_ci enum match_result match; 169062306a36Sopenharmony_ci 169162306a36Sopenharmony_ci match = match_chain_strings(base_chain->srcline, 169262306a36Sopenharmony_ci pair_chain->srcline); 169362306a36Sopenharmony_ci if (match != MATCH_ERROR) 169462306a36Sopenharmony_ci return match == MATCH_EQ; 169562306a36Sopenharmony_ci 169662306a36Sopenharmony_ci match = match_chain_dso_addresses(base_chain->ms.map, 169762306a36Sopenharmony_ci base_chain->ip, 169862306a36Sopenharmony_ci pair_chain->ms.map, 169962306a36Sopenharmony_ci pair_chain->ip); 170062306a36Sopenharmony_ci 170162306a36Sopenharmony_ci return match == MATCH_EQ; 170262306a36Sopenharmony_ci} 170362306a36Sopenharmony_ci 170462306a36Sopenharmony_cibool callchain_cnode_matched(struct callchain_node *base_cnode, 170562306a36Sopenharmony_ci struct callchain_node *pair_cnode) 170662306a36Sopenharmony_ci{ 170762306a36Sopenharmony_ci struct callchain_list *base_chain, *pair_chain; 170862306a36Sopenharmony_ci bool match = false; 170962306a36Sopenharmony_ci 171062306a36Sopenharmony_ci pair_chain = list_first_entry(&pair_cnode->val, 171162306a36Sopenharmony_ci struct callchain_list, 171262306a36Sopenharmony_ci list); 171362306a36Sopenharmony_ci 171462306a36Sopenharmony_ci list_for_each_entry(base_chain, &base_cnode->val, list) { 171562306a36Sopenharmony_ci if (&pair_chain->list == &pair_cnode->val) 171662306a36Sopenharmony_ci return false; 171762306a36Sopenharmony_ci 171862306a36Sopenharmony_ci if (!base_chain->srcline || !pair_chain->srcline) { 171962306a36Sopenharmony_ci pair_chain = list_next_entry(pair_chain, list); 172062306a36Sopenharmony_ci continue; 172162306a36Sopenharmony_ci } 172262306a36Sopenharmony_ci 172362306a36Sopenharmony_ci match = chain_match(base_chain, pair_chain); 172462306a36Sopenharmony_ci if (!match) 172562306a36Sopenharmony_ci return false; 172662306a36Sopenharmony_ci 172762306a36Sopenharmony_ci pair_chain = list_next_entry(pair_chain, list); 172862306a36Sopenharmony_ci } 172962306a36Sopenharmony_ci 173062306a36Sopenharmony_ci /* 173162306a36Sopenharmony_ci * Say chain1 is ABC, chain2 is ABCD, we consider they are 173262306a36Sopenharmony_ci * not fully matched. 173362306a36Sopenharmony_ci */ 173462306a36Sopenharmony_ci if (pair_chain && (&pair_chain->list != &pair_cnode->val)) 173562306a36Sopenharmony_ci return false; 173662306a36Sopenharmony_ci 173762306a36Sopenharmony_ci return match; 173862306a36Sopenharmony_ci} 173962306a36Sopenharmony_ci 174062306a36Sopenharmony_cistatic u64 count_callchain_hits(struct hist_entry *he) 174162306a36Sopenharmony_ci{ 174262306a36Sopenharmony_ci struct rb_root *root = &he->sorted_chain; 174362306a36Sopenharmony_ci struct rb_node *rb_node = rb_first(root); 174462306a36Sopenharmony_ci struct callchain_node *node; 174562306a36Sopenharmony_ci u64 chain_hits = 0; 174662306a36Sopenharmony_ci 174762306a36Sopenharmony_ci while (rb_node) { 174862306a36Sopenharmony_ci node = rb_entry(rb_node, struct callchain_node, rb_node); 174962306a36Sopenharmony_ci chain_hits += node->hit; 175062306a36Sopenharmony_ci rb_node = rb_next(rb_node); 175162306a36Sopenharmony_ci } 175262306a36Sopenharmony_ci 175362306a36Sopenharmony_ci return chain_hits; 175462306a36Sopenharmony_ci} 175562306a36Sopenharmony_ci 175662306a36Sopenharmony_ciu64 callchain_total_hits(struct hists *hists) 175762306a36Sopenharmony_ci{ 175862306a36Sopenharmony_ci struct rb_node *next = rb_first_cached(&hists->entries); 175962306a36Sopenharmony_ci u64 chain_hits = 0; 176062306a36Sopenharmony_ci 176162306a36Sopenharmony_ci while (next) { 176262306a36Sopenharmony_ci struct hist_entry *he = rb_entry(next, struct hist_entry, 176362306a36Sopenharmony_ci rb_node); 176462306a36Sopenharmony_ci 176562306a36Sopenharmony_ci chain_hits += count_callchain_hits(he); 176662306a36Sopenharmony_ci next = rb_next(&he->rb_node); 176762306a36Sopenharmony_ci } 176862306a36Sopenharmony_ci 176962306a36Sopenharmony_ci return chain_hits; 177062306a36Sopenharmony_ci} 177162306a36Sopenharmony_ci 177262306a36Sopenharmony_cis64 callchain_avg_cycles(struct callchain_node *cnode) 177362306a36Sopenharmony_ci{ 177462306a36Sopenharmony_ci struct callchain_list *chain; 177562306a36Sopenharmony_ci s64 cycles = 0; 177662306a36Sopenharmony_ci 177762306a36Sopenharmony_ci list_for_each_entry(chain, &cnode->val, list) { 177862306a36Sopenharmony_ci if (chain->srcline && chain->branch_count) 177962306a36Sopenharmony_ci cycles += chain->cycles_count / chain->branch_count; 178062306a36Sopenharmony_ci } 178162306a36Sopenharmony_ci 178262306a36Sopenharmony_ci return cycles; 178362306a36Sopenharmony_ci} 1784