Esta página mostra como registrar um rastro do sistema usando a API ProfilingManager.
ProfilingManager também pode registrar outros tipos de perfil. Esse processo é semelhante ao registro de um rastro do sistema, mas cada tipo usa um builder diferente. Os perfis com suporte e os builders são:
Rastros do sistema:registrados usando
SystemTraceRequestBuilder, que são úteis para análise de latência e depuração geral de performance.Heap dumps: registrados usando
JavaHeapDumpRequestBuilder, que são úteis para detecção e otimização de vazamento de memória.Perfis de heap: registrados usando
HeapProfileRequestBuilder, que são úteis para otimização de memória.Perfis de pilha de chamadas: registrados usando
StackSamplingRequestBuilder, que são úteis para entender a execução de código e a análise de latência.
Adicionar dependências
Para ter a melhor experiência com a API ProfilingManager, adicione as seguintes bibliotecas do Jetpack ao arquivo 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' }
Registrar um rastro do sistema
Depois de adicionar as dependências necessárias, use o código a seguir para registrar um rastro do sistema. Este exemplo mostra como iniciar uma sessão de criação de perfil de um elemento combinável, gerenciando com segurança operações pesadas fora da linha de execução 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();
}
O código de exemplo configura e gerencia a sessão de criação de perfil seguindo estas etapas:
Configurar o executor. Crie um
Executorpara definir a linha de execução que vai receber os resultados da criação de perfil. A criação de perfil acontece em segundo plano. O uso de um executor de linha de execução não relacionada à interface ajuda a evitar erros de "O app não está respondendo" (ANR) se você adicionar mais processamento ao callback mais tarde.Processar resultados da criação de perfil. Crie um objeto
Consumer<ProfilingResult>. O sistema usa esse objeto para enviar resultados da criação de perfil deProfilingManagerde volta ao seu app.Criar a solicitação de criação de perfil. Crie um
SystemTraceRequestBuilderpara configurar a sessão de criação de perfil. Esse builder permite personalizar as configurações de rastreamento deProfilingManager. A personalização do builder é opcional. Se você não fizer isso, o sistema vai usar as configurações padrão.- Definir uma tag. Use
setTag()para adicionar uma tag ao nome do rastro. Essa tag ajuda a identificar o rastro. - Opcional: definir a duração. Use
setDurationMs()para especificar por quanto tempo criar o perfil em milissegundos. Por exemplo,60000define um rastro de 60 segundos. O rastro termina automaticamente após a duração especificada seCancellationSignalnão for acionado antes disso. - Escolher uma política de buffer. Use
setBufferFillPolicy()para definir como os dados de rastreamento são armazenados.BufferFillPolicy.RING_BUFFERsignifica que, quando o buffer está cheio, os novos dados substituem os mais antigos, mantendo um registro contínuo da atividade recente. - Definir um tamanho de buffer. Use
setBufferSizeKb()para especificar um tamanho de buffer para rastreamento que pode ser usado para controlar o tamanho do arquivo de rastro de saída.
- Definir uma tag. Use
Opcional: gerenciar o ciclo de vida da sessão. Crie um
CancellationSignal. Esse objeto permite interromper a sessão de criação de perfil quando quiser, oferecendo controle preciso sobre a duração.Iniciar e receber resultados. Ao chamar
requestProfiling(),ProfilingManagerinicia uma sessão de criação de perfil em segundo plano. Quando a criação de perfil é concluída, ela envia oProfilingResultpara o métodoresultCallback#accept. Se a criação de perfil for concluída, oProfilingResultvai fornecer o caminho em que o rastro foi salvo no dispositivo usandoProfilingResult#getResultFilePath. É possível acessar esse arquivo de maneira programática ou, para criação de perfil local, executandoadb pull <trace_path>no computador.Adicionar pontos de rastreamento personalizados. É possível adicionar pontos de rastreamento personalizados no código do app. No exemplo de código anterior, o
trace("MyApp:HeavyOperation") { ... }bloco cria uma divisão personalizada no perfil gerado.