Skip to content

Instantly share code, notes, and snippets.

@Fingercomp
Last active June 20, 2019 11:05
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Fingercomp/a688d221356cb371d940b947d0ca90a8 to your computer and use it in GitHub Desktop.
Save Fingercomp/a688d221356cb371d940b947d0ca90a8 to your computer and use it in GitHub Desktop.
A simple stacktracer for lua.
local function insertNonNil(t, v)
if v then
v = tostring(v)
if #v > 0 then
table.insert(t, v)
end
end
end
local function traceback(c)
for level = 0, math.huge do
local info = debug.getinfo(c, level, "ufnSl")
if not info then
break
end
local funcType, name, args, exec, defined = {}, nil, nil, "", {}
insertNonNil(funcType, info.what)
insertNonNil(funcType, info.namewhat)
table.insert(funcType, "function")
name = info.name or "<anon>"
if info.nparams then
args = {}
for an = 1, info.nparams do
local argName, argValue = debug.getlocal(c, level, an)
if argValue ~= nil then
argName = argName .. "=" .. tostring(argValue)
end
table.insert(args, argName)
end
end
if info.isvararg then
local varargs = {n = 0}
local gotNonNilValue = false
for an = 1, math.huge, 1 do
local argName, argValue = debug.getlocal(c, level, -an)
if not argName then
break
end
if argValue ~= nil then
gotNonNilValue = true
end
varargs.n = varargs.n + 1
varargs[varargs.n] = argValue
end
if varargs.n == 0 then
table.insert(args, "...")
elseif gotNonNilValue then
for an = 1, varargs.n, 1 do
table.insert(args, tostring(varargs[an]))
end
else
table.insert(args,
("<... (%d arg%s)>"):format(varargs.n,
varargs.n == 1 and "" or "s"))
end
end
if info.currentline and info.currentline ~= -1 then
exec = ":" .. tostring(info.currentline)
end
insertNonNil(defined, info.short_src)
if info.linedefined and info.linedefined ~= -1 then
table.insert(defined, info.linedefined)
end
funcType = table.concat(funcType, " ")
args = args and ("(" .. table.concat(args, ", ") .. ")")
defined = (defined[1] and (" in " .. defined[1] .. "") or "") ..
(defined[2] and (" at L" .. defined[2]) or "")
local line = ("#%2d: %s %s%s%s%s"):format(
level,
funcType,
name,
args or "",
exec,
#defined > 0 and (" (defined" .. defined .. ")") or ""
)
print(line)
end
end
local c = coroutine.create(function()
local function outer(f, g, a, b, ...)
g("outer", a, b)
f(g, a, b, "vararg test", nil)
end
local function inner(f, a, b, ...)
coroutine.yield()
f("inner", a, b)
end
outer(inner, print, 42, 24, nil)
end)
coroutine.resume(c)
traceback(c)
--> # 0: C field function yield(...) (defined in [C])
--> # 1: Lua local function f(f=function: 0x55a46ea8c590, a=42, b=24, vararg test, nil):109 (defined in trace.lua at L108)
--> # 2: Lua local function outer(f=function: 0x55a46ee15230, g=function: 0x55a46ea8c590, a=42, b=24, <... (1 arg)>):105 (defined in trace.lua at L103)
--> # 3: Lua function <anon>():113 (defined in trace.lua at L102)
coroutine.resume(c)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment