Whether you capture an app-driven profile or a trigger-based profile,
ProfilingManager saves the completed file on the device and delivers a
ProfilingResult to your registered callback. Call
ProfilingResult.getResultFilePath to get the path of the saved profile:
- During local testing: Pull the file to your computer using
adb. - In production: Pass the path to a
WorkManagerjob that compresses and uploads the profile to your server.
Retrieve profiles from a device
This section describes where ProfilingManager stores profiles on the device,
how automatic file cleanup works, and how to pull files locally using adb.
File locations and naming formats
ProfilingManager saves redacted profiles in your app-specific storage
directory at /data/user/0/<app>/files/profiling/. The filename format
depends on how the profile was captured:
App-driven profiles:
profile_<tag>_<datetime>_uid-<uid>.<profile-type-ext>If no tag was provided when requesting the profile,
<tag>_is omitted:profile_<datetime>_uid-<uid>.<profile-type-ext>.Trigger-based profiles:
profile_trigger-type-<trigger_type>_<datetime>_uid-<uid>.<profile-type-ext>
Where:
<app>is your app's package name.<tag>is up to the first 20 alphanumeric characters (and hyphens) of the caller-supplied tag configured withsetTagor passed torequestProfiling, converted to lowercase with any characters outsidea-z,0-9, and-removed.<trigger_type>is the integer constant code for the trigger fromProfilingTrigger. Always callProfilingResult.getTriggerTypein code instead of parsing the filename.<datetime>is the date and time the profile was recorded (yyyy-MM-dd-HH-mm-ss-SSS).<uid>is your app's Linux user ID (included on recentProfilingmodule versions).<profile-type-ext>is the file extension for the captured profile type:.perfetto-tracefor system traces.perfetto-java-heap-dumpfor Java heap dumps.perfetto-heap-profilefor heap profiles.perfetto-stack-samplefor stack sampling profiles
On-device storage and cleanup
To free up device storage promptly, delete each profile file in your upload
worker immediately after a successful upload (because automatic cleanup only
runs when ProfilingManager delivers results to your app, unuploaded files can
otherwise remain on disk if your app stops registering callbacks after ending a
campaign). As a fallback on recent Profiling module versions,
ProfilingManager also automatically deletes delivered files in your
app-specific storage directory that are older than 5 days when new results are
delivered.
Upload profiles in production with WorkManager
Because profile files can be several megabytes in size, schedule production uploads in the background when the device is on an unmetered network (Wi-Fi), charging, and idle.
Before uploading, compress the profile file (for example, using
GZIPOutputStream or ZipOutputStream). Perfetto protobuf profiles and Java
heap dumps compress significantly, reducing network bandwidth and backend
storage costs.
Add WorkManager dependencies
ProfilingManager is cloud-agnostic, so you can schedule the upload of profiles
to any backend using WorkManager. Add the WorkManager library to your
build.gradle.kts file:
Kotlin
dependencies { implementation("androidx.work:work-runtime:2.12.0") }
Groovy
dependencies { implementation 'androidx.work:work-runtime:2.12.0' }
Schedule an upload job
When your app receives a ProfilingResult, schedule the upload job. The
following example omits the initial profiling request; for a complete request
example, see Record a system trace.
Kotlin
class TraceUploadWorker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) { override fun doWork(): Result { // Perform your uploading work here Log.d("ProfileTest", "Uploading trace: " + inputData.getString("PROFILE_PATH")) return Result.success() } } fun setupProfileUploadWorker(profileFilepath: String?) { val workMgr = WorkManager.getInstance(applicationContext) val workRequestBuilder = OneTimeWorkRequest.Builder(TraceUploadWorker::class) val constraints = Constraints.Builder() .setRequiredNetworkType(NetworkType.UNMETERED) .setRequiresDeviceIdle(true) .setRequiresCharging(true) .build() workRequestBuilder.setConstraints(constraints) val inputDataBuilder = Data.Builder() inputDataBuilder.putString("PROFILE_PATH", profileFilepath) workRequestBuilder.setInputData(inputDataBuilder.build()) workMgr.enqueue(workRequestBuilder.build()) }
Java
public static class TraceUploadWorker extends Worker { public TraceUploadWorker( @androidx.annotation.NonNull Context context, @androidx.annotation.NonNull WorkerParameters workerParams) { super(context, workerParams); } @androidx.annotation.NonNull @Override public Result doWork() { // Perform your uploading work here Log.d("ProfileTest", "Uploading trace: " + getInputData().getString("PROFILE_PATH")); return Result.success(); } } public void setupProfileUploadWorker(String profileFilepath) { WorkManager workMgr = WorkManager.getInstance(getApplicationContext()); OneTimeWorkRequest.Builder workRequestBuilder = new OneTimeWorkRequest.Builder( TraceUploadWorker.class); Constraints constraints = new Constraints.Builder() .setRequiredNetworkType(NetworkType.UNMETERED) .setRequiresDeviceIdle(true) .setRequiresCharging(true) .build(); workRequestBuilder.setConstraints(constraints); Data.Builder inputDataBuilder = new Data.Builder(); inputDataBuilder.putString("PROFILE_PATH", profileFilepath); workRequestBuilder.setInputData(inputDataBuilder.build()); workMgr.enqueue(workRequestBuilder.build()); }
Code walkthrough
The preceding example performs the following steps:
- Define the worker: Create a
TraceUploadWorkerclass extendingWorker. ImplementdoWorkto verify that the profile file still exists, compress and upload it using your backend SDK or HTTP client, and delete the local file once the upload succeeds. - Schedule the work:
- Create a
OneTimeWorkRequestfor your worker. - Set constraints: Use
setRequiredNetworkType(NetworkType.UNMETERED),setRequiresDeviceIdle(true), andsetRequiresCharging(true)so the upload only runs when the device is on Wi-Fi, charging, and idle. - Pass data: Use
setInputDatato pass the profile path fromgetResultFilePathto the worker. - Enqueue: Submit the request to
WorkManagerby callingWorkManager.enqueue.
- Create a
Next steps
After retrieving or uploading profiles, visualize them in the Perfetto UI, query individual profiles with PerfettoSQL, analyze traces in bulk, or analyze profiles with AI. For guidance on setting up a scalable server-side analysis pipeline, see Deploying Bigtrace on Kubernetes.