linux/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * intel_pt_insn_decoder.c: Intel Processor Trace support
   4 * Copyright (c) 2013-2014, Intel Corporation.
   5 */
   6
   7#include <stdio.h>
   8#include <string.h>
   9#include <endian.h>
  10#include <byteswap.h>
  11
  12#include "event.h"
  13
  14#include "insn.h"
  15
  16#include "inat.c"
  17#include "insn.c"
  18
  19#include "intel-pt-insn-decoder.h"
  20#include "dump-insn.h"
  21
  22#if INTEL_PT_INSN_BUF_SZ < MAX_INSN_SIZE || INTEL_PT_INSN_BUF_SZ > MAX_INSN
  23#error Instruction buffer size too small
  24#endif
  25
  26/* Based on branch_type() from arch/x86/events/intel/lbr.c */
  27static void intel_pt_insn_decoder(struct insn *insn,
  28                                  struct intel_pt_insn *intel_pt_insn)
  29{
  30        enum intel_pt_insn_op op = INTEL_PT_OP_OTHER;
  31        enum intel_pt_insn_branch branch = INTEL_PT_BR_NO_BRANCH;
  32        int ext;
  33
  34        intel_pt_insn->rel = 0;
  35
  36        if (insn_is_avx(insn)) {
  37                intel_pt_insn->op = INTEL_PT_OP_OTHER;
  38                intel_pt_insn->branch = INTEL_PT_BR_NO_BRANCH;
  39                intel_pt_insn->length = insn->length;
  40                return;
  41        }
  42
  43        switch (insn->opcode.bytes[0]) {
  44        case 0xf:
  45                switch (insn->opcode.bytes[1]) {
  46                case 0x05: /* syscall */
  47                case 0x34: /* sysenter */
  48                        op = INTEL_PT_OP_SYSCALL;
  49                        branch = INTEL_PT_BR_INDIRECT;
  50                        break;
  51                case 0x07: /* sysret */
  52                case 0x35: /* sysexit */
  53                        op = INTEL_PT_OP_SYSRET;
  54                        branch = INTEL_PT_BR_INDIRECT;
  55                        break;
  56                case 0x80 ... 0x8f: /* jcc */
  57                        op = INTEL_PT_OP_JCC;
  58                        branch = INTEL_PT_BR_CONDITIONAL;
  59                        break;
  60                default:
  61                        break;
  62                }
  63                break;
  64        case 0x70 ... 0x7f: /* jcc */
  65                op = INTEL_PT_OP_JCC;
  66                branch = INTEL_PT_BR_CONDITIONAL;
  67                break;
  68        case 0xc2: /* near ret */
  69        case 0xc3: /* near ret */
  70        case 0xca: /* far ret */
  71        case 0xcb: /* far ret */
  72                op = INTEL_PT_OP_RET;
  73                branch = INTEL_PT_BR_INDIRECT;
  74                break;
  75        case 0xcf: /* iret */
  76                op = INTEL_PT_OP_IRET;
  77                branch = INTEL_PT_BR_INDIRECT;
  78                break;
  79        case 0xcc ... 0xce: /* int */
  80                op = INTEL_PT_OP_INT;
  81                branch = INTEL_PT_BR_INDIRECT;
  82                break;
  83        case 0xe8: /* call near rel */
  84                op = INTEL_PT_OP_CALL;
  85                branch = INTEL_PT_BR_UNCONDITIONAL;
  86                break;
  87        case 0x9a: /* call far absolute */
  88                op = INTEL_PT_OP_CALL;
  89                branch = INTEL_PT_BR_INDIRECT;
  90                break;
  91        case 0xe0 ... 0xe2: /* loop */
  92                op = INTEL_PT_OP_LOOP;
  93                branch = INTEL_PT_BR_CONDITIONAL;
  94                break;
  95        case 0xe3: /* jcc */
  96                op = INTEL_PT_OP_JCC;
  97                branch = INTEL_PT_BR_CONDITIONAL;
  98                break;
  99        case 0xe9: /* jmp */
 100        case 0xeb: /* jmp */
 101                op = INTEL_PT_OP_JMP;
 102                branch = INTEL_PT_BR_UNCONDITIONAL;
 103                break;
 104        case 0xea: /* far jmp */
 105                op = INTEL_PT_OP_JMP;
 106                branch = INTEL_PT_BR_INDIRECT;
 107                break;
 108        case 0xff: /* call near absolute, call far absolute ind */
 109                ext = (insn->modrm.bytes[0] >> 3) & 0x7;
 110                switch (ext) {
 111                case 2: /* near ind call */
 112                case 3: /* far ind call */
 113                        op = INTEL_PT_OP_CALL;
 114                        branch = INTEL_PT_BR_INDIRECT;
 115                        break;
 116                case 4:
 117                case 5:
 118                        op = INTEL_PT_OP_JMP;
 119                        branch = INTEL_PT_BR_INDIRECT;
 120                        break;
 121                default:
 122                        break;
 123                }
 124                break;
 125        default:
 126                break;
 127        }
 128
 129        intel_pt_insn->op = op;
 130        intel_pt_insn->branch = branch;
 131        intel_pt_insn->length = insn->length;
 132
 133        if (branch == INTEL_PT_BR_CONDITIONAL ||
 134            branch == INTEL_PT_BR_UNCONDITIONAL) {
 135#if __BYTE_ORDER == __BIG_ENDIAN
 136                switch (insn->immediate.nbytes) {
 137                case 1:
 138                        intel_pt_insn->rel = insn->immediate.value;
 139                        break;
 140                case 2:
 141                        intel_pt_insn->rel =
 142                                        bswap_16((short)insn->immediate.value);
 143                        break;
 144                case 4:
 145                        intel_pt_insn->rel = bswap_32(insn->immediate.value);
 146                        break;
 147                default:
 148                        intel_pt_insn->rel = 0;
 149                        break;
 150                }
 151#else
 152                intel_pt_insn->rel = insn->immediate.value;
 153#endif
 154        }
 155}
 156
 157int intel_pt_get_insn(const unsigned char *buf, size_t len, int x86_64,
 158                      struct intel_pt_insn *intel_pt_insn)
 159{
 160        struct insn insn;
 161
 162        insn_init(&insn, buf, len, x86_64);
 163        insn_get_length(&insn);
 164        if (!insn_complete(&insn) || insn.length > len)
 165                return -1;
 166        intel_pt_insn_decoder(&insn, intel_pt_insn);
 167        if (insn.length < INTEL_PT_INSN_BUF_SZ)
 168                memcpy(intel_pt_insn->buf, buf, insn.length);
 169        else
 170                memcpy(intel_pt_insn->buf, buf, INTEL_PT_INSN_BUF_SZ);
 171        return 0;
 172}
 173
 174int arch_is_branch(const unsigned char *buf, size_t len, int x86_64)
 175{
 176        struct intel_pt_insn in;
 177        if (intel_pt_get_insn(buf, len, x86_64, &in) < 0)
 178                return -1;
 179        return in.branch != INTEL_PT_BR_NO_BRANCH;
 180}
 181
 182const char *dump_insn(struct perf_insn *x, uint64_t ip __maybe_unused,
 183                      u8 *inbuf, int inlen, int *lenp)
 184{
 185        struct insn insn;
 186        int n, i;
 187        int left;
 188
 189        insn_init(&insn, inbuf, inlen, x->is64bit);
 190        insn_get_length(&insn);
 191        if (!insn_complete(&insn) || insn.length > inlen)
 192                return "<bad>";
 193        if (lenp)
 194                *lenp = insn.length;
 195        left = sizeof(x->out);
 196        n = snprintf(x->out, left, "insn: ");
 197        left -= n;
 198        for (i = 0; i < insn.length; i++) {
 199                n += snprintf(x->out + n, left, "%02x ", inbuf[i]);
 200                left -= n;
 201        }
 202        return x->out;
 203}
 204
 205const char *branch_name[] = {
 206        [INTEL_PT_OP_OTHER]     = "Other",
 207        [INTEL_PT_OP_CALL]      = "Call",
 208        [INTEL_PT_OP_RET]       = "Ret",
 209        [INTEL_PT_OP_JCC]       = "Jcc",
 210        [INTEL_PT_OP_JMP]       = "Jmp",
 211        [INTEL_PT_OP_LOOP]      = "Loop",
 212        [INTEL_PT_OP_IRET]      = "IRet",
 213        [INTEL_PT_OP_INT]       = "Int",
 214        [INTEL_PT_OP_SYSCALL]   = "Syscall",
 215        [INTEL_PT_OP_SYSRET]    = "Sysret",
 216};
 217
 218const char *intel_pt_insn_name(enum intel_pt_insn_op op)
 219{
 220        return branch_name[op];
 221}
 222
 223int intel_pt_insn_desc(const struct intel_pt_insn *intel_pt_insn, char *buf,
 224                       size_t buf_len)
 225{
 226        switch (intel_pt_insn->branch) {
 227        case INTEL_PT_BR_CONDITIONAL:
 228        case INTEL_PT_BR_UNCONDITIONAL:
 229                return snprintf(buf, buf_len, "%s %s%d",
 230                                intel_pt_insn_name(intel_pt_insn->op),
 231                                intel_pt_insn->rel > 0 ? "+" : "",
 232                                intel_pt_insn->rel);
 233        case INTEL_PT_BR_NO_BRANCH:
 234        case INTEL_PT_BR_INDIRECT:
 235                return snprintf(buf, buf_len, "%s",
 236                                intel_pt_insn_name(intel_pt_insn->op));
 237        default:
 238                break;
 239        }
 240        return 0;
 241}
 242
 243int intel_pt_insn_type(enum intel_pt_insn_op op)
 244{
 245        switch (op) {
 246        case INTEL_PT_OP_OTHER:
 247                return 0;
 248        case INTEL_PT_OP_CALL:
 249                return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL;
 250        case INTEL_PT_OP_RET:
 251                return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN;
 252        case INTEL_PT_OP_JCC:
 253                return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CONDITIONAL;
 254        case INTEL_PT_OP_JMP:
 255                return PERF_IP_FLAG_BRANCH;
 256        case INTEL_PT_OP_LOOP:
 257                return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CONDITIONAL;
 258        case INTEL_PT_OP_IRET:
 259                return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN |
 260                       PERF_IP_FLAG_INTERRUPT;
 261        case INTEL_PT_OP_INT:
 262                return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL |
 263                       PERF_IP_FLAG_INTERRUPT;
 264        case INTEL_PT_OP_SYSCALL:
 265                return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL |
 266                       PERF_IP_FLAG_SYSCALLRET;
 267        case INTEL_PT_OP_SYSRET:
 268                return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN |
 269                       PERF_IP_FLAG_SYSCALLRET;
 270        default:
 271                return 0;
 272        }
 273}
 274