调试python脚本用到cpython_通过GDB调试CPython时在Python源代码中设置断点的最佳方法...

I use GDB to understanding how CPython executes the test.py source file and I want to stop the CPython when it starts the execution of opcode I am interested.

OS: Ubuntu 18.04.2 LTS

Debugger: GNU gdb (Ubuntu 8.1-0ubuntu3) 8.1.0.20180409-git

The first problem - many CPython's .py own files are executed before my test.py gets its turn, so I can't just break at the _PyEval_EvalFrameDefault - there are many of them, so I should distinguish my file from others.

The second problem - I can't set the condition like "when the filename is equal to the test.py", because the filename is not a simple C string, it is the CPython's Unicode object, so the standard GDB string functions can't be used for comparing.

At this moment I do the next trick for breaking the execution at the needed line of test.py source:

For example, I have the source file:

x = ['a', 'b', 'c']

# I want to set the breakpoint at this line.

for e in x:

print(e)

I add the binary left shift operator to the code:

x = ['a', 'b', 'c']

# Added for breakpoint

a = 12

b = 2 << a

for e in x:

print(e)

And then, track the BINARY_LSHIFT opcode execution in the Python/ceval.c file by this GDB command:

break ceval.c:1327

I have chosen the BINARY_LSHIFT opcode, because of its seldom usage in the code. Thus, I can reach the needed part of .py file quickly - it happens once in the all other .py modules executed before my test.py.

I look the more straightforward way of doing the same, so

the questions:

Can I catch the moment the test.py starts executing? I should mention, what the test.py filename is appearing on different stages: parsing, compilation, execution. So, it also will be good to can break the CPython execution at the any stage.

Can I specify the line of the test.py, where I want to break? It is easy for .c files, but is not for .py files.

解决方案

My idea would be to use a C-extension, to make setting C-breakpoints possible in a python-script (similar to pdb.set_trace() or breakpoint() since Python3.7), which I will call cbreakpoint.

Consider the following python-script:

#example.py

from cbreakpoint import cbreakpoint

cbreakpoint(breakpoint_id=1)

print("hello")

cbreakpoint(breakpoint_id=2)

It could be used as follows in gdb:

>>> gdb --args python example.py

[gdb] b cbreakpoint

[gdb] run

Now, the debuger would stops at cbreakpoint(breakpoint_id=1) and cbreakpoint(breakpoint_id=2).

Here is proof of concept, written in Cython to avoid the otherwise needed boilerplate-code:

#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)

which can be build inplace via:

cythonize -i cbreakpoint.pyx

If Cython isn't installed, I have uploaded a version which doesn't depend on Cython (too much code for this post) on github.

It is also possible to break conditionally, given the breakpoint_id, i.e.:

>>> gdb --args python example.py

[gdb] break src/cbreakpoint.c:595 if breakpoint_id == 2

[gdb] run

will break only after hello was printed - at cbreakpoint with id=2 (while cbreakpoint with id=1 will be skipped). Depending on Cython version the line can vary, but can be found out once gdb stops at cbreakpoint.

It would also do something similar without any additional modules:

add breakpoint or import pdb; pdb.set_trace() instead of cbreakpoint

gdb --args python example.py + run

When pdb interrupts the program, hit Ctrl+C in order to interrupt in gdb.

Activate breakpoints in gdb.

continue in gdb and then in pdb (i.e. c+enter twice).

A small problem is, that after that the breakpoints might be hit while in pdb, so the first method is a little bit more robust.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值