本文說明如何在使用 AGDE 時啟用特殊偵錯工具。這些工具可找出難以診斷的記憶體毀損情形,並覆寫錯誤。
HWAddress Sanitizer 和 Address Sanitizer
HWAddress Sanitizer (HWASan) 和 Address Sanitizer (ASan) 是記憶體毀損偵錯工具,可偵測記憶體毀損情形並覆寫錯誤,例如以下情形:
- 堆疊緩衝區溢位和反向溢位
- 堆積緩衝區溢位和反向溢位
- 超出範圍的堆疊使用情形
- 重複釋放和野釋放錯誤
- 回傳後的堆疊使用情形 (僅限 HWASan)
建議您僅在偵錯或執行自動化測試時,才啟用 HWASan 或 ASan。這些工具雖然很有效,但使用時可能導致效能降低。
執行階段行為
啟用後,HWASan 和 ASan 會自動檢查應用程式中的記憶體毀損情形。
如果偵測到記憶體錯誤,應用程式會因 SIGBART
(訊號取消) 錯誤異常終止,並將詳細訊息顯示在 Logcat。訊息也會寫入 /data/tombstones
下的檔案。
錯誤訊息應如下所示:
ERROR: HWAddressSanitizer: tag-mismatch on address 0x0042a0826510 at pc 0x007b24d90a0c
WRITE of size 1 at 0x0042a0826510 tags: 32/3d (ptr/mem) in thread T0
#0 0x7b24d90a08 (/data/app/com.example.hellohwasan-eRpO2UhYylZaW0P_E0z7vA==/lib/arm64/libnative-lib.so+0x2a08)
#1 0x7b8f1e4ccc (/apex/com.android.art/lib64/libart.so+0x198ccc)
#2 0x7b8f1db364 (/apex/com.android.art/lib64/libart.so+0x18f364)
#3 0x7b8f2ad8d4 (/apex/com.android.art/lib64/libart.so+0x2618d4)
0x0042a0826510 is located 0 bytes to the right of 16-byte region [0x0042a0826500,0x0042a0826510)
allocated here:
#0 0x7b92a322bc (/apex/com.android.runtime/lib64/bionic/libclang_rt.hwasan-aarch64-android.so+0x212bc)
#1 0x7b24d909e0 (/data/app/com.example.hellohwasan-eRpO2UhYylZaW0P_E0z7vA==/lib/arm64/libnative-lib.so+0x29e0)
#2 0x7b8f1e4ccc (/apex/com.android.art/lib64/libart.so+0x198ccc)
必要條件
HWASan 相關規定
如要使用 HWASan,請按照下列步驟操作:
- 您必須使用 AGDE 24.1.99 以上版本。
- 應用程式必須使用 NDK 26 以上版本建構。
- 應用程式必須以目標 SDK 34 以上版本建構。
- 目標必須是搭載 Android 14 (API 級別 34) 以上版本的
arm64-v8a
裝置。
在專案中使用共用 C++ 標準程式庫
由於某項已知問題的影響,使用 libc++_static
時,ASan 目前無法處理 C++ 例外狀況。使用 libc++_shared
時則不會發生這個問題。
HWASan 擁有專屬的運算子 new
和 delete
實作方法,如果標準程式庫是靜態連結至專案,就無法使用這些方法。
如要變更這項設定,請參閱本文件中的連結 C++ 標準程式庫相關說明。
啟用影格指標產生功能
HWASan 與 ASan 可使用快速影格指標型解開器,為記憶體配置和取消配置事件產生堆疊追蹤資訊。這代表您必須在 C++ 編譯器設定中啟用影格指標產生功能,才能使用這些功能。您也需要停用影格指標省略最佳化功能。
如要變更這項設定,請參閱本文件的「啟用影格指標產生功能」一節。
設定 Visual Studio 專案以使用 HWASan 或 ASan
啟用 HWASan 或 ASan
如要啟用 HWASan 或 ASan,請在專案的「Property Pages」中,依序點選「Configuration Properties」>「General」。
圖 1:專案在 Visual Studio「Solution Explorer」視窗中的「Properties」選項。
圖 2:一般專案屬性中的「Address Sanitizer (ASan)」設定。
如要為專案啟用 HWASan,請將「Address Sanitizer (ASan)」設定變更為「Hardware ASan Enabled (fsanitize=hwaddress)」。
如要為專案啟用 ASan,請將「Address Sanitizer (ASan)」設定變更為「ASan Enabled (fsanitize=address)」。
啟用影格指標產生功能
如要控管影格指標產生功能,需使用「Omit Frame Pointer」C/C++ 編譯器設定。在專案的「Property Pages」對話方塊中,依序選取「Configuration Properties」>「C/C++」>「Optimization」,即可存取這項設定。
圖 3:「Omit Frame Pointer」設定的位置。
使用 HWASan 或 ASan 時,請將「Omit Frame Pointer」設定設為「No (-fno-omit-frame-pointer)」。
在共用程式庫模式中連結 C++ 標準程式庫
在專案「Property Pages」對話方塊中,依序點選「Configuration Properties」>「General」,即可在「Project Defaults」部分找到 C++ 標準程式庫的連結器模式設定。
圖 4:C++ 標準程式庫連結器模式設定的位置。
使用 HWASan 或 ASan 時,請將「Use of STL」設為「Use C++ Standard libraries (.so)」。這個值會以「共用程式庫」的形式將 C++ 標準程式庫連結至專案,這是讓 HWASan 與 ASan 正常運作的必要步驟。
針對 Address Sanitizer 使用情形建立建構設定
如果您偏向「暫時」使用 HWASan 或 ASan,就不建議專為這項用途建立新的建構設定。當專案規模較小,或是您單純在瞭解這項功能或處理測試期間發現的問題時,就可能屬於這種情況。
不過,如果覺得這項功能很實用,且預計經常使用,則建議您考慮為 HWASan 或 ASan 建立新的建構設定,如 Teapot 範例所示。舉例來說,如果您會在單元測試或遊戲的整晚冒煙測試中定期執行 Address Sanitizer,就可以採用這種做法。
如果您的大型專案使用大量不同的第三方程式庫,而且這些程式庫通常是靜態連結至 C++ 標準程式庫,那另外建立建構設定就可能特別實用。專屬建構設定可隨時確保專案設定準確無誤。
如要建立建構設定,請按一下專案「Property Pages」中的「Configuration Manager…」按鈕,然後開啟「Active solution configuration」下拉式選單。接著請選取
搭配自訂記憶體配置器使用 HWASan
HWASan 會自動攔截透過 malloc
(或 new
) 分配的記憶體,以便將標記插入指標並檢查標記是否不相符。
不過,使用自訂記憶體配置器時,HWASan 無法自動攔截自訂記憶體配置方法。因此,如果您想搭配自訂記憶體配置器使用 HWASan,請檢測記憶體配置器,以便明確呼叫 HWASan。只需幾行程式碼即可完成。
必要條件
您需要呼叫的 HWASan 方法已在這個標頭中定義:
#include "sanitizer/hwasan_interface.h"
檢測記憶體配置方法
以 16 個位元組區塊精細度和對齊方式分配物件。舉例來說,如果您有一個集區配置器,可提供 24 位元大小的固定大小物件,請將配置值四捨五入為 32 位元,並對齊至 16 位元。
產生 8 位元標記。代碼不得使用 0 到 16 的值,因為這些值是保留供內部使用。
啟用 HWASan,開始追蹤該標記的記憶體區域:
__hwasan_tag_memory((void*) address, tag, size);
將標記插入指標的頂端 8 位元:
address = __hwasan_tag_pointer((void*) address, tag);
檢測記憶體釋放方法
重設記憶體區域的標記,以便透過現有標記指標的後續存取作業失敗:
__hwasan_tag_memory(__hwasan_tag_pointer(ptr, 0), 0, size);
使用預先配置的物件集區
如果記憶體配置器在集區中預先配置物件,並將物件傳回至集區,而非實際釋放物件,則釋放方法可以直接使用新值覆寫記憶體和指標的標記:
```
__hwasan_tag_memory(__hwasan_tag_pointer(ptr, 0), tag, size);
ptr = __hwasan_tag_pointer((void*)ptr, tag);
```
如果您使用這種技術,配置方法就不需要標記指標或記憶體區塊,而是在您在集區中預先配置物件時,標記指標和記憶體區塊。如需使用此樣式的範例,請參閱 PoolAllocator 範例。