|
@@ -1616,7 +1616,11 @@ static int add_callchain_ip(struct thread *thread,
|
|
|
struct symbol **parent,
|
|
|
struct addr_location *root_al,
|
|
|
u8 *cpumode,
|
|
|
- u64 ip)
|
|
|
+ u64 ip,
|
|
|
+ bool branch,
|
|
|
+ struct branch_flags *flags,
|
|
|
+ int nr_loop_iter,
|
|
|
+ int samples)
|
|
|
{
|
|
|
struct addr_location al;
|
|
|
|
|
@@ -1668,7 +1672,8 @@ static int add_callchain_ip(struct thread *thread,
|
|
|
|
|
|
if (symbol_conf.hide_unresolved && al.sym == NULL)
|
|
|
return 0;
|
|
|
- return callchain_cursor_append(cursor, al.addr, al.map, al.sym);
|
|
|
+ return callchain_cursor_append(cursor, al.addr, al.map, al.sym,
|
|
|
+ branch, flags, nr_loop_iter, samples);
|
|
|
}
|
|
|
|
|
|
struct branch_info *sample__resolve_bstack(struct perf_sample *sample,
|
|
@@ -1757,7 +1762,9 @@ static int resolve_lbr_callchain_sample(struct thread *thread,
|
|
|
/* LBR only affects the user callchain */
|
|
|
if (i != chain_nr) {
|
|
|
struct branch_stack *lbr_stack = sample->branch_stack;
|
|
|
- int lbr_nr = lbr_stack->nr, j;
|
|
|
+ int lbr_nr = lbr_stack->nr, j, k;
|
|
|
+ bool branch;
|
|
|
+ struct branch_flags *flags;
|
|
|
/*
|
|
|
* LBR callstack can only get user call chain.
|
|
|
* The mix_chain_nr is kernel call chain
|
|
@@ -1772,23 +1779,41 @@ static int resolve_lbr_callchain_sample(struct thread *thread,
|
|
|
|
|
|
for (j = 0; j < mix_chain_nr; j++) {
|
|
|
int err;
|
|
|
+ branch = false;
|
|
|
+ flags = NULL;
|
|
|
+
|
|
|
if (callchain_param.order == ORDER_CALLEE) {
|
|
|
if (j < i + 1)
|
|
|
ip = chain->ips[j];
|
|
|
- else if (j > i + 1)
|
|
|
- ip = lbr_stack->entries[j - i - 2].from;
|
|
|
- else
|
|
|
+ else if (j > i + 1) {
|
|
|
+ k = j - i - 2;
|
|
|
+ ip = lbr_stack->entries[k].from;
|
|
|
+ branch = true;
|
|
|
+ flags = &lbr_stack->entries[k].flags;
|
|
|
+ } else {
|
|
|
ip = lbr_stack->entries[0].to;
|
|
|
+ branch = true;
|
|
|
+ flags = &lbr_stack->entries[0].flags;
|
|
|
+ }
|
|
|
} else {
|
|
|
- if (j < lbr_nr)
|
|
|
- ip = lbr_stack->entries[lbr_nr - j - 1].from;
|
|
|
+ if (j < lbr_nr) {
|
|
|
+ k = lbr_nr - j - 1;
|
|
|
+ ip = lbr_stack->entries[k].from;
|
|
|
+ branch = true;
|
|
|
+ flags = &lbr_stack->entries[k].flags;
|
|
|
+ }
|
|
|
else if (j > lbr_nr)
|
|
|
ip = chain->ips[i + 1 - (j - lbr_nr)];
|
|
|
- else
|
|
|
+ else {
|
|
|
ip = lbr_stack->entries[0].to;
|
|
|
+ branch = true;
|
|
|
+ flags = &lbr_stack->entries[0].flags;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- err = add_callchain_ip(thread, cursor, parent, root_al, &cpumode, ip);
|
|
|
+ err = add_callchain_ip(thread, cursor, parent,
|
|
|
+ root_al, &cpumode, ip,
|
|
|
+ branch, flags, 0, 0);
|
|
|
if (err)
|
|
|
return (err < 0) ? err : 0;
|
|
|
}
|
|
@@ -1813,6 +1838,7 @@ static int thread__resolve_callchain_sample(struct thread *thread,
|
|
|
int i, j, err, nr_entries;
|
|
|
int skip_idx = -1;
|
|
|
int first_call = 0;
|
|
|
+ int nr_loop_iter;
|
|
|
|
|
|
if (perf_evsel__has_branch_callstack(evsel)) {
|
|
|
err = resolve_lbr_callchain_sample(thread, cursor, sample, parent,
|
|
@@ -1868,14 +1894,37 @@ static int thread__resolve_callchain_sample(struct thread *thread,
|
|
|
be[i] = branch->entries[branch->nr - i - 1];
|
|
|
}
|
|
|
|
|
|
+ nr_loop_iter = nr;
|
|
|
nr = remove_loops(be, nr);
|
|
|
|
|
|
+ /*
|
|
|
+ * Get the number of iterations.
|
|
|
+ * It's only approximation, but good enough in practice.
|
|
|
+ */
|
|
|
+ if (nr_loop_iter > nr)
|
|
|
+ nr_loop_iter = nr_loop_iter - nr + 1;
|
|
|
+ else
|
|
|
+ nr_loop_iter = 0;
|
|
|
+
|
|
|
for (i = 0; i < nr; i++) {
|
|
|
- err = add_callchain_ip(thread, cursor, parent, root_al,
|
|
|
- NULL, be[i].to);
|
|
|
+ if (i == nr - 1)
|
|
|
+ err = add_callchain_ip(thread, cursor, parent,
|
|
|
+ root_al,
|
|
|
+ NULL, be[i].to,
|
|
|
+ true, &be[i].flags,
|
|
|
+ nr_loop_iter, 1);
|
|
|
+ else
|
|
|
+ err = add_callchain_ip(thread, cursor, parent,
|
|
|
+ root_al,
|
|
|
+ NULL, be[i].to,
|
|
|
+ true, &be[i].flags,
|
|
|
+ 0, 0);
|
|
|
+
|
|
|
if (!err)
|
|
|
err = add_callchain_ip(thread, cursor, parent, root_al,
|
|
|
- NULL, be[i].from);
|
|
|
+ NULL, be[i].from,
|
|
|
+ true, &be[i].flags,
|
|
|
+ 0, 0);
|
|
|
if (err == -EINVAL)
|
|
|
break;
|
|
|
if (err)
|
|
@@ -1903,7 +1952,9 @@ check_calls:
|
|
|
if (ip < PERF_CONTEXT_MAX)
|
|
|
++nr_entries;
|
|
|
|
|
|
- err = add_callchain_ip(thread, cursor, parent, root_al, &cpumode, ip);
|
|
|
+ err = add_callchain_ip(thread, cursor, parent,
|
|
|
+ root_al, &cpumode, ip,
|
|
|
+ false, NULL, 0, 0);
|
|
|
|
|
|
if (err)
|
|
|
return (err < 0) ? err : 0;
|
|
@@ -1919,7 +1970,8 @@ static int unwind_entry(struct unwind_entry *entry, void *arg)
|
|
|
if (symbol_conf.hide_unresolved && entry->sym == NULL)
|
|
|
return 0;
|
|
|
return callchain_cursor_append(cursor, entry->ip,
|
|
|
- entry->map, entry->sym);
|
|
|
+ entry->map, entry->sym,
|
|
|
+ false, NULL, 0, 0);
|
|
|
}
|
|
|
|
|
|
static int thread__resolve_callchain_unwind(struct thread *thread,
|