diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/.gitignore | 1 | ||||
| -rw-r--r-- | lib/bc.lua | 182 | ||||
| -rw-r--r-- | lib/dis_x64.lua | 19 | ||||
| -rw-r--r-- | lib/dis_x86.lua | 824 | ||||
| -rw-r--r-- | lib/dump.lua | 567 | ||||
| -rw-r--r-- | lib/v.lua | 156 |
6 files changed, 1749 insertions, 0 deletions
diff --git a/lib/.gitignore b/lib/.gitignore new file mode 100644 index 00000000..500e2855 --- /dev/null +++ b/lib/.gitignore | |||
| @@ -0,0 +1 @@ | |||
| vmdef.lua | |||
diff --git a/lib/bc.lua b/lib/bc.lua new file mode 100644 index 00000000..532f2493 --- /dev/null +++ b/lib/bc.lua | |||
| @@ -0,0 +1,182 @@ | |||
| 1 | ---------------------------------------------------------------------------- | ||
| 2 | -- LuaJIT bytecode listing module. | ||
| 3 | -- | ||
| 4 | -- Copyright (C) 2005-2009 Mike Pall. All rights reserved. | ||
| 5 | -- Released under the MIT/X license. See Copyright Notice in luajit.h | ||
| 6 | ---------------------------------------------------------------------------- | ||
| 7 | -- | ||
| 8 | -- This module lists the bytecode of a Lua function. If it's loaded by -jbc | ||
| 9 | -- it hooks into the parser and lists all functions of a chunk as they | ||
| 10 | -- are parsed. | ||
| 11 | -- | ||
| 12 | -- Example usage: | ||
| 13 | -- | ||
| 14 | -- luajit -jbc -e 'local x=0; for i=1,1e6 do x=x+i end; print(x)' | ||
| 15 | -- luajit -jbc=- foo.lua | ||
| 16 | -- luajit -jbc=foo.list foo.lua | ||
| 17 | -- | ||
| 18 | -- Default output is to stderr. To redirect the output to a file, pass a | ||
| 19 | -- filename as an argument (use '-' for stdout) or set the environment | ||
| 20 | -- variable LUAJIT_LISTFILE. The file is overwritten every time the module | ||
| 21 | -- is started. | ||
| 22 | -- | ||
| 23 | -- This module can also be used programmatically: | ||
| 24 | -- | ||
| 25 | -- local bc = require("jit.bc") | ||
| 26 | -- | ||
| 27 | -- local function foo() print("hello") end | ||
| 28 | -- | ||
| 29 | -- bc.dump(foo) --> -- BYTECODE -- [...] | ||
| 30 | -- print(bc.line(foo, 2)) --> 0002 KSTR 1 1 ; "hello" | ||
| 31 | -- | ||
| 32 | -- local out = { | ||
| 33 | -- -- Do something wich each line: | ||
| 34 | -- write = function(t, ...) io.write(...) end, | ||
| 35 | -- close = function(t) end, | ||
| 36 | -- flush = function(t) end, | ||
| 37 | -- } | ||
| 38 | -- bc.dump(foo, out) | ||
| 39 | -- | ||
| 40 | ------------------------------------------------------------------------------ | ||
| 41 | |||
| 42 | -- Cache some library functions and objects. | ||
| 43 | local jit = require("jit") | ||
| 44 | assert(jit.version_num == 20000, "LuaJIT core/library version mismatch") | ||
| 45 | local jutil = require("jit.util") | ||
| 46 | local vmdef = require("jit.vmdef") | ||
| 47 | local bit = require("bit") | ||
| 48 | local sub, gsub, format = string.sub, string.gsub, string.format | ||
| 49 | local byte, band, shr = string.byte, bit.band, bit.rshift | ||
| 50 | local funcinfo, funcbc, funck = jutil.funcinfo, jutil.funcbc, jutil.funck | ||
| 51 | local funcuvname = jutil.funcuvname | ||
| 52 | local bcnames = vmdef.bcnames | ||
| 53 | local stdout, stderr = io.stdout, io.stderr | ||
| 54 | |||
| 55 | ------------------------------------------------------------------------------ | ||
| 56 | |||
| 57 | local function ctlsub(c) | ||
| 58 | if c == "\n" then return "\\n" | ||
| 59 | elseif c == "\r" then return "\\r" | ||
| 60 | elseif c == "\t" then return "\\t" | ||
| 61 | elseif c == "\r" then return "\\r" | ||
| 62 | else return format("\\%03d", byte(c)) | ||
| 63 | end | ||
| 64 | end | ||
| 65 | |||
| 66 | -- Return one bytecode line. | ||
| 67 | local function bcline(func, pc, prefix) | ||
| 68 | local ins, m = funcbc(func, pc) | ||
| 69 | if not ins then return end | ||
| 70 | local ma, mb, mc = band(m, 7), band(m, 15*8), band(m, 15*128) | ||
| 71 | local a = band(shr(ins, 8), 0xff) | ||
| 72 | local oidx = 6*band(ins, 0xff) | ||
| 73 | local s = format("%04d %s %-6s %3s ", | ||
| 74 | pc, prefix or " ", sub(bcnames, oidx+1, oidx+6), ma == 0 and "" or a) | ||
| 75 | local d = shr(ins, 16) | ||
| 76 | if mc == 13*128 then -- BCMjump | ||
| 77 | if ma == 0 then | ||
| 78 | return format("%s=> %04d\n", sub(s, 1, -3), pc+d-0x7fff) | ||
| 79 | end | ||
| 80 | return format("%s=> %04d\n", s, pc+d-0x7fff) | ||
| 81 | end | ||
| 82 | if mb ~= 0 then d = band(d, 0xff) end | ||
| 83 | local kc | ||
| 84 | if mc == 10*128 then -- BCMstr | ||
| 85 | kc = funck(func, -d-1) | ||
| 86 | kc = format(#kc > 40 and '"%.40s"~' or '"%s"', gsub(kc, "%c", ctlsub)) | ||
| 87 | elseif mc == 9*128 then -- BCMnum | ||
| 88 | kc = funck(func, d) | ||
| 89 | elseif mc == 12*128 then -- BCMfunc | ||
| 90 | local fi = funcinfo(funck(func, -d-1)) | ||
| 91 | if fi.ffid then | ||
| 92 | kc = vmdef.ffnames[fi.ffid] | ||
| 93 | else | ||
| 94 | kc = fi.loc | ||
| 95 | end | ||
| 96 | elseif mc == 5*128 then -- BCMuv | ||
| 97 | kc = funcuvname(func, d) | ||
| 98 | end | ||
| 99 | if ma == 5 then -- BCMuv | ||
| 100 | local ka = funcuvname(func, a) | ||
| 101 | if kc then kc = ka.." ; "..kc else kc = ka end | ||
| 102 | end | ||
| 103 | if mb ~= 0 then | ||
| 104 | local b = shr(ins, 24) | ||
| 105 | if kc then return format("%s%3d %3d ; %s\n", s, b, d, kc) end | ||
| 106 | return format("%s%3d %3d\n", s, b, d) | ||
| 107 | end | ||
| 108 | if kc then return format("%s%3d ; %s\n", s, d, kc) end | ||
| 109 | if mc == 7*128 and d > 32767 then d = d - 65536 end -- BCMlits | ||
| 110 | return format("%s%3d\n", s, d) | ||
| 111 | end | ||
| 112 | |||
| 113 | -- Collect branch targets of a function. | ||
| 114 | local function bctargets(func) | ||
| 115 | local target = {} | ||
| 116 | for pc=1,1000000000 do | ||
| 117 | local ins, m = funcbc(func, pc) | ||
| 118 | if not ins then break end | ||
| 119 | if band(m, 15*128) == 13*128 then target[pc+shr(ins, 16)-0x7fff] = true end | ||
| 120 | end | ||
| 121 | return target | ||
| 122 | end | ||
| 123 | |||
| 124 | -- Dump bytecode instructions of a function. | ||
| 125 | local function bcdump(func, out) | ||
| 126 | if not out then out = stdout end | ||
| 127 | local fi = funcinfo(func) | ||
| 128 | out:write(format("-- BYTECODE -- %s-%d\n", fi.loc, fi.lastlinedefined)) | ||
| 129 | local target = bctargets(func) | ||
| 130 | for pc=1,1000000000 do | ||
| 131 | local s = bcline(func, pc, target[pc] and "=>") | ||
| 132 | if not s then break end | ||
| 133 | out:write(s) | ||
| 134 | end | ||
| 135 | out:write("\n") | ||
| 136 | out:flush() | ||
| 137 | end | ||
| 138 | |||
| 139 | ------------------------------------------------------------------------------ | ||
| 140 | |||
| 141 | -- Active flag and output file handle. | ||
| 142 | local active, out | ||
| 143 | |||
| 144 | -- List handler. | ||
| 145 | local function h_list(func) | ||
| 146 | return bcdump(func, out) | ||
| 147 | end | ||
| 148 | |||
| 149 | -- Detach list handler. | ||
| 150 | local function bclistoff() | ||
| 151 | if active then | ||
| 152 | active = false | ||
| 153 | jit.attach(h_list) | ||
| 154 | if out and out ~= stdout and out ~= stderr then out:close() end | ||
| 155 | out = nil | ||
| 156 | end | ||
| 157 | end | ||
| 158 | |||
| 159 | -- Open the output file and attach list handler. | ||
| 160 | local function bcliston(outfile) | ||
| 161 | if active then bclistoff() end | ||
| 162 | if not outfile then outfile = os.getenv("LUAJIT_LISTFILE") end | ||
| 163 | if outfile then | ||
| 164 | out = outfile == "-" and stdout or assert(io.open(outfile, "w")) | ||
| 165 | else | ||
| 166 | out = stderr | ||
| 167 | end | ||
| 168 | jit.attach(h_list, "bc") | ||
| 169 | active = true | ||
| 170 | end | ||
| 171 | |||
| 172 | -- Public module functions. | ||
| 173 | module(...) | ||
| 174 | |||
| 175 | line = bcline | ||
| 176 | dump = bcdump | ||
| 177 | targets = bctargets | ||
| 178 | |||
| 179 | on = bcliston | ||
| 180 | off = bclistoff | ||
| 181 | start = bcliston -- For -j command line option. | ||
| 182 | |||
diff --git a/lib/dis_x64.lua b/lib/dis_x64.lua new file mode 100644 index 00000000..da3d63f8 --- /dev/null +++ b/lib/dis_x64.lua | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | ---------------------------------------------------------------------------- | ||
| 2 | -- LuaJIT x64 disassembler wrapper module. | ||
| 3 | -- | ||
| 4 | -- Copyright (C) 2005-2009 Mike Pall. All rights reserved. | ||
| 5 | -- Released under the MIT/X license. See Copyright Notice in luajit.h | ||
| 6 | ---------------------------------------------------------------------------- | ||
| 7 | -- This module just exports the 64 bit functions from the combined | ||
| 8 | -- x86/x64 disassembler module. All the interesting stuff is there. | ||
| 9 | ------------------------------------------------------------------------------ | ||
| 10 | |||
| 11 | local require = require | ||
| 12 | |||
| 13 | module(...) | ||
| 14 | |||
| 15 | local dis_x86 = require(_PACKAGE.."dis_x86") | ||
| 16 | |||
| 17 | create = dis_x86.create64 | ||
| 18 | disass = dis_x86.disass64 | ||
| 19 | |||
diff --git a/lib/dis_x86.lua b/lib/dis_x86.lua new file mode 100644 index 00000000..8f127bee --- /dev/null +++ b/lib/dis_x86.lua | |||
| @@ -0,0 +1,824 @@ | |||
| 1 | ---------------------------------------------------------------------------- | ||
| 2 | -- LuaJIT x86/x64 disassembler module. | ||
| 3 | -- | ||
| 4 | -- Copyright (C) 2005-2009 Mike Pall. All rights reserved. | ||
| 5 | -- Released under the MIT/X license. See Copyright Notice in luajit.h | ||
| 6 | ---------------------------------------------------------------------------- | ||
| 7 | -- This is a helper module used by the LuaJIT machine code dumper module. | ||
| 8 | -- | ||
| 9 | -- Sending small code snippets to an external disassembler and mixing the | ||
| 10 | -- output with our own stuff was too fragile. So I had to bite the bullet | ||
| 11 | -- and write yet another x86 disassembler. Oh well ... | ||
| 12 | -- | ||
| 13 | -- The output format is very similar to what ndisasm generates. But it has | ||
| 14 | -- been developed independently by looking at the opcode tables from the | ||
| 15 | -- Intel and AMD manuals. The supported instruction set is quite extensive | ||
| 16 | -- and reflects what a current generation Intel or AMD CPU implements in | ||
| 17 | -- 32 bit and 64 bit mode. Yes, this includes MMX, SSE, SSE2, SSE3, SSSE3, | ||
| 18 | -- SSE4.1, SSE4.2, SSE4a and even privileged and hypervisor (VMX/SVM) | ||
| 19 | -- instructions. | ||
| 20 | -- | ||
| 21 | -- Notes: | ||
| 22 | -- * The (useless) a16 prefix, 3DNow and pre-586 opcodes are unsupported. | ||
| 23 | -- * No attempt at optimization has been made -- it's fast enough for my needs. | ||
| 24 | -- * The public API may change when more architectures are added. | ||
| 25 | ------------------------------------------------------------------------------ | ||
| 26 | |||
| 27 | local type = type | ||
| 28 | local sub, byte, format = string.sub, string.byte, string.format | ||
| 29 | local match, gmatch, gsub = string.match, string.gmatch, string.gsub | ||
| 30 | local lower, rep = string.lower, string.rep | ||
| 31 | |||
| 32 | -- Map for 1st opcode byte in 32 bit mode. Ugly? Well ... read on. | ||
| 33 | local map_opc1_32 = { | ||
| 34 | --0x | ||
| 35 | [0]="addBmr","addVmr","addBrm","addVrm","addBai","addVai","push es","pop es", | ||
| 36 | "orBmr","orVmr","orBrm","orVrm","orBai","orVai","push cs","opc2*", | ||
| 37 | --1x | ||
| 38 | "adcBmr","adcVmr","adcBrm","adcVrm","adcBai","adcVai","push ss","pop ss", | ||
| 39 | "sbbBmr","sbbVmr","sbbBrm","sbbVrm","sbbBai","sbbVai","push ds","pop ds", | ||
| 40 | --2x | ||
| 41 | "andBmr","andVmr","andBrm","andVrm","andBai","andVai","es:seg","daa", | ||
| 42 | "subBmr","subVmr","subBrm","subVrm","subBai","subVai","cs:seg","das", | ||
| 43 | --3x | ||
| 44 | "xorBmr","xorVmr","xorBrm","xorVrm","xorBai","xorVai","ss:seg","aaa", | ||
| 45 | "cmpBmr","cmpVmr","cmpBrm","cmpVrm","cmpBai","cmpVai","ds:seg","aas", | ||
| 46 | --4x | ||
| 47 | "incVR","incVR","incVR","incVR","incVR","incVR","incVR","incVR", | ||
| 48 | "decVR","decVR","decVR","decVR","decVR","decVR","decVR","decVR", | ||
| 49 | --5x | ||
| 50 | "pushUR","pushUR","pushUR","pushUR","pushUR","pushUR","pushUR","pushUR", | ||
| 51 | "popUR","popUR","popUR","popUR","popUR","popUR","popUR","popUR", | ||
| 52 | --6x | ||
| 53 | "sz*pushaw,pusha","sz*popaw,popa","boundVrm","arplWmr", | ||
| 54 | "fs:seg","gs:seg","o16:","a16", | ||
| 55 | "pushUi","imulVrmi","pushBs","imulVrms", | ||
| 56 | "insb","insVS","outsb","outsVS", | ||
| 57 | --7x | ||
| 58 | "joBj","jnoBj","jbBj","jnbBj","jzBj","jnzBj","jbeBj","jaBj", | ||
| 59 | "jsBj","jnsBj","jpeBj","jpoBj","jlBj","jgeBj","jleBj","jgBj", | ||
| 60 | --8x | ||
| 61 | "arith!Bmi","arith!Vmi","arith!Bmi","arith!Vms", | ||
| 62 | "testBmr","testVmr","xchgBrm","xchgVrm", | ||
| 63 | "movBmr","movVmr","movBrm","movVrm", | ||
| 64 | "movVmg","leaVrm","movWgm","popUm", | ||
| 65 | --9x | ||
| 66 | "nop*xchgVaR|pause|xchgWaR|repne nop","xchgVaR","xchgVaR","xchgVaR", | ||
| 67 | "xchgVaR","xchgVaR","xchgVaR","xchgVaR", | ||
| 68 | "sz*cbw,cwde,cdqe","sz*cwd,cdq,cqo","call farViw","wait", | ||
| 69 | "sz*pushfw,pushf","sz*popfw,popf","sahf","lahf", | ||
| 70 | --Ax | ||
| 71 | "movBao","movVao","movBoa","movVoa", | ||
| 72 | "movsb","movsVS","cmpsb","cmpsVS", | ||
| 73 | "testBai","testVai","stosb","stosVS", | ||
| 74 | "lodsb","lodsVS","scasb","scasVS", | ||
| 75 | --Bx | ||
| 76 | "movBRi","movBRi","movBRi","movBRi","movBRi","movBRi","movBRi","movBRi", | ||
| 77 | "movVRI","movVRI","movVRI","movVRI","movVRI","movVRI","movVRI","movVRI", | ||
| 78 | --Cx | ||
| 79 | "shift!Bmu","shift!Vmu","retBw","ret","$lesVrm","$ldsVrm","movBmi","movVmi", | ||
| 80 | "enterBwu","leave","retfBw","retf","int3","intBu","into","iretVS", | ||
| 81 | --Dx | ||
| 82 | "shift!Bm1","shift!Vm1","shift!Bmc","shift!Vmc","aamBu","aadBu","salc","xlatb", | ||
| 83 | "fp*0","fp*1","fp*2","fp*3","fp*4","fp*5","fp*6","fp*7", | ||
| 84 | --Ex | ||
| 85 | "loopneBj","loopeBj","loopBj","sz*jcxzBj,jecxzBj,jrcxzBj", | ||
| 86 | "inBau","inVau","outBua","outVua", | ||
| 87 | "callVj","jmpVj","jmp farViw","jmpBj","inBad","inVad","outBda","outVda", | ||
| 88 | --Fx | ||
| 89 | "lock:","int1","repne:rep","rep:","hlt","cmc","testb!Bm","testv!Vm", | ||
| 90 | "clc","stc","cli","sti","cld","std","incb!Bm","incd!Vm", | ||
| 91 | } | ||
| 92 | assert(#map_opc1_32 == 255) | ||
| 93 | |||
| 94 | -- Map for 1st opcode byte in 64 bit mode (overrides only). | ||
| 95 | local map_opc1_64 = setmetatable({ | ||
| 96 | [0x06]=false, [0x07]=false, [0x0e]=false, | ||
| 97 | [0x16]=false, [0x17]=false, [0x1e]=false, [0x1f]=false, | ||
| 98 | [0x27]=false, [0x2f]=false, [0x37]=false, [0x3f]=false, | ||
| 99 | [0x60]=false, [0x61]=false, [0x62]=false, [0x63]="movsxdVrDmt", [0x67]="a32:", | ||
| 100 | [0x40]="rex*", [0x41]="rex*b", [0x42]="rex*x", [0x43]="rex*xb", | ||
| 101 | [0x44]="rex*r", [0x45]="rex*rb", [0x46]="rex*rx", [0x47]="rex*rxb", | ||
| 102 | [0x48]="rex*w", [0x49]="rex*wb", [0x4a]="rex*wx", [0x4b]="rex*wxb", | ||
| 103 | [0x4c]="rex*wr", [0x4d]="rex*wrb", [0x4e]="rex*wrx", [0x4f]="rex*wrxb", | ||
| 104 | [0x82]=false, [0x9a]=false, [0xc4]=false, [0xc5]=false, [0xce]=false, | ||
| 105 | [0xd4]=false, [0xd5]=false, [0xd6]=false, [0xea]=false, | ||
| 106 | }, { __index = map_opc1_32 }) | ||
| 107 | |||
| 108 | -- Map for 2nd opcode byte (0F xx). True CISC hell. Hey, I told you. | ||
| 109 | -- Prefix dependent MMX/SSE opcodes: (none)|rep|o16|repne, -|F3|66|F2 | ||
| 110 | local map_opc2 = { | ||
| 111 | --0x | ||
| 112 | [0]="sldt!Dmp","sgdt!Ump","larVrm","lslVrm",nil,"syscall","clts","sysret", | ||
| 113 | "invd","wbinvd",nil,"ud1",nil,"$prefetch!Bm","femms","3dnowMrmu", | ||
| 114 | --1x | ||
| 115 | "movupsXrm|movssXrm|movupdXrm|movsdXrm", | ||
| 116 | "movupsXmr|movssXmr|movupdXmr|movsdXmr", | ||
| 117 | "movhlpsXrm$movlpsXrm|movsldupXrm|movlpdXrm|movddupXrm", | ||
| 118 | "movlpsXmr||movlpdXmr", | ||
| 119 | "unpcklpsXrm||unpcklpdXrm", | ||
| 120 | "unpckhpsXrm||unpckhpdXrm", | ||
| 121 | "movlhpsXrm$movhpsXrm|movshdupXrm|movhpdXrm", | ||
| 122 | "movhpsXmr||movhpdXmr", | ||
| 123 | "$prefetcht!Bm","hintnopVm","hintnopVm","hintnopVm", | ||
| 124 | "hintnopVm","hintnopVm","hintnopVm","hintnopVm", | ||
| 125 | --2x | ||
| 126 | "movUmx$","movUmy$","movUxm$","movUym$","movUmz$",nil,"movUzm$",nil, | ||
| 127 | "movapsXrm||movapdXrm", | ||
| 128 | "movapsXmr||movapdXmr", | ||
| 129 | "cvtpi2psXrMm|cvtsi2ssXrVm|cvtpi2pdXrMm|cvtsi2sdXrVm", | ||
| 130 | "movntpsXmr|movntssXmr|movntpdXmr|movntsdXmr", | ||
| 131 | "cvttps2piMrXm|cvttss2siVrXm|cvttpd2piMrXm|cvttsd2siVrXm", | ||
| 132 | "cvtps2piMrXm|cvtss2siVrXm|cvtpd2piMrXm|cvtsd2siVrXm", | ||
| 133 | "ucomissXrm||ucomisdXrm", | ||
| 134 | "comissXrm||comisdXrm", | ||
| 135 | --3x | ||
| 136 | "wrmsr","rdtsc","rdmsr","rdpmc","sysenter","sysexit",nil,"getsec", | ||
| 137 | "opc3*38",nil,"opc3*3a",nil,nil,nil,nil,nil, | ||
| 138 | --4x | ||
| 139 | "cmovoVrm","cmovnoVrm","cmovbVrm","cmovnbVrm", | ||
| 140 | "cmovzVrm","cmovnzVrm","cmovbeVrm","cmovaVrm", | ||
| 141 | "cmovsVrm","cmovnsVrm","cmovpeVrm","cmovpoVrm", | ||
| 142 | "cmovlVrm","cmovgeVrm","cmovleVrm","cmovgVrm", | ||
| 143 | --5x | ||
| 144 | "movmskpsVrXm$||movmskpdVrXm$","sqrtpsXrm|sqrtssXrm|sqrtpdXrm|sqrtsdXrm", | ||
| 145 | "rsqrtpsXrm|rsqrtssXrm","rcppsXrm|rcpssXrm", | ||
| 146 | "andpsXrm||andpdXrm","andnpsXrm||andnpdXrm", | ||
| 147 | "orpsXrm||orpdXrm","xorpsXrm||xorpdXrm", | ||
| 148 | "addpsXrm|addssXrm|addpdXrm|addsdXrm","mulpsXrm|mulssXrm|mulpdXrm|mulsdXrm", | ||
| 149 | "cvtps2pdXrm|cvtss2sdXrm|cvtpd2psXrm|cvtsd2ssXrm", | ||
| 150 | "cvtdq2psXrm|cvttps2dqXrm|cvtps2dqXrm", | ||
| 151 | "subpsXrm|subssXrm|subpdXrm|subsdXrm","minpsXrm|minssXrm|minpdXrm|minsdXrm", | ||
| 152 | "divpsXrm|divssXrm|divpdXrm|divsdXrm","maxpsXrm|maxssXrm|maxpdXrm|maxsdXrm", | ||
| 153 | --6x | ||
| 154 | "punpcklbwPrm","punpcklwdPrm","punpckldqPrm","packsswbPrm", | ||
| 155 | "pcmpgtbPrm","pcmpgtwPrm","pcmpgtdPrm","packuswbPrm", | ||
| 156 | "punpckhbwPrm","punpckhwdPrm","punpckhdqPrm","packssdwPrm", | ||
| 157 | "||punpcklqdqXrm","||punpckhqdqXrm", | ||
| 158 | "movPrVSm","movqMrm|movdquXrm|movdqaXrm", | ||
| 159 | --7x | ||
| 160 | "pshufwMrmu|pshufhwXrmu|pshufdXrmu|pshuflwXrmu","pshiftw!Pmu", | ||
| 161 | "pshiftd!Pmu","pshiftq!Mmu||pshiftdq!Xmu", | ||
| 162 | "pcmpeqbPrm","pcmpeqwPrm","pcmpeqdPrm","emms|", | ||
| 163 | "vmreadUmr||extrqXmuu$|insertqXrmuu$","vmwriteUrm||extrqXrm$|insertqXrm$", | ||
| 164 | nil,nil, | ||
| 165 | "||haddpdXrm|haddpsXrm","||hsubpdXrm|hsubpsXrm", | ||
| 166 | "movVSmMr|movqXrm|movVSmXr","movqMmr|movdquXmr|movdqaXmr", | ||
| 167 | --8x | ||
| 168 | "joVj","jnoVj","jbVj","jnbVj","jzVj","jnzVj","jbeVj","jaVj", | ||
| 169 | "jsVj","jnsVj","jpeVj","jpoVj","jlVj","jgeVj","jleVj","jgVj", | ||
| 170 | --9x | ||
| 171 | "setoBm","setnoBm","setbBm","setnbBm","setzBm","setnzBm","setbeBm","setaBm", | ||
| 172 | "setsBm","setnsBm","setpeBm","setpoBm","setlBm","setgeBm","setleBm","setgBm", | ||
| 173 | --Ax | ||
| 174 | "push fs","pop fs","cpuid","btVmr","shldVmru","shldVmrc",nil,nil, | ||
| 175 | "push gs","pop gs","rsm","btsVmr","shrdVmru","shrdVmrc","fxsave!Dmp","imulVrm", | ||
| 176 | --Bx | ||
| 177 | "cmpxchgBmr","cmpxchgVmr","$lssVrm","btrVmr", | ||
| 178 | "$lfsVrm","$lgsVrm","movzxVrBmt","movzxVrWmt", | ||
| 179 | "|popcntVrm","ud2Dp","bt!Vmu","btcVmr", | ||
| 180 | "bsfVrm","bsrVrm|lzcntVrm|bsrWrm","movsxVrBmt","movsxVrWmt", | ||
| 181 | --Cx | ||
| 182 | "xaddBmr","xaddVmr", | ||
| 183 | "cmppsXrmu|cmpssXrmu|cmppdXrmu|cmpsdXrmu","$movntiVmr|", | ||
| 184 | "pinsrwPrWmu","pextrwDrPmu", | ||
| 185 | "shufpsXrmu||shufpdXrmu","$cmpxchg!Qmp", | ||
| 186 | "bswapVR","bswapVR","bswapVR","bswapVR","bswapVR","bswapVR","bswapVR","bswapVR", | ||
| 187 | --Dx | ||
| 188 | "||addsubpdXrm|addsubpsXrm","psrlwPrm","psrldPrm","psrlqPrm", | ||
| 189 | "paddqPrm","pmullwPrm", | ||
| 190 | "|movq2dqXrMm|movqXmr|movdq2qMrXm$","pmovmskbVrMm||pmovmskbVrXm", | ||
| 191 | "psubusbPrm","psubuswPrm","pminubPrm","pandPrm", | ||
| 192 | "paddusbPrm","padduswPrm","pmaxubPrm","pandnPrm", | ||
| 193 | --Ex | ||
| 194 | "pavgbPrm","psrawPrm","psradPrm","pavgwPrm", | ||
| 195 | "pmulhuwPrm","pmulhwPrm", | ||
| 196 | "|cvtdq2pdXrm|cvttpd2dqXrm|cvtpd2dqXrm","$movntqMmr||$movntdqXmr", | ||
| 197 | "psubsbPrm","psubswPrm","pminswPrm","porPrm", | ||
| 198 | "paddsbPrm","paddswPrm","pmaxswPrm","pxorPrm", | ||
| 199 | --Fx | ||
| 200 | "|||lddquXrm","psllwPrm","pslldPrm","psllqPrm", | ||
| 201 | "pmuludqPrm","pmaddwdPrm","psadbwPrm","maskmovqMrm||maskmovdquXrm$", | ||
| 202 | "psubbPrm","psubwPrm","psubdPrm","psubqPrm", | ||
| 203 | "paddbPrm","paddwPrm","padddPrm","ud", | ||
| 204 | } | ||
| 205 | assert(map_opc2[255] == "ud") | ||
| 206 | |||
| 207 | -- Map for three-byte opcodes. Can't wait for their next invention. | ||
| 208 | local map_opc3 = { | ||
| 209 | ["38"] = { -- [66] 0f 38 xx | ||
| 210 | --0x | ||
| 211 | [0]="pshufbPrm","phaddwPrm","phadddPrm","phaddswPrm", | ||
| 212 | "pmaddubswPrm","phsubwPrm","phsubdPrm","phsubswPrm", | ||
| 213 | "psignbPrm","psignwPrm","psigndPrm","pmulhrswPrm", | ||
| 214 | nil,nil,nil,nil, | ||
| 215 | --1x | ||
| 216 | "||pblendvbXrma",nil,nil,nil, | ||
| 217 | "||blendvpsXrma","||blendvpdXrma",nil,"||ptestXrm", | ||
| 218 | nil,nil,nil,nil, | ||
| 219 | "pabsbPrm","pabswPrm","pabsdPrm",nil, | ||
| 220 | --2x | ||
| 221 | "||pmovsxbwXrm","||pmovsxbdXrm","||pmovsxbqXrm","||pmovsxwdXrm", | ||
| 222 | "||pmovsxwqXrm","||pmovsxdqXrm",nil,nil, | ||
| 223 | "||pmuldqXrm","||pcmpeqqXrm","||$movntdqaXrm","||packusdwXrm", | ||
| 224 | nil,nil,nil,nil, | ||
| 225 | --3x | ||
| 226 | "||pmovzxbwXrm","||pmovzxbdXrm","||pmovzxbqXrm","||pmovzxwdXrm", | ||
| 227 | "||pmovzxwqXrm","||pmovzxdqXrm",nil,"||pcmpgtqXrm", | ||
| 228 | "||pminsbXrm","||pminsdXrm","||pminuwXrm","||pminudXrm", | ||
| 229 | "||pmaxsbXrm","||pmaxsdXrm","||pmaxuwXrm","||pmaxudXrm", | ||
| 230 | --4x | ||
| 231 | "||pmulddXrm","||phminposuwXrm", | ||
| 232 | --Fx | ||
| 233 | [0xf0] = "|||crc32TrBmt",[0xf1] = "|||crc32TrVmt", | ||
| 234 | }, | ||
| 235 | |||
| 236 | ["3a"] = { -- [66] 0f 3a xx | ||
| 237 | --0x | ||
| 238 | [0x00]=nil,nil,nil,nil,nil,nil,nil,nil, | ||
| 239 | "||roundpsXrmu","||roundpdXrmu","||roundssXrmu","||roundsdXrmu", | ||
| 240 | "||blendpsXrmu","||blendpdXrmu","||pblendwXrmu","palignrPrmu", | ||
| 241 | --1x | ||
| 242 | nil,nil,nil,nil, | ||
| 243 | "||pextrbVmXru","||pextrwVmXru","||pextrVmSXru","||extractpsVmXru", | ||
| 244 | nil,nil,nil,nil,nil,nil,nil,nil, | ||
| 245 | --2x | ||
| 246 | "||pinsrbXrVmu","||insertpsXrmu","||pinsrXrVmuS",nil, | ||
| 247 | --4x | ||
| 248 | [0x40] = "||dppsXrmu", | ||
| 249 | [0x41] = "||dppdXrmu", | ||
| 250 | [0x42] = "||mpsadbwXrmu", | ||
| 251 | --6x | ||
| 252 | [0x60] = "||pcmpestrmXrmu",[0x61] = "||pcmpestriXrmu", | ||
| 253 | [0x62] = "||pcmpistrmXrmu",[0x63] = "||pcmpistriXrmu", | ||
| 254 | }, | ||
| 255 | } | ||
| 256 | |||
| 257 | -- Map for VMX/SVM opcodes 0F 01 C0-FF (sgdt group with register operands). | ||
| 258 | local map_opcvm = { | ||
| 259 | [0xc1]="vmcall",[0xc2]="vmlaunch",[0xc3]="vmresume",[0xc4]="vmxoff", | ||
| 260 | [0xc8]="monitor",[0xc9]="mwait", | ||
| 261 | [0xd8]="vmrun",[0xd9]="vmmcall",[0xda]="vmload",[0xdb]="vmsave", | ||
| 262 | [0xdc]="stgi",[0xdd]="clgi",[0xde]="skinit",[0xdf]="invlpga", | ||
| 263 | [0xf8]="swapgs",[0xf9]="rdtscp", | ||
| 264 | } | ||
| 265 | |||
| 266 | -- Map for FP opcodes. And you thought stack machines are simple? | ||
| 267 | local map_opcfp = { | ||
| 268 | -- D8-DF 00-BF: opcodes with a memory operand. | ||
| 269 | -- D8 | ||
| 270 | [0]="faddFm","fmulFm","fcomFm","fcompFm","fsubFm","fsubrFm","fdivFm","fdivrFm", | ||
| 271 | "fldFm",nil,"fstFm","fstpFm","fldenvVm","fldcwWm","fnstenvVm","fnstcwWm", | ||
| 272 | -- DA | ||
| 273 | "fiaddDm","fimulDm","ficomDm","ficompDm", | ||
| 274 | "fisubDm","fisubrDm","fidivDm","fidivrDm", | ||
| 275 | -- DB | ||
| 276 | "fildDm","fisttpDm","fistDm","fistpDm",nil,"fld twordFmp",nil,"fstp twordFmp", | ||
| 277 | -- DC | ||
| 278 | "faddGm","fmulGm","fcomGm","fcompGm","fsubGm","fsubrGm","fdivGm","fdivrGm", | ||
| 279 | -- DD | ||
| 280 | "fldGm","fisttpQm","fstGm","fstpGm","frstorDmp",nil,"fnsaveDmp","fnstswWm", | ||
| 281 | -- DE | ||
| 282 | "fiaddWm","fimulWm","ficomWm","ficompWm", | ||
| 283 | "fisubWm","fisubrWm","fidivWm","fidivrWm", | ||
| 284 | -- DF | ||
| 285 | "fildWm","fisttpWm","fistWm","fistpWm", | ||
| 286 | "fbld twordFmp","fildQm","fbstp twordFmp","fistpQm", | ||
| 287 | -- xx C0-FF: opcodes with a pseudo-register operand. | ||
| 288 | -- D8 | ||
| 289 | "faddFf","fmulFf","fcomFf","fcompFf","fsubFf","fsubrFf","fdivFf","fdivrFf", | ||
| 290 | -- D9 | ||
| 291 | "fldFf","fxchFf",{"fnop"},nil, | ||
| 292 | {"fchs","fabs",nil,nil,"ftst","fxam"}, | ||
| 293 | {"fld1","fldl2t","fldl2e","fldpi","fldlg2","fldln2","fldz"}, | ||
| 294 | {"f2xm1","fyl2x","fptan","fpatan","fxtract","fprem1","fdecstp","fincstp"}, | ||
| 295 | {"fprem","fyl2xp1","fsqrt","fsincos","frndint","fscale","fsin","fcos"}, | ||
| 296 | -- DA | ||
| 297 | "fcmovbFf","fcmoveFf","fcmovbeFf","fcmovuFf",nil,{nil,"fucompp"},nil,nil, | ||
| 298 | -- DB | ||
| 299 | "fcmovnbFf","fcmovneFf","fcmovnbeFf","fcmovnuFf", | ||
| 300 | {nil,nil,"fnclex","fninit"},"fucomiFf","fcomiFf",nil, | ||
| 301 | -- DC | ||
| 302 | "fadd toFf","fmul toFf",nil,nil, | ||
| 303 | "fsub toFf","fsubr toFf","fdivr toFf","fdiv toFf", | ||
| 304 | -- DD | ||
| 305 | "ffreeFf",nil,"fstFf","fstpFf","fucomFf","fucompFf",nil,nil, | ||
| 306 | -- DE | ||
| 307 | "faddpFf","fmulpFf",nil,{nil,"fcompp"}, | ||
| 308 | "fsubrpFf","fsubpFf","fdivrpFf","fdivpFf", | ||
| 309 | -- DF | ||
| 310 | nil,nil,nil,nil,{"fnstsw ax"},"fucomipFf","fcomipFf",nil, | ||
| 311 | } | ||
| 312 | assert(map_opcfp[126] == "fcomipFf") | ||
| 313 | |||
| 314 | -- Map for opcode groups. The subkey is sp from the ModRM byte. | ||
| 315 | local map_opcgroup = { | ||
| 316 | arith = { "add", "or", "adc", "sbb", "and", "sub", "xor", "cmp" }, | ||
| 317 | shift = { "rol", "ror", "rcl", "rcr", "shl", "shr", "sal", "sar" }, | ||
| 318 | testb = { "testBmi", "testBmi", "not", "neg", "mul", "imul", "div", "idiv" }, | ||
| 319 | testv = { "testVmi", "testVmi", "not", "neg", "mul", "imul", "div", "idiv" }, | ||
| 320 | incb = { "inc", "dec" }, | ||
| 321 | incd = { "inc", "dec", "callDmp", "$call farDmp", | ||
| 322 | "jmpDmp", "$jmp farDmp", "pushUm" }, | ||
| 323 | sldt = { "sldt", "str", "lldt", "ltr", "verr", "verw" }, | ||
| 324 | sgdt = { "vm*$sgdt", "vm*$sidt", "$lgdt", "vm*$lidt", | ||
| 325 | "smsw", nil, "lmsw", "vm*$invlpg" }, | ||
| 326 | bt = { nil, nil, nil, nil, "bt", "bts", "btr", "btc" }, | ||
| 327 | cmpxchg = { nil, "sz*,cmpxchg8bQmp,cmpxchg16bXmp", nil, nil, | ||
| 328 | nil, nil, "vmptrld|vmxon|vmclear", "vmptrst" }, | ||
| 329 | pshiftw = { nil, nil, "psrlw", nil, "psraw", nil, "psllw" }, | ||
| 330 | pshiftd = { nil, nil, "psrld", nil, "psrad", nil, "pslld" }, | ||
| 331 | pshiftq = { nil, nil, "psrlq", nil, nil, nil, "psllq" }, | ||
| 332 | pshiftdq = { nil, nil, "psrlq", "psrldq", nil, nil, "psllq", "pslldq" }, | ||
| 333 | fxsave = { "$fxsave", "$fxrstor", "$ldmxcsr", "$stmxcsr", | ||
| 334 | nil, "lfenceDp$", "mfenceDp$", "sfenceDp$clflush" }, | ||
| 335 | prefetch = { "prefetch", "prefetchw" }, | ||
| 336 | prefetcht = { "prefetchnta", "prefetcht0", "prefetcht1", "prefetcht2" }, | ||
| 337 | } | ||
| 338 | |||
| 339 | ------------------------------------------------------------------------------ | ||
| 340 | |||
| 341 | -- Maps for register names. | ||
| 342 | local map_regs = { | ||
| 343 | B = { "al", "cl", "dl", "bl", "ah", "ch", "dh", "bh", | ||
| 344 | "r8b", "r9b", "r10b", "r11b", "r12b", "r13b", "r14b", "r15b" }, | ||
| 345 | B64 = { "al", "cl", "dl", "bl", "spl", "bpl", "sil", "dil", | ||
| 346 | "r8b", "r9b", "r10b", "r11b", "r12b", "r13b", "r14b", "r15b" }, | ||
| 347 | W = { "ax", "cx", "dx", "bx", "sp", "bp", "si", "di", | ||
| 348 | "r8w", "r9w", "r10w", "r11w", "r12w", "r13w", "r14w", "r15w" }, | ||
| 349 | D = { "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi", | ||
| 350 | "r8d", "r9d", "r10d", "r11d", "r12d", "r13d", "r14d", "r15d" }, | ||
| 351 | Q = { "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi", | ||
| 352 | "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15" }, | ||
| 353 | M = { "mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7", | ||
| 354 | "mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7" }, -- No x64 ext! | ||
| 355 | X = { "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7", | ||
| 356 | "xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15" }, | ||
| 357 | } | ||
| 358 | local map_segregs = { "es", "cs", "ss", "ds", "fs", "gs", "segr6", "segr7" } | ||
| 359 | |||
| 360 | -- Maps for size names. | ||
| 361 | local map_sz2n = { | ||
| 362 | B = 1, W = 2, D = 4, Q = 8, M = 8, X = 16, | ||
| 363 | } | ||
| 364 | local map_sz2prefix = { | ||
| 365 | B = "byte", W = "word", D = "dword", | ||
| 366 | Q = "qword", | ||
| 367 | M = "qword", X = "xword", | ||
| 368 | F = "dword", G = "qword", -- No need for sizes/register names for these two. | ||
| 369 | } | ||
| 370 | |||
| 371 | ------------------------------------------------------------------------------ | ||
| 372 | |||
| 373 | -- Output a nicely formatted line with an opcode and operands. | ||
| 374 | local function putop(ctx, text, operands) | ||
| 375 | local code, pos, hex = ctx.code, ctx.pos, "" | ||
| 376 | local hmax = ctx.hexdump | ||
| 377 | if hmax > 0 then | ||
| 378 | for i=ctx.start,pos-1 do | ||
| 379 | hex = hex..format("%02X", byte(code, i, i)) | ||
| 380 | end | ||
| 381 | if #hex > hmax then hex = sub(hex, 1, hmax)..". " | ||
| 382 | else hex = hex..rep(" ", hmax-#hex+2) end | ||
| 383 | end | ||
| 384 | if operands then text = text.." "..operands end | ||
| 385 | if ctx.o16 then text = "o16 "..text; ctx.o16 = false end | ||
| 386 | if ctx.a32 then text = "a32 "..text; ctx.a32 = false end | ||
| 387 | if ctx.rep then text = ctx.rep.." "..text; ctx.rep = false end | ||
| 388 | if ctx.rex then | ||
| 389 | local t = (ctx.rexw and "w" or "")..(ctx.rexr and "r" or "").. | ||
| 390 | (ctx.rexx and "x" or "")..(ctx.rexb and "b" or "") | ||
| 391 | if t ~= "" then text = "rex."..t.." "..text end | ||
| 392 | ctx.rexw = false; ctx.rexr = false; ctx.rexx = false; ctx.rexb = false | ||
| 393 | ctx.rex = false | ||
| 394 | end | ||
| 395 | if ctx.seg then | ||
| 396 | local text2, n = gsub(text, "%[", "["..ctx.seg..":") | ||
| 397 | if n == 0 then text = ctx.seg.." "..text else text = text2 end | ||
| 398 | ctx.seg = false | ||
| 399 | end | ||
| 400 | if ctx.lock then text = "lock "..text; ctx.lock = false end | ||
| 401 | local imm = ctx.imm | ||
| 402 | if imm then | ||
| 403 | local sym = ctx.symtab[imm] | ||
| 404 | if sym then text = text.."\t->"..sym end | ||
| 405 | end | ||
| 406 | ctx.out(format("%08x %s%s\n", ctx.addr+ctx.start, hex, text)) | ||
| 407 | ctx.mrm = false | ||
| 408 | ctx.start = pos | ||
| 409 | ctx.imm = nil | ||
| 410 | end | ||
| 411 | |||
| 412 | -- Clear all prefix flags. | ||
| 413 | local function clearprefixes(ctx) | ||
| 414 | ctx.o16 = false; ctx.seg = false; ctx.lock = false; ctx.rep = false | ||
| 415 | ctx.rexw = false; ctx.rexr = false; ctx.rexx = false; ctx.rexb = false | ||
| 416 | ctx.rex = false; ctx.a32 = false | ||
| 417 | end | ||
| 418 | |||
| 419 | -- Fallback for incomplete opcodes at the end. | ||
| 420 | local function incomplete(ctx) | ||
| 421 | ctx.pos = ctx.stop+1 | ||
| 422 | clearprefixes(ctx) | ||
| 423 | return putop(ctx, "(incomplete)") | ||
| 424 | end | ||
| 425 | |||
| 426 | -- Fallback for unknown opcodes. | ||
| 427 | local function unknown(ctx) | ||
| 428 | clearprefixes(ctx) | ||
| 429 | return putop(ctx, "(unknown)") | ||
| 430 | end | ||
| 431 | |||
| 432 | -- Return an immediate of the specified size. | ||
| 433 | local function getimm(ctx, pos, n) | ||
| 434 | if pos+n-1 > ctx.stop then return incomplete(ctx) end | ||
| 435 | local code = ctx.code | ||
| 436 | if n == 1 then | ||
| 437 | local b1 = byte(code, pos, pos) | ||
| 438 | return b1 | ||
| 439 | elseif n == 2 then | ||
| 440 | local b1, b2 = byte(code, pos, pos+1) | ||
| 441 | return b1+b2*256 | ||
| 442 | else | ||
| 443 | local b1, b2, b3, b4 = byte(code, pos, pos+3) | ||
| 444 | local imm = b1+b2*256+b3*65536+b4*16777216 | ||
| 445 | ctx.imm = imm | ||
| 446 | return imm | ||
| 447 | end | ||
| 448 | end | ||
| 449 | |||
| 450 | -- Process pattern string and generate the operands. | ||
| 451 | local function putpat(ctx, name, pat) | ||
| 452 | local operands, regs, sz, mode, sp, rm, sc, rx, sdisp | ||
| 453 | local code, pos, stop = ctx.code, ctx.pos, ctx.stop | ||
| 454 | |||
| 455 | -- Chars used: 1DFGIMPQRSTUVWXacdfgijmoprstuwxyz | ||
| 456 | for p in gmatch(pat, ".") do | ||
| 457 | local x = nil | ||
| 458 | if p == "V" or p == "U" then | ||
| 459 | if ctx.rexw then sz = "Q"; ctx.rexw = false | ||
| 460 | elseif ctx.o16 then sz = "W"; ctx.o16 = false | ||
| 461 | elseif p == "U" and ctx.x64 then sz = "Q" | ||
| 462 | else sz = "D" end | ||
| 463 | regs = map_regs[sz] | ||
| 464 | elseif p == "T" then | ||
| 465 | if ctx.rexw then sz = "Q"; ctx.rexw = false else sz = "D" end | ||
| 466 | regs = map_regs[sz] | ||
| 467 | elseif p == "B" then | ||
| 468 | sz = "B" | ||
| 469 | regs = ctx.rex and map_regs.B64 or map_regs.B | ||
| 470 | elseif match(p, "[WDQMXFG]") then | ||
| 471 | sz = p | ||
| 472 | regs = map_regs[sz] | ||
| 473 | elseif p == "P" then | ||
| 474 | sz = ctx.o16 and "X" or "M"; ctx.o16 = false | ||
| 475 | regs = map_regs[sz] | ||
| 476 | elseif p == "S" then | ||
| 477 | name = name..lower(sz) | ||
| 478 | elseif p == "s" then | ||
| 479 | local imm = getimm(ctx, pos, 1); if not imm then return end | ||
| 480 | x = imm <= 127 and format("+0x%02x", imm) | ||
| 481 | or format("-0x%02x", 256-imm) | ||
| 482 | pos = pos+1 | ||
| 483 | elseif p == "u" then | ||
| 484 | local imm = getimm(ctx, pos, 1); if not imm then return end | ||
| 485 | x = format("0x%02x", imm) | ||
| 486 | pos = pos+1 | ||
| 487 | elseif p == "w" then | ||
| 488 | local imm = getimm(ctx, pos, 2); if not imm then return end | ||
| 489 | x = format("0x%x", imm) | ||
| 490 | pos = pos+2 | ||
| 491 | elseif p == "o" then -- [offset] | ||
| 492 | if ctx.x64 then | ||
| 493 | local imm1 = getimm(ctx, pos, 4); if not imm1 then return end | ||
| 494 | local imm2 = getimm(ctx, pos+4, 4); if not imm2 then return end | ||
| 495 | x = format("[0x%08x%08x]", imm2, imm1) | ||
| 496 | pos = pos+8 | ||
| 497 | else | ||
| 498 | local imm = getimm(ctx, pos, 4); if not imm then return end | ||
| 499 | x = format("[0x%08x]", imm) | ||
| 500 | pos = pos+4 | ||
| 501 | end | ||
| 502 | elseif p == "i" or p == "I" then | ||
| 503 | local n = map_sz2n[sz] | ||
| 504 | if n == 8 and ctx.x64 and p == "I" then | ||
| 505 | local imm1 = getimm(ctx, pos, 4); if not imm1 then return end | ||
| 506 | local imm2 = getimm(ctx, pos+4, 4); if not imm2 then return end | ||
| 507 | x = format("0x%08x%08x", imm2, imm1) | ||
| 508 | else | ||
| 509 | if n == 8 then n = 4 end | ||
| 510 | local imm = getimm(ctx, pos, n); if not imm then return end | ||
| 511 | if sz == "Q" and (imm < 0 or imm > 0x7fffffff) then | ||
| 512 | imm = (0xffffffff+1)-imm | ||
| 513 | x = format(imm > 65535 and "-0x%08x" or "-0x%x", imm) | ||
| 514 | else | ||
| 515 | x = format(imm > 65535 and "0x%08x" or "0x%x", imm) | ||
| 516 | end | ||
| 517 | end | ||
| 518 | pos = pos+n | ||
| 519 | elseif p == "j" then | ||
| 520 | local n = map_sz2n[sz] | ||
| 521 | if n == 8 then n = 4 end | ||
| 522 | local imm = getimm(ctx, pos, n); if not imm then return end | ||
| 523 | if sz == "B" and imm > 127 then imm = imm-256 | ||
| 524 | elseif imm > 2147483647 then imm = imm-4294967296 end | ||
| 525 | pos = pos+n | ||
| 526 | imm = imm + pos + ctx.addr | ||
| 527 | if imm > 4294967295 and not ctx.x64 then imm = imm-4294967296 end | ||
| 528 | ctx.imm = imm | ||
| 529 | if sz == "W" then | ||
| 530 | x = format("word 0x%04x", imm%65536) | ||
| 531 | elseif ctx.x64 then | ||
| 532 | local lo = imm % 0x1000000 | ||
| 533 | x = format("0x%02x%06x", (imm-lo) / 0x1000000, lo) | ||
| 534 | else | ||
| 535 | x = format("0x%08x", imm) | ||
| 536 | end | ||
| 537 | elseif p == "R" then | ||
| 538 | local r = byte(code, pos-1, pos-1)%8 | ||
| 539 | if ctx.rexb then r = r + 8; ctx.rexb = false end | ||
| 540 | x = regs[r+1] | ||
| 541 | elseif p == "a" then x = regs[1] | ||
| 542 | elseif p == "c" then x = "cl" | ||
| 543 | elseif p == "d" then x = "dx" | ||
| 544 | elseif p == "1" then x = "1" | ||
| 545 | else | ||
| 546 | if not mode then | ||
| 547 | mode = ctx.mrm | ||
| 548 | if not mode then | ||
| 549 | if pos > stop then return incomplete(ctx) end | ||
| 550 | mode = byte(code, pos, pos) | ||
| 551 | pos = pos+1 | ||
| 552 | end | ||
| 553 | rm = mode%8; mode = (mode-rm)/8 | ||
| 554 | sp = mode%8; mode = (mode-sp)/8 | ||
| 555 | sdisp = "" | ||
| 556 | if mode < 3 then | ||
| 557 | if rm == 4 then | ||
| 558 | if pos > stop then return incomplete(ctx) end | ||
| 559 | sc = byte(code, pos, pos) | ||
| 560 | pos = pos+1 | ||
| 561 | rm = sc%8; sc = (sc-rm)/8 | ||
| 562 | rx = sc%8; sc = (sc-rx)/8 | ||
| 563 | if ctx.rexx then rx = rx + 8; ctx.rexx = false end | ||
| 564 | if rx == 4 then rx = nil end | ||
| 565 | end | ||
| 566 | if mode > 0 or rm == 5 then | ||
| 567 | local dsz = mode | ||
| 568 | if dsz ~= 1 then dsz = 4 end | ||
| 569 | local disp = getimm(ctx, pos, dsz); if not disp then return end | ||
| 570 | if mode == 0 then rm = nil end | ||
| 571 | if rm or rx or (not sc and ctx.x64 and not ctx.a32) then | ||
| 572 | if dsz == 1 and disp > 127 then | ||
| 573 | sdisp = format("-0x%x", 256-disp) | ||
| 574 | elseif disp >= 0 and disp <= 0x7fffffff then | ||
| 575 | sdisp = format("+0x%x", disp) | ||
| 576 | else | ||
| 577 | sdisp = format("-0x%x", (0xffffffff+1)-disp) | ||
| 578 | end | ||
| 579 | else | ||
| 580 | sdisp = format(ctx.x64 and not ctx.a32 and | ||
| 581 | not (disp >= 0 and disp <= 0x7fffffff) | ||
| 582 | and "0xffffffff%08x" or "0x%08x", disp) | ||
| 583 | end | ||
| 584 | pos = pos+dsz | ||
| 585 | end | ||
| 586 | end | ||
| 587 | if rm and ctx.rexb then rm = rm + 8; ctx.rexb = false end | ||
| 588 | if ctx.rexr then sp = sp + 8; ctx.rexr = false end | ||
| 589 | end | ||
| 590 | if p == "m" then | ||
| 591 | if mode == 3 then x = regs[rm+1] | ||
| 592 | else | ||
| 593 | local aregs = ctx.a32 and map_regs.D or ctx.aregs | ||
| 594 | local srm, srx = "", "" | ||
| 595 | if rm then srm = aregs[rm+1] | ||
| 596 | elseif not sc and ctx.x64 and not ctx.a32 then srm = "rip" end | ||
| 597 | ctx.a32 = false | ||
| 598 | if rx then | ||
| 599 | if rm then srm = srm.."+" end | ||
| 600 | srx = aregs[rx+1] | ||
| 601 | if sc > 0 then srx = srx.."*"..(2^sc) end | ||
| 602 | end | ||
| 603 | x = format("[%s%s%s]", srm, srx, sdisp) | ||
| 604 | end | ||
| 605 | if mode < 3 and | ||
| 606 | (not match(pat, "[aRrgp]") or match(pat, "t")) then -- Yuck. | ||
| 607 | x = map_sz2prefix[sz].." "..x | ||
| 608 | end | ||
| 609 | elseif p == "r" then x = regs[sp+1] | ||
| 610 | elseif p == "g" then x = map_segregs[sp+1] | ||
| 611 | elseif p == "p" then -- Suppress prefix. | ||
| 612 | elseif p == "f" then x = "st"..rm | ||
| 613 | elseif p == "x" then | ||
| 614 | if sp == 0 and ctx.lock and not ctx.x64 then | ||
| 615 | x = "CR8"; ctx.lock = false | ||
| 616 | else | ||
| 617 | x = "CR"..sp | ||
| 618 | end | ||
| 619 | elseif p == "y" then x = "DR"..sp | ||
| 620 | elseif p == "z" then x = "TR"..sp | ||
| 621 | elseif p == "t" then | ||
| 622 | else | ||
| 623 | error("bad pattern `"..pat.."'") | ||
| 624 | end | ||
| 625 | end | ||
| 626 | if x then operands = operands and operands..", "..x or x end | ||
| 627 | end | ||
| 628 | ctx.pos = pos | ||
| 629 | return putop(ctx, name, operands) | ||
| 630 | end | ||
| 631 | |||
| 632 | -- Forward declaration. | ||
| 633 | local map_act | ||
| 634 | |||
| 635 | -- Fetch and cache MRM byte. | ||
| 636 | local function getmrm(ctx) | ||
| 637 | local mrm = ctx.mrm | ||
| 638 | if not mrm then | ||
| 639 | local pos = ctx.pos | ||
| 640 | if pos > ctx.stop then return nil end | ||
| 641 | mrm = byte(ctx.code, pos, pos) | ||
| 642 | ctx.pos = pos+1 | ||
| 643 | ctx.mrm = mrm | ||
| 644 | end | ||
| 645 | return mrm | ||
| 646 | end | ||
| 647 | |||
| 648 | -- Dispatch to handler depending on pattern. | ||
| 649 | local function dispatch(ctx, opat, patgrp) | ||
| 650 | if not opat then return unknown(ctx) end | ||
| 651 | if match(opat, "%|") then -- MMX/SSE variants depending on prefix. | ||
| 652 | local p | ||
| 653 | if ctx.rep then | ||
| 654 | p = ctx.rep=="rep" and "%|([^%|]*)" or "%|[^%|]*%|[^%|]*%|([^%|]*)" | ||
| 655 | ctx.rep = false | ||
| 656 | elseif ctx.o16 then p = "%|[^%|]*%|([^%|]*)"; ctx.o16 = false | ||
| 657 | else p = "^[^%|]*" end | ||
| 658 | opat = match(opat, p) | ||
| 659 | if not opat then return unknown(ctx) end | ||
| 660 | -- ctx.rep = false; ctx.o16 = false | ||
| 661 | --XXX fails for 66 f2 0f 38 f1 06 crc32 eax,WORD PTR [esi] | ||
| 662 | --XXX remove in branches? | ||
| 663 | end | ||
| 664 | if match(opat, "%$") then -- reg$mem variants. | ||
| 665 | local mrm = getmrm(ctx); if not mrm then return incomplete(ctx) end | ||
| 666 | opat = match(opat, mrm >= 192 and "^[^%$]*" or "%$(.*)") | ||
| 667 | if opat == "" then return unknown(ctx) end | ||
| 668 | end | ||
| 669 | if opat == "" then return unknown(ctx) end | ||
| 670 | local name, pat = match(opat, "^([a-z0-9 ]*)(.*)") | ||
| 671 | if pat == "" and patgrp then pat = patgrp end | ||
| 672 | return map_act[sub(pat, 1, 1)](ctx, name, pat) | ||
| 673 | end | ||
| 674 | |||
| 675 | -- Get a pattern from an opcode map and dispatch to handler. | ||
| 676 | local function dispatchmap(ctx, opcmap) | ||
| 677 | local pos = ctx.pos | ||
| 678 | local opat = opcmap[byte(ctx.code, pos, pos)] | ||
| 679 | pos = pos + 1 | ||
| 680 | ctx.pos = pos | ||
| 681 | return dispatch(ctx, opat) | ||
| 682 | end | ||
| 683 | |||
| 684 | -- Map for action codes. The key is the first char after the name. | ||
| 685 | map_act = { | ||
| 686 | -- Simple opcodes without operands. | ||
| 687 | [""] = function(ctx, name, pat) | ||
| 688 | return putop(ctx, name) | ||
| 689 | end, | ||
| 690 | |||
| 691 | -- Operand size chars fall right through. | ||
| 692 | B = putpat, W = putpat, D = putpat, Q = putpat, | ||
| 693 | V = putpat, U = putpat, T = putpat, | ||
| 694 | M = putpat, X = putpat, P = putpat, | ||
| 695 | F = putpat, G = putpat, | ||
| 696 | |||
| 697 | -- Collect prefixes. | ||
| 698 | [":"] = function(ctx, name, pat) | ||
| 699 | ctx[pat == ":" and name or sub(pat, 2)] = name | ||
| 700 | if ctx.pos - ctx.start > 5 then return unknown(ctx) end -- Limit #prefixes. | ||
| 701 | end, | ||
| 702 | |||
| 703 | -- Chain to special handler specified by name. | ||
| 704 | ["*"] = function(ctx, name, pat) | ||
| 705 | return map_act[name](ctx, name, sub(pat, 2)) | ||
| 706 | end, | ||
| 707 | |||
| 708 | -- Use named subtable for opcode group. | ||
| 709 | ["!"] = function(ctx, name, pat) | ||
| 710 | local mrm = getmrm(ctx); if not mrm then return incomplete(ctx) end | ||
| 711 | return dispatch(ctx, map_opcgroup[name][((mrm-(mrm%8))/8)%8+1], sub(pat, 2)) | ||
| 712 | end, | ||
| 713 | |||
| 714 | -- o16,o32[,o64] variants. | ||
| 715 | sz = function(ctx, name, pat) | ||
| 716 | if ctx.o16 then ctx.o16 = false | ||
| 717 | else | ||
| 718 | pat = match(pat, ",(.*)") | ||
| 719 | if ctx.rexw then | ||
| 720 | local p = match(pat, ",(.*)") | ||
| 721 | if p then pat = p; ctx.rexw = false end | ||
| 722 | end | ||
| 723 | end | ||
| 724 | pat = match(pat, "^[^,]*") | ||
| 725 | return dispatch(ctx, pat) | ||
| 726 | end, | ||
| 727 | |||
| 728 | -- Two-byte opcode dispatch. | ||
| 729 | opc2 = function(ctx, name, pat) | ||
| 730 | return dispatchmap(ctx, map_opc2) | ||
| 731 | end, | ||
| 732 | |||
| 733 | -- Three-byte opcode dispatch. | ||
| 734 | opc3 = function(ctx, name, pat) | ||
| 735 | return dispatchmap(ctx, map_opc3[pat]) | ||
| 736 | end, | ||
| 737 | |||
| 738 | -- VMX/SVM dispatch. | ||
| 739 | vm = function(ctx, name, pat) | ||
| 740 | return dispatch(ctx, map_opcvm[ctx.mrm]) | ||
| 741 | end, | ||
| 742 | |||
| 743 | -- Floating point opcode dispatch. | ||
| 744 | fp = function(ctx, name, pat) | ||
| 745 | local mrm = getmrm(ctx); if not mrm then return incomplete(ctx) end | ||
| 746 | local rm = mrm%8 | ||
| 747 | local idx = pat*8 + ((mrm-rm)/8)%8 | ||
| 748 | if mrm >= 192 then idx = idx + 64 end | ||
| 749 | local opat = map_opcfp[idx] | ||
| 750 | if type(opat) == "table" then opat = opat[rm+1] end | ||
| 751 | return dispatch(ctx, opat) | ||
| 752 | end, | ||
| 753 | |||
| 754 | -- REX prefix. | ||
| 755 | rex = function(ctx, name, pat) | ||
| 756 | if ctx.rex then return unknown(ctx) end -- Only 1 REX prefix allowed. | ||
| 757 | for p in gmatch(pat, ".") do ctx["rex"..p] = true end | ||
| 758 | ctx.rex = true | ||
| 759 | end, | ||
| 760 | |||
| 761 | -- Special case for nop with REX prefix. | ||
| 762 | nop = function(ctx, name, pat) | ||
| 763 | return dispatch(ctx, ctx.rex and pat or "nop") | ||
| 764 | end, | ||
| 765 | } | ||
| 766 | |||
| 767 | ------------------------------------------------------------------------------ | ||
| 768 | |||
| 769 | -- Disassemble a block of code. | ||
| 770 | local function disass_block(ctx, ofs, len) | ||
| 771 | if not ofs then ofs = 0 end | ||
| 772 | local stop = len and ofs+len or #ctx.code | ||
| 773 | ofs = ofs + 1 | ||
| 774 | ctx.start = ofs | ||
| 775 | ctx.pos = ofs | ||
| 776 | ctx.stop = stop | ||
| 777 | ctx.imm = nil | ||
| 778 | ctx.mrm = false | ||
| 779 | clearprefixes(ctx) | ||
| 780 | while ctx.pos <= stop do dispatchmap(ctx, ctx.map1) end | ||
| 781 | if ctx.pos ~= ctx.start then incomplete(ctx) end | ||
| 782 | end | ||
| 783 | |||
| 784 | -- Extended API: create a disassembler context. Then call ctx:disass(ofs, len). | ||
| 785 | local function create_(code, addr, out) | ||
| 786 | local ctx = {} | ||
| 787 | ctx.code = code | ||
| 788 | ctx.addr = (addr or 0) - 1 | ||
| 789 | ctx.out = out or io.write | ||
| 790 | ctx.symtab = {} | ||
| 791 | ctx.disass = disass_block | ||
| 792 | ctx.hexdump = 16 | ||
| 793 | ctx.x64 = false | ||
| 794 | ctx.map1 = map_opc1_32 | ||
| 795 | ctx.aregs = map_regs.D | ||
| 796 | return ctx | ||
| 797 | end | ||
| 798 | |||
| 799 | local function create64_(code, addr, out) | ||
| 800 | local ctx = create_(code, addr, out) | ||
| 801 | ctx.x64 = true | ||
| 802 | ctx.map1 = map_opc1_64 | ||
| 803 | ctx.aregs = map_regs.Q | ||
| 804 | return ctx | ||
| 805 | end | ||
| 806 | |||
| 807 | -- Simple API: disassemble code (a string) at address and output via out. | ||
| 808 | local function disass_(code, addr, out) | ||
| 809 | create_(code, addr, out):disass() | ||
| 810 | end | ||
| 811 | |||
| 812 | local function disass64_(code, addr, out) | ||
| 813 | create64_(code, addr, out):disass() | ||
| 814 | end | ||
| 815 | |||
| 816 | |||
| 817 | -- Public module functions. | ||
| 818 | module(...) | ||
| 819 | |||
| 820 | create = create_ | ||
| 821 | create64 = create64_ | ||
| 822 | disass = disass_ | ||
| 823 | disass64 = disass64_ | ||
| 824 | |||
diff --git a/lib/dump.lua b/lib/dump.lua new file mode 100644 index 00000000..9fde87c1 --- /dev/null +++ b/lib/dump.lua | |||
| @@ -0,0 +1,567 @@ | |||
| 1 | ---------------------------------------------------------------------------- | ||
| 2 | -- LuaJIT compiler dump module. | ||
| 3 | -- | ||
| 4 | -- Copyright (C) 2005-2009 Mike Pall. All rights reserved. | ||
| 5 | -- Released under the MIT/X license. See Copyright Notice in luajit.h | ||
| 6 | ---------------------------------------------------------------------------- | ||
| 7 | -- | ||
| 8 | -- This module can be used to debug the JIT compiler itself. It dumps the | ||
| 9 | -- code representations and structures used in various compiler stages. | ||
| 10 | -- | ||
| 11 | -- Example usage: | ||
| 12 | -- | ||
| 13 | -- luajit -jdump -e "local x=0; for i=1,1e6 do x=x+i end; print(x)" | ||
| 14 | -- luajit -jdump=im -e "for i=1,1000 do for j=1,1000 do end end" | less -R | ||
| 15 | -- luajit -jdump=is myapp.lua | less -R | ||
| 16 | -- luajit -jdump=-b myapp.lua | ||
| 17 | -- luajit -jdump=+aH,myapp.html myapp.lua | ||
| 18 | -- luajit -jdump=ixT,myapp.dump myapp.lua | ||
| 19 | -- | ||
| 20 | -- The first argument specifies the dump mode. The second argument gives | ||
| 21 | -- the output file name. Default output is to stdout, unless the environment | ||
| 22 | -- variable LUAJIT_DUMPFILE is set. The file is overwritten every time the | ||
| 23 | -- module is started. | ||
| 24 | -- | ||
| 25 | -- Different features can be turned on or off with the dump mode. If the | ||
| 26 | -- mode starts with a '+', the following features are added to the default | ||
| 27 | -- set of features; a '-' removes them. Otherwise the features are replaced. | ||
| 28 | -- | ||
| 29 | -- The following dump features are available (* marks the default): | ||
| 30 | -- | ||
| 31 | -- * t Print a line for each started, ended or aborted trace (see also -jv). | ||
| 32 | -- * b Dump the traced bytecode. | ||
| 33 | -- * i Dump the IR (intermediate representation). | ||
| 34 | -- r Augment the IR with register/stack slots. | ||
| 35 | -- s Dump the snapshot map. | ||
| 36 | -- * m Dump the generated machine code. | ||
| 37 | -- x Print each taken trace exit. | ||
| 38 | -- X Print each taken trace exit and the contents of all registers. | ||
| 39 | -- | ||
| 40 | -- The output format can be set with the following characters: | ||
| 41 | -- | ||
| 42 | -- T Plain text output. | ||
| 43 | -- A ANSI-colored text output | ||
| 44 | -- H Colorized HTML + CSS output. | ||
| 45 | -- | ||
| 46 | -- The default output format is plain text. It's set to ANSI-colored text | ||
| 47 | -- if the COLORTERM variable is set. Note: this is independent of any output | ||
| 48 | -- redirection, which is actually considered a feature. | ||
| 49 | -- | ||
| 50 | -- You probably want to use less -R to enjoy viewing ANSI-colored text from | ||
| 51 | -- a pipe or a file. Add this to your ~/.bashrc: export LESS="-R" | ||
| 52 | -- | ||
| 53 | ------------------------------------------------------------------------------ | ||
| 54 | |||
| 55 | -- Cache some library functions and objects. | ||
| 56 | local jit = require("jit") | ||
| 57 | assert(jit.version_num == 20000, "LuaJIT core/library version mismatch") | ||
| 58 | local jutil = require("jit.util") | ||
| 59 | local vmdef = require("jit.vmdef") | ||
| 60 | local funcinfo, funcbc = jutil.funcinfo, jutil.funcbc | ||
| 61 | local traceinfo, traceir, tracek = jutil.traceinfo, jutil.traceir, jutil.tracek | ||
| 62 | local tracemc, traceexitstub = jutil.tracemc, jutil.traceexitstub | ||
| 63 | local tracesnap = jutil.tracesnap | ||
| 64 | local bit = require("bit") | ||
| 65 | local band, shl, shr = bit.band, bit.lshift, bit.rshift | ||
| 66 | local sub, gsub, format = string.sub, string.gsub, string.format | ||
| 67 | local byte, char, rep = string.byte, string.char, string.rep | ||
| 68 | local type, tostring = type, tostring | ||
| 69 | local stdout, stderr = io.stdout, io.stderr | ||
| 70 | |||
| 71 | -- Load other modules on-demand. | ||
| 72 | local bcline, discreate | ||
| 73 | |||
| 74 | -- Active flag, output file handle and dump mode. | ||
| 75 | local active, out, dumpmode | ||
| 76 | |||
| 77 | ------------------------------------------------------------------------------ | ||
| 78 | |||
| 79 | local symtab = {} | ||
| 80 | local nexitsym = 0 | ||
| 81 | |||
| 82 | -- Fill symbol table with trace exit addresses. | ||
| 83 | local function fillsymtab(nexit) | ||
| 84 | local t = symtab | ||
| 85 | if nexit > nexitsym then | ||
| 86 | for i=nexitsym,nexit-1 do t[traceexitstub(i)] = tostring(i) end | ||
| 87 | nexitsym = nexit | ||
| 88 | end | ||
| 89 | return t | ||
| 90 | end | ||
| 91 | |||
| 92 | local function dumpwrite(s) | ||
| 93 | out:write(s) | ||
| 94 | end | ||
| 95 | |||
| 96 | -- Disassemble machine code. | ||
| 97 | local function dump_mcode(tr) | ||
| 98 | local info = traceinfo(tr) | ||
| 99 | if not info then return end | ||
| 100 | local mcode, addr, loop = tracemc(tr) | ||
| 101 | if not mcode then return end | ||
| 102 | if not discreate then | ||
| 103 | discreate = require("jit.dis_"..jit.arch).create | ||
| 104 | end | ||
| 105 | out:write("---- TRACE ", tr, " mcode ", #mcode, "\n") | ||
| 106 | local ctx = discreate(mcode, addr, dumpwrite) | ||
| 107 | ctx.hexdump = 0 | ||
| 108 | ctx.symtab = fillsymtab(info.nexit) | ||
| 109 | if loop ~= 0 then | ||
| 110 | symtab[addr+loop] = "LOOP" | ||
| 111 | ctx:disass(0, loop) | ||
| 112 | out:write("->LOOP:\n") | ||
| 113 | ctx:disass(loop, #mcode-loop) | ||
| 114 | symtab[addr+loop] = nil | ||
| 115 | else | ||
| 116 | ctx:disass(0, #mcode) | ||
| 117 | end | ||
| 118 | end | ||
| 119 | |||
| 120 | ------------------------------------------------------------------------------ | ||
| 121 | |||
| 122 | local irtype_text = { | ||
| 123 | [0] = "nil", | ||
| 124 | "fal", | ||
| 125 | "tru", | ||
| 126 | "lud", | ||
| 127 | "str", | ||
| 128 | "ptr", | ||
| 129 | "thr", | ||
| 130 | "pro", | ||
| 131 | "fun", | ||
| 132 | "t09", | ||
| 133 | "tab", | ||
| 134 | "udt", | ||
| 135 | "num", | ||
| 136 | "int", | ||
| 137 | "i8 ", | ||
| 138 | "u8 ", | ||
| 139 | "i16", | ||
| 140 | "u16", | ||
| 141 | } | ||
| 142 | |||
| 143 | local colortype_ansi = { | ||
| 144 | [0] = "%s", | ||
| 145 | "%s", | ||
| 146 | "%s", | ||
| 147 | "%s", | ||
| 148 | "\027[32m%s\027[m", | ||
| 149 | "%s", | ||
| 150 | "\027[1m%s\027[m", | ||
| 151 | "%s", | ||
| 152 | "\027[1m%s\027[m", | ||
| 153 | "%s", | ||
| 154 | "\027[31m%s\027[m", | ||
| 155 | "\027[36m%s\027[m", | ||
| 156 | "\027[34m%s\027[m", | ||
| 157 | "\027[35m%s\027[m", | ||
| 158 | "\027[35m%s\027[m", | ||
| 159 | "\027[35m%s\027[m", | ||
| 160 | "\027[35m%s\027[m", | ||
| 161 | "\027[35m%s\027[m", | ||
| 162 | } | ||
| 163 | |||
| 164 | local function colorize_text(s, t) | ||
| 165 | return s | ||
| 166 | end | ||
| 167 | |||
| 168 | local function colorize_ansi(s, t) | ||
| 169 | return format(colortype_ansi[t], s) | ||
| 170 | end | ||
| 171 | |||
| 172 | local irtype_ansi = setmetatable({}, | ||
| 173 | { __index = function(tab, t) | ||
| 174 | local s = colorize_ansi(irtype_text[t], t); tab[t] = s; return s; end }) | ||
| 175 | |||
| 176 | local html_escape = { ["<"] = "<", [">"] = ">", ["&"] = "&", } | ||
| 177 | |||
| 178 | local function colorize_html(s, t) | ||
| 179 | s = gsub(s, "[<>&]", html_escape) | ||
| 180 | return format('<span class="irt_%s">%s</span>', irtype_text[t], s) | ||
| 181 | end | ||
| 182 | |||
| 183 | local irtype_html = setmetatable({}, | ||
| 184 | { __index = function(tab, t) | ||
| 185 | local s = colorize_html(irtype_text[t], t); tab[t] = s; return s; end }) | ||
| 186 | |||
| 187 | local header_html = [[ | ||
| 188 | <style type="text/css"> | ||
| 189 | background { background: #ffffff; color: #000000; } | ||
| 190 | pre.ljdump { | ||
| 191 | font-size: 10pt; | ||
| 192 | background: #f0f4ff; | ||
| 193 | color: #000000; | ||
| 194 | border: 1px solid #bfcfff; | ||
| 195 | padding: 0.5em; | ||
| 196 | margin-left: 2em; | ||
| 197 | margin-right: 2em; | ||
| 198 | } | ||
| 199 | span.irt_str { color: #00a000; } | ||
| 200 | span.irt_thr, span.irt_fun { color: #404040; font-weight: bold; } | ||
| 201 | span.irt_tab { color: #c00000; } | ||
| 202 | span.irt_udt { color: #00c0c0; } | ||
| 203 | span.irt_num { color: #0000c0; } | ||
| 204 | span.irt_int { color: #c000c0; } | ||
| 205 | </style> | ||
| 206 | ]] | ||
| 207 | |||
| 208 | local colorize, irtype | ||
| 209 | |||
| 210 | -- Lookup table to convert some literals into names. | ||
| 211 | local litname = { | ||
| 212 | ["SLOAD "] = { [0] = "", "I", "R", "RI", "P", "PI", "PR", "PRI", }, | ||
| 213 | ["XLOAD "] = { [0] = "", "unaligned", }, | ||
| 214 | ["TOINT "] = { [0] = "check", "index", "", }, | ||
| 215 | ["FLOAD "] = vmdef.irfield, | ||
| 216 | ["FREF "] = vmdef.irfield, | ||
| 217 | ["FPMATH"] = vmdef.irfpm, | ||
| 218 | } | ||
| 219 | |||
| 220 | local function ctlsub(c) | ||
| 221 | if c == "\n" then return "\\n" | ||
| 222 | elseif c == "\r" then return "\\r" | ||
| 223 | elseif c == "\t" then return "\\t" | ||
| 224 | elseif c == "\r" then return "\\r" | ||
| 225 | else return format("\\%03d", byte(c)) | ||
| 226 | end | ||
| 227 | end | ||
| 228 | |||
| 229 | local function formatk(tr, idx) | ||
| 230 | local k, t, slot = tracek(tr, idx) | ||
| 231 | local tn = type(k) | ||
| 232 | local s | ||
| 233 | if tn == "number" then | ||
| 234 | if k == 2^52+2^51 then | ||
| 235 | s = "bias" | ||
| 236 | else | ||
| 237 | s = format("%+.14g", k) | ||
| 238 | end | ||
| 239 | elseif tn == "string" then | ||
| 240 | s = format(#k > 20 and '"%.20s"~' or '"%s"', gsub(k, "%c", ctlsub)) | ||
| 241 | elseif tn == "function" then | ||
| 242 | local fi = funcinfo(k) | ||
| 243 | if fi.ffid then | ||
| 244 | s = vmdef.ffnames[fi.ffid] | ||
| 245 | else | ||
| 246 | s = fi.loc | ||
| 247 | end | ||
| 248 | elseif tn == "table" then | ||
| 249 | s = format("{%p}", k) | ||
| 250 | elseif tn == "userdata" then | ||
| 251 | if t == 11 then | ||
| 252 | s = format("userdata:%p", k) | ||
| 253 | else | ||
| 254 | s = format("[%p]", k) | ||
| 255 | if s == "[0x00000000]" then s = "NULL" end | ||
| 256 | end | ||
| 257 | else | ||
| 258 | s = tostring(k) -- For primitives. | ||
| 259 | end | ||
| 260 | s = colorize(format("%-4s", s), t) | ||
| 261 | if slot then | ||
| 262 | s = format("%s @%d", s, slot) | ||
| 263 | end | ||
| 264 | return s | ||
| 265 | end | ||
| 266 | |||
| 267 | local function printsnap(tr, snap) | ||
| 268 | for i=1,#snap do | ||
| 269 | local ref = snap[i] | ||
| 270 | if not ref then | ||
| 271 | out:write("---- ") | ||
| 272 | elseif ref < 0 then | ||
| 273 | out:write(formatk(tr, ref), " ") | ||
| 274 | else | ||
| 275 | local m, ot, op1, op2 = traceir(tr, ref) | ||
| 276 | local t = band(ot, 15) | ||
| 277 | local sep = " " | ||
| 278 | if t == 8 then | ||
| 279 | local oidx = 6*shr(ot, 8) | ||
| 280 | local op = sub(vmdef.irnames, oidx+1, oidx+6) | ||
| 281 | if op == "FRAME " then | ||
| 282 | sep = "|" | ||
| 283 | end | ||
| 284 | end | ||
| 285 | out:write(colorize(format("%04d", ref), t), sep) | ||
| 286 | end | ||
| 287 | end | ||
| 288 | out:write("]\n") | ||
| 289 | end | ||
| 290 | |||
| 291 | -- Dump snapshots (not interleaved with IR). | ||
| 292 | local function dump_snap(tr) | ||
| 293 | out:write("---- TRACE ", tr, " snapshots\n") | ||
| 294 | for i=0,1000000000 do | ||
| 295 | local snap = tracesnap(tr, i) | ||
| 296 | if not snap then break end | ||
| 297 | out:write(format("#%-3d %04d [ ", i, snap[0])) | ||
| 298 | printsnap(tr, snap) | ||
| 299 | end | ||
| 300 | end | ||
| 301 | |||
| 302 | -- NYI: should really get the register map from the disassembler. | ||
| 303 | local reg_map = { | ||
| 304 | [0] = "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi", | ||
| 305 | "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7", | ||
| 306 | } | ||
| 307 | |||
| 308 | -- Return a register name or stack slot for a rid/sp location. | ||
| 309 | local function ridsp_name(ridsp) | ||
| 310 | local rid = band(ridsp, 0xff) | ||
| 311 | if ridsp > 255 then return format("[%x]", shr(ridsp, 8)*4) end | ||
| 312 | if rid < 128 then return reg_map[rid] end | ||
| 313 | return "" | ||
| 314 | end | ||
| 315 | |||
| 316 | -- Dump IR and interleaved snapshots. | ||
| 317 | local function dump_ir(tr, dumpsnap, dumpreg) | ||
| 318 | local info = traceinfo(tr) | ||
| 319 | if not info then return end | ||
| 320 | local nins = info.nins | ||
| 321 | out:write("---- TRACE ", tr, " IR\n") | ||
| 322 | local irnames = vmdef.irnames | ||
| 323 | local snapref = 65536 | ||
| 324 | local snap, snapno | ||
| 325 | if dumpsnap then | ||
| 326 | snap = tracesnap(tr, 0) | ||
| 327 | snapref = snap[0] | ||
| 328 | snapno = 0 | ||
| 329 | end | ||
| 330 | for ins=1,nins do | ||
| 331 | if ins >= snapref then | ||
| 332 | if dumpreg then | ||
| 333 | out:write(format(".... SNAP #%-3d [ ", snapno)) | ||
| 334 | else | ||
| 335 | out:write(format(".... SNAP #%-3d [ ", snapno)) | ||
| 336 | end | ||
| 337 | printsnap(tr, snap) | ||
| 338 | snapno = snapno + 1 | ||
| 339 | snap = tracesnap(tr, snapno) | ||
| 340 | snapref = snap and snap[0] or 65536 | ||
| 341 | end | ||
| 342 | local m, ot, op1, op2, ridsp = traceir(tr, ins) | ||
| 343 | local oidx, t = 6*shr(ot, 8), band(ot, 31) | ||
| 344 | local op = sub(irnames, oidx+1, oidx+6) | ||
| 345 | if op == "LOOP " then | ||
| 346 | if dumpreg then | ||
| 347 | out:write(format("%04d ------------ LOOP ------------\n", ins)) | ||
| 348 | else | ||
| 349 | out:write(format("%04d ------ LOOP ------------\n", ins)) | ||
| 350 | end | ||
| 351 | elseif op ~= "NOP " and (dumpreg or op ~= "RENAME") then | ||
| 352 | if dumpreg then | ||
| 353 | out:write(format("%04d %-5s ", ins, ridsp_name(ridsp))) | ||
| 354 | else | ||
| 355 | out:write(format("%04d ", ins)) | ||
| 356 | end | ||
| 357 | out:write(format("%s%s %s %s ", | ||
| 358 | band(ot, 64) == 0 and " " or ">", | ||
| 359 | band(ot, 128) == 0 and " " or "+", | ||
| 360 | irtype[t], op)) | ||
| 361 | local m1 = band(m, 3) | ||
| 362 | if m1 ~= 3 then -- op1 != IRMnone | ||
| 363 | if op1 < 0 then | ||
| 364 | out:write(formatk(tr, op1)) | ||
| 365 | else | ||
| 366 | out:write(format(m1 == 0 and "%04d" or "#%-3d", op1)) | ||
| 367 | end | ||
| 368 | local m2 = band(m, 3*4) | ||
| 369 | if m2 ~= 3*4 then -- op2 != IRMnone | ||
| 370 | if m2 == 1*4 then -- op2 == IRMlit | ||
| 371 | local litn = litname[op] | ||
| 372 | if litn and litn[op2] then | ||
| 373 | out:write(" ", litn[op2]) | ||
| 374 | else | ||
| 375 | out:write(format(" #%-3d", op2)) | ||
| 376 | end | ||
| 377 | elseif op2 < 0 then | ||
| 378 | out:write(" ", formatk(tr, op2)) | ||
| 379 | else | ||
| 380 | out:write(format(" %04d", op2)) | ||
| 381 | end | ||
| 382 | end | ||
| 383 | end | ||
| 384 | out:write("\n") | ||
| 385 | end | ||
| 386 | end | ||
| 387 | if snap then | ||
| 388 | if dumpreg then | ||
| 389 | out:write(format(".... SNAP #%-3d [ ", snapno)) | ||
| 390 | else | ||
| 391 | out:write(format(".... SNAP #%-3d [ ", snapno)) | ||
| 392 | end | ||
| 393 | printsnap(tr, snap) | ||
| 394 | end | ||
| 395 | end | ||
| 396 | |||
| 397 | ------------------------------------------------------------------------------ | ||
| 398 | |||
| 399 | local recprefix = "" | ||
| 400 | local recdepth = 0 | ||
| 401 | |||
| 402 | -- Format trace error message. | ||
| 403 | local function fmterr(err, info) | ||
| 404 | if type(err) == "number" then | ||
| 405 | if type(info) == "function" then | ||
| 406 | local fi = funcinfo(info) | ||
| 407 | if fi.ffid then | ||
| 408 | info = vmdef.ffnames[fi.ffid] | ||
| 409 | else | ||
| 410 | info = fi.loc | ||
| 411 | end | ||
| 412 | end | ||
| 413 | err = format(vmdef.traceerr[err], info) | ||
| 414 | end | ||
| 415 | return err | ||
| 416 | end | ||
| 417 | |||
| 418 | -- Dump trace states. | ||
| 419 | local function dump_trace(what, tr, func, pc, otr, oex) | ||
| 420 | if what == "stop" or (what == "abort" and dumpmode.a) then | ||
| 421 | if dumpmode.i then dump_ir(tr, dumpmode.s, dumpmode.r and what == "stop") | ||
| 422 | elseif dumpmode.s then dump_snap(tr) end | ||
| 423 | if dumpmode.m then dump_mcode(tr) end | ||
| 424 | end | ||
| 425 | if what == "start" then | ||
| 426 | if dumpmode.H then out:write('<pre class="ljdump">\n') end | ||
| 427 | out:write("---- TRACE ", tr, " ", what) | ||
| 428 | if otr then out:write(" ", otr, "/", oex) end | ||
| 429 | local fi = funcinfo(func, pc) | ||
| 430 | out:write(" ", fi.loc, "\n") | ||
| 431 | recprefix = "" | ||
| 432 | reclevel = 0 | ||
| 433 | elseif what == "stop" or what == "abort" then | ||
| 434 | out:write("---- TRACE ", tr, " ", what) | ||
| 435 | recprefix = nil | ||
| 436 | if what == "abort" then | ||
| 437 | local fi = funcinfo(func, pc) | ||
| 438 | out:write(" ", fi.loc, " -- ", fmterr(otr, oex), "\n") | ||
| 439 | else | ||
| 440 | local link = traceinfo(tr).link | ||
| 441 | if link == tr then | ||
| 442 | link = "loop" | ||
| 443 | elseif link == 0 then | ||
| 444 | link = "interpreter" | ||
| 445 | end | ||
| 446 | out:write(" -> ", link, "\n") | ||
| 447 | end | ||
| 448 | if dumpmode.H then out:write("</pre>\n\n") else out:write("\n") end | ||
| 449 | else | ||
| 450 | out:write("---- TRACE ", what, "\n\n") | ||
| 451 | end | ||
| 452 | out:flush() | ||
| 453 | end | ||
| 454 | |||
| 455 | -- Dump recorded bytecode. | ||
| 456 | local function dump_record(tr, func, pc, depth, callee) | ||
| 457 | if depth ~= recdepth then | ||
| 458 | recdepth = depth | ||
| 459 | recprefix = rep(" .", depth) | ||
| 460 | end | ||
| 461 | local line = bcline(func, pc, recprefix) | ||
| 462 | if dumpmode.H then line = gsub(line, "[<>&]", html_escape) end | ||
| 463 | if type(callee) == "function" then | ||
| 464 | local fi = funcinfo(callee) | ||
| 465 | if fi.ffid then | ||
| 466 | out:write(sub(line, 1, -2), " ; ", vmdef.ffnames[fi.ffid], "\n") | ||
| 467 | else | ||
| 468 | out:write(sub(line, 1, -2), " ; ", fi.loc, "\n") | ||
| 469 | end | ||
| 470 | else | ||
| 471 | out:write(line) | ||
| 472 | end | ||
| 473 | if band(funcbc(func, pc), 0xff) < 16 then -- Write JMP for cond. ORDER BC | ||
| 474 | out:write(bcline(func, pc+1, recprefix)) | ||
| 475 | end | ||
| 476 | end | ||
| 477 | |||
| 478 | ------------------------------------------------------------------------------ | ||
| 479 | |||
| 480 | -- Dump taken trace exits. | ||
| 481 | local function dump_texit(tr, ex, ngpr, nfpr, ...) | ||
| 482 | out:write("---- TRACE ", tr, " exit ", ex, "\n") | ||
| 483 | if dumpmode.X then | ||
| 484 | local regs = {...} | ||
| 485 | for i=1,ngpr do | ||
| 486 | out:write(format(" %08x", regs[i])) | ||
| 487 | if i % 8 == 0 then out:write("\n") end | ||
| 488 | end | ||
| 489 | for i=1,nfpr do | ||
| 490 | out:write(format(" %+17.14g", regs[ngpr+i])) | ||
| 491 | if i % 4 == 0 then out:write("\n") end | ||
| 492 | end | ||
| 493 | end | ||
| 494 | end | ||
| 495 | |||
| 496 | ------------------------------------------------------------------------------ | ||
| 497 | |||
| 498 | -- Detach dump handlers. | ||
| 499 | local function dumpoff() | ||
| 500 | if active then | ||
| 501 | active = false | ||
| 502 | jit.attach(dump_texit) | ||
| 503 | jit.attach(dump_record) | ||
| 504 | jit.attach(dump_trace) | ||
| 505 | if out and out ~= stdout and out ~= stderr then out:close() end | ||
| 506 | out = nil | ||
| 507 | end | ||
| 508 | end | ||
| 509 | |||
| 510 | -- Open the output file and attach dump handlers. | ||
| 511 | local function dumpon(opt, outfile) | ||
| 512 | if active then dumpoff() end | ||
| 513 | |||
| 514 | local colormode = os.getenv("COLORTERM") and "A" or "T" | ||
| 515 | if opt then | ||
| 516 | opt = gsub(opt, "[TAH]", function(mode) colormode = mode; return ""; end) | ||
| 517 | end | ||
| 518 | |||
| 519 | local m = { t=true, b=true, i=true, m=true, } | ||
| 520 | if opt and opt ~= "" then | ||
| 521 | local o = sub(opt, 1, 1) | ||
| 522 | if o ~= "+" and o ~= "-" then m = {} end | ||
| 523 | for i=1,#opt do m[sub(opt, i, i)] = (o ~= "-") end | ||
| 524 | end | ||
| 525 | dumpmode = m | ||
| 526 | |||
| 527 | if m.t or m.b or m.i or m.s or m.m then | ||
| 528 | jit.attach(dump_trace, "trace") | ||
| 529 | end | ||
| 530 | if m.b then | ||
| 531 | jit.attach(dump_record, "record") | ||
| 532 | if not bcline then bcline = require("jit.bc").line end | ||
| 533 | end | ||
| 534 | if m.x or m.X then | ||
| 535 | jit.attach(dump_texit, "texit") | ||
| 536 | end | ||
| 537 | |||
| 538 | if not outfile then outfile = os.getenv("LUAJIT_DUMPFILE") end | ||
| 539 | if outfile then | ||
| 540 | out = outfile == "-" and stdout or assert(io.open(outfile, "w")) | ||
| 541 | else | ||
| 542 | out = stdout | ||
| 543 | end | ||
| 544 | |||
| 545 | m[colormode] = true | ||
| 546 | if colormode == "A" then | ||
| 547 | colorize = colorize_ansi | ||
| 548 | irtype = irtype_ansi | ||
| 549 | elseif colormode == "H" then | ||
| 550 | colorize = colorize_html | ||
| 551 | irtype = irtype_html | ||
| 552 | out:write(header_html) | ||
| 553 | else | ||
| 554 | colorize = colorize_text | ||
| 555 | irtype = irtype_text | ||
| 556 | end | ||
| 557 | |||
| 558 | active = true | ||
| 559 | end | ||
| 560 | |||
| 561 | -- Public module functions. | ||
| 562 | module(...) | ||
| 563 | |||
| 564 | on = dumpon | ||
| 565 | off = dumpoff | ||
| 566 | start = dumpon -- For -j command line option. | ||
| 567 | |||
diff --git a/lib/v.lua b/lib/v.lua new file mode 100644 index 00000000..39fb8ed5 --- /dev/null +++ b/lib/v.lua | |||
| @@ -0,0 +1,156 @@ | |||
| 1 | ---------------------------------------------------------------------------- | ||
| 2 | -- Verbose mode of the LuaJIT compiler. | ||
| 3 | -- | ||
| 4 | -- Copyright (C) 2005-2009 Mike Pall. All rights reserved. | ||
| 5 | -- Released under the MIT/X license. See Copyright Notice in luajit.h | ||
| 6 | ---------------------------------------------------------------------------- | ||
| 7 | -- | ||
| 8 | -- This module shows verbose information about the progress of the | ||
| 9 | -- JIT compiler. It prints one line for each generated trace. This module | ||
| 10 | -- is useful to see which code has been compiled or where the compiler | ||
| 11 | -- punts and falls back to the interpreter. | ||
| 12 | -- | ||
| 13 | -- Example usage: | ||
| 14 | -- | ||
| 15 | -- luajit -jv -e "for i=1,1000 do for j=1,1000 do end end" | ||
| 16 | -- luajit -jv=myapp.out myapp.lua | ||
| 17 | -- | ||
| 18 | -- Default output is to stderr. To redirect the output to a file, pass a | ||
| 19 | -- filename as an argument (use '-' for stdout) or set the environment | ||
| 20 | -- variable LUAJIT_VERBOSEFILE. The file is overwritten every time the | ||
| 21 | -- module is started. | ||
| 22 | -- | ||
| 23 | -- The output from the first example should look like this: | ||
| 24 | -- | ||
| 25 | -- [TRACE 1 (command line):1] | ||
| 26 | -- [TRACE 2 (1/3) (command line):1 -> 1] | ||
| 27 | -- | ||
| 28 | -- The first number in each line is the internal trace number. Next are | ||
| 29 | -- the file name ('(command line)') and the line number (':1') where the | ||
| 30 | -- trace has started. Side traces also show the parent trace number and | ||
| 31 | -- the exit number where they are attached to in parentheses ('(1/3)'). | ||
| 32 | -- An arrow at the end shows where the trace links to ('-> 1'), unless | ||
| 33 | -- it loops to itself. | ||
| 34 | -- | ||
| 35 | -- In this case the inner loop gets hot and is traced first, generating | ||
| 36 | -- a root trace. Then the last exit from the 1st trace gets hot, too, | ||
| 37 | -- and triggers generation of the 2nd trace. The side trace follows the | ||
| 38 | -- path along the outer loop and *around* the inner loop, back to its | ||
| 39 | -- start, and then links to the 1st trace. Yes, this may seem unusual, | ||
| 40 | -- if you know how traditional compilers work. Trace compilers are full | ||
| 41 | -- of surprises like this -- have fun! :-) | ||
| 42 | -- | ||
| 43 | -- Aborted traces are shown like this: | ||
| 44 | -- | ||
| 45 | -- [TRACE --- foo.lua:44 -- leaving loop in root trace at foo:lua:50] | ||
| 46 | -- | ||
| 47 | -- Don't worry -- trace aborts are quite common, even in programs which | ||
| 48 | -- can be fully compiled. The compiler may retry several times until it | ||
| 49 | -- finds a suitable trace. | ||
| 50 | -- | ||
| 51 | -- Of course this doesn't work with features that are not-yet-implemented | ||
| 52 | -- (NYI error messages). The VM simply falls back to the interpreter. This | ||
| 53 | -- may not matter at all if the particular trace is not very high up in | ||
| 54 | -- the CPU usage profile. Oh, and the interpreter is quite fast, too. | ||
| 55 | -- | ||
| 56 | -- Also check out the -jdump module, which prints all the gory details. | ||
| 57 | -- | ||
| 58 | ------------------------------------------------------------------------------ | ||
| 59 | |||
| 60 | -- Cache some library functions and objects. | ||
| 61 | local jit = require("jit") | ||
| 62 | assert(jit.version_num == 20000, "LuaJIT core/library version mismatch") | ||
| 63 | local jutil = require("jit.util") | ||
| 64 | local vmdef = require("jit.vmdef") | ||
| 65 | local funcinfo, traceinfo = jutil.funcinfo, jutil.traceinfo | ||
| 66 | local type, format = type, string.format | ||
| 67 | local stdout, stderr = io.stdout, io.stderr | ||
| 68 | |||
| 69 | -- Active flag and output file handle. | ||
| 70 | local active, out | ||
| 71 | |||
| 72 | ------------------------------------------------------------------------------ | ||
| 73 | |||
| 74 | local startloc, startex | ||
| 75 | |||
| 76 | -- Format trace error message. | ||
| 77 | local function fmterr(err, info) | ||
| 78 | if type(err) == "number" then | ||
| 79 | if type(info) == "function" then | ||
| 80 | local fi = funcinfo(info) | ||
| 81 | if fi.ffid then | ||
| 82 | info = vmdef.ffnames[fi.ffid] | ||
| 83 | else | ||
| 84 | info = fi.loc | ||
| 85 | end | ||
| 86 | end | ||
| 87 | err = format(vmdef.traceerr[err], info) | ||
| 88 | end | ||
| 89 | return err | ||
| 90 | end | ||
| 91 | |||
| 92 | -- Dump trace states. | ||
| 93 | local function dump_trace(what, tr, func, pc, otr, oex) | ||
| 94 | if what == "start" then | ||
| 95 | startloc = funcinfo(func, pc).loc | ||
| 96 | startex = otr and "("..otr.."/"..oex..") " or "" | ||
| 97 | else | ||
| 98 | if what == "abort" then | ||
| 99 | local loc = funcinfo(func, pc).loc | ||
| 100 | if loc ~= startloc then | ||
| 101 | out:write(format("[TRACE --- %s%s -- %s at %s]\n", | ||
| 102 | startex, startloc, fmterr(otr, oex), loc)) | ||
| 103 | else | ||
| 104 | out:write(format("[TRACE --- %s%s -- %s]\n", | ||
| 105 | startex, startloc, fmterr(otr, oex))) | ||
| 106 | end | ||
| 107 | elseif what == "stop" then | ||
| 108 | local link = traceinfo(tr).link | ||
| 109 | if link == 0 then | ||
| 110 | out:write(format("[TRACE %3s %s%s -- fallback to interpreter]\n", | ||
| 111 | tr, startex, startloc)) | ||
| 112 | elseif link == tr then | ||
| 113 | out:write(format("[TRACE %3s %s%s]\n", tr, startex, startloc)) | ||
| 114 | else | ||
| 115 | out:write(format("[TRACE %3s %s%s -> %d]\n", | ||
| 116 | tr, startex, startloc, link)) | ||
| 117 | end | ||
| 118 | else | ||
| 119 | out:write(format("[TRACE %s]\n", what)) | ||
| 120 | end | ||
| 121 | out:flush() | ||
| 122 | end | ||
| 123 | end | ||
| 124 | |||
| 125 | ------------------------------------------------------------------------------ | ||
| 126 | |||
| 127 | -- Detach dump handlers. | ||
| 128 | local function dumpoff() | ||
| 129 | if active then | ||
| 130 | active = false | ||
| 131 | jit.attach(dump_trace) | ||
| 132 | if out and out ~= stdout and out ~= stderr then out:close() end | ||
| 133 | out = nil | ||
| 134 | end | ||
| 135 | end | ||
| 136 | |||
| 137 | -- Open the output file and attach dump handlers. | ||
| 138 | local function dumpon(outfile) | ||
| 139 | if active then dumpoff() end | ||
| 140 | if not outfile then outfile = os.getenv("LUAJIT_VERBOSEFILE") end | ||
| 141 | if outfile then | ||
| 142 | out = outfile == "-" and stdout or assert(io.open(outfile, "w")) | ||
| 143 | else | ||
| 144 | out = stderr | ||
| 145 | end | ||
| 146 | jit.attach(dump_trace, "trace") | ||
| 147 | active = true | ||
| 148 | end | ||
| 149 | |||
| 150 | -- Public module functions. | ||
| 151 | module(...) | ||
| 152 | |||
| 153 | on = dumpon | ||
| 154 | off = dumpoff | ||
| 155 | start = dumpon -- For -j command line option. | ||
| 156 | |||
