python也有類似C語言gdb的工具pdb被啼,記錄下pdb的使用方法和心得。
基礎使用
先找了段簡單的測試程序:
#!/usr/bin/python
from ftplib import FTP
import sys
import socket
import pdb
def passwordCorrect(ip,port,username,password):
try:
client = FTP()
pdb.set_trace()
client.connect(ip,port)
client.login(username,password)
client.close()
except Exception, e:
pdb.set_trace()
client.close()
if str(e).find('unknown IP address')!=-1:
return 2
return 0
print "correct"
return 1
if __name__ == '__main__':
socket.setdefaulttimeout(3)
ret = passwordCorrect('127.0.0.1',21,'test','test')
print "return is ",ret
pdb的常用命令說明:
l #查看運行到哪行代碼
n #單步運行端姚,跳過函數(shù)
s #單步運行,可進入函數(shù)
p 變量 #查看變量值
b 行號 #斷點設置到第幾行
b #顯示所有斷點列表
cl 斷點號 #刪除某個斷點
cl #刪除所有斷點
c #跳到下一個斷點
r #return當前函數(shù)
exit #退出
調(diào)試記錄:
pdb設置斷點可以在程序里加入:
import pdb
在需要設置斷點的地方加入pdb.set_trace()
執(zhí)行 python pdb_test.py
1 [root@wh practice]# vim test.py
2 [root@wh practice]# python -m pdb test.py
3 > /home/practice/test.py(2)<module>()
4 -> from ftplib import FTP
5 (Pdb) c
6 > /home/practice/test.py(10)passwordCorrect()
7 -> client.connect(ip,port)
按c逐個執(zhí)行到下一個斷點挤悉,按p ip 就可以查看變量ip的值
exit退出當前函數(shù)
補充
使用 PDB 調(diào)試
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 “l(fā)oad cmp logs error!”
506 raise Exception, “l(fā)oad 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 #查看全部棧內(nèi)變量
- b(reak)醉蚁, 設置斷點蕾殴,例如 “b 77″员舵,就是在當前腳本的77行打上斷點棒仍,還能輸入函數(shù)名作為參數(shù),斷點就打到具體的函數(shù)入口抡蛙,如果只敲b护昧,會顯示現(xiàn)有的全部斷點
(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)惋耙,如果后面帶有參數(shù),就是清除指定的斷點(我在Python2.4上從來沒成功過!U篱弧R=稹);如果不帶參數(shù)就是清除所有的斷點
(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),讓程序運行下一行选泻,如果當前語句有一個函數(shù)調(diào)用冲粤,用n是不會進入被調(diào)用的函數(shù)體中的
s(tep),跟n相似页眯,但是如果當前有一個函數(shù)調(diào)用梯捕,那么s會進入被調(diào)用的函數(shù)體中
c(ont(inue)),讓程序正常運行窝撵,直到遇到斷點
j(ump)傀顾,讓程序跳轉到指定的行數(shù)
(Pdb) j 497
/home/jchen/regression/regressionLogCMP.py(497)compareLog()
-> pdb.set_trace()
- a(rgs),打印當前函數(shù)的參數(shù)
(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),退出調(diào)試
使用gdb調(diào)試Python進程
有時我們會想調(diào)試一個正在運行的Python進程魁兼,或者一個Python進程的coredump婉徘。例如現(xiàn)在遇到一個mod_wsgi的進程僵死了,不接受請求咐汞,想看看究竟是運行到哪行Python代碼呢盖呼?這時就需要祭出gdb了。
主要是三步:
1)確保你的gdb版本>=7
2)安裝python-debuginfo包(如:python-debuginfo-2.6.6-29.el6_2.2.x86_64.rpm化撕,這個版本號一定要跟你所用的python版本一致(可以rpm -qa|grep python查看你安裝的python的詳細版本號)几晤。找包http://debuginfo.centos.org/6/x86_64/)
3)就可以用#gdb python 進程號,進行調(diào)試了侯谁。需要在系統(tǒng)安裝 gdb
和 Python debug 擴展锌仅。Python debug 擴展包含了一些 debug 命令,并且添加了一些 Python 特定的命令到gdb
墙贱。在主流的 Linux 發(fā)行版中热芹,你可以輕松的安裝他們:
Fedora:
sudo yum install gdb python-debuginfo
Ubunt:
sudo apt-get install gdb python2.7-dbg
在一些老系統(tǒng)上面,也一樣可以使用 gdb
惨撇,具體看文章末尾伊脓。
準備
1. 確認你的gdb版本是>=7,gdb從版本7開始支持對Python的debug。
2.確認gdb連接的Python是所要debug的Python报腔,否則請重新編譯gdb株搔。
方法:
$ 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)]
在一些追求穩(wěn)定的發(fā)行版(例如CentOS),Python的版本會較低纯蛾,這時都會自己編譯一個Python使用纤房。而從源里安裝的gdb會連接源里Python的版本。例如在CentOS 5.4翻诉,源里的Python是2.4.3炮姨,從源安裝的gdb也會連接到Python 2.4.3。
使用 GDB
有兩種可行的方法:
- 一開始就使用
gdb
來啟動應用 - 連接到一個已經(jīng)運行的 Python 進程
在 gdb
下面啟動 Python 同樣有兩種方式:
交互式:
...
(gdb) run <programname>.py <arguments>
自動:
$ gdb -ex r --args python <programname>.py <arguments>
這樣的話碰煌,它會一直運行直到退出舒岸、段錯誤、或者人為的停止(使用 Ctrl+C
)芦圾。
如果進程已經(jīng)開始運行蛾派,你可以通過 PID 來接入它:
$ gdb python <pid of running process>
調(diào)試進程
如果你的程序段錯誤了, gdb
會自動暫停程序个少,這樣你可以切換到 gdb
命令行來檢查狀態(tài)洪乍。你也可以人為地使用Ctrl+C
來暫停程序運行。
查看 EasierPythonDebugging獲得 gdb
里面的 Python 命令列表夜焦。
查看 C 調(diào)用棧
如果你在 debug 段錯誤典尾,你最想做的可能就是查看 C 調(diào)用棧。
在 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
...
運氣好的話钾埂,你可以直接看到問題出現(xiàn)在什么地方。如果它提供的信息不能直接幫你解決問題科平,你可以嘗試繼續(xù)追蹤調(diào)用棧褥紫。調(diào)式的結果取決于 debug 信息的有效程度。
查看 Python 調(diào)用棧
如果你安裝了 Python 擴展瞪慧,你可以使用
(gdb) py-bt
可以獲取熟悉的 Python 源代碼髓考。
對掛住的進程開刀
如果一個進程看上去掛住了,他可能在等待什么東西(比如鎖弃酌、IO 等等)氨菇。也有可能在拼命的跑循環(huán)。連接上這個進程妓湘,然后檢查調(diào)用棧也許可以幫上忙查蓉。
如果進程在瘋狂循環(huán),你可以先讓它運行一會榜贴,使用 cont
命令豌研,然后使用 Ctrl+C
來暫停,并且打印出調(diào)用棧。
如果一些線程卡住了鹃共,下面的命令可能會幫上忙:
(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
...
引用
- http://fedoraproject.org/wiki/Features/EasierPythonDebugging
- https://code.google.com/p/spyderlib/wiki/HowToDebugDeadlock
老系統(tǒng)上的 GDB
有時候你需要在老系統(tǒng)上面安裝 gdb
晶衷,這時候你可能需要下列信息:
GDB Macros
一些隨著 Python 發(fā)布的 GDB 腳本可以用來調(diào)試 Python 進程。你可以把 Python 源碼里面的 Misc/gdbinit
拷貝到~/.gdbinit
阴孟,或者從Subversion來拷貝他們房铭。請注意你的 Python,確保使用正確的代碼版本温眉,否則有些功能可能無法工作。
請注意有些新的 GDB 命令只有在 debug 需要的庫存在才能正常工作翁狐。
這個腳本在 Ubuntu 上面的 gcc 4.5.2 工作時类溢,會爆出錯誤No symbol "co" in current context.
,是因為call_function
在PyEval_EvalFrameEx 和PyEval_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