gdb 调试python 进程,包括core dump 调试

本文介绍了使用gdb调试运行中的Python进程的方法。包括设置core dump文件生成目录、开启core dump文件生成、让修改生效等步骤。还说明了测试环境、准备工作,以及如何生成core file、查看当前执行位置源码、调用栈、变量值等,同时介绍了调试多线程程序的操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

设置core dump 文件生成目录
https://blog.youkuaiyun.com/chengde6896383/article/details/83345684

使用 gdb 调试运行中的 Python 进程
https://mozillazg.com/2017/07/debug-running-python-process-with-gdb.html

步骤一:开启core dump文件生成

ulimit -c unlimited

步骤二:设置core dump文件位置

vi /etc/sysctl.conf

修改(添加)如下两个变量

kernel.core_pattern =/var/core/core_%e_%p

kernel.core_uses_pid= 0


这里是改为生成目录在/var/core/,%e代表程序名称,%p是进程ID

如果想直接生成在可执行文件相同目录,前面不要加任何目录,直接

kernel.core_pattern =core_%e_%p


步骤三:让修改生效

sysctl -p/etc/sysctl.conf

 

 

假设一个服务器上运行了下面这样的 test.py 程序,我们怎样才能知道程序是否在正常运行,运行到哪一步了呢?

import time


def do(x):
    time.sleep(10)


def main():
    for x in range(10000):
        do(x)


if __name__ == '__main__':
    main()

这个程序既没有日志也没有 print 输出,通过查看日志文件/标准输出/标准错误是没有办法确认程序状况的。 一种可行的办法就是使用 gdb 来查看程序当前的运行状况。

测试环境

  • 系统: Ubuntu 16.04.1 LTS
  • Python: 2.7.12

准备工作

安装 gdb 和 python2.7-dbg:

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

设置 /proc/sys/kernel/yama/ptrace_scope:

$ echo 0 |sudo tee /proc/sys/kernel/yama/ptrace_scope

运行 test.py:

$ python test.py &
[1] 6489

通过 gdb python PID 来调试运行中的进程:

$ gdb python 6489
GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.04) 7.11.1
...
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from python...Reading symbols from /usr/lib/debug/.build-id/90/d1300febaeb0a626baa2540d19df2416cd3361.debug...done.
done.
...
Reading symbols from /lib/ld-linux.so.2...Reading symbols from /usr/lib/debug//lib/i386-linux-gnu/ld-2.23.so...done.
done.
0xb778fc31 in __kernel_vsyscall ()
(gdb)

生成 core file

为了不影响运行中的进程,可以通过生成 core file 的方式来保存进程的当前信息:

(gdb) generate-core-file
warning: target file /proc/6489/cmdline contained unexpected null characters
Saved corefile core.6489
(gdb) quit
A debugging session is active.

    Inferior 1 [process 6489] will be detached.

Quit anyway? (y or n) y

可以通过 gdb python core.PID 的方式来读取 core file:

$ gdb python core.6489
GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.04) 7.11.1
...
Type "apropos word" to search for commands related to "word"...
Reading symbols from python...Reading symbols from /usr/lib/debug/.build-id/90/d1300febaeb0a626baa2540d19df2416cd3361.debug...done.
done.

warning: core file may not match specified executable file.
[New LWP 6489]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/i386-linux-gnu/libthread_db.so.1".
Core was generated by `python'.
#0  0xb778fc31 in __kernel_vsyscall ()
(gdb)

可用的 python 相关的命令

可以通过输入 py 然后加 tab 键的方式来查看可用的命令:

(gdb) py
py-bt               py-down             py-locals           py-up               python-interactive
py-bt-full          py-list             py-print            python

可以通过 help cmd 查看各个命令的说明:

(gdb) help py-bt
Display the current python frame and all the frames within its call stack (if any)

当前执行位置的源码

(gdb) py-list
   1    # -*- coding: utf-8 -*-
   2    import time
   3
   4
   5    def do(x):
  >6        time.sleep(10)
   7
   8
   9    def main():
  10        for x in range(10000):
  11            do(x)
(gdb)

可以看到当前正在执行 time.sleep(10)

当前位置的调用栈

(gdb) py-bt
Traceback (most recent call first):
  <built-in function sleep>
  File "test.py", line 6, in do
    time.sleep(10)
  File "test.py", line 11, in main
    do(x)
  File "test.py", line 15, in <module>
    main()
(gdb)

可以看出来是 main() -> do(x) -> time.sleep(10)

查看变量的值

(gdb) py-list
   1    # -*- coding: utf-8 -*-
   2    import time
   3
   4
   5    def do(x):
  >6        time.sleep(10)
   7
   8
   9    def main():
  10        for x in range(10000):
  11            do(x)
(gdb) py-print x
local 'x' = 12
(gdb)
(gdb) py-locals
x = 12
(gdb)

查看上层调用方的信息

(gdb) py-up
#9 Frame 0xb74c0994, for file test.py, line 11, in main (x=12)
    do(x)
(gdb) py-list
   6        time.sleep(10)
   7
   8
   9    def main():
  10        for x in range(10000):
 >11            do(x)
  12
  13
  14    if __name__ == '__main__':
  15        main()
(gdb) py-print x
local 'x' = 12
(gdb)

可以通过 py-down 回去:

(gdb) py-down
#6 Frame 0xb74926e4, for file test.py, line 6, in do (x=12)
    time.sleep(10)
(gdb) py-list
   1    # -*- coding: utf-8 -*-
   2    import time
   3
   4
   5    def do(x):
  >6        time.sleep(10)
   7
   8
   9    def main():
  10        for x in range(10000):
  11            do(x)
(gdb)

调试多线程程序

测试程序 test2.py:

# -*- coding: utf-8 -*-
from threading import Thread
import time


def do(x):
    x = x * 3
    time.sleep(x * 60)


def main():
    threads = []
    for x in range(1, 3):
        t = Thread(target=do, args=(x,))
        t.start()
    for x in threads:
        x.join()


if __name__ == '__main__':
    main()
$ python test2.py &
[2] 12281

查看所有线程

info threads

$ gdb python core.12281

(gdb) info threads
  Id   Target Id         Frame
* 1    Thread 0xb74b9700 (LWP 11039) 0xb7711c31 in __kernel_vsyscall ()
  2    Thread 0xb73b8b40 (LWP 11040) 0xb7711c31 in __kernel_vsyscall ()
  3    Thread 0xb69ffb40 (LWP 11041) 0xb7711c31 in __kernel_vsyscall ()
(gdb)

可以看到这个程序当前有 3 个线程, 当前进入的是 1 号线程。

切换线程

thread ID

(gdb) thread 3
[Switching to thread 3 (Thread 0xb69ffb40 (LWP 11041))]
#0  0xb7711c31 in __kernel_vsyscall ()
(gdb) info threads
  Id   Target Id         Frame
  1    Thread 0xb74b9700 (LWP 11039) 0xb7711c31 in __kernel_vsyscall ()
  2    Thread 0xb73b8b40 (LWP 11040) 0xb7711c31 in __kernel_vsyscall ()
* 3    Thread 0xb69ffb40 (LWP 11041) 0xb7711c31 in __kernel_vsyscall ()
(gdb)

现在切换到了 3 号线程。

可以通过前面所说的 py- 命令来查看当前线程的其他信息:

[Current thread is 1 (Thread 0xb74b9700 (LWP 11039))]
(gdb) py-list
 335            waiter.acquire()
 336            self.__waiters.append(waiter)
 337            saved_state = self._release_save()
 338            try:    # restore state no matter what (e.g., KeyboardInterrupt)
 339                if timeout is None:
>340                    waiter.acquire()
 341                    if __debug__:
 342                        self._note("%s.wait(): got it", self)
 343                else:
 344                    # Balancing act:  We can't afford a pure busy loop, so we
 345                    # have to sleep; but if we sleep the whole timeout time,
(gdb) thread 2
[Switching to thread 2 (Thread 0xb73b8b40 (LWP 11040))]
#0  0xb7711c31 in __kernel_vsyscall ()
(gdb) py-list
   3    import time
   4
   5
   6    def do(x):
   7        x = x * 3
  >8        time.sleep(x * 60)
   9
  10
  11    def main():
  12        threads = []
  13        for x in range(1, 3):
(gdb)

同时操作所有线程

thread apply all CMD 或 t a a CMD

(gdb) thread apply all py-list

Thread 3 (Thread 0xb69ffb40 (LWP 11041)):
   3    import time
   4
   5
   6    def do(x):
   7        x = x * 3
  >8        time.sleep(x * 60)
   9
  10
  11    def main():
  12        threads = []
  13        for x in range(1, 3):

Thread 2 (Thread 0xb73b8b40 (LWP 11040)):
   3    import time
   4
   5
   6    def do(x):
   7        x = x * 3
  >8        time.sleep(x * 60)
   9
  10
  11    def main():
  12        threads = []
  13        for x in range(1, 3):

---Type <return> to continue, or q <return> to quit---
Thread 1 (Thread 0xb74b9700 (LWP 11039)):
 335            waiter.acquire()
 336            self.__waiters.append(waiter)
 337            saved_state = self._release_save()
 338            try:    # restore state no matter what (e.g., KeyboardInterrupt)
 339                if timeout is None:
>340                    waiter.acquire()
 341                    if __debug__:
 342                        self._note("%s.wait(): got it", self)
 343                else:
 344                    # Balancing act:  We can't afford a pure busy loop, so we
 345                    # have to sleep; but if we sleep the whole timeout time,
(gdb)

常用的 gdb python 相关的操作就是这些, 同时也不要忘记原来的 gdb 命令都是可以使用的哦。

<think>好的,我现在需要回答用户关于如何使用GDB调试Python代码的问题。首先,我得回忆一下GDB的基本用途,它主要是用于调试C/C++程序的,但用户想用它来调试Python代码。这可能涉及到Python的C扩展或者解释器本身的问题。 接下来,我应该考虑用户可能的场景。用户可能遇到了Python程序的崩溃,比如段错误(segmentation fault),这时候用普通的Python调试器(如pdb)可能无法捕捉到,因为问题可能出在底层的C扩展或者Python解释器。这时候GDB就显得有用了。 我需要分步骤解释如何使用GDB调试Python代码。首先,用户需要安装GDBPython调试符号。不同操作系统安装方式不同,比如Ubuntu可能需要安装python3-dbg或python-dbg包。然后,如何启动GDB附加到Python进程上,或者直接运行Python脚本。 然后,可能需要介绍一些常用的GDB命令,比如设置断点、查看堆栈、打印变量等。但要注意,Python代码和C代码在堆栈上的区别,用户可能需要切换上下文来查看Python层面的信息。 另外,PyEval_SetTrace这样的函数可以用来设置跟踪函数,帮助在Python代码中设置断点。或者使用gdbPython扩展脚本,比如libpython.py,它提供了更多Python友好的命令,比如py-bt来查看Python堆栈。 可能还需要提到一些常见的调试场景,比如调试段错误,或者死锁问题。例如,当Python程序卡住时,用GDB获取线程信息,检查是否有锁未释放。 同时,需要提醒用户,GDB调试需要一定的C语言和Python内部机制的知识,对于普通Python代码问题,可能还是优先使用pdb或其他Python调试工具更高效。只有在涉及底层问题时,才需要使用GDB。 最后,要确保步骤清晰,语言通俗易懂,避免过多专业术语,或者如果使用术语,需要简单解释。比如解释什么是core dump,如何生成和分析。 可能还需要检查是否有遗漏的步骤,比如如何获取Python调试符号,不同Linux发行版的包管理命令,或者如何在macOS上使用GDB(可能需要代码签名的问题)等。不过用户可能主要在Linux环境下使用,所以可以侧重Linux的步骤。 另外,可能提到一些工具,如faulthandler模块,它可以在程序崩溃时输出堆栈信息,辅助调试。或者结合coredumpGDB进行分析。 总结下来,回答的结构应该是: 1. 介绍GDB调试Python的适用场景(C扩展、解释器问题) 2. 安装必要的工具和调试符号 3. 启动GDB附加到Python进程 4. 常用GDB命令和技巧 5. 使用Python特定的GDB脚本(如libpython.py) 6. 示例调试步骤(比如处理段错误) 7. 注意事项和替代工具建议 需要确保每个步骤都简明扼要,用户能跟着操作。例如,具体如何安装python3-dbg,如何运行gdb --args python3 script.py,然后使用bt命令查看堆栈,py-list查看Python代码行等。 可能还需要指出,对于纯Python代码的问题,GDB并不是最佳选择,但对于底层问题非常有用。同时,调试时可能需要保持符号表,编译时的调试信息等。 最后,检查是否有错误,比如GDB命令的正确性,或者步骤是否合理,避免误导用户。</think>GDB调试Python代码主要用于分析底层问题(如C扩展崩溃、解释器死锁),以下是具体步骤和技巧: 1. 适用场景 - 段错误(Segmentation Fault) - Python解释器崩溃 - C扩展模块问题 - 死锁/线程问题 - 内存泄漏分析 2. 准备工作 ```bash # Ubuntu/Debian sudo apt-get install gdb python3-dbg # CentOS/RHEL sudo yum install gdb python3-debuginfo # macOS(需先安装Homebrew) brew install gdb codesign --entitlements gdb-entitlement.xml -fs gdb-cert $(which gdb) ``` 3. 基础调试流程 ```bash # 直接调试脚本 gdb --args python3 your_script.py # 附加到运行中的进程 gdb -p $(pgrep -f your_script.py) # 常用命令 (gdb) bt full # 查看完整堆栈 (gdb) info threads # 显示所有线程 (gdb) py-bt # 显示Python堆栈(需加载libpython) (gdb) py-list # 显示当前Python代码位置 ``` 4. 增强调试(使用libpython脚本) ```bash # 获取调试脚本 wget https://raw.githubusercontent.com/python/cpython/main/Tools/gdb/libpython.py # GDB中加载 (gdb) source libpython.py # 特殊命令 (gdb) py-up/py-down # 在Python堆栈帧间移动 (gdb) py-print x # 打印Python变量 (gdb) py-locals # 显示当前作用域变量 ``` 5. 段错误调试示例 ```python # test.py import ctypes ctypes.string_at(0) # 人为制造段错误 ``` 调试过程: ```bash $ gdb --args python3 test.py (gdb) r # 出现段错误后 (gdb) bt full (gdb) py-bt (gdb) info registers ``` 6. 线程死锁调试 ```python # deadlock.py import threading lock = threading.Lock() lock.acquire() lock.acquire() # 第二次获取会死锁 ``` 调试步骤: ```bash $ gdb -p $(pgrep -f deadlock.py) (gdb) info threads (gdb) thread 2 (gdb) py-bt (gdb) py-list ``` 7. 内存分析技巧 ```bash (gdb) malloc_info 0 stdout # 显示内存分配情况 (gdb) py-bt 10 # 显示最近10个Python调用 (gdb) dump binary memory dump.bin 0x0000555555a00000 0x0000555555c00000 ``` 8. 替代方案建议 - 常规调试优先使用pdb: ```bash python3 -m pdb your_script.py ``` - 输出崩溃堆栈: ```python import faulthandler faulthandler.enable() ``` - 内存分析工具:Valgrind、tracemalloc - 性能分析:cProfile、py-spy 注意事项: 1. 生产环境调试可能影响性能 2. 需要保持调试环境与生产环境一致 3. 部分Python优化版本(如--with-pydebug)会提供更多调试信息 4. 解释器内部函数名通常以_Py或Py开头 通过GDB调试可以深入分析以下问题: - C扩展中的非法内存访问 - 引用计数错误 - GIL竞争问题 - Python对象内存布局 - 解释器字节码执行异常 建议结合core dump文件进行分析: ```bash ulimit -c unlimited ./crash_script.py # 生成core文件 gdb python3 core # 加载core dump ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值