Recupera e carica i profili

Indipendentemente dal fatto che tu acquisisca un profilo basato sull'app o un profilo basato su trigger, ProfilingManager salva il file completato sul dispositivo e invia un ProfilingResult al tuo callback registrato. Chiama ProfilingResult.getResultFilePath per ottenere il percorso del profilo salvato:

Recupera i profili da un dispositivo

Questa sezione descrive dove ProfilingManager archivia i profili sul dispositivo, come funziona la pulizia automatica dei file e come estrarre i file localmente utilizzando adb.

Posizioni dei file e formati di denominazione

ProfilingManager salva i profili oscurati nella directory di archiviazione specifica dell'app in /data/user/0/<app>/files/profiling/. Il formato del nome file dipende da come è stato acquisito il profilo:

  • Profili basati su app:

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

    Se non è stato fornito alcun tag durante la richiesta del profilo, <tag>_ viene omesso: profile_<datetime>_uid-<uid>.<profile-type-ext>.

  • Profili basati su trigger:

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

Dove:

  • <app> è il nome del pacchetto della tua app.
  • <tag> è composto dai primi 20 caratteri alfanumerici (e trattini) del tag fornito dal chiamante configurato con setTag o passato a requestProfiling, convertito in minuscolo con la rimozione di tutti i caratteri al di fuori di a-z, 0-9 e -.
  • <trigger_type> è il codice della costante intera per l'attivatore di ProfilingTrigger. Chiama sempre ProfilingResult.getTriggerType nel codice anziché analizzare il nome file.
  • <datetime> è la data e l'ora in cui è stato registrato il profilo (yyyy-MM-dd-HH-mm-ss-SSS).
  • <uid> è l'ID utente Linux della tua app (incluso nelle versioni recenti del modulo Profiling).
  • <profile-type-ext> è l'estensione del file per il tipo di profilo acquisito:
    • .perfetto-trace per i tracciamenti del sistema
    • .perfetto-java-heap-dump per i dump dell'heap Java
    • .perfetto-heap-profile per i profili heap
    • .perfetto-stack-sample per i profili di campionamento dello stack

Spazio di archiviazione on-device e pulizia

Per liberare rapidamente lo spazio sul dispositivo, elimina ogni file del profilo nel worker di caricamento immediatamente dopo un caricamento riuscito (poiché la pulizia automatica viene eseguita solo quando ProfilingManager fornisce i risultati alla tua app, i file non caricati possono altrimenti rimanere sul disco se l'app interrompe la registrazione dei callback dopo la fine di una campagna). Come opzione di riserva nelle versioni recenti del modulo Profiling, ProfilingManager elimina automaticamente anche i file recapitati nella directory di archiviazione specifica dell'app che risalgono a più di 5 giorni prima quando vengono recapitati nuovi risultati.

Carica i profili in produzione con WorkManager

Poiché i file dei profili possono avere dimensioni di diversi megabyte, pianifica i caricamenti di produzione in background quando il dispositivo è connesso a una rete non a consumo (Wi-Fi), è in carica e non è in uso.

Prima di caricarlo, comprimi il file del profilo (ad esempio, utilizzando GZIPOutputStream o ZipOutputStream). I profili protobuf Perfetto e i dump dell'heap Java si comprimono in modo significativo, riducendo la larghezza di banda della rete e i costi di archiviazione del backend.

Aggiungi le dipendenze di WorkManager

ProfilingManager è indipendente dal cloud, quindi puoi pianificare il caricamento dei profili su qualsiasi backend utilizzando WorkManager. Aggiungi la libreria WorkManager al file build.gradle.kts:

Kotlin

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

Alla moda

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

Pianifica un job di caricamento

Quando la tua app riceve un ProfilingResult, pianifica il job di caricamento. L'esempio seguente omette la richiesta di profilazione iniziale. Per un esempio di richiesta completo, consulta Registra un tracciamento del sistema.

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());
}

Procedura dettagliata del codice

L'esempio precedente esegue i seguenti passaggi:

  • Definisci il worker: crea una classe TraceUploadWorker che estende Worker. Implementa doWork per verificare che il file del profilo esista ancora, comprimilo e caricalo utilizzando l'SDK di backend o il client HTTP ed elimina il file locale una volta completato il caricamento.
  • Pianifica il lavoro:
    • Crea un OneTimeWorkRequest per il tuo worker.
    • Imposta i vincoli: utilizza setRequiredNetworkType(NetworkType.UNMETERED), setRequiresDeviceIdle(true) e setRequiresCharging(true) in modo che il caricamento venga eseguito solo quando il dispositivo è connesso al Wi-Fi, in carica e inattivo.
    • Passa i dati: utilizza setInputData per passare il percorso del profilo da getResultFilePath al worker.
    • Enqueue: invia la richiesta a WorkManager chiamando WorkManager.enqueue.

Passaggi successivi

Dopo aver recuperato o caricato i profili, visualizzali nell'interfaccia utente di Perfetto, esegui query sui singoli profili con PerfettoSQL, analizza le tracce in blocco o analizza i profili con l'AI. Per indicazioni sulla configurazione di una pipeline di analisi lato server scalabile, consulta Deployment di Bigtrace su Kubernetes.