快速掌握Lua 5.3 —— 調試庫 (1)


Q:什么是活動函數?

A:程序中被調用但還未執行完成的函數。

function g()
--[[ 此時函數"g"被調用但還未執行完成,是活動函數。所以這里獲取的是函數"g"的信息。
"debug.getinfo(2)"獲取的才是函數"f"的信息。]]

local x = debug.getinfo(1, "n")
for k, v in pairs(x) do
print(k, v)
end
end

function f()
-- 此時函數"f"被調用但還未執行完成,是活動函數。所以這里獲取的是函數"f"的信息。
local x = debug.getinfo(1, "n")
for k, v in pairs(x) do
print(k, v)
end
print()

g()
end

f()
--[[ result:
namewhat global
name f

namewhat global
name g
]]

Q:什么是調用棧?

A:Lua存儲活動函數所使用的棧。每個線程都有自己獨立的調用棧。

Q:什么是調用棧的級別?

A:調用調試庫函數的函數的棧級別是1,調用該函數的函數的棧級別是2,以此類推。

function foo()
-- 調用調試庫的函數。
...
end

function goo()
foo()
end

function hoo()
goo()
end

--[[ 被調用的調試庫函數棧級別為0,"foo"的棧級別為1,"goo"的棧級別為2,"hoo"的棧級別為3。
如果還有別的函數調用"hoo()",則棧級別以此類推。]]

hoo()

Q:如何查看調用棧信息?

A:

--[[ debug.traceback([thread,] [message [, level]])
首先打印"message",接着從第"level"個棧級別開始打印"thread"線程中的調用棧信息。
如果"message"不是字符串或"nil",則函數不做任何處理直接返回"message"
"thread"默認為當前線程,"level"默認為1。]]
function foo()
print(debug.traceback("This is traceback: "))
print()
print(debug.traceback("Traceback from stack_level 2: ", 2))
print()
print(debug.traceback({}))
end

function goo()
foo()
end

function hoo()
goo()
end

hoo()
--[[ results:
This is traceback:
stack traceback:
E:\a.lua:2: in function 'foo'
E:\a.lua:8: in function 'goo'
E:\a.lua:12: in function 'hoo'
E:\a.lua:15: in main chunk
[C]: in ?

Traceback from stack_level 2:
stack traceback:
E:\a.lua:8: in function 'goo'
E:\a.lua:12: in function 'hoo'
E:\a.lua:15: in main chunk
[C]: in ?

table: 00522F78
]]

Q:如何查看函數信息?

A:

--[[ debug.getinfo([thread,] f [, what])
返回一個"table",其中包含線程"thread"中的函數"f""what"指定的相關信息。
"thread"默認為當前線程。"f"可以是函數名,也可以是一個數值,如果是數值則代表該函數的棧級別。
如果通過名字指定的函數不存在,則報錯;如果通過數值指定的函數不存在,則返回"nil"
如果"what"不指定,默認情況下返回除合法行號表外的所有域:
source: 創建這個函數的"chunk"的名字。
如果"source"'@'打頭,表示這個函數定義在一個文件中,而'@'之后的部分就是文件名。
"source"'='打頭,表示之后的部分由用戶行為來決定如何表示源碼。
其它的情況下,這個函數定義在一個字符串中,而"source"正是那個字符串。
short_src: 一個“可打印版本”的"source",用於出錯信息。
linedefined: 函數定義開始處的行號。
lastlinedefined: 函數定義結束處的行號。
what: 如果函數是一個Lua函數,則為一個字符串"Lua"
如果是一個C函數,則為"C"
如果是一個"chunk"的主體部分,則為"main"
currentline: 給定函數正在執行的那一行。當提供不了行號信息的時候,"currentline"被設為-1
name: 給定函數的一個合理的名字。
因為Lua中的函數是"first-class values",所以它們沒有固定的名字。
一些函數可能是全局復合變量的值,另一些可能僅僅只是被保存在一個"table"的某個域中。
Lua會檢查函數是怎樣被調用的,以此來找到一個適合的名字。
如果它找不到名字,該域就被設置為"NULL"
namewhat: 用於解釋"name"域。
其值可以是"global""local""method""field""upvalue",或是""
這取決於函數怎樣被調用。(Lua用空串表示其它選項都不符合)
istailcall: 如果函數以尾調用形式調用,這個值為"true"。在這種情況下,當前棧級別的調用者不在棧中。
nups: 函數的"upvalue"個數。
nparams: 函數固定形參個數(對於C函數永遠是0)。
isvararg: 如果函數是一個可變參數函數則為"true"(對於C函數永遠為"true")。
func: 函數本身。
activelines: 合法行號表。
表中的整數索引用於描述函數中哪些行是有效行。
有效行指有實際代碼的行,即你可以置入斷點的行。無效行包括空行和只有注釋的行。
"what"可以指定如下參數,以指定返回值"table"中包含上面所有域中的哪些域:
'n': 包含"name""namewhat"域;
'S': 包含"source""short_src""linedefined""lastlinedefined"以及"what"域;
'l': 包含"currentline"域;
't': 包含"istailcall"域;
'u': 包含"nup""nparams"以及"isvararg"域;
'f': 包含"func"域;
'L': 包含"activelines"域;]]
-- 簡易版"debug.traceback()"。
function traceback()
local level = 1
while true do
local info = debug.getinfo(level, "Sl")
if not info then break end
if info.what == "C" then -- is a C function?
print(level, "C function")
else -- a Lua function
print(string.format("[%s]:%d", info.short_src, info.currentline))
end
level = level + 1
end
end

Q:如何調試函數局部變量信息?

A:

--[[ debug.getlocal([thread,] f, local)
返回在線程"thread"中棧級別為"f"處函數的索引為"local"的局部變量的名字和值。
"thread"默認為當前線程。此函數不僅用於訪問顯式定義的局部變量,也包括形參、臨時變量等。
函數"f"中第一個形參或是定義的第一個局部變量的索引為1,然后遵循在代碼中定義的順序索引值遞增,
只計算函數當前作用域中的活動變量。
負索引代表可變參數。-1指第一個可變參數,以此類推。如果指定的"local"處沒有變量,則返回"nil"。
如果指定的"f"越界,則報錯。(你可以調用"debug.getinfo()"來檢查棧級別是否合法)
以'('開頭的變量名表示沒有名字的變量(比如是循環控制用到的控制變量,或是去除了調試信息的代碼塊)。
"f"也可以是一個函數。這種情況下,此函數僅能返回"f"形參的名字。]]

function foo(a, b) -- 1, 2
local x -- 3
do local c = a - b end -- "c"的作用范圍只在"do-end"之間,所以不會在函數"foo"中被計數。
local a = 1 -- 4
while true do
local name, value = debug.getlocal(1, a) -- 這里的"a"是上面"local a = 1"的"a"。
if not name then break end
print(name, value)
a = a + 1 -- 索引+1,下一個變量。
end
end

foo(10, 20)
--[[ result:
a 10
b 20
x nil
a 4
]]

print()
for i = 1, 4 do
print(debug.getlocal(foo, i)) -- 提供函數名字,只能打印其形參。
end
--[[ result:
a
b
nil
nil
]]


--[[ debug.setlocal([thread,] level, local, value)
與"debug.getlocal()"的功能相對,
將"value"賦給"thread"線程中棧級別為"level"處函數的索引為"local"的局部變量。
"thread"默認為當前線程。"level"只能指定為棧級別,而不能指定為函數名稱。
關於索引以及異常返回值,參見"debug.getlocal"函數。
如果執行成功,函數返回局部變量的名字。]]

function foo(a, b) -- 1, 2
local x -- 3
do local c = a - b end -- "c"的作用范圍只在"do-end"之間,所以不會在函數"foo"中被計數。
local a = 1 -- 4
print(debug.getlocal(1, 1)) -- a 10
debug.setlocal(1, 1, 50)
print(debug.getlocal(1, 1)) -- a 50
end

foo(10, 20)

Q:如何調試”metatable”信息?

A:

--[[ debug.getmetatable(value)
返回"value"的"metatable",若"value"沒有"metatable"則返回"nil"。

debug.setmetatable(value, table)
將"value"的"metatable"設置為"table"(可以為"nil"),函數返回"value"。]]

local t1 = {__index = function (table, key)
return "metatable 1"
end
}

local t2 = {__index = function (table, key)
return "metatable 2"
end
}

local t = {}
setmetatable(t, t1)
print(t1, debug.getmetatable(t)) --> table: 00802C50 table: 00802C50
debug.setmetatable(t, t2)
print(t2, debug.getmetatable(t)) --> table: 00802D60 table: 00802D60

Q:如何調試”userdata”信息?

A:

--[[ debug.getuservalue(u)
返回關聯在"u"上的Lua值。如果"u"不是"userdata",則返回"nil"。

debug.setuservalue(udata, value)
將"value"設置為"udata"的關聯值。"udata"必須是一個"full userdata"。]]

附加:

1、盡可能只在調試過程中使用調試庫中的函數。首先,庫中一些函數的性能並不卓越。其次,它打破了Lua語言中一些基本的規則,比如函數中定義的局部變量無法在其外部被訪問。最后,你一定不希望在你的最終產品中見到它的身影,所以你可以使用,debug = nil來剔除調試庫,同時減少最終產品的大小。
2、debug.getinfo()對於”Tail Calls”,只將被包裹函數計入棧級別的計算,包裹函數不計入,

function g()
local x = debug.getinfo(1) -- 這里獲取的是函數"g"的信息。函數"f"不計入棧級別的計算。

for k, v in pairs(x) do
print(k, v)
end
end

function f()
return g()
end

f()

所以要查看”Tail Calls”的包裹函數信息,請直接指定函數名。


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
  © 2014-2022 ITdaan.com 联系我们: