206 lines
6.4 KiB
Lua
206 lines
6.4 KiB
Lua
-- luacheck: globals debuggery whosit minetest futil
|
|
-- based on debuggery by flux, but outputs result in chat and calculates max/avg
|
|
local f = string.format
|
|
local S = debuggery.S
|
|
|
|
local get_us_time = minetest.get_us_time
|
|
local log = debuggery.log
|
|
local log_level = minetest.settings:get("debug_log_level") or "action"
|
|
|
|
local pairs_by_key = futil.table.pairs_by_key
|
|
|
|
local s = debuggery.settings
|
|
|
|
whosit = _G.whosit or {}
|
|
_G.whosit = whosit
|
|
|
|
local old_values = whosit.instrument_old_values or {}
|
|
whosit.instrument_old_values = old_values
|
|
local total_steps = whosit.instrument_total_steps or {}
|
|
whosit.instrument_total_steps = total_steps
|
|
local total_elapsed = whosit.instrument_total_elapsed or {}
|
|
whosit.instrument_total_elapsed = total_elapsed
|
|
local total_calls = whosit.instrument_total_calls or {}
|
|
whosit.instrument_total_calls = total_calls
|
|
local max_time = whosit.instrument_max_time or {}
|
|
whosit.instrument_max_time = max_time
|
|
|
|
local num_instrumented = 0
|
|
|
|
local function instrument(name, value, _cache)
|
|
if type(value) == "function" then
|
|
log("action", "instrumenting %q", name)
|
|
return function(...)
|
|
if s.instrument_log_every_call then
|
|
log("action", "%s(%s)", name, dump({ ... }))
|
|
end
|
|
local begin = get_us_time()
|
|
local rvs = { value(...) }
|
|
local elapsed = get_us_time() - begin
|
|
total_elapsed[name] = (total_elapsed[name] or 0) + elapsed
|
|
total_calls[name] = (total_calls[name] or 0) + 1
|
|
max_time[name] = math.max(elapsed, max_time[name] or 0)
|
|
-- if s.instrument_log_every_call then
|
|
-- log("action", "%s(...) -> %s", name, dump(rvs))
|
|
-- end
|
|
return unpack(rvs)
|
|
end
|
|
elseif type(value) == "table" then
|
|
_cache = _cache or {}
|
|
local cached = _cache[value]
|
|
if cached then
|
|
return cached
|
|
end
|
|
|
|
local t = {}
|
|
_cache[value] = t
|
|
|
|
for k, v in pairs(value) do
|
|
if type(k) == "string" then
|
|
t[k] = instrument(f("%s.%s", name, k), v, _cache)
|
|
else
|
|
t[k] = instrument(f("%s[%s]", name, k), v, _cache)
|
|
end
|
|
end
|
|
|
|
setmetatable(t, instrument(f("getmetatable(%s)", name), getmetatable(value), _cache))
|
|
|
|
return t
|
|
else
|
|
return value
|
|
end
|
|
end
|
|
|
|
local function instrument_mod(mod)
|
|
log("action", "instrumenting %s", mod)
|
|
old_values[mod] = _G[mod]
|
|
_G[mod] = instrument(mod, _G[mod])
|
|
num_instrumented = num_instrumented + 1
|
|
end
|
|
|
|
local function uninstrument_mod(mod)
|
|
log("action", "uninstrumenting %s", mod)
|
|
_G[mod] = old_values[mod]
|
|
old_values[mod] = nil
|
|
num_instrumented = num_instrumented - 1
|
|
end
|
|
|
|
minetest.register_chatcommand(
|
|
"whosit_instrument_mod",
|
|
{
|
|
params = S("<global_name>"),
|
|
description = S("toggles recording timing data for all functions declared in a particular global"),
|
|
privs = { [s.admin_priv] = true },
|
|
func = function(name, param)
|
|
if param == "" then
|
|
local mods = {}
|
|
for mod in pairs(old_values) do
|
|
table.insert(mods, mod)
|
|
end
|
|
if #mods == 0 then
|
|
return true, S("no mods currently instrumented")
|
|
else
|
|
return true, S("mods currently instrumented: @1", table.concat(mods, ", "))
|
|
end
|
|
end
|
|
|
|
if not (minetest.global_exists(param) and _G[param]) then
|
|
return false, S("unknown global @1", param)
|
|
end
|
|
|
|
if old_values[param] then
|
|
uninstrument_mod(param)
|
|
return true, S("instrumentation disabled for @1", param)
|
|
else
|
|
instrument_mod(param)
|
|
return true, S("instrumentation enabled for @1", param)
|
|
end
|
|
end,
|
|
})
|
|
|
|
local function get_modname(fname)
|
|
local dot = fname:find('.',1,true)
|
|
if dot then
|
|
return fname:sub(1, dot - 1)
|
|
else
|
|
return fname
|
|
end
|
|
end
|
|
|
|
function whosit.instrument_summary_lines(perstep)
|
|
if futil.table.is_empty(total_calls) then
|
|
return false, "nothing to report"
|
|
end
|
|
|
|
local out = {}
|
|
for name, num_calls in pairs_by_key(total_calls, function(a,b) return total_elapsed[a] > total_elapsed[b] end) do
|
|
local steps = perstep and total_steps[get_modname(name)] or 1
|
|
local te = math.round(total_elapsed[name])
|
|
local avg = total_elapsed[name]/num_calls
|
|
local tm = max_time[name]
|
|
|
|
table.insert(out, f("%15.1f | n:%11d | avg:%9.1f | max:%9.1f -- %s()", te/steps, num_calls/steps, avg, tm, name))
|
|
end
|
|
return true, out
|
|
end
|
|
|
|
minetest.register_chatcommand(
|
|
"whosit_instrument_summary",
|
|
{
|
|
-- params = S("<global_name>"),
|
|
-- description = S("toggles recording timing data for all functions declared in a particular global"),
|
|
privs = { [s.admin_priv] = true },
|
|
func = function(name, param)
|
|
if futil.table.is_empty(total_calls) then
|
|
return false, "nothing to report"
|
|
end
|
|
local ok, res = whosit.instrument_summary_lines()
|
|
if ok then
|
|
core.chat_send_player(name, table.concat(res, '\n'))
|
|
end
|
|
return true, "done."
|
|
end,
|
|
})
|
|
|
|
|
|
minetest.register_chatcommand(
|
|
"whosit_instrument_reset",
|
|
{
|
|
-- params = S("<global_name>"),
|
|
-- description = S("toggles recording timing data for all functions declared in a particular global"),
|
|
privs = { [s.admin_priv] = true },
|
|
func = function(name, param)
|
|
for name, _ in pairs(total_calls) do
|
|
total_calls[name] = nil
|
|
total_elapsed[name] = nil
|
|
max_time[name] = nil
|
|
end
|
|
for name, _ in pairs(total_steps) do
|
|
total_steps[name] = nil
|
|
end
|
|
return true, "Stats reset."
|
|
end,
|
|
})
|
|
|
|
|
|
|
|
function whosit.instrument_summary_text(perstep)
|
|
local _,out = whosit.instrument_summary_lines(perstep)
|
|
return table.concat(out, '\n')
|
|
end
|
|
|
|
function whosit.instrument_update_steps()
|
|
for name,_ in pairs(old_values) do
|
|
total_steps[name] = (total_steps[name] or 0) + 1
|
|
end
|
|
end
|
|
|
|
if not whosit.instrument_globalstep_registered then
|
|
minetest.register_globalstep(
|
|
function(_dtime)
|
|
whosit.instrument_update_steps()
|
|
end
|
|
)
|
|
whosit.instrument_globalstep_registered = true
|
|
end
|