Python 调试方法
FROM http://kamushin.github.io/debug/python.html
背景
這幾天一直在查一個線上程序 hang 住的問題. 這個程序總是在運(yùn)行50分鐘后 hang 住, 通過以下的一些調(diào)試手段,發(fā)現(xiàn)是打日志的時候因為 buffer 滿被 block 了.
Python 日志是默認(rèn)打到 stderr 的, 無論日志級別. 而我這個程序是被另一個程序調(diào)起的, 父進(jìn)程沒有接收子進(jìn)程的 stderr, 導(dǎo)致了 buffer 被打滿.
在調(diào)試的過程中, 用到了以下幾種 Python 調(diào)試手段, 于是記錄以下.
GDB
GDB是一個廣為人知的調(diào)試器, 而且線上可用, 非常贊. 但是默認(rèn)配置的 GDB 并不能打印 Python 當(dāng)前調(diào)用棧. 我們需要對其做些配置.
首先進(jìn)行g(shù)db的安裝, 需要gdb7以上版本
sudo yum install gdb python-debuginfo
然后下載這份 gdb 配置文件 http://svn.python.org/projects/python/trunk/Misc/gdbinit 到 ~/.gdbinit
對于一個線上已經(jīng)hang住的程序來說, 可以用 gdb -p pid 的形式進(jìn)行 attach, 打印出當(dāng)前調(diào)用棧.
一般來說, 必須是帶debug symbol的Python 編譯版本才能打印出足夠多的信息, 但是線上的 Python 版本往往是不帶debug symbol 的, 于是我們要修改下上述的配置文件
對~/.gdbinit 進(jìn)行上述修改, 即可成功打印出當(dāng)前 hang住進(jìn)程的調(diào)用棧.
具體到我這次遇到的問題, 在打出調(diào)用棧后發(fā)現(xiàn)是卡死在 log 模塊的 emit 上, 于是 strace 下看到果然是卡死在 write 的系統(tǒng)調(diào)用上, 順利找到了原因.
更多的用法可以看https://wiki.python.org/moin/DebuggingWithGdb, 不過大部分的用法依然需要debug symbol, 按照 wiki 來,不一定可以順利實現(xiàn).
PDB
PDB是 Python 自帶的一個調(diào)試模塊. 可以以python -m pdf xxx.py 的形式, 以調(diào)試模式啟動一個 Python 進(jìn)程.
雖然似乎不能 attach 到已運(yùn)行的進(jìn)程上, 但是提供了一個簡單快速的調(diào)試方式.
Singal AND InteractiveConsole
上述的方式都是不需要侵入代碼的, 這里再提供一種侵入代碼的方式.
import code, traceback, signaldef debug(sig, frame):"""Interrupt running process, and provide a python prompt forinteractive debugging."""d={'_frame':frame} # Allow access to frame object.d.update(frame.f_globals) # Unless shadowed by globald.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基本原理是給SIGUSR1信號加上一個handler, handler 執(zhí)行時會把當(dāng)前的變量加載到一個交互式窗口, 然后開啟交互式console, 接下來就像打開一個 REPL 一樣了, 可以查看當(dāng)前的變量值, 可以改變變量值, 可以調(diào)用函數(shù)看看結(jié)果是什么, 查看完后^d離開, 就可以讓程序繼續(xù)執(zhí)行下去.
在加好 handler 后, 我們可以用os.kill(pid, signal.SIGUSR1)的方式, 調(diào)起 handler, 進(jìn)行調(diào)試.
值得注意的是, 由于和console 的交互需要 stdout 的支持, 而父子進(jìn)程默認(rèn)是不共享 stdout 的,所以當(dāng)要調(diào)試子進(jìn)程的時候, 需要重定向子進(jìn)程的 stdout 到父進(jìn)程的 stdout, 這個很簡單,就不貼代碼了.
總結(jié)
以上是生活随笔為你收集整理的Python 调试方法的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: %@include%和jsp:inclu
- 下一篇: python/selenium/chro