為什麼需要專用工具#
撰寫低階多執行緒程式碼極為容易出錯,存在數十種細微的錯誤可能,而且往往只在客戶環境、重要 demo 或交付期限前才出現。使用專用工具可以大幅節省除錯時間。
靜態分析工具#
透過靜態分析(static analysis)檢查原始碼,找出可能的並行錯誤模式。
FindBugs(Java)#
- 可偵測 “Multi-threaded Correctness” 類別下的 45 種錯誤
- 包括:錯誤的同步化元素、
wait()/notify()呼叫不匹配、不一致的同步化、未保護的欄位、忘記釋放鎖 - 可從命令列、GUI、IDE 或 CI 流程中執行
$ java -jar findbugs.jar -textui Counter.class
M M IS: Inconsistent synchronization of Counter.n;
locked 60% of time
Unsynchronized access at Counter.java:[line 9]FindBugs 能發現
increment()有synchronized但decrement()沒有的不一致問題——這類錯誤在 code review 中也容易被忽略。
動態分析工具#
另一類工具分析程式的執行時期行為,可以偵測 race condition、deadlock、API 誤用和效能退化。
動態分析工具會使程式慢數個數量級,且大幅增加記憶體使用量。這是為了細緻檢查多執行緒間的依賴關係所必須付出的代價。
Intel Inspector#
- 可偵測 OpenMP 和 POSIX Threads 程式碼中的 data race 和 deadlock
- 提供 GUI 介面,清楚列出問題位置與類型
以 OpenMP 計數器程式為例,Intel Inspector 找出兩個 data race:
- 變數
i在執行緒間錯誤共享 - 變數
counter未使用原子操作遞增
修正方式:用 #pragma omp threadprivate(i) 讓每個執行緒有自己的 i,並用 #pragma omp atomic 保護 counter++。
Helgrind(Valgrind 工具套件)#
- 偵測 C、C++、Fortran 中使用 POSIX Threads 的同步化錯誤
- 能偵測潛在 deadlock(potential deadlock),即使 deadlock 在該次執行中沒有實際發生
valgrind --tool=helgrind deadlockHelgrind 透過建立鎖取得順序的模型來偵測潛在 deadlock:
- 觀察
bob建立的取鎖順序為 m1 -> m2 - 發現
alice違反了此順序(m2 -> m1) - 即使程式跑了 61,266 次才實際 deadlock,Helgrind 只需一次執行就能偵測到
重點回顧#
- 使用靜態分析工具(如 FindBugs)掃描原始碼,找出同步化和鎖定錯誤
- 使用動態分析工具(如 Intel Inspector、Helgrind)在執行時期偵測 race condition、潛在 deadlock 和 API 誤用
- 動態分析工具能找到指令間距離很遠的細微 bug,這是大多數靜態分析工具做不到的
- Helgrind 透過 partial ordering 模型,即使 deadlock 未實際發生也能偵測到潛在風險