Last active
November 26, 2024 19:11
-
-
Save Fingercomp/a688d221356cb371d940b947d0ca90a8 to your computer and use it in GitHub Desktop.
A simple stacktracer for lua.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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