aboutsummaryrefslogtreecommitdiff
path: root/lib/dump.lua
diff options
context:
space:
mode:
Diffstat (limited to 'lib/dump.lua')
-rw-r--r--lib/dump.lua696
1 files changed, 0 insertions, 696 deletions
diff --git a/lib/dump.lua b/lib/dump.lua
deleted file mode 100644
index 3d62c4ea..00000000
--- a/lib/dump.lua
+++ /dev/null
@@ -1,696 +0,0 @@
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.
10--
11-- Example usage:
12--
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
19--
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.
24--
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.
28--
29-- The following dump features are available (* marks the default):
30--
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--
40-- The output format can be set with the following characters:
41--
42-- T Plain text output.
43-- A ANSI-colored text output
44-- H Colorized HTML + CSS output.
45--
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.
49--
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"
52--
53------------------------------------------------------------------------------
54
55-- Cache some library functions and objects.
56local jit = require("jit")
57assert(jit.version_num == 20000, "LuaJIT core/library version mismatch")
58local jutil = require("jit.util")
59local vmdef = require("jit.vmdef")
60local funcinfo, funcbc = jutil.funcinfo, jutil.funcbc
61local traceinfo, traceir, tracek = jutil.traceinfo, jutil.traceir, jutil.tracek
62local tracemc, tracesnap = jutil.tracemc, jutil.tracesnap
63local traceexitstub, ircalladdr = jutil.traceexitstub, jutil.ircalladdr
64local bit = require("bit")
65local band, shl, shr = bit.band, bit.lshift, bit.rshift
66local sub, gsub, format = string.sub, string.gsub, string.format
67local byte, char, rep = string.byte, string.char, string.rep
68local type, tostring = type, tostring
69local stdout, stderr = io.stdout, io.stderr
70
71-- Load other modules on-demand.
72local bcline, disass
73
74-- Active flag, output file handle and dump mode.
75local active, out, dumpmode
76
77------------------------------------------------------------------------------
78
79local symtabmt = { __index = false }
80local symtab = {}
81local nexitsym = 0
82
83-- Fill nested symbol table with per-trace exit stub addresses.
84local 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
97end
98
99-- Fill symbol table with trace exit stub addresses.
100local 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
107 end
108 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
119 end
120 t[addr] = tostring(i)
121 end
122 nexitsym = nexit
123 end
124 return t
125end
126
127local function dumpwrite(s)
128 out:write(s)
129end
130
131-- Disassemble machine code.
132local 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)
150 end
151end
152
153------------------------------------------------------------------------------
154
155local 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",
180}
181
182local 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",
207}
208
209local function colorize_text(s, t)
210 return s
211end
212
213local function colorize_ansi(s, t)
214 return format(colortype_ansi[t], s)
215end
216
217local irtype_ansi = setmetatable({},
218 { __index = function(tab, t)
219 local s = colorize_ansi(irtype_text[t], t); tab[t] = s; return s; end })
220
221local html_escape = { ["<"] = "&lt;", [">"] = "&gt;", ["&"] = "&amp;", }
222
223local function colorize_html(s, t)
224 s = gsub(s, "[<>&]", html_escape)
225 return format('<span class="irt_%s">%s</span>', irtype_text[t], s)
226end
227
228local irtype_html = setmetatable({},
229 { __index = function(tab, t)
230 local s = colorize_html(irtype_text[t], t); tab[t] = s; return s; end })
231
232local header_html = [[
233<style type="text/css">
234background { background: #ffffff; color: #000000; }
235pre.ljdump {
236font-size: 10pt;
237background: #f0f4ff;
238color: #000000;
239border: 1px solid #bfcfff;
240padding: 0.5em;
241margin-left: 2em;
242margin-right: 2em;
243}
244span.irt_str { color: #00a000; }
245span.irt_thr, span.irt_fun { color: #404040; font-weight: bold; }
246span.irt_tab { color: #c00000; }
247span.irt_udt, span.irt_lud { color: #00c0c0; }
248span.irt_num { color: #4040c0; }
249span.irt_int, span.irt_i8, span.irt_u8, span.irt_i16, span.irt_u16 { color: #b040b0; }
250</style>
251]]
252
253local colorize, irtype
254
255-- Lookup tables to convert some literals into names.
256local 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,
282}
283
284local 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))
290 end
291end
292
293local 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 "(?)"
303 end
304end
305
306local 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)
315 end
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
328 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.
334 end
335 s = colorize(format("%-4s", s), t)
336 if slot then
337 s = format("%s @%d", s, slot)
338 end
339 return s
340end
341
342local 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)))
356 end
357 out:write(band(sn, 0x10000) == 0 and " " or "|") -- SNAP_FRAME
358 else
359 out:write("---- ")
360 end
361 end
362 out:write("]\n")
363end
364
365-- Dump snapshots (not interleaved with IR).
366local 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)
373 end
374end
375
376-- Return a register name or stack slot for a rid/sp location.
377local 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 ""
383end
384
385-- Dump CALL* function ref and return optional ctype.
386local 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)
393 end
394 end
395 if ins < 0 then
396 out:write(format("[0x%x](", tonumber((tracek(tr, ins)))))
397 else
398 out:write(format("%04d (", ins))
399 end
400 return ctype
401end
402
403-- Recursively gather CALL* args and dump them.
404local 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))
417 end
418 else
419 out:write(format("%04d", ins))
420 end
421 end
422end
423
424-- Dump IR and interleaved snapshots.
425local 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
437 end
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))
444 end
445 printsnap(tr, snap)
446 snapno = snapno + 1
447 snap = tracesnap(tr, snapno)
448 snapref = snap and snap[0] or 65536
449 end
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))
458 end
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))
465 end
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)
477 end
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))
488 end
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))
498 end
499 elseif op2 < 0 then
500 out:write(" ", formatk(tr, op2))
501 else
502 out:write(format(" %04d", op2))
503 end
504 end
505 end
506 out:write("\n")
507 end
508 end
509 if snap then
510 if dumpreg then
511 out:write(format(".... SNAP #%-3d [ ", snapno))
512 else
513 out:write(format(".... SNAP #%-3d [ ", snapno))
514 end
515 printsnap(tr, snap)
516 end
517end
518
519------------------------------------------------------------------------------
520
521local recprefix = ""
522local recdepth = 0
523
524-- Format trace error message.
525local 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)
529 end
530 return err
531end
532
533-- Dump trace states.
534local 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
539 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")
560 end
561 end
562 if dumpmode.H then out:write("</pre>\n\n") else out:write("\n") end
563 else
564 out:write("---- TRACE ", what, "\n\n")
565 end
566 out:flush()
567end
568
569-- Dump recorded bytecode.
570local function dump_record(tr, func, pc, depth, callee)
571 if depth ~= recdepth then
572 recdepth = depth
573 recprefix = rep(" .", depth)
574 end
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
582 end
583 if pc <= 0 then
584 out:write(sub(line, 1, -2), " ; ", fmtfunc(func), "\n")
585 else
586 out:write(line)
587 end
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.
590 end
591end
592
593------------------------------------------------------------------------------
594
595-- Dump taken trace exits.
596local 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
604 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
609 end
610 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
615 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
620 end
621 end
622 end
623end
624
625------------------------------------------------------------------------------
626
627-- Detach dump handlers.
628local 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
636 end
637end
638
639-- Open the output file and attach dump handlers.
640local function dumpon(opt, outfile)
641 if active then dumpoff() end
642
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)
646 end
647
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
653 end
654 dumpmode = m
655
656 if m.t or m.b or m.i or m.s or m.m then
657 jit.attach(dump_trace, "trace")
658 end
659 if m.b then
660 jit.attach(dump_record, "record")
661 if not bcline then bcline = require("jit.bc").line end
662 end
663 if m.x or m.X then
664 jit.attach(dump_texit, "texit")
665 end
666
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
672 end
673
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
685 end
686
687 active = true
688end
689
690-- Public module functions.
691module(...)
692
693on = dumpon
694off = dumpoff
695start = dumpon -- For -j command line option.
696