检索和上传配置文件

无论您捕获的是应用驱动的配置文件还是基于触发器的配置文件,ProfilingManager 都会将完成的文件保存在设备上,并向您注册的回调传送 ProfilingResult。调用 ProfilingResult.getResultFilePath 以获取已保存配置文件的路径:

从设备中检索配置文件

本部分介绍了 ProfilingManager 在设备上存储配置文件的位置、自动文件清理的工作原理,以及如何使用 adb 在本地拉取文件。

文件位置和命名格式

ProfilingManager 会将经过隐去处理的配置文件保存在 /data/user/0/<app>/files/profiling/ 的应用专属存储目录中。文件名格式取决于配置文件的捕获方式:

  • 应用驱动的配置文件:

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

    如果在请求配置文件时未提供标记,则会省略 <tag>_:profile_<datetime>_uid-<uid>.<profile-type-ext>。

  • 基于触发器的配置文件:

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

其中:

  • <app> 是应用的软件包名称。
  • <tag> 是使用 setTag 配置或传递给 requestProfiling 的调用方提供的标记的前 20 个字母数字字符(和连字符),转换为小写,并移除了 a-z、0-9 和 - 之外的所有字符。
  • <trigger_type> 是 ProfilingTrigger 中触发器的整数常量代码。始终在代码中调用 ProfilingResult.getTriggerType,而不是解析文件名。
  • <datetime> 是记录性能剖析文件的日期和时间 (yyyy-MM-dd-HH-mm-ss-SSS)。
  • <uid> 是应用的 Linux 用户 ID(包含在最近的 Profiling 模块版本中)。
  • <profile-type-ext> 是所捕获性能剖析类型的文件扩展名:
    • .perfetto-trace(适用于系统跟踪)
    • .perfetto-java-heap-dump,适用于 Java 堆转储
    • .perfetto-heap-profile(针对堆分析报告)
    • .perfetto-stack-sample,适用于堆栈采样配置文件

设备端存储和清理

为了及时释放设备存储空间,请在每次成功上传后立即删除上传工作器中的每个配置文件(因为自动清理仅在 ProfilingManager 向应用提供结果时运行,否则,如果应用在结束广告系列后停止注册回调,未上传的文件可能会保留在磁盘上)。作为近期 Profiling 模块版本的后备方案,ProfilingManager 也会在提供新结果时自动删除应用专用存储目录中超过 5 天的已提供文件。

在正式版中使用 WorkManager 上传配置文件

由于配置文件的大小可能达到几兆字节,因此请在设备处于不按流量计费的网络 (Wi-Fi) 中、正在充电且处于空闲状态时,在后台安排正式版上传。

在上传之前,请压缩配置文件(例如,使用 GZIPOutputStream 或 ZipOutputStream)。Perfetto protobuf 配置文件和 Java 堆转储会显著压缩,从而降低网络带宽和后端存储费用。

添加 WorkManager 依赖项

ProfilingManager 与云无关,因此您可以使用 WorkManager 安排将配置文件上传到任何后端。将 WorkManager 库添加到 build.gradle.kts 文件中:

Kotlin

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

Groovy

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

安排上传作业

当您的应用收到 ProfilingResult 时,请安排上传作业。以下示例省略了初始性能分析请求;如需查看完整的请求示例,请参阅记录系统跟踪记录。

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

代码演示

上述示例执行以下步骤:

  • 定义工作器:创建一个扩展 Worker 的 TraceUploadWorker 类。实现 doWork,以验证配置文件是否仍然存在,使用后端 SDK 或 HTTP 客户端对其进行压缩和上传,并在上传成功后删除本地文件。
  • 安排工作:
    • 为您的 worker 创建 OneTimeWorkRequest。
    • 设置限制:使用 setRequiredNetworkType(NetworkType.UNMETERED)、setRequiresDeviceIdle(true) 和 setRequiresCharging(true),以便仅在设备连接到 Wi-Fi、正在充电且处于空闲状态时才运行上传。
    • 传递数据:使用 setInputData 将配置文件路径从 getResultFilePath 传递给工作器。
    • 加入队列:通过调用 WorkManager.enqueue 将请求提交到 WorkManager。

后续步骤

检索或上传配置文件后,在 Perfetto 界面中直观呈现这些配置文件、使用 PerfettoSQL 查询单个配置文件、批量分析跟踪记录或使用 AI 分析配置文件。如需有关设置可扩缩的服务器端分析流水线的指导,请参阅在 Kubernetes 上部署 Bigtrace。