2015-02-10 153 views
1

讀「每個程序員應該知道的97件事」我發現有關代碼分析工具的有趣文章。使用反彙編程序來調試錯誤Python

作者聲稱,從Python標準庫彙編器可以調試你的每一天代碼

這裏去非常有用: 「有一件事這個庫(Python標準庫反彙編器)可以拆卸 是你最後的堆棧跟蹤,給你反饋哪個字節碼指令拋出最後一個未捕獲的異常。「

但有沒有這方面的解釋書中

所以,是否有人有想法上述模塊怎麼可能對調試有用嗎?

+2

開始[這裏](https://docs.python.org/3/library/dis.html)。 – Kevin 2015-02-10 16:01:07

+0

異常通常是數據問題的結果,而不是代碼。 – 2015-02-10 16:38:29

回答

2

儘管反彙編程序可以幫助您瞭解Python如何理解您編寫的內容,但它不是唯一的工具。還有其他工具也可以提供幫助。我們將看到其中一些可以一起工作。

所以這裏有一個小片的Python:

# Python bytecode 3.4 (3310) 
# Disassembled from Python 3.4.2 (default, May 17 2015, 22:17:04) 
# [GCC 4.8.2] 
# Timestamp in code: 1499405520 (2017-07-07 01:32:00) 
# Source code size mod 2**32: 39 bytes 

# Method Name:  <module> 
# Filename:   five.py 
# Argument count: 0 
# Kw-only arguments: 0 
# Number of locals: 0 
# Stack size:  2 
# Flags:    0x00000040 (NOFREE) 
# First Line:  1 
# Constants: 
# 0: <code object five at 0x7f99dd4e88a0, file "five.py", line 1> 
# 1: 'five' 
# 2: None 
# Names: 
# 0: five 
# 1: print 
    1: 
     LOAD_CONST    0 (<code object five at 0x7f99dd4e88a0, file "five.py", line 1>) 
     LOAD_CONST    1 ('five') 
     MAKE_FUNCTION    0 (0 positional, 0 name and default, 0 annotations) 
     STORE_NAME    0 (five) 

    3: 
     LOAD_NAME     1 (print) 
     LOAD_NAME     0 (five) 
     CALL_FUNCTION    0 (0 positional, 0 keyword pair) 
     CALL_FUNCTION    1 (1 positional, 0 keyword pair) 
     POP_TOP 
     LOAD_CONST    2 (None) 
     RETURN_VALUE 
... 

(這是Python的:使用跨平臺的反彙編我寫這就是所謂xdis它的拆卸

def five(): 
    return 5 
print(five()) 

這裏部分3.4,其他版本略有不同)。

首先要注意的是python認爲這段代碼來自一個路徑名爲的文件。如果你碰巧重命名文件而不是Python代碼,這可能會混淆Python。或者文件名可能是tmp/five.py,然後你應該找那個。此外,在Python版本3及更高版本中,文件的大小爲(模2 ** 32)作爲檢查以查看文件系統上的five.py是否與編譯該文件時看到的一樣。

我將注意力放在代碼的開頭:我們正在加載一個常量對象,它恰好是函數的代碼!然後再輸入函數的名稱,最後調用MAKE_FUNCTION並將其保存在一個名爲五個的變量中。

如果您習慣於像C++,Go或Java這樣的編譯語言,但不這樣做的事情有點不同尋常,那就是當您運行該程序時,該功能就是在當地創建的。如果我的節目之前有另外的指令,並分別改爲:

x = five() # five is hasn't been defined here! 
def five(): ... 

因爲該MAKE_FUNCTION尚未運行,因此在開始5尚未確定這將失敗。

現在,我還會建議你也許可以這樣使用調試器,以及和我再次trepan2或具有該裝配內置了一個命令,拆卸,甚至逆解析器trepan3建議學習。

反彙編可以闡明的另一個地方是Python對代碼進行優化的極少數情況。

考慮這個Python源代碼:

if 1: 
    y = 5 

在這裏,Python版本後約2.3只會注意到if 1:是多餘的,並刪除代碼。但是,如果你不得不說,而不是:

x = 1 
if x: 
    y = 5 

這足以混淆的Python,以保持在拆卸測試是我認爲你可以知道這一點的唯一方法。

最後一個方面,是正確理解其中當你在調試器中停止或出現錯誤時。你經常(但不是總是)弄清楚你有錯誤的路線,但有時候這可能會讓你感到困惑。正常 Python屏蔽了這裏很有用的信息,指令偏移量,但是我會告訴你如何得到這些信息以及指令在哪裏出錯。

假設我的代碼是:

prev = [100] + range(3) 
x = prev[prev[prev[0]]] 

如果我跑這我會得到一個IndexError例外。但是哪個「prev」是?

trepan2(或trepan3k)這裏暴露指令指針。它還可以訪問反彙編程序和解析程序。那麼讓我們來看看如何在這裏使用:

trepan2 /tmp/boom.py 
-> 2 prev = [100] + range(3) 
(trepan2) next 
(/tmp/boom.py:3 @19): <module> 
-- 3 x = prev[prev[prev[0]]] 
(trepan2) next 
(/tmp/boom.py:3 @32): <module> 
!! 3 x = prev[prev[prev[0]]] 
R=> (<type 'exceptions.IndexError'>, 'list index out of range', <traceback object at 
(trepan2) info pc 
PC offset is 32. 
    2  0 LOAD_CONST   0   100 
      3 BUILD_LIST   1 
      6 LOAD_NAME   0   0 
      9 LOAD_CONST   1   3 
      12 CALL_FUNCTION  1   1 positional, 0 keyword pair 
      15 BINARY_ADD   None 
      16 STORE_NAME   1   1 

    3  19 LOAD_NAME   1   1 
      22 LOAD_NAME   1   1 
      25 LOAD_NAME   1   1 
      28 LOAD_CONST   2   0 
      31 BINARY_SUBSCR  None 
    --> 32 BINARY_SUBSCR  None 
      33 BINARY_SUBSCR  None 
      34 STORE_NAME   2   2 
      37 LOAD_CONST   3   None 
      40 RETURN_VALUE  None 

好的。所以我們看到,其中正好是我們偏移32(@ 32之前在偏移@ 19處停止之後),但這是什麼意思?該環鋸將調試轉換回Python的這個,所以你不必自己做:

(trepan2) deparse -p 
instruction: 32 BINARY_SUBSCR 
x = prev[prev[prev[0]]] 
     ------------- 
Contained in... 
Grammar Symbol: binary_subscr 
x = prev[prev[prev[0]]] 
    ------------------- 
(trepan2) prev 
[100, 0, 1, 2] 

以上,那麼,說明你在偏移32(不是31或33)和特定上一頁訪問不是第一次訪問prev[0]但後面的那個prev[prev[0]]

雖然在調試器內部同時具有disasembler和deparser,但它使您不必知道很多有關正在發生的事情。但我不認爲知道指令的作用或指令的順序是很痛苦的。