Commit-ID:  237fae79f50d2d0c7bdeb039bc2c87fc6d52c7e7
Gitweb:     http://git.kernel.org/tip/237fae79f50d2d0c7bdeb039bc2c87fc6d52c7e7
Author:     Adrian Hunter <adrian.hun...@intel.com>
AuthorDate: Thu, 13 Aug 2015 10:14:55 +0300
Committer:  Arnaldo Carvalho de Melo <a...@redhat.com>
CommitDate: Mon, 17 Aug 2015 11:11:36 -0300

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.hun...@intel.com>
Cc: Jiri Olsa <jo...@redhat.com>
Link: 
http://lkml.kernel.org/r/1439450095-30122-1-git-send-email-adrian.hun...@intel.com
Signed-off-by: Arnaldo Carvalho de Melo <a...@redhat.com>
---
 tools/build/Makefile.build                         |   2 +
 tools/perf/.gitignore                              |   1 +
 tools/perf/Makefile.perf                           |  12 +-
 tools/perf/util/intel-pt-decoder/Build             |  12 +-
 .../util/intel-pt-decoder/gen-insn-attr-x86.awk    | 386 +++++++++++++++++++++
 tools/perf/util/intel-pt-decoder/inat.c            |  96 +++++
 .../perf/util/intel-pt-decoder}/inat.h             |   0
 .../perf/util/intel-pt-decoder}/insn.c             |   0
 .../perf/util/intel-pt-decoder}/insn.h             |   0
 .../util/intel-pt-decoder/intel-pt-insn-decoder.c  | 246 +++++++++++++
 .../util/intel-pt-decoder/intel-pt-insn-decoder.h  |  65 ++++
 .../perf/util/intel-pt-decoder}/x86-opcode-map.txt |   0
 12 files changed, 817 insertions(+), 3 deletions(-)

diff --git a/tools/build/Makefile.build b/tools/build/Makefile.build
index faca2bf..8120af9 100644
--- a/tools/build/Makefile.build
+++ b/tools/build/Makefile.build
@@ -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       $@
diff --git a/tools/perf/.gitignore b/tools/perf/.gitignore
index 09db62b..3d1bb80 100644
--- a/tools/perf/.gitignore
+++ b/tools/perf/.gitignore
@@ -29,3 +29,4 @@ config.mak.autogen
 *.pyc
 *.pyo
 .config-detected
+util/intel-pt-decoder/inat-tables.c
diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf
index 4b58dae..d9863cb 100644
--- a/tools/perf/Makefile.perf
+++ b/tools/perf/Makefile.perf
@@ -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)
 
diff --git a/tools/perf/util/intel-pt-decoder/Build 
b/tools/perf/util/intel-pt-decoder/Build
index 9d67381..5a46ce1 100644
--- a/tools/perf/util/intel-pt-decoder/Build
+++ b/tools/perf/util/intel-pt-decoder/Build
@@ -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
diff --git a/tools/perf/util/intel-pt-decoder/gen-insn-attr-x86.awk 
b/tools/perf/util/intel-pt-decoder/gen-insn-attr-x86.awk
new file mode 100644
index 0000000..51756734
--- /dev/null
+++ b/tools/perf/util/intel-pt-decoder/gen-insn-attr-x86.awk
@@ -0,0 +1,386 @@
+#!/bin/awk -f
+# gen-insn-attr-x86.awk: Instruction attribute table generator
+# Written by Masami Hiramatsu <mhira...@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 "};"
+}
diff --git a/tools/perf/util/intel-pt-decoder/inat.c 
b/tools/perf/util/intel-pt-decoder/inat.c
new file mode 100644
index 0000000..feeaa50
--- /dev/null
+++ b/tools/perf/util/intel-pt-decoder/inat.c
@@ -0,0 +1,96 @@
+/*
+ * x86 instruction attribute tables
+ *
+ * Written by Masami Hiramatsu <mhira...@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];
+}
diff --git a/arch/x86/include/asm/inat.h 
b/tools/perf/util/intel-pt-decoder/inat.h
similarity index 100%
copy from arch/x86/include/asm/inat.h
copy to tools/perf/util/intel-pt-decoder/inat.h
diff --git a/arch/x86/lib/insn.c b/tools/perf/util/intel-pt-decoder/insn.c
similarity index 100%
copy from arch/x86/lib/insn.c
copy to tools/perf/util/intel-pt-decoder/insn.c
diff --git a/arch/x86/include/asm/insn.h 
b/tools/perf/util/intel-pt-decoder/insn.h
similarity index 100%
copy from arch/x86/include/asm/insn.h
copy to tools/perf/util/intel-pt-decoder/insn.h
diff --git a/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.c 
b/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.c
new file mode 100644
index 0000000..46980fc
--- /dev/null
+++ b/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.c
@@ -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;
+       }
+}
diff --git a/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.h 
b/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.h
new file mode 100644
index 0000000..b0adbf3
--- /dev/null
+++ b/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.h
@@ -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
diff --git a/arch/x86/lib/x86-opcode-map.txt 
b/tools/perf/util/intel-pt-decoder/x86-opcode-map.txt
similarity index 100%
copy from arch/x86/lib/x86-opcode-map.txt
copy to tools/perf/util/intel-pt-decoder/x86-opcode-map.txt
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to