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 <linux/kernel.h>
   8#include <stdio.h>
   9#include <string.h>
  10#include <endian.h>
  11#include <byteswap.h>
  12#include "../../../arch/x86/include/asm/insn.h"
  13
  14#include "../../../arch/x86/lib/inat.c"
  15#include "../../../arch/x86/lib/insn.c"
  16
  17#include "event.h"
  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 0x01:
  47                        switch (insn->modrm.bytes[0]) {
  48                        case 0xc2: /* vmlaunch */
  49                        case 0xc3: /* vmresume */
  50                                op = INTEL_PT_OP_VMENTRY;
  51                                branch = INTEL_PT_BR_INDIRECT;
  52                                break;
  53                        default:
  54                                break;
  55                        }
  56                        break;
  57                case 0x05: /* syscall */
  58                case 0x34: /* sysenter */
  59                        op = INTEL_PT_OP_SYSCALL;
  60                        branch = INTEL_PT_BR_INDIRECT;
  61                        break;
  62                case 0x07: /* sysret */
  63                case 0x35: /* sysexit */
  64                        op = INTEL_PT_OP_SYSRET;
  65                        branch = INTEL_PT_BR_INDIRECT;
  66                        break;
  67                case 0x80 ... 0x8f: /* jcc */
  68                        op = INTEL_PT_OP_JCC;
  69                        branch = INTEL_PT_BR_CONDITIONAL;
  70                        break;
  71                default:
  72                        break;
  73                }
  74                break;
  75        case 0x70 ... 0x7f: /* jcc */
  76                op = INTEL_PT_OP_JCC;
  77                branch = INTEL_PT_BR_CONDITIONAL;
  78                break;
  79        case 0xc2: /* near ret */
  80        case 0xc3: /* near ret */
  81        case 0xca: /* far ret */
  82        case 0xcb: /* far ret */
  83                op = INTEL_PT_OP_RET;
  84                branch = INTEL_PT_BR_INDIRECT;
  85                break;
  86        case 0xcf: /* iret */
  87                op = INTEL_PT_OP_IRET;
  88                branch = INTEL_PT_BR_INDIRECT;
  89                break;
  90        case 0xcc ... 0xce: /* int */
  91                op = INTEL_PT_OP_INT;
  92                branch = INTEL_PT_BR_INDIRECT;
  93                break;
  94        case 0xe8: /* call near rel */
  95                op = INTEL_PT_OP_CALL;
  96                branch = INTEL_PT_BR_UNCONDITIONAL;
  97                break;
  98        case 0x9a: /* call far absolute */
  99                op = INTEL_PT_OP_CALL;
 100                branch = INTEL_PT_BR_INDIRECT;
 101                break;
 102        case 0xe0 ... 0xe2: /* loop */
 103                op = INTEL_PT_OP_LOOP;
 104                branch = INTEL_PT_BR_CONDITIONAL;
 105                break;
 106        case 0xe3: /* jcc */
 107                op = INTEL_PT_OP_JCC;
 108                branch = INTEL_PT_BR_CONDITIONAL;
 109                break;
 110        case 0xe9: /* jmp */
 111        case 0xeb: /* jmp */
 112                op = INTEL_PT_OP_JMP;
 113                branch = INTEL_PT_BR_UNCONDITIONAL;
 114                break;
 115        case 0xea: /* far jmp */
 116                op = INTEL_PT_OP_JMP;
 117                branch = INTEL_PT_BR_INDIRECT;
 118                break;
 119        case 0xff: /* call near absolute, call far absolute ind */
 120                ext = (insn->modrm.bytes[0] >> 3) & 0x7;
 121                switch (ext) {
 122                case 2: /* near ind call */
 123                case 3: /* far ind call */
 124                        op = INTEL_PT_OP_CALL;
 125                        branch = INTEL_PT_BR_INDIRECT;
 126                        break;
 127                case 4:
 128                case 5:
 129                        op = INTEL_PT_OP_JMP;
 130                        branch = INTEL_PT_BR_INDIRECT;
 131                        break;
 132                default:
 133                        break;
 134                }
 135                break;
 136        default:
 137                break;
 138        }
 139
 140        intel_pt_insn->op = op;
 141        intel_pt_insn->branch = branch;
 142        intel_pt_insn->length = insn->length;
 143
 144        if (branch == INTEL_PT_BR_CONDITIONAL ||
 145            branch == INTEL_PT_BR_UNCONDITIONAL) {
 146#if __BYTE_ORDER == __BIG_ENDIAN
 147                switch (insn->immediate.nbytes) {
 148                case 1:
 149                        intel_pt_insn->rel = insn->immediate.value;
 150                        break;
 151                case 2:
 152                        intel_pt_insn->rel =
 153                                        bswap_16((short)insn->immediate.value);
 154                        break;
 155                case 4:
 156                        intel_pt_insn->rel = bswap_32(insn->immediate.value);
 157                        break;
 158                default:
 159                        intel_pt_insn->rel = 0;
 160                        break;
 161                }
 162#else
 163                intel_pt_insn->rel = insn->immediate.value;
 164#endif
 165        }
 166}
 167
 168int intel_pt_get_insn(const unsigned char *buf, size_t len, int x86_64,
 169                      struct intel_pt_insn *intel_pt_insn)
 170{
 171        struct insn insn;
 172        int ret;
 173
 174        ret = insn_decode(&insn, buf, len,
 175                          x86_64 ? INSN_MODE_64 : INSN_MODE_32);
 176        if (ret < 0 || insn.length > len)
 177                return -1;
 178
 179        intel_pt_insn_decoder(&insn, intel_pt_insn);
 180        if (insn.length < INTEL_PT_INSN_BUF_SZ)
 181                memcpy(intel_pt_insn->buf, buf, insn.length);
 182        else
 183                memcpy(intel_pt_insn->buf, buf, INTEL_PT_INSN_BUF_SZ);
 184        return 0;
 185}
 186
 187int arch_is_branch(const unsigned char *buf, size_t len, int x86_64)
 188{
 189        struct intel_pt_insn in;
 190        if (intel_pt_get_insn(buf, len, x86_64, &in) < 0)
 191                return -1;
 192        return in.branch != INTEL_PT_BR_NO_BRANCH;
 193}
 194
 195const char *dump_insn(struct perf_insn *x, uint64_t ip __maybe_unused,
 196                      u8 *inbuf, int inlen, int *lenp)
 197{
 198        struct insn insn;
 199        int n, i, ret;
 200        int left;
 201
 202        ret = insn_decode(&insn, inbuf, inlen,
 203                          x->is64bit ? INSN_MODE_64 : INSN_MODE_32);
 204
 205        if (ret < 0 || insn.length > inlen)
 206                return "<bad>";
 207        if (lenp)
 208                *lenp = insn.length;
 209        left = sizeof(x->out);
 210        n = snprintf(x->out, left, "insn: ");
 211        left -= n;
 212        for (i = 0; i < insn.length; i++) {
 213                n += snprintf(x->out + n, left, "%02x ", inbuf[i]);
 214                left -= n;
 215        }
 216        return x->out;
 217}
 218
 219const char *branch_name[] = {
 220        [INTEL_PT_OP_OTHER]     = "Other",
 221        [INTEL_PT_OP_CALL]      = "Call",
 222        [INTEL_PT_OP_RET]       = "Ret",
 223        [INTEL_PT_OP_JCC]       = "Jcc",
 224        [INTEL_PT_OP_JMP]       = "Jmp",
 225        [INTEL_PT_OP_LOOP]      = "Loop",
 226        [INTEL_PT_OP_IRET]      = "IRet",
 227        [INTEL_PT_OP_INT]       = "Int",
 228        [INTEL_PT_OP_SYSCALL]   = "Syscall",
 229        [INTEL_PT_OP_SYSRET]    = "Sysret",
 230        [INTEL_PT_OP_VMENTRY]   = "VMentry",
 231};
 232
 233const char *intel_pt_insn_name(enum intel_pt_insn_op op)
 234{
 235        return branch_name[op];
 236}
 237
 238int intel_pt_insn_desc(const struct intel_pt_insn *intel_pt_insn, char *buf,
 239                       size_t buf_len)
 240{
 241        switch (intel_pt_insn->branch) {
 242        case INTEL_PT_BR_CONDITIONAL:
 243        case INTEL_PT_BR_UNCONDITIONAL:
 244                return snprintf(buf, buf_len, "%s %s%d",
 245                                intel_pt_insn_name(intel_pt_insn->op),
 246                                intel_pt_insn->rel > 0 ? "+" : "",
 247                                intel_pt_insn->rel);
 248        case INTEL_PT_BR_NO_BRANCH:
 249        case INTEL_PT_BR_INDIRECT:
 250                return snprintf(buf, buf_len, "%s",
 251                                intel_pt_insn_name(intel_pt_insn->op));
 252        default:
 253                break;
 254        }
 255        return 0;
 256}
 257
 258int intel_pt_insn_type(enum intel_pt_insn_op op)
 259{
 260        switch (op) {
 261        case INTEL_PT_OP_OTHER:
 262                return 0;
 263        case INTEL_PT_OP_CALL:
 264                return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL;
 265        case INTEL_PT_OP_RET:
 266                return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN;
 267        case INTEL_PT_OP_JCC:
 268                return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CONDITIONAL;
 269        case INTEL_PT_OP_JMP:
 270                return PERF_IP_FLAG_BRANCH;
 271        case INTEL_PT_OP_LOOP:
 272                return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CONDITIONAL;
 273        case INTEL_PT_OP_IRET:
 274                return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN |
 275                       PERF_IP_FLAG_INTERRUPT;
 276        case INTEL_PT_OP_INT:
 277                return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL |
 278                       PERF_IP_FLAG_INTERRUPT;
 279        case INTEL_PT_OP_SYSCALL:
 280                return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL |
 281                       PERF_IP_FLAG_SYSCALLRET;
 282        case INTEL_PT_OP_SYSRET:
 283                return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN |
 284                       PERF_IP_FLAG_SYSCALLRET;
 285        case INTEL_PT_OP_VMENTRY:
 286                return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL |
 287                       PERF_IP_FLAG_VMENTRY;
 288        default:
 289                return 0;
 290        }
 291}
 292