Python 調試方法

FROM http://kamushin.github.io/debug/python.htmlhtml

背景

這幾天一直在查一個線上程序 hang 住的問題. 這個程序老是在運行50分鐘後 hang 住, 經過如下的一些調試手段,發現是打日誌的時候由於 buffer 滿被 block 了.
Python 日誌是默認打到 stderr 的, 不管日誌級別. 而我這個程序是被另外一個程序調起的, 父進程沒有接收子進程的 stderr, 致使了 buffer 被打滿.
在調試的過程當中, 用到了如下幾種 Python 調試手段, 因而記錄如下.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 的, 因而咱們要修改下上述的配置文件git

<<<<         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 來,不必定能夠順利實現.github

PDB

PDB是 Python 自帶的一個調試模塊. 能夠以python -m pdf xxx.py 的形式, 以調試模式啓動一個 Python 進程.
雖然彷佛不能 attach 到已運行的進程上, 可是提供了一個簡單快速的調試方式.shell

Singal AND InteractiveConsole

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

import code, traceback, signal

def 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

基本原理是給SIGUSR1信號加上一個handler, handler 執行時會把當前的變量加載到一個交互式窗口, 而後開啓交互式console, 接下來就像打開一個 REPL 同樣了, 能夠查看當前的變量值, 能夠改變變量值, 能夠調用函數看看結果是什麼, 查看完後^d離開, 就可讓程序繼續執行下去.
在加好 handler 後, 咱們能夠用os.kill(pid, signal.SIGUSR1)的方式, 調起 handler, 進行調試.
值得注意的是, 因爲和console 的交互須要 stdout 的支持, 而父子進程默認是不共享 stdout 的,因此當要調試子進程的時候, 須要重定向子進程的 stdout 到父進程的 stdout, 這個很簡單,就不貼代碼了.ide

相關文章
相關標籤/搜索