Recupera y sube perfiles

Ya sea que captures un perfil controlado por la app o un perfil basado en activadores, ProfilingManager guarda el archivo completado en el dispositivo y entrega un ProfilingResult a tu devolución de llamada registrada. Llama a ProfilingResult.getResultFilePath para obtener la ruta de acceso del perfil guardado:

Cómo recuperar perfiles de un dispositivo

En esta sección, se describe dónde almacena ProfilingManager los perfiles en el dispositivo, cómo funciona la limpieza automática de archivos y cómo extraer archivos de forma local con adb.

Ubicaciones de archivos y formatos de nombres

ProfilingManager guarda los perfiles ocultados en el directorio de almacenamiento específico de la app en /data/user/0/<app>/files/profiling/. El formato del nombre de archivo depende de cómo se capturó el perfil:

  • Perfiles controlados por apps:

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

    Si no se proporcionó ninguna etiqueta cuando se solicitó el perfil, se omite <tag>_: profile_<datetime>_uid-<uid>.<profile-type-ext>.

  • Perfiles basados en activadores:

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

En la que:

  • <app> es el nombre del paquete de tu app.
  • <tag> son los primeros 20 caracteres alfanuméricos (y guiones) de la etiqueta proporcionada por el emisor configurada con setTag o pasada a requestProfiling, convertida a minúsculas con cualquier carácter fuera de a-z, 0-9 y - quitados.
  • <trigger_type> es el código de constante de número entero para el activador de ProfilingTrigger. Siempre llama a ProfilingResult.getTriggerType en el código en lugar de analizar el nombre del archivo.
  • <datetime> es la fecha y la hora en que se registró el perfil (yyyy-MM-dd-HH-mm-ss-SSS).
  • <uid> es el ID de usuario de Linux de tu app (incluido en las versiones recientes del módulo Profiling).
  • <profile-type-ext> es la extensión de archivo para el tipo de perfil capturado:
    • .perfetto-trace para los registros del sistema
    • .perfetto-java-heap-dump para volcados de montón de Java
    • .perfetto-heap-profile para perfiles del montón
    • .perfetto-stack-sample para perfiles de muestreo de pila

Almacenamiento y limpieza en el dispositivo

Para liberar almacenamiento del dispositivo de inmediato, borra cada archivo de perfil en tu trabajador de carga inmediatamente después de una carga exitosa (debido a que la limpieza automática solo se ejecuta cuando ProfilingManager entrega resultados a tu app, los archivos no subidos pueden permanecer en el disco si tu app deja de registrar devoluciones de llamada después de finalizar una campaña). Como resguardo en las versiones recientes del módulo Profiling, ProfilingManager también borra automáticamente los archivos entregados en el directorio de almacenamiento específico de la app que tengan más de 5 días de antigüedad cuando se entregan resultados nuevos.

Sube perfiles en producción con WorkManager

Debido a que los archivos de perfil pueden tener un tamaño de varios megabytes, programa las cargas de producción en segundo plano cuando el dispositivo esté en una red no medida (Wi-Fi), se esté cargando y esté inactivo.

Antes de subirlo, comprime el archivo de perfil (por ejemplo, con GZIPOutputStream o ZipOutputStream). Los perfiles de protobuf de Perfetto y los volcados de montón de Java se comprimen de forma significativa, lo que reduce el ancho de banda de la red y los costos de almacenamiento del backend.

Cómo agregar dependencias de WorkManager

ProfilingManager es independiente de la nube, por lo que puedes programar la carga de perfiles en cualquier backend con WorkManager. Agrega la biblioteca WorkManager a tu archivo build.gradle.kts:

Kotlin

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

Groovy

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

Cómo programar un trabajo de carga

Cuando tu app reciba un ProfilingResult, programa el trabajo de carga. En el siguiente ejemplo, se omite la solicitud de generación de perfiles inicial. Para ver un ejemplo de solicitud completo, consulta Cómo registrar un seguimiento 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());
}

Explicación del código

En el ejemplo anterior, se realizan los siguientes pasos:

  • Define el trabajador: Crea una clase TraceUploadWorker que extienda Worker. Implementa doWork para verificar que el archivo de perfil aún exista, comprimirlo y subirlo con tu SDK de backend o cliente HTTP, y borrar el archivo local una vez que se complete la carga.
  • Programa el trabajo:
    • Crea un OneTimeWorkRequest para tu trabajador.
    • Establece restricciones: Usa setRequiredNetworkType(NetworkType.UNMETERED), setRequiresDeviceIdle(true) y setRequiresCharging(true) para que la carga solo se ejecute cuando el dispositivo esté conectado a Wi-Fi, cargándose y en estado inactivo.
    • Pasa datos: Usa setInputData para pasar la ruta de acceso del perfil de getResultFilePath al trabajador.
    • Enqueue: Envía la solicitud a WorkManager llamando a WorkManager.enqueue.

Próximos pasos

Después de recuperar o subir perfiles, visualízalos en la IU de Perfetto, consulta perfiles individuales con PerfettoSQL, analiza seguimientos de forma masiva o analiza perfiles con IA. Para obtener orientación sobre cómo configurar una canalización de análisis escalable del lado del servidor, consulta Implementa Bigtrace en Kubernetes.