プロファイルを取得してアップロードする

アプリ主導のプロファイルとトリガーベースのプロファイルのどちらをキャプチャする場合でも、ProfilingManager は完成したファイルをデバイスに保存し、登録済みのコールバックに ProfilingResult を配信します。ProfilingResult.getResultFilePath を呼び出して、保存されたプロファイルのパスを取得します。

デバイスからプロファイルを取得する

このセクションでは、ProfilingManager がデバイス上のプロファイルを保存する場所、自動ファイル クリーンアップの仕組み、adb を使用してファイルをローカルに pull する方法について説明します。

ファイルの場所と命名形式

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: システム トレース
    • Java ヒープダンプの場合は .perfetto-java-heap-dump
    • .perfetto-heap-profile(ヒープ プロファイルの場合)
    • .perfetto-stack-sample: スタック サンプリング プロファイル

オンデバイスのストレージとクリーンアップ

デバイスのストレージをすぐに解放するには、アップロードが成功した直後に、アップロード ワーカー内の各プロファイル ファイルを削除します(自動クリーンアップは、ProfilingManager がアプリに結果を配信するときにのみ実行されるため、キャンペーンの終了後にアプリがコールバックの登録を停止すると、アップロードされていないファイルがディスクに残る可能性があります)。最近の Profiling モジュール バージョンのフォールバックとして、ProfilingManager は、新しい結果が配信されると、アプリ固有のストレージ ディレクトリにある 5 日以上経過した配信済みファイルも自動的に削除します。

WorkManager を使用して本番環境でプロファイルをアップロードする

プロファイル ファイルのサイズは数 MB になることがあるため、デバイスが定額制ネットワーク(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 クライアントを使用して圧縮してアップロードし、アップロードが成功したらローカル ファイルを削除します。
  • 作業のスケジュールを設定する:
    • ワーカーの OneTimeWorkRequest を作成します。
    • 制約を設定する: setRequiredNetworkType(NetworkType.UNMETERED)、setRequiresDeviceIdle(true)、setRequiresCharging(true) を使用して、デバイスが Wi-Fi に接続され、充電中で、アイドル状態のときにのみアップロードが実行されるようにします。
    • データを渡す: setInputData を使用して、getResultFilePath からワーカーにプロファイル パスを渡します。
    • エンキュー: WorkManager.enqueue を呼び出して、リクエストを WorkManager に送信します。

次のステップ

プロファイルを取得またはアップロードしたら、Perfetto UI で可視化したり、PerfettoSQL で個々のプロファイルをクエリしたり、トレースを一括分析したり、AI でプロファイルを分析したりできます。スケーラブルなサーバーサイド分析パイプラインの設定方法については、Kubernetes への Bigtrace のデプロイをご覧ください。