Cette page explique comment enregistrer une trace système à l'aide de l'API ProfilingManager.
ProfilingManager peut également enregistrer d'autres types de profils. Ce processus est semblable à l'enregistrement d'une trace système, mais chaque type utilise un compilateur différent. Les profils compatibles et leurs compilateurs sont les suivants :
Traces système : enregistrées à l'aide de
SystemTraceRequestBuilder, qui sont utiles pour l'analyse de la latence et le débogage des performances générales.Empreintes de la mémoire : enregistrées à l'aide de
JavaHeapDumpRequestBuilder, qui sont utiles pour la détection et l'optimisation des fuites de mémoire.Profils de mémoire : enregistrés à l'aide de
HeapProfileRequestBuilder, qui sont utiles pour l'optimisation de la mémoire.Profils de pile d'appels : enregistrés à l'aide de
StackSamplingRequestBuilder, qui sont utiles pour comprendre l'exécution de code et l'analyse de la latence.
Ajouter des dépendances
Pour une expérience optimale avec l'API ProfilingManager, ajoutez les bibliothèques Jetpack suivantes à votre fichier build.gradle.kts.
Kotlin
dependencies { implementation("androidx.tracing:tracing-ktx:2.0.1") implementation("androidx.core:core:1.19.0") }
Groovy
dependencies { implementation 'androidx.tracing:tracing:2.0.1' implementation 'androidx.core:core:1.19.0' }
Enregistrer une trace système
Après avoir ajouté les dépendances requises, utilisez le code suivant pour enregistrer une trace système. Cet exemple montre comment démarrer une session de profilage à partir d'un composable tout en gérant en toute sécurité les opérations lourdes en dehors du thread principal.
Kotlin
@RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
@Composable
fun ProfiledScreen(modifier: Modifier = Modifier) {
// Use the application context: requestProfiling resolves the ProfilingManager
// system service from it, so there's no reason to hand it a short-lived Activity.
val appContext = LocalContext.current.applicationContext
val scope = rememberCoroutineScope()
Button(
onClick = {
// Run the orchestration off the main thread. Profiling a heavy operation
// on the UI thread would freeze the UI (ANR) and distort the very metrics
// you're trying to capture.
//
// Note: this scope is tied to composition. If the user leaves this screen
// mid-session, the coroutine is cancelled and stopSignal.cancel() might not
// run, but setDurationMs() acts as a safety net and ends the trace.
scope.launch(Dispatchers.Default) {
val callbackExecutor = Dispatchers.IO.asExecutor()
val resultCallback = Consumer<ProfilingResult> { profilingResult ->
if (profilingResult.errorCode == ProfilingResult.ERROR_NONE) {
Log.d("ProfileTest", "Result file: ${profilingResult.resultFilePath}")
} else {
// errorMessage explains the failure (e.g., rate limiting); keep it.
Log.e(
"ProfileTest",
"Profiling failed errorCode=${profilingResult.errorCode} " +
"errorMessage=${profilingResult.errorMessage}"
)
}
}
val stopSignal = CancellationSignal()
val requestBuilder = SystemTraceRequestBuilder().apply {
setCancellationSignal(stopSignal)
setTag("FOO") // Caller-supplied tag for identification.
setDurationMs(60000) // Hard cap: ends the session if cancel() never fires.
setBufferFillPolicy(BufferFillPolicy.RING_BUFFER)
setBufferSizeKb(32768)
}
// 1. Start the session. This is asynchronous system IPC. The tracing
// engine takes a moment to start and allocate buffers.
requestProfiling(appContext, requestBuilder.build(), callbackExecutor, resultCallback)
// 2. The API exposes no "profiling started" signal, so pad with a short,
// best-effort delay before running the code you care about. This is
// approximate. Increase it on slower or heavily loaded devices.
delay(STARTUP_PADDING_MS)
// 3. The session is already recording every thread in your app. This slice
// doesn't scope what's captured. It just labels this region of the
// timeline so heavyOperation() is easier to find. trace { } closes the
// section even if the block throws.
trace("MyApp:HeavyOperation") {
heavyOperation()
}
// 4. Stop recording. Until this fires or the setDurationMs() cap is
// reached (whichever comes first), the session keeps capturing app-wide
// activity.
stopSignal.cancel()
}
}
) {
Text("Run & Profile Heavy Operation")
}
}
// Best-effort wait for the system trace engine to initialize before profiling.
// There is no deterministic start callback; tune this for your target devices.
private const val STARTUP_PADDING_MS = 100L
fun heavyOperation() {
// Background computations 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(32768);
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();
}
L'exemple de code configure et gère la session de profilage en suivant les étapes suivantes :
Configurer l'exécuteur Créez un
Executorpour définir le thread qui recevra les résultats du profilage. Le profilage s'effectue en arrière-plan. L'utilisation d'un exécuteur de thread non UI permet d'éviter les erreurs « L'application ne répond pas » (ANR) si vous ajoutez ultérieurement un traitement au rappel.Gérer les résultats du profilage Créez un objet
Consumer<ProfilingResult>. Le système utilise cet objet pour renvoyer les résultats du profilage deProfilingManagerà votre application.Créer la requête de profilage Créez un
SystemTraceRequestBuilderpour configurer votre session de profilage. Ce compilateur vous permet de personnaliser les paramètres de traceProfilingManager. La personnalisation du compilateur est facultative. Si vous ne le faites pas, le système utilise les paramètres par défaut.- Définir un tag Utilisez
setTag()pour ajouter un tag au nom de la trace. Ce tag vous aide à identifier la trace. - Facultatif : définir la durée Utilisez
setDurationMs()pour spécifier la durée du profilage en millisecondes. Par exemple,60000définit une trace de 60 secondes. La trace se termine automatiquement après la durée spécifiée siCancellationSignaln'est pas déclenché avant. - Choisir une règle de mémoire tampon Utilisez
setBufferFillPolicy()pour définir le mode de stockage des données de trace.BufferFillPolicy.RING_BUFFERsignifie que lorsque la mémoire tampon est pleine, les nouvelles données écrasent les données les plus anciennes, ce qui permet de conserver un enregistrement continu de l'activité récente. - Définir une taille de mémoire tampon Utilisez
setBufferSizeKb()pour spécifier une taille de mémoire tampon pour le traçage, que vous pouvez utiliser pour contrôler la taille du fichier de trace de sortie.
- Définir un tag Utilisez
Facultatif : gérer le cycle de vie de la session Créez un
CancellationSignal. Cet objet vous permet d'arrêter la session de profilage quand vous le souhaitez, ce qui vous donne un contrôle précis sur sa durée.Démarrer et recevoir les résultats Lorsque vous appelez
requestProfiling(),ProfilingManagerdémarre une session de profilage en arrière-plan. Une fois le profilage terminé, il envoie leProfilingResultà votre méthoderesultCallback#accept. Si le profilage se termine correctement, leProfilingResultfournit le chemin d'accès où la trace a été enregistrée sur votre appareil viaProfilingResult#getResultFilePath. Vous pouvez obtenir ce fichier par programmation ou, pour le profilage local, en exécutantadb pull <trace_path>à partir de votre ordinateur.Ajouter des points de trace personnalisés Vous pouvez ajouter des points de trace personnalisés dans le code de votre application. Dans l'exemple de code précédent, le
trace("MyApp:HeavyOperation") { ... }bloc crée une tranche personnalisée dans le profil généré.