斷點除錯和日誌除錯之間的平衡點:函式計算除錯之 Python 篇
雲棲君導讀: 本文將介紹一種結合函式堆疊,捕獲棧中local變數的方式來達到快速定位bug的目的。
Python 除錯篇
很多初學者喜歡使用斷點除錯,方便之處是可以查到執行期各種棧內的變數值,來幫助debug。
但這一點如果脫離了IDE,其實是非常困難的。在伺服器的執行過程中,更需要使用attach的方式才可能做到這點。
對於一些生產環境的錯誤定位,用斷點除錯幾乎是完全不可能的。
而使用日誌來做錯誤定位,對於一些指令碼語言,尤其弱型別的語言,當你將一個變數經過多個函式傳遞的過程中,如果傳遞過程中不小心有拼寫錯誤,只有最後使用到這個變數的地方才報出錯誤來,使用日誌的方式要定位什麼地方寫錯了非常困難,對於生產環境中多分支呼叫鏈極長的邏輯,更是難上加難。
本文將介紹一種結合函式堆疊,捕獲棧中local變數的方式來達到快速定位bug的目的。
相對於其他runtime,python可以獲取到很多執行時資訊。通常來說,通過異常捕獲,python 預設的traceback 給出的棧和錯誤資訊已經能幫助開發者除錯了。但時常來說,這並不是萬能的,偶爾會遇到一些問題,必須加日誌才能解決。但如果開發者日誌加得不夠細,生產環境中也很難立即重現,此時有什麼好辦法呢?
下面我們通過一個簡單的例子來講解如何在斷點除錯和日誌除錯中找一個平衡點定位python指令碼中的bug。
假設 bar 中陣列訪問越界,實際的bug在c = a + b 那行,其實本應該是c = a - b,我們沒有日誌,此時如何定位到這個bug?
使用範例:
-
我們先建立程式碼目錄:
-
複製貼上以下程式碼到 ~/sandbox/fc/traceback/python/main.py
-
然後切換到 ~/sandbox/fc/traceback 目錄
-
執行 shell 命令: fcli shell,關於 fcli
-
執行下述命令,其中 -d python 指的是當前程式碼所在目錄 python ,而 python2.7 指 python2.7 runtime
-
接下來我們使用 pip 安裝 tracebackturbo 這個庫
-
本地可以先做一下測試:
測試結果:
我們可以看到棧中每個local變數都已經被print了出來,在生產環境中,我們可以在 service 上設定 logstore,將這部分錯誤資訊輸出到日誌服務。
比較
我們可以比較全日誌 及 traceback 日誌的優缺點:
-
全日誌
優點
可以隱藏敏感資訊
對於無報錯,無異常丟擲的程式碼也可以做有效記錄
缺點
日誌可能記錄不全,線上問題調查很困難
需要記錄大量日誌,太多的日誌會導致效能低下
-
traceback 日誌
優點
報錯時可以拿到整個棧的資訊,分析問題可以非常全面
日誌簡潔,在沒有報錯的時候,不會有其他資訊干擾
由於只在報錯才有日誌,正常情況下只有try的開銷,相對來說效能更高
缺點
區域性變數中含有敏感資訊,可能會暴露給日誌檢視人員
實現原理簡介
接下來我們瞭解一下這個庫的實現原理,簡要提一下計算機系統執行時棧的結構:
棧結構
frame 的結構
通常來說,各類計算機語言的 runtime 實現(實現細節及名稱可能各不相同)都會包含上述資訊。
function proto 結構
每個變數包含
-
宣告行
-
相對於frame 基地址偏移
-
區域性變數宣告週期對應指令集
對於任何一個未 return 的函式,如果我們拿到了這個棧,就可以獲取到棧頂的若干 frame ,找到function proto,就可以找到各個區域性變數的偏移,通過 frame 基地址相加,我們就可以得到每個區域性變數的地址,獲取到每個變數的內容。
相關連結
-
lua traceback
https://github.com/rocaltair/ltrace
-
lua 呼叫動態連結庫 crash
https://github.com/rocaltair/lua-coredump