[Android] R8 retrace 실행하는 방법

이지훈·2024년 5월 9일
1
post-custom-banner

서두

debug 에선 문제 없던 프로젝트가, 프로가드를 적용한 release build 를 진행할 경우 문제가 발생하는 경우가 많은데, 그럴 때 활용 해볼 수 있는 R8 retrace 의 사용 방법을 알아보도록 하겠다.

본론

우선 프로가드에 의해 문제가 생긴 경우, 다음과 같이 에러 문구를 확인하여도 그 의미를 파악하기 쉽지 않은 경우가 많다.

FATAL EXCEPTION: main
Process: com.unifest.android, PID: 23793                     
qb.i: Function 'onAction' (JVM signature: onAction(Lcom/unifest/android/feature/intro/viewmodel/IntroUiAction;)V) not resolved in class ra.r: no members found  
at r.q0.b(Unknown Source:555)
at nc.v1.b(Unknown Source:18)                                                   
at nc.j0.r(Unknown Source:7)                                                
at nc.j0.toString(Unknown Source:2)                                             
at ec.h.toString(Unknown Source:12)                                             
at java.lang.String.valueOf(String.java:4092)  
...

이럴 때 이제 사용 해볼 수 있는게 R8 retrace 인데, 암호화된 stack trace 를 복호화하여 에러의 원인을(original stack trace) 알려주는 도구이다.

이런식으로 사용법을 제시해주는데 솔직히 이것만 보고는 어떻게 채워넣으라는 것인지 이해를 하지 못했다. 부연 설명이 필요할 것 같아서(글을 작성하는 이유) 첨언 하자면,

  1. path-to-mapping-file
    우선 mapping 파일은 아래의 위치에 존재한다. app 을 빌드하면 생성되는 폴더 내에 위치를 잘 적어주면 된다.

    내 경우엔 다음과 같은 경로로 설정하면 되었다.

    /Users/{username}/StudioProjects/{projectname}/app/build/outputs/mapping/release/mapping.txt

  1. path-to-stack-trace-file
    이게 좀 골치가 아팠는데,
    에러 로그는 발생했는데, 이게 파일이 있다고? 라는 생각이 들어서 찾아본 결과
    에러 로그를 복사 붙혀넣기 한 txt 파일의 경로를 적으라는 것 이었다.

에러로그를 전체 복사 한 뒤에, 이를 obfuscated-stacktrace.txt 라는 파일에 저장해서 프로젝트 root 위치에 넣어주고 이를 터미널 내에 커멘드에 포함시켜 주었다

retrace /Users/{username}/StudioProjects/{projectname}/app/build/outputs/mapping/release/mapping.txt obfuscated-stacktrace.txt

window 일 경우 메모장을 켜서 간단하게 텍스트 파일을 만들어서 저장할 수 있지만, mac 환경에선 아래 블로그의 방법을 활용하면 텍스트 파일을 만들 수 있다.
https://m.blog.naver.com/kim870103/221876294140

아직도 어려운 맥북

  1. option

옵션 같은 경우엔 공식문서에서 처럼 위의 선택지 중 필요한 하나를 선택하거나, 생략해도 무방하다.

결과

암호화되어 보이지 않던 에러가 다음과 같이 이해할 수 있는 형태의 에러 로그로 변한 것을 확인할 수 있었다.

compose runtime internal 관련 문제라는 것을 확인할 수 있었다. 본게임 시작

at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(Unknown Source:109)                                                                                                            at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(Unknown Source:35)
at androidx.compose.ui.platform.ComposeView.Content(Unknown Source:428)
at androidx.compose.ui.platform.AbstractComposeView$ensureCompositionCreated$1.invoke(Unknown Source:252)                                                                                                       at androidx.compose.ui.platform.AbstractComposeView$ensureCompositionCreated$1.invoke$bridge(Unknown Source:0)
at androidx.compose.ui.platform.AbstractComposeView$ensureCompositionCreated$1.invoke(Unknown Source:251)                                                                                                       at androidx.compose.animation.AnimatedContentKt$AnimatedContent$6$1$4$1.invoke$bridge(Unknown Source:0)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(Unknown Source:109)                                                                                                            at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(Unknown Source:35)
at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(Unknown Source:228)
at androidx.compose.ui.platform.CompositionLocalsKt.ProvideCommonCompositionLocals(Unknown Source:186)
at androidx.compose.ui.platform.AndroidCompositionLocals_androidKt$ProvideAndroidCompositionLocals$3.invoke(Unknown Source:119)                                                                                                         at androidx.compose.material.ButtonKt$Button$3.invoke$bridge(Unknown Source:0)
at androidx.compose.ui.platform.AndroidCompositionLocals_androidKt$ProvideAndroidCompositionLocals$3.invoke(Unknown Source:118)                                                                                                         at androidx.compose.foundation.lazy.grid.LazyGridDslKt$rememberColumnWidthSums$1$1.invoke$bridge(Unknown Source:0)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(Unknown Source:109)                                                                                                            at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(Unknown Source:35)
at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(Unknown Source:228)
at androidx.compose.ui.platform.AndroidCompositionLocals_androidKt.ProvideAndroidCompositionLocals(Unknown Source:110)
at androidx.compose.ui.platform.WrappedComposition$setContent$1$1$2.invoke(Unknown Source:139)                                                                                                          at androidx.compose.ui.platform.WrappedComposition$setContent$1$1$2.invoke$bridge(Unknown Source:0)
at androidx.compose.ui.platform.WrappedComposition$setContent$1$1$2.invoke(Unknown Source:138)                                                                                                          at androidx.compose.ui.platform.WrappedComposition$setContent$1$1$2.invoke$bridge(Unknown Source:0)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(Unknown Source:109)                                                                                                            at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(Unknown Source:35)
at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(Unknown Source:248)
at androidx.compose.ui.platform.WrappedComposition$setContent$1$1.invoke(Unknown Source:138)                                                                                                            at androidx.compose.ui.platform.WrappedComposition$setContent$1$1$2.invoke$bridge(Unknown Source:0)
at androidx.compose.ui.platform.WrappedComposition$setContent$1$1.invoke(Unknown Source:123)                                                                                                            at androidx.compose.ui.platform.WrappedComposition$setContent$1$1$2.invoke$bridge(Unknown Source:0)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(Unknown Source:109)                                                                                                            at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(Unknown Source:35)
at androidx.compose.runtime.ActualJvm_jvmKt.invokeComposable(Unknown Source:90)
at androidx.compose.runtime.ComposerImpl.doCompose(Unknown Source:3302)
at androidx.compose.runtime.ComposerImpl.composeContent$runtime_release(Unknown Source:3235)
at androidx.compose.runtime.CompositionImpl.composeContent(Unknown Source:725)
at androidx.compose.runtime.Recomposer.composeInitial$runtime_release(Unknown Source:1071)
at androidx.compose.runtime.CompositionImpl.composeInitial(Unknown Source:633)
at androidx.compose.runtime.CompositionImpl.setContent(Unknown Source:619)
at androidx.compose.ui.platform.WrappedComposition$setContent$1.invoke(Unknown Source:123)                                                                                                      at androidx.compose.ui.platform.WrappedComposition$setContent$1.invoke(Unknown Source:114)                                                                                                      at androidx.compose.foundation.BorderModifierNode$drawGenericBorder$1.invoke$bridge(Unknown Source:0)
at androidx.compose.ui.platform.AndroidComposeView.setOnViewTreeOwnersAvailable(Unknown Source:1289)
at androidx.compose.ui.platform.WrappedComposition.setContent(Unknown Source:114)
at androidx.compose.ui.platform.WrappedComposition.onStateChanged(Unknown Source:164)
at androidx.lifecycle.LifecycleRegistry$ObserverWithState.dispatchEvent(Unknown Source:320)
at androidx.lifecycle.LifecycleRegistry.addObserver(Unknown Source:198)
at androidx.compose.ui.platform.WrappedComposition$setContent$1.invoke(Unknown Source:121)                                                                                                      at androidx.compose.ui.platform.WrappedComposition$setContent$1.invoke(Unknown Source:114)                                                                                                      at androidx.compose.foundation.BorderModifierNode$drawGenericBorder$1.invoke$bridge(Unknown Source:0)
at androidx.compose.ui.platform.AndroidComposeView.onAttachedToWindow(Unknown Source:1364)
at android.view.View.dispatchAttachedToWindow(View.java:23215)
at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3698)
at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3705)
at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3705)
at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3705)
at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3705)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:3883)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:3275)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:11257)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1650)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1659)
at android.view.Choreographer.doCallbacks(Choreographer.java:1129)
at android.view.Choreographer.doFrame(Choreographer.java:1055)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1622)
at android.os.Handler.handleCallback(Handler.java:958)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loopOnce(Looper.java:230)
at android.os.Looper.loop(Looper.java:319)
at android.app.ActivityThread.main(ActivityThread.java:8913)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:608)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1103)

프로가드 때문에 고생하시는 분들 화이팅 입니다..!

profile
실력은 고통의 총합이다. Android Developer
post-custom-banner

0개의 댓글