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.17.0")  }  

Groovy

 dependencies {  implementation 'androidx.tracing:tracing:1.3.0'  implementation 'androidx.core:core:1.17.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.