Uygulamaya dayalı profilleme

Bu sayfada, ProfilingManager API kullanılarak sistem izinin nasıl kaydedileceği gösterilmektedir.

ProfilingManager, diğer profil türlerini de kaydedebilir. Bu işlem, sistem izi kaydetmeye benzer ancak her tür farklı bir oluşturucu kullanır. Desteklenen profiller ve oluşturucuları şunlardır:

  • Sistem İzleri: Gecikme analizi ve genel performans hata ayıklama için yararlı olan SystemTraceRequestBuilder kullanılarak kaydedilir.

  • Yığın dökümleri: JavaHeapDumpRequestBuilder kullanılarak kaydedilir. Bellek sızıntısı algılama ve optimizasyon için faydalıdır.

  • Yığın profilleri: HeapProfileRequestBuilder kullanılarak kaydedilir. Bu profiller, bellek optimizasyonu için yararlıdır.

  • Çağrı yığını profilleri: StackSamplingRequestBuilder kullanılarak kaydedilir. Bu profiller, kod yürütme ve gecikme analizini anlamak için yararlıdır.

Bağımlılık ekleme

ProfilingManager API ile en iyi deneyimi elde etmek için aşağıdaki Jetpack kitaplıklarını build.gradle.kts dosyanıza ekleyin.

Kotlin

   dependencies {
       implementation("androidx.tracing:tracing-ktx:2.0.1")
       implementation("androidx.core:core:1.19.0")
   }
   

Modern

   dependencies {
       implementation 'androidx.tracing:tracing:2.0.1'
       implementation 'androidx.core:core:1.19.0'
   }
   

Sistem izi kaydetme

Gerekli bağımlılıkları ekledikten sonra sistem izi kaydetmek için aşağıdaki kodu kullanın. Bu örnekte, ana iş parçacığı dışındaki ağır işlemleri güvenli bir şekilde yönetirken bir composable'dan profil oluşturma oturumunun nasıl başlatılacağı gösterilmektedir.

Kotlin

@RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
@Composable
fun ProfiledScreen(modifier: Modifier = Modifier) {
    // Use the application context: requestProfiling resolves the ProfilingManager
    // system service from it, so there's no reason to hand it a short-lived Activity.
    val appContext = LocalContext.current.applicationContext
    val scope = rememberCoroutineScope()

    Button(
        onClick = {
            // Run the orchestration off the main thread. Profiling a heavy operation
            // on the UI thread would freeze the UI (ANR) and distort the very metrics
            // you're trying to capture.
            //
            // Note: this scope is tied to composition. If the user leaves this screen
            // mid-session, the coroutine is cancelled and stopSignal.cancel() might not
            // run, but setDurationMs() acts as a safety net and ends the trace.
            scope.launch(Dispatchers.Default) {
                val callbackExecutor = Dispatchers.IO.asExecutor()
                val resultCallback = Consumer<ProfilingResult> { profilingResult ->
                    if (profilingResult.errorCode == ProfilingResult.ERROR_NONE) {
                        Log.d("ProfileTest", "Result file: ${profilingResult.resultFilePath}")
                    } else {
                        // errorMessage explains the failure (e.g., rate limiting); keep it.
                        Log.e(
                            "ProfileTest",
                            "Profiling failed errorCode=${profilingResult.errorCode} " +
                                "errorMessage=${profilingResult.errorMessage}"
                        )
                    }
                }

                val stopSignal = CancellationSignal()
                val requestBuilder = SystemTraceRequestBuilder().apply {
                    setCancellationSignal(stopSignal)
                    setTag("FOO") // Caller-supplied tag for identification.
                    setDurationMs(60000) // Hard cap: ends the session if cancel() never fires.
                    setBufferFillPolicy(BufferFillPolicy.RING_BUFFER)
                    setBufferSizeKb(32768)
                }

                // 1. Start the session. This is asynchronous system IPC. The tracing
                //    engine takes a moment to start and allocate buffers.
                requestProfiling(appContext, requestBuilder.build(), callbackExecutor, resultCallback)

                // 2. The API exposes no "profiling started" signal, so pad with a short,
                //    best-effort delay before running the code you care about. This is
                //    approximate. Increase it on slower or heavily loaded devices.
                delay(STARTUP_PADDING_MS)

                // 3. The session is already recording every thread in your app. This slice
                //    doesn't scope what's captured. It just labels this region of the
                //    timeline so heavyOperation() is easier to find. trace { } closes the
                //    section even if the block throws.

                trace("MyApp:HeavyOperation") {
                    heavyOperation()
                }

                // 4. Stop recording. Until this fires or the setDurationMs() cap is
                //    reached (whichever comes first), the session keeps capturing app-wide
                //    activity.

                stopSignal.cancel()
            }
        }
    ) {
        Text("Run & Profile Heavy Operation")
    }
}

// Best-effort wait for the system trace engine to initialize before profiling.
// There is no deterministic start callback; tune this for your target devices.
private const val STARTUP_PADDING_MS = 100L

fun heavyOperation() {
    // Background computations to profile.
}

Java

void heavyOperation() {
  // Computations you want to profile
}

void sampleRecordSystemTrace() {
  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());
          }
        }
      };
  CancellationSignal stopSignal = new CancellationSignal();

  SystemTraceRequestBuilder requestBuilder = new SystemTraceRequestBuilder();
  requestBuilder.setCancellationSignal(stopSignal);
  requestBuilder.setTag("FOO");
  requestBuilder.setDurationMs(60000);
  requestBuilder.setBufferFillPolicy(BufferFillPolicy.RING_BUFFER);
  requestBuilder.setBufferSizeKb(32768);
  Profiling.requestProfiling(getApplicationContext(), requestBuilder.build(), mainExecutor,
      resultCallback);

  // Wait some time for profiling to start.

  Trace.beginSection("MyApp:HeavyOperation");
  heavyOperation();
  Trace.endSection();

  // Once the interesting code section is profiled, stop profile
  stopSignal.cancel();
}

Örnek kod, aşağıdaki adımları uygulayarak profilleme oturumunu oluşturur ve yönetir:

  1. Yürütücüyü ayarlayın. Profil oluşturma sonuçlarını alacak ileti dizisini tanımlamak için Executor oluşturun. Profil oluşturma işlemi arka planda gerçekleşir. Kullanıcı arayüzü iş parçacığı dışındaki bir iş parçacığı yürütücü kullanmak, geri çağırmaya daha sonra daha fazla işlem eklerseniz Uygulama Yanıt Vermiyor (ANR) hatalarını önlemeye yardımcı olur.

  2. Profil oluşturma sonuçlarını işleme Consumer<ProfilingResult> nesnesi oluşturun. Sistem, bu nesneyi kullanarak ProfilingManager profil oluşturma sonuçlarını uygulamanıza geri gönderir.

  3. Profillendirme isteğini oluşturun. Profil oluşturma oturumunuzu ayarlamak için SystemTraceRequestBuilder oluşturun. Bu oluşturucu, iz ayarlarını ProfilingManager özelleştirmenize olanak tanır. Oluşturucuyu özelleştirmek isteğe bağlıdır. Özelleştirmezseniz sistem varsayılan ayarları kullanır.

    • Bir etiket tanımlayın. İz adına etiket eklemek için setTag() simgesini kullanın. Bu etiket, izlemeyi tanımlamanıza yardımcı olur.
    • İsteğe bağlı: Süreyi ayarlayın. Milisaniye cinsinden ne kadar süreyle profil oluşturulacağını belirtmek için setDurationMs() kullanın. Örneğin, 60000 60 saniyelik bir izleme ayarlar. Belirtilen süre içinde CancellationSignal tetiklenmezse izleme otomatik olarak sona erer.
    • Bir arabellek politikası seçin. İzleme verilerinin nasıl depolandığını tanımlamak için setBufferFillPolicy() kullanın. BufferFillPolicy.RING_BUFFER, arabellek dolduğunda yeni verilerin en eski verilerin üzerine yazılacağı ve son etkinliklerin sürekli olarak kaydedileceği anlamına gelir.
    • Arabellek boyutu ayarlayın. Çıkış izleme dosyasının boyutunu kontrol etmek için kullanabileceğiniz izleme arabellek boyutunu belirtmek üzere setBufferSizeKb() seçeneğini kullanın.
  4. İsteğe bağlı: Oturum yaşam döngüsünü yönetin. CancellationSignal oluşturun. Bu nesne, profil oluşturma oturumunu istediğiniz zaman durdurmanıza olanak tanıyarak süresi üzerinde hassas kontrol sağlar.

  5. Başlatın ve sonuçları alın. requestProfiling()'ı aradığınızda, ProfilingManager arka planda bir profil oluşturma oturumu başlatır. Profil oluşturma işlemi tamamlandıktan sonra ProfilingResult, resultCallback#accept yönteminize gönderilir. Profillendirme işlemi başarıyla tamamlanırsa ProfilingResult, ProfilingResult#getResultFilePath aracılığıyla izlemenin cihazınıza kaydedildiği yolu sağlar. Bu dosyayı programatik olarak veya yerel profilleme için bilgisayarınızdan adb pull <trace_path> komutunu çalıştırarak alabilirsiniz.

  6. Özel izleme noktaları ekleyin. Uygulamanızın koduna özel izleme noktaları ekleyebilirsiniz. Önceki kod örneğinde, trace("MyApp:HeavyOperation") { ... } bloğu oluşturulan profilde özel bir dilim oluşturur.