On this page

Lua 调试(Debug)

Lua 调试(Debug) 全面指南

Lua 提供了一套强大的调试工具,帮助开发者诊断和修复代码问题。以下是 Lua 调试功能的完整介绍:

1. 基础调试工具

print 调试法(最简单直接)

print("变量值:", variable)  -- 打印变量值
print("执行到此处")        -- 跟踪执行流程

debug.traceback()

function problematic()
    error("出错了!")
end

local ok, err = pcall(problematic)
if not ok then
    print(debug.traceback(err))  -- 打印调用栈
end

2. 调试库核心函数

获取变量信息

-- 获取局部变量
local a = 10
local name, value = debug.getlocal(1, 1)
print(name, value)  -- 输出: a   10

-- 获取上值(闭包变量)
local function counter()
    local i = 0
    return function()
        i = i + 1
        return i
    end
end

local c = counter()
local name, value = debug.getupvalue(c, 1)
print(name, value)  -- 输出: i   0

设置变量值

debug.setlocal(1, 1, 20)  -- 修改局部变量
debug.setupvalue(c, 1, 5) -- 修改上值

3. 钩子(Hook)函数

设置钩子

local function debugHook(event, line)
    print("事件:", event, "行号:", line)
    local info = debug.getinfo(2)
    print("正在执行:", info.name or "匿名函数", "定义在:", info.short_src, info.linedefined)
end

-- 设置钩子
debug.sethook(debugHook, "l")  -- 'l' = 行事件, 'c' = 调用事件, 'r' = 返回事件

移除钩子

debug.sethook()  -- 无参数调用即移除钩子

4. 交互式调试

使用 debug.debug()

function buggyFunction()
    local x = 10
    debug.debug()  -- 进入交互调试模式
    print(x)
end

-- 调试提示符:
-- lua_debug> print(x)  -- 可以查看变量
-- lua_debug> x = 20    -- 可以修改变量
-- lua_debug> cont      -- 继续执行

5. 断点模拟

条件断点实现

function breakpoint(condition, message)
    if condition then
        message = message or "条件断点触发"
        print(message)
        debug.debug()
    end
end

-- 使用
for i = 1, 100 do
    breakpoint(i == 50, "i达到50")
end

6. 调试器集成

使用 ZeroBrane Studio

  1. 安装 ZeroBrane Studio
  2. 添加调试代码:
    require("mobdebug").start()  -- 启动调试器
    
  3. 设置断点并启动调试

使用 VSCode 的 Lua 插件

  1. 安装 Lua Debug 扩展
  2. 配置 launch.json:
    {
     "version": "0.2.0",
     "configurations": [
         {
             "type": "lua",
             "request": "launch",
             "name": "Lua Debug",
             "program": "${file}"
         }
     ]
    }
    

7. 性能分析

简单性能测量

local function measure(f, ...)
    local start = os.clock()
    local result = {f(...)}
    local duration = os.clock() - start
    return table.unpack(result), duration
end

-- 使用
local sum, time = measure(function()
    local s = 0
    for i = 1, 1e6 do s = s + i end
    return s
end)

print(string.format("耗时: %.4f秒", time))

8. 高级调试技巧

检查函数字节码

local function test(a, b)
    return a + b
end

-- 打印字节码
print(string.dump(test))

元表调试

local function debugMetaAccess(t)
    local mt = {
        __index = function(_, k)
            print("访问:", k)
            return rawget(t, k)
        end,
        __newindex = function(_, k, v)
            print("设置:", k, "=", v)
            rawset(t, k, v)
        end
    }
    return setmetatable({}, mt)
end

local t = debugMetaAccess{name = "test"}
t.name    -- 输出: 访问: name
t.age = 20 -- 输出: 设置: age = 20

9. 常见调试场景

内存泄漏检测

local function checkMemory()
    collectgarbage("collect")
    print("内存使用:", collectgarbage("count").."KB")
end

checkMemory()  -- 在关键点调用检查内存

协程调试

local co = coroutine.create(function()
    local x = 10
    coroutine.yield()
    print(x)
end)

-- 检查协程状态
print(coroutine.status(co))  -- suspended
print(debug.getlocal(co, 1, 1))  -- 获取协程局部变量

10. 调试最佳实践

  1. 模块化调试:为每个模块创建调试开关

    local DEBUG = true
    local function debugPrint(...)
        if DEBUG then print(...) end
    end
    
  2. 错误处理与调试结合

    local ok, err = pcall(buggyFunction)
    if not ok then
        logError(err, debug.traceback())
        debug.debug()  -- 进入调试
    end
    
  3. 版本控制友好:使用条件调试代码

    -- 只在开发环境启用调试
    if os.getenv("ENV") == "development" then
        require("mobdebug").start()
    end
    

Lua 的调试功能虽然不如一些集成开发环境强大,但结合这些技巧和工具,完全可以满足日常调试需求。关键是根据不同问题选择合适的调试方法。