核心概念#
對付非確定性的並行錯誤,一個強大的方法是錄製(capture)程式執行過程的詳細記錄,然後在 debugger 中重播(replicate),使原本不可重現的 bug 變得可重現。
可用工具#
- Intel PinPlay / DrDebug Program Record/Replay Toolkit:搭配 Eclipse 或 gdb 使用,適用於 Intel 架構的二進位檔
- Chronon:Java 應用程式專用的錄製與除錯工具
這些工具能記錄真正並行執行(多核心上的)操作的實際順序,包括主記憶體的讀寫順序。
工作流程#
- 反覆執行並錄製:啟用錄製功能重複執行程式,直到 bug 出現,保存該次錄製
- 分析錄製:找到 bug 首次發生的時間點(可使用 program slice analysis 技術識別執行緒間的可疑依賴)
- 重播錄製:在 debugger 下重播,直到到達 bug 發生的時間點
- 分析程式狀態:檢查該時間點的所有執行緒與變數,找出根本原因
實際範例:使用 PinPlay 除錯 Race Condition#
以一個存在 race condition 的計數器程式為例(兩個執行緒非原子地讀取、遞增、寫回 counter 變數):
錄製失敗的執行#
$ gdb_record race
(gdb) break main
(gdb) continue
(gdb) pin record on # 開始錄製
(gdb) continue # 程式執行完畢,結果不正確重播驗證#
錄製資料存在 pinball 目錄中,用 replay 指令重播會得到完全相同的結果:
$ replay pinball/log_0
counter=127873 # 每次重播結果都一樣追蹤變數找到問題#
- 用
gdb_replay載入錄製,設定pin trace tmp at 16追蹤變數值 - 將 trace 輸出到檔案,尋找重複的值(代表兩個執行緒讀到相同的舊值)
- 設定條件中斷點,在重複值出現時暫停
- 檢查所有執行緒——發現兩個執行緒共享相同的
tmp值,確認需要原子操作
錄製會使程式慢至少兩個數量級,因此在複雜程式中應盡可能延後開始錄製的時機。
重點回顧#
- 透過錄製失敗的執行過程,將非確定性的並行錯誤轉化為可重現的問題
- 重播時可以反覆在相同的執行序列上進行分析,不再受隨機性影響
- 利用 trace 和條件中斷點在重播中精確定位 race condition 發生的時刻
- 錄製的效能代價很高,應策略性地選擇錄製的起始時機