perf intel-pt: Fix "Unexpected indirect branch" error
authorAdrian Hunter <adrian.hunter@intel.com>
Thu, 31 May 2018 10:23:45 +0000 (13:23 +0300)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 3 Jul 2018 09:25:01 +0000 (11:25 +0200)
commit 9fb523363f6e3984457fee95bb7019395384ffa7 upstream.

Some Atom CPUs can produce FUP packets that contain NLIP (next linear
instruction pointer) instead of CLIP (current linear instruction
pointer).  That will result in "Unexpected indirect branch" errors. Fix
by comparing IP to NLIP in that case.

Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
Cc: stable@vger.kernel.org
Link: http://lkml.kernel.org/r/1527762225-26024-5-git-send-email-adrian.hunter@intel.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
tools/perf/util/intel-pt-decoder/intel-pt-decoder.c
tools/perf/util/intel-pt-decoder/intel-pt-decoder.h
tools/perf/util/intel-pt.c

index 881d7c5e5e2a3c8114eaccb9524c16f7494482aa..d404bed7003aacee1079af492ba3f20334edc06e 100644 (file)
@@ -113,6 +113,7 @@ struct intel_pt_decoder {
        bool have_cyc;
        bool fixup_last_mtc;
        bool have_last_ip;
+       enum intel_pt_param_flags flags;
        uint64_t pos;
        uint64_t last_ip;
        uint64_t ip;
@@ -226,6 +227,8 @@ struct intel_pt_decoder *intel_pt_decoder_new(struct intel_pt_params *params)
        decoder->return_compression = params->return_compression;
        decoder->branch_enable      = params->branch_enable;
 
+       decoder->flags              = params->flags;
+
        decoder->period             = params->period;
        decoder->period_type        = params->period_type;
 
@@ -1097,6 +1100,15 @@ static bool intel_pt_fup_event(struct intel_pt_decoder *decoder)
        return ret;
 }
 
+static inline bool intel_pt_fup_with_nlip(struct intel_pt_decoder *decoder,
+                                         struct intel_pt_insn *intel_pt_insn,
+                                         uint64_t ip, int err)
+{
+       return decoder->flags & INTEL_PT_FUP_WITH_NLIP && !err &&
+              intel_pt_insn->branch == INTEL_PT_BR_INDIRECT &&
+              ip == decoder->ip + intel_pt_insn->length;
+}
+
 static int intel_pt_walk_fup(struct intel_pt_decoder *decoder)
 {
        struct intel_pt_insn intel_pt_insn;
@@ -1109,10 +1121,11 @@ static int intel_pt_walk_fup(struct intel_pt_decoder *decoder)
                err = intel_pt_walk_insn(decoder, &intel_pt_insn, ip);
                if (err == INTEL_PT_RETURN)
                        return 0;
-               if (err == -EAGAIN) {
+               if (err == -EAGAIN ||
+                   intel_pt_fup_with_nlip(decoder, &intel_pt_insn, ip, err)) {
                        if (intel_pt_fup_event(decoder))
                                return 0;
-                       return err;
+                       return -EAGAIN;
                }
                decoder->set_fup_tx_flags = false;
                if (err)
index fc1752d50019c680f0e29a7e2176e716015ff632..51c18d67f4ca855d02f19be640490ab7a9e74356 100644 (file)
@@ -60,6 +60,14 @@ enum {
        INTEL_PT_ERR_MAX,
 };
 
+enum intel_pt_param_flags {
+       /*
+        * FUP packet can contain next linear instruction pointer instead of
+        * current linear instruction pointer.
+        */
+       INTEL_PT_FUP_WITH_NLIP  = 1 << 0,
+};
+
 struct intel_pt_state {
        enum intel_pt_sample_type type;
        int err;
@@ -106,6 +114,7 @@ struct intel_pt_params {
        unsigned int mtc_period;
        uint32_t tsc_ctc_ratio_n;
        uint32_t tsc_ctc_ratio_d;
+       enum intel_pt_param_flags flags;
 };
 
 struct intel_pt_decoder;
index a39fc1200e2d9a0cacb0c4b737d533a61ad1b35b..3b118fa9da89bee1680b5662497854b49320905e 100644 (file)
@@ -784,6 +784,7 @@ static struct intel_pt_queue *intel_pt_alloc_queue(struct intel_pt *pt,
                                                   unsigned int queue_nr)
 {
        struct intel_pt_params params = { .get_trace = 0, };
+       struct perf_env *env = pt->machine->env;
        struct intel_pt_queue *ptq;
 
        ptq = zalloc(sizeof(struct intel_pt_queue));
@@ -865,6 +866,9 @@ static struct intel_pt_queue *intel_pt_alloc_queue(struct intel_pt *pt,
                }
        }
 
+       if (env->cpuid && !strncmp(env->cpuid, "GenuineIntel,6,92,", 18))
+               params.flags |= INTEL_PT_FUP_WITH_NLIP;
+
        ptq->decoder = intel_pt_decoder_new(&params);
        if (!ptq->decoder)
                goto out_free;