Stack traces no Compose

O Jetpack Compose executa seu código em várias fases diferentes, o que faz com que algumas partes da função @Composable sejam executadas separadamente umas das outras. Falhas nessas fases podem resultar em stack traces difíceis de decifrar, dificultando a identificação da função ou linha de código exata que causou a falha.

Adicionar informações de origem aos rastreamentos de pilha

Para melhorar a legibilidade do stack trace, uma API de ativação oferece detalhes mais completos sobre o local da falha, incluindo nomes e locais combináveis. Assim, você pode:

  • Identificar e resolver com eficiência as fontes de falhas
  • Isolar falhas para amostras reproduzíveis
  • Investigar falhas que antes mostravam apenas frames de pilha internos

O tempo de execução do Compose pode detectar o local da falha na composição e reconstruir um rastreamento de pilha com base na sua hierarquia de @Composable. O stack trace é anexado para falhas em:

  • Composição
  • DisposableEffect e LaunchedEffect (exceto onDispose ou cancelamento)
  • Corrotinas iniciadas em rememberCoroutineScope
  • Medir, criar layout e desenhar passes

Para ativar esse recurso, adicione as seguintes linhas ao ponto de entrada do aplicativo:

// Enable stack traces at application level: onCreate
class SampleStackTracesEnabledApp : Application() {

    override fun onCreate() {
        super.onCreate()
        // Enable Compose stack traces for minified builds only.
        Composer.setDiagnosticStackTraceMode(ComposeStackTraceMode.Auto)

        // Alternatively:
        // Enable verbose Compose stack traces for local debugging
        Composer.setDiagnosticStackTraceMode(ComposeStackTraceMode.SourceInformation)
    }
}

O ideal é fazer essa configuração antes de criar qualquer composição para verificar se as informações de rastreamento de pilha estão sendo coletadas corretamente.

Há quatro opções para o ComposeStackTraceMode:

  • Auto: opção recomendada, porque usa GroupKeys se o app for minimizado e None caso contrário.
  • GroupKeys: os rastreamentos de pilha são criados para apps minimizados. As informações da chave de grupo são mantidas mesmo após a minificação e são usadas com o arquivo de mapeamento do ProGuard emitido pelo compilador do Compose e pelo R8 para reconstruir um local aproximado das funções @Composable. Esses stack traces são menos precisos e otimizados para evitar trabalho adicional no tempo de execução. O compilador do Compose oferece suporte à emissão de mapeamentos adicionais do R8 a partir do Kotlin 2.3.0.
  • SourceInformation: útil para builds não minimizados, coleta informações de origem e as adiciona ao stack trace. Os resultados são mais precisos, mas têm um custo de desempenho significativo, semelhante ao de anexar o inspetor de layout. Eles são criados para serem usados em versões de depuração dos apps e receber leituras precisas sobre uma falha que exige mais informações sobre o local. As informações de origem são removidas dos apps minimizados para otimizar o tamanho e o desempenho do binário.
  • None: nenhum detalhe extra de rastreamento de pilha adicionado.

Ao usar a opção SourceInformation, o rastreamento de pilha aparece como um DiagnosticComposeException na lista de exceções suprimidas:

java.lang.IllegalStateException: Test layout error
    at <original trace>
Suppressed: androidx.compose.runtime.DiagnosticComposeException:
Composition stack when thrown:
    at ReusableComposeNode(Composables.kt:<unknown line>)
    at Layout(Layout.kt:79)
    at <lambda>(TempErrorsTest.kt:164)
    at <lambda>(BoxWithConstraints.kt:66)
    at ReusableContentHost(Composables.kt:164)
    at <lambda>(SubcomposeLayout.kt:514)
    at SubcomposeLayout(SubcomposeLayout.kt:114)
    at SubcomposeLayout(SubcomposeLayout.kt:80)
    at BoxWithConstraints(BoxWithConstraints.kt:64)
    at SubcomposeLayoutErrorComposable(TempErrorsTest.kt:164)
    at <lambda>(TempErrorsTest.kt:86)
    at Content(ComposeView.android.kt:430)
    at <lambda>(ComposeView.android.kt:249)
    at CompositionLocalProvider(CompositionLocal.kt:364)
    at ProvideCommonCompositionLocals(CompositionLocals.kt:193)
    at <lambda>(AndroidCompositionLocals.android.kt:113)
    at CompositionLocalProvider(CompositionLocal.kt:364)
    at ProvideAndroidCompositionLocals(AndroidCompositionLocals.android.kt:102)
    at <lambda>(Wrapper.android.kt:141)
    at CompositionLocalProvider(CompositionLocal.kt:384)
    at <lambda>(Wrapper.android.kt:140)

Limitações conhecidas

Há alguns problemas conhecidos com frames de rastreamento de pilha:

Stack traces de informações da fonte

Números de linha ausentes (<unknown line>) no primeiro frame de pilha para falhas na composição. Como a introspecção das informações de origem acontece após uma falha, os dados da tabela de slots podem estar incompletos e perder o número da linha. ReusableComposeNode e remember não produzem informações de origem. Por isso, você vai encontrar <unknown line> nos frames de pilha dessas funções.

Stack traces de chaves de grupo

Por design, os rastreamentos de pilha baseados em GroupKeys só podem apontar para a primeira linha da função @Composable. Elas também não contêm dados para funções que não produzem um grupo, como funções inline ou que não retornam unidades.

Falhas na coleta de stack traces

Se a coleta de stack trace falhar por qualquer motivo, essa exceção será anexada como uma exceção suprimida em vez de DiagnosticComposeException.

Informe sobre falhas suprimidas ou inconsistências de rastreamento de pilha ao componente Compose Runtime.