linux/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.c
<<
>>
Prefs
   1/*
   2 * intel_pt_insn_decoder.c: Intel Processor Trace support
   3 * Copyright (c) 2013-2014, Intel Corporation.
   4 *
   5 * This program is free software; you can redistribute it and/or modify it
   6 * under the terms and conditions of the GNU General Public License,
   7 * version 2, as published by the Free Software Foundation.
   8 *
   9 * This program is distributed in the hope it will be useful, but WITHOUT
  10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  12 * more details.
  13 *
  14 */
  15
  16#include <stdio.h>
  17#include <string.h>
  18#include <endian.h>
  19#include <byteswap.h>
  20
  21#include "event.h"
  22
  23#include "insn.h"
  24
  25#include "inat.c"
  26#include "insn.c"
  27
  28#include "intel-pt-insn-decoder.h"
  29
  30/* Based on branch_type() from perf_event_intel_lbr.c */
  31static void intel_pt_insn_decoder(struct insn *insn,
  32                                  struct intel_pt_insn *intel_pt_insn)
  33{
  34        enum intel_pt_insn_op op = INTEL_PT_OP_OTHER;
  35        enum intel_pt_insn_branch branch = INTEL_PT_BR_NO_BRANCH;
  36        int ext;
  37
  38        if (insn_is_avx(insn)) {
  39                intel_pt_insn->op = INTEL_PT_OP_OTHER;
  40                intel_pt_insn->branch = INTEL_PT_BR_NO_BRANCH;
  41                intel_pt_insn->length = insn->length;
  42                return;
  43        }
  44
  45        switch (insn->opcode.bytes[0]) {
  46        case 0xf:
  47                switch (insn->opcode.bytes[1]) {
  48                case 0x05: /* syscall */
  49                case 0x34: /* sysenter */
  50                        op = INTEL_PT_OP_SYSCALL;
  51                        branch = INTEL_PT_BR_INDIRECT;
  52                        break;
  53                case 0x07: /* sysret */
  54                case 0x35: /* sysexit */
  55                        op = INTEL_PT_OP_SYSRET;
  56                        branch = INTEL_PT_BR_INDIRECT;
  57                        break;
  58                case 0x80 ... 0x8f: /* jcc */
  59                        op = INTEL_PT_OP_JCC;
  60                        branch = INTEL_PT_BR_CONDITIONAL;
  61                        break;
  62                default:
  63                        break;
  64                }
  65                break;
  66        case 0x70 ... 0x7f: /* jcc */
  67                op = INTEL_PT_OP_JCC;
  68                branch = INTEL_PT_BR_CONDITIONAL;
  69                break;
  70        case 0xc2: /* near ret */
  71        case 0xc3: /* near ret */
  72        case 0xca: /* far ret */
  73        case 0xcb: /* far ret */
  74                op = INTEL_PT_OP_RET;
  75                branch = INTEL_PT_BR_INDIRECT;
  76                break;
  77        case 0xcf: /* iret */
  78                op = INTEL_PT_OP_IRET;
  79                branch = INTEL_PT_BR_INDIRECT;
  80                break;
  81        case 0xcc ... 0xce: /* int */
  82                op = INTEL_PT_OP_INT;
  83                branch = INTEL_PT_BR_INDIRECT;
  84                break;
  85        case 0xe8: /* call near rel */
  86                op = INTEL_PT_OP_CALL;
  87                branch = INTEL_PT_BR_UNCONDITIONAL;
  88                break;
  89        case 0x9a: /* call far absolute */
  90                op = INTEL_PT_OP_CALL;
  91                branch = INTEL_PT_BR_INDIRECT;
  92                break;
  93        case 0xe0 ... 0xe2: /* loop */
  94                op = INTEL_PT_OP_LOOP;
  95                branch = INTEL_PT_BR_CONDITIONAL;
  96                break;
  97        case 0xe3: /* jcc */
  98                op = INTEL_PT_OP_JCC;
  99                branch = INTEL_PT_BR_CONDITIONAL;
 100                break;
 101        case 0xe9: /* jmp */
 102        case 0xeb: /* jmp */
 103                op = INTEL_PT_OP_JMP;
 104                branch = INTEL_PT_BR_UNCONDITIONAL;
 105                break;
 106        case 0xea: /* far jmp */
 107                op = INTEL_PT_OP_JMP;
 108                branch = INTEL_PT_BR_INDIRECT;
 109                break;
 110        case 0xff: /* call near absolute, call far absolute ind */
 111                ext = (insn->modrm.bytes[0] >> 3) & 0x7;
 112                switch (ext) {
 113                case 2: /* near ind call */
 114                case 3: /* far ind call */
 115                        op = INTEL_PT_OP_CALL;
 116                        branch = INTEL_PT_BR_INDIRECT;
 117                        break;
 118                case 4:
 119                case 5:
 120                        op = INTEL_PT_OP_JMP;
 121                        branch = INTEL_PT_BR_INDIRECT;
 122                        break;
 123                default:
 124                        break;
 125                }
 126                break;
 127        default:
 128                break;
 129        }
 130
 131        intel_pt_insn->op = op;
 132        intel_pt_insn->branch = branch;
 133        intel_pt_insn->length = insn->length;
 134
 135        if (branch == INTEL_PT_BR_CONDITIONAL ||
 136            branch == INTEL_PT_BR_UNCONDITIONAL) {
 137#if __BYTE_ORDER == __BIG_ENDIAN
 138                switch (insn->immediate.nbytes) {
 139                case 1:
 140                        intel_pt_insn->rel = insn->immediate.value;
 141                        break;
 142                case 2:
 143                        intel_pt_insn->rel =
 144                                        bswap_16((short)insn->immediate.value);
 145                        break;
 146                case 4:
 147                        intel_pt_insn->rel = bswap_32(insn->immediate.value);
 148                        break;
 149                default:
 150                        intel_pt_insn->rel = 0;
 151                        break;
 152                }
 153#else
 154                intel_pt_insn->rel = insn->immediate.value;
 155#endif
 156        }
 157}
 158
 159int intel_pt_get_insn(const unsigned char *buf, size_t len, int x86_64,
 160                      struct intel_pt_insn *intel_pt_insn)
 161{
 162        struct insn insn;
 163
 164        insn_init(&insn, buf, len, x86_64);
 165        insn_get_length(&insn);
 166        if (!insn_complete(&insn) || insn.length > len)
 167                return -1;
 168        intel_pt_insn_decoder(&insn, intel_pt_insn);
 169        if (insn.length < INTEL_PT_INSN_DBG_BUF_SZ)
 170                memcpy(intel_pt_insn->buf, buf, insn.length);
 171        else
 172                memcpy(intel_pt_insn->buf, buf, INTEL_PT_INSN_DBG_BUF_SZ);
 173        return 0;
 174}
 175
 176const char *branch_name[] = {
 177        [INTEL_PT_OP_OTHER]     = "Other",
 178        [INTEL_PT_OP_CALL]      = "Call",
 179        [INTEL_PT_OP_RET]       = "Ret",
 180        [INTEL_PT_OP_JCC]       = "Jcc",
 181        [INTEL_PT_OP_JMP]       = "Jmp",
 182        [INTEL_PT_OP_LOOP]      = "Loop",
 183        [INTEL_PT_OP_IRET]      = "IRet",
 184        [INTEL_PT_OP_INT]       = "Int",
 185        [INTEL_PT_OP_SYSCALL]   = "Syscall",
 186        [INTEL_PT_OP_SYSRET]    = "Sysret",
 187};
 188
 189const char *intel_pt_insn_name(enum intel_pt_insn_op op)
 190{
 191        return branch_name[op];
 192}
 193
 194int intel_pt_insn_desc(const struct intel_pt_insn *intel_pt_insn, char *buf,
 195                       size_t buf_len)
 196{
 197        switch (intel_pt_insn->branch) {
 198        case INTEL_PT_BR_CONDITIONAL:
 199        case INTEL_PT_BR_UNCONDITIONAL:
 200                return snprintf(buf, buf_len, "%s %s%d",
 201                                intel_pt_insn_name(intel_pt_insn->op),
 202                                intel_pt_insn->rel > 0 ? "+" : "",
 203                                intel_pt_insn->rel);
 204        case INTEL_PT_BR_NO_BRANCH:
 205        case INTEL_PT_BR_INDIRECT:
 206                return snprintf(buf, buf_len, "%s",
 207                                intel_pt_insn_name(intel_pt_insn->op));
 208        default:
 209                break;
 210        }
 211        return 0;
 212}
 213
 214size_t intel_pt_insn_max_size(void)
 215{
 216        return MAX_INSN_SIZE;
 217}
 218
 219int intel_pt_insn_type(enum intel_pt_insn_op op)
 220{
 221        switch (op) {
 222        case INTEL_PT_OP_OTHER:
 223                return 0;
 224        case INTEL_PT_OP_CALL:
 225                return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL;
 226        case INTEL_PT_OP_RET:
 227                return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN;
 228        case INTEL_PT_OP_JCC:
 229                return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CONDITIONAL;
 230        case INTEL_PT_OP_JMP:
 231                return PERF_IP_FLAG_BRANCH;
 232        case INTEL_PT_OP_LOOP:
 233                return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CONDITIONAL;
 234        case INTEL_PT_OP_IRET:
 235                return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN |
 236                       PERF_IP_FLAG_INTERRUPT;
 237        case INTEL_PT_OP_INT:
 238                return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL |
 239                       PERF_IP_FLAG_INTERRUPT;
 240        case INTEL_PT_OP_SYSCALL:
 241                return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL |
 242                       PERF_IP_FLAG_SYSCALLRET;
 243        case INTEL_PT_OP_SYSRET:
 244                return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN |
 245                       PERF_IP_FLAG_SYSCALLRET;
 246        default:
 247                return 0;
 248        }
 249}
 250