FFI: Fix symbol resolving error messages on Windows.
[luajit-2.0.git] / lib / dump.lua
blob3d62c4eae6214a7ac480dcab95a1f6393d5b5997
1 ----------------------------------------------------------------------------
2 -- LuaJIT compiler dump module.
3 --
4 -- Copyright (C) 2005-2012 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 if jit.arch == "mips" or jit.arch == "mipsel" then
88 t[traceexitstub(tr, 0)] = "exit"
89 return
90 end
91 for i=0,nexit-1 do
92 local addr = traceexitstub(tr, i)
93 t[addr] = tostring(i)
94 end
95 local addr = traceexitstub(tr, nexit)
96 if addr then t[addr] = "stack_check" end
97 end
99 -- Fill symbol table with trace exit stub addresses.
100 local function fillsymtab(tr, nexit)
101 local t = symtab
102 if nexitsym == 0 then
103 local ircall = vmdef.ircall
104 for i=0,#ircall do
105 local addr = ircalladdr(i)
106 if addr ~= 0 then t[addr] = ircall[i] end
109 if nexitsym == 1000000 then -- Per-trace exit stubs.
110 fillsymtab_tr(tr, nexit)
111 elseif nexit > nexitsym then -- Shared exit stubs.
112 for i=nexitsym,nexit-1 do
113 local addr = traceexitstub(i)
114 if addr == nil then -- Fall back to per-trace exit stubs.
115 fillsymtab_tr(tr, nexit)
116 setmetatable(symtab, symtabmt)
117 nexit = 1000000
118 break
120 t[addr] = tostring(i)
122 nexitsym = nexit
124 return t
127 local function dumpwrite(s)
128 out:write(s)
131 -- Disassemble machine code.
132 local function dump_mcode(tr)
133 local info = traceinfo(tr)
134 if not info then return end
135 local mcode, addr, loop = tracemc(tr)
136 if not mcode then return end
137 if not disass then disass = require("jit.dis_"..jit.arch) end
138 out:write("---- TRACE ", tr, " mcode ", #mcode, "\n")
139 local ctx = disass.create(mcode, addr, dumpwrite)
140 ctx.hexdump = 0
141 ctx.symtab = fillsymtab(tr, info.nexit)
142 if loop ~= 0 then
143 symtab[addr+loop] = "LOOP"
144 ctx:disass(0, loop)
145 out:write("->LOOP:\n")
146 ctx:disass(loop, #mcode-loop)
147 symtab[addr+loop] = nil
148 else
149 ctx:disass(0, #mcode)
153 ------------------------------------------------------------------------------
155 local irtype_text = {
156 [0] = "nil",
157 "fal",
158 "tru",
159 "lud",
160 "str",
161 "p32",
162 "thr",
163 "pro",
164 "fun",
165 "p64",
166 "cdt",
167 "tab",
168 "udt",
169 "flt",
170 "num",
171 "i8 ",
172 "u8 ",
173 "i16",
174 "u16",
175 "int",
176 "u32",
177 "i64",
178 "u64",
179 "sfp",
182 local colortype_ansi = {
183 [0] = "%s",
184 "%s",
185 "%s",
186 "\027[36m%s\027[m",
187 "\027[32m%s\027[m",
188 "%s",
189 "\027[1m%s\027[m",
190 "%s",
191 "\027[1m%s\027[m",
192 "%s",
193 "\027[33m%s\027[m",
194 "\027[31m%s\027[m",
195 "\027[36m%s\027[m",
196 "\027[34m%s\027[m",
197 "\027[34m%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",
203 "\027[35m%s\027[m",
204 "\027[35m%s\027[m",
205 "\027[35m%s\027[m",
206 "\027[35m%s\027[m",
209 local function colorize_text(s, t)
210 return s
213 local function colorize_ansi(s, t)
214 return format(colortype_ansi[t], s)
217 local irtype_ansi = setmetatable({},
218 { __index = function(tab, t)
219 local s = colorize_ansi(irtype_text[t], t); tab[t] = s; return s; end })
221 local html_escape = { ["<"] = "&lt;", [">"] = "&gt;", ["&"] = "&amp;", }
223 local function colorize_html(s, t)
224 s = gsub(s, "[<>&]", html_escape)
225 return format('<span class="irt_%s">%s</span>', irtype_text[t], s)
228 local irtype_html = setmetatable({},
229 { __index = function(tab, t)
230 local s = colorize_html(irtype_text[t], t); tab[t] = s; return s; end })
232 local header_html = [[
233 <style type="text/css">
234 background { background: #ffffff; color: #000000; }
235 pre.ljdump {
236 font-size: 10pt;
237 background: #f0f4ff;
238 color: #000000;
239 border: 1px solid #bfcfff;
240 padding: 0.5em;
241 margin-left: 2em;
242 margin-right: 2em;
244 span.irt_str { color: #00a000; }
245 span.irt_thr, span.irt_fun { color: #404040; font-weight: bold; }
246 span.irt_tab { color: #c00000; }
247 span.irt_udt, span.irt_lud { color: #00c0c0; }
248 span.irt_num { color: #4040c0; }
249 span.irt_int, span.irt_i8, span.irt_u8, span.irt_i16, span.irt_u16 { color: #b040b0; }
250 </style>
253 local colorize, irtype
255 -- Lookup tables to convert some literals into names.
256 local litname = {
257 ["SLOAD "] = setmetatable({}, { __index = function(t, mode)
258 local s = ""
259 if band(mode, 1) ~= 0 then s = s.."P" end
260 if band(mode, 2) ~= 0 then s = s.."F" end
261 if band(mode, 4) ~= 0 then s = s.."T" end
262 if band(mode, 8) ~= 0 then s = s.."C" end
263 if band(mode, 16) ~= 0 then s = s.."R" end
264 if band(mode, 32) ~= 0 then s = s.."I" end
265 t[mode] = s
266 return s
267 end}),
268 ["XLOAD "] = { [0] = "", "R", "V", "RV", "U", "RU", "VU", "RVU", },
269 ["CONV "] = setmetatable({}, { __index = function(t, mode)
270 local s = irtype[band(mode, 31)]
271 s = irtype[band(shr(mode, 5), 31)].."."..s
272 if band(mode, 0x400) ~= 0 then s = s.." trunc"
273 elseif band(mode, 0x800) ~= 0 then s = s.." sext" end
274 local c = shr(mode, 14)
275 if c == 2 then s = s.." index" elseif c == 3 then s = s.." check" end
276 t[mode] = s
277 return s
278 end}),
279 ["FLOAD "] = vmdef.irfield,
280 ["FREF "] = vmdef.irfield,
281 ["FPMATH"] = vmdef.irfpm,
284 local function ctlsub(c)
285 if c == "\n" then return "\\n"
286 elseif c == "\r" then return "\\r"
287 elseif c == "\t" then return "\\t"
288 elseif c == "\r" then return "\\r"
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)
378 if not disass then disass = require("jit.dis_"..jit.arch) end
379 local rid = band(ridsp, 0xff)
380 if ridsp > 255 then return format("[%x]", shr(ridsp, 8)*4) end
381 if rid < 128 then return disass.regname(rid) end
382 return ""
385 -- Dump CALL* function ref and return optional ctype.
386 local function dumpcallfunc(tr, ins)
387 local ctype
388 if ins > 0 then
389 local m, ot, op1, op2 = traceir(tr, ins)
390 if band(ot, 31) == 0 then -- nil type means CARG(func, ctype).
391 ins = op1
392 ctype = formatk(tr, op2)
395 if ins < 0 then
396 out:write(format("[0x%x](", tonumber((tracek(tr, ins)))))
397 else
398 out:write(format("%04d (", ins))
400 return ctype
403 -- Recursively gather CALL* args and dump them.
404 local function dumpcallargs(tr, ins)
405 if ins < 0 then
406 out:write(formatk(tr, ins))
407 else
408 local m, ot, op1, op2 = traceir(tr, ins)
409 local oidx = 6*shr(ot, 8)
410 local op = sub(vmdef.irnames, oidx+1, oidx+6)
411 if op == "CARG " then
412 dumpcallargs(tr, op1)
413 if op2 < 0 then
414 out:write(" ", formatk(tr, op2))
415 else
416 out:write(" ", format("%04d", op2))
418 else
419 out:write(format("%04d", ins))
424 -- Dump IR and interleaved snapshots.
425 local function dump_ir(tr, dumpsnap, dumpreg)
426 local info = traceinfo(tr)
427 if not info then return end
428 local nins = info.nins
429 out:write("---- TRACE ", tr, " IR\n")
430 local irnames = vmdef.irnames
431 local snapref = 65536
432 local snap, snapno
433 if dumpsnap then
434 snap = tracesnap(tr, 0)
435 snapref = snap[0]
436 snapno = 0
438 for ins=1,nins do
439 if ins >= snapref then
440 if dumpreg then
441 out:write(format(".... SNAP #%-3d [ ", snapno))
442 else
443 out:write(format(".... SNAP #%-3d [ ", snapno))
445 printsnap(tr, snap)
446 snapno = snapno + 1
447 snap = tracesnap(tr, snapno)
448 snapref = snap and snap[0] or 65536
450 local m, ot, op1, op2, ridsp = traceir(tr, ins)
451 local oidx, t = 6*shr(ot, 8), band(ot, 31)
452 local op = sub(irnames, oidx+1, oidx+6)
453 if op == "LOOP " then
454 if dumpreg then
455 out:write(format("%04d ------------ LOOP ------------\n", ins))
456 else
457 out:write(format("%04d ------ LOOP ------------\n", ins))
459 elseif op ~= "NOP " and op ~= "CARG " and
460 (dumpreg or op ~= "RENAME") then
461 if dumpreg then
462 out:write(format("%04d %-5s ", ins, ridsp_name(ridsp)))
463 else
464 out:write(format("%04d ", ins))
466 out:write(format("%s%s %s %s ",
467 band(ot, 128) == 0 and " " or ">",
468 band(ot, 64) == 0 and " " or "+",
469 irtype[t], op))
470 local m1, m2 = band(m, 3), band(m, 3*4)
471 if sub(op, 1, 4) == "CALL" then
472 local ctype
473 if m2 == 1*4 then -- op2 == IRMlit
474 out:write(format("%-10s (", vmdef.ircall[op2]))
475 else
476 ctype = dumpcallfunc(tr, op2)
478 if op1 ~= -1 then dumpcallargs(tr, op1) end
479 out:write(")")
480 if ctype then out:write(" ctype ", ctype) end
481 elseif op == "CNEW " and op2 == -1 then
482 out:write(formatk(tr, op1))
483 elseif m1 ~= 3 then -- op1 != IRMnone
484 if op1 < 0 then
485 out:write(formatk(tr, op1))
486 else
487 out:write(format(m1 == 0 and "%04d" or "#%-3d", op1))
489 if m2 ~= 3*4 then -- op2 != IRMnone
490 if m2 == 1*4 then -- op2 == IRMlit
491 local litn = litname[op]
492 if litn and litn[op2] then
493 out:write(" ", litn[op2])
494 elseif op == "UREFO " or op == "UREFC " then
495 out:write(format(" #%-3d", shr(op2, 8)))
496 else
497 out:write(format(" #%-3d", op2))
499 elseif op2 < 0 then
500 out:write(" ", formatk(tr, op2))
501 else
502 out:write(format(" %04d", op2))
506 out:write("\n")
509 if snap then
510 if dumpreg then
511 out:write(format(".... SNAP #%-3d [ ", snapno))
512 else
513 out:write(format(".... SNAP #%-3d [ ", snapno))
515 printsnap(tr, snap)
519 ------------------------------------------------------------------------------
521 local recprefix = ""
522 local recdepth = 0
524 -- Format trace error message.
525 local function fmterr(err, info)
526 if type(err) == "number" then
527 if type(info) == "function" then info = fmtfunc(info) end
528 err = format(vmdef.traceerr[err], info)
530 return err
533 -- Dump trace states.
534 local function dump_trace(what, tr, func, pc, otr, oex)
535 if what == "stop" or (what == "abort" and dumpmode.a) then
536 if dumpmode.i then dump_ir(tr, dumpmode.s, dumpmode.r and what == "stop")
537 elseif dumpmode.s then dump_snap(tr) end
538 if dumpmode.m then dump_mcode(tr) end
540 if what == "start" then
541 if dumpmode.H then out:write('<pre class="ljdump">\n') end
542 out:write("---- TRACE ", tr, " ", what)
543 if otr then out:write(" ", otr, "/", oex) end
544 out:write(" ", fmtfunc(func, pc), "\n")
545 recprefix = ""
546 elseif what == "stop" or what == "abort" then
547 out:write("---- TRACE ", tr, " ", what)
548 recprefix = nil
549 if what == "abort" then
550 out:write(" ", fmtfunc(func, pc), " -- ", fmterr(otr, oex), "\n")
551 else
552 local info = traceinfo(tr)
553 local link, ltype = info.link, info.linktype
554 if link == tr or link == 0 then
555 out:write(" -> ", ltype, "\n")
556 elseif ltype == "root" then
557 out:write(" -> ", link, "\n")
558 else
559 out:write(" -> ", link, " ", ltype, "\n")
562 if dumpmode.H then out:write("</pre>\n\n") else out:write("\n") end
563 else
564 out:write("---- TRACE ", what, "\n\n")
566 out:flush()
569 -- Dump recorded bytecode.
570 local function dump_record(tr, func, pc, depth, callee)
571 if depth ~= recdepth then
572 recdepth = depth
573 recprefix = rep(" .", depth)
575 local line
576 if pc >= 0 then
577 line = bcline(func, pc, recprefix)
578 if dumpmode.H then line = gsub(line, "[<>&]", html_escape) end
579 else
580 line = "0000 "..recprefix.." FUNCC \n"
581 callee = func
583 if pc <= 0 then
584 out:write(sub(line, 1, -2), " ; ", fmtfunc(func), "\n")
585 else
586 out:write(line)
588 if pc >= 0 and band(funcbc(func, pc), 0xff) < 16 then -- ORDER BC
589 out:write(bcline(func, pc+1, recprefix)) -- Write JMP for cond.
593 ------------------------------------------------------------------------------
595 -- Dump taken trace exits.
596 local function dump_texit(tr, ex, ngpr, nfpr, ...)
597 out:write("---- TRACE ", tr, " exit ", ex, "\n")
598 if dumpmode.X then
599 local regs = {...}
600 if jit.arch == "x64" then
601 for i=1,ngpr do
602 out:write(format(" %016x", regs[i]))
603 if i % 4 == 0 then out:write("\n") end
605 else
606 for i=1,ngpr do
607 out:write(format(" %08x", regs[i]))
608 if i % 8 == 0 then out:write("\n") end
611 if jit.arch == "mips" or jit.arch == "mipsel" then
612 for i=1,nfpr,2 do
613 out:write(format(" %+17.14g", regs[ngpr+i]))
614 if i % 8 == 7 then out:write("\n") end
616 else
617 for i=1,nfpr do
618 out:write(format(" %+17.14g", regs[ngpr+i]))
619 if i % 4 == 0 then out:write("\n") end
625 ------------------------------------------------------------------------------
627 -- Detach dump handlers.
628 local function dumpoff()
629 if active then
630 active = false
631 jit.attach(dump_texit)
632 jit.attach(dump_record)
633 jit.attach(dump_trace)
634 if out and out ~= stdout and out ~= stderr then out:close() end
635 out = nil
639 -- Open the output file and attach dump handlers.
640 local function dumpon(opt, outfile)
641 if active then dumpoff() end
643 local colormode = os.getenv("COLORTERM") and "A" or "T"
644 if opt then
645 opt = gsub(opt, "[TAH]", function(mode) colormode = mode; return ""; end)
648 local m = { t=true, b=true, i=true, m=true, }
649 if opt and opt ~= "" then
650 local o = sub(opt, 1, 1)
651 if o ~= "+" and o ~= "-" then m = {} end
652 for i=1,#opt do m[sub(opt, i, i)] = (o ~= "-") end
654 dumpmode = m
656 if m.t or m.b or m.i or m.s or m.m then
657 jit.attach(dump_trace, "trace")
659 if m.b then
660 jit.attach(dump_record, "record")
661 if not bcline then bcline = require("jit.bc").line end
663 if m.x or m.X then
664 jit.attach(dump_texit, "texit")
667 if not outfile then outfile = os.getenv("LUAJIT_DUMPFILE") end
668 if outfile then
669 out = outfile == "-" and stdout or assert(io.open(outfile, "w"))
670 else
671 out = stdout
674 m[colormode] = true
675 if colormode == "A" then
676 colorize = colorize_ansi
677 irtype = irtype_ansi
678 elseif colormode == "H" then
679 colorize = colorize_html
680 irtype = irtype_html
681 out:write(header_html)
682 else
683 colorize = colorize_text
684 irtype = irtype_text
687 active = true
690 -- Public module functions.
691 module(...)
693 on = dumpon
694 off = dumpoff
695 start = dumpon -- For -j command line option.