ProfilingManager은 시스템 트리거를 기반으로 프로필 캡처를 지원합니다. 시스템은 기록 프로세스를 관리하고 결과 프로필을 앱에 제공합니다.
트리거는 성능이 중요한 이벤트에 연결됩니다. 시스템 기록 프로필은 이러한 트리거와 관련된 중요한 사용자 여정 (CUJ)에 관한 자세한 디버깅 정보를 제공합니다.
이전 데이터 캡처
많은 트리거에서 이벤트까지의 과거 데이터를 분석해야 합니다. 트리거 자체는 근본 원인이 아니라 문제의 결과인 경우가 많습니다. 트리거가 발생한 후에만 프로파일링을 시작하면 근본 원인이 이미 손실되었을 수 있습니다.
예를 들어 UI 스레드에서 장기 실행 작업을 실행하면 애플리케이션 응답 없음 (ANR) 오류가 발생합니다. 시스템이 ANR을 감지하고 앱에 신호를 보낼 때쯤이면 작업이 완료되었을 수 있습니다. 이 시점에서 프로필을 시작하면 실제 차단 작업이 누락됩니다.
일부 트리거가 발생하는 시점을 정확하게 예측할 수 없으므로 사전에 프로필을 수동으로 시작할 수 없습니다.
트리거 기반 캡처를 사용해야 하는 이유
프로파일링 트리거를 사용하는 주요 이유는 앱이 이러한 이벤트가 발생하기 전에 수동으로 녹화를 시작할 수 없는 예측 불가능한 이벤트의 데이터를 캡처하기 위해서입니다. 프로파일링 트리거는 다음 용도로 사용할 수 있습니다.
- 성능 문제 디버그: ANR, 메모리 누수, 기타 안정성 문제를 진단합니다.
- 중요한 사용자 여정 최적화: 앱 시작과 같은 흐름을 분석하고 개선합니다.
- 사용자 행동 이해: 사용자 시작 앱 종료와 같은 이벤트에 대한 통계를 얻습니다.
트리거 설정
다음 코드는 TRIGGER_TYPE_APP_FULLY_DRAWN 트리거를 등록하고 비율 제한을 적용하는 방법을 보여줍니다.
Kotlin
fun recordWithTrigger() { val profilingManager = applicationContext.getSystemService(ProfilingManager::class.java) val triggers = ArrayList<ProfilingTrigger>() val triggerBuilder = ProfilingTrigger.Builder(ProfilingTrigger.TRIGGER_TYPE_APP_FULLY_DRAWN) .setRateLimitingPeriodHours(1) triggers.add(triggerBuilder.build()) val mainExecutor: Executor = Executors.newSingleThreadExecutor() val resultCallback = Consumer<ProfilingResult> { profilingResult -> if (profilingResult.errorCode == ProfilingResult.ERROR_NONE) { Log.d( "ProfileTest", "Received profiling result file=" + profilingResult.resultFilePath ) setupProfileUploadWorker(profilingResult.resultFilePath) } else { Log.e( "ProfileTest", "Profiling failed errorcode=" + profilingResult.errorCode + " errormsg=" + profilingResult.errorMessage ) } } profilingManager.registerForAllProfilingResults(mainExecutor, resultCallback) profilingManager.addProfilingTriggers(triggers)
자바
public void recordWithTrigger() { ProfilingManager profilingManager = getApplicationContext().getSystemService( ProfilingManager.class); List<ProfilingTrigger> triggers = new ArrayList<>(); ProfilingTrigger.Builder triggerBuilder = new ProfilingTrigger.Builder( ProfilingTrigger.TRIGGER_TYPE_APP_FULLY_DRAWN); triggerBuilder.setRateLimitingPeriodHours(1); triggers.add(triggerBuilder.build()); Executor mainExecutor = Executors.newSingleThreadExecutor(); Consumer<ProfilingResult> resultCallback = new Consumer<ProfilingResult>() { @Override public void accept(ProfilingResult profilingResult) { if (profilingResult.getErrorCode() == ProfilingResult.ERROR_NONE) { Log.d( "ProfileTest", "Received profiling result file=" + profilingResult.getResultFilePath()); setupProfileUploadWorker(profilingResult.getResultFilePath()); } else { Log.e( "ProfileTest", "Profiling failed errorcode=" + profilingResult.getErrorCode() + " errormsg=" + profilingResult.getErrorMessage()); } } }; profilingManager.registerForAllProfilingResults(mainExecutor, resultCallback); profilingManager.addProfilingTriggers(triggers);
이 코드는 다음 단계를 실행합니다.
- 관리자 가져오기:
ProfilingManager서비스를 가져옵니다. - 트리거 정의:
TRIGGER_TYPE_APP_FULLY_DRAWN의ProfilingTrigger를 빌드합니다. 이 이벤트는 앱이 시작이 완료되었으며 상호작용이 가능하다고 보고할 때 발생합니다. - 비율 제한 설정: 이 특정 트리거(
setRateLimitingPeriodHours(1))에 1시간 비율 제한을 적용합니다. 이렇게 하면 앱이 시간당 두 개 이상의 시작 프로필을 기록하지 않습니다. - 리스너 등록:
registerForAllProfilingResults를 호출하여 결과를 처리하는 콜백을 정의합니다. 이 콜백은getResultFilePath()을 통해 저장된 프로필의 경로를 수신합니다. - 트리거 추가:
addProfilingTriggers를 사용하여ProfilingManager에 트리거 목록을 등록합니다. - 이벤트 발생: 시스템에
TRIGGER_TYPE_APP_FULLY_DRAWN이벤트를 내보내는reportFullyDrawn()를 호출하여 시스템 백그라운드 트레이스가 실행 중이고 비율 제한기 할당량이 있다고 가정하여 프로필 수집을 트리거합니다. 이 선택적 단계에서는 앱이 이 트리거에 대해reportFullyDrawn()를 호출해야 하므로 엔드 투 엔드 흐름을 보여줍니다.
트레이스 가져오기
시스템은 트리거 기반 프로필을 다른 프로필과 동일한 디렉터리에 저장합니다. 트리거된 트레이스의 파일 이름은 다음 형식을 따릅니다.
profile_trigger_<profile_type_code>_<datetime>.<profile-type-name>
ADB를 사용하여 파일을 가져올 수 있습니다. 예를 들어 ADB를 사용하여 예시 코드로 캡처한 시스템 트레이스를 가져오려면 다음과 같이 표시될 수 있습니다.
adb pull /data/user/0/com.example.sampleapp/files/profiling/profile_trigger_1_2025-05-06-14-12-40.perfetto-trace
이러한 trace 시각화에 관한 자세한 내용은 프로파일링 데이터 가져오기 및 분석을 참고하세요.
백그라운드 추적 작동 방식
트리거 전의 데이터를 캡처하기 위해 OS는 주기적으로 백그라운드 트레이스를 시작합니다. 이 백그라운드 트레이스가 활성 상태이고 앱이 등록된 동안 트리거가 발생하면 시스템은 트레이스 프로필을 앱의 디렉터리에 저장합니다. 그러면 프로필에 트리거를 유발한 정보가 포함됩니다.
프로필이 저장되면 시스템은 registerForAllProfilingResults에 제공된 콜백을 사용하여 앱에 알립니다. 이 콜백은 ProfilingResult#getResultFilePath()를 호출하여 액세스할 수 있는 캡처된 프로필의 경로를 제공합니다.
기기 성능과 배터리 수명에 미치는 영향을 줄이기 위해 시스템은 백그라운드 트레이스를 지속적으로 실행하지 않습니다. 대신 샘플링 방법을 사용합니다. 시스템은 설정된 기간 (최소 및 최대 기간 포함) 내에서 무작위로 백그라운드 트레이스를 시작합니다. 이러한 트레이스를 무작위로 배치하면 트리거 범위가 개선됩니다.
시스템 트리거 프로필에는 시스템 정의 최대 크기가 있으므로 링 버퍼를 사용합니다. 버퍼가 가득 차면 새 추적 데이터가 가장 오래된 데이터를 덮어씁니다. 그림 1에 표시된 것처럼 버퍼가 가득 차면 캡처된 트레이스가 백그라운드 녹화의 전체 기간을 포함하지 않을 수 있습니다. 대신 트리거까지의 가장 최근 활동을 나타냅니다.
트리거별 비율 제한 구현
빈도가 높은 트리거는 앱의 비율 제한기 할당량을 빠르게 소모할 수 있습니다. 비율 제한기를 더 잘 이해하려면 비율 제한기 작동 방식을 참고하세요. 단일 트리거 유형이 할당량을 소진하지 않도록 트리거별 비율 제한을 구현할 수 있습니다.
ProfilingManager는 앱 정의 트리거별 비율 제한을 지원합니다. 이렇게 하면 기존 비율 제한기 외에 시간 기반 제한 레이어를 추가할 수 있습니다. setRateLimitingPeriodHours API를 사용하여 트리거의 특정 쿨다운 시간을 설정합니다. 쿨다운이 만료되면 다시 트리거할 수 있습니다.
로컬에서 트리거 디버그
백그라운드 트레이스는 무작위 시간에 실행되므로 로컬에서 디버깅 트리거가 어렵습니다. 테스트를 위해 백그라운드 트레이스를 강제하려면 다음 ADB 명령어를 사용하세요.
adb shell device_config put profiling_testing system_triggered_profiling.testing_package_name <com.example.myapp>
이 명령어는 시스템이 지정된 패키지의 연속 백그라운드 트레이스를 시작하도록 강제하여 비율 제한기가 허용하는 경우 모든 트리거가 프로필을 수집할 수 있도록 합니다.
로컬에서 디버깅할 때 비율 제한기를 사용 중지하는 등 다른 디버그 옵션을 사용 설정할 수도 있습니다. 자세한 내용은 로컬 프로파일링을 위한 디버그 명령어를 참고하세요.