]>
Commit | Line | Data |
---|---|---|
237fae79 AH |
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 | ||
5839a550 | 23 | #include "insn.h" |
237fae79 AH |
24 | |
25 | #include "inat.c" | |
26 | #include "insn.c" | |
27 | ||
28 | #include "intel-pt-insn-decoder.h" | |
29 | ||
faaa8768 | 30 | #if INTEL_PT_INSN_BUF_SZ < MAX_INSN_SIZE || INTEL_PT_INSN_BUF_SZ > MAX_INSN |
32f98aab AH |
31 | #error Instruction buffer size too small |
32 | #endif | |
33 | ||
940b2f2f | 34 | /* Based on branch_type() from arch/x86/events/intel/lbr.c */ |
237fae79 AH |
35 | static void intel_pt_insn_decoder(struct insn *insn, |
36 | struct intel_pt_insn *intel_pt_insn) | |
37 | { | |
38 | enum intel_pt_insn_op op = INTEL_PT_OP_OTHER; | |
39 | enum intel_pt_insn_branch branch = INTEL_PT_BR_NO_BRANCH; | |
40 | int ext; | |
41 | ||
42 | if (insn_is_avx(insn)) { | |
43 | intel_pt_insn->op = INTEL_PT_OP_OTHER; | |
44 | intel_pt_insn->branch = INTEL_PT_BR_NO_BRANCH; | |
45 | intel_pt_insn->length = insn->length; | |
46 | return; | |
47 | } | |
48 | ||
49 | switch (insn->opcode.bytes[0]) { | |
50 | case 0xf: | |
51 | switch (insn->opcode.bytes[1]) { | |
52 | case 0x05: /* syscall */ | |
53 | case 0x34: /* sysenter */ | |
54 | op = INTEL_PT_OP_SYSCALL; | |
55 | branch = INTEL_PT_BR_INDIRECT; | |
56 | break; | |
57 | case 0x07: /* sysret */ | |
58 | case 0x35: /* sysexit */ | |
59 | op = INTEL_PT_OP_SYSRET; | |
60 | branch = INTEL_PT_BR_INDIRECT; | |
61 | break; | |
62 | case 0x80 ... 0x8f: /* jcc */ | |
63 | op = INTEL_PT_OP_JCC; | |
64 | branch = INTEL_PT_BR_CONDITIONAL; | |
65 | break; | |
66 | default: | |
67 | break; | |
68 | } | |
69 | break; | |
70 | case 0x70 ... 0x7f: /* jcc */ | |
71 | op = INTEL_PT_OP_JCC; | |
72 | branch = INTEL_PT_BR_CONDITIONAL; | |
73 | break; | |
74 | case 0xc2: /* near ret */ | |
75 | case 0xc3: /* near ret */ | |
76 | case 0xca: /* far ret */ | |
77 | case 0xcb: /* far ret */ | |
78 | op = INTEL_PT_OP_RET; | |
79 | branch = INTEL_PT_BR_INDIRECT; | |
80 | break; | |
81 | case 0xcf: /* iret */ | |
82 | op = INTEL_PT_OP_IRET; | |
83 | branch = INTEL_PT_BR_INDIRECT; | |
84 | break; | |
85 | case 0xcc ... 0xce: /* int */ | |
86 | op = INTEL_PT_OP_INT; | |
87 | branch = INTEL_PT_BR_INDIRECT; | |
88 | break; | |
89 | case 0xe8: /* call near rel */ | |
90 | op = INTEL_PT_OP_CALL; | |
91 | branch = INTEL_PT_BR_UNCONDITIONAL; | |
92 | break; | |
93 | case 0x9a: /* call far absolute */ | |
94 | op = INTEL_PT_OP_CALL; | |
95 | branch = INTEL_PT_BR_INDIRECT; | |
96 | break; | |
97 | case 0xe0 ... 0xe2: /* loop */ | |
98 | op = INTEL_PT_OP_LOOP; | |
99 | branch = INTEL_PT_BR_CONDITIONAL; | |
100 | break; | |
101 | case 0xe3: /* jcc */ | |
102 | op = INTEL_PT_OP_JCC; | |
103 | branch = INTEL_PT_BR_CONDITIONAL; | |
104 | break; | |
105 | case 0xe9: /* jmp */ | |
106 | case 0xeb: /* jmp */ | |
107 | op = INTEL_PT_OP_JMP; | |
108 | branch = INTEL_PT_BR_UNCONDITIONAL; | |
109 | break; | |
110 | case 0xea: /* far jmp */ | |
111 | op = INTEL_PT_OP_JMP; | |
112 | branch = INTEL_PT_BR_INDIRECT; | |
113 | break; | |
114 | case 0xff: /* call near absolute, call far absolute ind */ | |
115 | ext = (insn->modrm.bytes[0] >> 3) & 0x7; | |
116 | switch (ext) { | |
117 | case 2: /* near ind call */ | |
118 | case 3: /* far ind call */ | |
119 | op = INTEL_PT_OP_CALL; | |
120 | branch = INTEL_PT_BR_INDIRECT; | |
121 | break; | |
122 | case 4: | |
123 | case 5: | |
124 | op = INTEL_PT_OP_JMP; | |
125 | branch = INTEL_PT_BR_INDIRECT; | |
126 | break; | |
127 | default: | |
128 | break; | |
129 | } | |
130 | break; | |
131 | default: | |
132 | break; | |
133 | } | |
134 | ||
135 | intel_pt_insn->op = op; | |
136 | intel_pt_insn->branch = branch; | |
137 | intel_pt_insn->length = insn->length; | |
138 | ||
139 | if (branch == INTEL_PT_BR_CONDITIONAL || | |
140 | branch == INTEL_PT_BR_UNCONDITIONAL) { | |
141 | #if __BYTE_ORDER == __BIG_ENDIAN | |
142 | switch (insn->immediate.nbytes) { | |
143 | case 1: | |
144 | intel_pt_insn->rel = insn->immediate.value; | |
145 | break; | |
146 | case 2: | |
147 | intel_pt_insn->rel = | |
148 | bswap_16((short)insn->immediate.value); | |
149 | break; | |
150 | case 4: | |
151 | intel_pt_insn->rel = bswap_32(insn->immediate.value); | |
152 | break; | |
97db6206 AH |
153 | default: |
154 | intel_pt_insn->rel = 0; | |
155 | break; | |
237fae79 AH |
156 | } |
157 | #else | |
158 | intel_pt_insn->rel = insn->immediate.value; | |
159 | #endif | |
160 | } | |
161 | } | |
162 | ||
163 | int intel_pt_get_insn(const unsigned char *buf, size_t len, int x86_64, | |
164 | struct intel_pt_insn *intel_pt_insn) | |
165 | { | |
166 | struct insn insn; | |
167 | ||
168 | insn_init(&insn, buf, len, x86_64); | |
169 | insn_get_length(&insn); | |
170 | if (!insn_complete(&insn) || insn.length > len) | |
171 | return -1; | |
172 | intel_pt_insn_decoder(&insn, intel_pt_insn); | |
32f98aab | 173 | if (insn.length < INTEL_PT_INSN_BUF_SZ) |
237fae79 AH |
174 | memcpy(intel_pt_insn->buf, buf, insn.length); |
175 | else | |
32f98aab | 176 | memcpy(intel_pt_insn->buf, buf, INTEL_PT_INSN_BUF_SZ); |
237fae79 AH |
177 | return 0; |
178 | } | |
179 | ||
180 | const char *branch_name[] = { | |
181 | [INTEL_PT_OP_OTHER] = "Other", | |
182 | [INTEL_PT_OP_CALL] = "Call", | |
183 | [INTEL_PT_OP_RET] = "Ret", | |
184 | [INTEL_PT_OP_JCC] = "Jcc", | |
185 | [INTEL_PT_OP_JMP] = "Jmp", | |
186 | [INTEL_PT_OP_LOOP] = "Loop", | |
187 | [INTEL_PT_OP_IRET] = "IRet", | |
188 | [INTEL_PT_OP_INT] = "Int", | |
189 | [INTEL_PT_OP_SYSCALL] = "Syscall", | |
190 | [INTEL_PT_OP_SYSRET] = "Sysret", | |
191 | }; | |
192 | ||
193 | const char *intel_pt_insn_name(enum intel_pt_insn_op op) | |
194 | { | |
195 | return branch_name[op]; | |
196 | } | |
197 | ||
198 | int intel_pt_insn_desc(const struct intel_pt_insn *intel_pt_insn, char *buf, | |
199 | size_t buf_len) | |
200 | { | |
201 | switch (intel_pt_insn->branch) { | |
202 | case INTEL_PT_BR_CONDITIONAL: | |
203 | case INTEL_PT_BR_UNCONDITIONAL: | |
204 | return snprintf(buf, buf_len, "%s %s%d", | |
205 | intel_pt_insn_name(intel_pt_insn->op), | |
206 | intel_pt_insn->rel > 0 ? "+" : "", | |
207 | intel_pt_insn->rel); | |
208 | case INTEL_PT_BR_NO_BRANCH: | |
209 | case INTEL_PT_BR_INDIRECT: | |
210 | return snprintf(buf, buf_len, "%s", | |
211 | intel_pt_insn_name(intel_pt_insn->op)); | |
212 | default: | |
213 | break; | |
214 | } | |
215 | return 0; | |
216 | } | |
217 | ||
237fae79 AH |
218 | int intel_pt_insn_type(enum intel_pt_insn_op op) |
219 | { | |
220 | switch (op) { | |
221 | case INTEL_PT_OP_OTHER: | |
222 | return 0; | |
223 | case INTEL_PT_OP_CALL: | |
224 | return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL; | |
225 | case INTEL_PT_OP_RET: | |
226 | return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN; | |
227 | case INTEL_PT_OP_JCC: | |
228 | return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CONDITIONAL; | |
229 | case INTEL_PT_OP_JMP: | |
230 | return PERF_IP_FLAG_BRANCH; | |
231 | case INTEL_PT_OP_LOOP: | |
232 | return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CONDITIONAL; | |
233 | case INTEL_PT_OP_IRET: | |
234 | return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN | | |
235 | PERF_IP_FLAG_INTERRUPT; | |
236 | case INTEL_PT_OP_INT: | |
237 | return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL | | |
238 | PERF_IP_FLAG_INTERRUPT; | |
239 | case INTEL_PT_OP_SYSCALL: | |
240 | return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL | | |
241 | PERF_IP_FLAG_SYSCALLRET; | |
242 | case INTEL_PT_OP_SYSRET: | |
243 | return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN | | |
244 | PERF_IP_FLAG_SYSCALLRET; | |
245 | default: | |
246 | return 0; | |
247 | } | |
248 | } |