bc.lua (6448B)
1 ---------------------------------------------------------------------------- 2 -- LuaJIT bytecode listing module. 3 -- 4 -- Copyright (C) 2005-2016 Mike Pall. All rights reserved. 5 -- Released under the MIT 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 with 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 == 20100, "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 else return format("\\%03d", byte(c)) 62 end 63 end 64 65 -- Return one bytecode line. 66 local function bcline(func, pc, prefix) 67 local ins, m = funcbc(func, pc) 68 if not ins then return end 69 local ma, mb, mc = band(m, 7), band(m, 15*8), band(m, 15*128) 70 local a = band(shr(ins, 8), 0xff) 71 local oidx = 6*band(ins, 0xff) 72 local op = sub(bcnames, oidx+1, oidx+6) 73 local s = format("%04d %s %-6s %3s ", 74 pc, prefix or " ", op, ma == 0 and "" or a) 75 local d = shr(ins, 16) 76 if mc == 13*128 then -- BCMjump 77 return format("%s=> %04d\n", s, pc+d-0x7fff) 78 end 79 if mb ~= 0 then 80 d = band(d, 0xff) 81 elseif mc == 0 then 82 return s.."\n" 83 end 84 local kc 85 if mc == 10*128 then -- BCMstr 86 kc = funck(func, -d-1) 87 kc = format(#kc > 40 and '"%.40s"~' or '"%s"', gsub(kc, "%c", ctlsub)) 88 elseif mc == 9*128 then -- BCMnum 89 kc = funck(func, d) 90 if op == "TSETM " then kc = kc - 2^52 end 91 elseif mc == 12*128 then -- BCMfunc 92 local fi = funcinfo(funck(func, -d-1)) 93 if fi.ffid then 94 kc = vmdef.ffnames[fi.ffid] 95 else 96 kc = fi.loc 97 end 98 elseif mc == 5*128 then -- BCMuv 99 kc = funcuvname(func, d) 100 end 101 if ma == 5 then -- BCMuv 102 local ka = funcuvname(func, a) 103 if kc then kc = ka.." ; "..kc else kc = ka end 104 end 105 if mb ~= 0 then 106 local b = shr(ins, 24) 107 if kc then return format("%s%3d %3d ; %s\n", s, b, d, kc) end 108 return format("%s%3d %3d\n", s, b, d) 109 end 110 if kc then return format("%s%3d ; %s\n", s, d, kc) end 111 if mc == 7*128 and d > 32767 then d = d - 65536 end -- BCMlits 112 return format("%s%3d\n", s, d) 113 end 114 115 -- Collect branch targets of a function. 116 local function bctargets(func) 117 local target = {} 118 for pc=1,1000000000 do 119 local ins, m = funcbc(func, pc) 120 if not ins then break end 121 if band(m, 15*128) == 13*128 then target[pc+shr(ins, 16)-0x7fff] = true end 122 end 123 return target 124 end 125 126 -- Dump bytecode instructions of a function. 127 local function bcdump(func, out, all, prefix) 128 prefix = prefix or "" 129 if not out then out = stdout end 130 local fi = funcinfo(func) 131 if all and fi.children then 132 for n=-1,-1000000000,-1 do 133 local k = funck(func, n) 134 if not k then break end 135 if type(k) == "proto" then bcdump(k, out, true) end 136 end 137 end 138 out:write(format(prefix.."+- BYTECODE -- %s-%d\n", fi.loc, fi.lastlinedefined)) 139 for i=1,(#fi.uvinit) do 140 local uvv = band(fi.uvinit[i],0xfff) -- ORDER PROTO_UV_* in lj_obj.h 141 local loc = band(fi.uvinit[i],0x8000)~=0 and " PARENT" or "" 142 local imu = band(fi.uvinit[i],0x4000)~=0 and " IMMUTABLE" or "" 143 local env = band(fi.uvinit[i],0x2000)~=0 and " ENV" or "" 144 local clo = band(fi.uvinit[i],0x1000)~=0 and " CLOSURE" or "" 145 out:write((prefix.."| UV@%d = %d%s%s%s%s %04x\n"):format(i-1,uvv,loc,imu,env,clo,fi.uvinit[i])) 146 if clo ~= "" then 147 local k = funck(func, -band(fi.uvinit[i],0xfff)-1) 148 out:write(prefix .. "| |\n") 149 if k then 150 bcdump(k, out, nil, prefix .. "| ") 151 else 152 print ("cant load k",-band(fi.uvinit[i],0xfff)-1) 153 end 154 end 155 end 156 out:write(prefix.."+-----------\n") 157 158 local target = bctargets(func) 159 for pc=1,1000000000 do 160 local s = bcline(func, pc, target[pc] and "=>") 161 if not s then break end 162 out:write(prefix .. s) 163 end 164 out:write(prefix.."\n") 165 out:flush() 166 end 167 168 ------------------------------------------------------------------------------ 169 170 -- Active flag and output file handle. 171 local active, out 172 173 -- List handler. 174 local function h_list(func) 175 return bcdump(func, out) 176 end 177 178 -- Detach list handler. 179 local function bclistoff() 180 if active then 181 active = false 182 jit.attach(h_list) 183 if out and out ~= stdout and out ~= stderr then out:close() end 184 out = nil 185 end 186 end 187 188 -- Open the output file and attach list handler. 189 local function bcliston(outfile) 190 if active then bclistoff() end 191 if not outfile then outfile = os.getenv("LUAJIT_LISTFILE") end 192 if outfile then 193 out = outfile == "-" and stdout or assert(io.open(outfile, "w")) 194 else 195 out = stderr 196 end 197 jit.attach(h_list, "bc") 198 active = true 199 end 200 201 -- Public module functions. 202 return { 203 line = bcline, 204 dump = bcdump, 205 targets = bctargets, 206 on = bcliston, 207 off = bclistoff, 208 start = bcliston -- For -j command line option. 209 } 210