Fix for -jdump docs.
[luajit-2.0.git] / src / jit / dump.lua
blobfe57af8f171e3c63f26885897c732f95b0a101a5
1 ----------------------------------------------------------------------------
2 -- LuaJIT compiler dump module.
3 --
4 -- Copyright (C) 2005-2014 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.
39 -- a Print the IR of aborted traces, too.
41 -- The output format can be set with the following characters:
43 -- T Plain text output.
44 -- A ANSI-colored text output
45 -- H Colorized HTML + CSS output.
47 -- The default output format is plain text. It's set to ANSI-colored text
48 -- if the COLORTERM variable is set. Note: this is independent of any output
49 -- redirection, which is actually considered a feature.
51 -- You probably want to use less -R to enjoy viewing ANSI-colored text from
52 -- a pipe or a file. Add this to your ~/.bashrc: export LESS="-R"
54 ------------------------------------------------------------------------------
56 -- Cache some library functions and objects.
57 local jit = require("jit")
58 assert(jit.version_num == 20002, "LuaJIT core/library version mismatch")
59 local jutil = require("jit.util")
60 local vmdef = require("jit.vmdef")
61 local funcinfo, funcbc = jutil.funcinfo, jutil.funcbc
62 local traceinfo, traceir, tracek = jutil.traceinfo, jutil.traceir, jutil.tracek
63 local tracemc, tracesnap = jutil.tracemc, jutil.tracesnap
64 local traceexitstub, ircalladdr = jutil.traceexitstub, jutil.ircalladdr
65 local bit = require("bit")
66 local band, shl, shr = bit.band, bit.lshift, bit.rshift
67 local sub, gsub, format = string.sub, string.gsub, string.format
68 local byte, char, rep = string.byte, string.char, string.rep
69 local type, tostring = type, tostring
70 local stdout, stderr = io.stdout, io.stderr
72 -- Load other modules on-demand.
73 local bcline, disass
75 -- Active flag, output file handle and dump mode.
76 local active, out, dumpmode
78 ------------------------------------------------------------------------------
80 local symtabmt = { __index = false }
81 local symtab = {}
82 local nexitsym = 0
84 -- Fill nested symbol table with per-trace exit stub addresses.
85 local function fillsymtab_tr(tr, nexit)
86 local t = {}
87 symtabmt.__index = t
88 if jit.arch == "mips" or jit.arch == "mipsel" then
89 t[traceexitstub(tr, 0)] = "exit"
90 return
91 end
92 for i=0,nexit-1 do
93 local addr = traceexitstub(tr, i)
94 t[addr] = tostring(i)
95 end
96 local addr = traceexitstub(tr, nexit)
97 if addr then t[addr] = "stack_check" end
98 end
100 -- Fill symbol table with trace exit stub addresses.
101 local function fillsymtab(tr, nexit)
102 local t = symtab
103 if nexitsym == 0 then
104 local ircall = vmdef.ircall
105 for i=0,#ircall do
106 local addr = ircalladdr(i)
107 if addr ~= 0 then t[addr] = ircall[i] end
110 if nexitsym == 1000000 then -- Per-trace exit stubs.
111 fillsymtab_tr(tr, nexit)
112 elseif nexit > nexitsym then -- Shared exit stubs.
113 for i=nexitsym,nexit-1 do
114 local addr = traceexitstub(i)
115 if addr == nil then -- Fall back to per-trace exit stubs.
116 fillsymtab_tr(tr, nexit)
117 setmetatable(symtab, symtabmt)
118 nexit = 1000000
119 break
121 t[addr] = tostring(i)
123 nexitsym = nexit
125 return t
128 local function dumpwrite(s)
129 out:write(s)
132 -- Disassemble machine code.
133 local function dump_mcode(tr)
134 local info = traceinfo(tr)
135 if not info then return end
136 local mcode, addr, loop = tracemc(tr)
137 if not mcode then return end
138 if not disass then disass = require("jit.dis_"..jit.arch) end
139 out:write("---- TRACE ", tr, " mcode ", #mcode, "\n")
140 local ctx = disass.create(mcode, addr, dumpwrite)
141 ctx.hexdump = 0
142 ctx.symtab = fillsymtab(tr, info.nexit)
143 if loop ~= 0 then
144 symtab[addr+loop] = "LOOP"
145 ctx:disass(0, loop)
146 out:write("->LOOP:\n")
147 ctx:disass(loop, #mcode-loop)
148 symtab[addr+loop] = nil
149 else
150 ctx:disass(0, #mcode)
154 ------------------------------------------------------------------------------
156 local irtype_text = {
157 [0] = "nil",
158 "fal",
159 "tru",
160 "lud",
161 "str",
162 "p32",
163 "thr",
164 "pro",
165 "fun",
166 "p64",
167 "cdt",
168 "tab",
169 "udt",
170 "flt",
171 "num",
172 "i8 ",
173 "u8 ",
174 "i16",
175 "u16",
176 "int",
177 "u32",
178 "i64",
179 "u64",
180 "sfp",
183 local colortype_ansi = {
184 [0] = "%s",
185 "%s",
186 "%s",
187 "\027[36m%s\027[m",
188 "\027[32m%s\027[m",
189 "%s",
190 "\027[1m%s\027[m",
191 "%s",
192 "\027[1m%s\027[m",
193 "%s",
194 "\027[33m%s\027[m",
195 "\027[31m%s\027[m",
196 "\027[36m%s\027[m",
197 "\027[34m%s\027[m",
198 "\027[34m%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",
203 "\027[35m%s\027[m",
204 "\027[35m%s\027[m",
205 "\027[35m%s\027[m",
206 "\027[35m%s\027[m",
207 "\027[35m%s\027[m",
210 local function colorize_text(s, t)
211 return s
214 local function colorize_ansi(s, t)
215 return format(colortype_ansi[t], s)
218 local irtype_ansi = setmetatable({},
219 { __index = function(tab, t)
220 local s = colorize_ansi(irtype_text[t], t); tab[t] = s; return s; end })
222 local html_escape = { ["<"] = "&lt;", [">"] = "&gt;", ["&"] = "&amp;", }
224 local function colorize_html(s, t)
225 s = gsub(s, "[<>&]", html_escape)
226 return format('<span class="irt_%s">%s</span>', irtype_text[t], s)
229 local irtype_html = setmetatable({},
230 { __index = function(tab, t)
231 local s = colorize_html(irtype_text[t], t); tab[t] = s; return s; end })
233 local header_html = [[
234 <style type="text/css">
235 background { background: #ffffff; color: #000000; }
236 pre.ljdump {
237 font-size: 10pt;
238 background: #f0f4ff;
239 color: #000000;
240 border: 1px solid #bfcfff;
241 padding: 0.5em;
242 margin-left: 2em;
243 margin-right: 2em;
245 span.irt_str { color: #00a000; }
246 span.irt_thr, span.irt_fun { color: #404040; font-weight: bold; }
247 span.irt_tab { color: #c00000; }
248 span.irt_udt, span.irt_lud { color: #00c0c0; }
249 span.irt_num { color: #4040c0; }
250 span.irt_int, span.irt_i8, span.irt_u8, span.irt_i16, span.irt_u16 { color: #b040b0; }
251 </style>
254 local colorize, irtype
256 -- Lookup tables to convert some literals into names.
257 local litname = {
258 ["SLOAD "] = setmetatable({}, { __index = function(t, mode)
259 local s = ""
260 if band(mode, 1) ~= 0 then s = s.."P" end
261 if band(mode, 2) ~= 0 then s = s.."F" end
262 if band(mode, 4) ~= 0 then s = s.."T" end
263 if band(mode, 8) ~= 0 then s = s.."C" end
264 if band(mode, 16) ~= 0 then s = s.."R" end
265 if band(mode, 32) ~= 0 then s = s.."I" end
266 t[mode] = s
267 return s
268 end}),
269 ["XLOAD "] = { [0] = "", "R", "V", "RV", "U", "RU", "VU", "RVU", },
270 ["CONV "] = setmetatable({}, { __index = function(t, mode)
271 local s = irtype[band(mode, 31)]
272 s = irtype[band(shr(mode, 5), 31)].."."..s
273 if band(mode, 0x400) ~= 0 then s = s.." trunc"
274 elseif band(mode, 0x800) ~= 0 then s = s.." sext" end
275 local c = shr(mode, 14)
276 if c == 2 then s = s.." index" elseif c == 3 then s = s.." check" end
277 t[mode] = s
278 return s
279 end}),
280 ["FLOAD "] = vmdef.irfield,
281 ["FREF "] = vmdef.irfield,
282 ["FPMATH"] = vmdef.irfpm,
285 local function ctlsub(c)
286 if c == "\n" then return "\\n"
287 elseif c == "\r" then return "\\r"
288 elseif c == "\t" then return "\\t"
289 else return format("\\%03d", byte(c))
293 local function fmtfunc(func, pc)
294 local fi = funcinfo(func, pc)
295 if fi.loc then
296 return fi.loc
297 elseif fi.ffid then
298 return vmdef.ffnames[fi.ffid]
299 elseif fi.addr then
300 return format("C:%x", fi.addr)
301 else
302 return "(?)"
306 local function formatk(tr, idx)
307 local k, t, slot = tracek(tr, idx)
308 local tn = type(k)
309 local s
310 if tn == "number" then
311 if k == 2^52+2^51 then
312 s = "bias"
313 else
314 s = format("%+.14g", k)
316 elseif tn == "string" then
317 s = format(#k > 20 and '"%.20s"~' or '"%s"', gsub(k, "%c", ctlsub))
318 elseif tn == "function" then
319 s = fmtfunc(k)
320 elseif tn == "table" then
321 s = format("{%p}", k)
322 elseif tn == "userdata" then
323 if t == 12 then
324 s = format("userdata:%p", k)
325 else
326 s = format("[%p]", k)
327 if s == "[0x00000000]" then s = "NULL" end
329 elseif t == 21 then -- int64_t
330 s = sub(tostring(k), 1, -3)
331 if sub(s, 1, 1) ~= "-" then s = "+"..s end
332 else
333 s = tostring(k) -- For primitives.
335 s = colorize(format("%-4s", s), t)
336 if slot then
337 s = format("%s @%d", s, slot)
339 return s
342 local function printsnap(tr, snap)
343 local n = 2
344 for s=0,snap[1]-1 do
345 local sn = snap[n]
346 if shr(sn, 24) == s then
347 n = n + 1
348 local ref = band(sn, 0xffff) - 0x8000 -- REF_BIAS
349 if ref < 0 then
350 out:write(formatk(tr, ref))
351 elseif band(sn, 0x80000) ~= 0 then -- SNAP_SOFTFPNUM
352 out:write(colorize(format("%04d/%04d", ref, ref+1), 14))
353 else
354 local m, ot, op1, op2 = traceir(tr, ref)
355 out:write(colorize(format("%04d", ref), band(ot, 31)))
357 out:write(band(sn, 0x10000) == 0 and " " or "|") -- SNAP_FRAME
358 else
359 out:write("---- ")
362 out:write("]\n")
365 -- Dump snapshots (not interleaved with IR).
366 local function dump_snap(tr)
367 out:write("---- TRACE ", tr, " snapshots\n")
368 for i=0,1000000000 do
369 local snap = tracesnap(tr, i)
370 if not snap then break end
371 out:write(format("#%-3d %04d [ ", i, snap[0]))
372 printsnap(tr, snap)
376 -- Return a register name or stack slot for a rid/sp location.
377 local function ridsp_name(ridsp, ins)
378 if not disass then disass = require("jit.dis_"..jit.arch) end
379 local rid, slot = band(ridsp, 0xff), shr(ridsp, 8)
380 if rid == 253 or rid == 254 then
381 return (slot == 0 or slot == 255) and " {sink" or format(" {%04d", ins-slot)
383 if ridsp > 255 then return format("[%x]", slot*4) end
384 if rid < 128 then return disass.regname(rid) end
385 return ""
388 -- Dump CALL* function ref and return optional ctype.
389 local function dumpcallfunc(tr, ins)
390 local ctype
391 if ins > 0 then
392 local m, ot, op1, op2 = traceir(tr, ins)
393 if band(ot, 31) == 0 then -- nil type means CARG(func, ctype).
394 ins = op1
395 ctype = formatk(tr, op2)
398 if ins < 0 then
399 out:write(format("[0x%x](", tonumber((tracek(tr, ins)))))
400 else
401 out:write(format("%04d (", ins))
403 return ctype
406 -- Recursively gather CALL* args and dump them.
407 local function dumpcallargs(tr, ins)
408 if ins < 0 then
409 out:write(formatk(tr, ins))
410 else
411 local m, ot, op1, op2 = traceir(tr, ins)
412 local oidx = 6*shr(ot, 8)
413 local op = sub(vmdef.irnames, oidx+1, oidx+6)
414 if op == "CARG " then
415 dumpcallargs(tr, op1)
416 if op2 < 0 then
417 out:write(" ", formatk(tr, op2))
418 else
419 out:write(" ", format("%04d", op2))
421 else
422 out:write(format("%04d", ins))
427 -- Dump IR and interleaved snapshots.
428 local function dump_ir(tr, dumpsnap, dumpreg)
429 local info = traceinfo(tr)
430 if not info then return end
431 local nins = info.nins
432 out:write("---- TRACE ", tr, " IR\n")
433 local irnames = vmdef.irnames
434 local snapref = 65536
435 local snap, snapno
436 if dumpsnap then
437 snap = tracesnap(tr, 0)
438 snapref = snap[0]
439 snapno = 0
441 for ins=1,nins do
442 if ins >= snapref then
443 if dumpreg then
444 out:write(format(".... SNAP #%-3d [ ", snapno))
445 else
446 out:write(format(".... SNAP #%-3d [ ", snapno))
448 printsnap(tr, snap)
449 snapno = snapno + 1
450 snap = tracesnap(tr, snapno)
451 snapref = snap and snap[0] or 65536
453 local m, ot, op1, op2, ridsp = traceir(tr, ins)
454 local oidx, t = 6*shr(ot, 8), band(ot, 31)
455 local op = sub(irnames, oidx+1, oidx+6)
456 if op == "LOOP " then
457 if dumpreg then
458 out:write(format("%04d ------------ LOOP ------------\n", ins))
459 else
460 out:write(format("%04d ------ LOOP ------------\n", ins))
462 elseif op ~= "NOP " and op ~= "CARG " and
463 (dumpreg or op ~= "RENAME") then
464 local rid = band(ridsp, 255)
465 if dumpreg then
466 out:write(format("%04d %-6s", ins, ridsp_name(ridsp, ins)))
467 else
468 out:write(format("%04d ", ins))
470 out:write(format("%s%s %s %s ",
471 (rid == 254 or rid == 253) and "}" or
472 (band(ot, 128) == 0 and " " or ">"),
473 band(ot, 64) == 0 and " " or "+",
474 irtype[t], op))
475 local m1, m2 = band(m, 3), band(m, 3*4)
476 if sub(op, 1, 4) == "CALL" then
477 local ctype
478 if m2 == 1*4 then -- op2 == IRMlit
479 out:write(format("%-10s (", vmdef.ircall[op2]))
480 else
481 ctype = dumpcallfunc(tr, op2)
483 if op1 ~= -1 then dumpcallargs(tr, op1) end
484 out:write(")")
485 if ctype then out:write(" ctype ", ctype) end
486 elseif op == "CNEW " and op2 == -1 then
487 out:write(formatk(tr, op1))
488 elseif m1 ~= 3 then -- op1 != IRMnone
489 if op1 < 0 then
490 out:write(formatk(tr, op1))
491 else
492 out:write(format(m1 == 0 and "%04d" or "#%-3d", op1))
494 if m2 ~= 3*4 then -- op2 != IRMnone
495 if m2 == 1*4 then -- op2 == IRMlit
496 local litn = litname[op]
497 if litn and litn[op2] then
498 out:write(" ", litn[op2])
499 elseif op == "UREFO " or op == "UREFC " then
500 out:write(format(" #%-3d", shr(op2, 8)))
501 else
502 out:write(format(" #%-3d", op2))
504 elseif op2 < 0 then
505 out:write(" ", formatk(tr, op2))
506 else
507 out:write(format(" %04d", op2))
511 out:write("\n")
514 if snap then
515 if dumpreg then
516 out:write(format(".... SNAP #%-3d [ ", snapno))
517 else
518 out:write(format(".... SNAP #%-3d [ ", snapno))
520 printsnap(tr, snap)
524 ------------------------------------------------------------------------------
526 local recprefix = ""
527 local recdepth = 0
529 -- Format trace error message.
530 local function fmterr(err, info)
531 if type(err) == "number" then
532 if type(info) == "function" then info = fmtfunc(info) end
533 err = format(vmdef.traceerr[err], info)
535 return err
538 -- Dump trace states.
539 local function dump_trace(what, tr, func, pc, otr, oex)
540 if what == "stop" or (what == "abort" and dumpmode.a) then
541 if dumpmode.i then dump_ir(tr, dumpmode.s, dumpmode.r and what == "stop")
542 elseif dumpmode.s then dump_snap(tr) end
543 if dumpmode.m then dump_mcode(tr) end
545 if what == "start" then
546 if dumpmode.H then out:write('<pre class="ljdump">\n') end
547 out:write("---- TRACE ", tr, " ", what)
548 if otr then out:write(" ", otr, "/", oex) end
549 out:write(" ", fmtfunc(func, pc), "\n")
550 recprefix = ""
551 elseif what == "stop" or what == "abort" then
552 out:write("---- TRACE ", tr, " ", what)
553 recprefix = nil
554 if what == "abort" then
555 out:write(" ", fmtfunc(func, pc), " -- ", fmterr(otr, oex), "\n")
556 else
557 local info = traceinfo(tr)
558 local link, ltype = info.link, info.linktype
559 if link == tr or link == 0 then
560 out:write(" -> ", ltype, "\n")
561 elseif ltype == "root" then
562 out:write(" -> ", link, "\n")
563 else
564 out:write(" -> ", link, " ", ltype, "\n")
567 if dumpmode.H then out:write("</pre>\n\n") else out:write("\n") end
568 else
569 out:write("---- TRACE ", what, "\n\n")
571 out:flush()
574 -- Dump recorded bytecode.
575 local function dump_record(tr, func, pc, depth, callee)
576 if depth ~= recdepth then
577 recdepth = depth
578 recprefix = rep(" .", depth)
580 local line
581 if pc >= 0 then
582 line = bcline(func, pc, recprefix)
583 if dumpmode.H then line = gsub(line, "[<>&]", html_escape) end
584 else
585 line = "0000 "..recprefix.." FUNCC \n"
586 callee = func
588 if pc <= 0 then
589 out:write(sub(line, 1, -2), " ; ", fmtfunc(func), "\n")
590 else
591 out:write(line)
593 if pc >= 0 and band(funcbc(func, pc), 0xff) < 16 then -- ORDER BC
594 out:write(bcline(func, pc+1, recprefix)) -- Write JMP for cond.
598 ------------------------------------------------------------------------------
600 -- Dump taken trace exits.
601 local function dump_texit(tr, ex, ngpr, nfpr, ...)
602 out:write("---- TRACE ", tr, " exit ", ex, "\n")
603 if dumpmode.X then
604 local regs = {...}
605 if jit.arch == "x64" then
606 for i=1,ngpr do
607 out:write(format(" %016x", regs[i]))
608 if i % 4 == 0 then out:write("\n") end
610 else
611 for i=1,ngpr do
612 out:write(format(" %08x", regs[i]))
613 if i % 8 == 0 then out:write("\n") end
616 if jit.arch == "mips" or jit.arch == "mipsel" then
617 for i=1,nfpr,2 do
618 out:write(format(" %+17.14g", regs[ngpr+i]))
619 if i % 8 == 7 then out:write("\n") end
621 else
622 for i=1,nfpr do
623 out:write(format(" %+17.14g", regs[ngpr+i]))
624 if i % 4 == 0 then out:write("\n") end
630 ------------------------------------------------------------------------------
632 -- Detach dump handlers.
633 local function dumpoff()
634 if active then
635 active = false
636 jit.attach(dump_texit)
637 jit.attach(dump_record)
638 jit.attach(dump_trace)
639 if out and out ~= stdout and out ~= stderr then out:close() end
640 out = nil
644 -- Open the output file and attach dump handlers.
645 local function dumpon(opt, outfile)
646 if active then dumpoff() end
648 local colormode = os.getenv("COLORTERM") and "A" or "T"
649 if opt then
650 opt = gsub(opt, "[TAH]", function(mode) colormode = mode; return ""; end)
653 local m = { t=true, b=true, i=true, m=true, }
654 if opt and opt ~= "" then
655 local o = sub(opt, 1, 1)
656 if o ~= "+" and o ~= "-" then m = {} end
657 for i=1,#opt do m[sub(opt, i, i)] = (o ~= "-") end
659 dumpmode = m
661 if m.t or m.b or m.i or m.s or m.m then
662 jit.attach(dump_trace, "trace")
664 if m.b then
665 jit.attach(dump_record, "record")
666 if not bcline then bcline = require("jit.bc").line end
668 if m.x or m.X then
669 jit.attach(dump_texit, "texit")
672 if not outfile then outfile = os.getenv("LUAJIT_DUMPFILE") end
673 if outfile then
674 out = outfile == "-" and stdout or assert(io.open(outfile, "w"))
675 else
676 out = stdout
679 m[colormode] = true
680 if colormode == "A" then
681 colorize = colorize_ansi
682 irtype = irtype_ansi
683 elseif colormode == "H" then
684 colorize = colorize_html
685 irtype = irtype_html
686 out:write(header_html)
687 else
688 colorize = colorize_text
689 irtype = irtype_text
692 active = true
695 -- Public module functions.
696 module(...)
698 on = dumpon
699 off = dumpoff
700 start = dumpon -- For -j command line option.