python linux 調試

使用 PDB 調試

python -m pdb 1.py

-m 的話,就會默認斷在第一行代碼

  • h(elp),會打印當前版本Pdb可用的命令,如果要查詢某個命令,可以輸入 h [command],例如:“h l” — 查看list命令
  • l(ist),可以列出當前將要運行的代碼塊

(Pdb) l
497 pdb.set_trace()
498 base_data = {}
499 new_data = {}
500 try:
501 execfile(base_file_name,{},base_data)
502 -> execfile(new_file_name,{},new_data)
503 except:
504 logger.writeLog(“error! load result log error!”)
505 print “load cmp logs error!”
506 raise Exception, “load cmp logs error!”
507

>斷點設置
   (Pdb)b  10 #斷點設置在本py的第10行
   或(Pdb)b  ots.py:20 #斷點設置到 ots.py第20行
   刪除斷點(Pdb)b #查看斷點編號
            (Pdb)cl 2 #刪除第2個斷點
    
>運行
    (Pdb)n #單步運行
    (Pdb)s #細點運行 也就是會下到,方法
    (Pdb)c #跳到下個斷點
>查看
    (Pdb)p param #查看當前 變量值
    (Pdb)l #查看運行到某處代碼
    (Pdb)a #查看全部棧內變量

  • b(reak), 設置斷點,例如 “b 77″,就是在當前腳本的77行打上斷點,還能輸入函數名作爲參數,斷點就打到具體的函數入口,如果只敲b,會顯示現有的全部斷點

(Pdb) b 504
Breakpoint 4 at /home/jchen/regression/regressionLogCMP.py:504

  • condition bpnumber [condition],設置條件斷點,下面語句就是對第4個斷點加上條件“a==3”

(Pdb) condition 4 a==3
(Pdb) b
Num Type Disp Enb Where
4 breakpoint keep yes at /home/jchen/regression/regressionLogCMP.py:504
stop only if a==3

  • cl(ear),如果後面帶有參數,就是清除指定的斷點(我在Python2.4上從來沒成功過!!!);如果不帶參數就是清除所有的斷點

(Pdb) cl
Clear all breaks? y

  • disable/enable,禁用/激活斷點

(Pdb) disable 3
(Pdb) b
Num Type Disp Enb Where
3 breakpoint keep no at /home/jchen/regression/regressionLogCMP.py:505

  • n(ext),讓程序運行下一行,如果當前語句有一個函數調用,用n是不會進入被調用的函數體中的
  • s(tep),跟n相似,但是如果當前有一個函數調用,那麼s會進入被調用的函數體中
  • c(ont(inue)),讓程序正常運行,直到遇到斷點
  • j(ump),讓程序跳轉到指定的行數

(Pdb) j 497
> /home/jchen/regression/regressionLogCMP.py(497)compareLog()
-> pdb.set_trace()

  • a(rgs),打印當前函數的參數

(Pdb) a
_logger =
_base = ./base/MRM-8137.log
_new = ./new/MRM-8137.log
_caseid = 5550001
_toStepNum = 10
_cmpMap = {‘_bcmpbinarylog’: ‘True’, ‘_bcmpLog’: ‘True’, ‘_bcmpresp’: ‘True’}

  • p,最有用的命令之一,打印某個變量

(Pdb) p _new
u’./new/MRM-8137.log’

  • !,感嘆號後面跟着語句,可以直接改變某個變量
  • q(uit),退出調試

使用gdb調試Python進程

準備

1. 確認你的gdb版本是>=7,gdb從版本7開始支持對Python的debug

2.確認gdb連接的Python是所要debug的Python,否則請重新編譯gdb。

方法:

1
2
3
4
5
6
7
$ gdb
(gdb) python
> import sys
>print sys.version
>end
2.4.3 ( #1, Sep 21 2011, 19:55:41) 
[GCC 4.1.2 20080704 (Red Hat 4.1.2-51)]

在一些追求穩定的發行版(例如CentOS),Python的版本會較低,這時都會自己編譯一個Python使用。而從源裏安裝的gdb會連接源裏Python的版本。例如在CentOS 5.4,源裏的Python是2.4.3,從源安裝的gdb也會連接到Python 2.4.3。


使用 GDB

有兩種可行的方法:

  1. 一開始就使用 gdb 來啓動應用
  2. 連接到一個已經運行的 Python 進程

gdb 下面啓動 Python 同樣有兩種方式:

交互式:

$ gdb python
...
(gdb) run <programname>.py <arguments>

自動:

$ gdb -ex r --args python <programname>.py <arguments>

這樣的話,它會一直運行直到退出、段錯誤、或者人爲的停止(使用 Ctrl+C)。

如果進程已經開始運行,你可以通過 PID 來接入它:

$ gdb python <pid of running process>

調試進程

如果你的程序段錯誤了, gdb 會自動暫停程序,這樣你可以切換到 gdb命令行來檢查狀態。你也可以人爲地使用Ctrl+C 來暫停程序運行。

查看 EasierPythonDebugging獲得 gdb 裏面的 Python 命令列表。

查看 C 調用棧

如果你在 debug 段錯誤,你最想做的可能就是查看 C 調用棧。

gdb 的命令行裏面,只要運行一下命令:

(gdb) bt
#0  0x0000002a95b3b705 in raise () from /lib/libc.so.6
#1  0x0000002a95b3ce8e in abort () from /lib/libc.so.6
#2  0x00000000004c164f in posix_abort (self=0x0, noargs=0x0)
    at ../Modules/posixmodule.c:7158
#3  0x0000000000489fac in call_function (pp_stack=0x7fbffff110, oparg=0)
    at ../Python/ceval.c:3531
#4  0x0000000000485fc2 in PyEval_EvalFrame (f=0x66ccd8)
    at ../Python/ceval.c:2163
...

運氣好的話,你可以直接看到問題出現在什麼地方。如果它提供的信息不能直接幫你解決問題,你可以嘗試繼續追蹤調用棧。調式的結果取決於 debug 信息的有效程度。

查看 Python 調用棧

如果你安裝了 Python 擴展,你可以使用

(gdb) py-bt

可以獲取熟悉的 Python 源代碼。

對掛住的進程開刀

如果一個進程看上去掛住了,他可能在等待什麼東西(比如鎖、IO 等等)。也有可能在拼命的跑循環。連接上這個進程,然後檢查調用棧也許可以幫上忙。

如果進程在瘋狂循環,你可以先讓它運行一會,使用 cont 命令,然後使用 Ctrl+C 來暫停,並且打印出調用棧。

如果一些線程卡住了,下面的命令可能會幫上忙:

(gdb) info threads
  Id   Target Id         Frame
  37   Thread 0xa29feb40 (LWP 17914) "NotificationThr" 0xb7fdd424 in __kernel_vsyscall ()
  36   Thread 0xa03fcb40 (LWP 17913) "python2.7" 0xb7fdd424 in __kernel_vsyscall ()
  35   Thread 0xa0bfdb40 (LWP 17911) "QProcessManager" 0xb7fdd424 in __kernel_vsyscall ()
  34   Thread 0xa13feb40 (LWP 17910) "python2.7" 0xb7fdd424 in __kernel_vsyscall ()
  33   Thread 0xa1bffb40 (LWP 17909) "python2.7" 0xb7fdd424 in __kernel_vsyscall ()
  31   Thread 0xa31ffb40 (LWP 17907) "QFileInfoGather" 0xb7fdd424 in __kernel_vsyscall ()
  30   Thread 0xa3fdfb40 (LWP 17906) "QInotifyFileSys" 0xb7fdd424 in __kernel_vsyscall ()
  29   Thread 0xa481cb40 (LWP 17905) "QFileInfoGather" 0xb7fdd424 in __kernel_vsyscall ()
  7    Thread 0xa508db40 (LWP 17883) "QThread" 0xb7fdd424 in __kernel_vsyscall ()
  6    Thread 0xa5cebb40 (LWP 17882) "python2.7" 0xb7fdd424 in __kernel_vsyscall ()
  5    Thread 0xa660cb40 (LWP 17881) "python2.7" 0xb7fdd424 in __kernel_vsyscall ()
  3    Thread 0xabdffb40 (LWP 17876) "gdbus" 0xb7fdd424 in __kernel_vsyscall ()
  2    Thread 0xac7b7b40 (LWP 17875) "dconf worker" 0xb7fdd424 in __kernel_vsyscall ()
* 1    Thread 0xb7d876c0 (LWP 17863) "python2.7" 0xb7fdd424 in __kernel_vsyscall ()

當前運行的線程被標記爲 *,要查看 Python 代碼運行到哪裏,使用 py-list 查看:

(gdb) py-list
2025        # Open external files with our Mac app
2026        if sys.platform == "darwin" and 'Spyder.app' in __file__:
2027            main.connect(app, SIGNAL('open_external_file(QString)'),
2028                         lambda fname: main.open_external_file(fname))
2029
>2030        app.exec_()
2031        return main
2032
2033
2034    def __remove_temp_session():
2035        if osp.isfile(TEMP_SESSION_PATH):

查看所有進程的 Python 代碼位置,可以使用:

(gdb) thread apply all py-list
...
 200
 201        def accept(self):
>202            sock, addr = self._sock.accept()
 203            return _socketobject(_sock=sock), addr
 204        accept.__doc__ = _realsocket.accept.__doc__
 205
 206        def dup(self):
 207            """dup() -> socket object

Thread 35 (Thread 0xa0bfdb40 (LWP 17911)):
Unable to locate python frame

Thread 34 (Thread 0xa13feb40 (LWP 17910)):
 197            for method in _delegate_methods:
 198                setattr(self, method, dummy)
 199        close.__doc__ = _realsocket.close.__doc__
 200
 201        def accept(self):
>202            sock, addr = self._sock.accept()
 203            return _socketobject(_sock=sock), addr
...

引用

老系統上的 GDB

有時候你需要在老系統上面安裝 gdb,這時候你可能需要下列信息:

GDB Macros

一些隨着 Python 發佈的 GDB 腳本可以用來調試 Python 進程。你可以把 Python 源碼裏面的 Misc/gdbinit 拷貝到~/.gdbinit,或者從Subversion來拷貝他們。請注意你的 Python,確保使用正確的代碼版本,否則有些功能可能無法工作。

請注意有些新的 GDB 命令只有在 debug 需要的庫存在才能正常工作。

這個腳本在 Ubuntu 上面的 gcc 4.5.2 工作時,會爆出錯誤No symbol "co" in current context.,是因爲call_functionPyEval_EvalFrameExPyEval_EvalCodeEx 之間。重新使用make "CFLAGS=-g -fno-inline -fno-strict-aliasing"編譯 Python 可以解決這個問題。

使用 Python Stack Traces GDB 腳本

在 gdb 命令行裏,可以這樣查看 Python stack trace:

(gdb) pystack

同樣的,可以獲取一列 stack frame 的 Python 變量:

(gdb) pystackv

更多 gdbinit 裏面沒定義的有用的腳本可以在這裏找到:

http://web.archive.org/web/20070915134837/http://www.mashebali.com/?Python_GDB_macros:The_Macros


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章