You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
309 lines
7.8 KiB
Lua
309 lines
7.8 KiB
Lua
--[[
|
|
https://github.com/sundream/ltrace
|
|
--]]
|
|
local ltrace_getenv
|
|
local getstack_from
|
|
local getstack
|
|
local dumpvalue
|
|
local dumpkey
|
|
local dumptable
|
|
local dumpframe
|
|
local dumpstack
|
|
local getfulltrace
|
|
|
|
local mTABLE_MAX_DEEP = 3
|
|
local mVALUE_MAX_LEN = 5120
|
|
local traceback = {}
|
|
|
|
-- local setfenv =
|
|
-- _G.setfenv or
|
|
-- function(f, t)
|
|
-- f = (type(f) == "function" and f or debug.getinfo(f + 1, "f").func)
|
|
-- local name
|
|
-- local up = 0
|
|
-- repeat
|
|
-- up = up + 1
|
|
-- name = debug.getupvalue(f, up)
|
|
-- until name == "_ENV" or name == nil
|
|
-- if name then
|
|
-- debug.upvaluejoin(
|
|
-- f,
|
|
-- up,
|
|
-- function()
|
|
-- return name
|
|
-- end,
|
|
-- 1
|
|
-- ) -- use unique upvalue
|
|
-- debug.setupvalue(f, up, t)
|
|
-- end
|
|
-- return f
|
|
-- end
|
|
|
|
local getfenv = _G.getfenv or function(f)
|
|
f = (type(f) == "function" and f or debug.getinfo(f + 1, "f").func)
|
|
local name, val
|
|
local up = 0
|
|
repeat
|
|
up = up + 1
|
|
name, val = debug.getupvalue(f, up)
|
|
until name == "_ENV" or name == nil
|
|
return val
|
|
end
|
|
|
|
local function canPrint(k, v)
|
|
if type(v) ~= "table" then
|
|
return true
|
|
end
|
|
if v == _G then
|
|
return false
|
|
end
|
|
return true
|
|
end
|
|
|
|
ltrace_getenv = function(realframe)
|
|
local env = {}
|
|
local indexmap = {}
|
|
local i = 1
|
|
local funcinfo = debug.getinfo(realframe, "nlSf")
|
|
if not funcinfo then
|
|
return
|
|
end
|
|
local k, v = debug.getlocal(realframe, i)
|
|
while k do
|
|
indexmap[k] = i
|
|
env[k] = v
|
|
i = i + 1
|
|
k, v = debug.getlocal(realframe, i)
|
|
end
|
|
|
|
setmetatable(env, {__index = getfenv(funcinfo.func)})
|
|
return env, funcinfo, indexmap
|
|
end
|
|
|
|
getstack_from = function(callfile, callline, maxframesz)
|
|
assert(callfile and callline)
|
|
local realframe = 0
|
|
local framestack = {}
|
|
local env, funcinfo, indexmap = ltrace_getenv(realframe)
|
|
while funcinfo do
|
|
if funcinfo.currentline == callline and funcinfo.short_src == callfile then
|
|
break
|
|
end
|
|
realframe = realframe + 1
|
|
env, funcinfo, indexmap = ltrace_getenv(realframe)
|
|
end
|
|
while funcinfo do
|
|
realframe = realframe + 1
|
|
env, funcinfo, indexmap = ltrace_getenv(realframe)
|
|
if not funcinfo then
|
|
break
|
|
end
|
|
table.insert(
|
|
framestack,
|
|
{
|
|
realframe = realframe,
|
|
env = env,
|
|
funcinfo = funcinfo,
|
|
indexmap = indexmap
|
|
}
|
|
)
|
|
if maxframesz and #framestack >= maxframesz then
|
|
break
|
|
end
|
|
end
|
|
return framestack
|
|
end
|
|
|
|
local function get_params(func)
|
|
local info = debug.getinfo(func, "u")
|
|
if not info then
|
|
return
|
|
end
|
|
local argNameList = {}
|
|
for i = 1, info.nparams do
|
|
local name = debug.getlocal(func, i)
|
|
table.insert(argNameList, name)
|
|
end
|
|
if info.isvararg then
|
|
table.insert(argNameList, "...")
|
|
end
|
|
return argNameList, info.nparams, info.isvararg
|
|
end
|
|
|
|
local uncomparedTypeMap = {
|
|
["table"] = true,
|
|
["userdata"] = true,
|
|
["function"] = true,
|
|
["boolean"] = true,
|
|
["thread"] = true
|
|
}
|
|
|
|
local function compare(a, b)
|
|
local ta = type(a)
|
|
local tb = type(b)
|
|
if ta ~= tb or uncomparedTypeMap[ta] or uncomparedTypeMap[tb] then
|
|
return tostring(a) < tostring(b)
|
|
end
|
|
return a < b
|
|
end
|
|
|
|
local function pairs_orderly(t, comp)
|
|
comp = comp or compare
|
|
local keys = {}
|
|
for k, v in pairs(t) do
|
|
table.insert(keys, k)
|
|
end
|
|
local size = #keys
|
|
table.sort(keys, comp)
|
|
|
|
local i = 0
|
|
return function(tbl, k)
|
|
i = i + 1
|
|
if i > size then
|
|
return
|
|
end
|
|
return keys[i], t[keys[i]]
|
|
end
|
|
end
|
|
|
|
local ignoreMap = {}
|
|
|
|
dumptable = function(value, depth)
|
|
assert(type(value) == "table")
|
|
local tostr
|
|
local meta = getmetatable(value)
|
|
if meta and meta.__tostring then
|
|
tostr = meta.__tostring
|
|
elseif value.__tostring then
|
|
tostr = value.__tostring
|
|
end
|
|
if tostr then
|
|
return tostr(value)
|
|
end
|
|
local rettbl = {}
|
|
depth = (depth or 0) + 1
|
|
if depth > mTABLE_MAX_DEEP then
|
|
return "{...}"
|
|
end
|
|
|
|
table.insert(rettbl, "{")
|
|
local content = {}
|
|
for k, v in pairs_orderly(value) do
|
|
if not ignoreMap[k] and canPrint(k, v) then
|
|
local line = {}
|
|
table.insert(line, dumpkey(k, depth) .. "=")
|
|
table.insert(line, dumpvalue(v, depth))
|
|
table.insert(content, table.concat(line))
|
|
end
|
|
end
|
|
table.insert(rettbl, table.concat(content, ", "))
|
|
table.insert(rettbl, "}")
|
|
return table.concat(rettbl)
|
|
end
|
|
|
|
dumpkey = function(key, depth)
|
|
local vtype = type(key)
|
|
if vtype == "table" then
|
|
return "[" .. dumptable(key, depth) .. "]"
|
|
elseif vtype == "string" then
|
|
if key:match("^%d") or key:match("%w+") ~= key then
|
|
return "[" .. string.format("%q", key) .. "]"
|
|
end
|
|
return tostring(key)
|
|
end
|
|
return "[" .. tostring(key) .. "]"
|
|
end
|
|
|
|
dumpvalue = function(v, depth)
|
|
local vtype = type(v)
|
|
if vtype == "table" then
|
|
return dumptable(v, depth)
|
|
elseif vtype == "string" then
|
|
return string.format("%q", v)
|
|
elseif vtype == "number" or vtype == "boolean" then
|
|
return tostring(v)
|
|
end
|
|
return string.format("<%s>", tostring(v))
|
|
end
|
|
|
|
dumpframe = function(frameidx, env, funcinfo, indexmap)
|
|
local fix = -1
|
|
if funcinfo.what ~= "Lua" then
|
|
return string.format("%d[C] : in <%s>", frameidx + fix, funcinfo.name or funcinfo.what or funcinfo.namewhat)
|
|
end
|
|
|
|
local out = {}
|
|
local args = get_params(funcinfo.func)
|
|
local funcstr = ""
|
|
if funcinfo.name then
|
|
funcstr = string.format("in <%s(%s)>", funcinfo.name, table.concat(args, ","))
|
|
end
|
|
table.insert(
|
|
out,
|
|
string.format("%d @%s:%d %s", frameidx + fix, funcinfo.short_src or "<stdin>", funcinfo.currentline, funcstr)
|
|
)
|
|
|
|
local valuelines = {}
|
|
local function compare(k1, k2)
|
|
return indexmap[k1] < indexmap[k2]
|
|
end
|
|
for k, v in pairs_orderly(env, compare) do
|
|
if canPrint(k, v) then
|
|
local vs = dumpvalue(v)
|
|
if #vs > mVALUE_MAX_LEN then
|
|
vs = vs:sub(1, mVALUE_MAX_LEN) .. "..."
|
|
end
|
|
-- table.insert(valuelines, string.format("\t%s<%s>:%s", k, indexmap[k] or "", vs))
|
|
table.insert(valuelines, string.format("\t%s:%s", k, vs))
|
|
end
|
|
end
|
|
|
|
table.insert(out, table.concat(valuelines, "\n"))
|
|
return table.concat(out, "\n")
|
|
end
|
|
|
|
dumpstack = function(stack)
|
|
local list = {}
|
|
for i, v in pairs(stack) do
|
|
local s = dumpframe(i, v.env, v.funcinfo, v.indexmap)
|
|
table.insert(list, s)
|
|
end
|
|
return list
|
|
end
|
|
|
|
getstack = function(level, maxframesz)
|
|
level = level or 2
|
|
local info = debug.getinfo(level, "nlSf")
|
|
return getstack_from(info.short_src, info.currentline, maxframesz)
|
|
end
|
|
|
|
getfulltrace = function(level, maxframesz)
|
|
local fullstack = getstack(level, maxframesz)
|
|
local list = dumpstack(fullstack)
|
|
local ret = {}
|
|
for _, v in pairs(list) do
|
|
table.insert(ret, v)
|
|
end
|
|
return table.concat(ret, "\n")
|
|
end
|
|
|
|
local function settdmaxdeep(depth)
|
|
mTABLE_MAX_DEEP = depth or mTABLE_MAX_DEEP
|
|
end
|
|
|
|
local function setvdmaxlen(len)
|
|
mVALUE_MAX_LEN = len or mVALUE_MAX_LEN
|
|
end
|
|
|
|
traceback.setvdmaxlen = setvdmaxlen
|
|
traceback.settdmaxdeep = settdmaxdeep
|
|
traceback.getstack_from = getstack_from
|
|
traceback.getstack = getstack
|
|
traceback.dumpvalue = dumpvalue
|
|
traceback.dumptable = dumptable
|
|
traceback.dumpframe = dumpframe
|
|
traceback.dumpstack = dumpstack
|
|
traceback.getfulltrace = getfulltrace
|
|
|
|
return traceback
|