#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <errno.h>
#include <stdint.h>
#include <inttypes.h>
#include <linux/compiler.h>
#include <linux/string.h>
#include <linux/zalloc.h>
#include "../auxtrace.h"
#include "intel-pt-insn-decoder.h"
#include "intel-pt-pkt-decoder.h"
#include "intel-pt-decoder.h"
#include "intel-pt-log.h"
#define BITULL(x) (1ULL << (x))
#define INTEL_PT_CYC_ENABLE BITULL(1)
#define INTEL_PT_CYC_THRESHOLD (BITULL(22) | BITULL(21) | BITULL(20) | BITULL(19))
#define INTEL_PT_CYC_THRESHOLD_SHIFT 19
#define INTEL_PT_BLK_SIZE 1024
#define BIT63 (((uint64_t)1 << 63))
#define SEVEN_BYTES 0xffffffffffffffULL
#define NO_VMCS 0xffffffffffULL
#define INTEL_PT_RETURN 1
#define INTEL_PT_MAX_LOOPS 100000
struct intel_pt_blk {
struct intel_pt_blk *prev;
uint64_t ip[INTEL_PT_BLK_SIZE];
};
struct intel_pt_stack {
struct intel_pt_blk *blk;
struct intel_pt_blk *spare;
int pos;
};
enum intel_pt_p_once {
INTEL_PT_PRT_ONCE_UNK_VMCS,
INTEL_PT_PRT_ONCE_ERANGE,
};
enum intel_pt_pkt_state {
INTEL_PT_STATE_NO_PSB,
INTEL_PT_STATE_NO_IP,
INTEL_PT_STATE_ERR_RESYNC,
INTEL_PT_STATE_IN_SYNC,
INTEL_PT_STATE_TNT_CONT,
INTEL_PT_STATE_TNT,
INTEL_PT_STATE_TIP,
INTEL_PT_STATE_TIP_PGD,
INTEL_PT_STATE_FUP,
INTEL_PT_STATE_FUP_NO_TIP,
INTEL_PT_STATE_FUP_IN_PSB,
INTEL_PT_STATE_RESAMPLE,
INTEL_PT_STATE_VM_TIME_CORRELATION,
};
static inline bool intel_pt_sample_time(enum intel_pt_pkt_state pkt_state)
{
switch (pkt_state) {
case INTEL_PT_STATE_NO_PSB:
case INTEL_PT_STATE_NO_IP:
case INTEL_PT_STATE_ERR_RESYNC:
case INTEL_PT_STATE_IN_SYNC:
case INTEL_PT_STATE_TNT_CONT:
case INTEL_PT_STATE_RESAMPLE:
case INTEL_PT_STATE_VM_TIME_CORRELATION:
return true;
case INTEL_PT_STATE_TNT:
case INTEL_PT_STATE_TIP:
case INTEL_PT_STATE_TIP_PGD:
case INTEL_PT_STATE_FUP:
case INTEL_PT_STATE_FUP_NO_TIP:
case INTEL_PT_STATE_FUP_IN_PSB:
return false;
default:
return true;
};
}
#ifdef INTEL_PT_STRICT
#define INTEL_PT_STATE_ERR1 INTEL_PT_STATE_NO_PSB
#define INTEL_PT_STATE_ERR2 INTEL_PT_STATE_NO_PSB
#define INTEL_PT_STATE_ERR3 INTEL_PT_STATE_NO_PSB
#define INTEL_PT_STATE_ERR4 INTEL_PT_STATE_NO_PSB
#else
#define INTEL_PT_STATE_ERR1 (decoder->pkt_state)
#define INTEL_PT_STATE_ERR2 INTEL_PT_STATE_NO_IP
#define INTEL_PT_STATE_ERR3 INTEL_PT_STATE_ERR_RESYNC
#define INTEL_PT_STATE_ERR4 INTEL_PT_STATE_IN_SYNC
#endif
struct intel_pt_decoder {
int (*get_trace)(struct intel_pt_buffer *buffer, void *data);
int (*walk_insn)(struct intel_pt_insn *intel_pt_insn,
uint64_t *insn_cnt_ptr, uint64_t *ip, uint64_t to_ip,
uint64_t max_insn_cnt, void *data);
bool (*pgd_ip)(uint64_t ip, void *data);
int (*lookahead)(void *data, intel_pt_lookahead_cb_t cb, void *cb_data);
struct intel_pt_vmcs_info *(*findnew_vmcs_info)(void *data, uint64_t vmcs);
void *data;
struct intel_pt_state state;
const unsigned char *buf;
size_t len;
bool return_compression;
bool branch_enable;
bool mtc_insn;
bool pge;
bool have_tma;
bool have_cyc;
bool fixup_last_mtc;
bool have_last_ip;
bool in_psb;
bool hop;
bool leap;
bool emulated_ptwrite;
bool vm_time_correlation;
bool vm_tm_corr_dry_run;
bool vm_tm_corr_reliable;
bool vm_tm_corr_same_buf;
bool vm_tm_corr_continuous;
bool nr;
bool next_nr;
bool iflag;
bool next_iflag;
enum intel_pt_param_flags flags;
uint64_t pos;
uint64_t last_ip;
uint64_t ip;
uint64_t pip_payload;
uint64_t timestamp;
uint64_t tsc_timestamp;
uint64_t ref_timestamp;
uint64_t buf_timestamp;
uint64_t sample_timestamp;
uint64_t ret_addr;
uint64_t ctc_timestamp;
uint64_t ctc_delta;
uint64_t cycle_cnt;
uint64_t cyc_ref_timestamp;
uint64_t first_timestamp;
uint64_t last_reliable_timestamp;
uint64_t vmcs;
uint64_t print_once;
uint64_t last_ctc;
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;
enum intel_pt_pkt_ctx pkt_ctx;
enum intel_pt_pkt_ctx prev_pkt_ctx;
enum intel_pt_blk_type blk_type;
int blk_type_pos;
struct intel_pt_pkt packet;
struct intel_pt_pkt tnt;
int pkt_step;
int pkt_len;
int last_packet_type;
unsigned int cbr;
unsigned int cbr_seen;
unsigned int max_non_turbo_ratio;
double max_non_turbo_ratio_fp;
double cbr_cyc_to_tsc;
double calc_cyc_to_tsc;
bool have_calc_cyc_to_tsc;
int exec_mode;
unsigned int insn_bytes;
uint64_t period;
enum intel_pt_period_type period_type;
uint64_t tot_insn_cnt;
uint64_t period_insn_cnt;
uint64_t period_mask;
uint64_t period_ticks;
uint64_t last_masked_timestamp;
uint64_t tot_cyc_cnt;
uint64_t sample_tot_cyc_cnt;
uint64_t base_cyc_cnt;
uint64_t cyc_cnt_timestamp;
uint64_t ctl;
uint64_t cyc_threshold;
double tsc_to_cyc;
bool continuous_period;
bool overflow;
bool set_fup_tx_flags;
bool set_fup_ptw;
bool set_fup_mwait;
bool set_fup_pwre;
bool set_fup_exstop;
bool set_fup_bep;
bool set_fup_cfe_ip;
bool set_fup_cfe;
bool set_fup_mode_exec;
bool sample_cyc;
unsigned int fup_tx_flags;
unsigned int tx_flags;
uint64_t fup_ptw_payload;
uint64_t fup_mwait_payload;
uint64_t fup_pwre_payload;
uint64_t cbr_payload;
uint64_t timestamp_insn_cnt;
uint64_t sample_insn_cnt;
uint64_t stuck_ip;
struct intel_pt_pkt fup_cfe_pkt;
int max_loops;
int no_progress;
int stuck_ip_prd;
int stuck_ip_cnt;
uint64_t psb_ip;
const unsigned char *next_buf;
size_t next_len;
unsigned char temp_buf[INTEL_PT_PKT_MAX_SZ];
int evd_cnt;
struct intel_pt_evd evd[INTEL_PT_MAX_EVDS];
};
static uint64_t intel_pt_lower_power_of_2(uint64_t x)
{
int i;
for (i = 0; x != 1; i++)
x >>= 1;
return x << i;
}
__printf(1, 2)
static void p_log(const char *fmt, ...)
{
char buf[512];
va_list args;
va_start(args, fmt);
vsnprintf(buf, sizeof(buf), fmt, args);
va_end(args);
fprintf(stderr, "%s\n", buf);
intel_pt_log("%s\n", buf);
}
static bool intel_pt_print_once(struct intel_pt_decoder *decoder,
enum intel_pt_p_once id)
{
uint64_t bit = 1ULL << id;
if (decoder->print_once & bit)
return false;
decoder->print_once |= bit;
return true;
}
static uint64_t intel_pt_cyc_threshold(uint64_t ctl)
{
if (!(ctl & INTEL_PT_CYC_ENABLE))
return 0;
return (ctl & INTEL_PT_CYC_THRESHOLD) >> INTEL_PT_CYC_THRESHOLD_SHIFT;
}
static void intel_pt_setup_period(struct intel_pt_decoder *decoder)
{
if (decoder->period_type == INTEL_PT_PERIOD_TICKS) {
uint64_t period;
period = intel_pt_lower_power_of_2(decoder->period);
decoder->period_mask = ~(period - 1);
decoder->period_ticks = period;
}
}
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;
if (!params->get_trace || !params->walk_insn)
return NULL;
decoder = zalloc(sizeof(struct intel_pt_decoder));
if (!decoder)
return NULL;
decoder->get_trace = params->get_trace;
decoder->walk_insn = params->walk_insn;
decoder->pgd_ip = params->pgd_ip;
decoder->lookahead = params->lookahead;
decoder->findnew_vmcs_info = params->findnew_vmcs_info;
decoder->data = params->data;
decoder->return_compression = params->return_compression;
decoder->branch_enable = params->branch_enable;
decoder->hop = params->quick >= 1;
decoder->leap = params->quick >= 2;
decoder->vm_time_correlation = params->vm_time_correlation;
decoder->vm_tm_corr_dry_run = params->vm_tm_corr_dry_run;
decoder->first_timestamp = params->first_timestamp;
decoder->last_reliable_timestamp = params->first_timestamp;
decoder->max_loops = params->max_loops ? params->max_loops : INTEL_PT_MAX_LOOPS;
decoder->flags = params->flags;
decoder->ctl = params->ctl;
decoder->period = params->period;
decoder->period_type = params->period_type;
decoder->max_non_turbo_ratio = params->max_non_turbo_ratio;
decoder->max_non_turbo_ratio_fp = params->max_non_turbo_ratio;
decoder->cyc_threshold = intel_pt_cyc_threshold(decoder->ctl);
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;
}
decoder->tsc_slip = 0x10000;
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);
if (decoder->hop)
intel_pt_log("Hop mode: decoding FUP and TIPs, but not TNT\n");
return decoder;
}
void intel_pt_set_first_timestamp(struct intel_pt_decoder *decoder,
uint64_t first_timestamp)
{
decoder->first_timestamp = first_timestamp;
}
static void intel_pt_pop_blk(struct intel_pt_stack *stack)
{
struct intel_pt_blk *blk = stack->blk;
stack->blk = blk->prev;
if (!stack->spare)
stack->spare = blk;
else
free(blk);
}
static uint64_t intel_pt_pop(struct intel_pt_stack *stack)
{
if (!stack->pos) {
if (!stack->blk)
return 0;
intel_pt_pop_blk(stack);
if (!stack->blk)
return 0;
stack->pos = INTEL_PT_BLK_SIZE;
}
return stack->blk->ip[--stack->pos];
}
static int intel_pt_alloc_blk(struct intel_pt_stack *stack)
{
struct intel_pt_blk *blk;
if (stack->spare) {
blk = stack->spare;
stack->spare = NULL;
} else {
blk = malloc(sizeof(struct intel_pt_blk));
if (!blk)
return -ENOMEM;
}
blk->prev = stack->blk;
stack->blk = blk;
stack->pos = 0;
return 0;
}
static int intel_pt_push(struct intel_pt_stack *stack, uint64_t ip)
{
int err;
if (!stack->blk || stack->pos == INTEL_PT_BLK_SIZE) {
err = intel_pt_alloc_blk(stack);
if (err)
return err;
}
stack->blk->ip[stack->pos++] = ip;
return 0;
}
static void intel_pt_clear_stack(struct intel_pt_stack *stack)
{
while (stack->blk)
intel_pt_pop_blk(stack);
stack->pos = 0;
}
static void intel_pt_free_stack(struct intel_pt_stack *stack)
{
intel_pt_clear_stack(stack);
zfree(&stack->blk);
zfree(&stack->spare);
}
void intel_pt_decoder_free(struct intel_pt_decoder *decoder)
{
intel_pt_free_stack(&decoder->stack);
free(decoder);
}
static int intel_pt_ext_err(int code)
{
switch (code) {
case -ENOMEM:
return INTEL_PT_ERR_NOMEM;
case -ENOSYS:
return INTEL_PT_ERR_INTERN;
case -EBADMSG:
return INTEL_PT_ERR_BADPKT;
case -ENODATA:
return INTEL_PT_ERR_NODATA;
case -EILSEQ:
return INTEL_PT_ERR_NOINSN;
case -ENOENT:
return INTEL_PT_ERR_MISMAT;
case -EOVERFLOW:
return INTEL_PT_ERR_OVR;
case -ENOSPC:
return INTEL_PT_ERR_LOST;
case -ELOOP:
return INTEL_PT_ERR_NELOOP;
case -ECONNRESET:
return INTEL_PT_ERR_EPTW;
default:
return INTEL_PT_ERR_UNK;
}
}
static const char *intel_pt_err_msgs[] = {
[INTEL_PT_ERR_NOMEM] = "Memory allocation failed",
[INTEL_PT_ERR_INTERN] = "Internal error",
[INTEL_PT_ERR_BADPKT] = "Bad packet",
[INTEL_PT_ERR_NODATA] = "No more data",
[INTEL_PT_ERR_NOINSN] = "Failed to get instruction",
[INTEL_PT_ERR_MISMAT] = "Trace doesn't match instruction",
[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 (refer perf config intel-pt.max-loops)",
[INTEL_PT_ERR_EPTW] = "Broken emulated ptwrite",
};
int intel_pt__strerror(int code, char *buf, size_t buflen)
{
if (code < 1 || code >= INTEL_PT_ERR_MAX)
code = INTEL_PT_ERR_UNK;
strlcpy(buf, intel_pt_err_msgs[code], buflen);
return 0;
}
static uint64_t intel_pt_calc_ip(const struct intel_pt_pkt *packet,
uint64_t last_ip)
{
uint64_t ip;
switch (packet->count) {
case 1:
ip = (last_ip & (uint64_t)0xffffffffffff0000ULL) |
packet->payload;
break;
case 2:
ip = (last_ip & (uint64_t)0xffffffff00000000ULL) |
packet->payload;
break;
case 3:
ip = packet->payload;
if (ip & (uint64_t)0x800000000000ULL)
ip |= (uint64_t)0xffff000000000000ULL;
break;
case 4:
ip = (last_ip & (uint64_t)0xffff000000000000ULL) |
packet->payload;
break;
case 6:
ip = packet->payload;
break;
default:
return 0;
}
return ip;
}
static inline void intel_pt_set_last_ip(struct intel_pt_decoder *decoder)
{
decoder->last_ip = intel_pt_calc_ip(&decoder->packet, decoder->last_ip);
decoder->have_last_ip = true;
}
static inline void intel_pt_set_ip(struct intel_pt_decoder *decoder)
{
intel_pt_set_last_ip(decoder);
decoder->ip = decoder->last_ip;
}
static void intel_pt_decoder_log_packet(struct intel_pt_decoder *decoder)
{
intel_pt_log_packet(&decoder->packet, decoder->pkt_len, decoder->pos,
decoder->buf);
}
static int intel_pt_bug(struct intel_pt_decoder *decoder)
{
intel_pt_log("ERROR: Internal error\n");
decoder->pkt_state = INTEL_PT_STATE_NO_PSB;
return -ENOSYS;
}
static inline void intel_pt_clear_tx_flags(struct intel_pt_decoder *decoder)
{
decoder->tx_flags = 0;
}
static inline void intel_pt_update_in_tx(struct intel_pt_decoder *decoder)
{
decoder->tx_flags = decoder->packet.payload & INTEL_PT_IN_TX;
}
static inline void intel_pt_update_pip(struct intel_pt_decoder *decoder)
{
decoder->pip_payload = decoder->packet.payload;
}
static inline void intel_pt_update_nr(struct intel_pt_decoder *decoder)
{
decoder->next_nr = decoder->pip_payload & 1;
}
static inline void intel_pt_set_nr(struct intel_pt_decoder *decoder)
{
decoder->nr = decoder->pip_payload & 1;
decoder->next_nr = decoder->nr;
}
static inline void intel_pt_set_pip(struct intel_pt_decoder *decoder)
{
intel_pt_update_pip(decoder);
intel_pt_set_nr(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);
if (decoder->pkt_state != INTEL_PT_STATE_NO_PSB) {
intel_pt_log("ERROR: Bad packet\n");
decoder->pkt_state = INTEL_PT_STATE_ERR1;
}
return -EBADMSG;
}
static inline void intel_pt_update_sample_time(struct intel_pt_decoder *decoder)
{
decoder->sample_timestamp = decoder->timestamp;
decoder->sample_insn_cnt = decoder->timestamp_insn_cnt;
decoder->state.cycles = decoder->tot_cyc_cnt;
}
static void intel_pt_reposition(struct intel_pt_decoder *decoder)
{
decoder->ip = 0;
decoder->pkt_state = INTEL_PT_STATE_NO_PSB;
decoder->timestamp = 0;
decoder->have_tma = false;
}
static int intel_pt_get_data(struct intel_pt_decoder *decoder, bool reposition)
{
struct intel_pt_buffer buffer = { .buf = 0, };
int ret;
decoder->pkt_step = 0;
intel_pt_log("Getting more data\n");
ret = decoder->get_trace(&buffer, decoder->data);
if (ret)
return ret;
decoder->buf = buffer.buf;
decoder->len = buffer.len;
if (!decoder->len) {
intel_pt_log("No more data\n");
return -ENODATA;
}
decoder->buf_timestamp = buffer.ref_timestamp;
if (!buffer.consecutive || reposition) {
intel_pt_reposition(decoder);
decoder->ref_timestamp = buffer.ref_timestamp;
decoder->state.trace_nr = buffer.trace_nr;
decoder->vm_tm_corr_same_buf = false;
intel_pt_log("Reference timestamp 0x%" PRIx64 "\n",
decoder->ref_timestamp);
return -ENOLINK;
}
return 0;
}
static int intel_pt_get_next_data(struct intel_pt_decoder *decoder,
bool reposition)
{
if (!decoder->next_buf)
return intel_pt_get_data(decoder, reposition);
decoder->buf = decoder->next_buf;
decoder->len = decoder->next_len;
decoder->next_buf = 0;
decoder->next_len = 0;
return 0;
}
static int intel_pt_get_split_packet(struct intel_pt_decoder *decoder)
{
unsigned char *buf = decoder->temp_buf;
size_t old_len, len, n;
int ret;
old_len = decoder->len;
len = decoder->len;
memcpy(buf, decoder->buf, len);
ret = intel_pt_get_data(decoder, false);
if (ret) {
decoder->pos += old_len;
return ret < 0 ? ret : -EINVAL;
}
n = INTEL_PT_PKT_MAX_SZ - len;
if (n > decoder->len)
n = decoder->len;
memcpy(buf + len, decoder->buf, n);
len += n;
decoder->prev_pkt_ctx = decoder->pkt_ctx;
ret = intel_pt_get_packet(buf, len, &decoder->packet, &decoder->pkt_ctx);
if (ret < (int)old_len) {
decoder->next_buf = decoder->buf;
decoder->next_len = decoder->len;
decoder->buf = buf;
decoder->len = old_len;
return intel_pt_bad_packet(decoder);
}
decoder->next_buf = decoder->buf + (ret - old_len);
decoder->next_len = decoder->len - (ret - old_len);
decoder->buf = buf;
decoder->len = ret;
return ret;
}
struct intel_pt_pkt_info {
struct intel_pt_decoder *decoder;
struct intel_pt_pkt packet;
uint64_t pos;
int pkt_len;
int last_packet_type;
void *data;
};
typedef int (*intel_pt_pkt_cb_t)(struct intel_pt_pkt_info *pkt_info);
static int intel_pt_pkt_lookahead(struct intel_pt_decoder *decoder,
intel_pt_pkt_cb_t cb, void *data)
{
struct intel_pt_pkt_info pkt_info;
const unsigned char *buf = decoder->buf;
enum intel_pt_pkt_ctx pkt_ctx = decoder->pkt_ctx;
size_t len = decoder->len;
int ret;
pkt_info.decoder = decoder;
pkt_info.pos = decoder->pos;
pkt_info.pkt_len = decoder->pkt_step;
pkt_info.last_packet_type = decoder->last_packet_type;
pkt_info.data = data;
while (1) {
do {
pkt_info.pos += pkt_info.pkt_len;
buf += pkt_info.pkt_len;
len -= pkt_info.pkt_len;
if (!len)
return INTEL_PT_NEED_MORE_BYTES;
ret = intel_pt_get_packet(buf, len, &pkt_info.packet,
&pkt_ctx);
if (!ret)
return INTEL_PT_NEED_MORE_BYTES;
if (ret < 0)
return ret;
pkt_info.pkt_len = ret;
} while (pkt_info.packet.type == INTEL_PT_PAD);
ret = cb(&pkt_info);
if (ret)
return 0;
pkt_info.last_packet_type = pkt_info.packet.type;
}
}
struct intel_pt_calc_cyc_to_tsc_info {
uint64_t cycle_cnt;
unsigned int cbr;
uint32_t last_mtc;
uint64_t ctc_timestamp;
uint64_t ctc_delta;
uint64_t tsc_timestamp;
uint64_t timestamp;
bool have_tma;
bool fixup_last_mtc;
bool from_mtc;
double cbr_cyc_to_tsc;
};
static void intel_pt_fixup_last_mtc(uint32_t mtc, int mtc_shift,
uint32_t *last_mtc)
{
uint32_t first_missing_bit = 1U << (16 - mtc_shift);
uint32_t mask = ~(first_missing_bit - 1);
*last_mtc |= mtc & mask;
if (*last_mtc >= mtc) {
*last_mtc -= first_missing_bit;
*last_mtc &= 0xff;
}
}
static int intel_pt_calc_cyc_cb(struct intel_pt_pkt_info *pkt_info)
{
struct intel_pt_decoder *decoder = pkt_info->decoder;
struct intel_pt_calc_cyc_to_tsc_info *data = pkt_info->data;
uint64_t timestamp;
double cyc_to_tsc;
unsigned int cbr;
uint32_t mtc, mtc_delta, ctc, fc, ctc_rem;
switch (pkt_info->packet.type) {
case INTEL_PT_TNT:
case INTEL_PT_TIP_PGE:
case INTEL_PT_TIP:
case INTEL_PT_FUP:
case INTEL_PT_PSB:
case INTEL_PT_PIP:
case INTEL_PT_MODE_EXEC:
case INTEL_PT_MODE_TSX:
case INTEL_PT_PSBEND:
case INTEL_PT_PAD:
case INTEL_PT_VMCS:
case INTEL_PT_MNT:
case INTEL_PT_PTWRITE:
case INTEL_PT_PTWRITE_IP:
case INTEL_PT_BBP:
case INTEL_PT_BIP:
case INTEL_PT_BEP:
case INTEL_PT_BEP_IP:
case INTEL_PT_CFE:
case INTEL_PT_CFE_IP:
case INTEL_PT_EVD:
return 0;
case INTEL_PT_MTC:
if (!data->have_tma)
return 0;
mtc = pkt_info->packet.payload;
if (decoder->mtc_shift > 8 && data->fixup_last_mtc) {
data->fixup_last_mtc = false;
intel_pt_fixup_last_mtc(mtc, decoder->mtc_shift,
&data->last_mtc);
}
if (mtc > data->last_mtc)
mtc_delta = mtc - data->last_mtc;
else
mtc_delta = mtc + 256 - data->last_mtc;
data->ctc_delta += mtc_delta << decoder->mtc_shift;
data->last_mtc = mtc;
if (decoder->tsc_ctc_mult) {
timestamp = data->ctc_timestamp +
data->ctc_delta * decoder->tsc_ctc_mult;
} else {
timestamp = data->ctc_timestamp +
multdiv(data->ctc_delta,
decoder->tsc_ctc_ratio_n,
decoder->tsc_ctc_ratio_d);
}
if (timestamp < data->timestamp)
return 1;
if (pkt_info->last_packet_type != INTEL_PT_CYC) {
data->timestamp = timestamp;
return 0;
}
break;
case INTEL_PT_TSC:
if (data->from_mtc)
return 1;
timestamp = pkt_info->packet.payload |
(data->timestamp & (0xffULL << 56));
if (data->from_mtc && timestamp < data->timestamp &&
data->timestamp - timestamp < decoder->tsc_slip)
return 1;
if (timestamp < data->timestamp)
timestamp += (1ULL << 56);
if (pkt_info->last_packet_type != INTEL_PT_CYC) {
if (data->from_mtc)
return 1;
data->tsc_timestamp = timestamp;
data->timestamp = timestamp;
return 0;
}
break;
case INTEL_PT_TMA:
if (data->from_mtc)
return 1;
if (!decoder->tsc_ctc_ratio_d)
return 0;
ctc = pkt_info->packet.payload;
fc = pkt_info->packet.count;
ctc_rem = ctc & decoder->ctc_rem_mask;
data->last_mtc = (ctc >> decoder->mtc_shift) & 0xff;
data->ctc_timestamp = data->tsc_timestamp - fc;
if (decoder->tsc_ctc_mult) {
data->ctc_timestamp -= ctc_rem * decoder->tsc_ctc_mult;
} else {
data->ctc_timestamp -=
multdiv(ctc_rem, decoder->tsc_ctc_ratio_n,
decoder->tsc_ctc_ratio_d);
}
data->ctc_delta = 0;
data->have_tma = true;
data->fixup_last_mtc = true;
return 0;
case INTEL_PT_CYC:
data->cycle_cnt += pkt_info->packet.payload;
return 0;
case INTEL_PT_CBR:
cbr = pkt_info->packet.payload;
if (data->cbr && data->cbr != cbr)
return 1;
data->cbr = cbr;
data->cbr_cyc_to_tsc = decoder->max_non_turbo_ratio_fp / cbr;
return 0;
case INTEL_PT_TIP_PGD:
case INTEL_PT_TRACESTOP:
case INTEL_PT_EXSTOP:
case INTEL_PT_EXSTOP_IP:
case INTEL_PT_MWAIT:
case INTEL_PT_PWRE:
case INTEL_PT_PWRX:
case INTEL_PT_OVF:
case INTEL_PT_BAD:
default:
return 1;
}
if (!data->cbr && decoder->cbr) {
data->cbr = decoder->cbr;
data->cbr_cyc_to_tsc = decoder->cbr_cyc_to_tsc;
}
if (!data->cycle_cnt)
return 1;
cyc_to_tsc = (double)(timestamp - decoder->timestamp) / data->cycle_cnt;
if (data->cbr && cyc_to_tsc > data->cbr_cyc_to_tsc &&
cyc_to_tsc / data->cbr_cyc_to_tsc > 1.25) {
intel_pt_log("Timestamp: calculated %g TSC ticks per cycle too big (c.f. CBR-based value %g), pos " x64_fmt "\n",
cyc_to_tsc, data->cbr_cyc_to_tsc, pkt_info->pos);
return 1;
}
decoder->calc_cyc_to_tsc = cyc_to_tsc;
decoder->have_calc_cyc_to_tsc = true;
if (data->cbr) {
intel_pt_log("Timestamp: calculated %g TSC ticks per cycle c.f. CBR-based value %g, pos " x64_fmt "\n",
cyc_to_tsc, data->cbr_cyc_to_tsc, pkt_info->pos);
} else {
intel_pt_log("Timestamp: calculated %g TSC ticks per cycle c.f. unknown CBR-based value, pos " x64_fmt "\n",
cyc_to_tsc, pkt_info->pos);
}
return 1;
}
static void intel_pt_calc_cyc_to_tsc(struct intel_pt_decoder *decoder,
bool from_mtc)
{
struct intel_pt_calc_cyc_to_tsc_info data = {
.cycle_cnt = 0,
.cbr = 0,
.last_mtc = decoder->last_mtc,
.ctc_timestamp = decoder->ctc_timestamp,
.ctc_delta = decoder->ctc_delta,
.tsc_timestamp = decoder->tsc_timestamp,
.timestamp = decoder->timestamp,
.have_tma = decoder->have_tma,
.fixup_last_mtc = decoder->fixup_last_mtc,
.from_mtc = from_mtc,
.cbr_cyc_to_tsc = 0,
};
if (!from_mtc)
return;
intel_pt_pkt_lookahead(decoder, intel_pt_calc_cyc_cb, &data);
}
static int intel_pt_get_next_packet(struct intel_pt_decoder *decoder)
{
int ret;
decoder->last_packet_type = decoder->packet.type;
do {
decoder->pos += decoder->pkt_step;
decoder->buf += decoder->pkt_step;
decoder->len -= decoder->pkt_step;
if (!decoder->len) {
ret = intel_pt_get_next_data(decoder, false);
if (ret)
return ret;
}
decoder->prev_pkt_ctx = decoder->pkt_ctx;
ret = intel_pt_get_packet(decoder->buf, decoder->len,
&decoder->packet, &decoder->pkt_ctx);
if (ret == INTEL_PT_NEED_MORE_BYTES && BITS_PER_LONG == 32 &&
decoder->len < INTEL_PT_PKT_MAX_SZ && !decoder->next_buf) {
ret = intel_pt_get_split_packet(decoder);
if (ret < 0)
return ret;
}
if (ret <= 0)
return intel_pt_bad_packet(decoder);
decoder->pkt_len = ret;
decoder->pkt_step = ret;
intel_pt_decoder_log_packet(decoder);
} while (decoder->packet.type == INTEL_PT_PAD);
return 0;
}
static uint64_t intel_pt_next_period(struct intel_pt_decoder *decoder)
{
uint64_t timestamp, masked_timestamp;
timestamp = decoder->timestamp + decoder->timestamp_insn_cnt;
masked_timestamp = timestamp & decoder->period_mask;
if (decoder->continuous_period) {
if (masked_timestamp > decoder->last_masked_timestamp)
return 1;
} else {
timestamp += 1;
masked_timestamp = timestamp & decoder->period_mask;
if (masked_timestamp > decoder->last_masked_timestamp) {
decoder->last_masked_timestamp = masked_timestamp;
decoder->continuous_period = true;
}
}
if (masked_timestamp < decoder->last_masked_timestamp)
return decoder->period_ticks;
return decoder->period_ticks - (timestamp - masked_timestamp);
}
static uint64_t intel_pt_next_sample(struct intel_pt_decoder *decoder)
{
switch (decoder->period_type) {
case INTEL_PT_PERIOD_INSTRUCTIONS:
return decoder->period - decoder->period_insn_cnt;
case INTEL_PT_PERIOD_TICKS:
return intel_pt_next_period(decoder);
case INTEL_PT_PERIOD_NONE:
case INTEL_PT_PERIOD_MTC:
default:
return 0;
}
}
static void intel_pt_sample_insn(struct intel_pt_decoder *decoder)
{
uint64_t timestamp, masked_timestamp;
switch (decoder->period_type) {
case INTEL_PT_PERIOD_INSTRUCTIONS:
decoder->period_insn_cnt = 0;
break;
case INTEL_PT_PERIOD_TICKS:
timestamp = decoder->timestamp + decoder->timestamp_insn_cnt;
masked_timestamp = timestamp & decoder->period_mask;
if (masked_timestamp > decoder->last_masked_timestamp)
decoder->last_masked_timestamp = masked_timestamp;
else
decoder->last_masked_timestamp += decoder->period_ticks;
break;
case INTEL_PT_PERIOD_NONE:
case INTEL_PT_PERIOD_MTC:
default:
break;
}
decoder->state.type |= INTEL_PT_INSTRUCTION;
}
static void intel_pt_sample_fup_insn(struct intel_pt_decoder *decoder)
{
struct intel_pt_insn intel_pt_insn;
uint64_t max_insn_cnt, insn_cnt = 0;
int err;
decoder->state.insn_op = INTEL_PT_OP_OTHER;
decoder->state.insn_len = 0;
if (!decoder->branch_enable || !decoder->pge || decoder->hop ||
decoder->ip != decoder->last_ip)
return;
if (!decoder->mtc_insn)
decoder->mtc_insn = true;
max_insn_cnt = intel_pt_next_sample(decoder);
if (max_insn_cnt != 1)
return;
err = decoder->walk_insn(&intel_pt_insn, &insn_cnt, &decoder->ip,
0, max_insn_cnt, decoder->data);
if (err)
return;
if (intel_pt_insn.branch != INTEL_PT_BR_NO_BRANCH) {
intel_pt_log_at("ERROR: Unexpected branch at FUP instruction", decoder->ip);
return;
}
decoder->tot_insn_cnt += insn_cnt;
decoder->timestamp_insn_cnt += insn_cnt;
decoder->sample_insn_cnt += insn_cnt;
decoder->period_insn_cnt += insn_cnt;
intel_pt_sample_insn(decoder);
decoder->state.type |= INTEL_PT_INSTRUCTION;
decoder->ip += intel_pt_insn.length;
}
static int intel_pt_walk_insn(struct intel_pt_decoder *decoder,
struct intel_pt_insn *intel_pt_insn, uint64_t ip)
{
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,
max_insn_cnt, decoder->data);
decoder->tot_insn_cnt += insn_cnt;
decoder->timestamp_insn_cnt += insn_cnt;
decoder->sample_insn_cnt += insn_cnt;
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);
if (err == -ENOENT)
return -ENOLINK;
return -EILSEQ;
}
if (ip && decoder->ip == ip) {
err = -EAGAIN;
goto out;
}
if (max_insn_cnt && insn_cnt >= max_insn_cnt)
intel_pt_sample_insn(decoder);
if (intel_pt_insn->branch == INTEL_PT_BR_NO_BRANCH) {
decoder->state.type = INTEL_PT_INSTRUCTION;
decoder->state.from_ip = decoder->ip;
decoder->state.to_ip = 0;
decoder->ip += intel_pt_insn->length;
err = INTEL_PT_RETURN;
goto out;
}
if (intel_pt_insn->op == INTEL_PT_OP_CALL) {
if (intel_pt_insn->branch != INTEL_PT_BR_UNCONDITIONAL ||
intel_pt_insn->rel) {
err = intel_pt_push(&decoder->stack, decoder->ip +
intel_pt_insn->length);
if (err)
goto out;
}
} else if (intel_pt_insn->op == INTEL_PT_OP_RET) {
decoder->ret_addr = intel_pt_pop(&decoder->stack);
}
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;
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 > decoder->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;
memcpy(decoder->state.insn, intel_pt_insn->buf,
INTEL_PT_INSN_BUF_SZ);
if (decoder->tx_flags & INTEL_PT_IN_TX)
decoder->state.flags |= INTEL_PT_IN_TX;
return err;
}
static void intel_pt_mode_exec_status(struct intel_pt_decoder *decoder)
{
bool iflag = decoder->packet.count & INTEL_PT_IFLAG;
decoder->exec_mode = decoder->packet.payload;
decoder->iflag = iflag;
decoder->next_iflag = iflag;
decoder->state.from_iflag = iflag;
decoder->state.to_iflag = iflag;
}
static void intel_pt_mode_exec(struct intel_pt_decoder *decoder)
{
bool iflag = decoder->packet.count & INTEL_PT_IFLAG;
decoder->exec_mode = decoder->packet.payload;
decoder->next_iflag = iflag;
}
static void intel_pt_sample_iflag(struct intel_pt_decoder *decoder)
{
decoder->state.type |= INTEL_PT_IFLAG_CHG;
decoder->state.from_iflag = decoder->iflag;
decoder->state.to_iflag = decoder->next_iflag;
decoder->iflag = decoder->next_iflag;
}
static void intel_pt_sample_iflag_chg(struct intel_pt_decoder *decoder)
{
if (decoder->iflag != decoder->next_iflag)
intel_pt_sample_iflag(decoder);
}
static void intel_pt_clear_fup_event(struct intel_pt_decoder *decoder)
{
decoder->set_fup_tx_flags = false;
decoder->set_fup_ptw = false;
decoder->set_fup_mwait = false;
decoder->set_fup_pwre = false;
decoder->set_fup_exstop = false;
decoder->set_fup_bep = false;
decoder->set_fup_cfe_ip = false;
decoder->set_fup_cfe = false;
decoder->evd_cnt = 0;
decoder->set_fup_mode_exec = false;
decoder->iflag = decoder->next_iflag;
}
static bool intel_pt_fup_event(struct intel_pt_decoder *decoder, bool no_tip)
{
enum intel_pt_sample_type type = decoder->state.type;
bool sample_fup_insn = false;
bool ret = false;
decoder->state.type &= ~INTEL_PT_BRANCH;
decoder->state.insn_op = INTEL_PT_OP_OTHER;
decoder->state.insn_len = 0;
if (decoder->set_fup_cfe_ip || decoder->set_fup_cfe) {
bool ip = decoder->set_fup_cfe_ip;
decoder->set_fup_cfe_ip = false;
decoder->set_fup_cfe = false;
decoder->state.type |= INTEL_PT_EVT;
if (!ip && decoder->pge)
decoder->state.type |= INTEL_PT_BRANCH;
decoder->state.cfe_type = decoder->fup_cfe_pkt.count;
decoder->state.cfe_vector = decoder->fup_cfe_pkt.payload;
decoder->state.evd_cnt = decoder->evd_cnt;
decoder->state.evd = decoder->evd;
decoder->evd_cnt = 0;
if (ip || decoder->pge)
decoder->state.flags |= INTEL_PT_FUP_IP;
ret = true;
}
if (decoder->set_fup_mode_exec) {
decoder->set_fup_mode_exec = false;
intel_pt_sample_iflag(decoder);
sample_fup_insn = no_tip;
ret = true;
}
if (decoder->set_fup_tx_flags) {
decoder->set_fup_tx_flags = false;
decoder->tx_flags = decoder->fup_tx_flags;
decoder->state.type |= INTEL_PT_TRANSACTION;
if (decoder->fup_tx_flags & INTEL_PT_ABORT_TX)
decoder->state.type |= INTEL_PT_BRANCH;
decoder->state.flags = decoder->fup_tx_flags;
ret = true;
}
if (decoder->set_fup_ptw) {
decoder->set_fup_ptw = false;
decoder->state.type |= INTEL_PT_PTW;
decoder->state.flags |= INTEL_PT_FUP_IP;
decoder->state.ptw_payload = decoder->fup_ptw_payload;
ret = true;
}
if (decoder->set_fup_mwait) {
decoder->set_fup_mwait = false;
decoder->state.type |= INTEL_PT_MWAIT_OP;
decoder->state.mwait_payload = decoder->fup_mwait_payload;
ret = true;
}
if (decoder->set_fup_pwre) {
decoder->set_fup_pwre = false;
decoder->state.type |= INTEL_PT_PWR_ENTRY;
decoder->state.pwre_payload = decoder->fup_pwre_payload;
ret = true;
}
if (decoder->set_fup_exstop) {
decoder->set_fup_exstop = false;
decoder->state.type |= INTEL_PT_EX_STOP;
decoder->state.flags |= INTEL_PT_FUP_IP;
ret = true;
}
if (decoder->set_fup_bep) {
decoder->set_fup_bep = false;
decoder->state.type |= INTEL_PT_BLK_ITEMS;
ret = true;
}
if (decoder->overflow) {
decoder->overflow = false;
if (!ret && !decoder->pge) {
if (decoder->hop) {
decoder->state.type = 0;
decoder->pkt_state = INTEL_PT_STATE_RESAMPLE;
}
decoder->pge = true;
decoder->state.type |= INTEL_PT_BRANCH | INTEL_PT_TRACE_BEGIN;
decoder->state.from_ip = 0;
decoder->state.to_ip = decoder->ip;
return true;
}
}
if (ret) {
decoder->state.from_ip = decoder->ip;
decoder->state.to_ip = 0;
if (sample_fup_insn)
intel_pt_sample_fup_insn(decoder);
} else {
decoder->state.type = type;
}
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;
uint64_t ip;
int err;
ip = decoder->last_ip;
while (1) {
err = intel_pt_walk_insn(decoder, &intel_pt_insn, ip);
if (err == INTEL_PT_RETURN)
return 0;
if (err == -EAGAIN ||
intel_pt_fup_with_nlip(decoder, &intel_pt_insn, ip, err)) {
bool no_tip = decoder->pkt_state != INTEL_PT_STATE_FUP;
decoder->pkt_state = INTEL_PT_STATE_IN_SYNC;
if (intel_pt_fup_event(decoder, no_tip) && no_tip)
return 0;
return -EAGAIN;
}
decoder->set_fup_tx_flags = false;
if (err)
return err;
if (intel_pt_insn.branch == INTEL_PT_BR_INDIRECT) {
intel_pt_log_at("ERROR: Unexpected indirect branch",
decoder->ip);
decoder->pkt_state = INTEL_PT_STATE_ERR_RESYNC;
return -ENOENT;
}
if (intel_pt_insn.branch == INTEL_PT_BR_CONDITIONAL) {
intel_pt_log_at("ERROR: Unexpected conditional branch",
decoder->ip);
decoder->pkt_state = INTEL_PT_STATE_ERR_RESYNC;
return -ENOENT;
}
intel_pt_bug(decoder);
}
}
static int intel_pt_walk_tip(struct intel_pt_decoder *decoder)
{
struct intel_pt_insn intel_pt_insn;
int err;
err = intel_pt_walk_insn(decoder, &intel_pt_insn, 0);
if (err == INTEL_PT_RETURN &&
decoder->pgd_ip &&
decoder->pkt_state == INTEL_PT_STATE_TIP_PGD &&
(decoder->state.type & INTEL_PT_BRANCH) &&
decoder->pgd_ip(decoder->state.to_ip, decoder->data)) {
decoder->no_progress = 0;
decoder->pge = false;
decoder->continuous_period = false;
decoder->pkt_state = INTEL_PT_STATE_IN_SYNC;
decoder->state.type |= INTEL_PT_TRACE_END;
intel_pt_update_nr(decoder);
return 0;
}
if (err == INTEL_PT_RETURN)
return 0;
if (err)
return err;
intel_pt_update_nr(decoder);
intel_pt_sample_iflag_chg(decoder);
if (intel_pt_insn.branch == INTEL_PT_BR_INDIRECT) {
if (decoder->pkt_state == INTEL_PT_STATE_TIP_PGD) {
decoder->pge = false;
decoder->continuous_period = false;
decoder->pkt_state = INTEL_PT_STATE_IN_SYNC;
decoder->state.from_ip = decoder->ip;
if (decoder->packet.count == 0) {
decoder->state.to_ip = 0;
} else {
decoder->state.to_ip = decoder->last_ip;
decoder->ip = decoder->last_ip;
}
decoder->state.type |= INTEL_PT_TRACE_END;
} else {
decoder->pkt_state = INTEL_PT_STATE_IN_SYNC;
decoder->state.from_ip = decoder->ip;
if (decoder->packet.count == 0) {
decoder->state.to_ip = 0;
} else {
decoder->state.to_ip = decoder->last_ip;
decoder->ip = decoder->last_ip;
}
}
return 0;
}
if (intel_pt_insn.branch == INTEL_PT_BR_CONDITIONAL) {
uint64_t to_ip = decoder->ip + intel_pt_insn.length +
intel_pt_insn.rel;
if (decoder->pgd_ip &&
decoder->pkt_state == INTEL_PT_STATE_TIP_PGD &&
decoder->pgd_ip(to_ip, decoder->data)) {
decoder->pge = false;
decoder->continuous_period = false;
decoder->pkt_state = INTEL_PT_STATE_IN_SYNC;
decoder->ip = to_ip;
decoder->state.from_ip = decoder->ip;
decoder->state.to_ip = to_ip;
decoder->state.type |= INTEL_PT_TRACE_END;
return 0;
}
intel_pt_log_at("ERROR: Conditional branch when expecting indirect branch",
decoder->ip);
decoder->pkt_state = INTEL_PT_STATE_ERR_RESYNC;
return -ENOENT;
}
return intel_pt_bug(decoder);
}
struct eptw_data {
int bit_countdown;
uint64_t payload;
};
static int intel_pt_eptw_lookahead_cb(struct intel_pt_pkt_info *pkt_info)
{
struct eptw_data *data = pkt_info->data;
int nr_bits;
switch (pkt_info->packet.type) {
case INTEL_PT_PAD:
case INTEL_PT_MNT:
case INTEL_PT_MODE_EXEC:
case INTEL_PT_MODE_TSX:
case INTEL_PT_MTC:
case INTEL_PT_FUP:
case INTEL_PT_CYC:
case INTEL_PT_CBR:
case INTEL_PT_TSC:
case INTEL_PT_TMA:
case INTEL_PT_PIP:
case INTEL_PT_VMCS:
case INTEL_PT_PSB:
case INTEL_PT_PSBEND:
case INTEL_PT_PTWRITE:
case INTEL_PT_PTWRITE_IP:
case INTEL_PT_EXSTOP:
case INTEL_PT_EXSTOP_IP:
case INTEL_PT_MWAIT:
case INTEL_PT_PWRE:
case INTEL_PT_PWRX:
case INTEL_PT_BBP:
case INTEL_PT_BIP:
case INTEL_PT_BEP:
case INTEL_PT_BEP_IP:
case INTEL_PT_CFE:
case INTEL_PT_CFE_IP:
case INTEL_PT_EVD:
break;
case INTEL_PT_TNT:
nr_bits = data->bit_countdown;
if (nr_bits > pkt_info->packet.count)
nr_bits = pkt_info->packet.count;
data->payload <<= nr_bits;
data->payload |= pkt_info->packet.payload >> (64 - nr_bits);
data->bit_countdown -= nr_bits;
return !data->bit_countdown;
case INTEL_PT_TIP_PGE:
case INTEL_PT_TIP_PGD:
case INTEL_PT_TIP:
case INTEL_PT_BAD:
case INTEL_PT_OVF:
case INTEL_PT_TRACESTOP:
default:
return 1;
}
return 0;
}
static int intel_pt_emulated_ptwrite(struct intel_pt_decoder *decoder)
{
int n = 64 - decoder->tnt.count;
struct eptw_data data = {
.bit_countdown = n,
.payload = decoder->tnt.payload >> n,
};
decoder->emulated_ptwrite = false;
intel_pt_log("Emulated ptwrite detected\n");
intel_pt_pkt_lookahead(decoder, intel_pt_eptw_lookahead_cb, &data);
if (data.bit_countdown)
return -ECONNRESET;
decoder->state.type = INTEL_PT_PTW;
decoder->state.from_ip = decoder->ip;
decoder->state.to_ip = 0;
decoder->state.ptw_payload = data.payload;
return 0;
}
static int intel_pt_walk_tnt(struct intel_pt_decoder *decoder)
{
struct intel_pt_insn intel_pt_insn;
int err;
while (1) {
if (decoder->emulated_ptwrite)
return intel_pt_emulated_ptwrite(decoder);
err = intel_pt_walk_insn(decoder, &intel_pt_insn, 0);
if (err == INTEL_PT_RETURN) {
decoder->emulated_ptwrite = intel_pt_insn.emulated_ptwrite;
return 0;
}
if (err) {
decoder->emulated_ptwrite = false;
return err;
}
if (intel_pt_insn.op == INTEL_PT_OP_RET) {
if (!decoder->return_compression) {
intel_pt_log_at("ERROR: RET when expecting conditional branch",
decoder->ip);
decoder->pkt_state = INTEL_PT_STATE_ERR3;
return -ENOENT;
}
if (!decoder->ret_addr) {
intel_pt_log_at("ERROR: Bad RET compression (stack empty)",
decoder->ip);
decoder->pkt_state = INTEL_PT_STATE_ERR3;
return -ENOENT;
}
if (!(decoder->tnt.payload & BIT63)) {
intel_pt_log_at("ERROR: Bad RET compression (TNT=N)",
decoder->ip);
decoder->pkt_state = INTEL_PT_STATE_ERR3;
return -ENOENT;
}
decoder->tnt.count -= 1;
if (decoder->tnt.count)
decoder->pkt_state = INTEL_PT_STATE_TNT_CONT;
else
decoder->pkt_state = INTEL_PT_STATE_IN_SYNC;
decoder->tnt.payload <<= 1;
decoder->state.from_ip = decoder->ip;
decoder->ip = decoder->ret_addr;
decoder->state.to_ip = decoder->ip;
return 0;
}
if (intel_pt_insn.branch == INTEL_PT_BR_INDIRECT) {
err = intel_pt_get_next_packet(decoder);
if (err)
return err;
if (decoder->packet.type != INTEL_PT_TIP ||
decoder->packet.count == 0) {
intel_pt_log_at("ERROR: Missing deferred TIP for indirect branch",
decoder->ip);
decoder->pkt_state = INTEL_PT_STATE_ERR3;
decoder->pkt_step = 0;
return -ENOENT;
}
intel_pt_set_last_ip(decoder);
decoder->state.from_ip = decoder->ip;
decoder->state.to_ip = decoder->last_ip;
decoder->ip = decoder->last_ip;
intel_pt_update_nr(decoder);
intel_pt_sample_iflag_chg(decoder);
return 0;
}
if (intel_pt_insn.branch == INTEL_PT_BR_CONDITIONAL) {
decoder->tnt.count -= 1;
if (decoder->tnt.count)
decoder->pkt_state = INTEL_PT_STATE_TNT_CONT;
else
decoder->pkt_state = INTEL_PT_STATE_IN_SYNC;
if (decoder->tnt.payload & BIT63) {
decoder->tnt.payload <<= 1;
decoder->state.from_ip = decoder->ip;
decoder->ip += intel_pt_insn.length +
intel_pt_insn.rel;
decoder->state.to_ip = decoder->ip;
return 0;
}
if (decoder->state.type & INTEL_PT_INSTRUCTION) {
decoder->tnt.payload <<= 1;
decoder->state.type = INTEL_PT_INSTRUCTION;
decoder->state.from_ip = decoder->ip;
decoder->state.to_ip = 0;
decoder->ip += intel_pt_insn.length;
return 0;
}
decoder->sample_cyc = false;
decoder->ip += intel_pt_insn.length;
if (!decoder->tnt.count) {
intel_pt_update_sample_time(decoder);
return -EAGAIN;
}
decoder->tnt.payload <<= 1;
continue;
}
return intel_pt_bug(decoder);
}
}
static int intel_pt_mode_tsx(struct intel_pt_decoder *decoder, bool *no_tip)
{
unsigned int fup_tx_flags;
int err;
fup_tx_flags = decoder->packet.payload &
(INTEL_PT_IN_TX | INTEL_PT_ABORT_TX);
err = intel_pt_get_next_packet(decoder);
if (err)
return err;
if (decoder->packet.type == INTEL_PT_FUP) {
decoder->fup_tx_flags = fup_tx_flags;
decoder->set_fup_tx_flags = true;
if (!(decoder->fup_tx_flags & INTEL_PT_ABORT_TX))
*no_tip = true;
} else {
intel_pt_log_at("ERROR: Missing FUP after MODE.TSX",
decoder->pos);
intel_pt_update_in_tx(decoder);
}
return 0;
}
static int intel_pt_evd(struct intel_pt_decoder *decoder)
{
if (decoder->evd_cnt >= INTEL_PT_MAX_EVDS) {
intel_pt_log_at("ERROR: Too many EVD packets", decoder->pos);
return -ENOSYS;
}
decoder->evd[decoder->evd_cnt++] = (struct intel_pt_evd){
.type = decoder->packet.count,
.payload = decoder->packet.payload,
};
return 0;
}
static uint64_t intel_pt_8b_tsc(uint64_t timestamp, uint64_t ref_timestamp)
{
timestamp |= (ref_timestamp & (0xffULL << 56));
if (timestamp < ref_timestamp) {
if (ref_timestamp - timestamp > (1ULL << 55))
timestamp += (1ULL << 56);
} else {
if (timestamp - ref_timestamp > (1ULL << 55))
timestamp -= (1ULL << 56);
}
return timestamp;
}
static bool intel_pt_time_in_range(struct intel_pt_decoder *decoder,
uint64_t timestamp)
{
uint64_t max_timestamp = decoder->buf_timestamp;
if (!max_timestamp) {
max_timestamp = decoder->last_reliable_timestamp +
0x400000000ULL;
}
return timestamp >= decoder->last_reliable_timestamp &&
timestamp < decoder->buf_timestamp;
}
static void intel_pt_calc_tsc_timestamp(struct intel_pt_decoder *decoder)
{
uint64_t timestamp;
bool bad = false;
decoder->have_tma = false;
if (decoder->ref_timestamp) {
timestamp = intel_pt_8b_tsc(decoder->packet.payload,
decoder->ref_timestamp);
decoder->tsc_timestamp = timestamp;
decoder->timestamp = timestamp;
decoder->ref_timestamp = 0;
decoder->timestamp_insn_cnt = 0;
} else if (decoder->timestamp) {
timestamp = decoder->packet.payload |
(decoder->timestamp & (0xffULL << 56));
decoder->tsc_timestamp = timestamp;
if (timestamp < decoder->timestamp &&
decoder->timestamp - timestamp < decoder->tsc_slip) {
intel_pt_log_to("Suppressing backwards timestamp",
timestamp);
timestamp = decoder->timestamp;
}
if (timestamp < decoder->timestamp) {
if (!decoder->buf_timestamp ||
(timestamp + (1ULL << 56) < decoder->buf_timestamp)) {
intel_pt_log_to("Wraparound timestamp", timestamp);
timestamp += (1ULL << 56);
decoder->tsc_timestamp = timestamp;
} else {
intel_pt_log_to("Suppressing bad timestamp", timestamp);
timestamp = decoder->timestamp;
bad = true;
}
}
if (decoder->vm_time_correlation &&
(bad || !intel_pt_time_in_range(decoder, timestamp)) &&
intel_pt_print_once(decoder, INTEL_PT_PRT_ONCE_ERANGE))
p_log("Timestamp out of range");
decoder->timestamp = timestamp;
decoder->timestamp_insn_cnt = 0;
}
if (decoder->last_packet_type == INTEL_PT_CYC) {
decoder->cyc_ref_timestamp = decoder->timestamp;
decoder->cycle_cnt = 0;
decoder->have_calc_cyc_to_tsc = false;
intel_pt_calc_cyc_to_tsc(decoder, false);
}
intel_pt_log_to("Setting timestamp", decoder->timestamp);
}
static int intel_pt_overflow(struct intel_pt_decoder *decoder)
{
intel_pt_log("ERROR: Buffer overflow\n");
intel_pt_clear_tx_flags(decoder);
intel_pt_set_nr(decoder);
decoder->timestamp_insn_cnt = 0;
decoder->pkt_state = INTEL_PT_STATE_IN_SYNC;
decoder->state.from_ip = decoder->ip;
decoder->ip = 0;
decoder->pge = false;
intel_pt_clear_fup_event(decoder);
decoder->overflow = true;
return -EOVERFLOW;
}
static inline void intel_pt_mtc_cyc_cnt_pge(struct intel_pt_decoder *decoder)
{
if (decoder->have_cyc)
return;
decoder->cyc_cnt_timestamp = decoder->timestamp;
decoder->base_cyc_cnt = decoder->tot_cyc_cnt;
}
static inline void intel_pt_mtc_cyc_cnt_cbr(struct intel_pt_decoder *decoder)
{
decoder->tsc_to_cyc = decoder->cbr / decoder->max_non_turbo_ratio_fp;
if (decoder->pge)
intel_pt_mtc_cyc_cnt_pge(decoder);
}
static inline void intel_pt_mtc_cyc_cnt_upd(struct intel_pt_decoder *decoder)
{
uint64_t tot_cyc_cnt, tsc_delta;
if (decoder->have_cyc)
return;
decoder->sample_cyc = true;
if (!decoder->pge || decoder->timestamp <= decoder->cyc_cnt_timestamp)
return;
tsc_delta = decoder->timestamp - decoder->cyc_cnt_timestamp;
tot_cyc_cnt = tsc_delta * decoder->tsc_to_cyc + decoder->base_cyc_cnt;
if (tot_cyc_cnt > decoder->tot_cyc_cnt)
decoder->tot_cyc_cnt = tot_cyc_cnt;
}
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;
if (decoder->pge && !decoder->in_psb)
intel_pt_mtc_cyc_cnt_pge(decoder);
else
intel_pt_mtc_cyc_cnt_upd(decoder);
decoder->last_mtc = (ctc >> decoder->mtc_shift) & 0xff;
decoder->last_ctc = ctc - ctc_rem;
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;
decoder->fixup_last_mtc = 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 (decoder->mtc_shift > 8 && decoder->fixup_last_mtc) {
decoder->fixup_last_mtc = false;
intel_pt_fixup_last_mtc(mtc, decoder->mtc_shift,
&decoder->last_mtc);
}
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;
intel_pt_mtc_cyc_cnt_upd(decoder);
decoder->timestamp_insn_cnt = 0;
decoder->last_mtc = mtc;
if (decoder->last_packet_type == INTEL_PT_CYC) {
decoder->cyc_ref_timestamp = decoder->timestamp;
decoder->cycle_cnt = 0;
decoder->have_calc_cyc_to_tsc = false;
intel_pt_calc_cyc_to_tsc(decoder, true);
}
intel_pt_log_to("Setting timestamp", decoder->timestamp);
}
static void intel_pt_calc_cbr(struct intel_pt_decoder *decoder)
{
unsigned int cbr = decoder->packet.payload & 0xff;
decoder->cbr_payload = decoder->packet.payload;
if (decoder->cbr == cbr)
return;
decoder->cbr = cbr;
decoder->cbr_cyc_to_tsc = decoder->max_non_turbo_ratio_fp / cbr;
decoder->cyc_ref_timestamp = decoder->timestamp;
decoder->cycle_cnt = 0;
intel_pt_mtc_cyc_cnt_cbr(decoder);
}
static void intel_pt_calc_cyc_timestamp(struct intel_pt_decoder *decoder)
{
uint64_t timestamp = decoder->cyc_ref_timestamp;
decoder->have_cyc = true;
decoder->cycle_cnt += decoder->packet.payload;
if (decoder->pge)
decoder->tot_cyc_cnt += decoder->packet.payload;
decoder->sample_cyc = true;
if (!decoder->cyc_ref_timestamp)
return;
if (decoder->have_calc_cyc_to_tsc)
timestamp += decoder->cycle_cnt * decoder->calc_cyc_to_tsc;
else if (decoder->cbr)
timestamp += decoder->cycle_cnt * decoder->cbr_cyc_to_tsc;
else
return;
if (timestamp < decoder->timestamp)
intel_pt_log("Suppressing CYC timestamp " x64_fmt " less than current timestamp " x64_fmt "\n",
timestamp, decoder->timestamp);
else
decoder->timestamp = timestamp;
decoder->timestamp_insn_cnt = 0;
intel_pt_log_to("Setting timestamp", decoder->timestamp);
}
static void intel_pt_bbp(struct intel_pt_decoder *decoder)
{
if (decoder->prev_pkt_ctx == INTEL_PT_NO_CTX) {
memset(decoder->state.items.mask, 0, sizeof(decoder->state.items.mask));
decoder->state.items.is_32_bit = false;
}
decoder->blk_type = decoder->packet.payload;
decoder->blk_type_pos = intel_pt_blk_type_pos(decoder->blk_type);
if (decoder->blk_type == INTEL_PT_GP_REGS)
decoder->state.items.is_32_bit = decoder->packet.count;
if (decoder->blk_type_pos < 0) {
intel_pt_log("WARNING: Unknown block type %u\n",
decoder->blk_type);
} else if (decoder->state.items.mask[decoder->blk_type_pos]) {
intel_pt_log("WARNING: Duplicate block type %u\n",
decoder->blk_type);
}
}
static void intel_pt_bip(struct intel_pt_decoder *decoder)
{
uint32_t id = decoder->packet.count;
uint32_t bit = 1 << id;
int pos = decoder->blk_type_pos;
if (pos < 0 || id >= INTEL_PT_BLK_ITEM_ID_CNT) {
intel_pt_log("WARNING: Unknown block item %u type %d\n",
id, decoder->blk_type);
return;
}
if (decoder->state.items.mask[pos] & bit) {
intel_pt_log("WARNING: Duplicate block item %u type %d\n",
id, decoder->blk_type);
}
decoder->state.items.mask[pos] |= bit;
decoder->state.items.val[pos][id] = decoder->packet.payload;
}
static int intel_pt_walk_psbend(struct intel_pt_decoder *decoder)
{
int err;
decoder->in_psb = true;
while (1) {
err = intel_pt_get_next_packet(decoder);
if (err)
goto out;
switch (decoder->packet.type) {
case INTEL_PT_PSBEND:
err = 0;
goto out;
case INTEL_PT_TIP_PGD:
case INTEL_PT_TIP_PGE:
case INTEL_PT_TIP:
case INTEL_PT_TNT:
case INTEL_PT_TRACESTOP:
case INTEL_PT_BAD:
case INTEL_PT_PSB:
case INTEL_PT_PTWRITE:
case INTEL_PT_PTWRITE_IP:
case INTEL_PT_EXSTOP:
case INTEL_PT_EXSTOP_IP:
case INTEL_PT_MWAIT:
case INTEL_PT_PWRE:
case INTEL_PT_PWRX:
case INTEL_PT_BBP:
case INTEL_PT_BIP:
case INTEL_PT_BEP:
case INTEL_PT_BEP_IP:
case INTEL_PT_CFE:
case INTEL_PT_CFE_IP:
case INTEL_PT_EVD:
decoder->have_tma = false;
intel_pt_log("ERROR: Unexpected packet\n");
err = -EAGAIN;
goto out;
case INTEL_PT_OVF:
err = intel_pt_overflow(decoder);
goto out;
case INTEL_PT_TSC:
intel_pt_calc_tsc_timestamp(decoder);
break;
case INTEL_PT_TMA:
intel_pt_calc_tma(decoder);
break;
case INTEL_PT_CBR:
intel_pt_calc_cbr(decoder);
break;
case INTEL_PT_MODE_EXEC:
intel_pt_mode_exec_status(decoder);
break;
case INTEL_PT_PIP:
intel_pt_set_pip(decoder);
break;
case INTEL_PT_FUP:
decoder->pge = true;
if (decoder->packet.count) {
intel_pt_set_last_ip(decoder);
decoder->psb_ip = decoder->last_ip;
}
break;
case INTEL_PT_MODE_TSX:
intel_pt_update_in_tx(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:
intel_pt_calc_cyc_timestamp(decoder);
break;
case INTEL_PT_VMCS:
case INTEL_PT_MNT:
case INTEL_PT_PAD:
default:
break;
}
}
out:
decoder->in_psb = false;
return err;
}
static int intel_pt_walk_fup_tip(struct intel_pt_decoder *decoder)
{
int err;
if (decoder->tx_flags & INTEL_PT_ABORT_TX) {
decoder->tx_flags = 0;
decoder->state.flags &= ~INTEL_PT_IN_TX;
decoder->state.flags |= INTEL_PT_ABORT_TX;
} else {
decoder->state.flags |= INTEL_PT_ASYNC;
}
while (1) {
err = intel_pt_get_next_packet(decoder);
if (err)
return err;
switch (decoder->packet.type) {
case INTEL_PT_TNT:
case INTEL_PT_FUP:
case INTEL_PT_TRACESTOP:
case INTEL_PT_PSB:
case INTEL_PT_TSC:
case INTEL_PT_TMA:
case INTEL_PT_MODE_TSX:
case INTEL_PT_BAD:
case INTEL_PT_PSBEND:
case INTEL_PT_PTWRITE:
case INTEL_PT_PTWRITE_IP:
case INTEL_PT_EXSTOP:
case INTEL_PT_EXSTOP_IP:
case INTEL_PT_MWAIT:
case INTEL_PT_PWRE:
case INTEL_PT_PWRX:
case INTEL_PT_BBP:
case INTEL_PT_BIP:
case INTEL_PT_BEP:
case INTEL_PT_BEP_IP:
case INTEL_PT_CFE:
case INTEL_PT_CFE_IP:
case INTEL_PT_EVD:
intel_pt_log("ERROR: Missing TIP after FUP\n");
decoder->pkt_state = INTEL_PT_STATE_ERR3;
decoder->pkt_step = 0;
return -ENOENT;
case INTEL_PT_CBR:
intel_pt_calc_cbr(decoder);
break;
case INTEL_PT_OVF:
return intel_pt_overflow(decoder);
case INTEL_PT_TIP_PGD:
decoder->state.from_ip = decoder->ip;
if (decoder->packet.count == 0) {
decoder->state.to_ip = 0;
} else {
intel_pt_set_ip(decoder);
decoder->state.to_ip = decoder->ip;
}
decoder->pge = false;
decoder->continuous_period = false;
decoder->state.type |= INTEL_PT_TRACE_END;
intel_pt_update_nr(decoder);
return 0;
case INTEL_PT_TIP_PGE:
decoder->pge = true;
intel_pt_log("Omitting PGE ip " x64_fmt "\n",
decoder->ip);
decoder->state.from_ip = 0;
if (decoder->packet.count == 0) {
decoder->state.to_ip = 0;
} else {
intel_pt_set_ip(decoder);
decoder->state.to_ip = decoder->ip;
}
decoder->state.type |= INTEL_PT_TRACE_BEGIN;
intel_pt_mtc_cyc_cnt_pge(decoder);
intel_pt_set_nr(decoder);
return 0;
case INTEL_PT_TIP:
decoder->state.from_ip = decoder->ip;
if (decoder->packet.count == 0) {
decoder->state.to_ip = 0;
} else {
intel_pt_set_ip(decoder);
decoder->state.to_ip = decoder->ip;
}
intel_pt_update_nr(decoder);
intel_pt_sample_iflag_chg(decoder);
return 0;
case INTEL_PT_PIP:
intel_pt_update_pip(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:
intel_pt_calc_cyc_timestamp(decoder);
break;
case INTEL_PT_MODE_EXEC:
intel_pt_mode_exec(decoder);
break;
case INTEL_PT_VMCS:
case INTEL_PT_MNT:
case INTEL_PT_PAD:
break;
default:
return intel_pt_bug(decoder);
}
}
}
static int intel_pt_resample(struct intel_pt_decoder *decoder)
{
decoder->pkt_state = INTEL_PT_STATE_IN_SYNC;
decoder->state.type = INTEL_PT_INSTRUCTION;
decoder->state.from_ip = decoder->ip;
decoder->state.to_ip = 0;
return 0;
}
struct intel_pt_vm_tsc_info {
struct intel_pt_pkt pip_packet;
struct intel_pt_pkt vmcs_packet;
struct intel_pt_pkt tma_packet;
bool tsc, pip, vmcs, tma, psbend;
uint64_t ctc_delta;
uint64_t last_ctc;
int max_lookahead;
};
static int intel_pt_vm_psb_lookahead_cb(struct intel_pt_pkt_info *pkt_info)
{
struct intel_pt_vm_tsc_info *data = pkt_info->data;
switch (pkt_info->packet.type) {
case INTEL_PT_PAD:
case INTEL_PT_MNT:
case INTEL_PT_MODE_EXEC:
case INTEL_PT_MODE_TSX:
case INTEL_PT_MTC:
case INTEL_PT_FUP:
case INTEL_PT_CYC:
case INTEL_PT_CBR:
break;
case INTEL_PT_TSC:
data->tsc = true;
break;
case INTEL_PT_TMA:
data->tma_packet = pkt_info->packet;
data->tma = true;
break;
case INTEL_PT_PIP:
data->pip_packet = pkt_info->packet;
data->pip = true;
break;
case INTEL_PT_VMCS:
data->vmcs_packet = pkt_info->packet;
data->vmcs = true;
break;
case INTEL_PT_PSBEND:
data->psbend = true;
return 1;
case INTEL_PT_TIP_PGE:
case INTEL_PT_PTWRITE:
case INTEL_PT_PTWRITE_IP:
case INTEL_PT_EXSTOP:
case INTEL_PT_EXSTOP_IP:
case INTEL_PT_MWAIT:
case INTEL_PT_PWRE:
case INTEL_PT_PWRX:
case INTEL_PT_BBP:
case INTEL_PT_BIP:
case INTEL_PT_BEP:
case INTEL_PT_BEP_IP:
case INTEL_PT_OVF:
case INTEL_PT_BAD:
case INTEL_PT_TNT:
case INTEL_PT_TIP_PGD:
case INTEL_PT_TIP:
case INTEL_PT_PSB:
case INTEL_PT_TRACESTOP:
case INTEL_PT_CFE:
case INTEL_PT_CFE_IP:
case INTEL_PT_EVD:
default:
return 1;
}
return 0;
}
struct intel_pt_ovf_fup_info {
int max_lookahead;
bool found;
};
static int intel_pt_ovf_fup_lookahead_cb(struct intel_pt_pkt_info *pkt_info)
{
struct intel_pt_ovf_fup_info *data = pkt_info->data;
if (pkt_info->packet.type == INTEL_PT_CYC ||
pkt_info->packet.type == INTEL_PT_MTC ||
pkt_info->packet.type == INTEL_PT_TSC)
return !--(data->max_lookahead);
data->found = pkt_info->packet.type == INTEL_PT_FUP;
return 1;
}
static bool intel_pt_ovf_fup_lookahead(struct intel_pt_decoder *decoder)
{
struct intel_pt_ovf_fup_info data = {
.max_lookahead = 16,
.found = false,
};
intel_pt_pkt_lookahead(decoder, intel_pt_ovf_fup_lookahead_cb, &data);
return data.found;
}
static int intel_pt_tma_lookahead_cb(struct intel_pt_pkt_info *pkt_info)
{
struct intel_pt_vm_tsc_info *data = pkt_info->data;
if (pkt_info->packet.type == INTEL_PT_CYC ||
pkt_info->packet.type == INTEL_PT_MTC)
return !--(data->max_lookahead);
if (pkt_info->packet.type == INTEL_PT_TMA) {
data->tma_packet = pkt_info->packet;
data->tma = true;
}
return 1;
}
static uint64_t intel_pt_ctc_to_tsc(struct intel_pt_decoder *decoder, uint64_t ctc)
{
if (decoder->tsc_ctc_mult)
return ctc * decoder->tsc_ctc_mult;
else
return multdiv(ctc, decoder->tsc_ctc_ratio_n, decoder->tsc_ctc_ratio_d);
}
static uint64_t intel_pt_calc_expected_tsc(struct intel_pt_decoder *decoder,
uint32_t ctc,
uint32_t fc,
uint64_t last_ctc_timestamp,
uint64_t ctc_delta,
uint32_t last_ctc)
{
uint64_t last_mtc_ctc = last_ctc + ctc_delta;
uint64_t delta = (int16_t)((uint16_t)ctc - (uint16_t)last_mtc_ctc);
uint64_t new_ctc_delta = ctc_delta + delta;
uint64_t expected_tsc;
expected_tsc = last_ctc_timestamp + intel_pt_ctc_to_tsc(decoder, new_ctc_delta) + fc;
if (intel_pt_enable_logging) {
intel_pt_log_x64(last_mtc_ctc);
intel_pt_log_x32(last_ctc);
intel_pt_log_x64(ctc_delta);
intel_pt_log_x64(delta);
intel_pt_log_x32(ctc);
intel_pt_log_x64(new_ctc_delta);
intel_pt_log_x64(last_ctc_timestamp);
intel_pt_log_x32(fc);
intel_pt_log_x64(intel_pt_ctc_to_tsc(decoder, new_ctc_delta));
intel_pt_log_x64(expected_tsc);
}
return expected_tsc;
}
static uint64_t intel_pt_expected_tsc(struct intel_pt_decoder *decoder,
struct intel_pt_vm_tsc_info *data)
{
uint32_t ctc = data->tma_packet.payload;
uint32_t fc = data->tma_packet.count;
return intel_pt_calc_expected_tsc(decoder, ctc, fc,
decoder->ctc_timestamp,
data->ctc_delta, data->last_ctc);
}
static void intel_pt_translate_vm_tsc(struct intel_pt_decoder *decoder,
struct intel_pt_vmcs_info *vmcs_info)
{
uint64_t payload = decoder->packet.payload;
decoder->packet.payload -= vmcs_info->tsc_offset;
decoder->packet.payload &= SEVEN_BYTES;
if (!decoder->vm_tm_corr_dry_run)
memcpy((void *)decoder->buf + 1, &decoder->packet.payload, 7);
intel_pt_log("Translated VM TSC %#" PRIx64 " -> %#" PRIx64
" VMCS %#" PRIx64 " TSC Offset %#" PRIx64 "\n",
payload, decoder->packet.payload, vmcs_info->vmcs,
vmcs_info->tsc_offset);
}
static void intel_pt_translate_vm_tsc_offset(struct intel_pt_decoder *decoder,
uint64_t tsc_offset)
{
struct intel_pt_vmcs_info vmcs_info = {
.vmcs = NO_VMCS,
.tsc_offset = tsc_offset
};
intel_pt_translate_vm_tsc(decoder, &vmcs_info);
}
static inline bool in_vm(uint64_t pip_payload)
{
return pip_payload & 1;
}
static inline bool pip_in_vm(struct intel_pt_pkt *pip_packet)
{
return pip_packet->payload & 1;
}
static void intel_pt_print_vmcs_info(struct intel_pt_vmcs_info *vmcs_info)
{
p_log("VMCS: %#" PRIx64 " TSC Offset %#" PRIx64,
vmcs_info->vmcs, vmcs_info->tsc_offset);
}
static void intel_pt_vm_tm_corr_psb(struct intel_pt_decoder *decoder,
struct intel_pt_vm_tsc_info *data)
{
memset(data, 0, sizeof(*data));
data->ctc_delta = decoder->ctc_delta;
data->last_ctc = decoder->last_ctc;
intel_pt_pkt_lookahead(decoder, intel_pt_vm_psb_lookahead_cb, data);
if (data->tsc && !data->psbend)
p_log("ERROR: PSB without PSBEND");
decoder->in_psb = data->psbend;
}
static void intel_pt_vm_tm_corr_first_tsc(struct intel_pt_decoder *decoder,
struct intel_pt_vm_tsc_info *data,
struct intel_pt_vmcs_info *vmcs_info,
uint64_t host_tsc)
{
if (!decoder->in_psb) {
p_log("ERROR: First TSC is not in PSB+");
}
if (data->pip) {
if (pip_in_vm(&data->pip_packet)) {
if (vmcs_info && vmcs_info->tsc_offset) {
intel_pt_translate_vm_tsc(decoder, vmcs_info);
decoder->vm_tm_corr_reliable = true;
} else {
p_log("ERROR: First TSC, unknown TSC Offset");
}
} else {
decoder->vm_tm_corr_reliable = true;
}
} else {
decoder->vm_tm_corr_reliable = false;
if (intel_pt_time_in_range(decoder, host_tsc)) {
} else {
if (vmcs_info && vmcs_info->tsc_offset)
intel_pt_translate_vm_tsc(decoder, vmcs_info);
else
p_log("ERROR: First TSC, no PIP, unknown TSC Offset");
}
}
}
static void intel_pt_vm_tm_corr_tsc(struct intel_pt_decoder *decoder,
struct intel_pt_vm_tsc_info *data)
{
struct intel_pt_vmcs_info *vmcs_info;
uint64_t tsc_offset = 0;
uint64_t vmcs;
bool reliable = true;
uint64_t expected_tsc;
uint64_t host_tsc;
uint64_t ref_timestamp;
bool assign = false;
bool assign_reliable = false;
if (!decoder->in_psb) {
memset(data, 0, sizeof(*data));
data->ctc_delta = decoder->ctc_delta;
data->last_ctc = decoder->last_ctc;
data->max_lookahead = 16;
intel_pt_pkt_lookahead(decoder, intel_pt_tma_lookahead_cb, data);
if (decoder->pge) {
data->pip = true;
data->pip_packet.payload = decoder->pip_payload;
}
}
if (!data->tma) {
p_log("ERROR: TSC without TMA");
return;
}
vmcs = data->vmcs ? data->vmcs_packet.payload : decoder->vmcs;
if (vmcs == NO_VMCS)
vmcs = 0;
vmcs_info = decoder->findnew_vmcs_info(decoder->data, vmcs);
ref_timestamp = decoder->timestamp ? decoder->timestamp : decoder->buf_timestamp;
host_tsc = intel_pt_8b_tsc(decoder->packet.payload, ref_timestamp);
if (!decoder->ctc_timestamp) {
intel_pt_vm_tm_corr_first_tsc(decoder, data, vmcs_info, host_tsc);
return;
}
expected_tsc = intel_pt_expected_tsc(decoder, data);
tsc_offset = host_tsc - expected_tsc;
if (data->pip) {
if (pip_in_vm(&data->pip_packet)) {
if (!vmcs_info) {
p_log("ERROR: Missing VMCS");
intel_pt_translate_vm_tsc_offset(decoder, tsc_offset);
decoder->vm_tm_corr_reliable = false;
return;
}
} else {
decoder->last_reliable_timestamp = host_tsc;
decoder->vm_tm_corr_reliable = true;
return;
}
} else {
reliable = false;
if (decoder->in_psb) {
if (!tsc_offset)
return;
if (vmcs_info &&
(tsc_offset & SEVEN_BYTES) ==
(vmcs_info->tsc_offset & SEVEN_BYTES)) {
goto guest;
}
}
if (host_tsc >= expected_tsc && intel_pt_time_in_range(decoder, host_tsc)) {
decoder->vm_tm_corr_reliable = false;
return;
}
}
guest:
if (vmcs_info && vmcs_info->vmcs) {
if (vmcs_info->tsc_offset && vmcs_info->reliable) {
assign = false;
} else if (decoder->in_psb && data->pip && decoder->vm_tm_corr_reliable &&
decoder->vm_tm_corr_continuous && decoder->vm_tm_corr_same_buf) {
assign = true;
assign_reliable = true;
} else if (decoder->in_psb && data->pip && decoder->vm_tm_corr_same_buf) {
assign = true;
assign_reliable = false;
}
}
if (assign && (vmcs_info->tsc_offset != tsc_offset ||
vmcs_info->reliable != assign_reliable)) {
bool print = vmcs_info->tsc_offset != tsc_offset;
vmcs_info->tsc_offset = tsc_offset;
vmcs_info->reliable = assign_reliable;
if (print)
intel_pt_print_vmcs_info(vmcs_info);
}
if (vmcs_info && vmcs_info->tsc_offset) {
if (!vmcs_info->reliable)
reliable = false;
intel_pt_translate_vm_tsc(decoder, vmcs_info);
} else {
reliable = false;
if (vmcs_info) {
if (!vmcs_info->error_printed) {
p_log("ERROR: Unknown TSC Offset for VMCS %#" PRIx64,
vmcs_info->vmcs);
vmcs_info->error_printed = true;
}
} else {
if (intel_pt_print_once(decoder, INTEL_PT_PRT_ONCE_UNK_VMCS))
p_log("ERROR: Unknown VMCS");
}
intel_pt_translate_vm_tsc_offset(decoder, tsc_offset);
}
decoder->vm_tm_corr_reliable = reliable;
}
static void intel_pt_vm_tm_corr_pebs_tsc(struct intel_pt_decoder *decoder)
{
uint64_t host_tsc = decoder->packet.payload;
uint64_t guest_tsc = decoder->packet.payload;
struct intel_pt_vmcs_info *vmcs_info;
uint64_t vmcs;
vmcs = decoder->vmcs;
if (vmcs == NO_VMCS)
vmcs = 0;
vmcs_info = decoder->findnew_vmcs_info(decoder->data, vmcs);
if (decoder->pge) {
if (in_vm(decoder->pip_payload)) {
if (!vmcs_info) {
p_log("ERROR: Missing VMCS");
}
} else {
return;
}
} else {
if (intel_pt_time_in_range(decoder, host_tsc)) {
return;
}
}
if (vmcs_info) {
host_tsc = ((guest_tsc & SEVEN_BYTES) - vmcs_info->tsc_offset) & SEVEN_BYTES;
host_tsc = intel_pt_8b_tsc(host_tsc, decoder->timestamp);
intel_pt_log("Translated VM TSC %#" PRIx64 " -> %#" PRIx64
" VMCS %#" PRIx64 " TSC Offset %#" PRIx64 "\n",
guest_tsc, host_tsc, vmcs_info->vmcs,
vmcs_info->tsc_offset);
if (!intel_pt_time_in_range(decoder, host_tsc) &&
intel_pt_print_once(decoder, INTEL_PT_PRT_ONCE_ERANGE))
p_log("Timestamp out of range");
} else {
if (intel_pt_print_once(decoder, INTEL_PT_PRT_ONCE_UNK_VMCS))
p_log("ERROR: Unknown VMCS");
host_tsc = decoder->timestamp;
}
decoder->packet.payload = host_tsc;
if (!decoder->vm_tm_corr_dry_run)
memcpy((void *)decoder->buf + 1, &host_tsc, 8);
}
static int intel_pt_vm_time_correlation(struct intel_pt_decoder *decoder)
{
struct intel_pt_vm_tsc_info data = { .psbend = false };
bool pge;
int err;
if (decoder->in_psb)
intel_pt_vm_tm_corr_psb(decoder, &data);
while (1) {
err = intel_pt_get_next_packet(decoder);
if (err == -ENOLINK)
continue;
if (err)
break;
switch (decoder->packet.type) {
case INTEL_PT_TIP_PGD:
decoder->pge = false;
decoder->vm_tm_corr_continuous = false;
break;
case INTEL_PT_TNT:
case INTEL_PT_TIP:
case INTEL_PT_TIP_PGE:
decoder->pge = true;
break;
case INTEL_PT_OVF:
decoder->in_psb = false;
pge = decoder->pge;
decoder->pge = intel_pt_ovf_fup_lookahead(decoder);
if (pge != decoder->pge)
intel_pt_log("Surprising PGE change in OVF!");
if (!decoder->pge)
decoder->vm_tm_corr_continuous = false;
break;
case INTEL_PT_FUP:
if (decoder->in_psb)
decoder->pge = true;
break;
case INTEL_PT_TRACESTOP:
decoder->pge = false;
decoder->vm_tm_corr_continuous = false;
decoder->have_tma = false;
break;
case INTEL_PT_PSB:
intel_pt_vm_tm_corr_psb(decoder, &data);
break;
case INTEL_PT_PIP:
decoder->pip_payload = decoder->packet.payload;
break;
case INTEL_PT_MTC:
intel_pt_calc_mtc_timestamp(decoder);
break;
case INTEL_PT_TSC:
intel_pt_vm_tm_corr_tsc(decoder, &data);
intel_pt_calc_tsc_timestamp(decoder);
decoder->vm_tm_corr_same_buf = true;
decoder->vm_tm_corr_continuous = decoder->pge;
break;
case INTEL_PT_TMA:
intel_pt_calc_tma(decoder);
break;
case INTEL_PT_CYC:
intel_pt_calc_cyc_timestamp(decoder);
break;
case INTEL_PT_CBR:
intel_pt_calc_cbr(decoder);
break;
case INTEL_PT_PSBEND:
decoder->in_psb = false;
data.psbend = false;
break;
case INTEL_PT_VMCS:
if (decoder->packet.payload != NO_VMCS)
decoder->vmcs = decoder->packet.payload;
break;
case INTEL_PT_BBP:
decoder->blk_type = decoder->packet.payload;
break;
case INTEL_PT_BIP:
if (decoder->blk_type == INTEL_PT_PEBS_BASIC &&
decoder->packet.count == 2)
intel_pt_vm_tm_corr_pebs_tsc(decoder);
break;
case INTEL_PT_BEP:
case INTEL_PT_BEP_IP:
decoder->blk_type = 0;
break;
case INTEL_PT_CFE:
case INTEL_PT_CFE_IP:
case INTEL_PT_EVD:
case INTEL_PT_MODE_EXEC:
case INTEL_PT_MODE_TSX:
case INTEL_PT_MNT:
case INTEL_PT_PAD:
case INTEL_PT_PTWRITE_IP:
case INTEL_PT_PTWRITE:
case INTEL_PT_MWAIT:
case INTEL_PT_PWRE:
case INTEL_PT_EXSTOP_IP:
case INTEL_PT_EXSTOP:
case INTEL_PT_PWRX:
case INTEL_PT_BAD:
default:
break;
}
}
return err;
}
#define HOP_PROCESS 0
#define HOP_IGNORE 1
#define HOP_RETURN 2
#define HOP_AGAIN 3
static int intel_pt_scan_for_psb(struct intel_pt_decoder *decoder);
static int intel_pt_hop_trace(struct intel_pt_decoder *decoder, bool *no_tip, int *err)
{
*err = 0;
if (decoder->leap && !decoder->in_psb && decoder->packet.type != INTEL_PT_PSB) {
*err = intel_pt_scan_for_psb(decoder);
if (*err)
return HOP_RETURN;
}
switch (decoder->packet.type) {
case INTEL_PT_TNT:
return HOP_IGNORE;
case INTEL_PT_TIP_PGD:
decoder->pge = false;
if (!decoder->packet.count) {
intel_pt_set_nr(decoder);
return HOP_IGNORE;
}
intel_pt_set_ip(decoder);
decoder->state.type |= INTEL_PT_TRACE_END;
decoder->state.from_ip = 0;
decoder->state.to_ip = decoder->ip;
intel_pt_update_nr(decoder);
return HOP_RETURN;
case INTEL_PT_TIP:
if (!decoder->packet.count) {
intel_pt_set_nr(decoder);
return HOP_IGNORE;
}
intel_pt_set_ip(decoder);
decoder->state.type = INTEL_PT_INSTRUCTION;
decoder->state.from_ip = decoder->ip;
decoder->state.to_ip = 0;
intel_pt_update_nr(decoder);
intel_pt_sample_iflag_chg(decoder);
return HOP_RETURN;
case INTEL_PT_FUP:
if (!decoder->packet.count)
return HOP_IGNORE;
intel_pt_set_ip(decoder);
if (decoder->set_fup_mwait || decoder->set_fup_pwre)
*no_tip = true;
if (!decoder->branch_enable || !decoder->pge)
*no_tip = true;
if (*no_tip) {
decoder->state.type = INTEL_PT_INSTRUCTION;
decoder->state.from_ip = decoder->ip;
decoder->state.to_ip = 0;
intel_pt_fup_event(decoder, *no_tip);
return HOP_RETURN;
}
intel_pt_fup_event(decoder, *no_tip);
decoder->state.type |= INTEL_PT_INSTRUCTION | INTEL_PT_BRANCH;
*err = intel_pt_walk_fup_tip(decoder);
if (!*err && decoder->state.to_ip)
decoder->pkt_state = INTEL_PT_STATE_RESAMPLE;
return HOP_RETURN;
case INTEL_PT_PSB:
decoder->state.psb_offset = decoder->pos;
decoder->psb_ip = 0;
decoder->last_ip = 0;
decoder->have_last_ip = true;
*err = intel_pt_walk_psbend(decoder);
if (*err == -EAGAIN)
return HOP_AGAIN;
if (*err)
return HOP_RETURN;
decoder->state.type = INTEL_PT_PSB_EVT;
if (decoder->psb_ip) {
decoder->state.type |= INTEL_PT_INSTRUCTION;
decoder->ip = decoder->psb_ip;
}
decoder->state.from_ip = decoder->psb_ip;
decoder->state.to_ip = 0;
return HOP_RETURN;
case INTEL_PT_BAD:
case INTEL_PT_PAD:
case INTEL_PT_TIP_PGE:
case INTEL_PT_TSC:
case INTEL_PT_TMA:
case INTEL_PT_MODE_EXEC:
case INTEL_PT_MODE_TSX:
case INTEL_PT_MTC:
case INTEL_PT_CYC:
case INTEL_PT_VMCS:
case INTEL_PT_PSBEND:
case INTEL_PT_CBR:
case INTEL_PT_TRACESTOP:
case INTEL_PT_PIP:
case INTEL_PT_OVF:
case INTEL_PT_MNT:
case INTEL_PT_PTWRITE:
case INTEL_PT_PTWRITE_IP:
case INTEL_PT_EXSTOP:
case INTEL_PT_EXSTOP_IP:
case INTEL_PT_MWAIT:
case INTEL_PT_PWRE:
case INTEL_PT_PWRX:
case INTEL_PT_BBP:
case INTEL_PT_BIP:
case INTEL_PT_BEP:
case INTEL_PT_BEP_IP:
case INTEL_PT_CFE:
case INTEL_PT_CFE_IP:
case INTEL_PT_EVD:
default:
return HOP_PROCESS;
}
}
struct intel_pt_psb_info {
struct intel_pt_pkt fup_packet;
bool fup;
int after_psbend;
};
static int intel_pt_psb_lookahead_cb(struct intel_pt_pkt_info *pkt_info)
{
struct intel_pt_psb_info *data = pkt_info->data;
switch (pkt_info->packet.type) {
case INTEL_PT_PAD:
case INTEL_PT_MNT:
case INTEL_PT_TSC:
case INTEL_PT_TMA:
case INTEL_PT_MODE_EXEC:
case INTEL_PT_MODE_TSX:
case INTEL_PT_MTC:
case INTEL_PT_CYC:
case INTEL_PT_VMCS:
case INTEL_PT_CBR:
case INTEL_PT_PIP:
if (data->after_psbend) {
data->after_psbend -= 1;
if (!data->after_psbend)
return 1;
}
break;
case INTEL_PT_FUP:
if (data->after_psbend)
return 1;
if (data->fup || pkt_info->packet.count == 0)
return 1;
data->fup_packet = pkt_info->packet;
data->fup = true;
break;
case INTEL_PT_PSBEND:
if (!data->fup)
return 1;
data->after_psbend = 6;
break;
case INTEL_PT_TIP_PGE:
if (data->after_psbend)
data->fup = false;
return 1;
case INTEL_PT_PTWRITE:
case INTEL_PT_PTWRITE_IP:
case INTEL_PT_EXSTOP:
case INTEL_PT_EXSTOP_IP:
case INTEL_PT_MWAIT:
case INTEL_PT_PWRE:
case INTEL_PT_PWRX:
case INTEL_PT_BBP:
case INTEL_PT_BIP:
case INTEL_PT_BEP:
case INTEL_PT_BEP_IP:
case INTEL_PT_CFE:
case INTEL_PT_CFE_IP:
case INTEL_PT_EVD:
if (data->after_psbend) {
data->after_psbend -= 1;
if (!data->after_psbend)
return 1;
break;
}
return 1;
case INTEL_PT_OVF:
case INTEL_PT_BAD:
case INTEL_PT_TNT:
case INTEL_PT_TIP_PGD:
case INTEL_PT_TIP:
case INTEL_PT_PSB:
case INTEL_PT_TRACESTOP:
default:
return 1;
}
return 0;
}
static int intel_pt_psb(struct intel_pt_decoder *decoder)
{
int err;
decoder->last_ip = 0;
decoder->psb_ip = 0;
decoder->have_last_ip = true;
intel_pt_clear_stack(&decoder->stack);
err = intel_pt_walk_psbend(decoder);
if (err)
return err;
decoder->state.type = INTEL_PT_PSB_EVT;
decoder->state.from_ip = decoder->psb_ip;
decoder->state.to_ip = 0;
return 0;
}
static int intel_pt_fup_in_psb(struct intel_pt_decoder *decoder)
{
int err;
if (decoder->ip != decoder->last_ip) {
err = intel_pt_walk_fup(decoder);
if (!err || err != -EAGAIN)
return err;
}
decoder->pkt_state = INTEL_PT_STATE_IN_SYNC;
err = intel_pt_psb(decoder);
if (err) {
decoder->pkt_state = INTEL_PT_STATE_ERR3;
return -ENOENT;
}
return 0;
}
static bool intel_pt_psb_with_fup(struct intel_pt_decoder *decoder, int *err)
{
struct intel_pt_psb_info data = { .fup = false };
if (!decoder->branch_enable)
return false;
intel_pt_pkt_lookahead(decoder, intel_pt_psb_lookahead_cb, &data);
if (!data.fup)
return false;
decoder->packet = data.fup_packet;
intel_pt_set_last_ip(decoder);
decoder->pkt_state = INTEL_PT_STATE_FUP_IN_PSB;
*err = intel_pt_fup_in_psb(decoder);
return true;
}
static int intel_pt_walk_trace(struct intel_pt_decoder *decoder)
{
int last_packet_type = INTEL_PT_PAD;
bool no_tip = false;
int err;
while (1) {
err = intel_pt_get_next_packet(decoder);
if (err)
return err;
next:
err = 0;
if (decoder->cyc_threshold) {
if (decoder->sample_cyc && last_packet_type != INTEL_PT_CYC)
decoder->sample_cyc = false;
last_packet_type = decoder->packet.type;
}
if (decoder->hop) {
switch (intel_pt_hop_trace(decoder, &no_tip, &err)) {
case HOP_IGNORE:
continue;
case HOP_RETURN:
return err;
case HOP_AGAIN:
goto next;
default:
break;
}
}
switch (decoder->packet.type) {
case INTEL_PT_TNT:
if (!decoder->packet.count)
break;
decoder->tnt = decoder->packet;
decoder->pkt_state = INTEL_PT_STATE_TNT;
err = intel_pt_walk_tnt(decoder);
if (err == -EAGAIN)
break;
return err;
case INTEL_PT_TIP_PGD:
if (decoder->packet.count != 0)
intel_pt_set_last_ip(decoder);
decoder->pkt_state = INTEL_PT_STATE_TIP_PGD;
return intel_pt_walk_tip(decoder);
case INTEL_PT_TIP_PGE: {
decoder->pge = true;
decoder->overflow = false;
intel_pt_mtc_cyc_cnt_pge(decoder);
intel_pt_set_nr(decoder);
if (decoder->packet.count == 0) {
intel_pt_log_at("Skipping zero TIP.PGE",
decoder->pos);
break;
}
intel_pt_sample_iflag_chg(decoder);
intel_pt_set_ip(decoder);
decoder->state.from_ip = 0;
decoder->state.to_ip = decoder->ip;
decoder->state.type |= INTEL_PT_TRACE_BEGIN;
if (decoder->hop)
decoder->pkt_state = INTEL_PT_STATE_RESAMPLE;
return 0;
}
case INTEL_PT_OVF:
return intel_pt_overflow(decoder);
case INTEL_PT_TIP:
if (decoder->packet.count != 0)
intel_pt_set_last_ip(decoder);
decoder->pkt_state = INTEL_PT_STATE_TIP;
return intel_pt_walk_tip(decoder);
case INTEL_PT_FUP:
if (decoder->packet.count == 0) {
intel_pt_log_at("Skipping zero FUP",
decoder->pos);
no_tip = false;
break;
}
intel_pt_set_last_ip(decoder);
if (!decoder->branch_enable || !decoder->pge) {
decoder->ip = decoder->last_ip;
if (intel_pt_fup_event(decoder, no_tip))
return 0;
no_tip = false;
break;
}
if (decoder->set_fup_mwait)
no_tip = true;
if (no_tip)
decoder->pkt_state = INTEL_PT_STATE_FUP_NO_TIP;
else
decoder->pkt_state = INTEL_PT_STATE_FUP;
err = intel_pt_walk_fup(decoder);
if (err != -EAGAIN)
return err;
if (no_tip) {
no_tip = false;
break;
}
return intel_pt_walk_fup_tip(decoder);
case INTEL_PT_TRACESTOP:
decoder->pge = false;
decoder->continuous_period = false;
intel_pt_clear_tx_flags(decoder);
decoder->have_tma = false;
break;
case INTEL_PT_PSB:
decoder->state.psb_offset = decoder->pos;
decoder->psb_ip = 0;
if (intel_pt_psb_with_fup(decoder, &err))
return err;
err = intel_pt_psb(decoder);
if (err == -EAGAIN)
goto next;
return err;
case INTEL_PT_PIP:
intel_pt_update_pip(decoder);
break;
case INTEL_PT_MTC:
intel_pt_calc_mtc_timestamp(decoder);
if (decoder->period_type != INTEL_PT_PERIOD_MTC)
break;
if (!decoder->mtc_insn)
break;
decoder->mtc_insn = false;
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:
intel_pt_calc_cyc_timestamp(decoder);
break;
case INTEL_PT_CBR:
intel_pt_calc_cbr(decoder);
if (decoder->cbr != decoder->cbr_seen) {
decoder->state.type = 0;
return 0;
}
break;
case INTEL_PT_MODE_EXEC:
intel_pt_mode_exec(decoder);
err = intel_pt_get_next_packet(decoder);
if (err)
return err;
if (decoder->packet.type == INTEL_PT_FUP) {
decoder->set_fup_mode_exec = true;
no_tip = true;
}
goto next;
case INTEL_PT_MODE_TSX:
if (!decoder->pge || decoder->in_psb) {
intel_pt_update_in_tx(decoder);
break;
}
err = intel_pt_mode_tsx(decoder, &no_tip);
if (err)
return err;
goto next;
case INTEL_PT_BAD:
return intel_pt_bug(decoder);
case INTEL_PT_PSBEND:
case INTEL_PT_VMCS:
case INTEL_PT_MNT:
case INTEL_PT_PAD:
break;
case INTEL_PT_PTWRITE_IP:
decoder->fup_ptw_payload = decoder->packet.payload;
err = intel_pt_get_next_packet(decoder);
if (err)
return err;
if (decoder->packet.type == INTEL_PT_FUP) {
decoder->set_fup_ptw = true;
no_tip = true;
} else {
intel_pt_log_at("ERROR: Missing FUP after PTWRITE",
decoder->pos);
}
goto next;
case INTEL_PT_PTWRITE:
decoder->state.type = INTEL_PT_PTW;
decoder->state.from_ip = decoder->ip;
decoder->state.to_ip = 0;
decoder->state.ptw_payload = decoder->packet.payload;
return 0;
case INTEL_PT_MWAIT:
decoder->fup_mwait_payload = decoder->packet.payload;
decoder->set_fup_mwait = true;
break;
case INTEL_PT_PWRE:
if (decoder->set_fup_mwait) {
decoder->fup_pwre_payload =
decoder->packet.payload;
decoder->set_fup_pwre = true;
break;
}
decoder->state.type = INTEL_PT_PWR_ENTRY;
decoder->state.from_ip = decoder->ip;
decoder->state.to_ip = 0;
decoder->state.pwrx_payload = decoder->packet.payload;
return 0;
case INTEL_PT_EXSTOP_IP:
err = intel_pt_get_next_packet(decoder);
if (err)
return err;
if (decoder->packet.type == INTEL_PT_FUP) {
decoder->set_fup_exstop = true;
no_tip = true;
} else {
intel_pt_log_at("ERROR: Missing FUP after EXSTOP",
decoder->pos);
}
goto next;
case INTEL_PT_EXSTOP:
decoder->state.type = INTEL_PT_EX_STOP;
decoder->state.from_ip = decoder->ip;
decoder->state.to_ip = 0;
return 0;
case INTEL_PT_PWRX:
decoder->state.type = INTEL_PT_PWR_EXIT;
decoder->state.from_ip = decoder->ip;
decoder->state.to_ip = 0;
decoder->state.pwrx_payload = decoder->packet.payload;
return 0;
case INTEL_PT_BBP:
intel_pt_bbp(decoder);
break;
case INTEL_PT_BIP:
intel_pt_bip(decoder);
break;
case INTEL_PT_BEP:
decoder->state.type = INTEL_PT_BLK_ITEMS;
decoder->state.from_ip = decoder->ip;
decoder->state.to_ip = 0;
return 0;
case INTEL_PT_BEP_IP:
err = intel_pt_get_next_packet(decoder);
if (err)
return err;
if (decoder->packet.type == INTEL_PT_FUP) {
decoder->set_fup_bep = true;
no_tip = true;
} else {
intel_pt_log_at("ERROR: Missing FUP after BEP",
decoder->pos);
}
goto next;
case INTEL_PT_CFE:
decoder->fup_cfe_pkt = decoder->packet;
decoder->set_fup_cfe = true;
if (!decoder->pge) {
intel_pt_fup_event(decoder, true);
return 0;
}
break;
case INTEL_PT_CFE_IP:
decoder->fup_cfe_pkt = decoder->packet;
err = intel_pt_get_next_packet(decoder);
if (err)
return err;
if (decoder->packet.type == INTEL_PT_FUP) {
decoder->set_fup_cfe_ip = true;
no_tip = true;
} else {
intel_pt_log_at("ERROR: Missing FUP after CFE",
decoder->pos);
}
goto next;
case INTEL_PT_EVD:
err = intel_pt_evd(decoder);
if (err)
return err;
break;
default:
return intel_pt_bug(decoder);
}
}
}
static inline bool intel_pt_have_ip(struct intel_pt_decoder *decoder)
{
return decoder->packet.count &&
(decoder->have_last_ip || decoder->packet.count == 3 ||
decoder->packet.count == 6);
}
static int intel_pt_walk_psb(struct intel_pt_decoder *decoder)
{
int err;
decoder->in_psb = true;
while (1) {
err = intel_pt_get_next_packet(decoder);
if (err)
goto out;
switch (decoder->packet.type) {
case INTEL_PT_TIP_PGD:
decoder->continuous_period = false;
fallthrough;
case INTEL_PT_TIP_PGE:
case INTEL_PT_TIP:
case INTEL_PT_PTWRITE:
case INTEL_PT_PTWRITE_IP:
case INTEL_PT_EXSTOP:
case INTEL_PT_EXSTOP_IP:
case INTEL_PT_MWAIT:
case INTEL_PT_PWRE:
case INTEL_PT_PWRX:
case INTEL_PT_BBP:
case INTEL_PT_BIP:
case INTEL_PT_BEP:
case INTEL_PT_BEP_IP:
case INTEL_PT_CFE:
case INTEL_PT_CFE_IP:
case INTEL_PT_EVD:
intel_pt_log("ERROR: Unexpected packet\n");
err = -ENOENT;
goto out;
case INTEL_PT_FUP:
decoder->pge = true;
if (intel_pt_have_ip(decoder)) {
uint64_t current_ip = decoder->ip;
intel_pt_set_ip(decoder);
decoder->psb_ip = decoder->ip;
if (current_ip)
intel_pt_log_to("Setting IP",
decoder->ip);
}
break;
case INTEL_PT_MTC:
intel_pt_calc_mtc_timestamp(decoder);
break;
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:
intel_pt_calc_cyc_timestamp(decoder);
break;
case INTEL_PT_CBR:
intel_pt_calc_cbr(decoder);
break;
case INTEL_PT_PIP:
intel_pt_set_pip(decoder);
break;
case INTEL_PT_MODE_EXEC:
intel_pt_mode_exec_status(decoder);
break;
case INTEL_PT_MODE_TSX:
intel_pt_update_in_tx(decoder);
break;
case INTEL_PT_TRACESTOP:
decoder->pge = false;
decoder->continuous_period = false;
intel_pt_clear_tx_flags(decoder);
fallthrough;
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;
else
decoder->pkt_state = INTEL_PT_STATE_ERR3;
err = -ENOENT;
goto out;
case INTEL_PT_BAD:
err = intel_pt_bug(decoder);
goto out;
case INTEL_PT_OVF:
err = intel_pt_overflow(decoder);
goto out;
case INTEL_PT_PSBEND:
err = 0;
goto out;
case INTEL_PT_PSB:
case INTEL_PT_VMCS:
case INTEL_PT_MNT:
case INTEL_PT_PAD:
default:
break;
}
}
out:
decoder->in_psb = false;
return err;
}
static int intel_pt_walk_to_ip(struct intel_pt_decoder *decoder)
{
int err;
while (1) {
err = intel_pt_get_next_packet(decoder);
if (err)
return err;
switch (decoder->packet.type) {
case INTEL_PT_TIP_PGD:
decoder->continuous_period = false;
decoder->pge = false;
if (intel_pt_have_ip(decoder))
intel_pt_set_ip(decoder);
if (!decoder->ip)
break;
decoder->state.type |= INTEL_PT_TRACE_END;
return 0;
case INTEL_PT_TIP_PGE:
decoder->pge = true;
intel_pt_mtc_cyc_cnt_pge(decoder);
if (intel_pt_have_ip(decoder))
intel_pt_set_ip(decoder);
if (!decoder->ip)
break;
decoder->state.type |= INTEL_PT_TRACE_BEGIN;
return 0;
case INTEL_PT_TIP:
decoder->pge = true;
if (intel_pt_have_ip(decoder))
intel_pt_set_ip(decoder);
if (!decoder->ip)
break;
return 0;
case INTEL_PT_FUP:
if (intel_pt_have_ip(decoder))
intel_pt_set_ip(decoder);
if (decoder->ip)
return 0;
break;
case INTEL_PT_MTC:
intel_pt_calc_mtc_timestamp(decoder);
break;
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:
intel_pt_calc_cyc_timestamp(decoder);
break;
case INTEL_PT_CBR:
intel_pt_calc_cbr(decoder);
break;
case INTEL_PT_PIP:
intel_pt_set_pip(decoder);
break;
case INTEL_PT_MODE_EXEC:
intel_pt_mode_exec_status(decoder);
break;
case INTEL_PT_MODE_TSX:
intel_pt_update_in_tx(decoder);
break;
case INTEL_PT_OVF:
return intel_pt_overflow(decoder);
case INTEL_PT_BAD:
return intel_pt_bug(decoder);
case INTEL_PT_TRACESTOP:
decoder->pge = false;
decoder->continuous_period = false;
intel_pt_clear_tx_flags(decoder);
decoder->have_tma = false;
break;
case INTEL_PT_PSB:
decoder->state.psb_offset = decoder->pos;
decoder->psb_ip = 0;
decoder->last_ip = 0;
decoder->have_last_ip = true;
intel_pt_clear_stack(&decoder->stack);
err = intel_pt_walk_psb(decoder);
if (err)
return err;
decoder->state.type = INTEL_PT_PSB_EVT;
decoder->state.from_ip = decoder->psb_ip;
decoder->state.to_ip = 0;
return 0;
case INTEL_PT_TNT:
case INTEL_PT_PSBEND:
case INTEL_PT_VMCS:
case INTEL_PT_MNT:
case INTEL_PT_PAD:
case INTEL_PT_PTWRITE:
case INTEL_PT_PTWRITE_IP:
case INTEL_PT_EXSTOP:
case INTEL_PT_EXSTOP_IP:
case INTEL_PT_MWAIT:
case INTEL_PT_PWRE:
case INTEL_PT_PWRX:
case INTEL_PT_BBP:
case INTEL_PT_BIP:
case INTEL_PT_BEP:
case INTEL_PT_BEP_IP:
case INTEL_PT_CFE:
case INTEL_PT_CFE_IP:
case INTEL_PT_EVD:
default:
break;
}
}
}
static int intel_pt_sync_ip(struct intel_pt_decoder *decoder)
{
int err;
intel_pt_clear_fup_event(decoder);
decoder->overflow = false;
if (!decoder->branch_enable) {
decoder->pkt_state = INTEL_PT_STATE_IN_SYNC;
decoder->state.type = 0;
return 0;
}
intel_pt_log("Scanning for full IP\n");
err = intel_pt_walk_to_ip(decoder);
if (err || ((decoder->state.type & INTEL_PT_PSB_EVT) && !decoder->ip))
return err;
if (decoder->hop)
decoder->pkt_state = INTEL_PT_STATE_RESAMPLE;
else
decoder->pkt_state = INTEL_PT_STATE_IN_SYNC;
decoder->state.from_ip = 0;
decoder->state.to_ip = decoder->ip;
intel_pt_log_to("Setting IP", decoder->ip);
return 0;
}
static int intel_pt_part_psb(struct intel_pt_decoder *decoder)
{
const unsigned char *end = decoder->buf + decoder->len;
size_t i;
for (i = INTEL_PT_PSB_LEN - 1; i; i--) {
if (i > decoder->len)
continue;
if (!memcmp(end - i, INTEL_PT_PSB_STR, i))
return i;
}
return 0;
}
static int intel_pt_rest_psb(struct intel_pt_decoder *decoder, int part_psb)
{
size_t rest_psb = INTEL_PT_PSB_LEN - part_psb;
const char *psb = INTEL_PT_PSB_STR;
if (rest_psb > decoder->len ||
memcmp(decoder->buf, psb + part_psb, rest_psb))
return 0;
return rest_psb;
}
static int intel_pt_get_split_psb(struct intel_pt_decoder *decoder,
int part_psb)
{
int rest_psb, ret;
decoder->pos += decoder->len;
decoder->len = 0;
ret = intel_pt_get_next_data(decoder, false);
if (ret)
return ret;
rest_psb = intel_pt_rest_psb(decoder, part_psb);
if (!rest_psb)
return 0;
decoder->pos -= part_psb;
decoder->next_buf = decoder->buf + rest_psb;
decoder->next_len = decoder->len - rest_psb;
memcpy(decoder->temp_buf, INTEL_PT_PSB_STR, INTEL_PT_PSB_LEN);
decoder->buf = decoder->temp_buf;
decoder->len = INTEL_PT_PSB_LEN;
return 0;
}
static int intel_pt_scan_for_psb(struct intel_pt_decoder *decoder)
{
unsigned char *next;
int ret;
intel_pt_log("Scanning for PSB\n");
while (1) {
if (!decoder->len) {
ret = intel_pt_get_next_data(decoder, false);
if (ret)
return ret;
}
next = memmem(decoder->buf, decoder->len, INTEL_PT_PSB_STR,
INTEL_PT_PSB_LEN);
if (!next) {
int part_psb;
part_psb = intel_pt_part_psb(decoder);
if (part_psb) {
ret = intel_pt_get_split_psb(decoder, part_psb);
if (ret)
return ret;
} else {
decoder->pos += decoder->len;
decoder->len = 0;
}
continue;
}
decoder->pkt_step = next - decoder->buf;
return intel_pt_get_next_packet(decoder);
}
}
static int intel_pt_sync(struct intel_pt_decoder *decoder)
{
int err;
decoder->pge = false;
decoder->continuous_period = false;
decoder->have_last_ip = false;
decoder->last_ip = 0;
decoder->psb_ip = 0;
decoder->ip = 0;
intel_pt_clear_stack(&decoder->stack);
err = intel_pt_scan_for_psb(decoder);
if (err)
return err;
if (decoder->vm_time_correlation) {
decoder->in_psb = true;
if (!decoder->timestamp)
decoder->timestamp = 1;
decoder->state.type = 0;
decoder->pkt_state = INTEL_PT_STATE_VM_TIME_CORRELATION;
return 0;
}
decoder->have_last_ip = true;
decoder->pkt_state = INTEL_PT_STATE_IN_SYNC;
err = intel_pt_walk_psb(decoder);
if (err)
return err;
decoder->state.type = INTEL_PT_PSB_EVT;
decoder->state.from_ip = decoder->psb_ip;
decoder->state.to_ip = 0;
if (decoder->ip) {
if (decoder->hop)
decoder->pkt_state = INTEL_PT_STATE_RESAMPLE;
else
decoder->pkt_state = INTEL_PT_STATE_IN_SYNC;
}
return 0;
}
static uint64_t intel_pt_est_timestamp(struct intel_pt_decoder *decoder)
{
uint64_t est = decoder->sample_insn_cnt << 1;
if (!decoder->cbr || !decoder->max_non_turbo_ratio)
goto out;
est *= decoder->max_non_turbo_ratio;
est /= decoder->cbr;
out:
return decoder->sample_timestamp + est;
}
const struct intel_pt_state *intel_pt_decode(struct intel_pt_decoder *decoder)
{
int err;
do {
decoder->state.type = INTEL_PT_BRANCH;
decoder->state.flags = 0;
switch (decoder->pkt_state) {
case INTEL_PT_STATE_NO_PSB:
err = intel_pt_sync(decoder);
break;
case INTEL_PT_STATE_NO_IP:
decoder->have_last_ip = false;
decoder->last_ip = 0;
decoder->ip = 0;
fallthrough;
case INTEL_PT_STATE_ERR_RESYNC:
err = intel_pt_sync_ip(decoder);
break;
case INTEL_PT_STATE_IN_SYNC:
err = intel_pt_walk_trace(decoder);
break;
case INTEL_PT_STATE_TNT:
case INTEL_PT_STATE_TNT_CONT:
err = intel_pt_walk_tnt(decoder);
if (err == -EAGAIN)
err = intel_pt_walk_trace(decoder);
break;
case INTEL_PT_STATE_TIP:
case INTEL_PT_STATE_TIP_PGD:
err = intel_pt_walk_tip(decoder);
break;
case INTEL_PT_STATE_FUP:
err = intel_pt_walk_fup(decoder);
if (err == -EAGAIN)
err = intel_pt_walk_fup_tip(decoder);
break;
case INTEL_PT_STATE_FUP_NO_TIP:
err = intel_pt_walk_fup(decoder);
if (err == -EAGAIN)
err = intel_pt_walk_trace(decoder);
break;
case INTEL_PT_STATE_FUP_IN_PSB:
err = intel_pt_fup_in_psb(decoder);
break;
case INTEL_PT_STATE_RESAMPLE:
err = intel_pt_resample(decoder);
break;
case INTEL_PT_STATE_VM_TIME_CORRELATION:
err = intel_pt_vm_time_correlation(decoder);
break;
default:
err = intel_pt_bug(decoder);
break;
}
} while (err == -ENOLINK);
if (err) {
decoder->state.err = intel_pt_ext_err(err);
if (err != -EOVERFLOW)
decoder->state.from_ip = decoder->ip;
intel_pt_update_sample_time(decoder);
decoder->sample_tot_cyc_cnt = decoder->tot_cyc_cnt;
intel_pt_set_nr(decoder);
} else {
decoder->state.err = 0;
if (decoder->cbr != decoder->cbr_seen) {
decoder->cbr_seen = decoder->cbr;
if (!decoder->state.type) {
decoder->state.from_ip = decoder->ip;
decoder->state.to_ip = 0;
}
decoder->state.type |= INTEL_PT_CBR_CHG;
decoder->state.cbr_payload = decoder->cbr_payload;
decoder->state.cbr = decoder->cbr;
}
if (intel_pt_sample_time(decoder->pkt_state)) {
intel_pt_update_sample_time(decoder);
if (decoder->sample_cyc) {
decoder->sample_tot_cyc_cnt = decoder->tot_cyc_cnt;
decoder->state.flags |= INTEL_PT_SAMPLE_IPC;
decoder->sample_cyc = false;
}
}
if (!decoder->have_cyc)
decoder->state.flags |= INTEL_PT_SAMPLE_IPC;
}
if ((decoder->state.type & INTEL_PT_PSB_EVT) && decoder->tsc_timestamp)
decoder->sample_timestamp = decoder->tsc_timestamp;
decoder->state.from_nr = decoder->nr;
decoder->state.to_nr = decoder->next_nr;
decoder->nr = decoder->next_nr;
decoder->state.timestamp = decoder->sample_timestamp;
decoder->state.est_timestamp = intel_pt_est_timestamp(decoder);
decoder->state.tot_insn_cnt = decoder->tot_insn_cnt;
decoder->state.tot_cyc_cnt = decoder->sample_tot_cyc_cnt;
return &decoder->state;
}
static bool intel_pt_next_psb(unsigned char **buf, size_t *len)
{
unsigned char *next;
next = memmem(*buf, *len, INTEL_PT_PSB_STR, INTEL_PT_PSB_LEN);
if (next) {
*len -= next - *buf;
*buf = next;
return true;
}
return false;
}
static bool intel_pt_step_psb(unsigned char **buf, size_t *len)
{
unsigned char *next;
if (!*len)
return false;
next = memmem(*buf + 1, *len - 1, INTEL_PT_PSB_STR, INTEL_PT_PSB_LEN);
if (next) {
*len -= next - *buf;
*buf = next;
return true;
}
return false;
}
static unsigned char *intel_pt_last_psb(unsigned char *buf, size_t len)
{
const char *n = INTEL_PT_PSB_STR;
unsigned char *p;
size_t k;
if (len < INTEL_PT_PSB_LEN)
return NULL;
k = len - INTEL_PT_PSB_LEN + 1;
while (1) {
p = memrchr(buf, n[0], k);
if (!p)
return NULL;
if (!memcmp(p + 1, n + 1, INTEL_PT_PSB_LEN - 1))
return p;
k = p - buf;
if (!k)
return NULL;
}
}
static bool intel_pt_next_tsc(unsigned char *buf, size_t len, uint64_t *tsc,
size_t *rem)
{
enum intel_pt_pkt_ctx ctx = INTEL_PT_NO_CTX;
struct intel_pt_pkt packet;
int ret;
while (len) {
ret = intel_pt_get_packet(buf, len, &packet, &ctx);
if (ret <= 0)
return false;
if (packet.type == INTEL_PT_TSC) {
*tsc = packet.payload;
*rem = len;
return true;
}
if (packet.type == INTEL_PT_PSBEND)
return false;
buf += ret;
len -= ret;
}
return false;
}
static int intel_pt_tsc_cmp(uint64_t tsc1, uint64_t tsc2)
{
const uint64_t halfway = (1ULL << 55);
if (tsc1 == tsc2)
return 0;
if (tsc1 < tsc2) {
if (tsc2 - tsc1 < halfway)
return -1;
else
return 1;
} else {
if (tsc1 - tsc2 < halfway)
return 1;
else
return -1;
}
}
#define MAX_PADDING (PERF_AUXTRACE_RECORD_ALIGNMENT - 1)
static unsigned char *adj_for_padding(unsigned char *buf_b,
unsigned char *buf_a, size_t len_a)
{
unsigned char *p = buf_b - MAX_PADDING;
unsigned char *q = buf_a + len_a - MAX_PADDING;
int i;
for (i = MAX_PADDING; i; i--, p++, q++) {
if (*p != *q)
break;
}
return p;
}
static unsigned char *intel_pt_find_overlap_tsc(unsigned char *buf_a,
size_t len_a,
unsigned char *buf_b,
size_t len_b, bool *consecutive,
bool ooo_tsc)
{
uint64_t tsc_a, tsc_b;
unsigned char *p;
size_t len, rem_a, rem_b;
p = intel_pt_last_psb(buf_a, len_a);
if (!p)
return buf_b;
len = len_a - (p - buf_a);
if (!intel_pt_next_tsc(p, len, &tsc_a, &rem_a)) {
len_a -= len;
p = intel_pt_last_psb(buf_a, len_a);
if (!p)
return buf_b;
len = len_a - (p - buf_a);
if (!intel_pt_next_tsc(p, len, &tsc_a, &rem_a))
return buf_b;
}
while (1) {
if (intel_pt_next_tsc(buf_b, len_b, &tsc_b, &rem_b)) {
int cmp = intel_pt_tsc_cmp(tsc_a, tsc_b);
if (!cmp && rem_b >= rem_a) {
unsigned char *start;
*consecutive = true;
start = buf_b + len_b - (rem_b - rem_a);
return adj_for_padding(start, buf_a, len_a);
}
if (cmp < 0 && !ooo_tsc)
return buf_b;
}
if (!intel_pt_step_psb(&buf_b, &len_b))
return buf_b + len_b;
}
}
unsigned char *intel_pt_find_overlap(unsigned char *buf_a, size_t len_a,
unsigned char *buf_b, size_t len_b,
bool have_tsc, bool *consecutive,
bool ooo_tsc)
{
unsigned char *found;
if (!intel_pt_next_psb(&buf_b, &len_b))
return buf_b + len_b;
if (!intel_pt_next_psb(&buf_a, &len_a))
return buf_b;
if (have_tsc) {
found = intel_pt_find_overlap_tsc(buf_a, len_a, buf_b, len_b,
consecutive, ooo_tsc);
if (found)
return found;
}
while (len_b < len_a) {
if (!intel_pt_step_psb(&buf_a, &len_a))
return buf_b;
}
while (1) {
found = memmem(buf_a, len_a, buf_b, len_a);
if (found) {
*consecutive = true;
return adj_for_padding(buf_b + len_a, buf_a, len_a);
}
if (!intel_pt_step_psb(&buf_a, &len_a))
return buf_b;
}
}
struct fast_forward_data {
uint64_t timestamp;
uint64_t buf_timestamp;
};
static int intel_pt_ff_cb(struct intel_pt_buffer *buffer, void *data)
{
struct fast_forward_data *d = data;
unsigned char *buf;
uint64_t tsc;
size_t rem;
size_t len;
buf = (unsigned char *)buffer->buf;
len = buffer->len;
if (!intel_pt_next_psb(&buf, &len) ||
!intel_pt_next_tsc(buf, len, &tsc, &rem))
return 0;
tsc = intel_pt_8b_tsc(tsc, buffer->ref_timestamp);
intel_pt_log("Buffer 1st timestamp " x64_fmt " ref timestamp " x64_fmt "\n",
tsc, buffer->ref_timestamp);
if (tsc < d->timestamp)
d->buf_timestamp = buffer->ref_timestamp;
else
return 1;
return 0;
}
int intel_pt_fast_forward(struct intel_pt_decoder *decoder, uint64_t timestamp)
{
struct fast_forward_data d = { .timestamp = timestamp };
unsigned char *buf;
size_t len;
int err;
intel_pt_log("Fast forward towards timestamp " x64_fmt "\n", timestamp);
err = decoder->lookahead(decoder->data, intel_pt_ff_cb, &d);
if (err < 0)
return err;
if (d.buf_timestamp) {
do {
decoder->pos += decoder->len;
decoder->len = 0;
err = intel_pt_get_next_data(decoder, true);
if (err && err != -ENOLINK)
return err;
} while (decoder->buf_timestamp != d.buf_timestamp);
}
if (!decoder->buf)
return 0;
buf = (unsigned char *)decoder->buf;
len = decoder->len;
if (!intel_pt_next_psb(&buf, &len))
return 0;
do {
uint64_t tsc;
size_t rem;
if (!intel_pt_next_tsc(buf, len, &tsc, &rem))
break;
tsc = intel_pt_8b_tsc(tsc, decoder->buf_timestamp);
if (tsc < timestamp) {
intel_pt_log("Fast forward to next PSB timestamp " x64_fmt "\n", tsc);
decoder->pos += decoder->len - len;
decoder->buf = buf;
decoder->len = len;
intel_pt_reposition(decoder);
} else {
break;
}
} while (intel_pt_step_psb(&buf, &len));
return 0;
}