Compose のスタック トレース

Jetpack Compose は、コードを複数の異なるフェーズで実行するため、@Composable 関数のコードの一部が別々に実行されます。これらのフェーズでクラッシュが発生すると、解読が難しいスタック トレースが生成され、クラッシュの原因となった関数やコード行を正確に特定することが困難になります。

スタック トレースにソース情報を追加する

スタック トレースの読みやすさを向上させるため、オプトイン API では、コンポーザブルの名前や場所など、クラッシュの場所に関する詳細な情報が提供されます。これにより、次のことが可能になります。

  • クラッシュの原因を効率的に特定して解決する
  • 再現可能なサンプルでクラッシュを分離する
  • 以前は内部スタック フレームのみが表示されていたクラッシュを調査

Compose ランタイムは、コンポジション内のクラッシュの場所を検出し、@Composable 階層に基づいてスタック トレースを再構築できます。スタック トレースは、次のクラッシュに対して追加されます。

  • 合成
  • DisposableEffectLaunchedEffectonDispose またはキャンセルを除く)
  • rememberCoroutineScope で起動されたコルーチン
  • パスの測定、レイアウト、描画

この機能を有効にするには、アプリケーションのエントリ ポイントに次の行を追加します。

// 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)
    }
}

理想的には、この構成はコンポジションを作成する前に行い、スタック トレース情報が正しく収集されることを確認します。

ComposeStackTraceMode には次の 4 つのオプションがあります。

  • Auto: アプリが縮小化されている場合は GroupKeys を使用し、それ以外の場合は None を使用するため、推奨されるオプションです。
  • GroupKeys: 縮小化されたアプリのスタック トレースが作成されます。グループキー情報は、最小化後も保持され、Compose コンパイラと R8 によって出力された ProGuard マッピング ファイルとともに使用され、@Composable 関数の大まかな場所を再構築します。これらのスタック トレースは精度が低く、実行時に追加の作業を行わないように最適化されています。Compose コンパイラは、Kotlin 2.3.0 以降で追加の R8 マッピングの出力に対応しています。
  • SourceInformation: 最小化されていないビルドに便利です。ソース情報を収集してスタック トレースに追加します。結果はより正確になりますが、レイアウト インスペクタをアタッチするのと同様に、パフォーマンス コストが大幅に発生します。これらは、アプリのデバッグ バージョンで使用され、クラッシュの場所に関する詳細な情報を必要とするクラッシュについて正確な読み取りを行うために作成されます。バイナリサイズとパフォーマンスを最適化するため、ソース情報は縮小化されたアプリから削除されます。
  • None: スタック トレースの詳細が追加されていません。

SourceInformation オプションを使用すると、スタック トレースは抑制された例外リストの DiagnosticComposeException として表示されます。

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)

既知の制限事項

スタック トレース フレームには、既知の問題がいくつかあります。

ソース情報のスタック トレース

コンポジションのクラッシュの最初のスタック フレームに、行番号(<unknown line>)がありません。ソース情報のイントロスペクションはクラッシュ後に発生するため、スロットテーブルのデータが不完全になり、行番号が削除される可能性があります。ReusableComposeNoderemember はソース情報を生成しないため、これらの関数のスタック フレームには <unknown line> が表示されます。

グループキーのスタック トレース

GroupKeys ベースのスタック トレースは、設計上、@Composable 関数の最初の行しか指すことができません。また、グループを生成しない関数(インライン関数や Unit を返さない関数など)のデータも含まれません。

スタック トレースの収集がクラッシュする

スタック トレースの収集がなんらかの理由でクラッシュした場合、その例外は DiagnosticComposeException ではなく、抑制された例外として追加されます。

抑制されたクラッシュやスタック トレースの不整合は、Compose ランタイム コンポーネントに報告してください。