2017-08-11 46 views
2

考虑以下两个例子:当一条线上的多条停靠点跟踪时,python停止在哪条线上?

x = 1; y = 2; z = 3 

和:

for i in range(3): print(i) 

在后者,如果你通过这个在调试步骤一样PDB,你会把它会停在print(i)在循环的每次迭代。

但是在第一个例子中,它会停止一次。

进一步调查,拆分多语句行,我们看到实际上有两个条目用于第一行co_lnotab。但dis.dis()就在于此。

至于对于循环,lnotab中只有一行,但是您停在每个迭代的位置偏移10处在跳转的目标处。那么,即使行号没有改变,什么会触发停止?

import dis 
>>> x = compile('x = 1; y = 2; z = 3', 'foo', 'exec') 
>>> x.co_lnotab 
b'\x04\x00\x04\x00' 
>>> dis.dis(x) 
    1   0 LOAD_CONST    0 (1) 
       2 STORE_NAME    0 (x) 
       4 LOAD_CONST    1 (2) 
       6 STORE_NAME    1 (y) 
       8 LOAD_CONST    2 (3) 
      10 STORE_NAME    2 (z) 
      12 LOAD_CONST    3 (4) 
      14 STORE_NAME    3 (a) 
      16 LOAD_CONST    4 (None) 
      18 RETURN_VALUE 
>>> y = compile('for i in range(3): print(i)', 'foo', 'exec') 
>>> y.co_lnotab 
b'\x0e\x00' 
>>> dis.dis(y) 
    1   0 SETUP_LOOP    24 (to 26) 
       2 LOAD_NAME    0 (range) 
       4 LOAD_CONST    0 (3) 
       6 CALL_FUNCTION   1 
       8 GET_ITER 
     >> 10 FOR_ITER    12 (to 24) 
      12 STORE_NAME    1 (i) 
      14 LOAD_NAME    2 (print) 
      16 LOAD_NAME    1 (i) 
      18 CALL_FUNCTION   1 
      20 POP_TOP 
      22 JUMP_ABSOLUTE   10 
     >> 24 POP_BLOCK 
     >> 26 LOAD_CONST    1 (None) 
      28 RETURN_VALUE 
>>> 

该逻辑的源代码在哪里?我看过Python C代码,但找不到它,请在ceval.c中寻找PyTrace_LINE

编辑

基于user2357112的答案和阅读代码建议在那里,我能够验证的代码的每个语句是一个可以停止/跟踪。 我用我的新的Python汇编,pyc-xasm,修改字节码这样:

2: 
      LOAD_CONST   (1) 
      STORE_NAME   (x) 
      JUMP_FORWARD   L2B 

L2A: 
    2: 
      LOAD_CONST   (2) 
      STORE_NAME   (y) 
      JUMP_FORWARD   L2D 
L2B: 
      JUMP_ABSOLUTE  L2A 

L2C: 
    2: 
      LOAD_CONST   (3) 
      STORE_NAME   (z) 
      JUMP_FORWARD   L3 
L2D: 
      JUMP_ABSOLUTE  L2C 

L3: 
    3: 
      LOAD_NAME   (x) 
      LOAD_NAME   (y) 
      BINARY_ADD 
      LOAD_NAME   (z) 
      BINARY_ADD 
      PRINT_ITEM 
      PRINT_NEWLINE 
      LOAD_CONST   (None) 
      RETURN_VALUE 

并运行,这将导致Python来的每一行之前停止。

+0

我很好奇你是什么让你看到这一点。你在做什么? –

+0

很长一段时间,我一直在研究调试器,其中包括python调试器。请参阅https://pypi.python.org/pypi/trepan3k。 我想你应该可以在任何你想要的地方设置一个断点,这可以通过在函数中指定一个字节码偏移来完成。或者决定在多个报表中停止使用哪个报表。随着最近一些字节码操作工具的推进,我可以做到这一点。尽管如此,这还是很多的工作,并不一定涵盖所有情况。 – rocky

+0

太棒了!我正在研究一个python调试器(字面上),您可能会感兴趣:https://github.com/alexmojaki/birdseye –

回答

3

PDB跟踪使用通过sys.settrace设置的跟踪功能。有一个数字,将触发跟踪功能的事件,但你看的那些都是线事件:

'line'
的解释即将执行代码新行或者重新执行循环条件。局部跟踪功能被调用; argNone;返回值指定新的本地跟踪功能。请参阅Objects/lnotab_notes.txt以了解其工作原理的详细说明。

正如文档所述,您可以在Objects/lnotab_notes.txt中看到线事件触发器的更详细的解释。最相关的部分是

我们通过如果 co_lnotab表明我们已经跃升为开头的行的,即只要求向前跳跃路线追踪功能解决这个问题如果当前的 指令偏移量与由 co_lnotab给出的行开始偏移量相匹配。但是,对于向后跳转,我们总是调用线跟踪功能 ,该功能可让调试器在对环路保护的每次评估(通常 不会成为一行中的第一个操作码)时停止。

因此,PDB将在行的开始处暂停,或者如果执行在代码中向后跳转。


如果你想看到触发线事件的源代码,它在Python/ceval.cmaybe_call_line_trace下。 PDB的源代码可预见地在Lib/pdb.py之下。