perf report: Properly handle branch count in match_chain()

Some of the code paths I introduced before returned too early without
running the code to handle a node's branch count.  By refactoring
match_chain to only have one exit point, this can be remedied.

Signed-off-by: Milian Wolff <milian.wolff@kdab.com>
Acked-by: Namhyung Kim <namhyung@kernel.org>
Cc: David Ahern <dsahern@gmail.com>
Cc: Jin Yao <yao.jin@linux.intel.com>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Ravi Bangoria <ravi.bangoria@linux.vnet.ibm.com>
Link: http://lkml.kernel.org/r/1707691.qaJ269GSZW@agathebauer
Link: http://lkml.kernel.org/r/20171018185350.14893-2-milian.wolff@kdab.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
Milian Wolff 2017-10-20 12:14:47 -03:00 committed by Arnaldo Carvalho de Melo
parent aa441895f7
commit bf36eb5c4b

View file

@ -666,83 +666,99 @@ static enum match_result match_chain_strings(const char *left,
return ret; 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
*/
static enum 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 ? left_map->dso : NULL;
struct dso *right_dso = right_map ? right_map->dso : NULL;
if (left_dso != right_dso)
return left_dso < right_dso ? MATCH_LT : MATCH_GT;
if (left_ip != right_ip)
return left_ip < right_ip ? MATCH_LT : MATCH_GT;
return MATCH_EQ;
}
static enum match_result match_chain(struct callchain_cursor_node *node, static enum match_result match_chain(struct callchain_cursor_node *node,
struct callchain_list *cnode) struct callchain_list *cnode)
{ {
struct symbol *sym = node->sym; enum match_result match = MATCH_ERROR;
u64 left, right;
struct dso *left_dso = NULL;
struct dso *right_dso = NULL;
if (callchain_param.key == CCKEY_SRCLINE) {
enum match_result match = match_chain_strings(cnode->srcline,
node->srcline);
/* if no srcline is available, fallback to symbol name */
if (match == MATCH_ERROR && cnode->ms.sym && node->sym)
match = match_chain_strings(cnode->ms.sym->name,
node->sym->name);
switch (callchain_param.key) {
case CCKEY_SRCLINE:
match = match_chain_strings(cnode->srcline, node->srcline);
if (match != MATCH_ERROR) if (match != MATCH_ERROR)
return match; break;
/* otherwise fall-back to symbol-based comparison below */
/* otherwise fall-back to IP-based comparison below */ __fallthrough;
} case CCKEY_FUNCTION:
if (node->sym && cnode->ms.sym) {
if (cnode->ms.sym && sym && callchain_param.key == CCKEY_FUNCTION) { /*
/* * Compare inlined frames based on their symbol name
* Compare inlined frames based on their symbol name because * because different inlined frames will have the same
* different inlined frames will have the same symbol start * symbol start. Otherwise do a faster comparison based
*/ * on the symbol start address.
if (cnode->ms.sym->inlined || node->sym->inlined) */
return match_chain_strings(cnode->ms.sym->name, if (cnode->ms.sym->inlined || node->sym->inlined) {
node->sym->name); match = match_chain_strings(cnode->ms.sym->name,
node->sym->name);
left = cnode->ms.sym->start; if (match != MATCH_ERROR)
right = sym->start; break;
left_dso = cnode->ms.map->dso;
right_dso = node->map->dso;
} else {
left = cnode->ip;
right = node->ip;
}
if (left == right && left_dso == right_dso) {
if (node->branch) {
cnode->branch_count++;
if (node->branch_from) {
/*
* It's "to" of a branch
*/
cnode->brtype_stat.branch_to = true;
if (node->branch_flags.predicted)
cnode->predicted_count++;
if (node->branch_flags.abort)
cnode->abort_count++;
branch_type_count(&cnode->brtype_stat,
&node->branch_flags,
node->branch_from,
node->ip);
} else { } else {
/* match = match_chain_dso_addresses(cnode->ms.map, cnode->ms.sym->start,
* It's "from" of a branch node->map, node->sym->start);
*/ break;
cnode->brtype_stat.branch_to = false;
cnode->cycles_count +=
node->branch_flags.cycles;
cnode->iter_count += node->nr_loop_iter;
cnode->iter_cycles += node->iter_cycles;
} }
} }
/* otherwise fall-back to IP-based comparison below */
return MATCH_EQ; __fallthrough;
case CCKEY_ADDRESS:
default:
match = match_chain_dso_addresses(cnode->ms.map, cnode->ip, node->map, node->ip);
break;
} }
return left > right ? MATCH_GT : MATCH_LT; if (match == MATCH_EQ && node->branch) {
cnode->branch_count++;
if (node->branch_from) {
/*
* It's "to" of a branch
*/
cnode->brtype_stat.branch_to = true;
if (node->branch_flags.predicted)
cnode->predicted_count++;
if (node->branch_flags.abort)
cnode->abort_count++;
branch_type_count(&cnode->brtype_stat,
&node->branch_flags,
node->branch_from,
node->ip);
} else {
/*
* It's "from" of a branch
*/
cnode->brtype_stat.branch_to = false;
cnode->cycles_count += node->branch_flags.cycles;
cnode->iter_count += node->nr_loop_iter;
cnode->iter_cycles += node->iter_cycles;
}
}
return match;
} }
/* /*