2013/11/01 | Comments

大约一年前,我接触了 Java 中的 Btrace 能够不停机查看线上 JVM 运行情况的特性让我艳羡不已。 另外还有强悍的 jStack 和 jConsole 来进行运行期侦测,JVM 的工业级强度果然不是盖的。

当时公司技术方面也遇到了一些瓶颈,一部分原因是 CPython 本身的 IO 模型问题, 另一方面也和早期代码写的极不工整脱不了关系。万般无奈之下,我们用 Jython 推翻重做了主要业务,效果立竿见影,但同时也把真实问题给规避掉了。

在这之后我们在 JVM 的领导下,走上了康庄大道。但我心中始终还有一个梗, 就是想对性能遇到瓶颈的 Python Process 进行线上侦测。 这篇文章就是开始的第一步。

PS:这篇文章理论上是可行的,但是在我机器(Ubuntu 12.04 / 系统自带 Python) 无法正常执行,会爆出 unable to read python frame information 的问题。解决方法我会在下篇文章中写出。这里只是单纯翻译一下原文。

原文可以移步 https://wiki.python.org/moin/DebuggingWithGdb



有一些类型的 bugs 很难使用 Python 直接进行 debug,比如

  • 段错误(无法被捕捉的 Python 异常)
  • 卡住的进程(这种情况下面没法使用 pdb 来进行跟踪)
  • 控制之外的后台处理 daemon 进程

这些情况下,你可以使用尝试使用 gdb

准备

需要在系统安装 gdb 和 Python debug 扩展。 Python debug 扩展包含了一些 debug 命令,并且添加了一些 Python 特定的命令到 gdb。 在主流的 Linux 发行版中,你可以轻松的安装他们:

Fedora:

1
sudo yum install gdb python-debuginfo

Ubunt:

1
sudo apt-get install gdb python2.7-dbg

在一些老系统上面,也一样可以使用 gdb,具体看文章末尾。

使用 GDB

有两种可行的方法:

  1. 一开始就使用 gdb 来启动应用
  2. 连接到一个已经运行的 Python 进程

gdb 下面启动 Python 同样有两种方式:

交互式:

1
2
3
$ gdb python
...
(gdb) run <programname>.py <arguments>

自动:

1
$ gdb -ex r --args python <programname>.py <arguments>

这样的话,它会一直运行直到退出、段错误、或者人为的停止(使用 Ctrl+C)。

如果进程已经开始运行,你可以通过 PID 来接入它:

1
$ gdb python <pid of running process>

调试进程

如果你的程序段错误了, gdb 会自动暂停程序,这样你可以切换到 gdb 命令行来检查状态。你也可以人为地使用 Ctrl+C 来暂停程序运行。

查看 EasierPythonDebugging 获得 gdb 里面的 Python 命令列表。

查看 C 调用栈

如果你在 debug 段错误,你最想做的可能就是查看 C 调用栈。

gdb 的命令行里面,只要运行一下命令:

1
2
3
4
5
6
7
8
9
10
(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 扩展,你可以使用

1
(gdb) py-bt

可以获取熟悉的 Python 源代码。

对挂住的进程开刀

如果一个进程看上去挂住了,他可能在等待什么东西(比如锁、IO 等等)。 也有可能在拼命的跑循环。连接上这个进程,然后检查调用栈也许可以帮上忙。

如果进程在疯狂循环,你可以先让它运行一会,使用 cont 命令, 然后使用 Ctrl+C 来暂停,并且打印出调用栈。

如果一些线程卡住了,下面的命令可能会帮上忙:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
(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 查看:

1
2
3
4
5
6
7
8
9
10
11
12
(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 …

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
## 引用 ##
* [http://fedoraproject.org/wiki/Features/EasierPythonDebugging](http://fedoraproject.org/wiki/Features/EasierPythonDebugging)
* [https://code.google.com/p/spyderlib/wiki/HowToDebugDeadlock](https://code.google.com/p/spyderlib/wiki/HowToDebugDeadlock)
## 老系统上的 GDB ##
有时候你需要在老系统上面安装 `gdb`,这时候你可能需要下列信息:
### GDB Macros ###
一些随着 Python 发布的 GDB 脚本可以用来调试 Python 进程。
你可以把 Python 源码里面的 `Misc/gdbinit` 拷贝到 `~/.gdbinit`,
或者从 [Subversion](http://svn.python.org/view/python/branches/release27-maint/Misc/gdbinit?view=log)
来拷贝他们。请注意你的 Python,确保使用正确的代码版本,否则有些功能可能无法工作。
请注意有些新的 GDB 命令只有在 debug 需要的库存在才能正常工作。
这个脚本在 Ubuntu 上面的 gcc 4.5.2 工作时,会爆出错误
`No symbol "co" in current context.`,是因为 `call_function` 在
[PyEval_EvalFrameEx](https://wiki.python.org/moin/EvalFrameEx) 和
[PyEval_EvalCodeEx](https://wiki.python.org/moin/EvalCodeEx) 之间。
重新使用 `make "CFLAGS=-g -fno-inline -fno-strict-aliasing"`
编译 Python 可以解决这个问题。
### 使用 Python Stack Traces GDB 脚本 ##
在 gdb 命令行里,可以这样查看 Python stack trace:

(gdb) pystack

1
2
同样的,可以获取一列 stack frame 的 Python 变量:

(gdb) pystackv “`

更多 gdbinit 里面没定义的有用的脚本可以在这里找到:

http://web.archive.org/web/20070915134837/http://www.mashebali.com/?Python_GDB_macros:The_Macros

版权所有 © 2010 转载本站文章请注明: 转载自 Log4D
原文链接: http://blog.log4d.com/2013/11/python-gdb/
您可以随意地转载本站的文章,但是必须在醒目位置注明来源及本站链接,不可以将本站文章商业化使用,或者修改、转换或者以本作品为基础进行创作。
3a1ff193cee606bd1e2ea554a16353ee

在 Python 中使用 GDB 来调试 转载的更多相关文章

  1. Python中super的用法【转载】

    Python中super的用法[转载] 转载dxk_093812 最后发布于2019-02-17 20:12:18 阅读数 1143  收藏 展开 转载自 Python面向对象中super用法与MRO ...

  2. python中重要的模块--asyncio 转载

    转载自: https://www.cnblogs.com/zhaof/p/8490045.html 一直对asyncio这个库比较感兴趣,毕竟这是官网也非常推荐的一个实现高并发的一个模块,python ...

  3. Visual Studio 2015中使用gdb远程调试linux程序

    VS的debug功能非常强大,相比而言linux上的图形化调试一直不是很好用. 如果可以使用VS来调试linux程序,应该是一件比较愉快的事情. 这在2015中变得可能,因为从2015开始VS支持An ...

  4. Python 中的垃圾回收机制(转载)

    from: https://foofish.net/python-gc.html GC作为现代编程语言的自动内存管理机制,专注于两件事:1. 找到内存中无用的垃圾资源 2. 清除这些垃圾并把内存让出来 ...

  5. vi/vim使用进阶: 在VIM中使用GDB调试 – 使用vimgdb

    vi/vim使用进阶: 在VIM中使用GDB调试 – 使用vimgdb << 返回vim使用进阶: 目录 本节所用命令的帮助入口: :help vimgdb 在UNIX系统最初设计时,有一 ...

  6. gdb可以调试python的pdb么

    1.如题: gdb可以调试python的pdb么? 答案:可以,实验如下. 2.实验: 生成一个a.py代码文件,代码如下: import pdb a = 1 print "111" ...

  7. 【转载】关于Python中的yield

    在介绍yield前有必要先说明下Python中的迭代器(iterator)和生成器(constructor). 一.迭代器(iterator) 在Python中,for循环可以用于Python中的任何 ...

  8. 【转载】Python中如何高效实现两个字典合并,三种方法比较。

    本文转载自:http://www.pythoner.com/13.html Python中将两个字典进行合并操作,是一个比较常见的问题.本文将介绍几种实现两个字典合并的方案,并对其进行比较. 对于这个 ...

  9. 转载:Python中的new style class机制实现

    1.Python中的对象模型python中所有东西都是对象 class对象:表示Python内置的类型和定义的类型instance对象(实例对象):表示由class对象创建的实例 1.1 对象间的关系 ...

随机推荐

  1. postgresql 数据库无法启动

    在数据库无法启动时,一般可以根据报错信息,采取对应措施即可,下面列出一些在数据库启动时报出错误比较严重而解决方式又不那么明显的处理方法. 模拟错误,查到pg_class系统表中一个索引在磁盘中的位置, ...

  2. WARNING: pgstat wait timeout

      在ELK的邮件报警中,发现了一个 WARNING: pgstat wait timeout 的报错信息,看字面意思是pgstat有关操作等待超时.   通过google查询,发现在pg的邮件列表中 ...

  3. thinkphp3 行为(behavior)分析和基本使用

    1. 名词解析 官方解析: 来自 http://document.thinkphp.cn/manual_3_2.html#behavior_extend 行为(Behavior)是一个比较抽象的概念, ...

  4. OpenLDAP介绍和安装

    LADP 1.目录服务 目录是一个为查询.浏览和搜索而优化的专业分布式数据库,它呈树状结构组织数据,就好象Linux/Unix系统中的文件目录一样.目录数据库和关系数据库不同,它有优异的读性能,但写性 ...

  5. webapi框架搭建-安全机制(一)

    本系列博客链接:webapi框架搭建系列博客 前言 webapi接口是开放给外部使用的,包括接口的地址,传参的规范,还有返回结果的说明.正因为接口的开放性,使得接口的安全很重要.试想一下,用抓包工具( ...

  6. noi题库(noi.openjudge.cn) 3.9数据结构之C++STL T1——T2

    T1 1806:词典 描述 你旅游到了一个国外的城市.那里的人们说的外国语言你不能理解.不过幸运的是,你有一本词典可以帮助你. 输入首先输入一个词典,词典中包含不超过100000个词条,每个词条占据一 ...

  7. 蓝桥杯 剪邮票 DFS (不错的题目)

    剪邮票 如[图1.jpg], 有12张连在一起的12生肖的邮票.现在你要从中剪下5张来,要求必须是连着的.(仅仅连接一个角不算相连)比如,[图2.jpg],[图3.jpg]中,粉红色所示部分就是合格的 ...

  8. 拟牛顿法/Quasi-Newton,DFP算法/Davidon-Fletcher-Powell,及BFGS算法/Broyden-Fletcher-Goldfarb-Shanno

    拟牛顿法/Quasi-Newton,DFP算法/Davidon-Fletcher-Powell,及BFGS算法/Broyden-Fletcher-Goldfarb-Shanno 转载须注明出处:htt ...

  9. Python入门系列教程(二)字符串

    字符串 1.字符串输出 name = 'xiaoming' print("姓名:%s"%name) 2.字符串输入 userName = raw_input('请输入用户名:') ...

  10. 你知道吗?undefined 与 null 的区别

    大多数计算机语言,有且仅有一个表示"无"的值,比如,C语言的NULL,Java语言的null,Python语言的none,Ruby语言的nil. 有点奇怪的是,JavaScript ...