Lua 調試庫


Lua 調試庫

http://blog.csdn.net/vermilliontear/article/details/50851045

http://blog.csdn.net/vermilliontear/article/details/50865156

 

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”的包裹函數信息,請直接指定函數名。

 

Q:如何調試”Closure”的”upvalue”信息?

A:

--[[ debug.getupvalue(f, up) 返回函數("Closure")"f"的第"up"個"upvalue"的名字和值。 Lua按照"upvalues"在匿名函數中出現的順序對其編號。如果指定的"up"索引越界,則返回"nil"。 以'('開頭的變量名表示沒有名字的變量(比如是循環控制用到的控制變量,或是去除了調試信息的代碼塊)。 debug.setupvalue(f, up, value) 與"debug.setupvalue()"的功能相對,將函數"f"("Closure")的第"up"個"upvalue"的值設置為"value"。 函數返回被設置的"upvalue"的名字。如果指定的"up"索引越界,則返回"nil"。 注:獲取與設置"upvalue"與"Closure"是否被調用(是否在調用棧上)無關。]] -- "Closure"。 function newCounter () local n = 0 local k = 0 return function () k = n n = n + 1 return n end end counter = newCounter() print(counter()) print(counter()) -- 此時"k"是1,"n"是2。 local i = 1 repeat name, val = debug.getupvalue(counter, i) if name then print ("index", i, name, "=", val) -- 依次輸出兩個"upvalues"的名字和值。 if(name == "n") then debug.setupvalue (counter, 2, 10) -- 設置"n"的值為10。 end i = i + 1 end until not name -- 此時"n"的值被設置為10。 print(counter()) -- 在此調用后"n"的值被加1,變為11。 --[[ results: 1 2 index 1 k = 1 index 2 n = 2 11 ]] --[[ debug.upvaluejoin(f1, n1, f2, n2) 讓"Closure""f1"的第"n1"個"upvalue"引用"Closure""f2"的第"n2"個"upvalue"。 debug.upvalueid(f, n) 返回指定"Closure""f"的第"n"個"upvalue"的標識符 (一個輕量用戶數據,每個"upvalue"的標識符唯一)。 這個標識符可以讓程序檢查兩個不同的"Closure"是否共享了相同的"upvalue(s)"。 ]] function newCounter() local n = 0 local k = 0 return function () k = n n = n + 1 return n end end counter = newCounter() function newCounter1() local n = 0 local k = 0 return function () k = n n = n + 1 return n end end counter1 = newCounter1() -- 每個"upvalue"都有自己獨有的ID。 print(debug.upvalueid(counter, 1)) --> userdata: 00559300 print(debug.upvalueid(counter, 2)) --> userdata: 00559348 print(debug.upvalueid(counter1, 1)) --> userdata: 005593D8 print(debug.upvalueid(counter1, 2)) --> userdata: 00559420 -- 讓"counter"的第一個"upvalue"引用"counter1"的第二個"upvalue"。 debug.upvaluejoin(counter, 1, counter1, 2) -- "counter"的第一個"upvalue"與"counter1"的第二個"upvalue"的ID相同。 print(debug.upvalueid(counter, 1)) --> userdata: 00559420 print(debug.upvalueid(counter, 2)) --> userdata: 00559348 print(debug.upvalueid(counter1, 1)) --> userdata: 005593D8 print(debug.upvalueid(counter1, 2)) --> userdata: 00559420

Q:如何追蹤程序的運行?

A:

--[[ debug.sethook([thread,] hook, mask [, count]) 將函數"hook"設置為線程"thread"的鈎子函數。 "mask"決定鈎子函數何時被觸發,"count"決定何時額外的調用一次鈎子函數。 "thread"默認為當前線程。"count"默認為0, 鈎子函數將在每運行"count"條指令時額外的調用一次鈎子函數,向鈎子函數傳遞事件"count"。 "mask"可以指定為如下值的一個或多個: 'c': 每當Lua調用一個函數時,調用鈎子函數,向鈎子函數傳遞事件"call"或"tail call"; 'r': 每當Lua從一個函數內返回時,調用鈎子函數,向鈎子函數傳遞事件"return"; 'l': 每當Lua進入新的一行時,調用鈎子函數,向鈎子函數傳遞事件"line"。 當鈎子函數被調用時,第一個參數是觸發這次調用的事件。對於"line"事件,有第二個參數,為當前行號。 函數不傳參,則為關閉鈎子函數。]] debug.sethook(print, "crl") function foo() local a = 1 end local x = 1 foo() local y = 1 --[[ results: return nil line 5 line 3 line 7 line 8 call nil line 4 line 5 return nil line 9 return nil return nil ]] --[[ debug.gethook([thread]) 返回鈎子函數的內存地址,鈎子函數的掩碼,"debug.sethook()"為鈎子函數設置的"count"。]] debug.sethook(print, "l", 9) print(debug.gethook()) debug.sethook() -- 關閉鈎子函數。 print(debug.gethook()) -- 沒有鈎子函數就什么都獲取不到了。 --[[ results: line 2 function: 013D1A70 l 9 line 3 nil 0 ]]

Q:如何查看Lua的注冊表信息?

A:

--[[ debug.getregistry() 函數返回Lua的"registry"。]]

Q:如何創建一個程序分析器?

A:調式庫除了用於調式以外還可以用於完成其他任務,這種常見的任務就是分析。對於一個實時的分析來說,最好使用C接口來完成。對於每一個鈎子函數其使用的Lua調用代價太大,並且通常會導致測量的結果不准確。然而,對於計數分析來說,Lua可以很好的勝任。

-- 一個記錄程序中函數被調用次數的小型基本分析器。 local Counters = {} -- key-value: 函數-計數 local Names = {} -- key-value:函數-函數名 local function hook() local f = debug.getinfo(2, "f").func -- 獲取被調用的函數本身。 if Counters[f] == nil then -- 如果是第一次被調用。 Counters[f] = 1 Names[f] = debug.getinfo(2, "Sn") -- 獲取函數信息。 else -- 如果之前被記錄過,這里只是增加其計數。 Counters[f] = Counters[f] + 1 end end local f = assert(load("print('Hello World!')")) debug.sethook(hook, "c") -- 當函數被調用時調用鈎子函數。 f() debug.sethook() -- 關閉鈎子函數。 -- 獲取結果。 function getname(func) local n = Names[func] if n.what == "C" then -- 如果是C函數,只返回其名字。 return n.name end -- 如果不是C函數,返回"[file]:line"的形式。 local loc = string.format("[%s]:%s", n.short_src, n.linedefined) if n.namewhat ~= "" then -- 如果不是匿名函數,返回一個合理的名字,"[file]:line (name)"。 return string.format("%s (%s)", loc, n.name) else -- 否則只返回"[file]:line"的形式。 return string.format("%s", loc) end end for func, count in pairs(Counters) do print(getname(func), count) end --[[ results: Hello World! [[string "print('Hello World!')"]]:0 (f) 1 print 1 sethook 1 nil 1 <-- 這個不知道是什么函數。 ]]

附加:

1、在鈎子函數內,你可以調用”debug.getinfo()”,指定棧級別為2, 來獲得正在運行的函數的詳細信息(”debug.getinfo()”的棧級別為0,鈎子函數的棧級別為1)。 
2、一個打印文件名及行號的精致的追蹤器,

function trace(event, line) local s = debug.getinfo(2).short_src print(s .. ":" .. line) end debug.sethook(trace, "l")

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM