天天看點

Lua文法小貼士(十一)debug庫Lua的調試庫包含兩種函數:自省函數和鈎子。自省函數可以用來檢查一個正在運作的程式的資訊,而鈎子可以跟蹤程式的運作。

Lua的調試庫包含兩種函數:自省函數和鈎子。自省函數可以用來檢查一個正在運作的程式的資訊,而鈎子可以跟蹤程式的運作。

自省函數主要是debug.getinfo函數,函數聲明為:

debug.getinfo([thread,]func[,what])
           

傳回一個函數的資訊表。可以直接傳入函數,也可以傳入一個數值,這個數值表示函數(運作在指定線程)的調用棧深度。0表示getinfo自己,1表示調用getinfo的函數,以此類推(一般是2)。如果這個數值大于活動函數的深度,就會傳回nil。

傳回值是一個table,包含以下字段:

source:函數定義的位置,如果函數是通過loadstring在一個字元串中定義的,那麼source就是這個字元串。如果函數是在一個檔案中定義的,那麼source就是這個檔案名加字首‘@’。

short_src:source的短版本(最多60個字元),可用于錯誤資訊中。

linedefined:該函數定義的源代碼中第一行的行号。

lastlinedefined:該函數定義的源代碼中最後一行的行号。

what:函數的類型。如果foo是一個普通的Lua行數,則為“Lua”,如果是一個C函數,則為“C”,如果是Lua主程式塊(chunk)部分,則為“main”。

name:該函數一個适當的名稱。

namewhat:可能是“global”、“local”、“method”、“field”或“”(空字元串)。空字元串表示沒有找到該函數的名稱。

nups:該函數的upvalue的數量

activelines:一個table,包含了該函數的所有活動行的幾何。所謂“活動行”就是含有代碼的行(相對于空行或者隻包含注釋的行)。

func:函數本身。

注:當foo是一個C函數時,隻有what、name和namewhat是有意義的。

what可選參數可以指定希望擷取那些資訊。

'n' name & namewhat
'f' func
'S' source, short_src, what, linedefined & lastlinedefined
'l' currentline
'L' activelines
'u' nups

debug.getlocal可以用來檢查任意活動函數的局部變量,該函數有兩個參數:一個是希望查詢的函數棧層,另一個是變量的索引。傳回值為變量的名字和目前值。

而變量的索引是在函數中出現的順序,隻限于函數目前作用域中活躍的變量。

local function foo(a,b)
    local x
    do local c = a - b end
    local a = 1
    while true do
        local name,value = debug.getlocal(1,a)
        if not name then break end
        print(name,value)
        a = a + 1
    end
end

foo(10,20)
           

列印結果:

a    10

b    20

x    nil

a    4

另外還可以使用debug.setlocal函數改變局部變量的值,除了與debug.getlocal一樣的參數外,還有第三個參數,也就是變量的新值。

此外還有getupvalue和setupvalue,用來通路和修改非局部變量,而第一個參數需要傳入函數。

debug庫中所有的自省函數都可以在參數清單前增加一個協同程式(參考Lua文法小貼士(八)協同程式)作為第一個(可選)參數,這樣就可以從外部來檢查協同程式。

例如:

local co = coroutine.create(function()
    local x = 10
    coroutine.yield()
    error("some error")
end)
coroutine.resume(co)
print(debug.traceback(co))
print(debug.getlocal(co,1,1))
           

這樣就可以回溯協同程式,并且通路co中的局部變量。

Lua中可以使用debug.sethook來将函數設定為鈎子函數。

函數聲明:

debug.sethook([thread,] hook[, mask][, count])
           

mask和count表示什麼時候hook會被調用,mask字元串中可以包含以下字元:

"c":每當lua調用(call)函數的時候

"r":每當lua從函數傳回(return)的時候

"l":每當lua執行一行新代碼的時候。

而當count值大于零的時候,每執行完count數量的指令後會觸發鈎子。

當沒有任何參數的時候(debug.sethook)表示關閉鈎子。

例如:

debug.sethook(print,"l")
           

将print設定為鈎子函數,它會列印出程式執行到的每一行。

還可以擷取更詳細的資訊,例如:

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

這裡使用了getinfo函數來擷取目前檔案名。