Debugging GDB调试CPython时在Python源代码中设置断点的最佳方法
我使用GDB来理解CPython如何执行Debugging GDB调试CPython时在Python源代码中设置断点的最佳方法,debugging,gdb,cpython,Debugging,Gdb,Cpython,我使用GDB来理解CPython如何执行test.py源文件,我想在开始执行我感兴趣的操作码时停止CPython OS:Ubuntu 18.04.2 LTS 调试器:GNU gdb(Ubuntu 8.1-0ubuntu3)8.1.0.20180409-git 第一个问题-许多CPython的.py自己的文件是在我的test.py轮到它之前执行的,所以我不能在\u PyEval\u evalframefault-有很多文件,所以我应该将我的文件与其他文件区分开来 第二个问题-我无法设置“当文件名
test.py
源文件,我想在开始执行我感兴趣的操作码时停止CPython
OS:Ubuntu 18.04.2 LTS调试器:GNU gdb(Ubuntu 8.1-0ubuntu3)8.1.0.20180409-git
第一个问题-许多CPython的
.py
自己的文件是在我的test.py
轮到它之前执行的,所以我不能在\u PyEval\u evalframefault
-有很多文件,所以我应该将我的文件与其他文件区分开来
第二个问题-我无法设置“当文件名等于test.py”这样的条件,因为文件名不是简单的C
字符串,而是CPython的Unicode对象,所以标准的GDB字符串函数不能用于比较
此时,我在所需的test.py
source行执行下一个技巧:
例如,我有一个源文件:
x = ['a', 'b', 'c']
# I want to set the breakpoint at this line.
for e in x:
print(e)
我将二进制左移位运算符添加到代码中:
x = ['a', 'b', 'c']
# Added for breakpoint
a = 12
b = 2 << a
for e in x:
print(e)
我选择了二进制移位
操作码,因为它很少在代码中使用。因此,我可以快速访问.py
文件中所需的部分-它在我的test.py
之前执行的所有其他.py
模块中发生一次
我想用更直接的方法来做同样的事情,所以
问题:
test.py
开始执行的时刻吗?我应该提到,test.py
文件名出现在不同的阶段:解析、编译和执行。因此,在任何阶段都可以打破CPython执行test.py
行吗?这对于.c
文件很容易,但对于.py
文件则不容易我的想法是使用C扩展,使在python脚本中设置C断点成为可能(类似于Python3.7或自Python3.7以来),我将调用
cbreakpoint
考虑以下python脚本:
#example.py
from cbreakpoint import cbreakpoint
cbreakpoint(breakpoint_id=1)
print("hello")
cbreakpoint(breakpoint_id=2)
它可以在gdb中按如下方式使用:
>>> gdb --args python example.py
[gdb] b cbreakpoint
[gdb] run
现在,调试程序将在cbreakpoint(断点\u id=1)
和cbreakpoint(断点\u id=2)
处停止
以下是用Cython编写的概念证明,以避免其他需要的样板代码:
#cbreakpoint.pyx
cdef extern from *:
"""
long long last_breakpoint_id = -1;
void cbreakpoint(long long breakpoint_id){
last_breakpoint_id = breakpoint_id;
}
"""
void c_cbreakpoint "cbreakpoint"(long long breakpoint_id)
def cbreakpoint(breakpoint_id = 0):
c_cbreakpoint(breakpoint_id)
可通过以下方式就地构建:
cythonize -i cbreakpoint.pyx
如果Cython没有安装,我已经上传了一个不依赖Cython的版本(这篇文章的代码太多)
考虑到断点id,也可以有条件地中断,即:
>>> gdb --args python example.py
[gdb] break src/cbreakpoint.c:595 if breakpoint_id == 2
[gdb] run
仅在打印了hello
后才会中断-在cbreakpoint
处,id=2(而cbreakpoint
处,id=1将被跳过)。根据Cython版本的不同,线路可能会有所不同,但一旦gdb停止在cbreakpoint
,就可以找到线路
它还可以在没有任何附加模块的情况下执行类似的操作:
断点
或导入pdb;pdb.set_trace()
而不是cbreakpoint
gdb--args python example.py
+runpdb
中断程序时,点击Ctrl+C
以在gdb中中断李>
gdb
中激活断点gdb
中继续,然后在pdb
中继续(即c+输入两次)
一个小问题是,在这之后,断点可能会在
pdb
中被命中,因此第一种方法更健壮。第二种方法不起作用。我做了几步№ 1,2,并获得pdb
的命令行,点击Ctrl+C
并获得程序接收信号SIGINT,中断。消息正在调试的Python程序被Ctrl+C
完全中断,因此之后就没有什么可调试的了。我正在尝试测试第一种方法,但使用的是纯C扩展,因为我根本不懂Cython。谢谢您的建议。@MiniMax No“程序收到信号SIGINT,Interrupt”正是您所需要的-在程序被中断之后。您需要输入c+enter(对于继续
)用于让gdb运行,然后另外c+输入
用于让pdb运行。@MiniMax您可以从提供的github链接安装cbreakpoint
,或者自己将其重写为c扩展-但是对于使用cythonize
的解决方案,除了安装cython之外,没有其他功能,将给定的代码保存为cbreakpoint.pyx并调用cythonize-生成的so可以在需要时使用。
>>> gdb --args python example.py
[gdb] break src/cbreakpoint.c:595 if breakpoint_id == 2
[gdb] run