【问题标题】:Why breakpoint() doesn't prompt idempotent location in for loop?为什么breakpoint()在for循环中不提示幂等位置?
【发布时间】:2019-12-22 04:27:08
【问题描述】:

我有一个非常简单的测试文件breakpoint_test.py

for i in range(3):                       #1
    breakpoint()                         #2
    print(f"first print in loop {i}")    #3
    breakpoint()                         #4
    print(f"second print in loop {i}")   #5

这是我运行时的输出并按住c继续

(py3) nakita@machine:~/tmp $ python breakpoint_test.py 
> /Users/nakita/tmp/breakpoint_test.py(3)<module>()
-> print(f"first print in loop {i}")                    <---------------looks good here!
(Pdb) c
first print in loop 0
> /Users/nakita/tmp/breakpoint_test.py(5)<module>()
-> print(f"second print in loop {i}")
(Pdb) c
second print in loop 0


> /Users/nakita/tmp/breakpoint_test.py(2)<module>()
-> breakpoint()                                         <---------------weird here!
(Pdb) c
first print in loop 1
> /Users/nakita/tmp/breakpoint_test.py(5)<module>()
-> print(f"second print in circle {i}")
(Pdb) c
second print in loop 1


> /Users/nakita/tmp/breakpoint_test.py(2)<module>()
-> breakpoint()
(Pdb) c
first print in loop 2
> /Users/nakita/tmp/breakpoint_test.py(5)<module>()
-> print(f"second print in circle {i}")
(Pdb) c
second print in loop 2

我希望breakpoint() 会提示将在breakpoint() 之后执行的行。当代码第一次进入for 循环时,这是真的。但是在第二个循环中迭代时,第一个断点()提示breakpoint() 行本身而不是-&gt; print(f"first print in loop {i}")。但是,循环体中的第二个breakpoint() 可以按我的预期工作。看起来行为首先是breakpoint() in for 循环体将被牺牲。有人知道为什么吗?

我在 python 3.7.3 和 3.8.0 上对其进行了测试。 我已阅读PEP 553 -- Built-in breakpoint()

【问题讨论】:

    标签: python python-3.x breakpoints pdb


    【解决方案1】:

    Python 试图在下一行的开头开始调试,但它的“下一行”检测有点不稳定。


    默认情况下,breakpoint 调用 pdb.set_trace,这会设置一个 trace function,它将在下一个跟踪事件上执行。在这种情况下,下一个跟踪事件是 'line' 事件,当 Python 认为执行已进入新行时触发。

    在循环中第一个breakpoint 的第一次执行以及第二个breakpoint 的所有执行中,下一个'line' 事件在下一行的第一个操作码上触发。但是,在第二次和以后执行第一个 breakpoint 时,会发生一些不同的事情。

    Python 通过检查当前字节码指令索引是否对应于行的第一条指令或位于执行最后一条指令之前的索引处的指令来确定新的源代码行已开始。您可以在 maybe_call_line_trace 中看到 Python/ceval.c

    Python 仅更新instr_prev,当跟踪处于活动状态时,该变量用于确定最后执行的指令。当您点击c 继续执行时,跟踪功能将被停用。 (如果您使用 PDB break 命令设置了任何断点,它将保持活动状态,因为跟踪功能需要处理这些,但 breakpoint() 调用不会通过该机制。)

    在“非奇怪”断点上,Python 在“一行的第一条指令”条件下,在下一行的第一个操作码上触发下一行事件。

    在“奇怪”的断点上,instr_prev 仍然具有您上次点击 c 时的值,因为此时已禁用跟踪。该值适用于当前行之后的行,因此 Python 在“执行最后一条指令之前的索引处的指令”条件下,在 breakpoint() 行的下一个操作码上触发下一行事件。 (breakpoint() 行的下一个操作码是 POP_TOP,用于清理 breakpoint 的返回值。)如果 Python 一直在更好地跟踪执行的最后一条指令,它就不会触发该事件。

    【讨论】:

    • 谢谢,这个回答给我带来了很多新东西。
    猜你喜欢
    • 2020-03-24
    • 2019-08-20
    • 2018-10-11
    • 1970-01-01
    • 2022-12-04
    • 1970-01-01
    • 2019-11-03
    • 2018-07-25
    • 1970-01-01
    相关资源
    最近更新 更多