lua debug相关方法详解
- 1. debug.debug()
- 2. debug.getinfo(func | level [, what])
- 3. debug.getlocal(func-or-level, localindex)
- 4. debug.setlocal(level, local_number, value)
- 5. debug.getupvalue(func, upvalue_index)
- 6. debug.setupvalue(func, upvalue_index, value)
- 7. debug.traceback([thread,] [message [, level]])
- 8. debug.sethook(func, mask [, count])
- 9. debug.getregistry()
- 10. debug.getmetatable(object)
- 11. debug.setmetatable(object, metatable)
- 12. debug.upvalueid(f, n)
- 13. debug.upvaluejoin(f1, n1, f2, n2)
在 Lua 中,debug 库提供了一组强大的函数,用于调试和跟踪代码的执行,这些函数可用于查看堆栈信息、检查运行时状态、设置钩子等
1. debug.debug()
该函数在交互模式下进入调试器,允许用户逐步执行代码
local function test()local a = 10local b = 20print(a + b)
endtest()
debug.debug() -- 触发调试器
执行 debug.debug() 将转到调试模式
2. debug.getinfo(func | level [, what])
此函数返回指定函数或调用的当前状态信息,level 是调用堆栈的深度,what 用于指定返回的信息类型(默认值为"flnSu"),第一个参数可以是一个函数或一个整数,如果是函数,debug.getinfo返回关于该函数的信息,如果是整数,表示调用栈的层级
what 参数可以是:
- f:返回函数对象
- n:返回函数的名称
- l:返回当前执行的行号
- S:返回源代码文件名和行号
- L:返回当前函数的所有行号范围
- u:返回函数的未使用参数的数量
- t:返回函数类型(C 函数或 Lua 函数)
- g:返回生成器的上下文
debug.getinfo 返回一个包含调试信息的表,表中可能包含以下字段:
- name:函数的名称
- namewhat:函数名称的类型,可以是 “global”, “local”, “field”, “method” 或 “”
- func:函数对象
- source:源代码文件名(以 @ 开头表示文件,以 = 开头表示匿名函数)
- short_src:源代码文件的简短名称
- linedefined:函数定义的起始行号
- lastlinedefined:函数定义的结束行号
- currentline:当前执行的行号
- nups:函数的上值数量
- nparams:函数的参数数量
- isvararg:函数是否是可变参数函数
- istailcall:当前是否是尾调用
- isC:函数是否是 C 函数
- isyieldable:函数是否可以挂起
local function sample()return 42
endlocal info = debug.getinfo(sample)
print(info.source) -- 打印函数的源代码路径
print(info.linedefined) -- 打印函数开始的行号
3. debug.getlocal(func-or-level, localindex)
返回指定级别的本地变量的值
- func-or-level:
- 可以是一个函数,表示要获取局部变量的函数
- 也可以是一个整数,表示调用栈的层级,1 表示当前函数,2 表示调用当前函数的函数,依此类推
- localindex:
- 局部变量的索引,从 1 开始,可以使用负数来表示从最后一个局部变量开始计数
- 返回值:
- 局部变量的值
- 如果变量不存在,返回 nil
获取当前函数的局部变量:
function test()local a = 10local b = 20local name, value = debug.getlocal(1, 1) -- 获取第一个局部变量print("Variable name value:", name, value)
endtest()
获取调用栈中上一层函数的局部变量:
function outer()local x = 100inner()
endfunction inner()local name, value = debug.getlocal(2, 1) -- 获取外层函数的第一个局部变量print("Outer variable name value:", name, value)
endouter()
注意事项
- debug.getlocal 返回的表包含变量的名称和值
- 使用负数索引时,-1 表示最后一个局部变量,-2 表示倒数第二个,依此类推
- 如果函数没有局部变量,或者指定的索引超出范围,返回 nil
4. debug.setlocal(level, local_number, value)
设置指定级别的本地变量的值
local function bar()local x = 10local y = 20debug.setlocal(1, 1, 30) -- 将第一个本地变量的值设置为 30print(x) -- 输出 30
endbar()
5. debug.getupvalue(func, upvalue_index)
该函数用于获取函数的上值(upvalue)信息,上值是 Lua 中函数闭包的概念,允许函数访问其定义环境中的局部变量,通过debug.getupvalue可查看函数的上值名称和对应的值
local function outer()local x = 10local y = 11local function inner()return x, yendreturn inner
endlocal inner = outer()
local name1, value1 = debug.getupvalue(inner, 1) -- 获取上值的值
local name2, value2 = debug.getupvalue(inner, 1) -- 获取上值的值
print(name1, value1, name2, value2) -- 输出 x 10 y 11
6. debug.setupvalue(func, upvalue_index, value)
设置指定函数的上值
local function outer()local x = 10local function inner()return xendreturn inner
endlocal inner = outer()
debug.setupvalue(inner, 1, 20) -- 修改上值
print(inner()) -- 输出 20
7. debug.traceback([thread,] [message [, level]])
返回当前堆栈的完整调用回溯,通常用于错误处理
local function causeError()error("An error occurred!")
endlocal function errorHandler()local message = debug.traceback("", 2)print("Error stack trace:\n" .. message)
endxpcall(causeError, errorHandler)
8. debug.sethook(func, mask [, count])
设置一个钩子函数,可使程序在每次调用或返回时执行指定的函数
hook, mask, count = debug.sethook([function [, mask [, count]]])
- function:
- 钩子函数,当满足特定条件时会被调用
- 如果设置为 nil,则取消当前的调试钩子
- mask: 控制钩子的触发条件,可以是以下值的任意组合:
- ‘c’: 每次调用
- ‘r’: 每次返回
- ‘l’: 每行代码执行时
- “count”:每隔 count 次执行调用钩子函数
- count(可选):
- 仅当 mask 包含"count" 时有效,指定每隔多少次执行调用钩子函数
local function hook()print("A line was executed!")
enddebug.sethook(hook, "l") -- 每次执行一行代码时调用 hooklocal function sampleFunction()print("Inside sample function.")
endsampleFunction()
debug.sethook() -- 取消钩子
9. debug.getregistry()
返回注册表(registry)表,Lua 使用这个表来存储全局数据
local registry = debug.getregistry()
print(registry) -- 输出注册表的内容
10. debug.getmetatable(object)
获取指定对象的元表(metatable)
local t = {}
local mt = { __index = function(t, k) return k end }
setmetatable(t, mt)local meta = debug.getmetatable(t)
print(meta) -- 输出元表
与getmetatable主要区别
- 权限检查:
- getmetatable 会尊重 __metatable 字段的设置,如果 __metatable 为 nil,则返回 nil;如果 __metatable 为其他值,则返回该值
- debug.getmetatable 忽略 __metatable 字段的设置,总是返回对象的实际元表
- 使用场景:
- 标准用途:如果只是想获取对象的元表,并且尊重 __metatable 字段的保护机制,使用 getmetatable
- 调试用途:如果需要绕过 __metatable 的保护机制,获取对象的实际元表,使用 debug.getmetatable
- 安全性:
- getmetatable 更安全,因为它尊重 __metatable 字段的设置,可以防止未经授权的访问和修改
- debug.getmetatable 由于可以绕过保护机制,使用时需要特别小心,以免破坏程序的正常运行
11. debug.setmetatable(object, metatable)
设置指定对象的元表
local t = {}
local mt = { __index = function(t, k) return k end }
debug.setmetatable(t, mt)print(getmetatable(t)) -- 输出新的元表
与setmetatable主要区别
- 权限检查:
- setmetatable 会尊重 __metatable 字段的保护机制,如果元表被保护,则无法修改,会抛出错误
- debug.setmetatable 无视 __metatable 字段的保护机制,可以强制修改元表
- 使用场景:
- 标准用途:如果只是想设置或修改对象的元表,并且尊重 __metatable 字段的保护机制,使用 setmetatable
- 调试用途:如果需要绕过 __metatable 的保护机制,强制修改对象的元表,使用 debug.setmetatable
- 安全性:
- setmetatable 更安全,因为它尊重 __metatable 字段的保护机制,可以防止未经授权的修改
- debug.setmetatable 由于可以绕过保护机制,使用时需要特别小心,以免破坏程序的正常运行
- 返回值:
- setmetatable 返回设置元表后的对象,可以用于链式操作
- debug.setmetatable 没有返回值
12. debug.upvalueid(f, n)
返回指定函数第 n 个上值的唯一标识符, 可以用来判断两个函数引用的 upvalue 是否是同一个值
local function outer()local x = 10local function inner()return xendreturn inner
endlocal inner = outer()
local upvalueId = debug.upvalueid(inner, 1)
print(upvalueId) -- 输出上值的唯一标识符
13. debug.upvaluejoin(f1, n1, f2, n2)
将两个函数的第 n1 和 n2 个上值关联起来,可将一个闭包的上值连接到另一个闭包的上值,以实现上值的共享
使用场景
- 共享状态:当需要多个闭包共享同一个变量时,可以通过 debug.upvaluejoin 来实现
- 调试和测试:在调试过程中,可能需要修改闭包的上值,以观察不同的行为
- 高级编程技巧:在某些高级编程技巧中,可能需要手动管理闭包的上值
-- 定义两个闭包,每个闭包有一个上值
local function closure1()local x = 10return function() return x end
endlocal function closure2()local y = 20return function() return y end
end-- 创建两个闭包实例
local func1 = closure1()
local func2 = closure2()-- 获取上值的索引
local up1_name, up1_value = debug.getupvalue(func1, 1)
local up2_name, up2_value = debug.getupvalue(func2, 1)-- 连接上值
debug.upvaluejoin(func1, 1, func2, 1)print(func1()) --> 输出20 是closure2的上值,触发了共享值
注意事项
- 风险:不正确的使用 debug.upvaluejoin 可能会导致程序行为不可预测,因此在使用时需要非常小心
- 性能:频繁地操作上值可能会影响性能,应尽量避免在性能敏感的代码中使用
- 可维护性:过度使用调试函数可能降低代码的可读性和可维护性,应尽量在必要时使用