perf tools: Add Intel PT support for decoding MTC packets
authorAdrian Hunter <adrian.hunter@intel.com>
Fri, 17 Jul 2015 16:33:55 +0000 (19:33 +0300)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Mon, 24 Aug 2015 20:46:56 +0000 (17:46 -0300)
MTC packets provide finer grain timestamp information than TSC packets.
MTC packets record time using the hardware crystal clock (CTC) which is
related to TSC packets using a TMA packet.

This patch just adds decoder support.

Support for a default value and validation of values is provided by a
later patch. Also documentation is updated in a separate patch.

For details refer to the June 2015 or later Intel 64 and IA-32
Architectures SDM Chapter 36 Intel Processor Trace.

Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Link: http://lkml.kernel.org/r/1437150840-31811-21-git-send-email-adrian.hunter@intel.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
tools/perf/util/intel-pt-decoder/intel-pt-decoder.c
tools/perf/util/intel-pt-decoder/intel-pt-decoder.h

index 4a0e9fb..f7119a1 100644 (file)
@@ -85,7 +85,9 @@ struct intel_pt_decoder {
        const unsigned char *buf;
        size_t len;
        bool return_compression;
+       bool mtc_insn;
        bool pge;
+       bool have_tma;
        uint64_t pos;
        uint64_t last_ip;
        uint64_t ip;
@@ -94,6 +96,15 @@ struct intel_pt_decoder {
        uint64_t tsc_timestamp;
        uint64_t ref_timestamp;
        uint64_t ret_addr;
+       uint64_t ctc_timestamp;
+       uint64_t ctc_delta;
+       uint32_t last_mtc;
+       uint32_t tsc_ctc_ratio_n;
+       uint32_t tsc_ctc_ratio_d;
+       uint32_t tsc_ctc_mult;
+       uint32_t tsc_slip;
+       uint32_t ctc_rem_mask;
+       int mtc_shift;
        struct intel_pt_stack stack;
        enum intel_pt_pkt_state pkt_state;
        struct intel_pt_pkt packet;
@@ -149,6 +160,13 @@ static void intel_pt_setup_period(struct intel_pt_decoder *decoder)
        }
 }
 
+static uint64_t multdiv(uint64_t t, uint32_t n, uint32_t d)
+{
+       if (!d)
+               return 0;
+       return (t / d) * n + ((t % d) * n) / d;
+}
+
 struct intel_pt_decoder *intel_pt_decoder_new(struct intel_pt_params *params)
 {
        struct intel_pt_decoder *decoder;
@@ -175,6 +193,39 @@ struct intel_pt_decoder *intel_pt_decoder_new(struct intel_pt_params *params)
 
        intel_pt_setup_period(decoder);
 
+       decoder->mtc_shift = params->mtc_period;
+       decoder->ctc_rem_mask = (1 << decoder->mtc_shift) - 1;
+
+       decoder->tsc_ctc_ratio_n = params->tsc_ctc_ratio_n;
+       decoder->tsc_ctc_ratio_d = params->tsc_ctc_ratio_d;
+
+       if (!decoder->tsc_ctc_ratio_n)
+               decoder->tsc_ctc_ratio_d = 0;
+
+       if (decoder->tsc_ctc_ratio_d) {
+               if (!(decoder->tsc_ctc_ratio_n % decoder->tsc_ctc_ratio_d))
+                       decoder->tsc_ctc_mult = decoder->tsc_ctc_ratio_n /
+                                               decoder->tsc_ctc_ratio_d;
+
+               /*
+                * Allow for timestamps appearing to backwards because a TSC
+                * packet has slipped past a MTC packet, so allow 2 MTC ticks
+                * or ...
+                */
+               decoder->tsc_slip = multdiv(2 << decoder->mtc_shift,
+                                       decoder->tsc_ctc_ratio_n,
+                                       decoder->tsc_ctc_ratio_d);
+       }
+       /* ... or 0x100 paranoia */
+       if (decoder->tsc_slip < 0x100)
+               decoder->tsc_slip = 0x100;
+
+       intel_pt_log("timestamp: mtc_shift %u\n", decoder->mtc_shift);
+       intel_pt_log("timestamp: tsc_ctc_ratio_n %u\n", decoder->tsc_ctc_ratio_n);
+       intel_pt_log("timestamp: tsc_ctc_ratio_d %u\n", decoder->tsc_ctc_ratio_d);
+       intel_pt_log("timestamp: tsc_ctc_mult %u\n", decoder->tsc_ctc_mult);
+       intel_pt_log("timestamp: tsc_slip %#x\n", decoder->tsc_slip);
+
        return decoder;
 }
 
@@ -368,6 +419,7 @@ static inline void intel_pt_update_in_tx(struct intel_pt_decoder *decoder)
 static int intel_pt_bad_packet(struct intel_pt_decoder *decoder)
 {
        intel_pt_clear_tx_flags(decoder);
+       decoder->have_tma = false;
        decoder->pkt_len = 1;
        decoder->pkt_step = 1;
        intel_pt_decoder_log_packet(decoder);
@@ -400,6 +452,7 @@ static int intel_pt_get_data(struct intel_pt_decoder *decoder)
                decoder->pkt_state = INTEL_PT_STATE_NO_PSB;
                decoder->ref_timestamp = buffer.ref_timestamp;
                decoder->timestamp = 0;
+               decoder->have_tma = false;
                decoder->state.trace_nr = buffer.trace_nr;
                intel_pt_log("Reference timestamp 0x%" PRIx64 "\n",
                             decoder->ref_timestamp);
@@ -523,6 +576,7 @@ static uint64_t intel_pt_next_sample(struct intel_pt_decoder *decoder)
        case INTEL_PT_PERIOD_TICKS:
                return intel_pt_next_period(decoder);
        case INTEL_PT_PERIOD_NONE:
+       case INTEL_PT_PERIOD_MTC:
        default:
                return 0;
        }
@@ -542,6 +596,7 @@ static void intel_pt_sample_insn(struct intel_pt_decoder *decoder)
                decoder->last_masked_timestamp = masked_timestamp;
                break;
        case INTEL_PT_PERIOD_NONE:
+       case INTEL_PT_PERIOD_MTC:
        default:
                break;
        }
@@ -555,6 +610,9 @@ static int intel_pt_walk_insn(struct intel_pt_decoder *decoder,
        uint64_t max_insn_cnt, insn_cnt = 0;
        int err;
 
+       if (!decoder->mtc_insn)
+               decoder->mtc_insn = true;
+
        max_insn_cnt = intel_pt_next_sample(decoder);
 
        err = decoder->walk_insn(intel_pt_insn, &insn_cnt, &decoder->ip, ip,
@@ -861,6 +919,8 @@ static void intel_pt_calc_tsc_timestamp(struct intel_pt_decoder *decoder)
 {
        uint64_t timestamp;
 
+       decoder->have_tma = false;
+
        if (decoder->ref_timestamp) {
                timestamp = decoder->packet.payload |
                            (decoder->ref_timestamp & (0xffULL << 56));
@@ -878,17 +938,18 @@ static void intel_pt_calc_tsc_timestamp(struct intel_pt_decoder *decoder)
        } else if (decoder->timestamp) {
                timestamp = decoder->packet.payload |
                            (decoder->timestamp & (0xffULL << 56));
+               decoder->tsc_timestamp = timestamp;
                if (timestamp < decoder->timestamp &&
-                   decoder->timestamp - timestamp < 0x100) {
-                       intel_pt_log_to("ERROR: Suppressing backwards timestamp",
+                   decoder->timestamp - timestamp < decoder->tsc_slip) {
+                       intel_pt_log_to("Suppressing backwards timestamp",
                                        timestamp);
                        timestamp = decoder->timestamp;
                }
                while (timestamp < decoder->timestamp) {
                        intel_pt_log_to("Wraparound timestamp", timestamp);
                        timestamp += (1ULL << 56);
+                       decoder->tsc_timestamp = timestamp;
                }
-               decoder->tsc_timestamp = timestamp;
                decoder->timestamp = timestamp;
                decoder->timestamp_insn_cnt = 0;
        }
@@ -900,11 +961,73 @@ static int intel_pt_overflow(struct intel_pt_decoder *decoder)
 {
        intel_pt_log("ERROR: Buffer overflow\n");
        intel_pt_clear_tx_flags(decoder);
+       decoder->have_tma = false;
        decoder->pkt_state = INTEL_PT_STATE_ERR_RESYNC;
        decoder->overflow = true;
        return -EOVERFLOW;
 }
 
+static void intel_pt_calc_tma(struct intel_pt_decoder *decoder)
+{
+       uint32_t ctc = decoder->packet.payload;
+       uint32_t fc = decoder->packet.count;
+       uint32_t ctc_rem = ctc & decoder->ctc_rem_mask;
+
+       if (!decoder->tsc_ctc_ratio_d)
+               return;
+
+       decoder->last_mtc = (ctc >> decoder->mtc_shift) & 0xff;
+       decoder->ctc_timestamp = decoder->tsc_timestamp - fc;
+       if (decoder->tsc_ctc_mult) {
+               decoder->ctc_timestamp -= ctc_rem * decoder->tsc_ctc_mult;
+       } else {
+               decoder->ctc_timestamp -= multdiv(ctc_rem,
+                                                 decoder->tsc_ctc_ratio_n,
+                                                 decoder->tsc_ctc_ratio_d);
+       }
+       decoder->ctc_delta = 0;
+       decoder->have_tma = true;
+       intel_pt_log("CTC timestamp " x64_fmt " last MTC %#x  CTC rem %#x\n",
+                    decoder->ctc_timestamp, decoder->last_mtc, ctc_rem);
+}
+
+static void intel_pt_calc_mtc_timestamp(struct intel_pt_decoder *decoder)
+{
+       uint64_t timestamp;
+       uint32_t mtc, mtc_delta;
+
+       if (!decoder->have_tma)
+               return;
+
+       mtc = decoder->packet.payload;
+
+       if (mtc > decoder->last_mtc)
+               mtc_delta = mtc - decoder->last_mtc;
+       else
+               mtc_delta = mtc + 256 - decoder->last_mtc;
+
+       decoder->ctc_delta += mtc_delta << decoder->mtc_shift;
+
+       if (decoder->tsc_ctc_mult) {
+               timestamp = decoder->ctc_timestamp +
+                           decoder->ctc_delta * decoder->tsc_ctc_mult;
+       } else {
+               timestamp = decoder->ctc_timestamp +
+                           multdiv(decoder->ctc_delta,
+                                   decoder->tsc_ctc_ratio_n,
+                                   decoder->tsc_ctc_ratio_d);
+       }
+
+       if (timestamp < decoder->timestamp)
+               intel_pt_log("Suppressing MTC timestamp " x64_fmt " less than current timestamp " x64_fmt "\n",
+                            timestamp, decoder->timestamp);
+       else
+               decoder->timestamp = timestamp;
+
+       decoder->timestamp_insn_cnt = 0;
+       decoder->last_mtc = mtc;
+}
+
 /* Walk PSB+ packets when already in sync. */
 static int intel_pt_walk_psbend(struct intel_pt_decoder *decoder)
 {
@@ -926,6 +1049,7 @@ static int intel_pt_walk_psbend(struct intel_pt_decoder *decoder)
                case INTEL_PT_TRACESTOP:
                case INTEL_PT_BAD:
                case INTEL_PT_PSB:
+                       decoder->have_tma = false;
                        intel_pt_log("ERROR: Unexpected packet\n");
                        return -EAGAIN;
 
@@ -937,6 +1061,7 @@ static int intel_pt_walk_psbend(struct intel_pt_decoder *decoder)
                        break;
 
                case INTEL_PT_TMA:
+                       intel_pt_calc_tma(decoder);
                        break;
 
                case INTEL_PT_CBR:
@@ -961,6 +1086,9 @@ static int intel_pt_walk_psbend(struct intel_pt_decoder *decoder)
                        break;
 
                case INTEL_PT_MTC:
+                       intel_pt_calc_mtc_timestamp(decoder);
+                       if (decoder->period_type == INTEL_PT_PERIOD_MTC)
+                               decoder->state.type |= INTEL_PT_INSTRUCTION;
                        break;
 
                case INTEL_PT_CYC:
@@ -1048,6 +1176,9 @@ static int intel_pt_walk_fup_tip(struct intel_pt_decoder *decoder)
                        break;
 
                case INTEL_PT_MTC:
+                       intel_pt_calc_mtc_timestamp(decoder);
+                       if (decoder->period_type == INTEL_PT_PERIOD_MTC)
+                               decoder->state.type |= INTEL_PT_INSTRUCTION;
                        break;
 
                case INTEL_PT_CYC:
@@ -1159,13 +1290,31 @@ next:
                        break;
 
                case INTEL_PT_MTC:
-                       break;
+                       intel_pt_calc_mtc_timestamp(decoder);
+                       if (decoder->period_type != INTEL_PT_PERIOD_MTC)
+                               break;
+                       /*
+                        * Ensure that there has been an instruction since the
+                        * last MTC.
+                        */
+                       if (!decoder->mtc_insn)
+                               break;
+                       decoder->mtc_insn = false;
+                       /* Ensure that there is a timestamp */
+                       if (!decoder->timestamp)
+                               break;
+                       decoder->state.type = INTEL_PT_INSTRUCTION;
+                       decoder->state.from_ip = decoder->ip;
+                       decoder->state.to_ip = 0;
+                       decoder->mtc_insn = false;
+                       return 0;
 
                case INTEL_PT_TSC:
                        intel_pt_calc_tsc_timestamp(decoder);
                        break;
 
                case INTEL_PT_TMA:
+                       intel_pt_calc_tma(decoder);
                        break;
 
                case INTEL_PT_CYC:
@@ -1237,6 +1386,7 @@ static int intel_pt_walk_psb(struct intel_pt_decoder *decoder)
                        break;
 
                case INTEL_PT_MTC:
+                       intel_pt_calc_mtc_timestamp(decoder);
                        break;
 
                case INTEL_PT_TSC:
@@ -1244,6 +1394,7 @@ static int intel_pt_walk_psb(struct intel_pt_decoder *decoder)
                        break;
 
                case INTEL_PT_TMA:
+                       intel_pt_calc_tma(decoder);
                        break;
 
                case INTEL_PT_CYC:
@@ -1267,6 +1418,7 @@ static int intel_pt_walk_psb(struct intel_pt_decoder *decoder)
 
                case INTEL_PT_TRACESTOP:
                case INTEL_PT_TNT:
+                       decoder->have_tma = false;
                        intel_pt_log("ERROR: Unexpected packet\n");
                        if (decoder->ip)
                                decoder->pkt_state = INTEL_PT_STATE_ERR4;
@@ -1329,6 +1481,7 @@ static int intel_pt_walk_to_ip(struct intel_pt_decoder *decoder)
                        break;
 
                case INTEL_PT_MTC:
+                       intel_pt_calc_mtc_timestamp(decoder);
                        break;
 
                case INTEL_PT_TSC:
@@ -1336,6 +1489,7 @@ static int intel_pt_walk_to_ip(struct intel_pt_decoder *decoder)
                        break;
 
                case INTEL_PT_TMA:
+                       intel_pt_calc_tma(decoder);
                        break;
 
                case INTEL_PT_CYC:
index 56cc47b..02c38fe 100644 (file)
@@ -36,6 +36,7 @@ enum intel_pt_period_type {
        INTEL_PT_PERIOD_NONE,
        INTEL_PT_PERIOD_INSTRUCTIONS,
        INTEL_PT_PERIOD_TICKS,
+       INTEL_PT_PERIOD_MTC,
 };
 
 enum {