美文网首页
2020-08-08 python linux 调试(使用PDB

2020-08-08 python linux 调试(使用PDB

作者: 昨天今天下雨天1 | 来源:发表于2020-08-10 09:10 被阅读0次

    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 #单步运行,跳过函数
    s #单步运行,可进入函数
    p 变量 #查看变量值
    b 行号 #断点设置到第几行
    b #显示所有断点列表
    cl 断点号 #删除某个断点
    cl #删除所有断点
    c #跳到下一个断点
    r #return当前函数
    exit #退出

    调试记录:

    pdb设置断点可以在程序里加入:

    import pdb
    

    在需要设置断点的地方加入pdb.set_trace()

    执行 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逐个执行到下一个断点,按p ip 就可以查看变量ip的值

    exit退出当前函数

    补充

    使用 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进程

    有时我们会想调试一个正在运行的Python进程,或者一个Python进程的coredump。例如现在遇到一个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 进程号,进行调试了。需要在系统安装 gdb 和 Python debug 扩展。Python debug 扩展包含了一些 debug 命令,并且添加了一些 Python 特定的命令到gdb。在主流的 Linux 发行版中,你可以轻松的安装他们:

    Fedora:

    sudo yum install gdb python-debuginfo
    

    Ubunt:

    sudo apt-get install gdb python2.7-dbg
    

    在一些老系统上面,也一样可以使用 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)]
    
    

    在一些追求稳定的发行版(例如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) 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
    

    相关文章

      网友评论

          本文标题:2020-08-08 python linux 调试(使用PDB

          本文链接:https://www.haomeiwen.com/subject/onacdktx.html