2011年6月2日 星期四

Python 3.3 中,幫助除錯的新模組-- faulthandler

原文網址 New faulthandler module in Python 3.3 helps debugging

當一名使用者跟你回報你的程式當掉或是卡住不動了,有的時候你只能盡力收集程式運行時資料, 並且確立使用方式來重現當時的狀況。 但就算你已經很清楚使用者如何使用你的程式,你也常常無法重現一樣的症狀,因為使用者的執行環境跟你的不一樣, 比如說作業系統或是編譯器的不同。如果運氣夠好的話,使用者可以安裝除錯工具,但大部份的情況下你只能期待另一個人在出了一樣的錯後能提供更多資訊來幫助你找到問題的根源。

嚴重錯誤

Python 3.3 加入了一個新的模組 faulthandler 來幫助程式設計師面對這個問題。 faulthandler 讓你在發生嚴重錯誤 時可以追朔執行過的程式碼。比如說數學運算時,分母為零( division by zero) 、異常中止( abort )、匯流排錯誤( bus error )。 你可以在你程式中使用 faulthandler.enable() 來開啟這個功能。或是在Python啟動時指定 -X faulthandler 這個選項。又或著是設定 PYTHONFAULTHANDLER=1 這個環境變數。 開啟後,程行遇到問題當掉後就可以看到類似下面的畫面:

Fatal Python error: Segmentation fault

Current thread 0x00007f7babc6b700:
  File "Lib/test/crashers/gc_inspection.py", line 29 in g
  File "Lib/test/crashers/gc_inspection.py", line 32 in <module>
Segmentation fault

運行超時( Timeout )

faulthandler 也可以偵測程式執行超時( timeout )。使用 faulthandler.dump_tracebacks_later(later) 來開始計算程式的運作時間, faulthandler.cancel_dump_tracebacks_later() 可以停止計時。 當執行超時發生,的產生的結果如下:

Timeout (0:01:00)!
Current thread 0x00007f987d459700:
  File "Lib/test/crashers/infinite_loop_re.py", line 20 in <module>

使用 repeat=True ,則每一次超出執行時間都會印出如上的畫面,或著指定 exit=True 來立刻關閉程式。 但這比較不安全,比如說這樣做沒有確實清空檔案的緩衝區。

由發出訊號來觀察執行狀況

我們更可以靠發出訊號給程式來追朔執行狀況,你可以用 faulthandler.register(signal) ,指定用發出訊號的方式趨使程式印出程式的執行狀況。 比如說在 UNIX上,你可以使用 SIGUSR1 : kill -USR1 <pid> 來讓你的程式印出當時的情況。 不過windows 是沒有這個功能的。 輸出會如下:

Current thread 0x00007fdc3da74700:
  File "Lib/test/crashers/infinite_loop_re.py", line 19 in <module>

另外,你可以用 faulthandler.dump_traceback() 來在任何時候自已印出執行狀況。

安全性考量以及所用的輸出檔案

由於安全性的考量 faulthandler 預設是關閉的。 會這麼做的主要原因是它會輸出結果到 sys.stderr 但如果sys.stder 被關閉了,且 sys.stderr 檔案描述子( file descriptor )被重用了, 我們的執行狀況就會被輸出到其他的地方,像是管線 (pipe)、socket、 或是某些檔案。這樣有可能讓存在程式中的敏感資料無意間洩露出去。 faulthandler 預設是使用 sys.stderr ,不過你可以選擇別的檔案。參考 faulthandler documentation 來了解更多的資訊。

舊的Python 也可以用

faulthandler 將會以第三方模組的型式放在 PyPI 上, 並且支援 Python 2.5 到 3.2。 但與3.3版相較,最大的不同是在 dump_tracebacks_later 的實作上: Python 3.3 使用執行緒並且運用一個有time out 機制的 lock。 而第三方函式庫實作的版本是使用 SIGALRM 以及 alarm()

Lock timeout 是 Python 3.3 的新功能,它計算時間的精準度可以達到百萬之一秒。 而使用 alarm() 的舊版 Python 的計時精準度只能達到秒的程度。 而且使用 SIGALRM 有可能中斷正在執行的系統呼叫( system call ),產生 EINTR 錯誤 (譯注: 關於 EINTR, 可以參考這篇 Python - Pipe 在 Signal 發生時的處理事項 ) 。

它真的有用

這個新的 faulthandler 模組已經幫助我們找到我們自已的編譯程式中存在的 race conditions。我們期望這也可以為你帶來幫助。

沒有留言:

張貼留言