zoukankan      html  css  js  c++  java
  • 使用gdb调试Python进程

    使用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。

    编译时注意,要把自己编译的Python路径加到PATH环境变量里,这样gdb configure的时候才会找到新版Python并连接

    3.下载libpython.py

    如何Debug

    假设要debug的进程号为1000

    1
    $ gdb -p 1000

    使用此命令即可使gdb附加到进程。

    载入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查看。

    一点经验

    • 在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解释器本身要做很多工作

  • 相关阅读:
    php模拟http请求的方法
    快递100接口开发
    live555从RTSP服务器读取数据到使用接收到的数据流程分析
    VLC源码分析知识总结
    VLC播放器架构剖析
    Android Audio System 之一:AudioTrack如何与AudioFlinger
    VLC各个Module模块之间共享变量的实现方法
    流媒体开发之--HLS--M3U8解析(2): HLS草案
    M3U8格式讲解及实际应用分析
    通用线程:POSIX 线程详解,第 3 部分 条件互斥量(pthread_cond_t)
  • 原文地址:https://www.cnblogs.com/dkblog/p/3806277.html
Copyright © 2011-2022 走看看