强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

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 操作、注册表访问

下一章:性能优化 / Performance