使用gdb調試Python進程

使用gdb調試Python進程

準備

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

2.確認gdb鏈接的Python是所要debug的Python,不然請從新編譯gdb。node

方法:python

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。linux

編譯時注意,要把本身編譯的Python路徑加到PATH環境變量裏,這樣gdb configure的時候纔會找到新版Python並鏈接web

3.下載libpython.pycentos

如何Debug

假設要debug的進程號爲1000bash

1
$ gdb -p 1000

使用此命令便可使gdb附加到進程。svn

載入libpython腳本

  • 若是你的gdb是redhat或fedora等廠商修改過的,會有--python選項,使用此選項便可指定gdb啓動時載入的Python擴展腳本(此腳本是擴展gdb的,不是咱們須要debug的腳本)。
    1
    $ gdb --python  /path/to/libpython  .py -p 1000
  • 若是安裝的是GNU的gdb,就須要打開gdb後手動載入libpython.py腳本

     

    1
    2
    3
    4
    5
    6
    (gdb) python
    >  import  sys
    >sys.path.insert(0,  '/path/to/libpython.py'  )
    >  import  libpython
    >end
    (gdb)

這時就可使用py-bt命令打印當前線程的Python traceback了函數

libpython還提供不少命令,例如py-print打印變量,py-locals打印全部本地變量等等,詳細可打開libpython.py查看。post

一點經驗

  • 在gdb可使用generate-core-file命令生成一個coredump文件。以後能夠用gdb –core來打開coredump文件進行debug。避免一直attach住進程,能夠快速重啓恢復服務
  • gdb-heap是gdb的一個擴展。能夠打印Python的內存使用狀況

參考資料

 

 

使用gdb調試python腳本

 

調試python腳本通常可經過記錄log和使用python自帶的pdb模塊完成, 但凡事總有例外,在如下三種狀況時上述方法就無能爲力了。
   1 段錯誤
   2 運行中的daemon程序
   3 core dump
這個時候就需祭出gdb進行調試。python2.6的源碼中提供了部分預約義函數以便你們使用gdb調試,咱們只需將文件Python-2.6/Misc/gdbinit所包括的內容加入到用戶目錄下的.gdbinit文件中便可,這樣每次啓動gdb時會自動完成這些宏的定義。但惋惜的是Python2.6.2 gdbini對於pylocals的定義竟然有錯誤, 看來是沒有隨着代碼的更新而同步更新。咱們只需將 while $_i < f->f_nlocals修改成 while $_i < f->f_code->co_nlocals便可。文章後面所附的幾個宏建議也加入的.gdbinit文件中,更多的宏可參考

http://web.archive.org/web/20070915134837/

http://www.mashebali.com/?Python_GDB_macros:The_Macros

   
   咱們首先須要構造一個會形成段錯誤的python腳本。老實說讓python發生段錯誤並不容易,但經過其外部調用庫就很簡單了。咱們將該文件命名爲gdb_test.py 
import sys, os, libxml2

def segv_test():
    s = "<html><body><div><a><a></a></a><a></a></div></body></html>"
    options = libxml2.HTML_PARSE_RECOVER + \
              libxml2.HTML_PARSE_NOERROR + \
              libxml2.HTML_PARSE_NOWARNING
    doc = libxml2.htmlReadDoc(s, None, 'utf-8', options).doc
    ctxt = doc.xpathNewContext()
    nodes = ctxt.xpathEval('//body/node()')
    nodes.reverse()
    for note in nodes:
        nexts = note.xpathEval('node()')
        note.unlinkNode() 
        note.freeNode() //freeNode會將該節點及其子節點釋放掉
        nexts[0].unlinkNode() 
        nexts[0].freeNode() //資源已經釋放,再次釋放會形成段錯誤

def main():
    segv_test()

if __name__ == "__main__":
    main()

   使用gdb運行該腳本,咱們會獲得段錯誤信息。
gdb python
r gdb_test.py

*** glibc detected *** double free or corruption (fasttop): 0x08104570 ***

Program received signal SIGABRT, Aborted.
[Switching to Thread -1208260928 (LWP 26159)]
0x00b987a2 in _dl_sysinfo_int80 () from /lib/ld-linux.so.2

   鍵入bt獲得以下堆棧信息: 
(gdb) bt
#0 0x00b987a2 in _dl_sysinfo_int80 () from /lib/ld-linux.so.2
#1 0x00c00825 in raise () from /lib/tls/libc.so.6
#2 0x00c02289 in abort () from /lib/tls/libc.so.6
#3 0x00c34cda in __libc_message () from /lib/tls/libc.so.6
#4 0x00c3b56f in _int_free () from /lib/tls/libc.so.6
#5 0x00c3b94a in free () from /lib/tls/libc.so.6
#6 0x009812c6 in xmlFreeNode () from /opt/sohumc/lib/libxml2.so.2
#7 0x0029d7f3 in libxml_xmlFreeNode () from /opt/sohumc/lib/python2.6/site-packages/libxml2mod.so
#8 0x00780bae in PyCFunction_Call (func=0x8104570, arg=0xd05820, kw=0x6) at Objects/methodobject.c:116
#9 0x007d8c79 in call_function (pp_stack=0xbff8c48c, oparg=0) at Python/ceval.c:3679
#10 0x007d6d2b in PyEval_EvalFrameEx (f=0x8124ef4, throwflag=0) at Python/ceval.c:2370
#11 0x007d8e36 in fast_function (func=0x6, pp_stack=0xbff8c5dc, n=1, na=1, nk=0) at Python/ceval.c:3765
#12 0x007d89cd in call_function (pp_stack=0xbff8c5dc, oparg=0) at Python/ceval.c:3700
#13 0x007d6d2b in PyEval_EvalFrameEx (f=0x81242fc, throwflag=0) at Python/ceval.c:2370
#14 0x007d8e36 in fast_function (func=0x6, pp_stack=0xbff8c72c, n=0, na=0, nk=0) at Python/ceval.c:3765
#15 0x007d89cd in call_function (pp_stack=0xbff8c72c, oparg=0) at Python/ceval.c:3700
#16 0x007d6d2b in PyEval_EvalFrameEx (f=0x810a7c4, throwflag=0) at Python/ceval.c:2370
#17 0x007d8e36 in fast_function (func=0x6, pp_stack=0xbff8c87c, n=0, na=0, nk=0) at Python/ceval.c:3765
#18 0x007d89cd in call_function (pp_stack=0xbff8c87c, oparg=0) at Python/ceval.c:3700
#19 0x007d6d2b in PyEval_EvalFrameEx (f=0x8091d0c, throwflag=0) at Python/ceval.c:2370
#20 0x007d76f9 in PyEval_EvalCodeEx (co=0xb7fa3728, globals=0x6, locals=0xb7f9902c, args=0x0, argcount=0, kws=0x0, kwcount=0, defs=0x0, defcount=0,
    closure=0x0) at Python/ceval.c:2942
#21 0x007d47cb in PyEval_EvalCode (co=0xb7fa3728, globals=0xb7f9902c, locals=0xb7f9902c) at Python/ceval.c:515
#22 0x007fbbce in run_mod (mod=0x80ea780, filename=0xbffc6be6 "gdb_test.py", globals=0xb7f9902c, locals=0xb7f9902c, flags=0xbff8ca8c, arena=0x807ef28)
    at Python/pythonrun.c:1330
#23 0x007fbb58 in PyRun_FileExFlags (fp=0x8091d00, filename=0xbffc6be6 "gdb_test.py", start=257, globals=0xb7f9902c, locals=0xb7f9902c, closeit=1,
    flags=0xbff8ca8c) at Python/pythonrun.c:1316
#24 0x007fb22d in PyRun_SimpleFileExFlags (fp=0x8091d00, filename=0xbffc6be6 "gdb_test.py", closeit=1, flags=0xbff8ca8c) at Python/pythonrun.c:926
#25 0x007facc9 in PyRun_AnyFileExFlags (fp=0x8091d00, filename=0xbffc6be6 "gdb_test.py", closeit=1, flags=0xbff8ca8c) at Python/pythonrun.c:731
#26 0x00808fea in Py_Main (argc=1, argv=0xbff8cbb4) at Modules/main.c:597
#27 0x080486ae in main (argc=2, argv=0xbff8cbb4) at Modules/python.c:23
   
   pystack和pystackv兩個宏可用來查看python內部的棧狀況;能夠看到程序時執行到freeNode函數時結束, 該函數位於libxml2.py的3141行。
(gdb) pystack
/opt/lib/python2.6/site-packages/libxml2.py (3141): freeNode
gdb_test.py (17): segv_test
gdb_test.py (21): main
gdb_test.py (24): <module>

   經過堆棧咱們能夠看到腳本內部各函數的調用關係, 那麼咱們如何查看函數內變量狀況呢? 正如你們所, python內部堆棧和函數的調用由PyEval_EvalFrameEx完成的, 一次PyEval_EvalFrameEx意味着一次函數調用,象上面的第19,13,10行分別對應於main, segv_test, freeNode函數, 將gdb定位到對應行後,使用pylocals宏便可查看該函數內部變量的詳細狀況。
(gdb) up 13
#13 0x007d6d2b in PyEval_EvalFrameEx (f=0x81242fc, throwflag=0) at Python/ceval.c:2370
2370    in Python/ceval.c
(gdb) pylocals
s:
object : '<html><body><div><a><a></a></a><a></a></div></body></html>'
type    : str
refcount: 3
address : 0xb7f64440
options:
object : 97
type    : int
refcount: 7
address : 0x8082c20
doc:
object : <xmlDoc (None) object at 0xb7cc04ec>
type    : instance
refcount: 1
address : 0xb7cc04ec
ctxt:
object : <libxml2.xpathContext instance at 0xb7f70ccc>
type    : instance
refcount: 1
address : 0xb7f70ccc
nodes:
object : [<xmlNode ((儓X? object at 0xb7cc0cac>]
type    : list
refcount: 2
address : 0xb7f70a8c
note:
object : <xmlNode ((?圶? object at 0xb7cc0cac>
type    : instance
refcount: 2
address : 0xb7cc0cac
nexts:
object : [<xmlNode (hhX? object at 0xb7cc750c>, <xmlNode (HXX? object at 0xb7cc76cc>, <xmlNode (@XX? object at 0xb7c9348c>]
type    : list
refcount: 1
address : 0xb7f4ce4c
    
   腳本調試時斷點的設置是個很麻煩的東西,我所能想到的有兩種方法:1 根據函數的python源碼進行斷點設置;2 採用sleep函數和ctrl+c來中斷程序的運行。不管怎麼樣使用逐條執行進行調試都是很痛苦的事情,由於這個時候python解釋器自己要作不少工做

相關文章
相關標籤/搜索