ljx

FORK: LuaJIT with native 5.2 and 5.3 support
git clone https://git.neptards.moe/neptards/ljx.git
Log | Files | Refs | README

dump.lua (19940B)


      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.
     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 --    a  Print the IR of aborted traces, too.
     40 --
     41 -- The output format can be set with the following characters:
     42 --
     43 --    T  Plain text output.
     44 --    A  ANSI-colored text output
     45 --    H  Colorized HTML + CSS output.
     46 --
     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.
     50 --
     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"
     53 --
     54 ------------------------------------------------------------------------------
     55 
     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
     71 
     72 -- Load other modules on-demand.
     73 local bcline, disass
     74 
     75 -- Active flag, output file handle and dump mode.
     76 local active, out, dumpmode
     77 
     78 ------------------------------------------------------------------------------
     79 
     80 local symtabmt = { __index = false }
     81 local symtab = {}
     82 local nexitsym = 0
     83 
     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
    100 
    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]
    111       end
    112     end
    113   end
    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
    124       end
    125       if addr < 0 then addr = addr + 2^32 end
    126       t[addr] = tostring(i)
    127     end
    128     nexitsym = nexit
    129   end
    130   return t
    131 end
    132 
    133 local function dumpwrite(s)
    134   out:write(s)
    135 end
    136 
    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)
    157   end
    158 end
    159 
    160 ------------------------------------------------------------------------------
    161 
    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",
    187 }
    188 
    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",
    214 }
    215 
    216 local function colorize_text(s, t)
    217   return s
    218 end
    219 
    220 local function colorize_ansi(s, t)
    221   return format(colortype_ansi[t], s)
    222 end
    223 
    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 })
    227 
    228 local html_escape = { ["<"] = "&lt;", [">"] = "&gt;", ["&"] = "&amp;", }
    229 
    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)
    233 end
    234 
    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 })
    238 
    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;
    250 }
    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>
    258 ]]
    259 
    260 local colorize, irtype
    261 
    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" },
    290 }
    291 
    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))
    297   end
    298 end
    299 
    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 "(?)"
    310   end
    311 end
    312 
    313 local function formatk(tr, idx, sn)
    314   local k, t, slot = tracek(tr, idx)
    315   local tn = type(k)
    316   local s
    317   if tn == "number" then
    318     if band(sn or 0, 0x30000) ~= 0 then
    319       s = band(sn, 0x20000) ~= 0 and "contpc" or "ftsz"
    320     elseif k == 2^52+2^51 then
    321       s = "bias"
    322     else
    323       s = format(0 < k and k < 0x1p-1026 and "%+a" or "%+.14g", k)
    324     end
    325   elseif tn == "string" then
    326     s = format(#k > 20 and '"%.20s"~' or '"%s"', gsub(k, "%c", ctlsub))
    327   elseif tn == "function" then
    328     s = fmtfunc(k)
    329   elseif tn == "table" then
    330     s = format("{%p}", k)
    331   elseif tn == "userdata" then
    332     if t == 12 then
    333       s = format("userdata:%p", k)
    334     else
    335       s = format("[%p]", k)
    336       if s == "[NULL]" then s = "NULL" end
    337     end
    338   elseif t == 21 then -- int64_t
    339     s = sub(tostring(k), 1, -3)
    340     if sub(s, 1, 1) ~= "-" then s = "+"..s end
    341   else
    342     s = tostring(k) -- For primitives.
    343   end
    344   s = colorize(format("%-4s", s), t)
    345   if slot then
    346     s = format("%s @%d", s, slot)
    347   end
    348   return s
    349 end
    350 
    351 local function printsnap(tr, snap)
    352   local n = 2
    353   for s=0,snap[1]-1 do
    354     local sn = snap[n]
    355     if shr(sn, 24) == s then
    356       n = n + 1
    357       local ref = band(sn, 0xffff) - 0x8000 -- REF_BIAS
    358       if ref < 0 then
    359 	out:write(formatk(tr, ref, sn))
    360       elseif band(sn, 0x80000) ~= 0 then -- SNAP_SOFTFPNUM
    361 	out:write(colorize(format("%04d/%04d", ref, ref+1), 14))
    362       else
    363 	local m, ot, op1, op2 = traceir(tr, ref)
    364 	out:write(colorize(format("%04d", ref), band(ot, 31)))
    365       end
    366       out:write(band(sn, 0x10000) == 0 and " " or "|") -- SNAP_FRAME
    367     else
    368       out:write("---- ")
    369     end
    370   end
    371   out:write("]\n")
    372 end
    373 
    374 -- Dump snapshots (not interleaved with IR).
    375 local function dump_snap(tr)
    376   out:write("---- TRACE ", tr, " snapshots\n")
    377   for i=0,1000000000 do
    378     local snap = tracesnap(tr, i)
    379     if not snap then break end
    380     out:write(format("#%-3d %04d [ ", i, snap[0]))
    381     printsnap(tr, snap)
    382   end
    383 end
    384 
    385 -- Return a register name or stack slot for a rid/sp location.
    386 local function ridsp_name(ridsp, ins)
    387   if not disass then disass = require("jit.dis_"..jit.arch) end
    388   local rid, slot = band(ridsp, 0xff), shr(ridsp, 8)
    389   if rid == 253 or rid == 254 then
    390     return (slot == 0 or slot == 255) and " {sink" or format(" {%04d", ins-slot)
    391   end
    392   if ridsp > 255 then return format("[%x]", slot*4) end
    393   if rid < 128 then return disass.regname(rid) end
    394   return ""
    395 end
    396 
    397 -- Dump CALL* function ref and return optional ctype.
    398 local function dumpcallfunc(tr, ins)
    399   local ctype
    400   if ins > 0 then
    401     local m, ot, op1, op2 = traceir(tr, ins)
    402     if band(ot, 31) == 0 then -- nil type means CARG(func, ctype).
    403       ins = op1
    404       ctype = formatk(tr, op2)
    405     end
    406   end
    407   if ins < 0 then
    408     out:write(format("[0x%x](", tonumber((tracek(tr, ins)))))
    409   else
    410     out:write(format("%04d (", ins))
    411   end
    412   return ctype
    413 end
    414 
    415 -- Recursively gather CALL* args and dump them.
    416 local function dumpcallargs(tr, ins)
    417   if ins < 0 then
    418     out:write(formatk(tr, ins))
    419   else
    420     local m, ot, op1, op2 = traceir(tr, ins)
    421     local oidx = 6*shr(ot, 8)
    422     local op = sub(vmdef.irnames, oidx+1, oidx+6)
    423     if op == "CARG  " then
    424       dumpcallargs(tr, op1)
    425       if op2 < 0 then
    426 	out:write(" ", formatk(tr, op2))
    427       else
    428 	out:write(" ", format("%04d", op2))
    429       end
    430     else
    431       out:write(format("%04d", ins))
    432     end
    433   end
    434 end
    435 
    436 -- Dump IR and interleaved snapshots.
    437 local function dump_ir(tr, dumpsnap, dumpreg)
    438   local info = traceinfo(tr)
    439   if not info then return end
    440   local nins = info.nins
    441   out:write("---- TRACE ", tr, " IR\n")
    442   local irnames = vmdef.irnames
    443   local snapref = 65536
    444   local snap, snapno
    445   if dumpsnap then
    446     snap = tracesnap(tr, 0)
    447     snapref = snap[0]
    448     snapno = 0
    449   end
    450   for ins=1,nins do
    451     if ins >= snapref then
    452       if dumpreg then
    453 	out:write(format("....              SNAP   #%-3d [ ", snapno))
    454       else
    455 	out:write(format("....        SNAP   #%-3d [ ", snapno))
    456       end
    457       printsnap(tr, snap)
    458       snapno = snapno + 1
    459       snap = tracesnap(tr, snapno)
    460       snapref = snap and snap[0] or 65536
    461     end
    462     local m, ot, op1, op2, ridsp = traceir(tr, ins)
    463     local oidx, t = 6*shr(ot, 8), band(ot, 31)
    464     local op = sub(irnames, oidx+1, oidx+6)
    465     if op == "LOOP  " then
    466       if dumpreg then
    467 	out:write(format("%04d ------------ LOOP ------------\n", ins))
    468       else
    469 	out:write(format("%04d ------ LOOP ------------\n", ins))
    470       end
    471     elseif op ~= "NOP   " and op ~= "CARG  " and
    472 	   (dumpreg or op ~= "RENAME") then
    473       local rid = band(ridsp, 255)
    474       if dumpreg then
    475 	out:write(format("%04d %-6s", ins, ridsp_name(ridsp, ins)))
    476       else
    477 	out:write(format("%04d ", ins))
    478       end
    479       out:write(format("%s%s %s %s ",
    480 		       (rid == 254 or rid == 253) and "}" or
    481 		       (band(ot, 128) == 0 and " " or ">"),
    482 		       band(ot, 64) == 0 and " " or "+",
    483 		       irtype[t], op))
    484       local m1, m2 = band(m, 3), band(m, 3*4)
    485       if sub(op, 1, 4) == "CALL" then
    486 	local ctype
    487 	if m2 == 1*4 then -- op2 == IRMlit
    488 	  out:write(format("%-10s  (", vmdef.ircall[op2]))
    489 	else
    490 	  ctype = dumpcallfunc(tr, op2)
    491 	end
    492 	if op1 ~= -1 then dumpcallargs(tr, op1) end
    493 	out:write(")")
    494 	if ctype then out:write(" ctype ", ctype) end
    495       elseif op == "CNEW  " and op2 == -1 then
    496 	out:write(formatk(tr, op1))
    497       elseif m1 ~= 3 then -- op1 != IRMnone
    498 	if op1 < 0 then
    499 	  out:write(formatk(tr, op1))
    500 	else
    501 	  out:write(format(m1 == 0 and "%04d" or "#%-3d", op1))
    502 	end
    503 	if m2 ~= 3*4 then -- op2 != IRMnone
    504 	  if m2 == 1*4 then -- op2 == IRMlit
    505 	    local litn = litname[op]
    506 	    if litn and litn[op2] then
    507 	      out:write("  ", litn[op2])
    508 	    elseif op == "UREFO " or op == "UREFC " then
    509 	      out:write(format("  #%-3d", shr(op2, 8)))
    510 	    else
    511 	      out:write(format("  #%-3d", op2))
    512 	    end
    513 	  elseif op2 < 0 then
    514 	    out:write("  ", formatk(tr, op2))
    515 	  else
    516 	    out:write(format("  %04d", op2))
    517 	  end
    518 	end
    519       end
    520       out:write("\n")
    521     end
    522   end
    523   if snap then
    524     if dumpreg then
    525       out:write(format("....              SNAP   #%-3d [ ", snapno))
    526     else
    527       out:write(format("....        SNAP   #%-3d [ ", snapno))
    528     end
    529     printsnap(tr, snap)
    530   end
    531 end
    532 
    533 ------------------------------------------------------------------------------
    534 
    535 local recprefix = ""
    536 local recdepth = 0
    537 
    538 -- Format trace error message.
    539 local function fmterr(err, info)
    540   if type(err) == "number" then
    541     if type(info) == "function" then info = fmtfunc(info) end
    542     err = format(vmdef.traceerr[err], info)
    543   end
    544   return err
    545 end
    546 
    547 -- Dump trace states.
    548 local function dump_trace(what, tr, func, pc, otr, oex)
    549   if what == "stop" or (what == "abort" and dumpmode.a) then
    550     if dumpmode.i then dump_ir(tr, dumpmode.s, dumpmode.r and what == "stop")
    551     elseif dumpmode.s then dump_snap(tr) end
    552     if dumpmode.m then dump_mcode(tr) end
    553   end
    554   if what == "start" then
    555     if dumpmode.H then out:write('<pre class="ljdump">\n') end
    556     out:write("---- TRACE ", tr, " ", what)
    557     if otr then out:write(" ", otr, "/", oex) end
    558     out:write(" ", fmtfunc(func, pc), "\n")
    559   elseif what == "stop" or what == "abort" then
    560     out:write("---- TRACE ", tr, " ", what)
    561     if what == "abort" then
    562       out:write(" ", fmtfunc(func, pc), " -- ", fmterr(otr, oex), "\n")
    563     else
    564       local info = traceinfo(tr)
    565       local link, ltype = info.link, info.linktype
    566       if link == tr or link == 0 then
    567 	out:write(" -> ", ltype, "\n")
    568       elseif ltype == "root" then
    569 	out:write(" -> ", link, "\n")
    570       else
    571 	out:write(" -> ", link, " ", ltype, "\n")
    572       end
    573     end
    574     if dumpmode.H then out:write("</pre>\n\n") else out:write("\n") end
    575   else
    576     if what == "flush" then symtab, nexitsym = {}, 0 end
    577     out:write("---- TRACE ", what, "\n\n")
    578   end
    579   out:flush()
    580 end
    581 
    582 -- Dump recorded bytecode.
    583 local function dump_record(tr, func, pc, depth, callee)
    584   if depth ~= recdepth then
    585     recdepth = depth
    586     recprefix = rep(" .", depth)
    587   end
    588   local line
    589   if pc >= 0 then
    590     line = bcline(func, pc, recprefix)
    591     if dumpmode.H then line = gsub(line, "[<>&]", html_escape) end
    592   else
    593     line = "0000 "..recprefix.." FUNCC      \n"
    594     callee = func
    595   end
    596   if pc <= 0 then
    597     out:write(sub(line, 1, -2), "         ; ", fmtfunc(func), "\n")
    598   else
    599     out:write(line)
    600   end
    601   if pc >= 0 and band(funcbc(func, pc), 0xff) < 16 then -- ORDER BC
    602     out:write(bcline(func, pc+1, recprefix)) -- Write JMP for cond.
    603   end
    604 end
    605 
    606 ------------------------------------------------------------------------------
    607 
    608 -- Dump taken trace exits.
    609 local function dump_texit(tr, ex, ngpr, nfpr, ...)
    610   out:write("---- TRACE ", tr, " exit ", ex, "\n")
    611   if dumpmode.X then
    612     local regs = {...}
    613     if jit.arch == "x64" then
    614       for i=1,ngpr do
    615 	out:write(format(" %016x", regs[i]))
    616 	if i % 4 == 0 then out:write("\n") end
    617       end
    618     else
    619       for i=1,ngpr do
    620 	out:write(" ", tohex(regs[i]))
    621 	if i % 8 == 0 then out:write("\n") end
    622       end
    623     end
    624     if jit.arch == "mips" or jit.arch == "mipsel" then
    625       for i=1,nfpr,2 do
    626 	out:write(format(" %+17.14g", regs[ngpr+i]))
    627 	if i % 8 == 7 then out:write("\n") end
    628       end
    629     else
    630       for i=1,nfpr do
    631 	out:write(format(" %+17.14g", regs[ngpr+i]))
    632 	if i % 4 == 0 then out:write("\n") end
    633       end
    634     end
    635   end
    636 end
    637 
    638 ------------------------------------------------------------------------------
    639 
    640 -- Detach dump handlers.
    641 local function dumpoff()
    642   if active then
    643     active = false
    644     jit.attach(dump_texit)
    645     jit.attach(dump_record)
    646     jit.attach(dump_trace)
    647     if out and out ~= stdout and out ~= stderr then out:close() end
    648     out = nil
    649   end
    650 end
    651 
    652 -- Open the output file and attach dump handlers.
    653 local function dumpon(opt, outfile)
    654   if active then dumpoff() end
    655 
    656   local colormode = os.getenv("COLORTERM") and "A" or "T"
    657   if opt then
    658     opt = gsub(opt, "[TAH]", function(mode) colormode = mode; return ""; end)
    659   end
    660 
    661   local m = { t=true, b=true, i=true, m=true, }
    662   if opt and opt ~= "" then
    663     local o = sub(opt, 1, 1)
    664     if o ~= "+" and o ~= "-" then m = {} end
    665     for i=1,#opt do m[sub(opt, i, i)] = (o ~= "-") end
    666   end
    667   dumpmode = m
    668 
    669   if m.t or m.b or m.i or m.s or m.m then
    670     jit.attach(dump_trace, "trace")
    671   end
    672   if m.b then
    673     jit.attach(dump_record, "record")
    674     if not bcline then bcline = require("jit.bc").line end
    675   end
    676   if m.x or m.X then
    677     jit.attach(dump_texit, "texit")
    678   end
    679 
    680   if not outfile then outfile = os.getenv("LUAJIT_DUMPFILE") end
    681   if outfile then
    682     out = outfile == "-" and stdout or assert(io.open(outfile, "w"))
    683   else
    684     out = stdout
    685   end
    686 
    687   m[colormode] = true
    688   if colormode == "A" then
    689     colorize = colorize_ansi
    690     irtype = irtype_ansi
    691   elseif colormode == "H" then
    692     colorize = colorize_html
    693     irtype = irtype_html
    694     out:write(header_html)
    695   else
    696     colorize = colorize_text
    697     irtype = irtype_text
    698   end
    699 
    700   active = true
    701 end
    702 
    703 -- Public module functions.
    704 return {
    705   on = dumpon,
    706   off = dumpoff,
    707   start = dumpon -- For -j command line option.
    708 }
    709