Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Explicitly using R8 will result in unexpected behavior (while implicitly using R8 works as expected) #6612

Closed
lauxjpn opened this issue Jan 5, 2022 · 7 comments
Assignees
Labels
Area: App+Library Build Issues when building Library projects or Application projects. need-info Issues that need more information from the author.
Milestone

Comments

@lauxjpn
Copy link
Contributor

lauxjpn commented Jan 5, 2022

Android application type

Android for .NET (net6.0-android, etc.)

Affected platform version

.NET 6.0.100

Description

I have the following basic project definition:

<TargetFramework>net6.0-android</TargetFramework>
<SupportedOSPlatformVersion>21.0</SupportedOSPlatformVersion>

I then build and deploy a Release version without explicitly specifying any related properties.
The app starts, but at some later point when using the app, some stuff is missing which then leads to the app crashing.

That is not completely unexpected, since the default behavior in .NET 6 is to aggressively link away pretty much everything that wasn't statically detected as being used.


I then set the following property:

<PublishTrimmed>false</PublishTrimmed>

The app builds, deploys and now also runs fully as expected.
Great, I could have stopped here, but didn't.


I then explicitly set the following property:

<AndroidLinkTool>r8</AndroidLinkTool>

When I now build the app, I get the following warning:

obj\Release\net6.0-android\android-x64\proguard\proguard_project_references.cfg: Warning XA4304 : ProGuard configuration file 'obj\Release\net6.0-android\android-x64\proguard\proguard_project_references.cfg' was not found.

When I run the app, I get the following exception immediately:

java.lang.ClassNotFoundException
2022-01-05 21:23:21.071 14245-14245/? E/LoadedApk: Unable to instantiate appComponentFactory
    java.lang.ClassNotFoundException: Didn't find class "androidx.core.app.CoreComponentFactory" on path: DexPathList[[zip file "/data/app/com.mycompany.myapp-8tGi8cO_TcaLu_Bsb0_Pvw==/base.apk", zip file "/data/app/com.mycompany.myapp-8tGi8cO_TcaLu_Bsb0_Pvw==/split_config.arm64_v8a.apk", zip file "/data/app/com.mycompany.myapp-8tGi8cO_TcaLu_Bsb0_Pvw==/split_config.en.apk", zip file "/data/app/com.mycompany.myapp-8tGi8cO_TcaLu_Bsb0_Pvw==/split_config.xhdpi.apk"],nativeLibraryDirectories=[/data/app/com.mycompany.myapp-8tGi8cO_TcaLu_Bsb0_Pvw==/lib/arm64, /data/app/com.mycompany.myapp-8tGi8cO_TcaLu_Bsb0_Pvw==/base.apk!/lib/arm64-v8a, /data/app/com.mycompany.myapp-8tGi8cO_TcaLu_Bsb0_Pvw==/split_config.arm64_v8a.apk!/lib/arm64-v8a, /data/app/com.mycompany.myapp-8tGi8cO_TcaLu_Bsb0_Pvw==/split_config.en.apk!/lib/arm64-v8a, /data/app/com.mycompany.myapp-8tGi8cO_TcaLu_Bsb0_Pvw==/split_config.xhdpi.apk!/lib/arm64-v8a, /system/lib64]]
        at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:134)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:379)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:312)
        at android.app.LoadedApk.createAppFactory(LoadedApk.java:226)
        at android.app.LoadedApk.createOrUpdateClassLoaderLocked(LoadedApk.java:740)
        at android.app.LoadedApk.getClassLoader(LoadedApk.java:819)
        at android.app.LoadedApk.getResources(LoadedApk.java:1041)
        at android.app.ContextImpl.createAppContext(ContextImpl.java:2360)
        at android.app.ActivityThread.handleBindApplication(ActivityThread.java:5996)
        at android.app.ActivityThread.access$1200(ActivityThread.java:213)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1807)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:193)
        at android.app.ActivityThread.main(ActivityThread.java:6923)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:870)
    	Suppressed: java.io.IOException: No original dex files found for dex location /data/app/com.mycompany.myapp-8tGi8cO_TcaLu_Bsb0_Pvw==/split_config.arm64_v8a.apk
        at dalvik.system.DexFile.openDexFileNative(Native Method)
        at dalvik.system.DexFile.openDexFile(DexFile.java:354)
        at dalvik.system.DexFile.<init>(DexFile.java:101)
        at dalvik.system.DexFile.<init>(DexFile.java:75)
        at dalvik.system.DexPathList.loadDexFile(DexPathList.java:394)
        at dalvik.system.DexPathList.makeDexElements(DexPathList.java:354)
        at dalvik.system.DexPathList.<init>(DexPathList.java:164)
        at dalvik.system.BaseDexClassLoader.<init>(BaseDexClassLoader.java:74)
        at dalvik.system.BaseDexClassLoader.<init>(BaseDexClassLoader.java:65)
        at dalvik.system.PathClassLoader.<init>(PathClassLoader.java:64)
        at com.android.internal.os.ClassLoaderFactory.createClassLoader(ClassLoaderFactory.java:73)
        at com.android.internal.os.ClassLoaderFactory.createClassLoader(ClassLoaderFactory.java:88)
        at android.app.ApplicationLoaders.getClassLoader(ApplicationLoaders.java:74)
        at android.app.ApplicationLoaders.getClassLoader(ApplicationLoaders.java:40)
        at android.app.LoadedApk.createOrUpdateClassLoaderLocked(LoadedApk.java:736)
        		... 12 more
    	Suppressed: java.io.IOException: No original dex files found for dex location /data/app/com.mycompany.myapp-8tGi8cO_TcaLu_Bsb0_Pvw==/split_config.en.apk
        at dalvik.system.DexFile.openDexFileNative(Native Method)
        at dalvik.system.DexFile.openDexFile(DexFile.java:354)
        at dalvik.system.DexFile.<init>(DexFile.java:101)
        at dalvik.system.DexFile.<init>(DexFile.java:75)
        at dalvik.system.DexPathList.loadDexFile(DexPathList.java:394)
        at dalvik.system.DexPathList.makeDexElements(DexPathList.java:354)
        at dalvik.system.DexPathList.<init>(DexPathList.java:164)
        at dalvik.system.BaseDexClassLoader.<init>(BaseDexClassLoader.java:74)
        at dalvik.system.BaseDexClassLoader.<init>(BaseDexClassLoader.java:65)

It appears, that suddenly R8 has trimmed so aggressively, that even the most basic stuff is missing.


I then disable trimming:

<AndroidLinkTool>r8</AndroidLinkTool>
<PublishTrimmed>false</PublishTrimmed>

The same exception is still being thrown.


I then try the following combination:

<AndroidLinkTool>r8</AndroidLinkTool>
<AndroidLinkMode>None</AndroidLinkMode>

The same exception is still being thrown.


I then disable shrinking via a Proguard configuration option:

<AndroidLinkTool>r8</AndroidLinkTool>
<PublishTrimmed>false</PublishTrimmed>
<ItemGroup>
    <ProguardConfiguration Include="Proguard\**\*.cfg" />
</ItemGroup>

Proguard\KeepClasses.cfg:

-dontshrink

While the build warning is still being shown, the app now runs fine again.


So it appears that when I explicitly set <AndroidLinkTool>r8</AndroidLinkTool> the behavior is different in comparison to when it is implicitly being set. This might be related to the proguard_project_references.cfg file missing at build time.

The behavior should be the same in both cases (the one with the missing proguard_project_references.cfg seems to be a bug).

Steps to Reproduce

See description.

Did you find any workaround?

Don't explicitly specify <AndroidLinkTool>r8</AndroidLinkTool>.

Relevant log output

See description.
@lauxjpn lauxjpn added Area: App+Library Build Issues when building Library projects or Application projects. needs-triage Issues that need to be assigned. labels Jan 5, 2022
@jonathanpeppers
Copy link
Member

There are 2 linkers in use here: 1) the .NET linker for C# code, 2) r8 for Java code.

Unfortunately no. 2 (r8) depends on no. 1 (.NET linker) being enabled, as it uses a custom linker step to generate proguard_project_references.cfg:

https://github.com/xamarin/xamarin-android/blob/main/src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/GenerateProguardConfiguration.cs

So if you set any of these when AndroidLinkMode=r8:

  • PublishTrimmed=false
  • AndroidLinkMode=None

Then you would probably hit an error that proguard_project_references.cfg is missing. We should probably make a better error message for these combinations... Whoops!

However, what are you actually trying to accomplish? Are you just wanting to enable AndroidLinkMode=r8 and your app no longer crash?

I think we need to see adb logcat output of the actual crash/stack trace, and we can help further. Thanks!

@jonathanpeppers jonathanpeppers added this to the .NET 6 milestone Jan 5, 2022
@jonathanpeppers jonathanpeppers added need-info Issues that need more information from the author. and removed needs-triage Issues that need to be assigned. labels Jan 5, 2022
@lauxjpn
Copy link
Contributor Author

lauxjpn commented Jan 5, 2022

@jonathanpeppers Thanks!


However, what are you actually trying to accomplish? Are you just wanting to enable AndroidLinkMode=r8 and your app no longer crash?

I had AndroidLinkMode=r8 already explicitly set, because it was mentioned in #5378 (comment), that R8 was necessary for the mapping.txt generation.

I was already aware at the time (because I have kind of living inside the android build target files over the last days to figure out how to get my projects to a beta testable state) that R8 is the implicit default (or the only option) in .NET 6.

Just didn't think it would hurt to set it explicitly.

So the linear progression outlined in my OP is not really how it chronologically happend 😄


While I got everything working with trimming disabled, I want to release a smaller package.

So after I have now removed the explicit AndroidLinkMode property (because I don't need it), I have re-enabled PublishTrimmed with the implicit TrimMode=link (this time on a Debug build so I can debug trimmed stuff more easily).

I am now tracking down an issue connected to trimming with images not being loaded by an ImageSource from a MemoryStream using Xamarin.Forms.

This is what I described previously as:

The app starts, but at some later point when using the app, some stuff is missing which then leads to the app crashing.

The following exception is thrown internally:

Exception
Image loading: Image load failed: System.ArgumentException: Arg_DlgtTargMeth
   at System.Delegate.CreateDelegate(Type type, Type target, String method, Boolean ignoreCase, Boolean throwOnBindFailure)
   at System.Delegate.CreateDelegate(Type type, Type target, String method)
   at Android.Runtime.AndroidTypeManager.RegisterNativeMembers(JniType , Type , String ) in /Users/builder/azdo/_work/1/s/xamarin-android/src/Mono.Android/Android.Runtime/AndroidRuntime.cs:line 424
   at Android.Runtime.JNIEnv.RegisterJniNatives(IntPtr , Int32 , IntPtr , IntPtr , Int32 ) in /Users/builder/azdo/_work/1/s/xamarin-android/src/Mono.Android/Android.Runtime/JNIEnv.cs:line 139
   at Java.Interop.JniEnvironment.StaticMethods.CallStaticObjectMethod(JniObjectReference , JniMethodInfo , JniArgumentValue* ) in /Users/builder/azdo/_work/1/s/xamarin-android/external/Java.Interop/src/Java.Interop/Java.Interop/JniEnvironment.g.cs:line 12645
   at Android.Runtime.JNIEnv.CallStaticObjectMethod(IntPtr , IntPtr , JValue* ) in /Users/builder/azdo/_work/1/s/xamarin-android/src/Mono.Android/Android.Runtime/JNIEnv.g.cs:line 562
   at Android.Runtime.JNIEnv.CallStaticObjectMethod(IntPtr , IntPtr , JValue[] ) in /Users/builder/azdo/_work/1/s/xamarin-android/src/Mono.Android/Android.Runtime/JNIEnv.g.cs:line 568
   at Android.Runtime.JNIEnv.FindClass(String ) in /Users/builder/azdo/_work/1/s/xamarin-android/src/Mono.Android/Android.Runtime/JNIEnv.cs:line 562
   at Android.Runtime.JNIEnv.AllocObject(String ) in /Users/builder/azdo/_work/1/s/xamarin-android/src/Mono.Android/Android.Runtime/JNIEnv.cs:line 342
   at Android.Runtime.JNIEnv.StartCreateInstance(String , String , JValue* ) in /Users/builder/azdo/_work/1/s/xamarin-android/src/Mono.Android/Android.Runtime/JNIEnv.cs:line 406
   at Android.Runtime.JNIEnv.StartCreateInstance(String , String , JValue[] ) in /Users/builder/azdo/_work/1/s/xamarin-android/src/Mono.Android/Android.Runtime/JNIEnv.cs:line 413
   at Android.Runtime.InputStreamAdapter..ctor(Stream ) in /Users/builder/azdo/_work/1/s/xamarin-android/src/Mono.Android/Android.Runtime/InputStreamAdapter.cs:line 12
   at Android.Runtime.InputStreamAdapter.ToLocalJniHandle(Stream ) in /Users/builder/azdo/_work/1/s/xamarin-android/src/Mono.Android/Android.Runtime/InputStreamAdapter.cs:line 53
   at Android.Graphics.BitmapFactory.DecodeStream(Stream ) in /Users/builder/azdo/_work/1/s/xamarin-android/src/Mono.Android/obj/Release/net6.0/android-31/mcw/Android.Graphics.BitmapFactory.cs:line 821
   at Android.Graphics.BitmapFactory.<>c__DisplayClass31_0.<DecodeStreamAsync>b__0() in /Users/builder/azdo/_work/1/s/xamarin-android/src/Mono.Android/obj/Release/net6.0/android-31/mcw/Android.Graphics.BitmapFactory.cs:line 835
   at System.Threading.Tasks.Task`1[[Android.Graphics.Bitmap, Mono.Android, Version=0.0.0.0, Culture=neutral, PublicKeyToken=84e04ff9cfb79065]].InnerInvoke()
   at System.Threading.Tasks.Task.<>c.<.cctor>b__272_0(Object )
   at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread , ExecutionContext , ContextCallback , Object )
--- End of stack trace from previous location ---
   at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread , ExecutionContext , ContextCallback , Object )
   at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& , Thread )
--- End of stack trace from previous location ---
   at Xamarin.Forms.Platform.Android.StreamImagesourceHandler.LoadImageAsync(ImageSource imagesource, Context context, CancellationToken cancelationToken) in E:\Sources\Xamarin.Forms.Net6\Xamarin.Forms.Platform.Android\Renderers\StreamImagesourceHandler.cs:line 19
   at Xamarin.Forms.Platform.Android.ResourceManager.GetFormsBitmapAsync(Context context, ImageSource imageSource, CancellationToken cancellationToken) in E:\Sources\Xamarin.Forms.Net6\Xamarin.Forms.Platform.Android\ResourceManager.cs:line 152

My guess is that the delegate target was trimmed, because it was not discovered by static analysis, and I have to figure out where it is to preserve it.
But that's not really connected to the OP anymore.

@lauxjpn
Copy link
Contributor Author

lauxjpn commented Jan 9, 2022

My guess is that the delegate target was trimmed, because it was not discovered by static analysis, and I have to figure out where it is to preserve it.
But that's not really connected to the OP anymore.

Just in case anybody stumbles across this issue here in the future:

To figure out the trimmed CreateDelegate targets, I ended up compiling a debug build of Xamarin.Android from source to add some additional logging to AndroidRuntime.RegisterNativeMembers(), which is in the call stack to Delegate.CreateDelegate() in my case before the app crashes.

I then used the workload containing dotnet SDK from ~\android-toolchain\dotnet\dotnet.exe to build my project and traced the calls that crash the app when using TrimMode=link.

In my case, the Android.Runtime.InputStreamAdapter class got stripped by ILLink.

I then created a Roots.xml file with the following content:

<linker>
    <assembly fullname="Mono.Android">
        <type fullname="Android.Runtime.InputStreamAdapter" />
    </assembly>
</linker>

Finally, I added the file to my project file:

<ItemGroup>
    <TrimmerRootDescriptor Include="Roots.xml" />
</ItemGroup>

@jonathanpeppers I think tracking down a simple issue like this is way too much effort (at least for the first one, until you got everything setup).

The initial Exception should really just include the target type and the missing method, since with TrimMode=link as the default for Android project release builds, this is likely to become a relatively common case.

@jonathanpeppers
Copy link
Member

Two two issues here?

  1. Figure out why Android.Runtime.InputStreamAdapter is getting linked away? @lauxjpn do you have a small code example that causes the issue? We would probably want to write a test to catch this going forward.

  2. The error message if we get an exception here:

https://github.com/xamarin/xamarin-android/blob/a533652fa68754c79d80a1e6b62dd62add307c8d/src/Mono.Android/Android.Runtime/AndroidRuntime.cs#L424-L425

We could catch ArgumentException? and log a better error?

https://github.com/dotnet/runtime/blob/57bfe474518ab5b7cfe6bf7424a79ce3af9d6657/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs#L251-L252

@lauxjpn
Copy link
Contributor Author

lauxjpn commented Jan 13, 2022

@lauxjpn do you have a small code example that causes the issue? We would probably want to write a test to catch this going forward.

@jonathanpeppers XamarinAndroidIssue6612.zip will show the Xamarin icon in Debug builds, but will fail to show it (and shortly later crash) in Release builds due to ILLink.

The Xamarin.Forms Image tag to show the icon is in the MainPage.xaml file and its Source property is set to an StreamImageSource using the ImageSource.FromStream() method in the MainPage constructor.

@jonathanpeppers
Copy link
Member

@lauxjpn I think the two issues here are fixed in:

Are you still hitting this issue using the latest RC 1 builds?

@jonathanpeppers
Copy link
Member

Closing for now, let us know, thanks!

@ghost ghost locked as resolved and limited conversation to collaborators Jun 2, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Area: App+Library Build Issues when building Library projects or Application projects. need-info Issues that need more information from the author.
Projects
None yet
Development

No branches or pull requests

3 participants