使用動態程式分析工具#

許多專門的工具可以對你的已編譯程式植入檢查例程、監控其執行,並回報偵測到的潛在錯誤。這類檢查稱為 dynamic analysis(動態分析),因為它是在程式實際執行時進行的。動態分析補充了 static analysis(靜態分析)和 debugging libraries 的不足。

動態分析 vs 靜態分析#

特性動態分析靜態分析
分析方式追蹤實際執行的程式碼推導可能執行的程式碼
False positives極低(幾乎不會誤報)較高
False negatives可能遺漏未執行路徑上的錯誤較低
效能影響顯著降低程式速度無(編譯時分析)

因為動態分析工具會大幅降低程式速度且可能回報大量低優先級錯誤,除錯時最好搭配非常具體的測試腳本來使用,而非完整的應用場景。

使用策略#

作為程式碼衛生維護方法,你可以:

  1. 用完整且真實的測試場景運行被分析的程式碼
  2. 將所有已知的回報錯誤加入 whitelist
  3. 之後只需關注新引入變更時出現的新錯誤

可偵測的問題類型#

  • 使用未初始化的值
  • 記憶體洩漏(memory leaks)
  • 超出可用記憶體空間的非法存取
  • 安全漏洞
  • 次優程式碼和不完整的程式碼覆蓋率
  • 隱式型別轉換、動態型別不一致、數值溢位
  • 並行錯誤(deadlocks 和 race conditions,參見 Item 62)

Valgrind:強大的記憶體檢查工具#

Valgrind 是一個廣泛使用的開源動態分析系統,其記憶體檢查元件特別強大。

以下程式在三行程式碼中包含了三個錯誤:記憶體洩漏、非法記憶體存取、回傳未初始化的值:

#include <stdlib.h>

int
main()
{
    char *c = malloc(42);

    c[42] = 1;
    return c[0];
}

使用以下命令執行:

valgrind --track-origins=yes --leak-check=yes memory

Valgrind 會精確識別出所有三個錯誤及其在程式中的位置:

  • Invalid write of size 1c[42] = 1 超出了 42 bytes 的配置範圍
  • Uninitialised valuereturn c[0] 使用了未初始化的值,並追溯到該值由 malloc 建立
  • Memory leak:配置的記憶體未被釋放

Wikipedia 的 dynamic program analysis 頁面列出了數十種工具,可以根據你的環境、問題類型和預算選擇合適的工具。

重點回顧#

  • 使用動態分析工具對程式的執行進行 runtime 檢查
  • 善用 Valgrind 等工具偵測記憶體相關的錯誤
  • 動態分析的 false positive 率極低,當工具報告錯誤時,幾乎可以確定是真實問題