メモ: R8 で難読化されたスタックトレースをすばやく元に戻すやつ

R8 retrace というツールを使うことで難読化されたスタックトレースを戻すことができる:

https://developer.android.com/tools/retrace

## 入手

command-line tools として SDK に含まれているのでそれが使えると便利。僕の環境だとここにあります:

```sh
~/Library/Android/sdk/cmdline-tools/latest/bin/retrace
```

or スタンドアローンで適当なバージョンのものを DL して使うことができるという具合です:

```sh
wget https://storage.googleapis.com/r8-releases/raw/8.1.50/r8retrace.jar
```

## 使い方

実行するコマンドはこういう具合です:

```sh
retrace mapping.txt <stacktrace-file>
```

or DL した jar を使う場合はこういうかんじ:

```sh
java -cp r8retrace.jar com.android.tools.r8.retrace.Retrace mapping.txt <stacktrace-file>
```

`<stacktrace-file>` はスタックトレースをコピーするなりして適当なファイルにまとめておきます。指定しない場合は標準入力でスタックトレースを入力することもできます。

`mapping.txt` は対象の build variant のビルドで出力されたものを指定する必要があります:

```
app/build/outputs/mapping/<yourapp>/mapping.txt
```

たとえばこういうスタックトレースがあるとしたら:

```
FATAL EXCEPTION: main
Process: io.github.manganoito.myapp20250203, PID: 10207
java.lang.IllegalStateException: test error
	at a.a.b(SourceFile:81)
	at G0.a.c(SourceFile:53)
	at z.a.a(SourceFile:44)
	at z.a.c(SourceFile:9)
	at q.l.g(SourceFile:220)
	at z.a.g(SourceFile:52)
	at U.i1.g(SourceFile:125)
	at z.a.g(SourceFile:52)
	at s.d.w(SourceFile:15)
	at s.o.k(SourceFile:197)
	at s.s.i(SourceFile:33)
	at s.n0.a(SourceFile:45)
	at s.n.a(SourceFile:5)
	at s.s.j(SourceFile:7)
	at R.p.h(SourceFile:22)
	at R.p.g(SourceFile:120)
	at R.k.d(SourceFile:184)
	at q.o.g(SourceFile:881)
	at R.m.a(SourceFile:72)
	at T.q.e(SourceFile:42)
	at B.t.f(SourceFile:277)
	at a.a.H(SourceFile:60)
	at B.r.a(SourceFile:48)
	at T.b0.a(SourceFile:145)
	at T.G.N(SourceFile:126)
	at T.G.e(SourceFile:92)
	at m.e.a(SourceFile:56)
	at T.q.e(SourceFile:42)
	at I.u.v(SourceFile:1)
	at T.v.e(SourceFile:11)
	at I.u.v(SourceFile:1)
	at T.v.e(SourceFile:11)
	at m.i.v(SourceFile:117)
	at T.v.e(SourceFile:11)
	at B.t.f(SourceFile:277)
	at a.a.H(SourceFile:60)
	at B.r.a(SourceFile:48)
	at T.b0.a(SourceFile:145)
	at T.G.N(SourceFile:126)
	at T.G.e(SourceFile:92)
	at R.B.a(SourceFile:39)
	at T.q.e(SourceFile:42)
	at B.t.f(SourceFile:277)
	at a.a.H(SourceFile:118)
	at B.r.a(SourceFile:48)
	at T.b0.a(SourceFile:145)
	at T.G.N(SourceFile:126)
	at T.N.c(SourceFile:17)
	at T.N.l(SourceFile:18)
	at T.N.i(SourceFile:62)
	at U.u.onMeasure(SourceFile:84)
	at android.view.View.measure(View.java:26496)
	at U.a.onMeasure(SourceFile:67)
	at android.view.View.measure(View.java:26496)
	at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6981)
	at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
	at android.view.View.measure(View.java:26496)
	at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6981)
	at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1608)
	at android.widget.LinearLayout.measureVertical(LinearLayout.java:878)
	at android.widget.LinearLayout.onMeasure(LinearLayout.java:721)
	at android.view.View.measure(View.java:26496)
	at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6981)
	at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
	at com.android.internal.policy.DecorView.onMeasure(DecorView.java:760)
	at android.view.View.measure(View.java:26496)
	at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:4042)
	at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:2658)
	at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2964)
	at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:2371)
	at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:9297)
	at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1231)
	at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1239)
	at android.view.Choreographer.doCallbacks(Choreographer.java:899)
	at android.view.Choreographer.doFrame(Choreographer.java:832)
	at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1214)
	at android.os.Handler.handleCallback(Handler.java:942)
	at android.os.Handler.dispatchMessage(Handler.java:99)
	at android.os.Looper.loopOnce(Looper.java:201)
	at android.os.Looper.loop(Looper.java:288)
	at android.app.ActivityThread.main(ActivityThread.java:7924)
	at java.lang.reflect.Method.invoke(Native Method)
	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936)
```

出力はこうなります:

```sh
~/Library/Android/sdk/cmdline-tools/latest/bin/retrace app/build/outputs/mapping/release/mapping.txt trace.log

FATAL EXCEPTION: main
Process: io.github.manganoito.myapp20250203, PID: 10207
java.lang.IllegalStateException: test error
	at io.github.manganoito.myapp20250203.MainActivityKt.Greeting(MainActivity.kt:35)
	at io.github.manganoito.myapp20250203.ComposableSingletons$MainActivityKt$lambda-1$1.invoke(MainActivity.kt:23)
	at io.github.manganoito.myapp20250203.ComposableSingletons$MainActivityKt$lambda-1$1.invoke(MainActivity.kt:22)
	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:118)
	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:35)
	at androidx.compose.material3.ScaffoldKt$ScaffoldLayoutWithMeasureFix$1$1$bodyContentPlaceables$1.invoke(Scaffold.kt:303)
	at androidx.compose.material3.ScaffoldKt$ScaffoldLayoutWithMeasureFix$1$1$bodyContentPlaceables$1.invoke(Scaffold.kt:285)
	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:109)
	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:35)
	at androidx.compose.ui.layout.LayoutNodeSubcompositionsState$subcompose$3$1$1.invoke(LayoutNodeSubcompositionsState.java:991)
	at androidx.compose.ui.layout.LayoutNodeSubcompositionsState$subcompose$3$1$1.invoke(LayoutNodeSubcompositionsState.java:477)
	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:109)
	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:35)
	at androidx.compose.runtime.ActualJvm_jvmKt.invokeComposable(ActualJvm_jvm.kt:97)
	at androidx.compose.runtime.ComposerImpl.doCompose(Composer.kt:3593)
	at androidx.compose.runtime.ComposerImpl.composeContent$runtime_release(Composer.kt:3520)
	at androidx.compose.runtime.CompositionImpl.composeContent(Composition.kt:743)
	at androidx.compose.runtime.Recomposer.composeInitial$runtime_release(Recomposer.kt:1122)
	at androidx.compose.runtime.ComposerImpl$CompositionContextImpl.composeInitial$runtime_release(Composer.kt:3874)
	at androidx.compose.runtime.CompositionImpl.composeInitial(Composition.kt:649)
	at androidx.compose.runtime.CompositionImpl.setContent(Composition.kt:635)
	at androidx.compose.ui.layout.LayoutNodeSubcompositionsState.subcomposeInto(SubcomposeLayout.kt:500)
	at androidx.compose.ui.layout.LayoutNodeSubcompositionsState.subcompose(SubcomposeLayout.kt:472)
	at androidx.compose.ui.layout.LayoutNodeSubcompositionsState.subcompose(SubcomposeLayout.kt:463)
	at androidx.compose.ui.layout.LayoutNodeSubcompositionsState.subcompose(SubcomposeLayout.kt:447)
	at androidx.compose.ui.layout.LayoutNodeSubcompositionsState$Scope.subcompose(SubcomposeLayout.kt:872)
	at androidx.compose.material3.ScaffoldKt$ScaffoldLayoutWithMeasureFix$1$1.invoke-0kLqBqw(Scaffold.kt:285)
	at androidx.compose.material3.ScaffoldKt$ScaffoldLayoutWithMeasureFix$1$1.invoke(Scaffold.kt:179)
	at androidx.compose.ui.layout.LayoutNodeSubcompositionsState$createMeasurePolicy$1.measure-3p2s80s(SubcomposeLayout.kt:709)
	at androidx.compose.ui.node.InnerNodeCoordinator.measure-BRTryo0(InnerNodeCoordinator.kt:126)
	at androidx.compose.ui.node.LayoutNodeLayoutDelegate$performMeasureBlock$1.invoke(LayoutNodeLayoutDelegate.java:252)
	at androidx.compose.ui.node.LayoutNodeLayoutDelegate$performMeasureBlock$1.invoke(LayoutNodeLayoutDelegate.java:251)
	at androidx.compose.runtime.snapshots.Snapshot$Companion.observe(Snapshot.java:503)
	at androidx.compose.runtime.snapshots.SnapshotStateObserver$ObservedScopeMap.observe(SnapshotStateObserver.kt:502)
	at androidx.compose.runtime.snapshots.SnapshotStateObserver.observeReads(SnapshotStateObserver.kt:258)
	at androidx.compose.ui.node.OwnerSnapshotObserver.observeReads$ui_release(OwnerSnapshotObserver.kt:133)
	at androidx.compose.ui.node.OwnerSnapshotObserver.observeMeasureSnapshotReads$ui_release(OwnerSnapshotObserver.kt:113)
	at androidx.compose.ui.node.LayoutNodeLayoutDelegate.performMeasure-BRTryo0(LayoutNodeLayoutDelegate.kt:1617)
	at androidx.compose.ui.node.LayoutNodeLayoutDelegate.access$performMeasure-BRTryo0(LayoutNodeLayoutDelegate.kt:36)
	at androidx.compose.ui.node.LayoutNodeLayoutDelegate$MeasurePassDelegate.remeasure-BRTryo0(LayoutNodeLayoutDelegate.kt:620)
	at androidx.compose.ui.node.LayoutNodeLayoutDelegate$MeasurePassDelegate.measure-BRTryo0(LayoutNodeLayoutDelegate.kt:596)
	at androidx.compose.foundation.layout.BoxMeasurePolicy.measure-3p2s80s(Box.kt:122)
	at androidx.compose.ui.node.InnerNodeCoordinator.measure-BRTryo0(InnerNodeCoordinator.kt:126)
	at androidx.compose.ui.graphics.SimpleGraphicsLayerModifier.measure-3p2s80s(GraphicsLayerModifier.kt:646)
	at androidx.compose.ui.node.LayoutModifierNodeCoordinator.measure-BRTryo0(LayoutModifierNodeCoordinator.kt:116)
	at androidx.compose.ui.graphics.SimpleGraphicsLayerModifier.measure-3p2s80s(GraphicsLayerModifier.kt:646)
	at androidx.compose.ui.node.LayoutModifierNodeCoordinator.measure-BRTryo0(LayoutModifierNodeCoordinator.kt:116)
	at androidx.compose.foundation.layout.FillNode.measure-3p2s80s(Size.kt:699)
	at androidx.compose.ui.node.LayoutModifierNodeCoordinator.measure-BRTryo0(LayoutModifierNodeCoordinator.kt:116)
	at androidx.compose.ui.node.LayoutNodeLayoutDelegate$performMeasureBlock$1.invoke(LayoutNodeLayoutDelegate.java:252)
	at androidx.compose.ui.node.LayoutNodeLayoutDelegate$performMeasureBlock$1.invoke(LayoutNodeLayoutDelegate.java:251)
	at androidx.compose.runtime.snapshots.Snapshot$Companion.observe(Snapshot.java:503)
	at androidx.compose.runtime.snapshots.SnapshotStateObserver$ObservedScopeMap.observe(SnapshotStateObserver.kt:502)
	at androidx.compose.runtime.snapshots.SnapshotStateObserver.observeReads(SnapshotStateObserver.kt:258)
	at androidx.compose.ui.node.OwnerSnapshotObserver.observeReads$ui_release(OwnerSnapshotObserver.kt:133)
	at androidx.compose.ui.node.OwnerSnapshotObserver.observeMeasureSnapshotReads$ui_release(OwnerSnapshotObserver.kt:113)
	at androidx.compose.ui.node.LayoutNodeLayoutDelegate.performMeasure-BRTryo0(LayoutNodeLayoutDelegate.kt:1617)
	at androidx.compose.ui.node.LayoutNodeLayoutDelegate.access$performMeasure-BRTryo0(LayoutNodeLayoutDelegate.kt:36)
	at androidx.compose.ui.node.LayoutNodeLayoutDelegate$MeasurePassDelegate.remeasure-BRTryo0(LayoutNodeLayoutDelegate.kt:620)
	at androidx.compose.ui.node.LayoutNodeLayoutDelegate$MeasurePassDelegate.measure-BRTryo0(LayoutNodeLayoutDelegate.kt:596)
	at androidx.compose.ui.layout.RootMeasurePolicy.measure-3p2s80s(RootMeasurePolicy.kt:38)
	at androidx.compose.ui.node.InnerNodeCoordinator.measure-BRTryo0(InnerNodeCoordinator.kt:126)
	at androidx.compose.ui.node.LayoutNodeLayoutDelegate$performMeasureBlock$1.invoke(LayoutNodeLayoutDelegate.java:252)
	at androidx.compose.ui.node.LayoutNodeLayoutDelegate$performMeasureBlock$1.invoke(LayoutNodeLayoutDelegate.java:251)
	at androidx.compose.runtime.snapshots.Snapshot$Companion.observe(Snapshot.java:2441)
	at androidx.compose.runtime.snapshots.SnapshotStateObserver$ObservedScopeMap.observe(SnapshotStateObserver.kt:502)
	at androidx.compose.runtime.snapshots.SnapshotStateObserver.observeReads(SnapshotStateObserver.kt:258)
	at androidx.compose.ui.node.OwnerSnapshotObserver.observeReads$ui_release(OwnerSnapshotObserver.kt:133)
	at androidx.compose.ui.node.OwnerSnapshotObserver.observeMeasureSnapshotReads$ui_release(OwnerSnapshotObserver.kt:113)
	at androidx.compose.ui.node.LayoutNodeLayoutDelegate.performMeasure-BRTryo0(LayoutNodeLayoutDelegate.kt:1617)
	at androidx.compose.ui.node.LayoutNodeLayoutDelegate.access$performMeasure-BRTryo0(LayoutNodeLayoutDelegate.kt:36)
	at androidx.compose.ui.node.LayoutNodeLayoutDelegate$MeasurePassDelegate.remeasure-BRTryo0(LayoutNodeLayoutDelegate.kt:620)
	at androidx.compose.ui.node.LayoutNode.remeasure-_Sx5XlM$ui_release(LayoutNode.kt:1145)
	at androidx.compose.ui.node.MeasureAndLayoutDelegate.doRemeasure-sdFAvZA(MeasureAndLayoutDelegate.kt:354)
	at androidx.compose.ui.node.MeasureAndLayoutDelegate.remeasureOnly(MeasureAndLayoutDelegate.kt:562)
	at androidx.compose.ui.node.MeasureAndLayoutDelegate.measureOnly(MeasureAndLayoutDelegate.kt:407)
	at androidx.compose.ui.platform.AndroidComposeView.onMeasure(AndroidComposeView.android.kt:1058)
	at android.view.View.measure(View.java:26496)
	at androidx.compose.ui.platform.AbstractComposeView.internalOnMeasure$ui_release(ComposeView.android.kt:302)
	at androidx.compose.ui.platform.AbstractComposeView.onMeasure(ComposeView.android.kt:289)
	at android.view.View.measure(View.java:26496)
	at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6981)
	at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
	at android.view.View.measure(View.java:26496)
	at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6981)
	at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1608)
	at android.widget.LinearLayout.measureVertical(LinearLayout.java:878)
	at android.widget.LinearLayout.onMeasure(LinearLayout.java:721)
	at android.view.View.measure(View.java:26496)
	at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6981)
	at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
	at com.android.internal.policy.DecorView.onMeasure(DecorView.java:760)
	at android.view.View.measure(View.java:26496)
	at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:4042)
	at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:2658)
	at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2964)
	at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:2371)
	at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:9297)
	at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1231)
	at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1239)
	at android.view.Choreographer.doCallbacks(Choreographer.java:899)
	at android.view.Choreographer.doFrame(Choreographer.java:832)
	at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1214)
	at android.os.Handler.handleCallback(Handler.java:942)
	at android.os.Handler.dispatchMessage(Handler.java:99)
	at android.os.Looper.loopOnce(Looper.java:201)
	at android.os.Looper.loop(Looper.java:288)
	at android.app.ActivityThread.main(ActivityThread.java:7924)
	at java.lang.reflect.Method.invoke(Native Method)
	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936)
```

期待通りというかんじで以上です。ちなみに標準入力経由の場合は入力後 (Mac) Control + D / (Win/Linux) Control + Z + Enter で出力できます。

## 経緯

https://issuetracker.google.com/issues/284177365#comment19 をみていて、恥ずかしながらこれで初めて知った次第です。