使用 Sanitizer
什麼是 Sanitizer?
Sanitizer 是一種靜態檢測工具,能夠協助找出傳統除錯器通常無法發現的錯誤。這在持續整合流程中,與 單元測試 結合時特別有用。
可於 Windows、macOS 與 Linux 上,透過 Clang(LLVM)、GCC 或 Visual Studio 編譯器使用 Sanitizer。部分平台 也可能提供其專屬的 Sanitizer。同一種 Sanitizer 若由不同編譯器提供,其輸出結果與行為可能會略有差異。
在 Godot 中使用 Sanitizer
Sanitizer 必須 重新編譯執行檔,這表示無法直接用官方 Godot 執行檔來運作 Sanitizer。
啟用任一種 Sanitizer 進行 編譯 時,產生的執行檔名稱會加上 .san 後綴,以和未啟用 Sanitizer 的版本做區分。
由於需要進行許多額外的執行時檢查,因此會影響效能,記憶體用量也會增加。部分 Sanitizer 可以於單一建置中同時啟用多種組合,但需注意同時啟用多個 Sanitizer 會大幅降低效能,導致產生的執行檔運作極慢。
某些選項可以透過環境變數傳遞給 Sanitizer,無需重新編譯執行檔。
位址 Sanitizer(ASAN)
適用於 Clang 與 GCC。
支援平台: Linux、macOS、Windows(Visual Studio)、Web
Address Sanitizer 通常是最常用的工具。它可以偵測像是緩衝區溢位或越界存取等問題。如果引擎當機並顯示像是 free(): invalid pointer 的訊息,通常就是緩衝區溢位所導致的。(這個訊息是由 C 執行時環境印出的,不是 Godot 印出的。)
在某些情況下(如偵測未初始化的記憶體讀取),位址 Sanitizer 並不適用,請改用 記憶體 Sanitizer(MSAN)。
你也可以在*執行* Godot 前(非編譯時)設定 ASAN_OPTIONS=detect_stack_use_after_return=1 環境變數,來偵測函式返回後的存取行為。此功能會增加位址 Sanitizer 的執行時負擔,建議僅在必要時啟用。
如需在 Godot 編譯時啟用位址 Sanitizer,請於編譯指令加入 use_asan=yes SCons 參數。啟用 ASAN 通常會讓產生的執行檔效能降低約兩倍。
警告
由於 設計決策,位址、記憶體及執行緒 Sanitizer 彼此互斥,單一執行檔僅能啟用其中之一。
洩漏 Sanitizer(LSAN)
適用於 Clang 與 GCC。
支援平台: Linux、Web
洩漏 Sanitizer 能偵測記憶體洩漏,也就是程式已經不再使用、但未釋放的記憶體。如果程式長時間執行,最終可能造成記憶體耗盡。由於 Godot 可能會運作於 專用伺服器 上數月甚至數年不重啟,因此發現洩漏時務必修正。
如需於 Godot 建置啟用洩漏 Sanitizer,請於編譯指令加入 use_lsan=yes SCons 參數。啟用 LSAN 對效能影響很小,但程式結束時因進行洩漏檢查,結束速度會明顯變慢。
記憶體 Sanitizer(MSAN)
僅支援 Clang,不支援 GCC。
支援平台: Linux
記憶體 Sanitizer 補足了 位址 Sanitizer(ASAN) 的不足。與位址 Sanitizer 不同,MSAN 能偵測未初始化記憶體的讀取行為。
如需在 Godot 建置時啟用記憶體 Sanitizer,請在編譯時加入 use_msan=yes SCons 參數。啟用 MSAN 通常會讓產生的執行檔效能降低約三倍。
警告
由於 設計決策,位址、記憶體及執行緒 Sanitizer 彼此互斥,單一執行檔僅能啟用其中之一。
執行緒 Sanitizer(TSAN)
適用於 Clang 與 GCC。
支援平台: Linux、macOS
執行緒 Sanitizer 用於追蹤多執行緒運作時的競爭條件。競爭條件是指多個執行緒同時嘗試修改同一份資料。作業系統排程執行緒的順序是不可預測的,這會導致僅偶發的不正確行為,因此也很難追蹤。為避免競爭條件,需加上鎖定,確保同一時間僅有一個執行緒能存取共享資料。
如需於 Godot 建置啟用執行緒 Sanitizer,請於編譯時加入 use_tsan=yes SCons 參數。啟用 TSAN 通常會讓執行檔效能降低約 10 倍,且記憶體使用量大幅增加(約 8 倍)。
警告
由於 設計決策,位址、記憶體及執行緒 Sanitizer 彼此互斥,單一執行檔僅能啟用其中之一。
備註
在 Linux 上,如果你遇到以下錯誤:
FATAL: ThreadSanitizer: unexpected memory mapping
你可能需要暫時降低系統中的位址空間配置隨機化(ASLR)熵值,可以執行以下指令:
sudo sysctl vm.mmap_rnd_bits=28
或者建議直接完全停用 ASLR,可以執行:
sudo sysctl kernel.randomize_va_space=0
等你完成 ThreadSanitizer 的使用後,請用下列指令提高 ASLR 熵值:
sudo sysctl vm.mmap_rnd_bits=32
或用下列指令重新啟用 ASLR:
sudo sysctl kernel.randomize_va_space=2
重新啟動你的電腦也會將 ASLR 狀態恢復為預設值。
請儘快還原這些設定,因為降低 ASLR 熵值或完全停用 ASLR 會帶來安全風險。
未定義行為 Sanitizer(UBSAN)
適用於 Clang 與 GCC。
支援平台: Linux、macOS、Web
未定義行為 Sanitizer 用於追蹤程式出現隨機或不可預期行為的情境。這通常源於被編譯器接受、但並不*正確*的 C/C++ 程式碼。使用不同最佳化選項編譯,同樣的未定義行為也可能導致不同執行結果。
如需於 Godot 建置啟用未定義行為 Sanitizer,請於編譯時加入 use_ubsan=yes SCons 參數。啟用 UBSAN 對效能影響極小。
特定平台 Sanitizer
Web
當你 為 Web 編譯 時,還有另外兩個可用的 Sanitizer SCons 選項:
use_assertions=yes會啟用 Emscripten 執行時斷言,可用於偵測各類錯誤。use_safe_heap=yes會啟用 Emscripten 的 SAFE_HEAP Sanitizer。其功能類似於 ASAN,但專注於 WebAssembly 特有的問題。SAFE_HEAP無法保證和同一執行檔內的 ASAN 與 UBSAN 相容,因此可能需分開建置。