if not modules then modules = { } end modules ['trac-tra'] = { version = 1.001, comment = "companion to trac-tra.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.has_attribute which is bound to a has_attribute local variable in mkiv) local debug = require "debug" local getinfo = debug.getinfo local type, next = type, next local concat = table.concat local format, find, lower, gmatch, gsub = string.format, string.find, string.lower, string.gmatch, string.gsub debugger = debugger or { } local counters = { } local names = { } -- one local function hook() local f = getinfo(2,"f").func local n = getinfo(2,"Sn") -- if n.what == "C" and n.name then print (n.namewhat .. ': ' .. n.name) end if f then local cf = counters[f] if cf == nil then counters[f] = 1 names[f] = n else counters[f] = cf + 1 end end end local function getname(func) local n = names[func] if n then if n.what == "C" then return n.name or '' else -- source short_src linedefined what name namewhat nups func local name = n.name or n.namewhat or n.what if not name or name == "" then name = "?" end return format("%s : %s : %s", n.short_src or "unknown source", n.linedefined or "--", name) end else return "unknown" end end function debugger.showstats(printer,threshold) printer = printer or texio.write or print threshold = threshold or 0 local total, grandtotal, functions = 0, 0, 0 printer("\n") -- ugly but ok -- table.sort(counters) for func, count in next, counters do if count > threshold then local name = getname(func) if not find(name,"for generator") then printer(format("%8i %s", count, name)) total = total + count end end grandtotal = grandtotal + count functions = functions + 1 end printer(format("functions: %s, total: %s, grand total: %s, threshold: %s\n", functions, total, grandtotal, threshold)) end -- two --~ local function hook() --~ local n = getinfo(2) --~ if n.what=="C" and not n.name then --~ local f = tostring(debug.traceback()) --~ local cf = counters[f] --~ if cf == nil then --~ counters[f] = 1 --~ names[f] = n --~ else --~ counters[f] = cf + 1 --~ end --~ end --~ end --~ function debugger.showstats(printer,threshold) --~ printer = printer or texio.write or print --~ threshold = threshold or 0 --~ local total, grandtotal, functions = 0, 0, 0 --~ printer("\n") -- ugly but ok --~ -- table.sort(counters) --~ for func, count in next, counters do --~ if count > threshold then --~ printer(format("%8i %s", count, func)) --~ total = total + count --~ end --~ grandtotal = grandtotal + count --~ functions = functions + 1 --~ end --~ printer(format("functions: %s, total: %s, grand total: %s, threshold: %s\n", functions, total, grandtotal, threshold)) --~ end -- rest function debugger.savestats(filename,threshold) local f = io.open(filename,'w') if f then debugger.showstats(function(str) f:write(str) end,threshold) f:close() end end function debugger.enable() debug.sethook(hook,"c") end function debugger.disable() debug.sethook() --~ counters[debug.getinfo(2,"f").func] = nil end function debugger.tracing() local n = tonumber(os.env['MTX.TRACE.CALLS']) or tonumber(os.env['MTX_TRACE_CALLS']) or 0 if n > 0 then function debugger.tracing() return true end ; return true else function debugger.tracing() return false end ; return false 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) setters = setters or { } setters.data = setters.data or { } --~ local function set(t,what,value) --~ local data, done = t.data, t.done --~ if type(what) == "string" then --~ what = aux.settings_to_array(what) -- inefficient but ok --~ end --~ for i=1,#what do --~ local w = what[i] --~ for d, f in next, data do --~ if done[d] then --~ -- prevent recursion due to wildcards --~ elseif find(d,w) then --~ done[d] = true --~ for i=1,#f do --~ f[i](value) --~ end --~ end --~ end --~ end --~ end local function set(t,what,value) local data, done = t.data, t.done if type(what) == "string" then what = aux.settings_to_hash(what) -- inefficient but ok end for w, v in next, what do if v == "" then v = value else v = toboolean(v) end for d, f in next, data do if done[d] then -- prevent recursion due to wildcards elseif find(d,w) then done[d] = true for i=1,#f do f[i](v) end end end end end local function reset(t) for d, f in next, t.data do for i=1,#f do f[i](false) end end end local function enable(t,what) set(t,what,true) end local function disable(t,what) local data = t.data if not what or what == "" then t.done = { } reset(t) else set(t,what,false) end end function setters.register(t,what,...) local data = t.data what = lower(what) local w = data[what] if not w then w = { } data[what] = w end for _, fnc in next, { ... } do local typ = type(fnc) if typ == "function" then w[#w+1] = fnc elseif typ == "string" then w[#w+1] = function(value) set(t,fnc,value,nesting) end end end end function setters.enable(t,what) local e = t.enable t.enable, t.done = enable, { } enable(t,string.simpleesc(tostring(what))) t.enable, t.done = e, { } end function setters.disable(t,what) local e = t.disable t.disable, t.done = disable, { } disable(t,string.simpleesc(tostring(what))) t.disable, t.done = e, { } end function setters.reset(t) t.done = { } reset(t) end function setters.list(t) -- pattern local list = table.sortedkeys(t.data) local user, system = { }, { } for l=1,#list do local what = list[l] if find(what,"^%*") then system[#system+1] = what else user[#user+1] = what end end return user, system end function setters.show(t) commands.writestatus("","") local list = setters.list(t) for k=1,#list do commands.writestatus(t.name,list[k]) end commands.writestatus("","") end -- we could have used a bit of oo and the trackers:enable syntax but -- there is already a lot of code around using the singular tracker -- we could make this into a module function setters.new(name) local t t = { data = { }, name = name, enable = function(...) setters.enable (t,...) end, disable = function(...) setters.disable (t,...) end, register = function(...) setters.register(t,...) end, list = function(...) setters.list (t,...) end, show = function(...) setters.show (t,...) end, } setters.data[name] = t return t end trackers = setters.new("trackers") directives = setters.new("directives") experiments = setters.new("experiments") -- nice trick: we overload two of the directives related functions with variants that -- do tracing (itself using a tracker) .. proof of concept local trace_directives = false local trace_directives = false trackers.register("system.directives", function(v) trace_directives = v end) local trace_experiments = false local trace_experiments = false trackers.register("system.experiments", function(v) trace_experiments = v end) local e = directives.enable local d = directives.disable function directives.enable(...) commands.writestatus("directives","enabling: %s",concat({...}," ")) e(...) end function directives.disable(...) commands.writestatus("directives","disabling: %s",concat({...}," ")) d(...) end local e = experiments.enable local d = experiments.disable function experiments.enable(...) commands.writestatus("experiments","enabling: %s",concat({...}," ")) e(...) end function experiments.disable(...) commands.writestatus("experiments","disabling: %s",concat({...}," ")) d(...) end -- a useful example directives.register("system.nostatistics", function(v) statistics.enable = not v end)