您的位置:首页 > 编程语言 > Python开发

使用gdb调试python脚本

2014-02-24 10:21 471 查看
     调试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解释器本身要做很多工作。 

   由于本身对于python源码不是很熟悉,因此对如何使用gdb对python脚本调试上也只是很粗略的理解, 这里权当抛砖引玉, 欢迎达人们给出分享的经验。

额外的宏:

define pbt 

set $i = 0 

set $j = 0 

while $i < 1000 

select $i 

if $eip >= &PyEval_EvalFrameEx 

if $eip < &PyEval_EvalCodeEx 

echo c frame # 

p $i 

echo py frame # 

p $j 

set $j = $j+1 

x/s ((PyStringObject*)f->f_code->co_filename)->ob_sval 

x/s ((PyStringObject*)f->f_code->co_name)->ob_sval 

echo line # 

p f->f_lineno 

end 

end 

set $i = $i+1 

end 

end 

document pbt 

show python backtrace 

end

define pyattrlist

set $dict = *(PyDictObject**)(((int)$arg0)+$arg0.ob_type.tp_dictoffset)

set $i = 0

set $j = 0

while $i < $dict.ma_mask

if 0 != $dict.ma_table[$i].me_value

   echo \nattr#:

   p $j

   x/s ((PyStringObject*)$dict.ma_table[$i].me_key).ob_sval

   pyobjinfo $dict.ma_table[$i].me_value

   set $j = $j+1

end

set $i = $i+1

end

end

document pyattrlist

show pythonic object attributes list

end

define pyattr

set $dict = *(PyDictObject**)(((int)$arg0)+$arg0.ob_type.tp_dictoffset)

set $i = 0

set $j = 0

while $i < $dict.ma_mask

if 0 != $dict.ma_table[$i].me_value

   if $j == $arg1

    set $attr = $dict.ma_table[$i].me_value

    echo $attr:

    p $attr

   end

   set $j = $j+1

end

set $i = $i+1

end

end

document pyattr

get pythonic object attribute

usage: pyattr <pyobj> <attr#>

atr# correlates to those shown in pyattrlist

end
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Linux gdb