Python 調試方法
背景
這幾天一直在查一個線上程序 hang 住的問題. 這個程序總是在運行50分鐘后 hang 住, 通過以下的一些調試手段,發現是打日志的時候因為 buffer 滿被 block 了.
Python 日志是默認打到 stderr 的, 無論日志級別. 而我這個程序是被另一個程序調起的, 父進程沒有接收子進程的 stderr, 導致了 buffer 被打滿.
在調試的過程中, 用到了以下幾種 Python 調試手段, 于是記錄以下.
GDB
GDB是一個廣為人知的調試器, 而且線上可用, 非常贊. 但是默認配置的 GDB 并不能打印 Python 當前調用棧. 我們需要對其做些配置.
首先進行gdb的安裝, 需要gdb7以上版本
sudo yum install gdb python-debuginfo
然后下載這份 gdb 配置文件 http://svn.python.org/projects/python/trunk/Misc/gdbinit 到 ~/.gdbinit
對于一個線上已經hang住的程序來說, 可以用 gdb -p pid 的形式進行 attach, 打印出當前調用棧.
一般來說, 必須是帶 debug symbol 的Python 編譯版本才能打印出足夠多的信息, 但是線上的 Python 版本往往是不帶 debug symbol 的, 于是我們要修改下上述的配置文件
<<<< if $pc > PyEval_EvalFrameEx && $pc < PyEval_EvalCodeEx >>>> if $pc > PyEval_EvalFrameEx && $pc < PyEval_EvalCodeEx && $fp != 0
對 ~/.gdbinit 進行上述修改, 即可成功打印出當前 hang住進程的調用棧.
具體到我這次遇到的問題, 在打出調用棧后發現是卡死在 log 模塊的 emit 上, 于是 strace 下看到果然是卡死在 write 的系統調用上, 順利找到了原因.
更多的用法可以看 https://wiki.python.org/moin/DebuggingWithGdb, 不過大部分的用法依然需要 debug symbol , 按照 wiki 來,不一定可以順利實現.
PDB
PDB是 Python 自帶的一個調試模塊. 可以以 python -m pdf xxx.py 的形式, 以調試模式啟動一個 Python 進程.
雖然似乎不能 attach 到已運行的進程上, 但是提供了一個簡單快速的調試方式.
Singal AND InteractiveConsole
上述的方式都是不需要侵入代碼的, 這里再提供一種侵入代碼的方式.
import code, traceback, signaldef debug(sig, frame): """Interrupt running process, and provide a python prompt for interactive debugging.""" d={'_frame':frame} # Allow access to frame object. d.update(frame.f_globals) # Unless shadowed by global d.update(frame.f_locals)
i = code.InteractiveConsole(d) message = "Signal received : entering python shell.\nTraceback:\n" message += ''.join(traceback.format_stack(frame)) i.interact(message)
def listen(): signal.signal(signal.SIGUSR1, debug) # Register handler </pre>
基本原理是給 SIGUSR1 信號加上一個handler, handler 執行時會把當前的變量加載到一個交互式窗口, 然后開啟交互式console, 接下來就像打開一個 REPL 一樣了, 可以查看當前的變量值, 可以改變變量值, 可以調用函數看看結果是什么, 查看完后 ^d 離開, 就可以讓程序繼續執行下去.
在加好 handler 后, 我們可以用 os.kill(pid, signal.SIGUSR1) 的方式, 調起 handler, 進行調試.
值得注意的是, 由于和console 的交互需要 stdout 的支持, 而父子進程默認是不共享 stdout 的,所以當要調試子進程的時候, 需要重定向子進程的 stdout 到父進程的 stdout, 這個很簡單,就不貼代碼了.
來自:http://kamushin.github.io/debug/python.html