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를 빌드합니다. 이 이벤트는 앱이 시작을 완료하고 상호작용이 가능하다고 보고할 때 발생합니다. - 비율 제한 설정: 이 특정 트리거에 1시간 비율 제한을 적용합니다
(
setRateLimitingPeriodHours(1)). 이렇게 하면 앱이 시간당 두 개 이상의 시작 프로필을 기록하지 못하도록 방지할 수 있습니다. - 리스너 등록:
registerForAllProfilingResults를 호출하여 결과를 처리하는 콜백을 정의합니다. 이 콜백은getResultFilePath()를 통해 저장된 프로필의 경로를 수신합니다. - 트리거 추가:
ProfilingManager를 사용하여 트리거 목록을addProfilingTriggers에 등록합니다. - 이벤트 실행:
reportFullyDrawn()을 호출합니다. 이 함수는 시스템 백그라운드 트레이스가 실행 중이고 비율 제한기 할당량이 사용 가능한 경우 프로필 수집을 트리거하는 시스템에TRIGGER_TYPE_APP_FULLY_DRAWN이벤트를 내보냅니다. 이 선택적 단계는 앱이 이 트리거에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
이러한 트레이스 시각화에 관한 자세한 내용은 프로파일링 데이터 가져오기 및 분석을 참고하세요.
백그라운드 트레이스 작동 방식
트리거 이전의 데이터를 캡처하기 위해 OS는 백그라운드 트레이스를 주기적으로 시작합니다. 이 백그라운드 트레이스가 활성 상태이고 앱이 등록된 동안 트리거가 발생하면 시스템은 트레이스 프로필을 앱의 디렉터리에 저장합니다. 그러면 프로필에 트리거로 이어지는 정보가 포함됩니다.
프로필이 저장되면 시스템은 registerForAllProfilingResults에 제공된 콜백을 사용하여 앱에 알립니다. 이 콜백은
`ProfilingResult#getResultFilePath()`를 호출하여 액세스할 수 있는 캡처된 프로필의 경로를 제공합니다.
ProfilingResult#getResultFilePath()
기기 성능과 배터리 수명에 미치는 영향을 줄이기 위해 시스템은 백그라운드 트레이스를 지속적으로 실행하지 않습니다. 대신 샘플링 방법을 사용합니다. 시스템은 설정된 기간 (최소 및 최대 기간 포함) 내에서 백그라운드 트레이스를 무작위로 시작합니다. 이러한 트레이스를 무작위로 간격을 두면 트리거 적용 범위가 개선됩니다.
시스템 트리거 프로필에는 시스템 정의 최대 크기가 있으므로 링 버퍼를 사용합니다. 버퍼가 가득 차면 새 트레이스 데이터가 가장 오래된 데이터를 덮어씁니다. 그림 1과 같이 버퍼가 가득 차면 캡처된 트레이스가 백그라운드 기록의 전체 기간을 포함하지 않을 수 있습니다. 대신 트리거로 이어지는 가장 최근 활동을 나타냅니다.
트리거별 비율 제한 구현
고빈도 트리거는 앱의 비율 제한기 할당량을 빠르게 소모할 수 있습니다. 비율 제한기를 더 잘 이해하려면 비율 제한기 작동 방식을 살펴보는 것이 좋습니다. 단일 트리거 유형이 할당량을 소모하지 못하도록 하려면 트리거별 비율 제한을 구현하면 됩니다.
ProfilingManager는 앱 정의 트리거별 비율 제한을 지원합니다. 이렇게 하면 기존 비율 제한기 외에 시간 기반 스로틀링 레이어를 추가할 수 있습니다. setRateLimitingPeriodHours API를 사용하여 트리거의 특정 휴지기를 설정합니다. 대기 시간이 만료되면 다시 트리거할 수 있습니다.
로컬에서 트리거 디버그
백그라운드 트레이스는 무작위 시간에 실행되므로 로컬에서 트리거를 디버그하기는 어렵습니다. 테스트를 위해 백그라운드 트레이스를 강제 실행하려면 다음 ADB 명령어를 사용하세요.
adb shell device_config put profiling_testing system_triggered_profiling.testing_package_name <com.example.myapp>
이 명령어는 시스템이 지정된 패키지의 연속 백그라운드 트레이스를 시작하도록 강제 실행하므로 비율 제한기가 허용하는 경우 모든 트리거가 프로필을 수집할 수 있습니다.
로컬에서 디버그할 때 비율 제한기를 사용 중지하는 등 다른 디버그 옵션을 사용 설정할 수도 있습니다. 자세한 내용은 로컬 프로파일링을 위한 디버그 명령어를 참고하세요.