Nesta página, mostramos como gravar um rastreamento do sistema usando a API ProfilingManager.
O ProfilingManager também pode gravar outros tipos de perfil. Esse processo é semelhante
a gravar um rastreamento do sistema, mas cada tipo usa um builder diferente. Os
perfis compatíveis e os builders deles são:
Traces do sistema:gravados usando
SystemTraceRequestBuilder, que são úteis para análise de latência e depuração geral de desempenho.Heap dumps:gravados usando
JavaHeapDumpRequestBuilder, que são úteis para detecção e otimização de vazamentos de memória.Perfis de heap:gravados usando
HeapProfileRequestBuilder, que são úteis para otimização de memória.Perfis de pilha de chamadas:gravados 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.2") implementation("androidx.core:core:1.19.0") }
Groovy
dependencies { implementation 'androidx.tracing:tracing:2.0.2' 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 combinável enquanto gerencia 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 exemplo de código configura e gerencia a sessão de criação de perfil seguindo estas etapas:
Configure o executor. Crie um
Executorpara definir a linha de execução que vai receber os resultados do perfil. A criação de perfil acontece em segundo plano. Usar um executor de linha de execução que não seja de UI ajuda a evitar erros de "O app não está respondendo" (ANR) se você adicionar mais processamento ao callback depois.Processar resultados de criação de perfil. Crie um objeto
Consumer<ProfilingResult>. O sistema usa esse objeto para enviar resultados de criação de perfil deProfilingManagerde volta ao seu app.Crie a solicitação de criação de perfil. Crie um
SystemTraceRequestBuilderpara configurar sua sessão de criação de perfil. Com esse builder, é possível personalizar as configurações de rastreamentoProfilingManager. A personalização do builder é opcional. Se você não fizer isso, o sistema usará as configurações padrão.- Defina uma tag. Use
setTag()para adicionar uma tag ao nome do rastreamento. Essa tag ajuda a identificar o rastreamento. - Opcional: defina a duração. Use
setDurationMs()para especificar por quanto tempo fazer o perfil em milissegundos. Por exemplo,60000define um rastreamento de 60 segundos. O rastreamento é encerrado automaticamente após a duração especificada seCancellationSignalnão for acionado antes disso. - Escolha 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 dados mais recentes substituem os mais antigos, mantendo um registro contínuo da atividade recente. - Defina 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 rastreamento de saída.
- Defina uma tag. Use
Opcional: gerencie o ciclo de vida da sessão. Crie um
CancellationSignal. Com esse objeto, você pode interromper a sessão de criação de perfil quando quiser, tendo controle preciso sobre a duração dela.Comece e receba resultados. Quando você chama
requestProfiling(),ProfilingManagerinicia uma sessão de criação de perfil em segundo plano. Depois que o criação de perfil é concluída, ela envia oProfilingResultpara seu métodoresultCallback#accept. Se o perfil for concluído com êxito, oProfilingResultvai fornecer o caminho em que o rastreamento foi salvo no dispositivo peloProfilingResult#getResultFilePath. Você pode receber esse arquivo de forma programática ou, para criação de perfil local, executandoadb pull <trace_path>no seu computador.Adicione pontos de trace personalizados. É possível adicionar pontos de trace personalizados no código do app. No exemplo de código anterior, o bloco
trace("MyApp:HeavyOperation") { ... }cria uma fração personalizada no perfil gerado.