From: Kan Liang <[email protected]>

LBR call stack only has user callchain. It is output as
PERF_SAMPLE_BRANCH_STACK data format. For the kernel callchain, it's
still from PERF_SAMPLE_CALLCHAIN.
The perf tool has to handle both data sources to construct a
complete callstack.

Signed-off-by: Kan Liang <[email protected]>
---
 tools/perf/util/machine.c | 194 ++++++++++++++++++++++++++++++----------------
 tools/perf/util/session.c |  41 ++++++++--
 2 files changed, 163 insertions(+), 72 deletions(-)

diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index 52e9490..f51014f 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -1399,18 +1399,70 @@ struct branch_info *sample__resolve_bstack(struct 
perf_sample *sample,
        return bi;
 }
 
+static inline int __thread__resolve_callchain_sample(struct thread *thread,
+                                                    u64 ip, u8 *cpumode,
+                                                    struct symbol **parent,
+                                                    struct addr_location 
*root_al,
+                                                    struct addr_location *al)
+{
+       if (ip >= PERF_CONTEXT_MAX) {
+               switch (ip) {
+               case PERF_CONTEXT_HV:
+                       *cpumode = PERF_RECORD_MISC_HYPERVISOR;
+                       break;
+               case PERF_CONTEXT_KERNEL:
+                       *cpumode = PERF_RECORD_MISC_KERNEL;
+                       break;
+               case PERF_CONTEXT_USER:
+                       *cpumode = PERF_RECORD_MISC_USER;
+                       break;
+               default:
+                       pr_debug("invalid callchain context: "
+                                "%"PRId64"\n", (s64) ip);
+                       /*
+                        * It seems the callchain is corrupted.
+                        * Discard all.
+                        */
+                       callchain_cursor_reset(&callchain_cursor);
+                       return 1;
+               }
+               return 0;
+       }
+
+       al->filtered = 0;
+       thread__find_addr_location(thread, *cpumode,
+                                  MAP__FUNCTION, ip, al);
+       if (al->sym != NULL) {
+               if (sort__has_parent && !*parent &&
+                   symbol__match_regex(al->sym, &parent_regex))
+                       *parent = al->sym;
+               else if (have_ignore_callees && root_al &&
+                 symbol__match_regex(al->sym, &ignore_callees_regex)) {
+                       /* Treat this symbol as the root,
+                          forgetting its callees. */
+                       *root_al = *al;
+                       callchain_cursor_reset(&callchain_cursor);
+               }
+       }
+
+       return callchain_cursor_append(&callchain_cursor,
+                                     ip, al->map, al->sym);
+}
+
 static int thread__resolve_callchain_sample(struct thread *thread,
-                                            struct ip_callchain *chain,
-                                            struct symbol **parent,
-                                            struct addr_location *root_al,
-                                            int max_stack)
+                                           struct perf_evsel *evsel,
+                                           struct perf_sample *sample,
+                                           struct symbol **parent,
+                                           struct addr_location *root_al,
+                                           int max_stack)
 {
+       struct ip_callchain *chain = sample->callchain;
        u8 cpumode = PERF_RECORD_MISC_USER;
        int chain_nr = min(max_stack, (int)chain->nr);
-       int i;
-       int j;
-       int err;
+       int i, j, err = 0;
        int skip_idx __maybe_unused;
+       int lbr = 0;
+       u64 ip;
 
        callchain_cursor_reset(&callchain_cursor);
 
@@ -1419,74 +1471,81 @@ static int thread__resolve_callchain_sample(struct 
thread *thread,
                return 0;
        }
 
-       /*
-        * Based on DWARF debug information, some architectures skip
-        * a callchain entry saved by the kernel.
-        */
-       skip_idx = arch_skip_callchain_idx(thread, chain);
+       if (evsel->attr.branch_sample_type & PERF_SAMPLE_BRANCH_CALL_STACK)
+               lbr = 1;
 
-       for (i = 0; i < chain_nr; i++) {
-               u64 ip;
-               struct addr_location al;
+again:
+       /* LBR call stack */
+       if (lbr) {
+               struct branch_stack *lbr_stack = sample->branch_stack;
+               int lbr_nr = lbr_stack->nr;
+               int mix_chain_nr;
 
-               if (callchain_param.order == ORDER_CALLEE)
-                       j = i;
-               else
-                       j = chain->nr - i - 1;
+               for (i = 0; i < chain_nr; i++) {
+                       if (chain->ips[i] == PERF_CONTEXT_USER)
+                               break;
+               }
 
-#ifdef HAVE_SKIP_CALLCHAIN_IDX
-               if (j == skip_idx)
-                       continue;
-#endif
-               ip = chain->ips[j];
+               /* LBR only affects the user callchain */
+               if (i == chain_nr) {
+                       lbr = 0;
+                       goto again;
+               }
 
-               if (ip >= PERF_CONTEXT_MAX) {
-                       switch (ip) {
-                       case PERF_CONTEXT_HV:
-                               cpumode = PERF_RECORD_MISC_HYPERVISOR;
-                               break;
-                       case PERF_CONTEXT_KERNEL:
-                               cpumode = PERF_RECORD_MISC_KERNEL;
-                               break;
-                       case PERF_CONTEXT_USER:
-                               cpumode = PERF_RECORD_MISC_USER;
-                               break;
-                       default:
-                               pr_debug("invalid callchain context: "
-                                        "%"PRId64"\n", (s64) ip);
-                               /*
-                                * It seems the callchain is corrupted.
-                                * Discard all.
-                                */
-                               callchain_cursor_reset(&callchain_cursor);
-                               return 0;
-                       }
-                       continue;
+               mix_chain_nr = i + 2 + lbr_nr;
+               if (mix_chain_nr > PERF_MAX_STACK_DEPTH) {
+                       pr_warning("corrupted callchain. skipping...\n");
+                       return 0;
                }
 
-               al.filtered = 0;
-               thread__find_addr_location(thread, cpumode,
-                                          MAP__FUNCTION, ip, &al);
-               if (al.sym != NULL) {
-                       if (sort__has_parent && !*parent &&
-                           symbol__match_regex(al.sym, &parent_regex))
-                               *parent = al.sym;
-                       else if (have_ignore_callees && root_al &&
-                         symbol__match_regex(al.sym, &ignore_callees_regex)) {
-                               /* Treat this symbol as the root,
-                                  forgetting its callees. */
-                               *root_al = al;
-                               callchain_cursor_reset(&callchain_cursor);
+               for (j = 0; j < mix_chain_nr; j++) {
+                       struct addr_location al;
+
+                       if (callchain_param.order == ORDER_CALLEE) {
+                               if (j < i + 2)
+                                       ip = chain->ips[j];
+                               else
+                                       ip = lbr_stack->entries[j - i - 2].from;
+                       } else {
+                               if (j < lbr_nr)
+                                       ip = lbr_stack->entries[lbr_nr - j - 
1].from;
+                               else
+                                       ip = chain->ips[i + 1 - (j - lbr_nr)];
                        }
+                       err = __thread__resolve_callchain_sample(thread,
+                               ip, &cpumode, parent, root_al, &al);
+                       if (err)
+                               goto exit;
                }
+       } else {
 
-               err = callchain_cursor_append(&callchain_cursor,
-                                             ip, al.map, al.sym);
-               if (err)
-                       return err;
-       }
+               /*
+                * Based on DWARF debug information, some architectures skip
+                * a callchain entry saved by the kernel.
+                */
+               skip_idx = arch_skip_callchain_idx(thread, chain);
 
-       return 0;
+               for (i = 0; i < chain_nr; i++) {
+                       struct addr_location al;
+
+                       if (callchain_param.order == ORDER_CALLEE)
+                               j = i;
+                       else
+                               j = chain->nr - i - 1;
+
+#ifdef HAVE_SKIP_CALLCHAIN_IDX
+                       if (j == skip_idx)
+                               continue;
+#endif
+                       ip = chain->ips[j];
+                       err = __thread__resolve_callchain_sample(thread,
+                               ip, &cpumode, parent, root_al, &al);
+                       if (err)
+                               goto exit;
+               }
+       }
+exit:
+       return (err < 0) ? err : 0;
 }
 
 static int unwind_entry(struct unwind_entry *entry, void *arg)
@@ -1503,8 +1562,9 @@ int thread__resolve_callchain(struct thread *thread,
                              struct addr_location *root_al,
                              int max_stack)
 {
-       int ret = thread__resolve_callchain_sample(thread, sample->callchain,
-                                                  parent, root_al, max_stack);
+       int ret = thread__resolve_callchain_sample(thread, evsel,
+                                                  sample, parent,
+                                                  root_al, max_stack);
        if (ret)
                return ret;
 
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
index f4478ce..fa476be 100644
--- a/tools/perf/util/session.c
+++ b/tools/perf/util/session.c
@@ -557,15 +557,45 @@ int perf_session_queue_event(struct perf_session *s, 
union perf_event *event,
        return 0;
 }
 
-static void callchain__printf(struct perf_sample *sample)
+static void callchain__printf(struct perf_evsel *evsel,
+                             struct perf_sample *sample)
 {
        unsigned int i;
+       u64 total_nr, callchain_nr;
+       int lbr = 0;
 
-       printf("... chain: nr:%" PRIu64 "\n", sample->callchain->nr);
+       total_nr = callchain_nr = sample->callchain->nr;
 
-       for (i = 0; i < sample->callchain->nr; i++)
+       if (evsel->attr.branch_sample_type & PERF_SAMPLE_BRANCH_CALL_STACK)
+               lbr = 1;
+
+       if (lbr) {
+               struct branch_stack *lbr_stack = sample->branch_stack;
+
+               for (i = 0; i < callchain_nr; i++) {
+                       if (sample->callchain->ips[i] == PERF_CONTEXT_USER)
+                               break;
+               }
+
+               if (i != callchain_nr) {
+                       total_nr = i + 1 + lbr_stack->nr;
+                       callchain_nr = i + 1;
+               }
+       }
+
+       printf("... chain: nr:%" PRIu64 "\n", total_nr);
+
+       for (i = 0; i < callchain_nr + 1; i++)
                printf("..... %2d: %016" PRIx64 "\n",
                       i, sample->callchain->ips[i]);
+
+       if (total_nr > callchain_nr) {
+               struct branch_stack *lbr_stack = sample->branch_stack;
+
+               for (i = 0; i < lbr_stack->nr; i++)
+                       printf("..... %2d: %016" PRIx64 "\n",
+                               (int)(i + callchain_nr + 1), 
lbr_stack->entries[i].from);
+       }
 }
 
 static void branch_stack__printf(struct perf_sample *sample)
@@ -691,9 +721,10 @@ static void dump_sample(struct perf_evsel *evsel, union 
perf_event *event,
        sample_type = evsel->attr.sample_type;
 
        if (sample_type & PERF_SAMPLE_CALLCHAIN)
-               callchain__printf(sample);
+               callchain__printf(evsel, sample);
 
-       if (sample_type & PERF_SAMPLE_BRANCH_STACK)
+       if ((sample_type & PERF_SAMPLE_BRANCH_STACK) &&
+               !(evsel->attr.branch_sample_type & 
PERF_SAMPLE_BRANCH_CALL_STACK))
                branch_stack__printf(sample);
 
        if (sample_type & PERF_SAMPLE_REGS_USER)
-- 
1.8.3.2

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to