// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2009-2011, Frederic Weisbecker <fweisbec@gmail.com> * * Handle the callchains from the stream in an ad-hoc radix tree and then * sort them in an rbtree. * * Using a radix for code path provides a fast retrieval and factorizes * memory use. Also that lets us use the paths in a hierarchical graph view. *
*/
if (!parse_callchain_mode(tok) ||
!parse_callchain_order(tok) ||
!parse_callchain_sort_key(tok) ||
!parse_callchain_value(tok)) { /* parsing ok - move on to the next */
try_stack_size = false; goto next;
} elseif (allow_record_opt && !record_opt_set) { if (parse_callchain_record(tok, &callchain_param)) goto try_numbers;
/* assume that number followed by 'dwarf' is stack size */ if (callchain_param.record_mode == CALLCHAIN_DWARF)
try_stack_size = true;
record_opt_set = true; goto next;
}
try_numbers: if (try_stack_size) { unsignedlong size = 0;
if (get_stack_size(tok, &size) < 0) return -1;
callchain_param.dump_size = size;
try_stack_size = false;
} elseif (!minpcnt_set) { /* try to get the min percent */
callchain_param.min_percent = strtod(tok, &endptr); if (tok == endptr) return -1;
minpcnt_set = true;
} else { /* try print limit at last */
callchain_param.print_limit = strtoul(tok, &endptr, 0); if (tok == endptr) return -1;
}
next:
arg = NULL;
}
switch (mode) { case CHAIN_FLAT: case CHAIN_FOLDED: if (rnode->hit < chain->hit)
p = &(*p)->rb_left; else
p = &(*p)->rb_right; break; case CHAIN_GRAPH_ABS: /* Falldown */ case CHAIN_GRAPH_REL: if (rnode_cumul < chain_cumul)
p = &(*p)->rb_left; else
p = &(*p)->rb_right; break; case CHAIN_NONE: default: break;
}
}
n = rb_first(&node->rb_root_in); while (n) {
child = rb_entry(n, struct callchain_node, rb_node_in);
n = rb_next(n);
__sort_chain_flat(rb_root, child, min_hit);
}
if (node->hit && node->hit >= min_hit)
rb_insert_callchain(rb_root, node, CHAIN_FLAT);
}
/* * Once we get every callchains from the stream, we can now * sort them by hit
*/ staticvoid
sort_chain_flat(struct rb_root *rb_root, struct callchain_root *root,
u64 min_hit, struct callchain_param *param __maybe_unused)
{
*rb_root = RB_ROOT;
__sort_chain_flat(rb_root, &root->node, min_hit);
}
int callchain_register_param(struct callchain_param *param)
{ switch (param->mode) { case CHAIN_GRAPH_ABS:
param->sort = sort_chain_graph_abs; break; case CHAIN_GRAPH_REL:
param->sort = sort_chain_graph_rel; break; case CHAIN_FLAT: case CHAIN_FOLDED:
param->sort = sort_chain_flat; break; case CHAIN_NONE: default: return -1;
} return 0;
}
/* * Create a child for a parent. If inherit_children, then the new child * will become the new parent of it's parent children
*/ staticstruct callchain_node *
create_child(struct callchain_node *parent, bool inherit_children)
{ struct callchain_node *new;
new = zalloc(sizeof(*new)); if (!new) {
perror("not enough memory to create child for code path tree"); return NULL;
}
new->parent = parent;
INIT_LIST_HEAD(&new->val);
INIT_LIST_HEAD(&new->parent_val);
if (inherit_children) { struct rb_node *n; struct callchain_node *child;
n = rb_first(&new->rb_root_in); while (n) {
child = rb_entry(n, struct callchain_node, rb_node_in);
child->parent = new;
n = rb_next(n);
}
/* make it the first child */
rb_link_node(&new->rb_node_in, NULL, &parent->rb_root_in.rb_node);
rb_insert_color(&new->rb_node_in, &parent->rb_root_in);
}
returnnew;
}
/* * Fill the node with callchain values
*/ staticint
fill_node(struct callchain_node *node, struct callchain_cursor *cursor)
{ struct callchain_cursor_node *cursor_node;
node->val_nr = cursor->nr - cursor->pos; if (!node->val_nr)
pr_warning("Warning: empty node in callchain tree\n");
cursor_node = callchain_cursor_current(cursor);
while (cursor_node) { struct callchain_list *call;
call = zalloc(sizeof(*call)); if (!call) {
perror("not enough memory for the code path tree"); return -ENOMEM;
}
call->ip = cursor_node->ip;
map_symbol__copy(&call->ms, &cursor_node->ms);
call->srcline = cursor_node->srcline;
if (cursor_node->branch) {
call->branch_count = 1;
if (cursor_node->branch_from) { /* * branch_from is set with value somewhere else * to imply it's "to" of a branch.
*/ if (!call->brtype_stat) {
call->brtype_stat = zalloc(sizeof(*call->brtype_stat)); if (!call->brtype_stat) {
perror("not enough memory for the code path branch statistics");
zfree(&call->brtype_stat); return -ENOMEM;
}
}
call->brtype_stat->branch_to = true;
if (cursor_node->branch_flags.predicted)
call->predicted_count = 1;
if (cursor_node->branch_flags.abort)
call->abort_count = 1;
if (cmp != 0)
ret = cmp < 0 ? MATCH_LT : MATCH_GT;
return ret;
}
/* * We need to always use relative addresses because we're aggregating * callchains from multiple threads, i.e. different address spaces, so * comparing absolute addresses make no sense as a symbol in a DSO may end up * in a different address when used in a different binary or even the same * binary but with some sort of address randomization technique, thus we need * to compare just relative addresses. -acme
*/ staticenum match_result match_chain_dso_addresses(struct map *left_map, u64 left_ip, struct map *right_map, u64 right_ip)
{ struct dso *left_dso = left_map ? map__dso(left_map) : NULL; struct dso *right_dso = right_map ? map__dso(right_map) : NULL;
switch (callchain_param.key) { case CCKEY_SRCLINE:
match = match_chain_strings(cnode->srcline, node->srcline); if (match != MATCH_ERROR) break; /* otherwise fall-back to symbol-based comparison below */
fallthrough; case CCKEY_FUNCTION: if (node->ms.sym && cnode->ms.sym) { /* * Compare inlined frames based on their symbol name * because different inlined frames will have the same * symbol start. Otherwise do a faster comparison based * on the symbol start address.
*/ if (cnode->ms.sym->inlined || node->ms.sym->inlined) {
match = match_chain_strings(cnode->ms.sym->name,
node->ms.sym->name); if (match != MATCH_ERROR) break;
} else {
match = match_chain_dso_addresses(cnode->ms.map, cnode->ms.sym->start,
node->ms.map, node->ms.sym->start); break;
}
} /* otherwise fall-back to IP-based comparison below */
fallthrough; case CCKEY_ADDRESS: default:
match = match_chain_dso_addresses(cnode->ms.map, cnode->ip, node->ms.map, node->ip); break;
}
if (match == MATCH_EQ && node->branch) {
cnode->branch_count++;
if (node->branch_from) { /* * It's "to" of a branch
*/ if (!cnode->brtype_stat) {
cnode->brtype_stat = zalloc(sizeof(*cnode->brtype_stat)); if (!cnode->brtype_stat) {
perror("not enough memory for the code path branch statistics"); return MATCH_ERROR;
}
}
cnode->brtype_stat->branch_to = true;
if (node->branch_flags.predicted)
cnode->predicted_count++;
if (node->branch_flags.abort)
cnode->abort_count++;
/* * Split the parent in two parts (a new child is created) and * give a part of its callchain to the created child. * Then create another child to host the given callchain of new branch
*/ staticint
split_add_child(struct callchain_node *parent, struct callchain_cursor *cursor, struct callchain_list *to_split,
u64 idx_parents, u64 idx_local, u64 period)
{ struct callchain_node *new; struct list_head *old_tail; unsignedint idx_total = idx_parents + idx_local;
/* split */ new = create_child(parent, true); if (new == NULL) return -1;
/* split the callchain and move a part to the new child */
old_tail = parent->val.prev;
list_del_range(&to_split->list, old_tail);
new->val.next = &to_split->list;
new->val.prev = old_tail;
to_split->list.prev = &new->val;
old_tail->next = &new->val;
/* create a new child for the new branch if any */ if (idx_total < cursor->nr) { struct callchain_node *first; struct callchain_list *cnode; struct callchain_cursor_node *node; struct rb_node *p, **pp;
node = callchain_cursor_current(cursor); new = add_child(parent, cursor, period); if (new == NULL) return -1;
/* * This is second child since we moved parent's children * to new (first) child above.
*/
p = parent->rb_root_in.rb_node;
first = rb_entry(p, struct callchain_node, rb_node_in);
cnode = list_first_entry(&first->val, struct callchain_list,
list);
/* If at least first entry matches, rely to children */
ret = append_chain(rnode, cursor, period); if (ret == MATCH_EQ) goto inc_children_hit; if (ret == MATCH_ERROR) return -1;
if (ret == MATCH_LT)
p = &parent->rb_left; else
p = &parent->rb_right;
} /* nothing in children, add to the current node */
rnode = add_child(root, cursor, period); if (rnode == NULL) return -1;
/* * Lookup in the current node * If we have a symbol, then compare the start to match * anywhere inside a function, unless function * mode is disabled.
*/
list_for_each_entry(cnode, &root->val, list) { struct callchain_cursor_node *node;
node = callchain_cursor_current(cursor); if (!node) break;
cmp = match_chain(node, cnode); if (cmp != MATCH_EQ) break;
found = true;
callchain_cursor_advance(cursor);
}
/* matches not, relay no the parent */ if (!found) {
WARN_ONCE(cmp == MATCH_ERROR, "Chain comparison error\n"); return cmp;
}
matches = cursor->pos - start;
/* we match only a part of the node. Split it and add the new chain */ if (matches < root->val_nr) { if (split_add_child(root, cursor, cnode, start, matches,
period) < 0) return MATCH_ERROR;
return MATCH_EQ;
}
/* we match 100% of the path, increment the hit */ if (matches == root->val_nr && cursor->pos == cursor->nr) {
root->hit += period;
root->count++; return MATCH_EQ;
}
/* We match the node and still have a part remaining */ if (append_chain_children(root, cursor, period) < 0) return MATCH_ERROR;
return MATCH_EQ;
}
int callchain_append(struct callchain_root *root, struct callchain_cursor *cursor,
u64 period)
{ if (cursor == NULL) return -1;
if (!cursor->nr) return 0;
callchain_cursor_commit(cursor);
if (append_chain_children(&root->node, cursor, period) < 0) return -1;
if (cursor->nr > root->max_depth)
root->max_depth = cursor->nr;
/* * Initialize a cursor before adding entries inside, but keep * the previously allocated entries as a cache.
*/ void callchain_cursor_reset(struct callchain_cursor *cursor)
{ struct callchain_cursor_node *node;
/* * It's necessary to use libunwind to reliably determine the caller of * a leaf function on aarch64, as otherwise we cannot know whether to * start from the LR or FP. * * Always starting from the LR can result in duplicate or entirely * erroneous entries. Always skipping the LR and starting from the FP * can result in missing entries.
*/ if (callchain_param.record_mode == CALLCHAIN_FP && !strcmp(arch, "arm64"))
dwarf_callchain_users = true;
}
match = chain_match(base_chain, pair_chain); if (!match) returnfalse;
pair_chain = list_next_entry(pair_chain, list);
}
/* * Say chain1 is ABC, chain2 is ABCD, we consider they are * not fully matched.
*/ if (pair_chain && (&pair_chain->list != &pair_cnode->val)) returnfalse;
int sample__for_each_callchain_node(struct thread *thread, struct evsel *evsel, struct perf_sample *sample, int max_stack, bool symbols, callchain_iter_fn cb, void *data)
{ struct callchain_cursor *cursor = get_tls_callchain_cursor(); int ret;
if (!cursor) return -ENOMEM;
/* Fill in the callchain. */
ret = __thread__resolve_callchain(thread, cursor, evsel, sample, /*parent=*/NULL, /*root_al=*/NULL,
max_stack, symbols); if (ret) return ret;
/* Switch from writing the callchain to reading it. */
callchain_cursor_commit(cursor);
while (1) { struct callchain_cursor_node *node = callchain_cursor_current(cursor);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.