You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
perf tools: Add Intel PT instruction decoder
Add support for decoding instructions for Intel Processor Trace. The kernel x86 instruction decoder is copied for this. This essentially provides intel_pt_get_insn() which takes a binary buffer, uses the kernel's x86 instruction decoder to get details of the instruction and then categorizes it for consumption by an Intel PT decoder. Signed-off-by: Adrian Hunter <adrian.hunter@intel.com> Cc: Jiri Olsa <jolsa@redhat.com> Link: http://lkml.kernel.org/r/1439450095-30122-1-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
committed by
Arnaldo Carvalho de Melo
parent
a4e925905c
commit
237fae79f5
@@ -57,6 +57,8 @@ quiet_cmd_cc_i_c = CPP $@
|
||||
quiet_cmd_cc_s_c = AS $@
|
||||
cmd_cc_s_c = $(CC) $(c_flags) -S -o $@ $<
|
||||
|
||||
quiet_cmd_gen = GEN $@
|
||||
|
||||
# Link agregate command
|
||||
# If there's nothing to link, create empty $@ object.
|
||||
quiet_cmd_ld_multi = LD $@
|
||||
|
||||
@@ -29,3 +29,4 @@ config.mak.autogen
|
||||
*.pyc
|
||||
*.pyo
|
||||
.config-detected
|
||||
util/intel-pt-decoder/inat-tables.c
|
||||
|
||||
@@ -76,6 +76,12 @@ include config/utilities.mak
|
||||
#
|
||||
# Define NO_AUXTRACE if you do not want AUX area tracing support
|
||||
|
||||
# As per kernel Makefile, avoid funny character set dependencies
|
||||
unexport LC_ALL
|
||||
LC_COLLATE=C
|
||||
LC_NUMERIC=C
|
||||
export LC_COLLATE LC_NUMERIC
|
||||
|
||||
ifeq ($(srctree),)
|
||||
srctree := $(patsubst %/,%,$(dir $(shell pwd)))
|
||||
srctree := $(patsubst %/,%,$(dir $(srctree)))
|
||||
@@ -135,6 +141,7 @@ INSTALL = install
|
||||
FLEX = flex
|
||||
BISON = bison
|
||||
STRIP = strip
|
||||
AWK = awk
|
||||
|
||||
LIB_DIR = $(srctree)/tools/lib/api/
|
||||
TRACE_EVENT_DIR = $(srctree)/tools/lib/traceevent/
|
||||
@@ -289,7 +296,7 @@ strip: $(PROGRAMS) $(OUTPUT)perf
|
||||
|
||||
PERF_IN := $(OUTPUT)perf-in.o
|
||||
|
||||
export srctree OUTPUT RM CC LD AR CFLAGS V BISON FLEX
|
||||
export srctree OUTPUT RM CC LD AR CFLAGS V BISON FLEX AWK
|
||||
build := -f $(srctree)/tools/build/Makefile.build dir=. obj
|
||||
|
||||
$(PERF_IN): $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)common-cmds.h FORCE
|
||||
@@ -565,7 +572,8 @@ clean: $(LIBTRACEEVENT)-clean $(LIBAPI)-clean config-clean
|
||||
$(Q)find . -name '*.o' -delete -o -name '\.*.cmd' -delete -o -name '\.*.d' -delete
|
||||
$(Q)$(RM) $(OUTPUT).config-detected
|
||||
$(call QUIET_CLEAN, core-progs) $(RM) $(ALL_PROGRAMS) perf perf-read-vdso32 perf-read-vdsox32
|
||||
$(call QUIET_CLEAN, core-gen) $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope* $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)FEATURE-DUMP $(OUTPUT)util/*-bison* $(OUTPUT)util/*-flex*
|
||||
$(call QUIET_CLEAN, core-gen) $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope* $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)FEATURE-DUMP $(OUTPUT)util/*-bison* $(OUTPUT)util/*-flex* \
|
||||
$(OUTPUT)util/intel-pt-decoder/inat-tables.c
|
||||
$(QUIET_SUBDIR0)Documentation $(QUIET_SUBDIR1) clean
|
||||
$(python-clean)
|
||||
|
||||
|
||||
@@ -1 +1,11 @@
|
||||
libperf-$(CONFIG_AUXTRACE) += intel-pt-pkt-decoder.o
|
||||
libperf-$(CONFIG_AUXTRACE) += intel-pt-pkt-decoder.o intel-pt-insn-decoder.o
|
||||
|
||||
inat_tables_script = util/intel-pt-decoder/gen-insn-attr-x86.awk
|
||||
inat_tables_maps = util/intel-pt-decoder/x86-opcode-map.txt
|
||||
|
||||
$(OUTPUT)util/intel-pt-decoder/inat-tables.c: $(inat_tables_script) $(inat_tables_maps)
|
||||
@$(call echo-cmd,gen)$(AWK) -f $(inat_tables_script) $(inat_tables_maps) > $@ || rm -f $@
|
||||
|
||||
$(OUTPUT)util/intel-pt-decoder/intel-pt-insn-decoder.o: util/intel-pt-decoder/inat.c $(OUTPUT)util/intel-pt-decoder/inat-tables.c
|
||||
|
||||
CFLAGS_intel-pt-insn-decoder.o += -I$(OUTPUT)util/intel-pt-decoder -Wno-override-init
|
||||
|
||||
@@ -0,0 +1,386 @@
|
||||
#!/bin/awk -f
|
||||
# gen-insn-attr-x86.awk: Instruction attribute table generator
|
||||
# Written by Masami Hiramatsu <mhiramat@redhat.com>
|
||||
#
|
||||
# Usage: awk -f gen-insn-attr-x86.awk x86-opcode-map.txt > inat-tables.c
|
||||
|
||||
# Awk implementation sanity check
|
||||
function check_awk_implement() {
|
||||
if (sprintf("%x", 0) != "0")
|
||||
return "Your awk has a printf-format problem."
|
||||
return ""
|
||||
}
|
||||
|
||||
# Clear working vars
|
||||
function clear_vars() {
|
||||
delete table
|
||||
delete lptable2
|
||||
delete lptable1
|
||||
delete lptable3
|
||||
eid = -1 # escape id
|
||||
gid = -1 # group id
|
||||
aid = -1 # AVX id
|
||||
tname = ""
|
||||
}
|
||||
|
||||
BEGIN {
|
||||
# Implementation error checking
|
||||
awkchecked = check_awk_implement()
|
||||
if (awkchecked != "") {
|
||||
print "Error: " awkchecked > "/dev/stderr"
|
||||
print "Please try to use gawk." > "/dev/stderr"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Setup generating tables
|
||||
print "/* x86 opcode map generated from x86-opcode-map.txt */"
|
||||
print "/* Do not change this code. */\n"
|
||||
ggid = 1
|
||||
geid = 1
|
||||
gaid = 0
|
||||
delete etable
|
||||
delete gtable
|
||||
delete atable
|
||||
|
||||
opnd_expr = "^[A-Za-z/]"
|
||||
ext_expr = "^\\("
|
||||
sep_expr = "^\\|$"
|
||||
group_expr = "^Grp[0-9A-Za-z]+"
|
||||
|
||||
imm_expr = "^[IJAOL][a-z]"
|
||||
imm_flag["Ib"] = "INAT_MAKE_IMM(INAT_IMM_BYTE)"
|
||||
imm_flag["Jb"] = "INAT_MAKE_IMM(INAT_IMM_BYTE)"
|
||||
imm_flag["Iw"] = "INAT_MAKE_IMM(INAT_IMM_WORD)"
|
||||
imm_flag["Id"] = "INAT_MAKE_IMM(INAT_IMM_DWORD)"
|
||||
imm_flag["Iq"] = "INAT_MAKE_IMM(INAT_IMM_QWORD)"
|
||||
imm_flag["Ap"] = "INAT_MAKE_IMM(INAT_IMM_PTR)"
|
||||
imm_flag["Iz"] = "INAT_MAKE_IMM(INAT_IMM_VWORD32)"
|
||||
imm_flag["Jz"] = "INAT_MAKE_IMM(INAT_IMM_VWORD32)"
|
||||
imm_flag["Iv"] = "INAT_MAKE_IMM(INAT_IMM_VWORD)"
|
||||
imm_flag["Ob"] = "INAT_MOFFSET"
|
||||
imm_flag["Ov"] = "INAT_MOFFSET"
|
||||
imm_flag["Lx"] = "INAT_MAKE_IMM(INAT_IMM_BYTE)"
|
||||
|
||||
modrm_expr = "^([CDEGMNPQRSUVW/][a-z]+|NTA|T[012])"
|
||||
force64_expr = "\\([df]64\\)"
|
||||
rex_expr = "^REX(\\.[XRWB]+)*"
|
||||
fpu_expr = "^ESC" # TODO
|
||||
|
||||
lprefix1_expr = "\\((66|!F3)\\)"
|
||||
lprefix2_expr = "\\(F3\\)"
|
||||
lprefix3_expr = "\\((F2|!F3|66\\&F2)\\)"
|
||||
lprefix_expr = "\\((66|F2|F3)\\)"
|
||||
max_lprefix = 4
|
||||
|
||||
# All opcodes starting with lower-case 'v' or with (v1) superscript
|
||||
# accepts VEX prefix
|
||||
vexok_opcode_expr = "^v.*"
|
||||
vexok_expr = "\\(v1\\)"
|
||||
# All opcodes with (v) superscript supports *only* VEX prefix
|
||||
vexonly_expr = "\\(v\\)"
|
||||
|
||||
prefix_expr = "\\(Prefix\\)"
|
||||
prefix_num["Operand-Size"] = "INAT_PFX_OPNDSZ"
|
||||
prefix_num["REPNE"] = "INAT_PFX_REPNE"
|
||||
prefix_num["REP/REPE"] = "INAT_PFX_REPE"
|
||||
prefix_num["XACQUIRE"] = "INAT_PFX_REPNE"
|
||||
prefix_num["XRELEASE"] = "INAT_PFX_REPE"
|
||||
prefix_num["LOCK"] = "INAT_PFX_LOCK"
|
||||
prefix_num["SEG=CS"] = "INAT_PFX_CS"
|
||||
prefix_num["SEG=DS"] = "INAT_PFX_DS"
|
||||
prefix_num["SEG=ES"] = "INAT_PFX_ES"
|
||||
prefix_num["SEG=FS"] = "INAT_PFX_FS"
|
||||
prefix_num["SEG=GS"] = "INAT_PFX_GS"
|
||||
prefix_num["SEG=SS"] = "INAT_PFX_SS"
|
||||
prefix_num["Address-Size"] = "INAT_PFX_ADDRSZ"
|
||||
prefix_num["VEX+1byte"] = "INAT_PFX_VEX2"
|
||||
prefix_num["VEX+2byte"] = "INAT_PFX_VEX3"
|
||||
|
||||
clear_vars()
|
||||
}
|
||||
|
||||
function semantic_error(msg) {
|
||||
print "Semantic error at " NR ": " msg > "/dev/stderr"
|
||||
exit 1
|
||||
}
|
||||
|
||||
function debug(msg) {
|
||||
print "DEBUG: " msg
|
||||
}
|
||||
|
||||
function array_size(arr, i,c) {
|
||||
c = 0
|
||||
for (i in arr)
|
||||
c++
|
||||
return c
|
||||
}
|
||||
|
||||
/^Table:/ {
|
||||
print "/* " $0 " */"
|
||||
if (tname != "")
|
||||
semantic_error("Hit Table: before EndTable:.");
|
||||
}
|
||||
|
||||
/^Referrer:/ {
|
||||
if (NF != 1) {
|
||||
# escape opcode table
|
||||
ref = ""
|
||||
for (i = 2; i <= NF; i++)
|
||||
ref = ref $i
|
||||
eid = escape[ref]
|
||||
tname = sprintf("inat_escape_table_%d", eid)
|
||||
}
|
||||
}
|
||||
|
||||
/^AVXcode:/ {
|
||||
if (NF != 1) {
|
||||
# AVX/escape opcode table
|
||||
aid = $2
|
||||
if (gaid <= aid)
|
||||
gaid = aid + 1
|
||||
if (tname == "") # AVX only opcode table
|
||||
tname = sprintf("inat_avx_table_%d", $2)
|
||||
}
|
||||
if (aid == -1 && eid == -1) # primary opcode table
|
||||
tname = "inat_primary_table"
|
||||
}
|
||||
|
||||
/^GrpTable:/ {
|
||||
print "/* " $0 " */"
|
||||
if (!($2 in group))
|
||||
semantic_error("No group: " $2 )
|
||||
gid = group[$2]
|
||||
tname = "inat_group_table_" gid
|
||||
}
|
||||
|
||||
function print_table(tbl,name,fmt,n)
|
||||
{
|
||||
print "const insn_attr_t " name " = {"
|
||||
for (i = 0; i < n; i++) {
|
||||
id = sprintf(fmt, i)
|
||||
if (tbl[id])
|
||||
print " [" id "] = " tbl[id] ","
|
||||
}
|
||||
print "};"
|
||||
}
|
||||
|
||||
/^EndTable/ {
|
||||
if (gid != -1) {
|
||||
# print group tables
|
||||
if (array_size(table) != 0) {
|
||||
print_table(table, tname "[INAT_GROUP_TABLE_SIZE]",
|
||||
"0x%x", 8)
|
||||
gtable[gid,0] = tname
|
||||
}
|
||||
if (array_size(lptable1) != 0) {
|
||||
print_table(lptable1, tname "_1[INAT_GROUP_TABLE_SIZE]",
|
||||
"0x%x", 8)
|
||||
gtable[gid,1] = tname "_1"
|
||||
}
|
||||
if (array_size(lptable2) != 0) {
|
||||
print_table(lptable2, tname "_2[INAT_GROUP_TABLE_SIZE]",
|
||||
"0x%x", 8)
|
||||
gtable[gid,2] = tname "_2"
|
||||
}
|
||||
if (array_size(lptable3) != 0) {
|
||||
print_table(lptable3, tname "_3[INAT_GROUP_TABLE_SIZE]",
|
||||
"0x%x", 8)
|
||||
gtable[gid,3] = tname "_3"
|
||||
}
|
||||
} else {
|
||||
# print primary/escaped tables
|
||||
if (array_size(table) != 0) {
|
||||
print_table(table, tname "[INAT_OPCODE_TABLE_SIZE]",
|
||||
"0x%02x", 256)
|
||||
etable[eid,0] = tname
|
||||
if (aid >= 0)
|
||||
atable[aid,0] = tname
|
||||
}
|
||||
if (array_size(lptable1) != 0) {
|
||||
print_table(lptable1,tname "_1[INAT_OPCODE_TABLE_SIZE]",
|
||||
"0x%02x", 256)
|
||||
etable[eid,1] = tname "_1"
|
||||
if (aid >= 0)
|
||||
atable[aid,1] = tname "_1"
|
||||
}
|
||||
if (array_size(lptable2) != 0) {
|
||||
print_table(lptable2,tname "_2[INAT_OPCODE_TABLE_SIZE]",
|
||||
"0x%02x", 256)
|
||||
etable[eid,2] = tname "_2"
|
||||
if (aid >= 0)
|
||||
atable[aid,2] = tname "_2"
|
||||
}
|
||||
if (array_size(lptable3) != 0) {
|
||||
print_table(lptable3,tname "_3[INAT_OPCODE_TABLE_SIZE]",
|
||||
"0x%02x", 256)
|
||||
etable[eid,3] = tname "_3"
|
||||
if (aid >= 0)
|
||||
atable[aid,3] = tname "_3"
|
||||
}
|
||||
}
|
||||
print ""
|
||||
clear_vars()
|
||||
}
|
||||
|
||||
function add_flags(old,new) {
|
||||
if (old && new)
|
||||
return old " | " new
|
||||
else if (old)
|
||||
return old
|
||||
else
|
||||
return new
|
||||
}
|
||||
|
||||
# convert operands to flags.
|
||||
function convert_operands(count,opnd, i,j,imm,mod)
|
||||
{
|
||||
imm = null
|
||||
mod = null
|
||||
for (j = 1; j <= count; j++) {
|
||||
i = opnd[j]
|
||||
if (match(i, imm_expr) == 1) {
|
||||
if (!imm_flag[i])
|
||||
semantic_error("Unknown imm opnd: " i)
|
||||
if (imm) {
|
||||
if (i != "Ib")
|
||||
semantic_error("Second IMM error")
|
||||
imm = add_flags(imm, "INAT_SCNDIMM")
|
||||
} else
|
||||
imm = imm_flag[i]
|
||||
} else if (match(i, modrm_expr))
|
||||
mod = "INAT_MODRM"
|
||||
}
|
||||
return add_flags(imm, mod)
|
||||
}
|
||||
|
||||
/^[0-9a-f]+\:/ {
|
||||
if (NR == 1)
|
||||
next
|
||||
# get index
|
||||
idx = "0x" substr($1, 1, index($1,":") - 1)
|
||||
if (idx in table)
|
||||
semantic_error("Redefine " idx " in " tname)
|
||||
|
||||
# check if escaped opcode
|
||||
if ("escape" == $2) {
|
||||
if ($3 != "#")
|
||||
semantic_error("No escaped name")
|
||||
ref = ""
|
||||
for (i = 4; i <= NF; i++)
|
||||
ref = ref $i
|
||||
if (ref in escape)
|
||||
semantic_error("Redefine escape (" ref ")")
|
||||
escape[ref] = geid
|
||||
geid++
|
||||
table[idx] = "INAT_MAKE_ESCAPE(" escape[ref] ")"
|
||||
next
|
||||
}
|
||||
|
||||
variant = null
|
||||
# converts
|
||||
i = 2
|
||||
while (i <= NF) {
|
||||
opcode = $(i++)
|
||||
delete opnds
|
||||
ext = null
|
||||
flags = null
|
||||
opnd = null
|
||||
# parse one opcode
|
||||
if (match($i, opnd_expr)) {
|
||||
opnd = $i
|
||||
count = split($(i++), opnds, ",")
|
||||
flags = convert_operands(count, opnds)
|
||||
}
|
||||
if (match($i, ext_expr))
|
||||
ext = $(i++)
|
||||
if (match($i, sep_expr))
|
||||
i++
|
||||
else if (i < NF)
|
||||
semantic_error($i " is not a separator")
|
||||
|
||||
# check if group opcode
|
||||
if (match(opcode, group_expr)) {
|
||||
if (!(opcode in group)) {
|
||||
group[opcode] = ggid
|
||||
ggid++
|
||||
}
|
||||
flags = add_flags(flags, "INAT_MAKE_GROUP(" group[opcode] ")")
|
||||
}
|
||||
# check force(or default) 64bit
|
||||
if (match(ext, force64_expr))
|
||||
flags = add_flags(flags, "INAT_FORCE64")
|
||||
|
||||
# check REX prefix
|
||||
if (match(opcode, rex_expr))
|
||||
flags = add_flags(flags, "INAT_MAKE_PREFIX(INAT_PFX_REX)")
|
||||
|
||||
# check coprocessor escape : TODO
|
||||
if (match(opcode, fpu_expr))
|
||||
flags = add_flags(flags, "INAT_MODRM")
|
||||
|
||||
# check VEX codes
|
||||
if (match(ext, vexonly_expr))
|
||||
flags = add_flags(flags, "INAT_VEXOK | INAT_VEXONLY")
|
||||
else if (match(ext, vexok_expr) || match(opcode, vexok_opcode_expr))
|
||||
flags = add_flags(flags, "INAT_VEXOK")
|
||||
|
||||
# check prefixes
|
||||
if (match(ext, prefix_expr)) {
|
||||
if (!prefix_num[opcode])
|
||||
semantic_error("Unknown prefix: " opcode)
|
||||
flags = add_flags(flags, "INAT_MAKE_PREFIX(" prefix_num[opcode] ")")
|
||||
}
|
||||
if (length(flags) == 0)
|
||||
continue
|
||||
# check if last prefix
|
||||
if (match(ext, lprefix1_expr)) {
|
||||
lptable1[idx] = add_flags(lptable1[idx],flags)
|
||||
variant = "INAT_VARIANT"
|
||||
}
|
||||
if (match(ext, lprefix2_expr)) {
|
||||
lptable2[idx] = add_flags(lptable2[idx],flags)
|
||||
variant = "INAT_VARIANT"
|
||||
}
|
||||
if (match(ext, lprefix3_expr)) {
|
||||
lptable3[idx] = add_flags(lptable3[idx],flags)
|
||||
variant = "INAT_VARIANT"
|
||||
}
|
||||
if (!match(ext, lprefix_expr)){
|
||||
table[idx] = add_flags(table[idx],flags)
|
||||
}
|
||||
}
|
||||
if (variant)
|
||||
table[idx] = add_flags(table[idx],variant)
|
||||
}
|
||||
|
||||
END {
|
||||
if (awkchecked != "")
|
||||
exit 1
|
||||
# print escape opcode map's array
|
||||
print "/* Escape opcode map array */"
|
||||
print "const insn_attr_t * const inat_escape_tables[INAT_ESC_MAX + 1]" \
|
||||
"[INAT_LSTPFX_MAX + 1] = {"
|
||||
for (i = 0; i < geid; i++)
|
||||
for (j = 0; j < max_lprefix; j++)
|
||||
if (etable[i,j])
|
||||
print " ["i"]["j"] = "etable[i,j]","
|
||||
print "};\n"
|
||||
# print group opcode map's array
|
||||
print "/* Group opcode map array */"
|
||||
print "const insn_attr_t * const inat_group_tables[INAT_GRP_MAX + 1]"\
|
||||
"[INAT_LSTPFX_MAX + 1] = {"
|
||||
for (i = 0; i < ggid; i++)
|
||||
for (j = 0; j < max_lprefix; j++)
|
||||
if (gtable[i,j])
|
||||
print " ["i"]["j"] = "gtable[i,j]","
|
||||
print "};\n"
|
||||
# print AVX opcode map's array
|
||||
print "/* AVX opcode map array */"
|
||||
print "const insn_attr_t * const inat_avx_tables[X86_VEX_M_MAX + 1]"\
|
||||
"[INAT_LSTPFX_MAX + 1] = {"
|
||||
for (i = 0; i < gaid; i++)
|
||||
for (j = 0; j < max_lprefix; j++)
|
||||
if (atable[i,j])
|
||||
print " ["i"]["j"] = "atable[i,j]","
|
||||
print "};"
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* x86 instruction attribute tables
|
||||
*
|
||||
* Written by Masami Hiramatsu <mhiramat@redhat.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*
|
||||
*/
|
||||
#include <asm/insn.h>
|
||||
|
||||
/* Attribute tables are generated from opcode map */
|
||||
#include "inat-tables.c"
|
||||
|
||||
/* Attribute search APIs */
|
||||
insn_attr_t inat_get_opcode_attribute(insn_byte_t opcode)
|
||||
{
|
||||
return inat_primary_table[opcode];
|
||||
}
|
||||
|
||||
int inat_get_last_prefix_id(insn_byte_t last_pfx)
|
||||
{
|
||||
insn_attr_t lpfx_attr;
|
||||
|
||||
lpfx_attr = inat_get_opcode_attribute(last_pfx);
|
||||
return inat_last_prefix_id(lpfx_attr);
|
||||
}
|
||||
|
||||
insn_attr_t inat_get_escape_attribute(insn_byte_t opcode, int lpfx_id,
|
||||
insn_attr_t esc_attr)
|
||||
{
|
||||
const insn_attr_t *table;
|
||||
int n;
|
||||
|
||||
n = inat_escape_id(esc_attr);
|
||||
|
||||
table = inat_escape_tables[n][0];
|
||||
if (!table)
|
||||
return 0;
|
||||
if (inat_has_variant(table[opcode]) && lpfx_id) {
|
||||
table = inat_escape_tables[n][lpfx_id];
|
||||
if (!table)
|
||||
return 0;
|
||||
}
|
||||
return table[opcode];
|
||||
}
|
||||
|
||||
insn_attr_t inat_get_group_attribute(insn_byte_t modrm, int lpfx_id,
|
||||
insn_attr_t grp_attr)
|
||||
{
|
||||
const insn_attr_t *table;
|
||||
int n;
|
||||
|
||||
n = inat_group_id(grp_attr);
|
||||
|
||||
table = inat_group_tables[n][0];
|
||||
if (!table)
|
||||
return inat_group_common_attribute(grp_attr);
|
||||
if (inat_has_variant(table[X86_MODRM_REG(modrm)]) && lpfx_id) {
|
||||
table = inat_group_tables[n][lpfx_id];
|
||||
if (!table)
|
||||
return inat_group_common_attribute(grp_attr);
|
||||
}
|
||||
return table[X86_MODRM_REG(modrm)] |
|
||||
inat_group_common_attribute(grp_attr);
|
||||
}
|
||||
|
||||
insn_attr_t inat_get_avx_attribute(insn_byte_t opcode, insn_byte_t vex_m,
|
||||
insn_byte_t vex_p)
|
||||
{
|
||||
const insn_attr_t *table;
|
||||
if (vex_m > X86_VEX_M_MAX || vex_p > INAT_LSTPFX_MAX)
|
||||
return 0;
|
||||
/* At first, this checks the master table */
|
||||
table = inat_avx_tables[vex_m][0];
|
||||
if (!table)
|
||||
return 0;
|
||||
if (!inat_is_group(table[opcode]) && vex_p) {
|
||||
/* If this is not a group, get attribute directly */
|
||||
table = inat_avx_tables[vex_m][vex_p];
|
||||
if (!table)
|
||||
return 0;
|
||||
}
|
||||
return table[opcode];
|
||||
}
|
||||
@@ -0,0 +1,221 @@
|
||||
#ifndef _ASM_X86_INAT_H
|
||||
#define _ASM_X86_INAT_H
|
||||
/*
|
||||
* x86 instruction attributes
|
||||
*
|
||||
* Written by Masami Hiramatsu <mhiramat@redhat.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*
|
||||
*/
|
||||
#include <asm/inat_types.h>
|
||||
|
||||
/*
|
||||
* Internal bits. Don't use bitmasks directly, because these bits are
|
||||
* unstable. You should use checking functions.
|
||||
*/
|
||||
|
||||
#define INAT_OPCODE_TABLE_SIZE 256
|
||||
#define INAT_GROUP_TABLE_SIZE 8
|
||||
|
||||
/* Legacy last prefixes */
|
||||
#define INAT_PFX_OPNDSZ 1 /* 0x66 */ /* LPFX1 */
|
||||
#define INAT_PFX_REPE 2 /* 0xF3 */ /* LPFX2 */
|
||||
#define INAT_PFX_REPNE 3 /* 0xF2 */ /* LPFX3 */
|
||||
/* Other Legacy prefixes */
|
||||
#define INAT_PFX_LOCK 4 /* 0xF0 */
|
||||
#define INAT_PFX_CS 5 /* 0x2E */
|
||||
#define INAT_PFX_DS 6 /* 0x3E */
|
||||
#define INAT_PFX_ES 7 /* 0x26 */
|
||||
#define INAT_PFX_FS 8 /* 0x64 */
|
||||
#define INAT_PFX_GS 9 /* 0x65 */
|
||||
#define INAT_PFX_SS 10 /* 0x36 */
|
||||
#define INAT_PFX_ADDRSZ 11 /* 0x67 */
|
||||
/* x86-64 REX prefix */
|
||||
#define INAT_PFX_REX 12 /* 0x4X */
|
||||
/* AVX VEX prefixes */
|
||||
#define INAT_PFX_VEX2 13 /* 2-bytes VEX prefix */
|
||||
#define INAT_PFX_VEX3 14 /* 3-bytes VEX prefix */
|
||||
|
||||
#define INAT_LSTPFX_MAX 3
|
||||
#define INAT_LGCPFX_MAX 11
|
||||
|
||||
/* Immediate size */
|
||||
#define INAT_IMM_BYTE 1
|
||||
#define INAT_IMM_WORD 2
|
||||
#define INAT_IMM_DWORD 3
|
||||
#define INAT_IMM_QWORD 4
|
||||
#define INAT_IMM_PTR 5
|
||||
#define INAT_IMM_VWORD32 6
|
||||
#define INAT_IMM_VWORD 7
|
||||
|
||||
/* Legacy prefix */
|
||||
#define INAT_PFX_OFFS 0
|
||||
#define INAT_PFX_BITS 4
|
||||
#define INAT_PFX_MAX ((1 << INAT_PFX_BITS) - 1)
|
||||
#define INAT_PFX_MASK (INAT_PFX_MAX << INAT_PFX_OFFS)
|
||||
/* Escape opcodes */
|
||||
#define INAT_ESC_OFFS (INAT_PFX_OFFS + INAT_PFX_BITS)
|
||||
#define INAT_ESC_BITS 2
|
||||
#define INAT_ESC_MAX ((1 << INAT_ESC_BITS) - 1)
|
||||
#define INAT_ESC_MASK (INAT_ESC_MAX << INAT_ESC_OFFS)
|
||||
/* Group opcodes (1-16) */
|
||||
#define INAT_GRP_OFFS (INAT_ESC_OFFS + INAT_ESC_BITS)
|
||||
#define INAT_GRP_BITS 5
|
||||
#define INAT_GRP_MAX ((1 << INAT_GRP_BITS) - 1)
|
||||
#define INAT_GRP_MASK (INAT_GRP_MAX << INAT_GRP_OFFS)
|
||||
/* Immediates */
|
||||
#define INAT_IMM_OFFS (INAT_GRP_OFFS + INAT_GRP_BITS)
|
||||
#define INAT_IMM_BITS 3
|
||||
#define INAT_IMM_MASK (((1 << INAT_IMM_BITS) - 1) << INAT_IMM_OFFS)
|
||||
/* Flags */
|
||||
#define INAT_FLAG_OFFS (INAT_IMM_OFFS + INAT_IMM_BITS)
|
||||
#define INAT_MODRM (1 << (INAT_FLAG_OFFS))
|
||||
#define INAT_FORCE64 (1 << (INAT_FLAG_OFFS + 1))
|
||||
#define INAT_SCNDIMM (1 << (INAT_FLAG_OFFS + 2))
|
||||
#define INAT_MOFFSET (1 << (INAT_FLAG_OFFS + 3))
|
||||
#define INAT_VARIANT (1 << (INAT_FLAG_OFFS + 4))
|
||||
#define INAT_VEXOK (1 << (INAT_FLAG_OFFS + 5))
|
||||
#define INAT_VEXONLY (1 << (INAT_FLAG_OFFS + 6))
|
||||
/* Attribute making macros for attribute tables */
|
||||
#define INAT_MAKE_PREFIX(pfx) (pfx << INAT_PFX_OFFS)
|
||||
#define INAT_MAKE_ESCAPE(esc) (esc << INAT_ESC_OFFS)
|
||||
#define INAT_MAKE_GROUP(grp) ((grp << INAT_GRP_OFFS) | INAT_MODRM)
|
||||
#define INAT_MAKE_IMM(imm) (imm << INAT_IMM_OFFS)
|
||||
|
||||
/* Attribute search APIs */
|
||||
extern insn_attr_t inat_get_opcode_attribute(insn_byte_t opcode);
|
||||
extern int inat_get_last_prefix_id(insn_byte_t last_pfx);
|
||||
extern insn_attr_t inat_get_escape_attribute(insn_byte_t opcode,
|
||||
int lpfx_id,
|
||||
insn_attr_t esc_attr);
|
||||
extern insn_attr_t inat_get_group_attribute(insn_byte_t modrm,
|
||||
int lpfx_id,
|
||||
insn_attr_t esc_attr);
|
||||
extern insn_attr_t inat_get_avx_attribute(insn_byte_t opcode,
|
||||
insn_byte_t vex_m,
|
||||
insn_byte_t vex_pp);
|
||||
|
||||
/* Attribute checking functions */
|
||||
static inline int inat_is_legacy_prefix(insn_attr_t attr)
|
||||
{
|
||||
attr &= INAT_PFX_MASK;
|
||||
return attr && attr <= INAT_LGCPFX_MAX;
|
||||
}
|
||||
|
||||
static inline int inat_is_address_size_prefix(insn_attr_t attr)
|
||||
{
|
||||
return (attr & INAT_PFX_MASK) == INAT_PFX_ADDRSZ;
|
||||
}
|
||||
|
||||
static inline int inat_is_operand_size_prefix(insn_attr_t attr)
|
||||
{
|
||||
return (attr & INAT_PFX_MASK) == INAT_PFX_OPNDSZ;
|
||||
}
|
||||
|
||||
static inline int inat_is_rex_prefix(insn_attr_t attr)
|
||||
{
|
||||
return (attr & INAT_PFX_MASK) == INAT_PFX_REX;
|
||||
}
|
||||
|
||||
static inline int inat_last_prefix_id(insn_attr_t attr)
|
||||
{
|
||||
if ((attr & INAT_PFX_MASK) > INAT_LSTPFX_MAX)
|
||||
return 0;
|
||||
else
|
||||
return attr & INAT_PFX_MASK;
|
||||
}
|
||||
|
||||
static inline int inat_is_vex_prefix(insn_attr_t attr)
|
||||
{
|
||||
attr &= INAT_PFX_MASK;
|
||||
return attr == INAT_PFX_VEX2 || attr == INAT_PFX_VEX3;
|
||||
}
|
||||
|
||||
static inline int inat_is_vex3_prefix(insn_attr_t attr)
|
||||
{
|
||||
return (attr & INAT_PFX_MASK) == INAT_PFX_VEX3;
|
||||
}
|
||||
|
||||
static inline int inat_is_escape(insn_attr_t attr)
|
||||
{
|
||||
return attr & INAT_ESC_MASK;
|
||||
}
|
||||
|
||||
static inline int inat_escape_id(insn_attr_t attr)
|
||||
{
|
||||
return (attr & INAT_ESC_MASK) >> INAT_ESC_OFFS;
|
||||
}
|
||||
|
||||
static inline int inat_is_group(insn_attr_t attr)
|
||||
{
|
||||
return attr & INAT_GRP_MASK;
|
||||
}
|
||||
|
||||
static inline int inat_group_id(insn_attr_t attr)
|
||||
{
|
||||
return (attr & INAT_GRP_MASK) >> INAT_GRP_OFFS;
|
||||
}
|
||||
|
||||
static inline int inat_group_common_attribute(insn_attr_t attr)
|
||||
{
|
||||
return attr & ~INAT_GRP_MASK;
|
||||
}
|
||||
|
||||
static inline int inat_has_immediate(insn_attr_t attr)
|
||||
{
|
||||
return attr & INAT_IMM_MASK;
|
||||
}
|
||||
|
||||
static inline int inat_immediate_size(insn_attr_t attr)
|
||||
{
|
||||
return (attr & INAT_IMM_MASK) >> INAT_IMM_OFFS;
|
||||
}
|
||||
|
||||
static inline int inat_has_modrm(insn_attr_t attr)
|
||||
{
|
||||
return attr & INAT_MODRM;
|
||||
}
|
||||
|
||||
static inline int inat_is_force64(insn_attr_t attr)
|
||||
{
|
||||
return attr & INAT_FORCE64;
|
||||
}
|
||||
|
||||
static inline int inat_has_second_immediate(insn_attr_t attr)
|
||||
{
|
||||
return attr & INAT_SCNDIMM;
|
||||
}
|
||||
|
||||
static inline int inat_has_moffset(insn_attr_t attr)
|
||||
{
|
||||
return attr & INAT_MOFFSET;
|
||||
}
|
||||
|
||||
static inline int inat_has_variant(insn_attr_t attr)
|
||||
{
|
||||
return attr & INAT_VARIANT;
|
||||
}
|
||||
|
||||
static inline int inat_accept_vex(insn_attr_t attr)
|
||||
{
|
||||
return attr & INAT_VEXOK;
|
||||
}
|
||||
|
||||
static inline int inat_must_vex(insn_attr_t attr)
|
||||
{
|
||||
return attr & INAT_VEXONLY;
|
||||
}
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,201 @@
|
||||
#ifndef _ASM_X86_INSN_H
|
||||
#define _ASM_X86_INSN_H
|
||||
/*
|
||||
* x86 instruction analysis
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*
|
||||
* Copyright (C) IBM Corporation, 2009
|
||||
*/
|
||||
|
||||
/* insn_attr_t is defined in inat.h */
|
||||
#include <asm/inat.h>
|
||||
|
||||
struct insn_field {
|
||||
union {
|
||||
insn_value_t value;
|
||||
insn_byte_t bytes[4];
|
||||
};
|
||||
/* !0 if we've run insn_get_xxx() for this field */
|
||||
unsigned char got;
|
||||
unsigned char nbytes;
|
||||
};
|
||||
|
||||
struct insn {
|
||||
struct insn_field prefixes; /*
|
||||
* Prefixes
|
||||
* prefixes.bytes[3]: last prefix
|
||||
*/
|
||||
struct insn_field rex_prefix; /* REX prefix */
|
||||
struct insn_field vex_prefix; /* VEX prefix */
|
||||
struct insn_field opcode; /*
|
||||
* opcode.bytes[0]: opcode1
|
||||
* opcode.bytes[1]: opcode2
|
||||
* opcode.bytes[2]: opcode3
|
||||
*/
|
||||
struct insn_field modrm;
|
||||
struct insn_field sib;
|
||||
struct insn_field displacement;
|
||||
union {
|
||||
struct insn_field immediate;
|
||||
struct insn_field moffset1; /* for 64bit MOV */
|
||||
struct insn_field immediate1; /* for 64bit imm or off16/32 */
|
||||
};
|
||||
union {
|
||||
struct insn_field moffset2; /* for 64bit MOV */
|
||||
struct insn_field immediate2; /* for 64bit imm or seg16 */
|
||||
};
|
||||
|
||||
insn_attr_t attr;
|
||||
unsigned char opnd_bytes;
|
||||
unsigned char addr_bytes;
|
||||
unsigned char length;
|
||||
unsigned char x86_64;
|
||||
|
||||
const insn_byte_t *kaddr; /* kernel address of insn to analyze */
|
||||
const insn_byte_t *end_kaddr; /* kernel address of last insn in buffer */
|
||||
const insn_byte_t *next_byte;
|
||||
};
|
||||
|
||||
#define MAX_INSN_SIZE 15
|
||||
|
||||
#define X86_MODRM_MOD(modrm) (((modrm) & 0xc0) >> 6)
|
||||
#define X86_MODRM_REG(modrm) (((modrm) & 0x38) >> 3)
|
||||
#define X86_MODRM_RM(modrm) ((modrm) & 0x07)
|
||||
|
||||
#define X86_SIB_SCALE(sib) (((sib) & 0xc0) >> 6)
|
||||
#define X86_SIB_INDEX(sib) (((sib) & 0x38) >> 3)
|
||||
#define X86_SIB_BASE(sib) ((sib) & 0x07)
|
||||
|
||||
#define X86_REX_W(rex) ((rex) & 8)
|
||||
#define X86_REX_R(rex) ((rex) & 4)
|
||||
#define X86_REX_X(rex) ((rex) & 2)
|
||||
#define X86_REX_B(rex) ((rex) & 1)
|
||||
|
||||
/* VEX bit flags */
|
||||
#define X86_VEX_W(vex) ((vex) & 0x80) /* VEX3 Byte2 */
|
||||
#define X86_VEX_R(vex) ((vex) & 0x80) /* VEX2/3 Byte1 */
|
||||
#define X86_VEX_X(vex) ((vex) & 0x40) /* VEX3 Byte1 */
|
||||
#define X86_VEX_B(vex) ((vex) & 0x20) /* VEX3 Byte1 */
|
||||
#define X86_VEX_L(vex) ((vex) & 0x04) /* VEX3 Byte2, VEX2 Byte1 */
|
||||
/* VEX bit fields */
|
||||
#define X86_VEX3_M(vex) ((vex) & 0x1f) /* VEX3 Byte1 */
|
||||
#define X86_VEX2_M 1 /* VEX2.M always 1 */
|
||||
#define X86_VEX_V(vex) (((vex) & 0x78) >> 3) /* VEX3 Byte2, VEX2 Byte1 */
|
||||
#define X86_VEX_P(vex) ((vex) & 0x03) /* VEX3 Byte2, VEX2 Byte1 */
|
||||
#define X86_VEX_M_MAX 0x1f /* VEX3.M Maximum value */
|
||||
|
||||
extern void insn_init(struct insn *insn, const void *kaddr, int buf_len, int x86_64);
|
||||
extern void insn_get_prefixes(struct insn *insn);
|
||||
extern void insn_get_opcode(struct insn *insn);
|
||||
extern void insn_get_modrm(struct insn *insn);
|
||||
extern void insn_get_sib(struct insn *insn);
|
||||
extern void insn_get_displacement(struct insn *insn);
|
||||
extern void insn_get_immediate(struct insn *insn);
|
||||
extern void insn_get_length(struct insn *insn);
|
||||
|
||||
/* Attribute will be determined after getting ModRM (for opcode groups) */
|
||||
static inline void insn_get_attribute(struct insn *insn)
|
||||
{
|
||||
insn_get_modrm(insn);
|
||||
}
|
||||
|
||||
/* Instruction uses RIP-relative addressing */
|
||||
extern int insn_rip_relative(struct insn *insn);
|
||||
|
||||
/* Init insn for kernel text */
|
||||
static inline void kernel_insn_init(struct insn *insn,
|
||||
const void *kaddr, int buf_len)
|
||||
{
|
||||
#ifdef CONFIG_X86_64
|
||||
insn_init(insn, kaddr, buf_len, 1);
|
||||
#else /* CONFIG_X86_32 */
|
||||
insn_init(insn, kaddr, buf_len, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline int insn_is_avx(struct insn *insn)
|
||||
{
|
||||
if (!insn->prefixes.got)
|
||||
insn_get_prefixes(insn);
|
||||
return (insn->vex_prefix.value != 0);
|
||||
}
|
||||
|
||||
/* Ensure this instruction is decoded completely */
|
||||
static inline int insn_complete(struct insn *insn)
|
||||
{
|
||||
return insn->opcode.got && insn->modrm.got && insn->sib.got &&
|
||||
insn->displacement.got && insn->immediate.got;
|
||||
}
|
||||
|
||||
static inline insn_byte_t insn_vex_m_bits(struct insn *insn)
|
||||
{
|
||||
if (insn->vex_prefix.nbytes == 2) /* 2 bytes VEX */
|
||||
return X86_VEX2_M;
|
||||
else
|
||||
return X86_VEX3_M(insn->vex_prefix.bytes[1]);
|
||||
}
|
||||
|
||||
static inline insn_byte_t insn_vex_p_bits(struct insn *insn)
|
||||
{
|
||||
if (insn->vex_prefix.nbytes == 2) /* 2 bytes VEX */
|
||||
return X86_VEX_P(insn->vex_prefix.bytes[1]);
|
||||
else
|
||||
return X86_VEX_P(insn->vex_prefix.bytes[2]);
|
||||
}
|
||||
|
||||
/* Get the last prefix id from last prefix or VEX prefix */
|
||||
static inline int insn_last_prefix_id(struct insn *insn)
|
||||
{
|
||||
if (insn_is_avx(insn))
|
||||
return insn_vex_p_bits(insn); /* VEX_p is a SIMD prefix id */
|
||||
|
||||
if (insn->prefixes.bytes[3])
|
||||
return inat_get_last_prefix_id(insn->prefixes.bytes[3]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Offset of each field from kaddr */
|
||||
static inline int insn_offset_rex_prefix(struct insn *insn)
|
||||
{
|
||||
return insn->prefixes.nbytes;
|
||||
}
|
||||
static inline int insn_offset_vex_prefix(struct insn *insn)
|
||||
{
|
||||
return insn_offset_rex_prefix(insn) + insn->rex_prefix.nbytes;
|
||||
}
|
||||
static inline int insn_offset_opcode(struct insn *insn)
|
||||
{
|
||||
return insn_offset_vex_prefix(insn) + insn->vex_prefix.nbytes;
|
||||
}
|
||||
static inline int insn_offset_modrm(struct insn *insn)
|
||||
{
|
||||
return insn_offset_opcode(insn) + insn->opcode.nbytes;
|
||||
}
|
||||
static inline int insn_offset_sib(struct insn *insn)
|
||||
{
|
||||
return insn_offset_modrm(insn) + insn->modrm.nbytes;
|
||||
}
|
||||
static inline int insn_offset_displacement(struct insn *insn)
|
||||
{
|
||||
return insn_offset_sib(insn) + insn->sib.nbytes;
|
||||
}
|
||||
static inline int insn_offset_immediate(struct insn *insn)
|
||||
{
|
||||
return insn_offset_displacement(insn) + insn->displacement.nbytes;
|
||||
}
|
||||
|
||||
#endif /* _ASM_X86_INSN_H */
|
||||
@@ -0,0 +1,246 @@
|
||||
/*
|
||||
* intel_pt_insn_decoder.c: Intel Processor Trace support
|
||||
* Copyright (c) 2013-2014, Intel Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <endian.h>
|
||||
#include <byteswap.h>
|
||||
|
||||
#include "event.h"
|
||||
|
||||
#include <asm/insn.h>
|
||||
|
||||
#include "inat.c"
|
||||
#include "insn.c"
|
||||
|
||||
#include "intel-pt-insn-decoder.h"
|
||||
|
||||
/* Based on branch_type() from perf_event_intel_lbr.c */
|
||||
static void intel_pt_insn_decoder(struct insn *insn,
|
||||
struct intel_pt_insn *intel_pt_insn)
|
||||
{
|
||||
enum intel_pt_insn_op op = INTEL_PT_OP_OTHER;
|
||||
enum intel_pt_insn_branch branch = INTEL_PT_BR_NO_BRANCH;
|
||||
int ext;
|
||||
|
||||
if (insn_is_avx(insn)) {
|
||||
intel_pt_insn->op = INTEL_PT_OP_OTHER;
|
||||
intel_pt_insn->branch = INTEL_PT_BR_NO_BRANCH;
|
||||
intel_pt_insn->length = insn->length;
|
||||
return;
|
||||
}
|
||||
|
||||
switch (insn->opcode.bytes[0]) {
|
||||
case 0xf:
|
||||
switch (insn->opcode.bytes[1]) {
|
||||
case 0x05: /* syscall */
|
||||
case 0x34: /* sysenter */
|
||||
op = INTEL_PT_OP_SYSCALL;
|
||||
branch = INTEL_PT_BR_INDIRECT;
|
||||
break;
|
||||
case 0x07: /* sysret */
|
||||
case 0x35: /* sysexit */
|
||||
op = INTEL_PT_OP_SYSRET;
|
||||
branch = INTEL_PT_BR_INDIRECT;
|
||||
break;
|
||||
case 0x80 ... 0x8f: /* jcc */
|
||||
op = INTEL_PT_OP_JCC;
|
||||
branch = INTEL_PT_BR_CONDITIONAL;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 0x70 ... 0x7f: /* jcc */
|
||||
op = INTEL_PT_OP_JCC;
|
||||
branch = INTEL_PT_BR_CONDITIONAL;
|
||||
break;
|
||||
case 0xc2: /* near ret */
|
||||
case 0xc3: /* near ret */
|
||||
case 0xca: /* far ret */
|
||||
case 0xcb: /* far ret */
|
||||
op = INTEL_PT_OP_RET;
|
||||
branch = INTEL_PT_BR_INDIRECT;
|
||||
break;
|
||||
case 0xcf: /* iret */
|
||||
op = INTEL_PT_OP_IRET;
|
||||
branch = INTEL_PT_BR_INDIRECT;
|
||||
break;
|
||||
case 0xcc ... 0xce: /* int */
|
||||
op = INTEL_PT_OP_INT;
|
||||
branch = INTEL_PT_BR_INDIRECT;
|
||||
break;
|
||||
case 0xe8: /* call near rel */
|
||||
op = INTEL_PT_OP_CALL;
|
||||
branch = INTEL_PT_BR_UNCONDITIONAL;
|
||||
break;
|
||||
case 0x9a: /* call far absolute */
|
||||
op = INTEL_PT_OP_CALL;
|
||||
branch = INTEL_PT_BR_INDIRECT;
|
||||
break;
|
||||
case 0xe0 ... 0xe2: /* loop */
|
||||
op = INTEL_PT_OP_LOOP;
|
||||
branch = INTEL_PT_BR_CONDITIONAL;
|
||||
break;
|
||||
case 0xe3: /* jcc */
|
||||
op = INTEL_PT_OP_JCC;
|
||||
branch = INTEL_PT_BR_CONDITIONAL;
|
||||
break;
|
||||
case 0xe9: /* jmp */
|
||||
case 0xeb: /* jmp */
|
||||
op = INTEL_PT_OP_JMP;
|
||||
branch = INTEL_PT_BR_UNCONDITIONAL;
|
||||
break;
|
||||
case 0xea: /* far jmp */
|
||||
op = INTEL_PT_OP_JMP;
|
||||
branch = INTEL_PT_BR_INDIRECT;
|
||||
break;
|
||||
case 0xff: /* call near absolute, call far absolute ind */
|
||||
ext = (insn->modrm.bytes[0] >> 3) & 0x7;
|
||||
switch (ext) {
|
||||
case 2: /* near ind call */
|
||||
case 3: /* far ind call */
|
||||
op = INTEL_PT_OP_CALL;
|
||||
branch = INTEL_PT_BR_INDIRECT;
|
||||
break;
|
||||
case 4:
|
||||
case 5:
|
||||
op = INTEL_PT_OP_JMP;
|
||||
branch = INTEL_PT_BR_INDIRECT;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
intel_pt_insn->op = op;
|
||||
intel_pt_insn->branch = branch;
|
||||
intel_pt_insn->length = insn->length;
|
||||
|
||||
if (branch == INTEL_PT_BR_CONDITIONAL ||
|
||||
branch == INTEL_PT_BR_UNCONDITIONAL) {
|
||||
#if __BYTE_ORDER == __BIG_ENDIAN
|
||||
switch (insn->immediate.nbytes) {
|
||||
case 1:
|
||||
intel_pt_insn->rel = insn->immediate.value;
|
||||
break;
|
||||
case 2:
|
||||
intel_pt_insn->rel =
|
||||
bswap_16((short)insn->immediate.value);
|
||||
break;
|
||||
case 4:
|
||||
intel_pt_insn->rel = bswap_32(insn->immediate.value);
|
||||
break;
|
||||
}
|
||||
#else
|
||||
intel_pt_insn->rel = insn->immediate.value;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
int intel_pt_get_insn(const unsigned char *buf, size_t len, int x86_64,
|
||||
struct intel_pt_insn *intel_pt_insn)
|
||||
{
|
||||
struct insn insn;
|
||||
|
||||
insn_init(&insn, buf, len, x86_64);
|
||||
insn_get_length(&insn);
|
||||
if (!insn_complete(&insn) || insn.length > len)
|
||||
return -1;
|
||||
intel_pt_insn_decoder(&insn, intel_pt_insn);
|
||||
if (insn.length < INTEL_PT_INSN_DBG_BUF_SZ)
|
||||
memcpy(intel_pt_insn->buf, buf, insn.length);
|
||||
else
|
||||
memcpy(intel_pt_insn->buf, buf, INTEL_PT_INSN_DBG_BUF_SZ);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *branch_name[] = {
|
||||
[INTEL_PT_OP_OTHER] = "Other",
|
||||
[INTEL_PT_OP_CALL] = "Call",
|
||||
[INTEL_PT_OP_RET] = "Ret",
|
||||
[INTEL_PT_OP_JCC] = "Jcc",
|
||||
[INTEL_PT_OP_JMP] = "Jmp",
|
||||
[INTEL_PT_OP_LOOP] = "Loop",
|
||||
[INTEL_PT_OP_IRET] = "IRet",
|
||||
[INTEL_PT_OP_INT] = "Int",
|
||||
[INTEL_PT_OP_SYSCALL] = "Syscall",
|
||||
[INTEL_PT_OP_SYSRET] = "Sysret",
|
||||
};
|
||||
|
||||
const char *intel_pt_insn_name(enum intel_pt_insn_op op)
|
||||
{
|
||||
return branch_name[op];
|
||||
}
|
||||
|
||||
int intel_pt_insn_desc(const struct intel_pt_insn *intel_pt_insn, char *buf,
|
||||
size_t buf_len)
|
||||
{
|
||||
switch (intel_pt_insn->branch) {
|
||||
case INTEL_PT_BR_CONDITIONAL:
|
||||
case INTEL_PT_BR_UNCONDITIONAL:
|
||||
return snprintf(buf, buf_len, "%s %s%d",
|
||||
intel_pt_insn_name(intel_pt_insn->op),
|
||||
intel_pt_insn->rel > 0 ? "+" : "",
|
||||
intel_pt_insn->rel);
|
||||
case INTEL_PT_BR_NO_BRANCH:
|
||||
case INTEL_PT_BR_INDIRECT:
|
||||
return snprintf(buf, buf_len, "%s",
|
||||
intel_pt_insn_name(intel_pt_insn->op));
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t intel_pt_insn_max_size(void)
|
||||
{
|
||||
return MAX_INSN_SIZE;
|
||||
}
|
||||
|
||||
int intel_pt_insn_type(enum intel_pt_insn_op op)
|
||||
{
|
||||
switch (op) {
|
||||
case INTEL_PT_OP_OTHER:
|
||||
return 0;
|
||||
case INTEL_PT_OP_CALL:
|
||||
return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL;
|
||||
case INTEL_PT_OP_RET:
|
||||
return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN;
|
||||
case INTEL_PT_OP_JCC:
|
||||
return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CONDITIONAL;
|
||||
case INTEL_PT_OP_JMP:
|
||||
return PERF_IP_FLAG_BRANCH;
|
||||
case INTEL_PT_OP_LOOP:
|
||||
return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CONDITIONAL;
|
||||
case INTEL_PT_OP_IRET:
|
||||
return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN |
|
||||
PERF_IP_FLAG_INTERRUPT;
|
||||
case INTEL_PT_OP_INT:
|
||||
return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL |
|
||||
PERF_IP_FLAG_INTERRUPT;
|
||||
case INTEL_PT_OP_SYSCALL:
|
||||
return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL |
|
||||
PERF_IP_FLAG_SYSCALLRET;
|
||||
case INTEL_PT_OP_SYSRET:
|
||||
return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN |
|
||||
PERF_IP_FLAG_SYSCALLRET;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* intel_pt_insn_decoder.h: Intel Processor Trace support
|
||||
* Copyright (c) 2013-2014, Intel Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef INCLUDE__INTEL_PT_INSN_DECODER_H__
|
||||
#define INCLUDE__INTEL_PT_INSN_DECODER_H__
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define INTEL_PT_INSN_DESC_MAX 32
|
||||
#define INTEL_PT_INSN_DBG_BUF_SZ 16
|
||||
|
||||
enum intel_pt_insn_op {
|
||||
INTEL_PT_OP_OTHER,
|
||||
INTEL_PT_OP_CALL,
|
||||
INTEL_PT_OP_RET,
|
||||
INTEL_PT_OP_JCC,
|
||||
INTEL_PT_OP_JMP,
|
||||
INTEL_PT_OP_LOOP,
|
||||
INTEL_PT_OP_IRET,
|
||||
INTEL_PT_OP_INT,
|
||||
INTEL_PT_OP_SYSCALL,
|
||||
INTEL_PT_OP_SYSRET,
|
||||
};
|
||||
|
||||
enum intel_pt_insn_branch {
|
||||
INTEL_PT_BR_NO_BRANCH,
|
||||
INTEL_PT_BR_INDIRECT,
|
||||
INTEL_PT_BR_CONDITIONAL,
|
||||
INTEL_PT_BR_UNCONDITIONAL,
|
||||
};
|
||||
|
||||
struct intel_pt_insn {
|
||||
enum intel_pt_insn_op op;
|
||||
enum intel_pt_insn_branch branch;
|
||||
int length;
|
||||
int32_t rel;
|
||||
unsigned char buf[INTEL_PT_INSN_DBG_BUF_SZ];
|
||||
};
|
||||
|
||||
int intel_pt_get_insn(const unsigned char *buf, size_t len, int x86_64,
|
||||
struct intel_pt_insn *intel_pt_insn);
|
||||
|
||||
const char *intel_pt_insn_name(enum intel_pt_insn_op op);
|
||||
|
||||
int intel_pt_insn_desc(const struct intel_pt_insn *intel_pt_insn, char *buf,
|
||||
size_t buf_len);
|
||||
|
||||
size_t intel_pt_insn_max_size(void);
|
||||
|
||||
int intel_pt_insn_type(enum intel_pt_insn_op op);
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user