ARTE TI

In Android 8.0 e versioni successive, l'ART Tooling Interface (ART TI) espone alcuni elementi interni di runtime e consente ai profiler e ai debugger di influenzare il comportamento di runtime delle app. Questo può essere utilizzato per implementare strumenti di ottimizzazione all'avanguardia forniti per l'implementazione di agenti nativi su altre piattaforme.

Gli elementi interni del runtime sono esposti agli agenti che sono stati caricati nel processo di runtime. Questi componenti comunicano con l'ART tramite chiamate dirette e chiamate di ritorno. Il runtime supporta più agenti in modo da poter separare diversi problemi di profilazione ortogonale. Gli agenti possono essere forniti all'avvio del runtime (quando vengono richiamati dalvikvm o app_process) o collegati a un processo già in esecuzione.

Poiché la possibilità di eseguire l'instrumentazione e modificare il comportamento dell'app e del runtime è molto potente, nell'ART TI sono state integrate due misure di sicurezza:

  • Innanzitutto, il codice che espone l'interfaccia dell'agente, JVMTI, è implementato come plug-in di runtime e non come componente di base del runtime. Il caricamento dei plug-in potrebbe essere limitato in modo che gli agenti non possano trovare i punti di interfaccia.
  • In secondo luogo, sia la classe ActivityManager sia il processo di runtime consentono agli agenti di collegarsi solo alle app di cui è possibile eseguire il debug. Le app di cui è possibile eseguire il debug sono state approvate dagli sviluppatori per essere analizzate e messe in primo piano e non vengono distribuite agli utenti finali. Il Google Play Store non consente la distribuzione di app di cui è possibile eseguire il debug. In questo modo, le app normali (inclusi i componenti principali) non possono essere instrumentate o manipolate.

Design

Il flusso generale e l'interconnessione in un'app sottoposta a misurazione sono mostrati nella Figura 1.

Flusso e interconnessione in un'app sottoposta a misurazione
Figura 1. Flusso e interconnessione di un'app instrumentata

Il plug-in ART libopenjdkjvmti espone l'ART TI, progettato per soddisfare le esigenze e i vincoli della piattaforma:

  • La ridefinizione della classe si basa su file Dex contenenti solo una singola definizione di classe, anziché su file di classe.
  • Le API in linguaggio Java per la strumentazione e la ridefinizione non sono esposte.

L'ART TI supporta anche i profiler di Android Studio.

Carica o allega un agente

Per collegare un agente all'avvio del runtime, utilizza questo comando per caricare sia il plug-in JVMTI sia l'agente specificato:

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

Non sono presenti misure di sicurezza quando un agente viene caricato all'avvio del runtime, quindi tieni presente che un runtime avviato manualmente consente la modifica completa senza misure di sicurezza. (Questo consente i test ART).

Nota: questo non si applica alle normali app (incluso il server di sistema) su un dispositivo. Le app vengono create da un zygote già in esecuzione e un processo zygote non è autorizzato a caricare agenti.

Per collegare un agente a un'app già in esecuzione, utilizza questo comando:

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

Se il plug-in JVMTI non è stato ancora caricato, l'attacco di un agente carica sia il plug-in sia la libreria dell'agente.

Un agente può essere collegato solo a un'app in esecuzione contrassegnata come debuggable (parte del file manifest dell'app, con l'attributo android:debuggable impostato su true sul nodo dell'app). Sia la classe ActivityManager sia l'ART eseguono controlli prima di consentire l'attacco di un agente. La classe ActivityManager controlla le informazioni attuali dell'app (derivate dai dati della classe PackageManager) per lo stato di debug e il runtime controlla il suo stato corrente, impostato all'avvio dell'app.

Sedi degli agenti

L'ambiente di runtime deve caricare gli agenti nel processo corrente in modo che l'agente possa associarsi direttamente e comunicare con esso. L'ART stesso è agnostico in merito alla posizione specifica da cui proviene l'agente. La stringa viene utilizzata per una chiamata dlopen. Le autorizzazioni del file system e i criteri SELinux limitano il caricamento effettivo.

Per caricare agenti che possono essere eseguiti da un'app di debug:

  • Incorpora l'agente nella directory della libreria dell'APK dell'app.
  • Utilizza run-as per copiare l'agente nella directory dei dati dell'app.

API

Il seguente metodo è stato aggiunto a 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 {

Altre API Android

Il comando attach-agent è visibile pubblicamente. Questo comando collega un agente JVMTI a un processo in esecuzione:

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;\''

I comandi am start -P e am start-profiler/stop-profiler sono simili al comando attach-agent.

JVMTI

Questa funzionalità espone l'API JVMTI agli agenti (codice nativo). Le funzionalità importanti includono:

  • Ridefinizione di un corso.
  • Monitoraggio dell'allocazione degli oggetti e della raccolta dei rifiuti.
  • Eseguire l'iterazione su tutti gli oggetti in un heap, seguendo l'albero di riferimento degli oggetti.
  • Ispezione degli stack di chiamate Java.
  • Sospensione (e ripresa) di tutti i thread.

Potrebbero essere disponibili funzionalità diverse su versioni diverse di Android.

Compatibilità

Questa funzionalità richiede il supporto di runtime di base disponibile solo su Android 8.0 e versioni successive. I produttori di dispositivi non devono apportare modifiche per implementare questa funzionalità. Fa parte di AOSP.

Convalida

CTS testa quanto segue su Android 8 e versioni successive:

  • Verifica che gli agenti si colleghino alle app di cui è possibile eseguire il debug e non riescano a collegarsi alle app non di cui è possibile eseguire il debug.
  • Testa tutte le API JVMTI implementate
  • Verifica che l'interfaccia binaria per gli agenti sia stabile

Ad Android 9 e versioni successive sono stati aggiunti test aggiuntivi, che sono inclusi nei test CTS per queste release.