if not modules then modules = { } end modules ['util-deb'] = { version = 1.001, comment = "companion to luat-lib.mkiv", author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", copyright = "PRAGMA ADE / ConTeXt Development Team", license = "see context related readme files" } -- the tag is kind of generic and used for functions that are not -- bound to a variable, like node.new, node.copy etc (contrary to for instance -- node.hasattribute which is bound to a hasattribute local variable in mkiv) local type, next, tostring, tonumber, xpcall, print = type, next, tostring, tonumber, xpcall, print local format, find, sub, gsub = string.format, string.find, string.sub, string.gsub local insert, remove, sort = table.insert, table.remove, table.sort local setmetatableindex = table.setmetatableindex utilities = utilities or { } local debugger = utilities.debugger or { } utilities.debugger = debugger local report = logs.reporter("debugger") local ticks = os.gettimeofday or os.clock local seconds = function(n) return n or 0 end local overhead = 0 local dummycalls = 10*1000 local nesting = 0 local names = { } local function initialize() ticks = lua.getpreciseticks seconds = lua.getpreciseseconds initialize = false end setmetatableindex(names,function(t,name) local v = setmetatableindex(function(t,source) local v = setmetatableindex(function(t,line) -- local v = { total = 0, count = 0, nesting = 0, ticks = 0 } local v = { 0, 0, 0, 0 } t[line] = v return v end) t[source] = v return v end) t[name] = v return v end) local getinfo = nil local sethook = nil -- local function hook(where) -- local f = getinfo(2,"nS") -- if f then -- local source = f.short_src -- if not source then -- return -- end -- local line = f.linedefined or 0 -- local name = f.name -- if not name then -- local what = f.what -- if what == "C" then -- name = "" -- else -- name = f.namewhat or what or "" -- end -- end -- local data = names[name][source][line] -- if where == "call" then -- local nesting = data.nesting -- if nesting == 0 then -- data.count = data.count + 1 -- insert(data,ticks()) -- data.nesting = 1 -- else -- data.nesting = nesting + 1 -- end -- elseif where == "return" then -- local nesting = data.nesting -- if nesting == 1 then -- local t = remove(data) -- if t then -- data.total = data.total + ticks() - t -- end -- data.nesting = 0 -- else -- data.nesting = nesting - 1 -- end -- end -- end -- end local getdebuginfo = lua.getdebuginfo -- local function hook(where) -- make two hooks -- local name, source, line = getdebuginfo() -- if name then -- local data = names[name][source][line] -- if where == "call" then -- local nesting = data.nesting -- if nesting == 0 then -- data.count = data.count + 1 -- -- insert(data,ticks()) -- data.nesting = 1 -- data.ticks = ticks() -- else -- data.nesting = nesting + 1 -- end -- elseif where == "return" then -- local nesting = data.nesting -- if nesting == 1 then -- -- local t = remove(data) -- local t = data.ticks -- if t then -- data.total = data.total + ticks() - t -- end -- data.nesting = 0 -- elseif nesting > 0 then -- data.nesting = nesting - 1 -- end -- elseif where == "tail call" then -- local nesting = data.nesting -- if nesting == 1 then -- -- local t = remove(data) -- local t = data.ticks -- if t then -- data.total = data.total + ticks() - t -- end -- data.nesting = 0 -- elseif nesting > 0 then -- data.nesting = nesting - 1 -- end -- end -- end -- end local function hook(where) -- make two hooks local name, source, line = getdebuginfo() if name then local data = names[name][source][line] if where == "call" then local nesting = data[3] if nesting == 0 then data[2] = data[2] + 1 data[3] = 1 data[4] = ticks() else data[3] = nesting + 1 end -- elseif where == "return" then else local nesting = data[3] if nesting == 1 then local t = data[4] if t then data[1] = data[1] + ticks() - t end data[3] = 0 elseif nesting > 0 then data[3] = nesting - 1 end -- elseif where == "tail call" then -- local nesting = data[3] -- if nesting == 1 then -- local t = data[4] -- if t then -- data[4] = data[4] + ticks() - t -- end -- data[3] = 0 -- elseif nesting > 0 then -- data[3] = nesting - 1 -- end end end end function debugger.showstats(printer,threshold) local printer = printer or report local calls = 0 local functions = 0 local dataset = { } local length = 0 local realtime = 0 local totaltime = 0 local threshold = threshold or 0 for name, sources in next, names do for source, lines in next, sources do for line, data in next, lines do -- local count = data.count local count = data[2] if count > threshold then if #name > length then length = #name end -- local total = data.total local total = data[1] local real = total if real > 0 then real = total - (count * overhead / dummycalls) if real < 0 then real = 0 end realtime = realtime + real end totaltime = totaltime + total if line < 0 then line = 0 end -- if name = "a" then -- -- weird name -- end dataset[#dataset+1] = { real, total, count, name, source, line } end end end end sort(dataset,function(a,b) if a[1] == b[1] then if a[2] == b[2] then if a[3] == b[3] then if a[4] == b[4] then if a[5] == b[5] then return a[6] < b[6] else return a[5] < b[5] end else return a[4] < b[4] end else return b[3] < a[3] end else return b[2] < a[2] end else return b[1] < a[1] end end) if length > 50 then length = 50 end local fmt = string.formatters["%4.9k s %3.3k %% %4.9k s %3.3k %% %8i # %-" .. length .. "s %4i %s"] for i=1,#dataset do local data = dataset[i] local real = data[1] local total = data[2] local count = data[3] local name = data[4] local source = data[5] local line = data[6] calls = calls + count functions = functions + 1 name = gsub(name,"%s+"," ") if #name > length then name = sub(name,1,length) end printer(fmt(seconds(total),100*total/totaltime,seconds(real),100*real/realtime,count,name,line,source)) end printer("") printer(format("functions : %i", functions)) printer(format("calls : %i", calls)) printer(format("overhead : %f", seconds(overhead/1000))) -- table.save("luatex-profile.lua",names) end local function getdebug() if sethook and getinfo then return end if not debug then local okay okay, debug = pcall(require,"debug") end if type(debug) ~= "table" then return end getinfo = debug.getinfo sethook = debug.sethook if type(getinfo) ~= "function" then getinfo = nil end if type(sethook) ~= "function" then sethook = nil end end function debugger.savestats(filename,threshold) local f = io.open(filename,'w') if f then debugger.showstats(function(str) f:write(str,"\n") end,threshold) f:close() end end function debugger.enable() getdebug() if sethook and getinfo and nesting == 0 then running = true if initialize then initialize() end sethook(hook,"cr") -- sethook(hook_c,"c") -- sethook(hook_r,"r") local function dummy() end local t = ticks() for i=1,dummycalls do dummy() end overhead = ticks() - t end if nesting > 0 then nesting = nesting + 1 end end function debugger.disable() if nesting > 0 then nesting = nesting - 1 end if sethook and getinfo and nesting == 0 then sethook() end end -- debugger.enable() -- -- print(math.sin(1*.5)) -- print(math.sin(1*.5)) -- print(math.sin(1*.5)) -- print(math.sin(1*.5)) -- print(math.sin(1*.5)) -- -- debugger.disable() -- -- print("") -- debugger.showstats() -- print("") -- debugger.showstats(print,3) -- -- from the lua book: local function showtraceback(rep) -- from lua site / adapted getdebug() if getinfo then local level = 2 -- we don't want this function to be reported local reporter = rep or report while true do local info = getinfo(level, "Sl") if not info then break elseif info.what == "C" then reporter("%2i : %s",level-1,"C function") else reporter("%2i : %s : %s",level-1,info.short_src,info.currentline) end level = level + 1 end end end debugger.showtraceback = showtraceback -- debug.showtraceback = showtraceback -- showtraceback() if luac then local show, dump = luac.print, string.dump function luac.inspect(v) if type(v) == "function" then local ok, str = xpcall(dump,function() end,v) if ok then v = str end end if type(v) == "string" then show(v,true) else print(v) end end end