프로필 검색 및 업로드

앱 기반 프로필을 캡처하든 트리거 기반 프로필을 캡처하든 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
    • Java 힙 덤프의 경우 .perfetto-java-heap-dump
    • 힙 프로필의 경우 .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())
}

자바

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를 만듭니다.
    • 제약 조건 설정: 기기가 Wi-Fi에 연결되어 있고 충전 중이며 유휴 상태일 때만 업로드가 실행되도록 setRequiredNetworkType(NetworkType.UNMETERED), setRequiresDeviceIdle(true), setRequiresCharging(true)를 사용합니다.
    • 데이터 전달: setInputData를 사용하여 getResultFilePath에서 작업자로 프로필 경로를 전달합니다.
    • 큐에 추가: WorkManager.enqueue를 호출하여 WorkManager에 요청을 제출합니다.

다음 단계

프로필을 가져오거나 업로드한 후 Perfetto UI에서 시각화하거나, PerfettoSQL로 개별 프로필을 쿼리하거나, 트레이스를 일괄 분석하거나, AI로 프로필을 분석합니다. 확장 가능한 서버 측 분석 파이프라인을 설정하는 방법에 대한 안내는 Kubernetes에 Bigtrace 배포를 참조하세요.