在透過原生程式碼對應用程式進行偵錯及分析時,使用偵錯工具通常很有幫助,但您需要在程序啟動時啟用這些工具。這就需要您在新程序中執行應用程式,而非從 zygote 複製。例如:
- 使用 strace 追蹤系統呼叫。
- 使用 malloc debug 或 Address Sanitizer (ASan) 找出記憶體錯誤。
- 使用 Simpleperf 進行分析。
使用包裝殼層指令碼
wrap.sh
使用起來很簡單:
- 編譯可進行偵錯的自訂 APK,其中包含以下內容:
- 一個名為
wrap.sh
的殼層指令碼。詳情請參閱建立包裝殼層指令碼和套件 wrap.sh。 - 殼層指令碼所需的其他任何工具 (例如您自己的
strace
二進位檔)。
- 一個名為
- 在裝置上安裝可進行偵錯的 APK。
- 啟動應用程式。
建立包裝殼層指令碼
當您啟動包含 wrap.sh
的可進行偵錯 APK 時,系統會執行該指令碼,並傳遞做為引數的啟動應用程式指令。這個指令碼負責啟動應用程式,但也可以變更任何環境或引數。此外,這個指令碼應遵循 MirBSD Korn shell (mksh) 語法。
下列程式碼片段示範如何編寫僅用於啟動應用程式的簡單 wrap.sh
檔案:
#!/system/bin/sh exec "$@"
Malloc debug
如要透過 wrap.sh
使用 malloc debug,請加入以下這行:
#!/system/bin/sh LIBC_DEBUG_MALLOC_OPTIONS=backtrace logwrapper "$@"
ASan
請參閱 ASan 說明文件中的範例,瞭解如何為 ASan 執行這項操作。
wrap.sh 套件
為充分運用 wrap.sh
,您的 APK 必須可進行偵錯。確認 Android 資訊清單中的 <application>
元素已設定 android:debuggable="true"
;或者,如果您使用的是 Android Studio,請確認已在 build.gradle
檔案中設定偵錯版本。
此外,您必須在應用程式的 build.gradle
檔案中將 useLegacyPackaging
設為 true
。在大多數情況下,這個選項會預設為 false
,因此建議您將這項設定明確設為 true
,以避免任何意外發生。
您必須將 wrap.sh
指令碼與應用程式的原生資料庫封裝在一起。如果您的應用程式不包含原生資料庫,請手動將 lib 目錄新增至專案目錄。針對您的應用程式支援的每個架構,您必須在該原生資料庫目錄下提供包裝殼層指令碼的副本。
以下範例展示同時支援 ARMv8 和 x86-64 架構的檔案版面配置:
# App Directory |- AndroidManifest.xml |- … |- lib |- arm64-v8a |- ... |- wrap.sh |- x86_64 |- ... |- wrap.sh
Android Studio 僅封裝 lib/
目錄中的 .so
檔案,因此如果您使用 Android Studio,便需要將 wrap.sh
檔案置於 src/main/resources/lib/*
目錄中,藉此確保檔案可正確封裝。
請注意,resources/lib/x86
在 UI 中會顯示為 lib.x86
,但它實際上應該是子目錄:
在使用 wrap.sh 時進行偵錯
如要在使用 wrap.sh
時附加偵錯工具,您必須為殼層指令碼手動啟用偵錯功能。執行這項操作的方式因版本而異,因此以下範例示範如何為所有支援 wrap.sh
的版本新增適當的選項:
#!/system/bin/sh
cmd=$1
shift
os_version=$(getprop ro.build.version.sdk)
if [ "$os_version" -eq "27" ]; then
cmd="$cmd -Xrunjdwp:transport=dt_android_adb,suspend=n,server=y -Xcompiler-option --debuggable $@"
elif [ "$os_version" -eq "28" ]; then
cmd="$cmd -XjdwpProvider:adbconnection -XjdwpOptions:suspend=n,server=y -Xcompiler-option --debuggable $@"
else
cmd="$cmd -XjdwpProvider:adbconnection -XjdwpOptions:suspend=n,server=y $@"
fi
exec $cmd