Lua 调试
Lua - 调试
Section titled “Lua - 调试”Lua 包含一个 debug 库,提供了强大的自省(introspection)能力。它主要用于构建调试器(debuggers)或执行高级自省任务,而不是供应用程序开发人员进行日常调试(日常调试更常使用 print 语句或专用调试器)。
debug 库允许你检查执行栈(execution stack)、获取函数信息(源码、行号、变量名)、访问局部变量(local variables)和 upvalue 变量(upvalue variables)、设置钩子(hooks)进行跟踪等等。
debug 库中的关键函数
Section titled “debug 库中的关键函数”以下是一些最常用的函数:
| 函数签名 | 用途 |
|---|---|
debug.debug() | 进入交互式调试模式。你可以输入 Lua 语句检查变量。输入 cont 退出。 |
debug.getfenv(object) | 返回给定对象(函数、线程或 userdata)的环境表(environment table)。在 Lua 5.2+ 中已弃用;请使用 _ENV 和 upvalue 进行词法作用域(lexical scoping)。 |
debug.setfenv(object, table) | 设置给定对象的环境。在 Lua 5.2+ 中已弃用。 |
debug.getinfo([thread,] func_or_stacklevel [, what_string]) | 返回一个关于函数的表。func_or_stacklevel 可以是一个函数或栈级别号。what_string 指定要检索的字段(例如,‘S’ 表示源码,‘l’ 表示当前行,‘n’ 表示名称,‘f’ 表示函数对象,‘u’ 表示 upvalue 的数量)。 |
debug.getlocal([thread,] stacklevel, local_index) | 返回位于 stacklevel 栈级别的函数中,local_index 索引处的局部变量的名称和值。如果不存在此类变量则返回 nil。 |
debug.setlocal([thread,] stacklevel, local_index, value) | 将 value 赋给局部变量。返回局部变量名称或 nil。 |
debug.getupvalue(func, upvalue_index) | 返回函数 func 中,upvalue_index 索引处的 upvalue 的名称和值。 |
debug.setupvalue(func, upvalue_index, value) | 将 value 赋给 upvalue。返回 upvalue 名称或 nil。 |
debug.getmetatable(value) | 返回给定 value 的元表(metatable)。 |
debug.setmetatable(value, metatable) | 设置给定 value 的元表。 |
debug.traceback([thread,] [message_string,] [level]) | 返回一个包含栈回溯(stack traceback)的字符串。对于构造自定义错误消息很有用。 |
debug.sethook([thread,] hook_func, mask_string, count) | 设置一个钩子函数,该函数在由 mask_string(‘c’、‘r’、‘l’)和 count 指定的特定事件(函数调用、返回、行、指令计数)发生时被调用。 |
示例:栈回溯和函数信息
Section titled “示例:栈回溯和函数信息”local function level_two() print("--- Inside level_two ---") -- Get info about the current function (level_two) local info_current = debug.getinfo(1, "Snl") -- S: source, n: name, l: currentline print(string.format("Current function name: %s, source: %s, line: %d", info_current.name or "?", info_current.short_src, info_current.linedefined))
-- Get info about the caller (level_one) local info_caller = debug.getinfo(2, "Snl") if info_caller then print(string.format("Caller function name: %s, source: %s, line defined: %d", info_caller.name or "?", info_caller.short_src, info_caller.linedefined)) end
print("Stack traceback from level_two:") print(debug.traceback("Custom message from level_two", 2)) -- Start traceback from callerend
local function level_one() print("--- Inside level_one ---") level_two()end
level_one()
print("--- Back in main chunk ---")local main_info = debug.getinfo(1, "S")print("Main chunk source:", main_info.short_src)输出将显示关于函数的信息和栈回溯。确切的行号和源文件路径取决于你如何保存和运行文件。
示例:检查和修改 Upvalue
Section titled “示例:检查和修改 Upvalue”Upvalue 是由闭包(closure)使用的外部局部变量。debug 库可以检查甚至修改它们。
local function create_counter() local n = 0 -- This is an upvalue for the returned function return function() n = n + 1 return n endend
local counter1 = create_counter()print("Counter1:", counter1()) -- Output: 1print("Counter1:", counter1()) -- Output: 2
-- Inspect and modify counter1's upvalue 'n'local i = 1repeat local name, value = debug.getupvalue(counter1, i) if name then print(string.format("Upvalue index %d: name='%s', value=%s", i, name, tostring(value))) if name == "n" then print("Modifying upvalue 'n' to 100") debug.setupvalue(counter1, i, 100) end i = i + 1 enduntil not name
print("Counter1 after modification:", counter1()) -- Output: 101upvalue 示例的输出:
Counter1: 1Counter1: 2Upvalue index 1: name='n', value=2Modifying upvalue 'n' to 100Counter1 after modification: 101尽管 debug 库功能强大,但对于大多数日常调试,开发人员会使用其他技术:
- **打印调试(Print Debugging):**最简单的形式,使用
print(variable)或print(require('inspect')(variable))(配合合适的inspect.lua库美化打印表)在程序的各个点检查变量值。 - **命令行调试器(Command-Line Debuggers):**允许单步执行代码、设置断点和从命令行检查变量的工具。
mobdebug是一个著名的远程调试器库,可用于构建或集成命令行调试器。 - **图形化调试器 / IDE 集成(Graphical Debuggers / IDE Integration):**现代 IDE 和文本编辑器通常带有 Lua 调试扩展。它们提供可视化界面用于断点、单步执行、变量检查、调用栈视图等。
现代调试工具
Section titled “现代调试工具”- **VS Code with Lua Extensions:**像 Visual Studio Code 这样的编辑器,配合
lua-language-server(by sumneko/LuaLS) 或EmmyLua等扩展,提供了出色的调试支持(通常使用mobdebug或类似协议)。这是非常流行的选择。 - **ZeroBrane Studio:**一个轻量级的 Lua IDE,内置调试器,支持针对各种 Lua 引擎(LuaJIT、LÖVE、Moai 等)的本地和远程调试。开源软件。
- **Other IDE Plugins:**IntelliJ IDEA(使用 EmmyLua/IntelliJ-Lua 插件)、Eclipse(例如使用 LDT - Lua Development Tools,尽管 LDT 的活跃度有所下降)等可能提供 Lua 调试功能。
历史调试工具
Section titled “历史调试工具”- **RemDebug:**Lua 5.0/5.1 的早期远程调试器。
- **clidebugger:**一个简单的 Lua 5.1 命令行调试器。
- **SciTE:**Lua 在 Windows 上的默认 IDE 包含一些基本的调试功能。
- **Decoda:**一个带有远程调试功能的图形化调试器(近年来状态不太明朗)。
常见的学习障碍:
- 理解栈级别(Stack Levels):
debug.getinfo和debug.getlocal中的栈级别是基于当前执行函数的,从 1 开始计数。级别 0 是getinfo本身,级别 1 是调用getinfo的函数,依此类推。 - **
debug库是低级别的(Low-Level):**它是用于构建调试器的工具,而不是一个终端用户调试器。对于应用程序调试,请使用专用调试工具或 IDE。 - **作用域和环境(Scope and Environments):**理解环境(Lua 5.2+ 中的
_ENV)和 upvalue 的工作原理对于有效使用相关调试函数至关重要。
更多资源:查阅 Lua 官方参考手册中关于 debug 库的部分。对于特定的调试器工具,请参考它们各自的文档(例如 ZeroBrane Studio 文档、VS Code 扩展市场)。