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 可能會導致程序行為不可預測,因此在使用時需要非常小心
- 性能:頻繁地操作上值可能會影響性能,應盡量避免在性能敏感的代碼中使用
- 可維護性:過度使用調試函數可能降低代碼的可讀性和可維護性,應盡量在必要時使用