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 = { ["<"] = "<", [">"] = ">", ["&"] = "&", } 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