FFI: Compile calls to stdcall, fastcall and vararg functions.
[luajit-2.0.git] / lib / dump.lua
blob0f0e9058e24e129a9f2c58b7e305adb749094292
1 ----------------------------------------------------------------------------
2 -- LuaJIT compiler dump module.
3 --
4 -- Copyright (C) 2005-2011 Mike Pall. All rights reserved.
5 -- Released under the MIT 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.
11 -- Example usage:
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
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.
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.
29 -- The following dump features are available (* marks the default):
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.
40 -- The output format can be set with the following characters:
42 -- T Plain text output.
43 -- A ANSI-colored text output
44 -- H Colorized HTML + CSS output.
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.
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"
53 ------------------------------------------------------------------------------
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, tracesnap = jutil.tracemc, jutil.tracesnap
63 local traceexitstub, ircalladdr = jutil.traceexitstub, jutil.ircalladdr
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
71 -- Load other modules on-demand.
72 local bcline, disass
74 -- Active flag, output file handle and dump mode.
75 local active, out, dumpmode
77 ------------------------------------------------------------------------------
79 local symtabmt = { __index = false }
80 local symtab = {}
81 local nexitsym = 0
83 -- Fill nested symbol table with per-trace exit stub addresses.
84 local function fillsymtab_tr(tr, nexit)
85 local t = {}
86 symtabmt.__index = t
87 for i=0,nexit-1 do
88 local addr = traceexitstub(tr, i)
89 t[addr] = tostring(i)
90 end
91 local addr = traceexitstub(tr, nexit)
92 if addr then t[addr] = "stack_check" end
93 end
95 -- Fill symbol table with trace exit stub addresses.
96 local function fillsymtab(tr, nexit)
97 local t = symtab
98 if nexitsym == 0 then
99 local ircall = vmdef.ircall
100 for i=0,#ircall do
101 local addr = ircalladdr(i)
102 if addr ~= 0 then t[addr] = ircall[i] end
105 if nexitsym == 1000000 then -- Per-trace exit stubs.
106 fillsymtab_tr(tr, nexit)
107 elseif nexit > nexitsym then -- Shared exit stubs.
108 for i=nexitsym,nexit-1 do
109 local addr = traceexitstub(i)
110 if addr == nil then -- Fall back to per-trace exit stubs.
111 fillsymtab_tr(tr, nexit)
112 setmetatable(symtab, symtabmt)
113 nexit = 1000000
114 break
116 t[addr] = tostring(i)
118 nexitsym = nexit
120 return t
123 local function dumpwrite(s)
124 out:write(s)
127 -- Disassemble machine code.
128 local function dump_mcode(tr)
129 local info = traceinfo(tr)
130 if not info then return end
131 local mcode, addr, loop = tracemc(tr)
132 if not mcode then return end
133 if not disass then disass = require("jit.dis_"..jit.arch) end
134 out:write("---- TRACE ", tr, " mcode ", #mcode, "\n")
135 local ctx = disass.create(mcode, addr, dumpwrite)
136 ctx.hexdump = 0
137 ctx.symtab = fillsymtab(tr, info.nexit)
138 if loop ~= 0 then
139 symtab[addr+loop] = "LOOP"
140 ctx:disass(0, loop)
141 out:write("->LOOP:\n")
142 ctx:disass(loop, #mcode-loop)
143 symtab[addr+loop] = nil
144 else
145 ctx:disass(0, #mcode)
149 ------------------------------------------------------------------------------
151 local irtype_text = {
152 [0] = "nil",
153 "fal",
154 "tru",
155 "lud",
156 "str",
157 "p32",
158 "thr",
159 "pro",
160 "fun",
161 "p64",
162 "cdt",
163 "tab",
164 "udt",
165 "flt",
166 "num",
167 "i8 ",
168 "u8 ",
169 "i16",
170 "u16",
171 "int",
172 "u32",
173 "i64",
174 "u64",
175 "sfp",
178 local colortype_ansi = {
179 [0] = "%s",
180 "%s",
181 "%s",
182 "\027[36m%s\027[m",
183 "\027[32m%s\027[m",
184 "%s",
185 "\027[1m%s\027[m",
186 "%s",
187 "\027[1m%s\027[m",
188 "%s",
189 "\027[33m%s\027[m",
190 "\027[31m%s\027[m",
191 "\027[36m%s\027[m",
192 "\027[34m%s\027[m",
193 "\027[34m%s\027[m",
194 "\027[35m%s\027[m",
195 "\027[35m%s\027[m",
196 "\027[35m%s\027[m",
197 "\027[35m%s\027[m",
198 "\027[35m%s\027[m",
199 "\027[35m%s\027[m",
200 "\027[35m%s\027[m",
201 "\027[35m%s\027[m",
202 "\027[35m%s\027[m",
205 local function colorize_text(s, t)
206 return s
209 local function colorize_ansi(s, t)
210 return format(colortype_ansi[t], s)
213 local irtype_ansi = setmetatable({},
214 { __index = function(tab, t)
215 local s = colorize_ansi(irtype_text[t], t); tab[t] = s; return s; end })
217 local html_escape = { ["<"] = "&lt;", [">"] = "&gt;", ["&"] = "&amp;", }
219 local function colorize_html(s, t)
220 s = gsub(s, "[<>&]", html_escape)
221 return format('<span class="irt_%s">%s</span>', irtype_text[t], s)
224 local irtype_html = setmetatable({},
225 { __index = function(tab, t)
226 local s = colorize_html(irtype_text[t], t); tab[t] = s; return s; end })
228 local header_html = [[
229 <style type="text/css">
230 background { background: #ffffff; color: #000000; }
231 pre.ljdump {
232 font-size: 10pt;
233 background: #f0f4ff;
234 color: #000000;
235 border: 1px solid #bfcfff;
236 padding: 0.5em;
237 margin-left: 2em;
238 margin-right: 2em;
240 span.irt_str { color: #00a000; }
241 span.irt_thr, span.irt_fun { color: #404040; font-weight: bold; }
242 span.irt_tab { color: #c00000; }
243 span.irt_udt, span.irt_lud { color: #00c0c0; }
244 span.irt_num { color: #4040c0; }
245 span.irt_int, span.irt_i8, span.irt_u8, span.irt_i16, span.irt_u16 { color: #b040b0; }
246 </style>
249 local colorize, irtype
251 -- Lookup tables to convert some literals into names.
252 local litname = {
253 ["SLOAD "] = setmetatable({}, { __index = function(t, mode)
254 local s = ""
255 if band(mode, 1) ~= 0 then s = s.."P" end
256 if band(mode, 2) ~= 0 then s = s.."F" end
257 if band(mode, 4) ~= 0 then s = s.."T" end
258 if band(mode, 8) ~= 0 then s = s.."C" end
259 if band(mode, 16) ~= 0 then s = s.."R" end
260 if band(mode, 32) ~= 0 then s = s.."I" end
261 t[mode] = s
262 return s
263 end}),
264 ["XLOAD "] = { [0] = "", "R", "V", "RV", "U", "RU", "VU", "RVU", },
265 ["CONV "] = setmetatable({}, { __index = function(t, mode)
266 local s = irtype[band(mode, 31)]
267 s = irtype[band(shr(mode, 5), 31)].."."..s
268 if band(mode, 0x400) ~= 0 then s = s.." trunc"
269 elseif band(mode, 0x800) ~= 0 then s = s.." sext" end
270 local c = shr(mode, 14)
271 if c == 2 then s = s.." index" elseif c == 3 then s = s.." check" end
272 t[mode] = s
273 return s
274 end}),
275 ["FLOAD "] = vmdef.irfield,
276 ["FREF "] = vmdef.irfield,
277 ["FPMATH"] = vmdef.irfpm,
280 local function ctlsub(c)
281 if c == "\n" then return "\\n"
282 elseif c == "\r" then return "\\r"
283 elseif c == "\t" then return "\\t"
284 elseif c == "\r" then return "\\r"
285 else return format("\\%03d", byte(c))
289 local function fmtfunc(func, pc)
290 local fi = funcinfo(func, pc)
291 if fi.loc then
292 return fi.loc
293 elseif fi.ffid then
294 return vmdef.ffnames[fi.ffid]
295 elseif fi.addr then
296 return format("C:%x", fi.addr)
297 else
298 return "(?)"
302 local function formatk(tr, idx)
303 local k, t, slot = tracek(tr, idx)
304 local tn = type(k)
305 local s
306 if tn == "number" then
307 if k == 2^52+2^51 then
308 s = "bias"
309 else
310 s = format("%+.14g", k)
312 elseif tn == "string" then
313 s = format(#k > 20 and '"%.20s"~' or '"%s"', gsub(k, "%c", ctlsub))
314 elseif tn == "function" then
315 s = fmtfunc(k)
316 elseif tn == "table" then
317 s = format("{%p}", k)
318 elseif tn == "userdata" then
319 if t == 12 then
320 s = format("userdata:%p", k)
321 else
322 s = format("[%p]", k)
323 if s == "[0x00000000]" then s = "NULL" end
325 elseif t == 21 then -- int64_t
326 s = sub(tostring(k), 1, -3)
327 if sub(s, 1, 1) ~= "-" then s = "+"..s end
328 else
329 s = tostring(k) -- For primitives.
331 s = colorize(format("%-4s", s), t)
332 if slot then
333 s = format("%s @%d", s, slot)
335 return s
338 local function printsnap(tr, snap)
339 local n = 2
340 for s=0,snap[1]-1 do
341 local sn = snap[n]
342 if shr(sn, 24) == s then
343 n = n + 1
344 local ref = band(sn, 0xffff) - 0x8000 -- REF_BIAS
345 if ref < 0 then
346 out:write(formatk(tr, ref))
347 elseif band(sn, 0x80000) ~= 0 then -- SNAP_SOFTFPNUM
348 out:write(colorize(format("%04d/%04d", ref, ref+1), 14))
349 else
350 local m, ot, op1, op2 = traceir(tr, ref)
351 out:write(colorize(format("%04d", ref), band(ot, 31)))
353 out:write(band(sn, 0x10000) == 0 and " " or "|") -- SNAP_FRAME
354 else
355 out:write("---- ")
358 out:write("]\n")
361 -- Dump snapshots (not interleaved with IR).
362 local function dump_snap(tr)
363 out:write("---- TRACE ", tr, " snapshots\n")
364 for i=0,1000000000 do
365 local snap = tracesnap(tr, i)
366 if not snap then break end
367 out:write(format("#%-3d %04d [ ", i, snap[0]))
368 printsnap(tr, snap)
372 -- Return a register name or stack slot for a rid/sp location.
373 local function ridsp_name(ridsp)
374 if not disass then disass = require("jit.dis_"..jit.arch) end
375 local rid = band(ridsp, 0xff)
376 if ridsp > 255 then return format("[%x]", shr(ridsp, 8)*4) end
377 if rid < 128 then return disass.regname(rid) end
378 return ""
381 -- Dump CALL* function ref and return optional ctype.
382 local function dumpcallfunc(tr, ins)
383 local ctype
384 if ins > 0 then
385 local m, ot, op1, op2 = traceir(tr, ins)
386 if band(ot, 31) == 0 then -- nil type means CARG(func, ctype).
387 ins = op1
388 ctype = formatk(tr, op2)
391 if ins < 0 then
392 out:write(format("[0x%x](", tonumber((tracek(tr, ins)))))
393 else
394 out:write(format("%04d (", ins))
396 return ctype
399 -- Recursively gather CALL* args and dump them.
400 local function dumpcallargs(tr, ins)
401 if ins < 0 then
402 out:write(formatk(tr, ins))
403 else
404 local m, ot, op1, op2 = traceir(tr, ins)
405 local oidx = 6*shr(ot, 8)
406 local op = sub(vmdef.irnames, oidx+1, oidx+6)
407 if op == "CARG " then
408 dumpcallargs(tr, op1)
409 if op2 < 0 then
410 out:write(" ", formatk(tr, op2))
411 else
412 out:write(" ", format("%04d", op2))
414 else
415 out:write(format("%04d", ins))
420 -- Dump IR and interleaved snapshots.
421 local function dump_ir(tr, dumpsnap, dumpreg)
422 local info = traceinfo(tr)
423 if not info then return end
424 local nins = info.nins
425 out:write("---- TRACE ", tr, " IR\n")
426 local irnames = vmdef.irnames
427 local snapref = 65536
428 local snap, snapno
429 if dumpsnap then
430 snap = tracesnap(tr, 0)
431 snapref = snap[0]
432 snapno = 0
434 for ins=1,nins do
435 if ins >= snapref then
436 if dumpreg then
437 out:write(format(".... SNAP #%-3d [ ", snapno))
438 else
439 out:write(format(".... SNAP #%-3d [ ", snapno))
441 printsnap(tr, snap)
442 snapno = snapno + 1
443 snap = tracesnap(tr, snapno)
444 snapref = snap and snap[0] or 65536
446 local m, ot, op1, op2, ridsp = traceir(tr, ins)
447 local oidx, t = 6*shr(ot, 8), band(ot, 31)
448 local op = sub(irnames, oidx+1, oidx+6)
449 if op == "LOOP " then
450 if dumpreg then
451 out:write(format("%04d ------------ LOOP ------------\n", ins))
452 else
453 out:write(format("%04d ------ LOOP ------------\n", ins))
455 elseif op ~= "NOP " and op ~= "CARG " and
456 (dumpreg or op ~= "RENAME") then
457 if dumpreg then
458 out:write(format("%04d %-5s ", ins, ridsp_name(ridsp)))
459 else
460 out:write(format("%04d ", ins))
462 out:write(format("%s%s %s %s ",
463 band(ot, 128) == 0 and " " or ">",
464 band(ot, 64) == 0 and " " or "+",
465 irtype[t], op))
466 local m1, m2 = band(m, 3), band(m, 3*4)
467 if sub(op, 1, 4) == "CALL" then
468 local ctype
469 if m2 == 1*4 then -- op2 == IRMlit
470 out:write(format("%-10s (", vmdef.ircall[op2]))
471 else
472 ctype = dumpcallfunc(tr, op2)
474 if op1 ~= -1 then dumpcallargs(tr, op1) end
475 out:write(")")
476 if ctype then out:write(" ctype ", ctype) end
477 elseif op == "CNEW " and op2 == -1 then
478 out:write(formatk(tr, op1))
479 elseif m1 ~= 3 then -- op1 != IRMnone
480 if op1 < 0 then
481 out:write(formatk(tr, op1))
482 else
483 out:write(format(m1 == 0 and "%04d" or "#%-3d", op1))
485 if m2 ~= 3*4 then -- op2 != IRMnone
486 if m2 == 1*4 then -- op2 == IRMlit
487 local litn = litname[op]
488 if litn and litn[op2] then
489 out:write(" ", litn[op2])
490 elseif op == "UREFO " or op == "UREFC " then
491 out:write(format(" #%-3d", shr(op2, 8)))
492 else
493 out:write(format(" #%-3d", op2))
495 elseif op2 < 0 then
496 out:write(" ", formatk(tr, op2))
497 else
498 out:write(format(" %04d", op2))
502 out:write("\n")
505 if snap then
506 if dumpreg then
507 out:write(format(".... SNAP #%-3d [ ", snapno))
508 else
509 out:write(format(".... SNAP #%-3d [ ", snapno))
511 printsnap(tr, snap)
515 ------------------------------------------------------------------------------
517 local recprefix = ""
518 local recdepth = 0
520 -- Format trace error message.
521 local function fmterr(err, info)
522 if type(err) == "number" then
523 if type(info) == "function" then info = fmtfunc(info) end
524 err = format(vmdef.traceerr[err], info)
526 return err
529 -- Dump trace states.
530 local function dump_trace(what, tr, func, pc, otr, oex)
531 if what == "stop" or (what == "abort" and dumpmode.a) then
532 if dumpmode.i then dump_ir(tr, dumpmode.s, dumpmode.r and what == "stop")
533 elseif dumpmode.s then dump_snap(tr) end
534 if dumpmode.m then dump_mcode(tr) end
536 if what == "start" then
537 if dumpmode.H then out:write('<pre class="ljdump">\n') end
538 out:write("---- TRACE ", tr, " ", what)
539 if otr then out:write(" ", otr, "/", oex) end
540 out:write(" ", fmtfunc(func, pc), "\n")
541 recprefix = ""
542 elseif what == "stop" or what == "abort" then
543 out:write("---- TRACE ", tr, " ", what)
544 recprefix = nil
545 if what == "abort" then
546 out:write(" ", fmtfunc(func, pc), " -- ", fmterr(otr, oex), "\n")
547 else
548 local info = traceinfo(tr)
549 local link, ltype = info.link, info.linktype
550 if link == tr or link == 0 then
551 out:write(" -> ", ltype, "\n")
552 elseif ltype == "root" then
553 out:write(" -> ", link, "\n")
554 else
555 out:write(" -> ", link, " ", ltype, "\n")
558 if dumpmode.H then out:write("</pre>\n\n") else out:write("\n") end
559 else
560 out:write("---- TRACE ", what, "\n\n")
562 out:flush()
565 -- Dump recorded bytecode.
566 local function dump_record(tr, func, pc, depth, callee)
567 if depth ~= recdepth then
568 recdepth = depth
569 recprefix = rep(" .", depth)
571 local line
572 if pc >= 0 then
573 line = bcline(func, pc, recprefix)
574 if dumpmode.H then line = gsub(line, "[<>&]", html_escape) end
575 else
576 line = "0000 "..recprefix.." FUNCC \n"
577 callee = func
579 if pc <= 0 then
580 out:write(sub(line, 1, -2), " ; ", fmtfunc(func), "\n")
581 else
582 out:write(line)
584 if pc >= 0 and band(funcbc(func, pc), 0xff) < 16 then -- ORDER BC
585 out:write(bcline(func, pc+1, recprefix)) -- Write JMP for cond.
589 ------------------------------------------------------------------------------
591 -- Dump taken trace exits.
592 local function dump_texit(tr, ex, ngpr, nfpr, ...)
593 out:write("---- TRACE ", tr, " exit ", ex, "\n")
594 if dumpmode.X then
595 local regs = {...}
596 if jit.arch == "x64" then
597 for i=1,ngpr do
598 out:write(format(" %016x", regs[i]))
599 if i % 4 == 0 then out:write("\n") end
601 else
602 for i=1,ngpr do
603 out:write(format(" %08x", regs[i]))
604 if i % 8 == 0 then out:write("\n") end
607 for i=1,nfpr do
608 out:write(format(" %+17.14g", regs[ngpr+i]))
609 if i % 4 == 0 then out:write("\n") end
614 ------------------------------------------------------------------------------
616 -- Detach dump handlers.
617 local function dumpoff()
618 if active then
619 active = false
620 jit.attach(dump_texit)
621 jit.attach(dump_record)
622 jit.attach(dump_trace)
623 if out and out ~= stdout and out ~= stderr then out:close() end
624 out = nil
628 -- Open the output file and attach dump handlers.
629 local function dumpon(opt, outfile)
630 if active then dumpoff() end
632 local colormode = os.getenv("COLORTERM") and "A" or "T"
633 if opt then
634 opt = gsub(opt, "[TAH]", function(mode) colormode = mode; return ""; end)
637 local m = { t=true, b=true, i=true, m=true, }
638 if opt and opt ~= "" then
639 local o = sub(opt, 1, 1)
640 if o ~= "+" and o ~= "-" then m = {} end
641 for i=1,#opt do m[sub(opt, i, i)] = (o ~= "-") end
643 dumpmode = m
645 if m.t or m.b or m.i or m.s or m.m then
646 jit.attach(dump_trace, "trace")
648 if m.b then
649 jit.attach(dump_record, "record")
650 if not bcline then bcline = require("jit.bc").line end
652 if m.x or m.X then
653 jit.attach(dump_texit, "texit")
656 if not outfile then outfile = os.getenv("LUAJIT_DUMPFILE") end
657 if outfile then
658 out = outfile == "-" and stdout or assert(io.open(outfile, "w"))
659 else
660 out = stdout
663 m[colormode] = true
664 if colormode == "A" then
665 colorize = colorize_ansi
666 irtype = irtype_ansi
667 elseif colormode == "H" then
668 colorize = colorize_html
669 irtype = irtype_html
670 out:write(header_html)
671 else
672 colorize = colorize_text
673 irtype = irtype_text
676 active = true
679 -- Public module functions.
680 module(...)
682 on = dumpon
683 off = dumpoff
684 start = dumpon -- For -j command line option.