天天看點

Python 調試方法

這幾天一直在查一個線上程式 hang 住的問題. 這個程式總是在運作50分鐘後 hang 住, 通過以下的一些調試手段,發現是打日志的時候因為 buffer 滿被 block 了. python 日志是預設打到 stderr 的, 無論日志級别. 而我這個程式是被另一個程式調起的, 父程序沒有接收子程序的 stderr, 導緻了 buffer 被打滿. 在調試的過程中, 用到了以下幾種 python 調試手段, 于是記錄以下.

gdb是一個廣為人知的調試器, 而且線上可用, 非常贊. 但是預設配置的 gdb 并不能列印 python 目前調用棧. 我們需要對其做些配置. 

首先進行gdb的安裝, 需要gdb7以上版本

<code>sudo yum install gdb python-debuginfo</code> 

然後下載下傳這份 gdb 配置檔案<code>http://svn.python.org/projects/python/trunk/misc/gdbinit</code> 到 <code>~/.gdbinit</code>

對于一個線上已經hang住的程式來說, 可以用<code>gdb -p pid</code>的形式進行 attach, 列印出目前調用棧.

一般來說, 必須是帶<code>debug symbol</code>的python 編譯版本才能列印出足夠多的資訊, 但是線上的 python 版本往往是不帶<code>debug symbol</code> 的, 于是我們要修改下上述的配置檔案

對<code>~/.gdbinit</code> 進行上述修改, 即可成功列印出目前 hang住程序的調用棧.

具體到我這次遇到的問題, 在打出調用棧後發現是卡死在 log 子產品的 emit 上, 于是 strace 下看到果然是卡死在 write 的系統調用上, 順利找到了原因.

更多的用法可以看https://wiki.python.org/moin/debuggingwithgdb, 不過大部分的用法依然需要<code>debug symbol</code>, 按照 wiki 來,不一定可以順利實作.

pdb是 python 自帶的一個調試子產品. 可以以<code>python -m pdf xxx.py</code> 的形式, 以調試模式啟動一個 python 程序. 雖然似乎不能 attach 到已運作的程序上, 但是提供了一個簡單快速的調試方式.

上述的方式都是不需要侵入代碼的, 這裡再提供一種侵入代碼的方式.

基本原理是給<code>sigusr1</code>信号加上一個handler, handler 執行時會把目前的變量加載到一個互動式視窗, 然後開啟互動式console, 接下來就像打開一個 repl 一樣了, 可以檢視目前的變量值, 可以改變變量值, 可以調用函數看看結果是什麼, 檢視完後<code>^d</code>離開, 就可以讓程式繼續執行下去. 

在加好 handler 後, 我們可以用<code>os.kill(pid, signal.sigusr1)</code>的方式, 調起 handler, 進行調試.

值得注意的是, 由于和console 的互動需要 stdout 的支援, 而父子程序預設是不共享 stdout 的,是以當要調試子程序的時候, 需要重定向子程序的 stdout 到父程序的 stdout, 這個很簡單,就不貼代碼了.