ART TI

在 Android 8.0 以上版本中,ART 工具介面 (ART TI) 會公開特定的執行階段內部資訊,並讓剖析器和偵錯工具影響應用程式的執行階段行為。這可用於實作最新的效能工具,這些工具可在其他平台上實作原生代理程式。

執行階段內部會向已載入至執行階段程序的代理程式公開。這些類別會透過直接呼叫和回呼與 ART 通訊。這個執行階段支援多個代理程式,因此可分離不同的正交剖析問題。代理程式可在執行階段開始時 (呼叫 dalvikvmapp_process) 提供,也可以附加至已執行的程序。

由於檢測及修改應用程式和執行階段行為的功能非常強大,因此 ART TI 已整合兩項安全措施:

  • 首先,公開代理程式介面的程式碼 (JVMTI) 會實作為執行階段外掛程式,而非執行階段的核心元件。外掛程式載入作業可能會受到限制,因此代理程式可能會遭到封鎖,無法找到任何介面點。
  • 其次,ActivityManager 類別和執行階段程序都只允許代理程式附加可偵錯的應用程式。可偵錯的應用程式已由開發人員簽署,可供分析和檢測,但不會發布給最終使用者。Google Play 商店不允許發布可偵錯的應用程式。這可確保一般應用程式 (包括核心元件) 無法進行檢測或操控。

設計

圖 1 顯示檢測應用程式中的一般流程和連結。

檢測應用程式中的流程和互連
圖 1. 檢測應用程式的流程和相互連結

ART 外掛程式 libopenjdkjvmti 會公開 ART TI,這是為了滿足平台的需求和限制而設計:

  • 類別重新定義功能是以 Dex 檔案為基礎,只包含單一類別定義,而非類別檔案。
  • 不會公開用於檢測和重新定義的 Java 語言 API。

ART TI 也支援 Android Studio 分析器。

載入或附加代理程式

如要在執行階段啟動時附加代理程式,請使用以下指令載入 JVMTI 外掛程式和指定的代理程式:

dalvikvm -Xplugin:libopenjdkjvmti.so -agentpath:/path/to/agent/libagent.so …

在執行階段啟動時載入的代理程式沒有安全措施,因此請注意,手動啟動的執行階段允許完全修改,但沒有安全措施。(這可進行 ART 測試)。

注意:這項規定不適用於裝置上的一般應用程式 (包括系統伺服器)。應用程式會從已執行的 zygote 中分支,且 zygote 程序不允許載入代理程式。

如要將代理程式附加至已執行的應用程式,請使用以下指令:

adb shell cmd activity attach-agent [process]
/path/to/agent/libagent.so[=agent-options]

如果尚未載入 JVMTI 外掛程式,附加代理程式會同時載入外掛程式和代理程式程式庫。

代理程式只能附加至標示為可偵錯的執行中應用程式 (應用程式資訊清單的一部分,應用程式節點上的屬性 android:debuggable 設為 true)。ActivityManager 類別和 ART 都會先執行檢查,再允許附加代理程式。ActivityManager 類別會檢查目前的應用程式資訊 (源自 PackageManager 類別資料),以便判斷是否可進行偵錯,而執行階段會檢查目前的狀態,也就是在啟動應用程式時設定的狀態。

服務專員位置

執行階段需要將代理程式載入至目前的程序,以便代理程式可直接繫結至程序並與之通訊。ART 本身不瞭解代理程式來自何處。字串用於 dlopen 呼叫。檔案系統權限和 SELinux 政策會限制實際載入作業。

如要提供可由可偵錯應用程式執行的代理程式,請執行下列操作:

  • 將代理程式嵌入應用程式 APK 的程式庫目錄中。
  • 使用 run-as 將代理程式複製到應用程式的資料目錄中。

API

已將下列方法新增至 android.os.Debug

/**
     * Attach a library as a jvmti agent to the current runtime, with the given classloader
     * determining the library search path.
     * Note: agents may only be attached to debuggable apps. Otherwise, this function will
     * throw a SecurityException.
     *
     * @param library the library containing the agent.
     * @param options the options passed to the agent.
     * @param classLoader the classloader determining the library search path.
     *
     * @throws IOException if the agent could not be attached.
     * @throws a SecurityException if the app is not debuggable.
     */
    public static void attachJvmtiAgent(@NonNull String library, @Nullable String options,
            @Nullable ClassLoader classLoader) throws IOException {

其他 Android API

attach-agent 指令會公開顯示。這個指令會將 JVMTI 代理程式附加至執行中的程序:

adb shell 'am attach-agent com.example.android.displayingbitmaps
\'/data/data/com.example.android.displayingbitmaps/code_cache/libfieldnulls.so=Ljava/lang/Class;.name:Ljava/lang/String;\''

am start -Pam start-profiler/stop-profiler 指令與 attach-agent 指令類似。

JVMTI

此功能會將 JVMTI API 公開給代理程式 (原生程式碼)。重要的功能包括:

  • 重新定義類別。
  • 追蹤物件配置和垃圾收集。
  • 依循物件的參照樹狀結構,逐一處理堆疊中的所有物件。
  • 檢查 Java 呼叫堆疊。
  • 暫停 (和恢復) 所有執行緒。

不同版本的 Android 可能提供不同的功能。

相容性

這項功能需要核心執行階段支援,但這項支援僅適用於 Android 8.0 以上版本。裝置製造商不需要進行任何變更即可實作這項功能。這是 AOSP 的一部分。

驗證

CTS 會在 Android 8 以上版本測試以下項目:

  • 測試代理程式是否已連結至可偵錯的應用程式,以及是否無法連結至無法偵錯的應用程式。
  • 測試所有已實作的 JVMTI API
  • 測試代理程式二進位介面是否穩定

我們已在 Android 9 以上版本中新增其他測試,並納入這些版本的 CTS 測試。