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.
For perf report -D option, both lbr and fp information will be
displayed.

Signed-off-by: Kan Liang <[email protected]>
---
 tools/perf/util/evsel.h   |  4 +++
 tools/perf/util/machine.c | 73 +++++++++++++++++++++++++++++++++++++++++------
 tools/perf/util/session.c | 56 ++++++++++++++++++++++++++++++++----
 3 files changed, 118 insertions(+), 15 deletions(-)

diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h
index 9797909..1bbaa74 100644
--- a/tools/perf/util/evsel.h
+++ b/tools/perf/util/evsel.h
@@ -368,4 +368,8 @@ for ((_evsel) = list_entry((_leader)->node.next, struct 
perf_evsel, node);  \
      (_evsel) && (_evsel)->leader == (_leader);                                
        \
      (_evsel) = list_entry((_evsel)->node.next, struct perf_evsel, node))
 
+static inline bool has_branch_callstack(struct perf_evsel *evsel)
+{
+       return evsel->attr.branch_sample_type & PERF_SAMPLE_BRANCH_CALL_STACK;
+}
 #endif /* __PERF_EVSEL_H */
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index 9c7d136..a63ae26 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -1451,15 +1451,19 @@ static inline int 
__thread__resolve_callchain_sample(struct thread *thread,
 
 }
 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, j, err = 0;
        int skip_idx __maybe_unused;
+       u64 ip;
+       bool lbr = has_branch_callstack(evsel);
 
        callchain_cursor_reset(&callchain_cursor);
 
@@ -1468,6 +1472,58 @@ static int thread__resolve_callchain_sample(struct 
thread *thread,
                return 0;
        }
 
+       if (lbr) {
+               for (i = 0; i < chain_nr; i++) {
+                       if (chain->ips[i] == PERF_CONTEXT_USER)
+                               break;
+               }
+
+               /* LBR only affects the user callchain */
+               if (i != chain_nr) {
+                       struct branch_stack *lbr_stack = sample->branch_stack;
+                       int lbr_nr = lbr_stack->nr;
+                       /*
+                        * LBR callstack can only get user call chain 
information.
+                        * The mix_chain_nr should be kernel call chain number 
plus
+                        * LBR user call chain number.
+                        * i is kernel call chain number, 1 is 
PERF_CONTEXT_USER,
+                        * lbr_nr + 1 is the user call chain number.
+                        * For details, please refer to the comments
+                        * in callchain__printf
+                        */
+                       int mix_chain_nr = i + 1 + lbr_nr + 1;
+
+                       if (mix_chain_nr > PERF_MAX_STACK_DEPTH) {
+                               pr_warning("corrupted callchain. 
skipping...\n");
+                               return 0;
+                       }
+
+                       for (j = 0; j < mix_chain_nr; j++) {
+                               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
+                                               ip = lbr_stack->entries[0].to;
+                               } else {
+                                       if (j < lbr_nr)
+                                               ip = lbr_stack->entries[lbr_nr 
- j - 1].from;
+                                       else if (j > lbr_nr)
+                                               ip = chain->ips[i + 1 - (j - 
lbr_nr)];
+                                       else
+                                               ip = lbr_stack->entries[0].to;
+                               }
+
+                               err = __thread__resolve_callchain_sample(thread,
+                                                       ip, &cpumode, parent, 
root_al);
+                               if (err)
+                                       goto exit;
+                       }
+                       return 0;
+               }
+       }
+
        /*
         * Based on DWARF debug information, some architectures skip
         * a callchain entry saved by the kernel.
@@ -1475,8 +1531,6 @@ static int thread__resolve_callchain_sample(struct thread 
*thread,
        skip_idx = arch_skip_callchain_idx(thread, chain);
 
        for (i = 0; i < chain_nr; i++) {
-               u64 ip;
-
                if (callchain_param.order == ORDER_CALLEE)
                        j = i;
                else
@@ -1489,7 +1543,7 @@ static int thread__resolve_callchain_sample(struct thread 
*thread,
                ip = chain->ips[j];
 
                err = __thread__resolve_callchain_sample(thread,
-                       ip, &cpumode, parent, root_al);
+                                       ip, &cpumode, parent, root_al);
                if (err)
                        goto exit;
        }
@@ -1512,8 +1566,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..794c46b 100644
--- a/tools/perf/util/session.c
+++ b/tools/perf/util/session.c
@@ -557,15 +557,59 @@ 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;
+       struct ip_callchain *callchain = sample->callchain;
+       bool lbr = has_branch_callstack(evsel);
 
-       printf("... chain: nr:%" PRIu64 "\n", sample->callchain->nr);
+       if (lbr) {
+               struct branch_stack *lbr_stack = sample->branch_stack;
+               u64 kernel_callchain_nr = callchain->nr;
 
-       for (i = 0; i < sample->callchain->nr; i++)
+               for (i = 0; i < kernel_callchain_nr; i++) {
+                       if (callchain->ips[i] == PERF_CONTEXT_USER)
+                               break;  
+               }
+
+               if ((i != kernel_callchain_nr) && lbr_stack->nr) {
+                       u64 total_nr;
+                       /*
+                        * LBR callstack can only get user call chain 
information,
+                        * So i is kernel call chain number, 1 is 
PERF_CONTEXT_USER.
+                        *
+                        * The user call chain is stored in LBR registers.
+                        * LBR are pair registers. The caller is stored in 
"from"
+                        * register, while the callee is stored in "to" 
register.
+                        * For example, there is a call stack 
"A"->"B"->"C"->"D".
+                        * The LBR registers will recorde like "C"->"D", 
"B"->"C",
+                        * "A"->"B". So only the first "to" register and all 
"from"
+                        * registers are needed to construct the whole stack.
+                        */
+                       total_nr = i + 1 + lbr_stack->nr + 1;
+                       kernel_callchain_nr = i + 1;
+
+                       printf("... LBR call chain: nr:%" PRIu64 "\n", 
total_nr);
+
+                       for (i = 0; i < kernel_callchain_nr; i++)
+                               printf("..... %2d: %016" PRIx64 "\n",
+                                      i, callchain->ips[i]);
+
+                       printf("..... %2d: %016" PRIx64 "\n",
+                              (int)(kernel_callchain_nr), 
lbr_stack->entries[0].to);
+                       for (i = 0; i < lbr_stack->nr; i++)
+                               printf("..... %2d: %016" PRIx64 "\n",
+                                      (int)(i + kernel_callchain_nr + 1), 
lbr_stack->entries[i].from);
+               }
+
+       }
+
+       printf("... FP chain: nr:%" PRIu64 "\n", callchain->nr);
+
+       for (i = 0; i < callchain->nr; i++)
                printf("..... %2d: %016" PRIx64 "\n",
-                      i, sample->callchain->ips[i]);
+                      i, callchain->ips[i]);
 }
 
 static void branch_stack__printf(struct perf_sample *sample)
@@ -691,9 +735,9 @@ 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) && 
!has_branch_callstack(evsel))
                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