Fix display of NULL (light)userdata in -jdump.
[luajit-2.0.git] / src / jit / dump.lua
blobec5f82761da2644599e02b5d7d050705f44fc84e
1 ----------------------------------------------------------------------------
2 -- LuaJIT compiler dump 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 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 == 20004, "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 == "[NULL]" 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 elseif what == "stop" or what == "abort" then
551 out:write("---- TRACE ", tr, " ", what)
552 if what == "abort" then
553 out:write(" ", fmtfunc(func, pc), " -- ", fmterr(otr, oex), "\n")
554 else
555 local info = traceinfo(tr)
556 local link, ltype = info.link, info.linktype
557 if link == tr or link == 0 then
558 out:write(" -> ", ltype, "\n")
559 elseif ltype == "root" then
560 out:write(" -> ", link, "\n")
561 else
562 out:write(" -> ", link, " ", ltype, "\n")
565 if dumpmode.H then out:write("</pre>\n\n") else out:write("\n") end
566 else
567 if what == "flush" then symtab, nexitsym = {}, 0 end
568 out:write("---- TRACE ", what, "\n\n")
570 out:flush()
573 -- Dump recorded bytecode.
574 local function dump_record(tr, func, pc, depth, callee)
575 if depth ~= recdepth then
576 recdepth = depth
577 recprefix = rep(" .", depth)
579 local line
580 if pc >= 0 then
581 line = bcline(func, pc, recprefix)
582 if dumpmode.H then line = gsub(line, "[<>&]", html_escape) end
583 else
584 line = "0000 "..recprefix.." FUNCC \n"
585 callee = func
587 if pc <= 0 then
588 out:write(sub(line, 1, -2), " ; ", fmtfunc(func), "\n")
589 else
590 out:write(line)
592 if pc >= 0 and band(funcbc(func, pc), 0xff) < 16 then -- ORDER BC
593 out:write(bcline(func, pc+1, recprefix)) -- Write JMP for cond.
597 ------------------------------------------------------------------------------
599 -- Dump taken trace exits.
600 local function dump_texit(tr, ex, ngpr, nfpr, ...)
601 out:write("---- TRACE ", tr, " exit ", ex, "\n")
602 if dumpmode.X then
603 local regs = {...}
604 if jit.arch == "x64" then
605 for i=1,ngpr do
606 out:write(format(" %016x", regs[i]))
607 if i % 4 == 0 then out:write("\n") end
609 else
610 for i=1,ngpr do
611 out:write(format(" %08x", regs[i]))
612 if i % 8 == 0 then out:write("\n") end
615 if jit.arch == "mips" or jit.arch == "mipsel" then
616 for i=1,nfpr,2 do
617 out:write(format(" %+17.14g", regs[ngpr+i]))
618 if i % 8 == 7 then out:write("\n") end
620 else
621 for i=1,nfpr do
622 out:write(format(" %+17.14g", regs[ngpr+i]))
623 if i % 4 == 0 then out:write("\n") end
629 ------------------------------------------------------------------------------
631 -- Detach dump handlers.
632 local function dumpoff()
633 if active then
634 active = false
635 jit.attach(dump_texit)
636 jit.attach(dump_record)
637 jit.attach(dump_trace)
638 if out and out ~= stdout and out ~= stderr then out:close() end
639 out = nil
643 -- Open the output file and attach dump handlers.
644 local function dumpon(opt, outfile)
645 if active then dumpoff() end
647 local colormode = os.getenv("COLORTERM") and "A" or "T"
648 if opt then
649 opt = gsub(opt, "[TAH]", function(mode) colormode = mode; return ""; end)
652 local m = { t=true, b=true, i=true, m=true, }
653 if opt and opt ~= "" then
654 local o = sub(opt, 1, 1)
655 if o ~= "+" and o ~= "-" then m = {} end
656 for i=1,#opt do m[sub(opt, i, i)] = (o ~= "-") end
658 dumpmode = m
660 if m.t or m.b or m.i or m.s or m.m then
661 jit.attach(dump_trace, "trace")
663 if m.b then
664 jit.attach(dump_record, "record")
665 if not bcline then bcline = require("jit.bc").line end
667 if m.x or m.X then
668 jit.attach(dump_texit, "texit")
671 if not outfile then outfile = os.getenv("LUAJIT_DUMPFILE") end
672 if outfile then
673 out = outfile == "-" and stdout or assert(io.open(outfile, "w"))
674 else
675 out = stdout
678 m[colormode] = true
679 if colormode == "A" then
680 colorize = colorize_ansi
681 irtype = irtype_ansi
682 elseif colormode == "H" then
683 colorize = colorize_html
684 irtype = irtype_html
685 out:write(header_html)
686 else
687 colorize = colorize_text
688 irtype = irtype_text
691 active = true
694 -- Public module functions.
695 module(...)
697 on = dumpon
698 off = dumpoff
699 start = dumpon -- For -j command line option.