Récupérer et importer des profils

Que vous capturiez un profil basé sur l'application ou un profil basé sur un déclencheur, ProfilingManager enregistre le fichier terminé sur l'appareil et envoie un ProfilingResult à votre rappel enregistré. Appelez ProfilingResult.getResultFilePath pour obtenir le chemin d'accès au profil enregistré :

Récupérer des profils depuis un appareil

Cette section décrit l'emplacement où ProfilingManager stocke les profils sur l'appareil, le fonctionnement du nettoyage automatique des fichiers et la manière d'extraire les fichiers localement à l'aide de adb.

Emplacements des fichiers et formats de dénomination

ProfilingManager enregistre les profils masqués dans votre répertoire de stockage spécifique à l'application à l'emplacement /data/user/0/<app>/files/profiling/. Le format du nom de fichier dépend de la manière dont le profil a été capturé :

  • Profils basés sur l'application :

    profile_<tag>_<datetime>_uid-<uid>.<profile-type-ext>
    

    Si aucun tag n'a été fourni lors de la demande du profil, <tag>_ est omis : profile_<datetime>_uid-<uid>.<profile-type-ext>.

  • Profils basés sur des déclencheurs :

    profile_trigger-type-<trigger_type>_<datetime>_uid-<uid>.<profile-type-ext>
    

Où :

  • <app> est le nom du package de votre application.
  • <tag> correspond aux 20 premiers caractères alphanumériques (et traits d'union) du tag fourni par l'appelant, configuré avec setTag ou transmis à requestProfiling, converti en minuscules, avec tous les caractères en dehors de a-z, 0-9 et - supprimés.
  • <trigger_type> est le code de constante entière du déclencheur de ProfilingTrigger. Appelez toujours ProfilingResult.getTriggerType dans le code au lieu d'analyser le nom du fichier.
  • <datetime> correspond à la date et à l'heure d'enregistrement du profil (yyyy-MM-dd-HH-mm-ss-SSS).
  • <uid> est l'ID utilisateur Linux de votre application (inclus dans les versions récentes du module Profiling).
  • <profile-type-ext> est l'extension de fichier du type de profil capturé :
    • .perfetto-trace pour les traces système
    • .perfetto-java-heap-dump pour les empreintes de la mémoire Java
    • .perfetto-heap-profile pour les profils de tas
    • .perfetto-stack-sample pour les profils d'échantillonnage de pile

Stockage et nettoyage sur l'appareil

Pour libérer rapidement de l'espace de stockage sur l'appareil, supprimez chaque fichier de profil de votre worker d'importation immédiatement après une importation réussie (car le nettoyage automatique ne s'exécute que lorsque ProfilingManager fournit des résultats à votre application, les fichiers non importés peuvent rester sur le disque si votre application cesse d'enregistrer des rappels après la fin d'une campagne). En tant que solution de secours sur les versions récentes du module Profiling, ProfilingManager supprime également automatiquement les fichiers livrés dans le répertoire de stockage spécifique à votre application qui datent de plus de cinq jours lorsque de nouveaux résultats sont livrés.

Importer des profils en production avec WorkManager

Étant donné que les fichiers de profil peuvent faire plusieurs mégaoctets, planifiez les importations de production en arrière-plan lorsque l'appareil est connecté à un réseau sans compteur (Wi-Fi), qu'il est en charge et qu'il est inactif.

Avant d'importer, compressez le fichier de profil (par exemple, à l'aide de GZIPOutputStream ou ZipOutputStream). Les profils protobuf Perfetto et les empreintes de la mémoire Java sont compressés de manière significative, ce qui réduit la bande passante réseau et les coûts de stockage du backend.

Ajouter des dépendances WorkManager

ProfilingManager est indépendant du cloud. Vous pouvez donc planifier l'importation de profils vers n'importe quel backend à l'aide de WorkManager. Ajoutez la bibliothèque WorkManager à votre fichier build.gradle.kts :

Kotlin

   dependencies {
       implementation("androidx.work:work-runtime:2.12.0")
   }
   

Groovy

   dependencies {
       implementation 'androidx.work:work-runtime:2.12.0'
   }
   

Planifier un job d'importation

Lorsque votre application reçoit un ProfilingResult, planifiez le job d'importation. L'exemple suivant omet la requête de profilage initiale. Pour obtenir un exemple de requête complet, consultez Enregistrer une trace système.

Kotlin

class TraceUploadWorker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) {
    override fun doWork(): Result {
        // Perform your uploading work here
        Log.d("ProfileTest", "Uploading trace: " + inputData.getString("PROFILE_PATH"))

        return Result.success()
    }
}

fun setupProfileUploadWorker(profileFilepath: String?) {
    val workMgr = WorkManager.getInstance(applicationContext)
    val workRequestBuilder = OneTimeWorkRequest.Builder(TraceUploadWorker::class)

    val constraints = Constraints.Builder()
        .setRequiredNetworkType(NetworkType.UNMETERED)
        .setRequiresDeviceIdle(true)
        .setRequiresCharging(true)
        .build()
    workRequestBuilder.setConstraints(constraints)

    val inputDataBuilder = Data.Builder()
    inputDataBuilder.putString("PROFILE_PATH", profileFilepath)
    workRequestBuilder.setInputData(inputDataBuilder.build())

    workMgr.enqueue(workRequestBuilder.build())
}

Java

public static class TraceUploadWorker extends Worker {

  public TraceUploadWorker(
      @androidx.annotation.NonNull Context context,
      @androidx.annotation.NonNull WorkerParameters workerParams) {
    super(context, workerParams);
  }

  @androidx.annotation.NonNull
  @Override
  public Result doWork() {
    // Perform your uploading work here
    Log.d("ProfileTest", "Uploading trace: " + getInputData().getString("PROFILE_PATH"));

    return Result.success();
  }
}

public void setupProfileUploadWorker(String profileFilepath) {
  WorkManager workMgr = WorkManager.getInstance(getApplicationContext());
  OneTimeWorkRequest.Builder workRequestBuilder = new OneTimeWorkRequest.Builder(
      TraceUploadWorker.class);

  Constraints constraints = new Constraints.Builder()
      .setRequiredNetworkType(NetworkType.UNMETERED)
      .setRequiresDeviceIdle(true)
      .setRequiresCharging(true)
      .build();
  workRequestBuilder.setConstraints(constraints);

  Data.Builder inputDataBuilder = new Data.Builder();
  inputDataBuilder.putString("PROFILE_PATH", profileFilepath);
  workRequestBuilder.setInputData(inputDataBuilder.build());

  workMgr.enqueue(workRequestBuilder.build());
}

Tutoriel du code

L'exemple précédent effectue les étapes suivantes :

  • Définir le nœud de calcul : créez une classe TraceUploadWorker qui étend Worker. Implémentez doWork pour vérifier que le fichier de profil existe toujours, compressez-le et importez-le à l'aide de votre SDK de backend ou de votre client HTTP, puis supprimez le fichier local une fois l'importation réussie.
  • Planifier le travail :
    • Créez un OneTimeWorkRequest pour votre nœud de calcul.
    • Définir des contraintes : utilisez setRequiredNetworkType(NetworkType.UNMETERED), setRequiresDeviceIdle(true) et setRequiresCharging(true) pour que l'importation ne s'exécute que lorsque l'appareil est connecté au Wi-Fi, en charge et inactif.
    • Transmettre des données : utilisez setInputData pour transmettre le chemin d'accès au profil de getResultFilePath au nœud de calcul.
    • Enqueue : envoyez la requête à WorkManager en appelant WorkManager.enqueue.

Étapes suivantes

Après avoir récupéré ou importé des profils, visualisez-les dans l'UI Perfetto, interrogez des profils individuels avec PerfettoSQL, analysez des traces de manière groupée ou analysez des profils avec l'IA. Pour savoir comment configurer un pipeline d'analyse côté serveur évolutif, consultez Déployer Bigtrace sur Kubernetes.