summaryrefslogtreecommitdiff
path: root/lib/dump.lua
diff options
context:
space:
mode:
Diffstat (limited to 'lib/dump.lua')
-rw-r--r--lib/dump.lua567
1 files changed, 567 insertions, 0 deletions
diff --git a/lib/dump.lua b/lib/dump.lua
new file mode 100644
index 00000000..9fde87c1
--- /dev/null
+++ b/lib/dump.lua
@@ -0,0 +1,567 @@
1----------------------------------------------------------------------------
2-- LuaJIT compiler dump module.
3--
4-- Copyright (C) 2005-2009 Mike Pall. All rights reserved.
5-- Released under the MIT/X 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, traceexitstub = jutil.tracemc, jutil.traceexitstub
63local tracesnap = jutil.tracesnap
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, discreate
73
74-- Active flag, output file handle and dump mode.
75local active, out, dumpmode
76
77------------------------------------------------------------------------------
78
79local symtab = {}
80local nexitsym = 0
81
82-- Fill symbol table with trace exit addresses.
83local function fillsymtab(nexit)
84 local t = symtab
85 if nexit > nexitsym then
86 for i=nexitsym,nexit-1 do t[traceexitstub(i)] = tostring(i) end
87 nexitsym = nexit
88 end
89 return t
90end
91
92local function dumpwrite(s)
93 out:write(s)
94end
95
96-- Disassemble machine code.
97local function dump_mcode(tr)
98 local info = traceinfo(tr)
99 if not info then return end
100 local mcode, addr, loop = tracemc(tr)
101 if not mcode then return end
102 if not discreate then
103 discreate = require("jit.dis_"..jit.arch).create
104 end
105 out:write("---- TRACE ", tr, " mcode ", #mcode, "\n")
106 local ctx = discreate(mcode, addr, dumpwrite)
107 ctx.hexdump = 0
108 ctx.symtab = fillsymtab(info.nexit)
109 if loop ~= 0 then
110 symtab[addr+loop] = "LOOP"
111 ctx:disass(0, loop)
112 out:write("->LOOP:\n")
113 ctx:disass(loop, #mcode-loop)
114 symtab[addr+loop] = nil
115 else
116 ctx:disass(0, #mcode)
117 end
118end
119
120------------------------------------------------------------------------------
121
122local irtype_text = {
123 [0] = "nil",
124 "fal",
125 "tru",
126 "lud",
127 "str",
128 "ptr",
129 "thr",
130 "pro",
131 "fun",
132 "t09",
133 "tab",
134 "udt",
135 "num",
136 "int",
137 "i8 ",
138 "u8 ",
139 "i16",
140 "u16",
141}
142
143local colortype_ansi = {
144 [0] = "%s",
145 "%s",
146 "%s",
147 "%s",
148 "\027[32m%s\027[m",
149 "%s",
150 "\027[1m%s\027[m",
151 "%s",
152 "\027[1m%s\027[m",
153 "%s",
154 "\027[31m%s\027[m",
155 "\027[36m%s\027[m",
156 "\027[34m%s\027[m",
157 "\027[35m%s\027[m",
158 "\027[35m%s\027[m",
159 "\027[35m%s\027[m",
160 "\027[35m%s\027[m",
161 "\027[35m%s\027[m",
162}
163
164local function colorize_text(s, t)
165 return s
166end
167
168local function colorize_ansi(s, t)
169 return format(colortype_ansi[t], s)
170end
171
172local irtype_ansi = setmetatable({},
173 { __index = function(tab, t)
174 local s = colorize_ansi(irtype_text[t], t); tab[t] = s; return s; end })
175
176local html_escape = { ["<"] = "&lt;", [">"] = "&gt;", ["&"] = "&amp;", }
177
178local function colorize_html(s, t)
179 s = gsub(s, "[<>&]", html_escape)
180 return format('<span class="irt_%s">%s</span>', irtype_text[t], s)
181end
182
183local irtype_html = setmetatable({},
184 { __index = function(tab, t)
185 local s = colorize_html(irtype_text[t], t); tab[t] = s; return s; end })
186
187local header_html = [[
188<style type="text/css">
189background { background: #ffffff; color: #000000; }
190pre.ljdump {
191font-size: 10pt;
192background: #f0f4ff;
193color: #000000;
194border: 1px solid #bfcfff;
195padding: 0.5em;
196margin-left: 2em;
197margin-right: 2em;
198}
199span.irt_str { color: #00a000; }
200span.irt_thr, span.irt_fun { color: #404040; font-weight: bold; }
201span.irt_tab { color: #c00000; }
202span.irt_udt { color: #00c0c0; }
203span.irt_num { color: #0000c0; }
204span.irt_int { color: #c000c0; }
205</style>
206]]
207
208local colorize, irtype
209
210-- Lookup table to convert some literals into names.
211local litname = {
212 ["SLOAD "] = { [0] = "", "I", "R", "RI", "P", "PI", "PR", "PRI", },
213 ["XLOAD "] = { [0] = "", "unaligned", },
214 ["TOINT "] = { [0] = "check", "index", "", },
215 ["FLOAD "] = vmdef.irfield,
216 ["FREF "] = vmdef.irfield,
217 ["FPMATH"] = vmdef.irfpm,
218}
219
220local function ctlsub(c)
221 if c == "\n" then return "\\n"
222 elseif c == "\r" then return "\\r"
223 elseif c == "\t" then return "\\t"
224 elseif c == "\r" then return "\\r"
225 else return format("\\%03d", byte(c))
226 end
227end
228
229local function formatk(tr, idx)
230 local k, t, slot = tracek(tr, idx)
231 local tn = type(k)
232 local s
233 if tn == "number" then
234 if k == 2^52+2^51 then
235 s = "bias"
236 else
237 s = format("%+.14g", k)
238 end
239 elseif tn == "string" then
240 s = format(#k > 20 and '"%.20s"~' or '"%s"', gsub(k, "%c", ctlsub))
241 elseif tn == "function" then
242 local fi = funcinfo(k)
243 if fi.ffid then
244 s = vmdef.ffnames[fi.ffid]
245 else
246 s = fi.loc
247 end
248 elseif tn == "table" then
249 s = format("{%p}", k)
250 elseif tn == "userdata" then
251 if t == 11 then
252 s = format("userdata:%p", k)
253 else
254 s = format("[%p]", k)
255 if s == "[0x00000000]" then s = "NULL" end
256 end
257 else
258 s = tostring(k) -- For primitives.
259 end
260 s = colorize(format("%-4s", s), t)
261 if slot then
262 s = format("%s @%d", s, slot)
263 end
264 return s
265end
266
267local function printsnap(tr, snap)
268 for i=1,#snap do
269 local ref = snap[i]
270 if not ref then
271 out:write("---- ")
272 elseif ref < 0 then
273 out:write(formatk(tr, ref), " ")
274 else
275 local m, ot, op1, op2 = traceir(tr, ref)
276 local t = band(ot, 15)
277 local sep = " "
278 if t == 8 then
279 local oidx = 6*shr(ot, 8)
280 local op = sub(vmdef.irnames, oidx+1, oidx+6)
281 if op == "FRAME " then
282 sep = "|"
283 end
284 end
285 out:write(colorize(format("%04d", ref), t), sep)
286 end
287 end
288 out:write("]\n")
289end
290
291-- Dump snapshots (not interleaved with IR).
292local function dump_snap(tr)
293 out:write("---- TRACE ", tr, " snapshots\n")
294 for i=0,1000000000 do
295 local snap = tracesnap(tr, i)
296 if not snap then break end
297 out:write(format("#%-3d %04d [ ", i, snap[0]))
298 printsnap(tr, snap)
299 end
300end
301
302-- NYI: should really get the register map from the disassembler.
303local reg_map = {
304 [0] = "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi",
305 "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7",
306}
307
308-- Return a register name or stack slot for a rid/sp location.
309local function ridsp_name(ridsp)
310 local rid = band(ridsp, 0xff)
311 if ridsp > 255 then return format("[%x]", shr(ridsp, 8)*4) end
312 if rid < 128 then return reg_map[rid] end
313 return ""
314end
315
316-- Dump IR and interleaved snapshots.
317local function dump_ir(tr, dumpsnap, dumpreg)
318 local info = traceinfo(tr)
319 if not info then return end
320 local nins = info.nins
321 out:write("---- TRACE ", tr, " IR\n")
322 local irnames = vmdef.irnames
323 local snapref = 65536
324 local snap, snapno
325 if dumpsnap then
326 snap = tracesnap(tr, 0)
327 snapref = snap[0]
328 snapno = 0
329 end
330 for ins=1,nins do
331 if ins >= snapref then
332 if dumpreg then
333 out:write(format(".... SNAP #%-3d [ ", snapno))
334 else
335 out:write(format(".... SNAP #%-3d [ ", snapno))
336 end
337 printsnap(tr, snap)
338 snapno = snapno + 1
339 snap = tracesnap(tr, snapno)
340 snapref = snap and snap[0] or 65536
341 end
342 local m, ot, op1, op2, ridsp = traceir(tr, ins)
343 local oidx, t = 6*shr(ot, 8), band(ot, 31)
344 local op = sub(irnames, oidx+1, oidx+6)
345 if op == "LOOP " then
346 if dumpreg then
347 out:write(format("%04d ------------ LOOP ------------\n", ins))
348 else
349 out:write(format("%04d ------ LOOP ------------\n", ins))
350 end
351 elseif op ~= "NOP " and (dumpreg or op ~= "RENAME") then
352 if dumpreg then
353 out:write(format("%04d %-5s ", ins, ridsp_name(ridsp)))
354 else
355 out:write(format("%04d ", ins))
356 end
357 out:write(format("%s%s %s %s ",
358 band(ot, 64) == 0 and " " or ">",
359 band(ot, 128) == 0 and " " or "+",
360 irtype[t], op))
361 local m1 = band(m, 3)
362 if m1 ~= 3 then -- op1 != IRMnone
363 if op1 < 0 then
364 out:write(formatk(tr, op1))
365 else
366 out:write(format(m1 == 0 and "%04d" or "#%-3d", op1))
367 end
368 local m2 = band(m, 3*4)
369 if m2 ~= 3*4 then -- op2 != IRMnone
370 if m2 == 1*4 then -- op2 == IRMlit
371 local litn = litname[op]
372 if litn and litn[op2] then
373 out:write(" ", litn[op2])
374 else
375 out:write(format(" #%-3d", op2))
376 end
377 elseif op2 < 0 then
378 out:write(" ", formatk(tr, op2))
379 else
380 out:write(format(" %04d", op2))
381 end
382 end
383 end
384 out:write("\n")
385 end
386 end
387 if snap then
388 if dumpreg then
389 out:write(format(".... SNAP #%-3d [ ", snapno))
390 else
391 out:write(format(".... SNAP #%-3d [ ", snapno))
392 end
393 printsnap(tr, snap)
394 end
395end
396
397------------------------------------------------------------------------------
398
399local recprefix = ""
400local recdepth = 0
401
402-- Format trace error message.
403local function fmterr(err, info)
404 if type(err) == "number" then
405 if type(info) == "function" then
406 local fi = funcinfo(info)
407 if fi.ffid then
408 info = vmdef.ffnames[fi.ffid]
409 else
410 info = fi.loc
411 end
412 end
413 err = format(vmdef.traceerr[err], info)
414 end
415 return err
416end
417
418-- Dump trace states.
419local function dump_trace(what, tr, func, pc, otr, oex)
420 if what == "stop" or (what == "abort" and dumpmode.a) then
421 if dumpmode.i then dump_ir(tr, dumpmode.s, dumpmode.r and what == "stop")
422 elseif dumpmode.s then dump_snap(tr) end
423 if dumpmode.m then dump_mcode(tr) end
424 end
425 if what == "start" then
426 if dumpmode.H then out:write('<pre class="ljdump">\n') end
427 out:write("---- TRACE ", tr, " ", what)
428 if otr then out:write(" ", otr, "/", oex) end
429 local fi = funcinfo(func, pc)
430 out:write(" ", fi.loc, "\n")
431 recprefix = ""
432 reclevel = 0
433 elseif what == "stop" or what == "abort" then
434 out:write("---- TRACE ", tr, " ", what)
435 recprefix = nil
436 if what == "abort" then
437 local fi = funcinfo(func, pc)
438 out:write(" ", fi.loc, " -- ", fmterr(otr, oex), "\n")
439 else
440 local link = traceinfo(tr).link
441 if link == tr then
442 link = "loop"
443 elseif link == 0 then
444 link = "interpreter"
445 end
446 out:write(" -> ", link, "\n")
447 end
448 if dumpmode.H then out:write("</pre>\n\n") else out:write("\n") end
449 else
450 out:write("---- TRACE ", what, "\n\n")
451 end
452 out:flush()
453end
454
455-- Dump recorded bytecode.
456local function dump_record(tr, func, pc, depth, callee)
457 if depth ~= recdepth then
458 recdepth = depth
459 recprefix = rep(" .", depth)
460 end
461 local line = bcline(func, pc, recprefix)
462 if dumpmode.H then line = gsub(line, "[<>&]", html_escape) end
463 if type(callee) == "function" then
464 local fi = funcinfo(callee)
465 if fi.ffid then
466 out:write(sub(line, 1, -2), " ; ", vmdef.ffnames[fi.ffid], "\n")
467 else
468 out:write(sub(line, 1, -2), " ; ", fi.loc, "\n")
469 end
470 else
471 out:write(line)
472 end
473 if band(funcbc(func, pc), 0xff) < 16 then -- Write JMP for cond. ORDER BC
474 out:write(bcline(func, pc+1, recprefix))
475 end
476end
477
478------------------------------------------------------------------------------
479
480-- Dump taken trace exits.
481local function dump_texit(tr, ex, ngpr, nfpr, ...)
482 out:write("---- TRACE ", tr, " exit ", ex, "\n")
483 if dumpmode.X then
484 local regs = {...}
485 for i=1,ngpr do
486 out:write(format(" %08x", regs[i]))
487 if i % 8 == 0 then out:write("\n") end
488 end
489 for i=1,nfpr do
490 out:write(format(" %+17.14g", regs[ngpr+i]))
491 if i % 4 == 0 then out:write("\n") end
492 end
493 end
494end
495
496------------------------------------------------------------------------------
497
498-- Detach dump handlers.
499local function dumpoff()
500 if active then
501 active = false
502 jit.attach(dump_texit)
503 jit.attach(dump_record)
504 jit.attach(dump_trace)
505 if out and out ~= stdout and out ~= stderr then out:close() end
506 out = nil
507 end
508end
509
510-- Open the output file and attach dump handlers.
511local function dumpon(opt, outfile)
512 if active then dumpoff() end
513
514 local colormode = os.getenv("COLORTERM") and "A" or "T"
515 if opt then
516 opt = gsub(opt, "[TAH]", function(mode) colormode = mode; return ""; end)
517 end
518
519 local m = { t=true, b=true, i=true, m=true, }
520 if opt and opt ~= "" then
521 local o = sub(opt, 1, 1)
522 if o ~= "+" and o ~= "-" then m = {} end
523 for i=1,#opt do m[sub(opt, i, i)] = (o ~= "-") end
524 end
525 dumpmode = m
526
527 if m.t or m.b or m.i or m.s or m.m then
528 jit.attach(dump_trace, "trace")
529 end
530 if m.b then
531 jit.attach(dump_record, "record")
532 if not bcline then bcline = require("jit.bc").line end
533 end
534 if m.x or m.X then
535 jit.attach(dump_texit, "texit")
536 end
537
538 if not outfile then outfile = os.getenv("LUAJIT_DUMPFILE") end
539 if outfile then
540 out = outfile == "-" and stdout or assert(io.open(outfile, "w"))
541 else
542 out = stdout
543 end
544
545 m[colormode] = true
546 if colormode == "A" then
547 colorize = colorize_ansi
548 irtype = irtype_ansi
549 elseif colormode == "H" then
550 colorize = colorize_html
551 irtype = irtype_html
552 out:write(header_html)
553 else
554 colorize = colorize_text
555 irtype = irtype_text
556 end
557
558 active = true
559end
560
561-- Public module functions.
562module(...)
563
564on = dumpon
565off = dumpoff
566start = dumpon -- For -j command line option.
567