androiddeobfuscationline-numbersandroid-r8

How can I get correct line numbers using retrace after R8 obfuscation?


I am using Android Studio 4.1.3 and its various bundled tools (AGP 4.1.3, Gradle 6.5, Android SDK Build Tools 31-rc2, Platform Tools 31.0.1, SDK Tools 26.1.1). I am using the default R8 tool.

I am obfuscating release builds like this:

release {
   // Enables code shrinking, obfuscation, and optimization for only
   // your project's release build type.
   minifyEnabled true

   // Enables resource shrinking, which is performed by the
   // Android Gradle plugin.
   shrinkResources false

   // Includes the default ProGuard rules files that are packaged with
   // the Android Gradle plugin. To learn more, go to the section about
   // R8 configuration files.
   proguardFiles getDefaultProguardFile(
           'proguard-android.txt'),
           'proguard-rules.pro'
}

My Proguard-rules.pro file has this at the top:

# hide the original source file name.
-renamesourcefileattribute SourceFile

-keepattributes Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,*Annotation*,EnclosingMethod

After the release build is created, here is an example stack trace - the first line shows that it obfuscated the class name (SplashActivity$a), renamed the source to 'SourceFile', and obfuscated the line number (2):

at com.reddragon.intouch.ui.SplashActivity$a.onStart(SourceFile:2)
at io.reactivex.rxjava3.observers.DisposableObserver.onSubscribe(SourceFile:2)
at io.reactivex.rxjava3.internal.operators.observable.ObservableObserveOn$ObserveOnObserver.onSubscribe(SourceFile:15)
at io.reactivex.rxjava3.internal.operators.observable.ObservableSubscribeOn.subscribeActual(SourceFile:2)
at io.reactivex.rxjava3.core.Observable.subscribe(SourceFile:12)
at io.reactivex.rxjava3.internal.operators.observable.ObservableObserveOn.subscribeActual(SourceFile:4)
at io.reactivex.rxjava3.core.Observable.subscribe(SourceFile:12)
at io.reactivex.rxjava3.core.Observable.subscribeWith(SourceFile:1)
at com.reddragon.intouch.ui.SplashActivity.onCreate(SourceFile:16)
at android.app.Activity.performCreate(Activity.java:7183)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1220)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2910)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3032)
at android.app.ActivityThread.-wrap11(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1696)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6944)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:327)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1374)

Using the retrace.bat tool (located in ~\SDK\tools\proguard\bin) with the appropriate mappings.txt file from the release build against that stack trace results in this output:

at com.reddragon.intouch.ui.SplashActivity$1.void onStart()(SourceFile:2)
at io.reactivex.rxjava3.observers.DisposableObserver.void onSubscribe(io.reactivex.rxjava3.disposables.Disposable)(SourceFile:2)
at io.reactivex.rxjava3.internal.operators.observable.ObservableObserveOn$ObserveOnObserver.void onSubscribe(io.reactivex.rxjava3.disposables.Disposable)(SourceFile:15)
at io.reactivex.rxjava3.internal.operators.observable.ObservableSubscribeOn.void subscribeActual(io.reactivex.rxjava3.core.Observer)(SourceFile:2)
at io.reactivex.rxjava3.core.Observable.void subscribe(io.reactivex.rxjava3.core.Observer)(SourceFile:12)
at io.reactivex.rxjava3.internal.operators.observable.ObservableObserveOn.void subscribeActual(io.reactivex.rxjava3.core.Observer)(SourceFile:4)
at io.reactivex.rxjava3.core.Observable.void subscribe(io.reactivex.rxjava3.core.Observer)(SourceFile:12)
at io.reactivex.rxjava3.core.Observable.io.reactivex.rxjava3.core.Observer subscribeWith(io.reactivex.rxjava3.core.Observer)(SourceFile:1)
at com.reddragon.intouch.ui.SplashActivity.void onCreate(android.os.Bundle)(SourceFile:16)
at android.app.Activity.performCreate(Activity.java:7183)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1220)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2910)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3032)
at android.app.ActivityThread.-wrap11(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1696)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6944)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:327)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1374)

Notice that the class names properly got de-obfuscated (SplashActivity$a became SplashActivity$1), but the line numbers did not get un-obfuscated (it still shows line 2 which is wrong). Interestingly, if I include this section in build.gradle:

debug {
  debuggable true
  minifyEnabled true

  // Enables resource shrinking, which is performed by the
  // Android Gradle plugin.
  shrinkResources false

  // Includes the default ProGuard rules files that are packaged with
  // the Android Gradle plugin. To learn more, go to the section about
  // R8 configuration files.
  proguardFiles getDefaultProguardFile(
          'proguard-android.txt'),
          'proguard-rules.pro'
}

For debug builds, the stack trace output has the file name hidden (it shows as 'SourceFile'), the class name is obfuscated, but the line number is left alone:

at com.reddragon.intouch.ui.SplashActivity$a.onStart(SourceFile:313)
at io.reactivex.rxjava3.observers.DisposableObserver.onSubscribe(SourceFile:74)
at io.reactivex.rxjava3.internal.operators.observable.ObservableObserveOn$ObserveOnObserver.onSubscribe(SourceFile:106)
at io.reactivex.rxjava3.internal.operators.observable.ObservableSubscribeOn.subscribeActual(SourceFile:34)
at io.reactivex.rxjava3.core.Observable.subscribe(SourceFile:13095)
at io.reactivex.rxjava3.internal.operators.observable.ObservableObserveOn.subscribeActual(SourceFile:45)
at io.reactivex.rxjava3.core.Observable.subscribe(SourceFile:13095)
at io.reactivex.rxjava3.core.Observable.subscribeWith(SourceFile:13148)
at com.reddragon.intouch.ui.SplashActivity.onCreate(SourceFile:309)
at android.app.Activity.performCreate(Activity.java:7183)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1220)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2910)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3032)
at android.app.ActivityThread.-wrap11(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1696)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6944)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:327)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1374)

Why can I not get the original line number back after running retrace on the stack trace from the 'release' build? Why is the line number not being obfuscated in the 'debug' build?


Solution

  • I have been having similar issues. It seems like R8 is not respecting -keepattributes LineNumberTable,SourceFile despite what Google is claiming in https://developer.android.com/studio/build/shrink-code#decode-stack-trace

    I ended up using ProGuard instead by setting android.enableR8=false in gradle.properties.

    The above solution won't work with Gradle 5 once it's out.