《Programming in Lua 3》讀書筆記(二十)

日期:2014.7.29
PartⅢ The Standard Libraries
24 The Debug Library

Lua的debug庫並不是提供一個調試器,而是提供一些供你寫調試器的一些操作。Lua實現這些功能是以C API來實現的,因此這個庫相當於提供使用Lua代碼訪問C API,這是從性能上考慮的。
Lua的debug庫包含兩類函數:introspective function和hooks(啥東西?)。前面一個函數允許我們對調試正在運行的程序(當前運行到的行數、局部變量的名字和值等);hook則允許我們回溯程序的運行過程。
Debug庫一個重要的概念是:stack level。指的是一個代表當前階段一個特定函數的活躍值:訪問debug庫的函數其level爲1,調用該函數的函數其level爲2。

24.1 Introspective Facilities
       debug庫中主要的內省函數是debug.getinfo。該函數的第一個參數可以是一個函數也可以是一個stack level。當參數是一個函數的時候,該函數返回一個table包含該函數的一些信息:
       當參數函數爲一個C函數的時候,返回信息僅包括函數類型(Lua C)、名字、函數名的前一個字段(global local 等)
       當以數值作爲參數調用該函數的時候,該數值代表一個stack level。此時會得到一個滿足該level的函數的信息。如當n爲1的時候,會得到進行執行調用的函數,當爲0的時候,得到getinfo這個函數本身,當n大於stack中活躍函數的數量的時候,返回nil。when you query an active function,calling debug.getinfo with a number,the result table has an extra field ,currentline,with the line where the function is at that moment.Moreover,func has the function that is active at that level.
        table中的字段name需要謹慎對待,因爲lua中的函數可以沒有名字,也可以有多個名字。
        作者也提到,getinfo函數不怎麼高效。lua以一種不損害程序運行的形式保存debug的信息,efficient retrieval is a secondary goal here.getinfo函數接受一個第二參數用來選擇需要得到的信息以提高效率,此時函數不會去收集用戶不需要的信息。該參數的類型是一個字符串型,一個字母收集一類字段的信息:
'n'     收集 name 和 namewhat
'f'      收集 func
'S'     收集source,short_src,what,linedefined,和lastlinedefined
'l'      收集 currentline
'L'      收集 activeline
'u'     收集 nup
下面的例子介紹了debug.getinfo函數的使用:

 
Accessing local variables
       可以使用debug.getlocal 函數查看任意活躍函數中的局部變量,這個函數有兩個參數:訪問的函數的stack level和訪問變量的index值。該函數返回兩個值:訪問變量的名字和值。如果參數index值大於函數中總的變量的值,那麼getlocal函數返回nil。如果stack level值不符合要求,則會引起錯誤(此時的技巧是通過getinfo函數來先驗證stack level)

        函數中的局部變量依據其出現的順序進行排序,最先出現的變量其index值最小,而且只會對當前函數有效區間內的變量進行計數,舉例來看:
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,29)
打印出來的值爲:
a     10
b     29
x     nil
a     4
       函數中首先出現的局部變量是a,其index爲1,其次是b,index爲2,接着是x,index爲3,最後爲函數體內部的局部變量a,其index爲4.此時要注意的是,函數體內的局部變量c,有其自身的有效範圍,此時已經在函數體的有效範圍之外了。(局部變量只會在其初始化代碼之後可見)。
       自Lua5.2開始,負數作爲參數會得到函數的一些額外信息:index -1代表第一個額外信息,此時這個name總是"(*vararg)".---這段內容還有待考證
       也可以通過函數debug.setlocal來改變局部變量的值,該函數的前兩個參數分別爲:stack level 和變量的index值,第三個參數則爲要設定的新的值。如果index超出了範圍,則該函數會返回nil值

 
Accessing non-local variables
       使用一個lua函數:getupvalue 可以使我們訪問非局部變量。與局部變量不同的是,非局部變量是當函數非active的時候纔會存在(這也是所謂的閉包),因此該函數的第一個參數不是一個stack level ,而是一個函數(準確的說是一個閉包),第二個參數是變量的index值,其順序和局部變量一致都是先出現的index值越小。(函數不能以同一個名字訪問兩個非局部變量)
       使用setupvalue 可以用來改變非局部變量的值,與setlocal一樣,函數也是三個參數,只不過第一個參數是一個閉包。

 
Accessing other coroutines
       dubug庫中的所有內省函數都接受一個可選參數--一個協同程序作爲其第一參數,因此我們也可以從外部查看協同程序,例如:
co = coroutine.create(function ( ... )
     local x = 10

     coroutine.yield()

     error("some error")

end)


coroutine.resume(co)

print(debug.traceback(co))
運行該段代碼打印出:
stack traceback:
     [C]: in function 'yield'
       此時在yield函數內部

       回溯不會執行resume,因爲協同程序和主程序不在同一個棧中。
       當協同程序引發錯誤的時候,不會存在與當前的這個棧中。這就意味着我們可以在其引發的錯誤之後查看該協同程序,繼續上述的例子,假如我們此時再一次resume協同程序,這次便會引發錯誤:
e.g.

coroutine.resume(co)

print(debug.traceback(co))

print(coroutine.resume(co))
--打印
stack traceback:
     [C]: in function 'yield'
false     some error

       而此時再一次回溯:
e.g.

coroutine.resume(co)

print(debug.traceback(co))

print(coroutine.resume(co))

print(debug.traceback(co))
--打印
stack traceback:
     [C]: in function 'yield'
false    some error
stack traceback:
     [C]: in function 'error'
       此時便會到error函數內部了

       我們甚至可以再函數產生錯誤之後再範圍協同程序內部的局部變量
print(debug.getlocal(co,1,1))
--打印
x 10           --這個是協同程序內部的局部變量x的值

 
24.2 Hooks
        Lua debug庫的hook機制允許我們註冊一個函數的調用,然後在程序運行的某個特定時間點調用我們註冊的那個函數。這裏有四個時間點(events 事件?)可以觸發一個hook:
call events           每次lua調用一個函數的時候就觸發這個事件
return events        每次函數返回值的時候觸發這個事件
line events           每當lua開始執行新的一行代碼的時候觸發這個事件
count events          進過給定數量的指令後觸發這個事件
        Lua通常以一個參數調用hooks,這個參數是字符串型,代表某個事件:"call"("tail call"),"return","line","count".當參數是line的時候,函數也接受新的一行的行數作爲第二個參數。而如果需要得到更多關於hook內部的信息,則需要使用函數:debug.getinfo.
        使用函數debug.sethook 可以註冊一個hook,參數爲兩個可選第三個參數:第一個參數是要註冊的函數;第二個參數是一個string字符串型,代表需要觸發的事件('c','r','l'); 可選的第三參數代表我們想獲得count event事件的頻率;call,return,line事件通過第二個參數控制,而可選的第三個參數用來控制count事件。關掉一個hook,則以無參數調用sethook。
     


24.3 Profiles
Lua的debug庫不僅可以用來做debug,還可以用來(profiling??)

轉載於:https://www.cnblogs.com/zhong-dev/p/4044565.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章