App-gesteuertes Profiling

Auf dieser Seite wird beschrieben, wie Sie mit der ProfilingManager API einen System-Trace aufzeichnen.

Mit ProfilingManager können auch andere Profiltypen aufgezeichnet werden. Dieser Vorgang ähnelt dem Aufzeichnen eines System-Traces, aber für jeden Typ wird ein anderer Builder verwendet. Die unterstützten Profile und ihre Builder sind:

  • System-Traces:werden mit SystemTraceRequestBuilder aufgezeichnet und sind nützlich für die Latenzanalyse und die allgemeine Fehlerbehebung bei der Leistung.

  • Heap-Dumps:werden mit JavaHeapDumpRequestBuilder aufgezeichnet und sind hilfreich für die Erkennung und Optimierung von Speicherlecks.

  • Heap-Profile: werden mit HeapProfileRequestBuilder aufgezeichnet und sind nützlich für die Speicheroptimierung.

  • Aufrufstack-Profile:werden mit StackSamplingRequestBuilder aufgezeichnet und sind nützlich, um die Codeausführung und die Latenz zu analysieren.

Abhängigkeiten hinzufügen

Fügen Sie der Datei build.gradle.kts die folgenden Jetpack-Bibliotheken hinzu, um die ProfilingManager API optimal zu nutzen.

Kotlin

   dependencies {
       implementation("androidx.tracing:tracing:1.3.0")
       implementation("androidx.core:core:1.18.0")
   }
   

Groovy

   dependencies {
       implementation 'androidx.tracing:tracing:1.3.0'
       implementation 'androidx.core:core:1.18.0'
   }
   

System-Trace aufzeichnen

Nachdem Sie die erforderlichen Abhängigkeiten hinzugefügt haben, verwenden Sie den folgenden Code, um einen System-Trace aufzuzeichnen. In diesem Beispiel wird eine grundlegende Einrichtung in einer Activity gezeigt, um eine Profilerstellungssitzung zu starten und zu verwalten.

Kotlin

@RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
fun sampleRecordSystemTrace() {
    val mainExecutor: Executor =
        Dispatchers.IO.asExecutor() // Your choice of executor for the callback to occur on.
    val resultCallback = Consumer<ProfilingResult> { profilingResult ->
        if (profilingResult.errorCode == ProfilingResult.ERROR_NONE) {
            Log.d(
                "ProfileTest",
                "Received profiling result file=" + profilingResult.resultFilePath
            )
        } else {
            Log.e(
                "ProfileTest",
                "Profiling failed errorcode=" + profilingResult.errorCode + " errormsg=" + profilingResult.errorMessage
            )
        }
    }
    val stopSignal = CancellationSignal()

    val requestBuilder = SystemTraceRequestBuilder()
    requestBuilder.setCancellationSignal(stopSignal)
    requestBuilder.setTag("FOO") // Caller supplied tag for identification
    requestBuilder.setDurationMs(60000)
    requestBuilder.setBufferFillPolicy(BufferFillPolicy.RING_BUFFER)
    requestBuilder.setBufferSizeKb(20971520)
    requestProfiling(applicationContext, 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()
}

fun heavyOperation() {
    // Computations you want 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(20971520);
  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();
}

Der Beispielcode richtet die Profilerstellungssitzung ein und verwaltet sie. Dazu werden die folgenden Schritte ausgeführt:

  1. Executor einrichten Erstellen Sie einen Executor, um den Thread zu definieren, der die Profilerstellungsergebnisse empfängt. Die Profilerstellung erfolgt im Hintergrund. Wenn Sie einen Executor für einen Nicht-UI-Thread verwenden, können Sie ANR-Fehler (App antwortet nicht) vermeiden, wenn Sie dem Callback später weitere Verarbeitung hinzufügen.

  2. Profilerstellungsergebnisse verarbeiten Erstellen Sie ein Consumer<ProfilingResult>-Objekt. Das System verwendet dieses Objekt, um Profilerstellungsergebnisse von ProfilingManager an Ihre App zurückzusenden.

  3. Profilerstellungsanfrage erstellen Erstellen Sie einen SystemTraceRequestBuilder, um Ihre Profilerstellungssitzung einzurichten. Mit diesem Builder können Sie die Trace-Einstellungen von ProfilingManager anpassen. Die Anpassung des Builders ist optional. Andernfalls verwendet das System die Standardeinstellungen.

    • Tag definieren Verwenden Sie setTag(), um dem Trace-Namen ein Tag hinzuzufügen. Mit diesem Tag können Sie den Trace identifizieren.
    • Optional: Dauer festlegen Verwenden Sie setDurationMs(), um die Dauer der Profilerstellung in Millisekunden anzugeben. Mit 60000 wird beispielsweise ein 60-Sekunden-Trace festgelegt. Der Trace wird nach der angegebenen Dauer automatisch beendet, wenn CancellationSignal nicht vorher ausgelöst wird.
    • Pufferrichtlinie auswählen Verwenden Sie setBufferFillPolicy(), um festzulegen, wie Trace-Daten gespeichert werden. BufferFillPolicy.RING_BUFFER bedeutet, dass neue Daten die ältesten Daten überschreiben, wenn der Puffer voll ist. So wird eine kontinuierliche Aufzeichnung der letzten Aktivitäten beibehalten.
    • Puffergröße festlegen Verwenden Sie setBufferSizeKb(), um eine Puffergröße für das Tracing anzugeben. Damit können Sie die Größe der Ausgabedatei des Traces steuern.
  4. Optional: Lebenszyklus der Sitzung verwalten Erstellen Sie ein CancellationSignal. Mit diesem Objekt können Sie die Profilerstellungssitzung jederzeit beenden und so die Länge genau steuern.

  5. Starten und Ergebnisse empfangen Wenn Sie requestProfiling() aufrufen, startet ProfilingManager im Hintergrund eine Profilerstellungssitzung. Sobald die Profilerstellung abgeschlossen ist, wird ProfilingResult an die Methode resultCallback#accept gesendet. Wenn die Profilerstellung erfolgreich abgeschlossen wurde, enthält die ProfilingResult den Pfad, in dem der Trace auf Ihrem Gerät gespeichert wurde über ProfilingResult#getResultFilePath. Sie können diese Datei programmatisch oder für die lokale Profilerstellung abrufen, indem Sie adb pull <trace_path> auf Ihrem Computer ausführen.

  6. Benutzerdefinierte Trace-Punkte hinzufügen Sie können dem Code Ihrer App benutzerdefinierte Trace-Punkte hinzufügen. Im vorherigen Codebeispiel wird mit Trace.beginSection() und Trace.endSection() ein Trace-Slice namens MyApp:HeavyOperation hinzugefügt. Dieses benutzerdefinierte Slice wird im generierten Profil angezeigt und hebt bestimmte Vorgänge in Ihrer App hervor.