การสร้างโปรไฟล์ที่ขับเคลื่อนโดยแอป

หน้านี้แสดงวิธีบันทึกการติดตามระบบโดยใช้ ProfilingManager API

ProfilingManager ยังบันทึกโปรไฟล์ประเภทอื่นๆ ได้ด้วย กระบวนการนี้คล้ายกับการบันทึกการติดตามของระบบ แต่การติดตามแต่ละประเภทจะใช้เครื่องมือสร้างที่แตกต่างกัน โปรไฟล์ที่รองรับและผู้สร้างโปรไฟล์มีดังนี้

  • การติดตามระบบ: บันทึกโดยใช้ SystemTraceRequestBuilder ซึ่งมีประโยชน์สำหรับการวิเคราะห์เวลาในการตอบสนองและการแก้ไขข้อบกพร่องด้านประสิทธิภาพทั่วไป

  • ฮีปดัมป์: บันทึกโดยใช้ JavaHeapDumpRequestBuilder ซึ่งมีประโยชน์ในการตรวจหาและเพิ่มประสิทธิภาพหน่วยความจำรั่วไหล

  • โปรไฟล์ฮีป: บันทึกโดยใช้ HeapProfileRequestBuilder ซึ่งมีประโยชน์สำหรับการเพิ่มประสิทธิภาพหน่วยความจำ

  • โปรไฟล์สแต็กการเรียกใช้: บันทึกโดยใช้ StackSamplingRequestBuilder ซึ่งมีประโยชน์ในการทำความเข้าใจการเรียกใช้โค้ดและการวิเคราะห์เวลาในการตอบสนอง

เพิ่มทรัพยากร Dependency

หากต้องการประสบการณ์การใช้งาน ProfilingManager API ที่ดีที่สุด ให้เพิ่มไลบรารี Jetpack ต่อไปนี้ลงในไฟล์ build.gradle.kts

Kotlin

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

ดึงดูด

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

บันทึกการติดตามของระบบ

หลังจากเพิ่มการขึ้นต่อกันที่จำเป็นแล้ว ให้ใช้โค้ดต่อไปนี้เพื่อบันทึกการติดตามระบบ ตัวอย่างนี้แสดงวิธีเริ่มเซสชันการทำโปรไฟล์จาก Composable ขณะจัดการการดำเนินการหนักๆ นอกเทรดหลักอย่างปลอดภัย

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();
}

โค้ดตัวอย่างจะตั้งค่าและจัดการเซสชันการทำโปรไฟล์โดยทำตาม ขั้นตอนต่อไปนี้

  1. ตั้งค่าผู้ดำเนินการ สร้าง Executor เพื่อกำหนดเธรดที่จะ รับผลลัพธ์การทำโปรไฟล์ การทำโปรไฟล์จะเกิดขึ้นในเบื้องหลัง การใช้ ตัวเรียกใช้เธรดที่ไม่ใช่ UI จะช่วยป้องกันข้อผิดพลาดแอปพลิเคชันไม่ตอบสนอง (ANR) หากคุณเพิ่มการประมวลผลลงใน Callback ในภายหลัง

  2. จัดการผลการทำโปรไฟล์ สร้างออบเจ็กต์ Consumer<ProfilingResult> ระบบใช้ออบเจ็กต์นี้เพื่อส่งผลลัพธ์การสร้างโปรไฟล์จาก ProfilingManager กลับไปยังแอปของคุณ

  3. สร้างคำขอการสร้างโปรไฟล์ สร้าง SystemTraceRequestBuilder เพื่อตั้งค่า เซสชันการจัดทำโปรไฟล์ เครื่องมือสร้างนี้ช่วยให้คุณปรับแต่ง ProfilingManagerการตั้งค่าการติดตามได้ การปรับแต่งเครื่องมือสร้างเป็นตัวเลือก หากคุณไม่ปรับแต่ง ระบบจะใช้การตั้งค่าเริ่มต้น

    • กำหนดแท็ก ใช้ setTag() เพื่อเพิ่มแท็กลงในชื่อการติดตาม แท็กนี้ช่วยให้คุณระบุร่องรอยได้
    • ไม่บังคับ: กำหนดระยะเวลา ใช้ setDurationMs() เพื่อระบุระยะเวลา ที่จะทำโปรไฟล์เป็นมิลลิวินาที เช่น 60000 จะตั้งค่าการติดตามเป็น 60 วินาที การติดตามจะสิ้นสุดโดยอัตโนมัติหลังจากระยะเวลาที่ระบุหาก CancellationSignal ไม่ทริกเกอร์ก่อนหน้านั้น
    • เลือกนโยบายบัฟเฟอร์ ใช้ setBufferFillPolicy() เพื่อกำหนดวิธีจัดเก็บข้อมูลการติดตาม BufferFillPolicy.RING_BUFFER หมายความว่าเมื่อบัฟเฟอร์เต็ม ข้อมูลใหม่จะเขียนทับข้อมูลที่เก่าที่สุด ทำให้บันทึกกิจกรรมล่าสุดอย่างต่อเนื่อง
    • ตั้งค่าขนาดบัฟเฟอร์ ใช้ setBufferSizeKb() เพื่อระบุขนาดบัฟเฟอร์ สำหรับการติดตาม ซึ่งคุณสามารถใช้เพื่อควบคุมขนาดของการติดตามเอาต์พุต ได้
  4. ไม่บังคับ: จัดการวงจรเซสชัน สร้าง CancellationSignal ออบเจ็กต์นี้ช่วยให้คุณหยุดเซสชันการสร้างโปรไฟล์ได้ทุกเมื่อที่ต้องการ ซึ่งจะช่วยให้คุณควบคุมระยะเวลาของเซสชันได้อย่างแม่นยำ

  5. เริ่มและรับผลลัพธ์ เมื่อคุณโทรหา requestProfiling() ProfilingManager จะเริ่มเซสชันการสร้างโปรไฟล์ในเบื้องหลัง เมื่อ การสร้างโปรไฟล์เสร็จสมบูรณ์แล้ว ระบบจะส่ง ProfilingResult ไปยังเมธอด resultCallback#accept ของคุณ หากการสร้างโปรไฟล์เสร็จสมบูรณ์ ProfilingResult จะระบุเส้นทางที่บันทึกการติดตามในอุปกรณ์ ผ่าน ProfilingResult#getResultFilePath คุณรับไฟล์นี้ได้ โดยใช้โปรแกรม หรือหากต้องการสร้างโปรไฟล์ในเครื่อง ให้เรียกใช้ adb pull <trace_path> จากคอมพิวเตอร์

  6. เพิ่มจุดติดตามที่กำหนดเอง คุณเพิ่มจุดติดตามที่กำหนดเองในโค้ดของแอปได้ ในตัวอย่างโค้ดก่อนหน้า บล็อก trace("MyApp:HeavyOperation") { ... } จะสร้างชิ้นที่กำหนดเองใน โปรไฟล์ที่สร้างขึ้น