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)funsampleRecordSystemTrace(){valmainExecutor:Executor=Dispatchers.IO.asExecutor()// Your choice of executor for the callback to occur on.valresultCallback=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)}}valstopSignal=CancellationSignal()valrequestBuilder=SystemTraceRequestBuilder()requestBuilder.setCancellationSignal(stopSignal)requestBuilder.setTag("FOO")// Caller supplied tag for identificationrequestBuilder.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 profilestopSignal.cancel()}funheavyOperation(){// Computations you want to profile}
voidheavyOperation(){// Computations you want to profile}voidsampleRecordSystemTrace(){ExecutormainExecutor=Executors.newSingleThreadExecutor();Consumer<ProfilingResult>resultCallback=newConsumer<ProfilingResult>(){@Overridepublicvoidaccept(ProfilingResultprofilingResult){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());}}};CancellationSignalstopSignal=newCancellationSignal();SystemTraceRequestBuilderrequestBuilder=newSystemTraceRequestBuilder();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 profilestopSignal.cancel();}
The sample code sets up and manages the profiling session by going through the
following steps:
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.
Handle profiling results. Create a Consumer<ProfilingResult> object.
The system uses this object to send profiling results from
ProfilingManager back to your app.
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.
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.
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.
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.
Content and code samples on this page are subject to the licenses described in the Content License. Java and OpenJDK are trademarks or registered trademarks of Oracle and/or its affiliates.
Last updated 2025-08-27 UTC.
[[["Easy to understand","easyToUnderstand","thumb-up"],["Solved my problem","solvedMyProblem","thumb-up"],["Other","otherUp","thumb-up"]],[["Missing the information I need","missingTheInformationINeed","thumb-down"],["Too complicated / too many steps","tooComplicatedTooManySteps","thumb-down"],["Out of date","outOfDate","thumb-down"],["Samples / code issue","samplesCodeIssue","thumb-down"],["Other","otherDown","thumb-down"]],["Last updated 2025-08-27 UTC."],[],[],null,["This page shows how to record a system trace using the `ProfilingManager` API.\n| **Tip:** `ProfilingManager` contains a rate limiter that is set up to reduce the impact of repeated profiling requests on device performance. When you're using this tool locally, you want to see every request so we recommend keeping the [rate limiter enabled](/topic/performance/tracing/profiling-manager/debug-mode#disable-rate-limiter).\n\nAdd dependencies\n\nFor the best experience with the `ProfilingManager` API, add the following\nJetpack libraries to your `build.gradle.kts` file. \n\nKotlin \n\n```kotlin\n dependencies {\n implementation(\"androidx.tracing:tracing:1.3.0\")\n implementation(\"androidx.core:core:1.17.0\")\n }\n \n```\n\nGroovy \n\n```groovy\n dependencies {\n implementation 'androidx.tracing:tracing:1.3.0'\n implementation 'androidx.core:core:1.17.0'\n }\n \n```\n\nRecord a system trace\n\nAfter adding the required dependencies, use the following code to record a\nsystem trace. This example shows a basic setup within an `Activity` to start and\nmanage a profiling session.\n\n\nKotlin \n\n```kotlin\n@RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)\nfun sampleRecordSystemTrace() {\n val mainExecutor: Executor =\n Dispatchers.IO.asExecutor() // Your choice of executor for the callback to occur on.\n val resultCallback = Consumer\u003cProfilingResult\u003e { profilingResult -\u003e\n if (profilingResult.errorCode == ProfilingResult.ERROR_NONE) {\n Log.d(\n \"ProfileTest\",\n \"Received profiling result file=\" + profilingResult.resultFilePath\n )\n } else {\n Log.e(\n \"ProfileTest\",\n \"Profiling failed errorcode=\" + profilingResult.errorCode + \" errormsg=\" + profilingResult.errorMessage\n )\n }\n }\n val stopSignal = CancellationSignal()\n\n val requestBuilder = SystemTraceRequestBuilder()\n requestBuilder.setCancellationSignal(stopSignal)\n requestBuilder.setTag(\"FOO\") // Caller supplied tag for identification\n requestBuilder.setDurationMs(60000)\n requestBuilder.setBufferFillPolicy(BufferFillPolicy.RING_BUFFER)\n requestBuilder.setBufferSizeKb(20971520)\n requestProfiling(applicationContext, requestBuilder.build(), mainExecutor, resultCallback)\n\n // Wait some time for profiling to start.\n\n Trace.beginSection(\"MyApp:HeavyOperation\")\n heavyOperation()\n Trace.endSection()\n\n // Once the interesting code section is profiled, stop profile\n stopSignal.cancel()\n}\n\nfun heavyOperation() {\n // Computations you want to profile\n}https://github.com/android/snippets/blob/e4396f6dd13aaa8099c4baa671cdd549a10f201c/misc/src/main/java/com/example/snippets/profiling/ProfilingManagerKotlinSnippets.kt#L43-L82\n```\n\nJava \n\n```java\nvoid heavyOperation() {\n // Computations you want to profile\n}\n\nvoid sampleRecordSystemTrace() {\n Executor mainExecutor = Executors.newSingleThreadExecutor();\n Consumer\u003cProfilingResult\u003e resultCallback =\n new Consumer\u003cProfilingResult\u003e() {\n @Override\n public void accept(ProfilingResult profilingResult) {\n if (profilingResult.getErrorCode() == ProfilingResult.ERROR_NONE) {\n Log.d(\n \"ProfileTest\",\n \"Received profiling result file=\" + profilingResult.getResultFilePath());\n } else {\n Log.e(\n \"ProfileTest\",\n \"Profiling failed errorcode=\"\n\n + profilingResult.getErrorCode()\n + \" errormsg=\"\n + profilingResult.getErrorMessage());\n }\n }\n };\n CancellationSignal stopSignal = new CancellationSignal();\n\n SystemTraceRequestBuilder requestBuilder = new SystemTraceRequestBuilder();\n requestBuilder.setCancellationSignal(stopSignal);\n requestBuilder.setTag(\"FOO\");\n requestBuilder.setDurationMs(60000);\n requestBuilder.setBufferFillPolicy(BufferFillPolicy.RING_BUFFER);\n requestBuilder.setBufferSizeKb(20971520);\n Profiling.requestProfiling(getApplicationContext(), requestBuilder.build(), mainExecutor,\n resultCallback);\n\n // Wait some time for profiling to start.\n\n Trace.beginSection(\"MyApp:HeavyOperation\");\n heavyOperation();\n Trace.endSection();\n\n // Once the interesting code section is profiled, stop profile\n stopSignal.cancel();\n}https://github.com/android/snippets/blob/e4396f6dd13aaa8099c4baa671cdd549a10f201c/misc/src/main/java/com/example/snippets/profiling/ProfilingManagerJavaSnippets.java#L26-L70\n```\n\n\u003cbr /\u003e\n\nThe sample code sets up and manages the profiling session by going through the\nfollowing steps:\n\n1. **Set up the executor.** Create an `Executor` to define the thread that will\n receive the profiling results. Profiling happens in the background. Using a\n non-UI thread executor helps prevent Application Not Responding (ANR) errors\n if you add more processing to the callback later.\n\n2. **Handle profiling results.** Create a `Consumer\u003cProfilingResult\u003e` object.\n The system uses this object to send profiling results from\n `ProfilingManager` back to your app.\n\n3. **Build the profiling request.** Create a `SystemTraceRequestBuilder` to set\n up your profiling session. This builder lets you customize\n `ProfilingManager` trace settings. Customizing the builder is optional; if\n you don't, the system uses default settings.\n\n - **Define a tag.** Use `setTag()` to add a tag to the trace name. This tag helps you identify the trace.\n - **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.\n - **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.\n - **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.\n\n | **Note:** Not all the Perfetto configurations are available for `ProfilingManager`.\n4. **Optional: Manage the session lifecycle.** Create a `CancellationSignal`.\n This object lets you stop the profiling session whenever you want, giving\n you precise control over its length.\n\n | **Note:** If you use neither `CancellationSignal` nor `setDurationMs()`, the system applies a default duration. If you define both, whichever happens first ends the session.\n5. **Start and receive results.** When you call `requestProfiling()`,\n `ProfilingManager` starts a profiling session in the background. Once\n profiling is done, it sends the `ProfilingResult` to your\n `resultCallback#accept` method. If profiling finishes successfully, the\n `ProfilingResult` provides the path where the trace was saved on your device\n through `ProfilingResult#getResultFilePath`. You can get this file\n programmatically or, for local profiling, by running `adb pull \u003ctrace_path\u003e`\n from your computer.\n\n | **Note:** If an error occurs during profiling, the `ProfilingResult` provides an error description through `profilingResult.getErrorMessage()` and an error code through `profilingResult.getErrorCode()`. A common reason for failure is if your app gets rate limited due to excessive requests.\n | **Note:** If the app dies before this result is delivered, delivery will be attempted again once the app starts and registers a general listener.\n6. **Add custom trace points.** You can add custom trace points in your app's\n code. In the previous code example, a trace slice named\n `MyApp:HeavyOperation` is added using `Trace.beginSection()` and\n `Trace.endSection()`. This custom slice appears in the generated profile,\n highlighting specific operations within your app."]]