2013年5月4日 星期六

pdb 的運作原理

    要了解 python debuger 的運作方式前,需要先了解 python 如何實作 function call。在 C語言中,每個被呼叫函數都有自己的 call stack拿來存放區域變數之類的東西。而在 python 中, call stack作用是由 frame object 物件完成。下面是官網對 frame object 的屬性介紹


Special read-only attributes: f_back is to the previous stack frame (towards the caller), or None if this is the bottom stack frame; f_code is the code object being executed in this frame; f_locals is the dictionary used to look up local variables;f_globals is used for global variables; f_builtins is used for built-in (intrinsic) names; f_restricted is a flag indicating whether the function is executing in restricted execution mode; f_lasti gives the precise instruction (this is an index into the bytecode string of the code object).
Special writable attributes: f_trace, if not None, is a function called at the start of each source code line (this is used by the debugger); f_exc_type, f_exc_value,f_exc_traceback represent the last exception raised in the parent frame provided another exception was ever raised in the current frame (in all other cases they are None); f_lineno is the current line number of the frame — writing to this from within a trace function jumps to the given line (only for the bottom-most frame). A debugger can implement a Jump command (aka Set Next Statement) by writing to f_lineno.

    要取出現在函數的 frame object 物件,可以使用 sys_getframe() 方法。


import sys

frame = sys._getframe()
frame.f_locals['a'] = 5
print a

    上面程式碼是一段很有趣的程式碼。我並沒有定義 a 這個變數,可是他卻不會出錯。為什麼呢? 因為 python 會把區域變數存放在 frame object 的 f_locals ( f_locals是一個 dict 物件) 裡,frame.f_locals['a'] = 5 我寫了這一行,相當建立一個區域變數 a 。所以不會出錯。

    frmae 有個很有趣的屬性,叫做  f_trace 。如果 f_trace 的值不是 None 的話,執行很一行程式碼前,都會先執行 f_trace 裡的 function。 這是很有趣的一個特性, python debuger 就是要靠 f_trace 屬性才能完成 debug 的動作。

    f_trace 的函數原型長這樣 function_name(frame, event, arg), frame 是指呼叫該 functoin 的 frame,event 是指因為什麼因素而呼叫該 function, arg 作用因不同 event 而不同。細節詳見 sys.settrace

   所以 pdb 運作原理十分簡單,就是把每個 frame 的 f_trace 指定成 pdb 的 trace_dispatch,每當程式碼要執行下一行時, python 就會呼叫 pdb.trace_dispatch 方法,檢查是否要停下來,要停下來就會呼叫 pdb.interaction  方法,不然什麼事情都不會做,只會回傳 pdb.trace_dispatch 而已。

    最後附上一個簡單的程式。這個程式會監控變數的值是否被改變,被改變的話就會呼叫 pdb。進入除錯模式。https://github.com/ya790206/example_code/blob/master/talk_about_pdb/watch_variable.py


    註:  當你把全部的 frame.f_trace 都指定成你要的 function 時,你還要呼叫 sys.settrace(your_func),這樣才算完成。不然他未必會在執行每一行前都先呼叫你的 function。

沒有留言:

張貼留言