核心概念#

對付非確定性的並行錯誤,一個強大的方法是錄製(capture)程式執行過程的詳細記錄,然後在 debugger 中重播(replicate),使原本不可重現的 bug 變得可重現。

可用工具#

  • Intel PinPlay / DrDebug Program Record/Replay Toolkit:搭配 Eclipse 或 gdb 使用,適用於 Intel 架構的二進位檔
  • Chronon:Java 應用程式專用的錄製與除錯工具

這些工具能記錄真正並行執行(多核心上的)操作的實際順序,包括主記憶體的讀寫順序。

工作流程#

  1. 反覆執行並錄製:啟用錄製功能重複執行程式,直到 bug 出現,保存該次錄製
  2. 分析錄製:找到 bug 首次發生的時間點(可使用 program slice analysis 技術識別執行緒間的可疑依賴)
  3. 重播錄製:在 debugger 下重播,直到到達 bug 發生的時間點
  4. 分析程式狀態:檢查該時間點的所有執行緒與變數,找出根本原因

實際範例:使用 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          # 每次重播結果都一樣

追蹤變數找到問題#

  1. gdb_replay 載入錄製,設定 pin trace tmp at 16 追蹤變數值
  2. 將 trace 輸出到檔案,尋找重複的值(代表兩個執行緒讀到相同的舊值)
  3. 設定條件中斷點,在重複值出現時暫停
  4. 檢查所有執行緒——發現兩個執行緒共享相同的 tmp 值,確認需要原子操作

錄製會使程式慢至少兩個數量級,因此在複雜程式中應盡可能延後開始錄製的時機。

重點回顧#

  • 透過錄製失敗的執行過程,將非確定性的並行錯誤轉化為可重現的問題
  • 重播時可以反覆在相同的執行序列上進行分析,不再受隨機性影響
  • 利用 trace條件中斷點在重播中精確定位 race condition 發生的時刻
  • 錄製的效能代價很高,應策略性地選擇錄製的起始時機