lua debug相关方法详解
- [1. debug.debug()](#1. debug.debug())
- [2. debug.getinfo(func | level [, what])](#2. debug.getinfo(func | level [, what]))
- [3. debug.getlocal(func-or-level, localindex)](#3. debug.getlocal(func-or-level, localindex))
- [4. debug.setlocal(level, local_number, value)](#4. debug.setlocal(level, local_number, value))
- [5. debug.getupvalue(func, upvalue_index)](#5. debug.getupvalue(func, upvalue_index))
- [6. debug.setupvalue(func, upvalue_index, value)](#6. debug.setupvalue(func, upvalue_index, value))
- [7. debug.traceback([thread,] [message [, level]])](#7. debug.traceback([thread,] [message [, level]]))
- [8. debug.sethook(func, mask [, count])](#8. debug.sethook(func, mask [, count]))
- [9. debug.getregistry()](#9. debug.getregistry())
- [10. debug.getmetatable(object)](#10. debug.getmetatable(object))
- [11. debug.setmetatable(object, metatable)](#11. debug.setmetatable(object, metatable))
- [12. debug.upvalueid(f, n)](#12. debug.upvalueid(f, n))
- [13. debug.upvaluejoin(f1, n1, f2, n2)](#13. debug.upvaluejoin(f1, n1, f2, n2))
在 Lua 中,debug 库提供了一组强大的函数,用于调试和跟踪代码的执行,这些函数可用于查看堆栈信息、检查运行时状态、设置钩子等
1. debug.debug()
该函数在交互模式下进入调试器,允许用户逐步执行代码
lua
local function test()
local a = 10
local b = 20
print(a + b)
end
test()
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:函数是否可以挂起
lua
local function sample()
return 42
end
local 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
获取当前函数的局部变量:
lua
function test()
local a = 10
local b = 20
local name, value = debug.getlocal(1, 1) -- 获取第一个局部变量
print("Variable name value:", name, value)
end
test()
获取调用栈中上一层函数的局部变量:
lua
function outer()
local x = 100
inner()
end
function inner()
local name, value = debug.getlocal(2, 1) -- 获取外层函数的第一个局部变量
print("Outer variable name value:", name, value)
end
outer()
注意事项
- debug.getlocal 返回的表包含变量的名称和值
- 使用负数索引时,-1 表示最后一个局部变量,-2 表示倒数第二个,依此类推
- 如果函数没有局部变量,或者指定的索引超出范围,返回 nil
4. debug.setlocal(level, local_number, value)
设置指定级别的本地变量的值
lua
local function bar()
local x = 10
local y = 20
debug.setlocal(1, 1, 30) -- 将第一个本地变量的值设置为 30
print(x) -- 输出 30
end
bar()
5. debug.getupvalue(func, upvalue_index)
该函数用于获取函数的上值(upvalue)信息,上值是 Lua 中函数闭包的概念,允许函数访问其定义环境中的局部变量,通过debug.getupvalue可查看函数的上值名称和对应的值
lua
local function outer()
local x = 10
local y = 11
local function inner()
return x, y
end
return inner
end
local 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)
设置指定函数的上值
lua
local function outer()
local x = 10
local function inner()
return x
end
return inner
end
local inner = outer()
debug.setupvalue(inner, 1, 20) -- 修改上值
print(inner()) -- 输出 20
7. debug.traceback([thread,] [message [, level]])
返回当前堆栈的完整调用回溯,通常用于错误处理
lua
local function causeError()
error("An error occurred!")
end
local function errorHandler()
local message = debug.traceback("", 2)
print("Error stack trace:\n" .. message)
end
xpcall(causeError, errorHandler)
8. debug.sethook(func, mask [, count])
设置一个钩子函数,可使程序在每次调用或返回时执行指定的函数
lua
hook, mask, count = debug.sethook([function [, mask [, count]]])
- function:
- 钩子函数,当满足特定条件时会被调用
- 如果设置为 nil,则取消当前的调试钩子
- mask: 控制钩子的触发条件,可以是以下值的任意组合:
- 'c': 每次调用
- 'r': 每次返回
- 'l': 每行代码执行时
- "count":每隔 count 次执行调用钩子函数
- count(可选):
- 仅当 mask 包含"count" 时有效,指定每隔多少次执行调用钩子函数
lua
local function hook()
print("A line was executed!")
end
debug.sethook(hook, "l") -- 每次执行一行代码时调用 hook
local function sampleFunction()
print("Inside sample function.")
end
sampleFunction()
debug.sethook() -- 取消钩子
9. debug.getregistry()
返回注册表(registry)表,Lua 使用这个表来存储全局数据
lua
local registry = debug.getregistry()
print(registry) -- 输出注册表的内容
10. debug.getmetatable(object)
获取指定对象的元表(metatable)
lua
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)
设置指定对象的元表
lua
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 是否是同一个值
lua
local function outer()
local x = 10
local function inner()
return x
end
return inner
end
local inner = outer()
local upvalueId = debug.upvalueid(inner, 1)
print(upvalueId) -- 输出上值的唯一标识符
13. debug.upvaluejoin(f1, n1, f2, n2)
将两个函数的第 n1 和 n2 个上值关联起来,可将一个闭包的上值连接到另一个闭包的上值,以实现上值的共享
使用场景
- 共享状态:当需要多个闭包共享同一个变量时,可以通过 debug.upvaluejoin 来实现
- 调试和测试:在调试过程中,可能需要修改闭包的上值,以观察不同的行为
- 高级编程技巧:在某些高级编程技巧中,可能需要手动管理闭包的上值
lua
-- 定义两个闭包,每个闭包有一个上值
local function closure1()
local x = 10
return function() return x end
end
local function closure2()
local y = 20
return 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 可能会导致程序行为不可预测,因此在使用时需要非常小心
- 性能:频繁地操作上值可能会影响性能,应尽量避免在性能敏感的代码中使用
- 可维护性:过度使用调试函数可能降低代码的可读性和可维护性,应尽量在必要时使用