Lua 从入门到精通 / 15 - 调试库 / Debug Library
调试库 / Debug Library
Lua 的 debug 库提供了强大的内省能力——查看调用栈、检查局部变量、设置钩子。但要注意:debug 库有性能开销,且在沙箱环境中应被禁用。
Lua’s debug library offers powerful introspection — call stack inspection, local variable access, hooks. But beware: it has performance overhead and should be disabled in sandboxes.
🟢 基础 / Basics
1. 获取函数信息 / Getting Function Info
-- debug.getinfo(level_or_func, what)
-- what: "S"=source, "l"=currentline, "n"=name, "f"=func, "u"=nups, "L"=activelines
local function foo()
local info = debug.getinfo(1) -- 1 = 当前函数
print("Source: " .. info.short_src)
print("Line: " .. info.currentline)
print("Name: " .. (info.name or "?"))
end
foo()
-- Source: stdin
-- Line: 2
-- Name: foo
-- 查看调用者的信息
local function bar()
local caller = debug.getinfo(2) -- 2 = 调用 bar 的函数
print("Called by: " .. (caller.name or "unknown"))
end
2. 获取局部变量 / Getting Local Variables
-- debug.getlocal(level, index)
-- level: 调用栈层级, index: 局部变量索引
local function dumpLocals(level)
level = level or 1
local i = 1
while true do
local name, value = debug.getlocal(level + 1, i)
if not name then break end
print(string.format(" %s = %s (%s)", name, tostring(value), type(value)))
i = i + 1
end
end
local function test()
local x = 42
local y = "hello"
local z = {1, 2, 3}
dumpLocals(1)
end
test()
-- x = 42 (number)
-- y = hello (string)
-- z = table: 0x... (table)
3. debug.traceback / Traceback
-- debug.traceback(level_or_thread, message, level)
-- 返回调用栈的字符串表示
local function a() return b() end
local function b() return c() end
local function c()
return debug.traceback("Stack trace:")
end
print(a())
-- Stack trace:
-- stdin:4: in function 'c'
-- stdin:2: in function 'b'
-- stdin:1: in function 'a'
-- stdin:6: in main chunk
🟡 进阶 / Intermediate
1. 钩子函数 / Hook Functions
-- debug.sethook(hookfunc, mask, count)
-- mask: "c"=call, "r"=return, "l"=line
-- count: 每 count 条指令触发一次
-- 行号钩子:每执行一行都触发
debug.sethook(function(event, line)
print("[HOOK] " .. event .. " at line " .. line)
end, "l")
local x = 1
local y = 2
local z = x + y
debug.sethook() -- 取消钩子
-- 函数调用钩子
local calls = {}
debug.sethook(function(event)
local info = debug.getinfo(2, "n")
if event == "call" then
calls[info.name or "?"] = (calls[info.name or "?"] or 0) + 1
end
end, "c")
-- ... 执行一些代码 ...
debug.sethook()
for name, count in pairs(calls) do
print(name .. ": " .. count .. " calls")
end
2. 修改局部变量 / Modifying Local Variables
-- debug.setlocal(level, name_or_index, value)
local function fixBug()
local x = 10
debug.setlocal(1, 1, 999) -- 将 x 修改为 999
print(x) -- 999!
end
fixBug()
3. 函数活动行 / Active Lines
-- debug.getinfo(f, "L").activelines
-- 返回函数中包含有效代码的行号集合
local function example()
local a = 1
-- comment
local b = 2
return a + b
end
local info = debug.getinfo(example, "L")
for line in pairs(info.activelines) do
print("Active line: " .. line)
end
🔴 高级 / Advanced
1. 安全隐患与沙箱 / Security & Sandboxing
-- debug 库可以让代码突破沙箱限制!
-- 例如:通过 debug.getregistry() 访问注册表
-- 通过 debug.getupvalue() 访问闭包的内部状态
-- 通过 debug.setupvalue() 修改闭包的内部状态
-- 沙箱中必须禁用 debug 库
local safeEnv = {
print = print,
math = math,
string = string,
table = table,
-- 不暴露 debug, io, os, loadfile, dofile
}
-- 加载代码到沙箱
local fn = load("print(debug.getinfo(1))", "sandbox", "t", safeEnv)
fn() -- 错误:debug 未定义
-- 检查代码是否使用了 debug 库
local function isSafe(code)
return not code:match("debug%s*[.%(]")
end
2. 简易调试器实现 / Simple Debugger
-- 基于行钩子的简易调试器
local function simpleDebugger()
local breakpoints = {}
local stepMode = false
debug.sethook(function(event, line)
if stepMode or breakpoints[line] then
print(string.format("Stopped at line %d", line))
while true do
io.write("debug> ")
local cmd = io.read("*l")
if cmd == "c" then -- continue
stepMode = false
break
elseif cmd == "s" then -- step
stepMode = true
break
elseif cmd == "l" then -- locals
dumpLocals(3)
elseif cmd == "bt" then -- backtrace
print(debug.traceback("", 3))
elseif cmd == "q" then -- quit
debug.sethook()
error("Debugger quit")
elseif cmd and cmd:match("^b (%d+)$") then
local ln = tonumber(cmd:match("^b (%d+)$"))
breakpoints[ln] = true
print("Breakpoint set at line " .. ln)
end
end
end
end, "l")
end
-- 调试器命令:
-- c 继续执行 / continue
-- s 单步 / step
-- l 查看局部变量 / list locals
-- bt 调用栈 / backtrace
-- b N 在第 N 行设置断点 / set breakpoint at line N
-- q 退出调试器 / quit
3. upvalue 操作 / Upvalue Manipulation
-- debug.getupvalue(func, index) — 获取闭包的 upvalue
-- debug.setupvalue(func, index, value) — 修改闭包的 upvalue
function makeCounter()
local count = 0
return function()
count = count + 1
return count
end
end
local counter = makeCounter()
print(counter()) -- 1
print(counter()) -- 2
-- 查看 upvalue
local name, value = debug.getupvalue(counter, 1)
print(name, value) -- count 2
-- 修改 upvalue
debug.setupvalue(counter, 1, 100)
print(counter()) -- 101
小结 / Summary
| 层级 | 你需要知道的 / What You Need to Know |
|---|---|
| 🟢 基础 | debug.getinfo、debug.getlocal、debug.traceback |
| 🟡 进阶 | sethook(line/call/return)、setlocal、activelines |
| 🔴 高级 | 沙箱安全、调试器实现、upvalue 操作、注册表访问 |