How to capture a profile

This page shows how to record a system trace using the ProfilingManager API.

Add dependencies

For the best experience with the ProfilingManager API, add the following Jetpack libraries to your build.gradle.kts file.

Kotlin

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

Groovy

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

Record a system trace

After adding the required dependencies, use the following code to record a system trace. This example shows a basic setup within an Activity to start and manage a profiling session.

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

The sample code sets up and manages the profiling session by going through the following steps:

  1. Set up the executor. Create an Executor to define the thread that will receive the profiling results. Profiling happens in the background. Using a non-UI thread executor helps prevent Application Not Responding (ANR) errors if you add more processing to the callback later.

  2. Handle profiling results. Create a Consumer<ProfilingResult> object. The system uses this object to send profiling results from ProfilingManager back to your app.

  3. Build the profiling request. Create a SystemTraceRequestBuilder to set up your profiling session. This builder lets you customize ProfilingManager trace settings. Customizing the builder is optional; if you don't, the system uses default settings.

    • Define a tag. Use setTag() to add a tag to the trace name. This tag helps you identify the trace.
    • Optional: Set the duration. Use setDurationMs() to specify how long to profile in milliseconds. For example, 60000 sets a 60-second trace. The trace automatically ends after the specified duration if CancellationSignal isn't triggered before that.
    • Choose a buffer policy. Use setBufferFillPolicy() to define how trace data is stored. BufferFillPolicy.RING_BUFFER means that when the buffer is full, new data overwrites the oldest data, keeping a continuous record of recent activity.
    • Set a buffer size. Use setBufferSizeKb() to specify a buffer size for tracing which you can use to control the size of the output trace file.
  4. Optional: Manage the session lifecycle. Create a CancellationSignal. This object lets you stop the profiling session whenever you want, giving you precise control over its length.

  5. Start and receive results. When you call requestProfiling(), ProfilingManager starts a profiling session in the background. Once profiling is done, it sends the ProfilingResult to your resultCallback#accept method. If profiling finishes successfully, the ProfilingResult provides the path where the trace was saved on your device through ProfilingResult#getResultFilePath. You can get this file programmatically or, for local profiling, by running adb pull <trace_path> from your computer.

  6. Add custom trace points. You can add custom trace points in your app's code. In the previous code example, a trace slice named MyApp:HeavyOperation is added using Trace.beginSection() and Trace.endSection(). This custom slice appears in the generated profile, highlighting specific operations within your app.