2017-08-11 88 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之下。