[PATCH 2/2] perf tools: Fix Intel PT getting stuck in a loop

From: Adrian Hunter
Date: Wed Jun 10 2015 - 08:51:34 EST


Check for being stuck in a loop. That can happen if a
decoder error results in the decoder erroneously setting
the ip to an address that is itself in an infinite loop
that consumes no packets. The only way to be in a loop
that consumes no packets is if it consists of unconditional
branches. So the check for being stuck is if we see
a repeating cycle of consecutive unconditional branches.

Signed-off-by: Adrian Hunter <adrian.hunter@xxxxxxxxx>
---
.../perf/util/intel-pt-decoder/intel-pt-decoder.c | 42 ++++++++++++++++++++++
.../perf/util/intel-pt-decoder/intel-pt-decoder.h | 1 +
2 files changed, 43 insertions(+)

diff --git a/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c b/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c
index 748a7a078313..e8ff6573ecec 100644
--- a/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c
+++ b/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c
@@ -37,6 +37,9 @@

#define INTEL_PT_RETURN 1

+/* Maximum number of loops with no packets consumed i.e. stuck in a loop */
+#define INTEL_PT_MAX_LOOPS 10000
+
struct intel_pt_blk {
struct intel_pt_blk *prev;
uint64_t ip[INTEL_PT_BLK_SIZE];
@@ -114,6 +117,10 @@ struct intel_pt_decoder {
unsigned int fup_tx_flags;
unsigned int tx_flags;
uint64_t timestamp_insn_cnt;
+ uint64_t stuck_ip;
+ int no_progress;
+ int stuck_ip_prd;
+ int stuck_ip_cnt;
const unsigned char *next_buf;
size_t next_len;
unsigned char temp_buf[INTEL_PT_PKT_MAX_SZ];
@@ -263,6 +270,8 @@ static int intel_pt_ext_err(int code)
return INTEL_PT_ERR_OVR;
case -ENOSPC:
return INTEL_PT_ERR_LOST;
+ case -ELOOP:
+ return INTEL_PT_ERR_NELOOP;
default:
return INTEL_PT_ERR_UNK;
}
@@ -278,6 +287,7 @@ static const char *intel_pt_err_msgs[] = {
[INTEL_PT_ERR_OVR] = "Overflow packet",
[INTEL_PT_ERR_LOST] = "Lost trace data",
[INTEL_PT_ERR_UNK] = "Unknown error!",
+ [INTEL_PT_ERR_NELOOP] = "Never-ending loop",
};

int intel_pt__strerror(int code, char *buf, size_t buflen)
@@ -550,6 +560,7 @@ static int intel_pt_walk_insn(struct intel_pt_decoder *decoder,
decoder->period_insn_cnt += insn_cnt;

if (err) {
+ decoder->no_progress = 0;
decoder->pkt_state = INTEL_PT_STATE_ERR2;
intel_pt_log_at("ERROR: Failed to get instruction",
decoder->ip);
@@ -589,13 +600,44 @@ static int intel_pt_walk_insn(struct intel_pt_decoder *decoder,
}

if (intel_pt_insn->branch == INTEL_PT_BR_UNCONDITIONAL) {
+ int cnt = decoder->no_progress++;
+
decoder->state.from_ip = decoder->ip;
decoder->ip += intel_pt_insn->length +
intel_pt_insn->rel;
decoder->state.to_ip = decoder->ip;
err = INTEL_PT_RETURN;
+
+ /*
+ * Check for being stuck in a loop. This can happen if a
+ * decoder error results in the decoder erroneously setting the
+ * ip to an address that is itself in an infinite loop that
+ * consumes no packets. When that happens, there must be an
+ * unconditional branch.
+ */
+ if (cnt) {
+ if (cnt == 1) {
+ decoder->stuck_ip = decoder->state.to_ip;
+ decoder->stuck_ip_prd = 1;
+ decoder->stuck_ip_cnt = 1;
+ } else if (cnt > INTEL_PT_MAX_LOOPS ||
+ decoder->state.to_ip == decoder->stuck_ip) {
+ intel_pt_log_at("ERROR: Never-ending loop",
+ decoder->state.to_ip);
+ decoder->pkt_state = INTEL_PT_STATE_ERR_RESYNC;
+ err = -ELOOP;
+ goto out;
+ } else if (!--decoder->stuck_ip_cnt) {
+ decoder->stuck_ip_prd += 1;
+ decoder->stuck_ip_cnt = decoder->stuck_ip_prd;
+ decoder->stuck_ip = decoder->state.to_ip;
+ }
+ }
+ goto out_no_progress;
}
out:
+ decoder->no_progress = 0;
+out_no_progress:
decoder->state.insn_op = intel_pt_insn->op;
decoder->state.insn_len = intel_pt_insn->length;

diff --git a/tools/perf/util/intel-pt-decoder/intel-pt-decoder.h b/tools/perf/util/intel-pt-decoder/intel-pt-decoder.h
index 955263adfd8d..706c6bccc57e 100644
--- a/tools/perf/util/intel-pt-decoder/intel-pt-decoder.h
+++ b/tools/perf/util/intel-pt-decoder/intel-pt-decoder.h
@@ -48,6 +48,7 @@ enum {
INTEL_PT_ERR_OVR,
INTEL_PT_ERR_LOST,
INTEL_PT_ERR_UNK,
+ INTEL_PT_ERR_NELOOP,
INTEL_PT_ERR_MAX,
};

--
1.9.1

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