ART TI

Dans Android 8.0 et versions ultérieures, l'ART Tooling Interface (ART TI) expose certains éléments internes de l'exécution et permet aux profileurs et aux débogueurs d'influencer le comportement d'exécution des applications. Cela peut être utilisé pour implémenter des outils de performance de pointe fournis pour l'implémentation d'agents natifs sur d'autres plates-formes.

Les éléments internes du runtime sont exposés aux agents qui ont été chargés dans le processus d'exécution. Ceux-ci communiquent avec l’ART via des appels directs et des rappels. Le runtime prend en charge plusieurs agents afin que les différents problèmes de profilage orthogonal puissent être séparés. Les agents peuvent être soit fournis au démarrage de l'exécution (lorsque dalvikvm ou app_process sont invoqués), soit attachés à un processus déjà en cours d'exécution.

La capacité d'instrumenter et de modifier le comportement des applications et du runtime étant très puissante, deux mesures de sécurité ont été intégrées à l'ART TI :

  • Premièrement, le code exposant l'interface de l'agent, JVMTI, est implémenté en tant que plug-in d'exécution et non en tant que composant principal du runtime. Le chargement du plugin peut être restreint, de sorte que les agents puissent être empêchés de trouver l'un des points d'interface.
  • Deuxièmement, la classe ActivityManager et le processus d'exécution autorisent uniquement les agents à se connecter à des applications déboguables. Les applications débogables ont été approuvées par leurs développeurs pour être analysées et instrumentées, et ne sont pas distribuées aux utilisateurs finaux. Le Google Play Store n'autorise pas la distribution d'applications déboguables. Cela garantit que les applications normales (y compris les composants principaux) ne peuvent pas être instrumentées ou manipulées.

Conception

Le flux général et l'interconnexion dans une application instrumentée sont illustrés à la figure 1 .

Flow and interconnection in an instrumented app
Figure 1. Flux et interconnexion d'une application instrumentée

Le plugin ART libopenjdkjvmti expose ART TI, qui est conçu pour s'adapter aux besoins et aux contraintes de la plateforme :

  • La redéfinition des classes est basée sur des fichiers Dex , contenant une seule définition de classe, au lieu de fichiers de classe.
  • Les API du langage Java pour l'instrumentation et la redéfinition ne sont pas exposées.

L'ART TI prend également en charge les profileurs Android Studio.

Charger ou attacher un agent

Pour attacher un agent au démarrage de l'exécution, utilisez cette commande pour charger à la fois le plug-in JVMTI et l'agent donné :

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

Aucune mesure de sécurité n'est en place lorsqu'un agent est chargé au démarrage du runtime. Gardez donc à l'esprit qu'un runtime démarré manuellement permet une modification complète sans mesures de sécurité. (Cela permet les tests ART.)

Remarque : Ceci ne s'applique pas aux applications normales (y compris le serveur système) sur un appareil. Les applications sont dérivées d'un zygote déjà en cours d'exécution, et un processus zygote n'est pas autorisé à charger des agents.

Pour associer un agent à une application déjà en cours d'exécution, utilisez cette commande :

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

Si le plugin JVMTI n'a pas encore été chargé, l'attachement d'un agent charge à la fois le plugin et la bibliothèque d'agent.

Un agent ne peut être attaché qu'à une application en cours d'exécution marquée comme déboguable (partie du manifeste de l'application, avec l'attribut android:debuggable défini sur true sur le nœud de l'application). La classe ActivityManager et l'ART effectuent des vérifications avant d'autoriser l'attachement d'un agent. La classe ActivityManager vérifie les informations de l'application actuelle (dérivées des données de la classe PackageManager ) pour l'état déboguable, et le runtime vérifie son état actuel, qui a été défini au démarrage de l'application.

Emplacements des agents

Le moteur d'exécution doit charger les agents dans le processus en cours, afin que l'agent puisse s'y lier directement et communiquer avec lui. L’ART lui-même est agnostique quant à l’endroit précis d’où vient l’agent. La chaîne est utilisée pour un appel dlopen . Les autorisations du système de fichiers et les politiques SELinux limitent le chargement réel.

Pour fournir des agents pouvant être exécutés par une application débogable, procédez comme suit :

  • Intégrez l'agent dans le répertoire de la bibliothèque de l'APK de l'application.
  • Utilisez run-as pour copier l'agent dans le répertoire de données de l'application.

Apis

La méthode suivante a été ajoutée à 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 {

Autres API Android

La commande attach-agent est visible publiquement. Cette commande attache un agent JVMTI à un processus en cours d'exécution :

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

Les commandes am start -P et am start-profiler/stop-profiler sont similaires à la commande attach-agent.

JVMTI

Cette fonctionnalité expose l'API JVMTI aux agents (code natif). Les capacités importantes incluent :

  • Redéfinir une classe.
  • Suivi de l'allocation d'objets et du garbage collection.
  • Itérer sur tous les objets d'un tas, en suivant l'arborescence de référence des objets.
  • Inspection des piles d'appels Java.
  • Suspendre (et reprendre) tous les threads.

Différentes fonctionnalités peuvent être disponibles sur différentes versions d'Android.

Compatibilité

Cette fonctionnalité nécessite une prise en charge du runtime de base qui n'est disponible que sur Android 8.0 et versions ultérieures. Les fabricants d'appareils n'ont pas besoin d'apporter de modifications pour implémenter cette fonctionnalité. Cela fait partie de l'AOSP.

Validation

CTS teste les éléments suivants sur Android 8 et versions ultérieures :

  • Teste que les agents s'attachent aux applications déboguables et ne parviennent pas à s'attacher aux applications non déboguables.
  • Teste toutes les API JVMTI implémentées
  • Teste que l'interface binaire pour les agents est stable

Des tests supplémentaires ont été ajoutés à Android 9 et versions ultérieures et sont inclus dans les tests CTS pour ces versions.