Merge branch 'master' into v2.1
[luajit-2.0.git] / src / jit / dump.lua
blobc9016ce548fedc3b76b9247696d553cbc58ba2c2
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 == 20100, "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, tohex = bit.band, bit.lshift, bit.rshift, bit.tohex
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 if addr < 0 then addr = addr + 2^32 end
95 t[addr] = tostring(i)
96 end
97 local addr = traceexitstub(tr, nexit)
98 if addr then t[addr] = "stack_check" end
99 end
101 -- Fill symbol table with trace exit stub addresses.
102 local function fillsymtab(tr, nexit)
103 local t = symtab
104 if nexitsym == 0 then
105 local ircall = vmdef.ircall
106 for i=0,#ircall do
107 local addr = ircalladdr(i)
108 if addr ~= 0 then
109 if addr < 0 then addr = addr + 2^32 end
110 t[addr] = ircall[i]
114 if nexitsym == 1000000 then -- Per-trace exit stubs.
115 fillsymtab_tr(tr, nexit)
116 elseif nexit > nexitsym then -- Shared exit stubs.
117 for i=nexitsym,nexit-1 do
118 local addr = traceexitstub(i)
119 if addr == nil then -- Fall back to per-trace exit stubs.
120 fillsymtab_tr(tr, nexit)
121 setmetatable(symtab, symtabmt)
122 nexit = 1000000
123 break
125 if addr < 0 then addr = addr + 2^32 end
126 t[addr] = tostring(i)
128 nexitsym = nexit
130 return t
133 local function dumpwrite(s)
134 out:write(s)
137 -- Disassemble machine code.
138 local function dump_mcode(tr)
139 local info = traceinfo(tr)
140 if not info then return end
141 local mcode, addr, loop = tracemc(tr)
142 if not mcode then return end
143 if not disass then disass = require("jit.dis_"..jit.arch) end
144 if addr < 0 then addr = addr + 2^32 end
145 out:write("---- TRACE ", tr, " mcode ", #mcode, "\n")
146 local ctx = disass.create(mcode, addr, dumpwrite)
147 ctx.hexdump = 0
148 ctx.symtab = fillsymtab(tr, info.nexit)
149 if loop ~= 0 then
150 symtab[addr+loop] = "LOOP"
151 ctx:disass(0, loop)
152 out:write("->LOOP:\n")
153 ctx:disass(loop, #mcode-loop)
154 symtab[addr+loop] = nil
155 else
156 ctx:disass(0, #mcode)
160 ------------------------------------------------------------------------------
162 local irtype_text = {
163 [0] = "nil",
164 "fal",
165 "tru",
166 "lud",
167 "str",
168 "p32",
169 "thr",
170 "pro",
171 "fun",
172 "p64",
173 "cdt",
174 "tab",
175 "udt",
176 "flt",
177 "num",
178 "i8 ",
179 "u8 ",
180 "i16",
181 "u16",
182 "int",
183 "u32",
184 "i64",
185 "u64",
186 "sfp",
189 local colortype_ansi = {
190 [0] = "%s",
191 "%s",
192 "%s",
193 "\027[36m%s\027[m",
194 "\027[32m%s\027[m",
195 "%s",
196 "\027[1m%s\027[m",
197 "%s",
198 "\027[1m%s\027[m",
199 "%s",
200 "\027[33m%s\027[m",
201 "\027[31m%s\027[m",
202 "\027[36m%s\027[m",
203 "\027[34m%s\027[m",
204 "\027[34m%s\027[m",
205 "\027[35m%s\027[m",
206 "\027[35m%s\027[m",
207 "\027[35m%s\027[m",
208 "\027[35m%s\027[m",
209 "\027[35m%s\027[m",
210 "\027[35m%s\027[m",
211 "\027[35m%s\027[m",
212 "\027[35m%s\027[m",
213 "\027[35m%s\027[m",
216 local function colorize_text(s, t)
217 return s
220 local function colorize_ansi(s, t)
221 return format(colortype_ansi[t], s)
224 local irtype_ansi = setmetatable({},
225 { __index = function(tab, t)
226 local s = colorize_ansi(irtype_text[t], t); tab[t] = s; return s; end })
228 local html_escape = { ["<"] = "&lt;", [">"] = "&gt;", ["&"] = "&amp;", }
230 local function colorize_html(s, t)
231 s = gsub(s, "[<>&]", html_escape)
232 return format('<span class="irt_%s">%s</span>', irtype_text[t], s)
235 local irtype_html = setmetatable({},
236 { __index = function(tab, t)
237 local s = colorize_html(irtype_text[t], t); tab[t] = s; return s; end })
239 local header_html = [[
240 <style type="text/css">
241 background { background: #ffffff; color: #000000; }
242 pre.ljdump {
243 font-size: 10pt;
244 background: #f0f4ff;
245 color: #000000;
246 border: 1px solid #bfcfff;
247 padding: 0.5em;
248 margin-left: 2em;
249 margin-right: 2em;
251 span.irt_str { color: #00a000; }
252 span.irt_thr, span.irt_fun { color: #404040; font-weight: bold; }
253 span.irt_tab { color: #c00000; }
254 span.irt_udt, span.irt_lud { color: #00c0c0; }
255 span.irt_num { color: #4040c0; }
256 span.irt_int, span.irt_i8, span.irt_u8, span.irt_i16, span.irt_u16 { color: #b040b0; }
257 </style>
260 local colorize, irtype
262 -- Lookup tables to convert some literals into names.
263 local litname = {
264 ["SLOAD "] = setmetatable({}, { __index = function(t, mode)
265 local s = ""
266 if band(mode, 1) ~= 0 then s = s.."P" end
267 if band(mode, 2) ~= 0 then s = s.."F" end
268 if band(mode, 4) ~= 0 then s = s.."T" end
269 if band(mode, 8) ~= 0 then s = s.."C" end
270 if band(mode, 16) ~= 0 then s = s.."R" end
271 if band(mode, 32) ~= 0 then s = s.."I" end
272 t[mode] = s
273 return s
274 end}),
275 ["XLOAD "] = { [0] = "", "R", "V", "RV", "U", "RU", "VU", "RVU", },
276 ["CONV "] = setmetatable({}, { __index = function(t, mode)
277 local s = irtype[band(mode, 31)]
278 s = irtype[band(shr(mode, 5), 31)].."."..s
279 if band(mode, 0x800) ~= 0 then s = s.." sext" end
280 local c = shr(mode, 14)
281 if c == 2 then s = s.." index" elseif c == 3 then s = s.." check" end
282 t[mode] = s
283 return s
284 end}),
285 ["FLOAD "] = vmdef.irfield,
286 ["FREF "] = vmdef.irfield,
287 ["FPMATH"] = vmdef.irfpm,
288 ["BUFHDR"] = { [0] = "RESET", "APPEND" },
289 ["TOSTR "] = { [0] = "INT", "NUM", "CHAR" },
292 local function ctlsub(c)
293 if c == "\n" then return "\\n"
294 elseif c == "\r" then return "\\r"
295 elseif c == "\t" then return "\\t"
296 else return format("\\%03d", byte(c))
300 local function fmtfunc(func, pc)
301 local fi = funcinfo(func, pc)
302 if fi.loc then
303 return fi.loc
304 elseif fi.ffid then
305 return vmdef.ffnames[fi.ffid]
306 elseif fi.addr then
307 return format("C:%x", fi.addr)
308 else
309 return "(?)"
313 local function formatk(tr, idx)
314 local k, t, slot = tracek(tr, idx)
315 local tn = type(k)
316 local s
317 if tn == "number" then
318 if k == 2^52+2^51 then
319 s = "bias"
320 else
321 s = format("%+.14g", k)
323 elseif tn == "string" then
324 s = format(#k > 20 and '"%.20s"~' or '"%s"', gsub(k, "%c", ctlsub))
325 elseif tn == "function" then
326 s = fmtfunc(k)
327 elseif tn == "table" then
328 s = format("{%p}", k)
329 elseif tn == "userdata" then
330 if t == 12 then
331 s = format("userdata:%p", k)
332 else
333 s = format("[%p]", k)
334 if s == "[0x00000000]" then s = "NULL" end
336 elseif t == 21 then -- int64_t
337 s = sub(tostring(k), 1, -3)
338 if sub(s, 1, 1) ~= "-" then s = "+"..s end
339 else
340 s = tostring(k) -- For primitives.
342 s = colorize(format("%-4s", s), t)
343 if slot then
344 s = format("%s @%d", s, slot)
346 return s
349 local function printsnap(tr, snap)
350 local n = 2
351 for s=0,snap[1]-1 do
352 local sn = snap[n]
353 if shr(sn, 24) == s then
354 n = n + 1
355 local ref = band(sn, 0xffff) - 0x8000 -- REF_BIAS
356 if ref < 0 then
357 out:write(formatk(tr, ref))
358 elseif band(sn, 0x80000) ~= 0 then -- SNAP_SOFTFPNUM
359 out:write(colorize(format("%04d/%04d", ref, ref+1), 14))
360 else
361 local m, ot, op1, op2 = traceir(tr, ref)
362 out:write(colorize(format("%04d", ref), band(ot, 31)))
364 out:write(band(sn, 0x10000) == 0 and " " or "|") -- SNAP_FRAME
365 else
366 out:write("---- ")
369 out:write("]\n")
372 -- Dump snapshots (not interleaved with IR).
373 local function dump_snap(tr)
374 out:write("---- TRACE ", tr, " snapshots\n")
375 for i=0,1000000000 do
376 local snap = tracesnap(tr, i)
377 if not snap then break end
378 out:write(format("#%-3d %04d [ ", i, snap[0]))
379 printsnap(tr, snap)
383 -- Return a register name or stack slot for a rid/sp location.
384 local function ridsp_name(ridsp, ins)
385 if not disass then disass = require("jit.dis_"..jit.arch) end
386 local rid, slot = band(ridsp, 0xff), shr(ridsp, 8)
387 if rid == 253 or rid == 254 then
388 return (slot == 0 or slot == 255) and " {sink" or format(" {%04d", ins-slot)
390 if ridsp > 255 then return format("[%x]", slot*4) end
391 if rid < 128 then return disass.regname(rid) end
392 return ""
395 -- Dump CALL* function ref and return optional ctype.
396 local function dumpcallfunc(tr, ins)
397 local ctype
398 if ins > 0 then
399 local m, ot, op1, op2 = traceir(tr, ins)
400 if band(ot, 31) == 0 then -- nil type means CARG(func, ctype).
401 ins = op1
402 ctype = formatk(tr, op2)
405 if ins < 0 then
406 out:write(format("[0x%x](", tonumber((tracek(tr, ins)))))
407 else
408 out:write(format("%04d (", ins))
410 return ctype
413 -- Recursively gather CALL* args and dump them.
414 local function dumpcallargs(tr, ins)
415 if ins < 0 then
416 out:write(formatk(tr, ins))
417 else
418 local m, ot, op1, op2 = traceir(tr, ins)
419 local oidx = 6*shr(ot, 8)
420 local op = sub(vmdef.irnames, oidx+1, oidx+6)
421 if op == "CARG " then
422 dumpcallargs(tr, op1)
423 if op2 < 0 then
424 out:write(" ", formatk(tr, op2))
425 else
426 out:write(" ", format("%04d", op2))
428 else
429 out:write(format("%04d", ins))
434 -- Dump IR and interleaved snapshots.
435 local function dump_ir(tr, dumpsnap, dumpreg)
436 local info = traceinfo(tr)
437 if not info then return end
438 local nins = info.nins
439 out:write("---- TRACE ", tr, " IR\n")
440 local irnames = vmdef.irnames
441 local snapref = 65536
442 local snap, snapno
443 if dumpsnap then
444 snap = tracesnap(tr, 0)
445 snapref = snap[0]
446 snapno = 0
448 for ins=1,nins do
449 if ins >= snapref then
450 if dumpreg then
451 out:write(format(".... SNAP #%-3d [ ", snapno))
452 else
453 out:write(format(".... SNAP #%-3d [ ", snapno))
455 printsnap(tr, snap)
456 snapno = snapno + 1
457 snap = tracesnap(tr, snapno)
458 snapref = snap and snap[0] or 65536
460 local m, ot, op1, op2, ridsp = traceir(tr, ins)
461 local oidx, t = 6*shr(ot, 8), band(ot, 31)
462 local op = sub(irnames, oidx+1, oidx+6)
463 if op == "LOOP " then
464 if dumpreg then
465 out:write(format("%04d ------------ LOOP ------------\n", ins))
466 else
467 out:write(format("%04d ------ LOOP ------------\n", ins))
469 elseif op ~= "NOP " and op ~= "CARG " and
470 (dumpreg or op ~= "RENAME") then
471 local rid = band(ridsp, 255)
472 if dumpreg then
473 out:write(format("%04d %-6s", ins, ridsp_name(ridsp, ins)))
474 else
475 out:write(format("%04d ", ins))
477 out:write(format("%s%s %s %s ",
478 (rid == 254 or rid == 253) and "}" or
479 (band(ot, 128) == 0 and " " or ">"),
480 band(ot, 64) == 0 and " " or "+",
481 irtype[t], op))
482 local m1, m2 = band(m, 3), band(m, 3*4)
483 if sub(op, 1, 4) == "CALL" then
484 local ctype
485 if m2 == 1*4 then -- op2 == IRMlit
486 out:write(format("%-10s (", vmdef.ircall[op2]))
487 else
488 ctype = dumpcallfunc(tr, op2)
490 if op1 ~= -1 then dumpcallargs(tr, op1) end
491 out:write(")")
492 if ctype then out:write(" ctype ", ctype) end
493 elseif op == "CNEW " and op2 == -1 then
494 out:write(formatk(tr, op1))
495 elseif m1 ~= 3 then -- op1 != IRMnone
496 if op1 < 0 then
497 out:write(formatk(tr, op1))
498 else
499 out:write(format(m1 == 0 and "%04d" or "#%-3d", op1))
501 if m2 ~= 3*4 then -- op2 != IRMnone
502 if m2 == 1*4 then -- op2 == IRMlit
503 local litn = litname[op]
504 if litn and litn[op2] then
505 out:write(" ", litn[op2])
506 elseif op == "UREFO " or op == "UREFC " then
507 out:write(format(" #%-3d", shr(op2, 8)))
508 else
509 out:write(format(" #%-3d", op2))
511 elseif op2 < 0 then
512 out:write(" ", formatk(tr, op2))
513 else
514 out:write(format(" %04d", op2))
518 out:write("\n")
521 if snap then
522 if dumpreg then
523 out:write(format(".... SNAP #%-3d [ ", snapno))
524 else
525 out:write(format(".... SNAP #%-3d [ ", snapno))
527 printsnap(tr, snap)
531 ------------------------------------------------------------------------------
533 local recprefix = ""
534 local recdepth = 0
536 -- Format trace error message.
537 local function fmterr(err, info)
538 if type(err) == "number" then
539 if type(info) == "function" then info = fmtfunc(info) end
540 err = format(vmdef.traceerr[err], info)
542 return err
545 -- Dump trace states.
546 local function dump_trace(what, tr, func, pc, otr, oex)
547 if what == "stop" or (what == "abort" and dumpmode.a) then
548 if dumpmode.i then dump_ir(tr, dumpmode.s, dumpmode.r and what == "stop")
549 elseif dumpmode.s then dump_snap(tr) end
550 if dumpmode.m then dump_mcode(tr) end
552 if what == "start" then
553 if dumpmode.H then out:write('<pre class="ljdump">\n') end
554 out:write("---- TRACE ", tr, " ", what)
555 if otr then out:write(" ", otr, "/", oex) end
556 out:write(" ", fmtfunc(func, pc), "\n")
557 elseif what == "stop" or what == "abort" then
558 out:write("---- TRACE ", tr, " ", what)
559 if what == "abort" then
560 out:write(" ", fmtfunc(func, pc), " -- ", fmterr(otr, oex), "\n")
561 else
562 local info = traceinfo(tr)
563 local link, ltype = info.link, info.linktype
564 if link == tr or link == 0 then
565 out:write(" -> ", ltype, "\n")
566 elseif ltype == "root" then
567 out:write(" -> ", link, "\n")
568 else
569 out:write(" -> ", link, " ", ltype, "\n")
572 if dumpmode.H then out:write("</pre>\n\n") else out:write("\n") end
573 else
574 out:write("---- TRACE ", what, "\n\n")
576 out:flush()
579 -- Dump recorded bytecode.
580 local function dump_record(tr, func, pc, depth, callee)
581 if depth ~= recdepth then
582 recdepth = depth
583 recprefix = rep(" .", depth)
585 local line
586 if pc >= 0 then
587 line = bcline(func, pc, recprefix)
588 if dumpmode.H then line = gsub(line, "[<>&]", html_escape) end
589 else
590 line = "0000 "..recprefix.." FUNCC \n"
591 callee = func
593 if pc <= 0 then
594 out:write(sub(line, 1, -2), " ; ", fmtfunc(func), "\n")
595 else
596 out:write(line)
598 if pc >= 0 and band(funcbc(func, pc), 0xff) < 16 then -- ORDER BC
599 out:write(bcline(func, pc+1, recprefix)) -- Write JMP for cond.
603 ------------------------------------------------------------------------------
605 -- Dump taken trace exits.
606 local function dump_texit(tr, ex, ngpr, nfpr, ...)
607 out:write("---- TRACE ", tr, " exit ", ex, "\n")
608 if dumpmode.X then
609 local regs = {...}
610 if jit.arch == "x64" then
611 for i=1,ngpr do
612 out:write(format(" %016x", regs[i]))
613 if i % 4 == 0 then out:write("\n") end
615 else
616 for i=1,ngpr do
617 out:write(" ", tohex(regs[i]))
618 if i % 8 == 0 then out:write("\n") end
621 if jit.arch == "mips" or jit.arch == "mipsel" then
622 for i=1,nfpr,2 do
623 out:write(format(" %+17.14g", regs[ngpr+i]))
624 if i % 8 == 7 then out:write("\n") end
626 else
627 for i=1,nfpr do
628 out:write(format(" %+17.14g", regs[ngpr+i]))
629 if i % 4 == 0 then out:write("\n") end
635 ------------------------------------------------------------------------------
637 -- Detach dump handlers.
638 local function dumpoff()
639 if active then
640 active = false
641 jit.attach(dump_texit)
642 jit.attach(dump_record)
643 jit.attach(dump_trace)
644 if out and out ~= stdout and out ~= stderr then out:close() end
645 out = nil
649 -- Open the output file and attach dump handlers.
650 local function dumpon(opt, outfile)
651 if active then dumpoff() end
653 local colormode = os.getenv("COLORTERM") and "A" or "T"
654 if opt then
655 opt = gsub(opt, "[TAH]", function(mode) colormode = mode; return ""; end)
658 local m = { t=true, b=true, i=true, m=true, }
659 if opt and opt ~= "" then
660 local o = sub(opt, 1, 1)
661 if o ~= "+" and o ~= "-" then m = {} end
662 for i=1,#opt do m[sub(opt, i, i)] = (o ~= "-") end
664 dumpmode = m
666 if m.t or m.b or m.i or m.s or m.m then
667 jit.attach(dump_trace, "trace")
669 if m.b then
670 jit.attach(dump_record, "record")
671 if not bcline then bcline = require("jit.bc").line end
673 if m.x or m.X then
674 jit.attach(dump_texit, "texit")
677 if not outfile then outfile = os.getenv("LUAJIT_DUMPFILE") end
678 if outfile then
679 out = outfile == "-" and stdout or assert(io.open(outfile, "w"))
680 else
681 out = stdout
684 m[colormode] = true
685 if colormode == "A" then
686 colorize = colorize_ansi
687 irtype = irtype_ansi
688 elseif colormode == "H" then
689 colorize = colorize_html
690 irtype = irtype_html
691 out:write(header_html)
692 else
693 colorize = colorize_text
694 irtype = irtype_text
697 active = true
700 -- Public module functions.
701 return {
702 on = dumpon,
703 off = dumpoff,
704 start = dumpon -- For -j command line option.