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

AOT+LLVM throws for Enum.GetUnderlyingType() on Android in .NET 7 #68914

Closed
jonathanpeppers opened this issue May 5, 2022 · 18 comments · Fixed by #74796
Closed

AOT+LLVM throws for Enum.GetUnderlyingType() on Android in .NET 7 #68914

jonathanpeppers opened this issue May 5, 2022 · 18 comments · Fixed by #74796

Comments

@jonathanpeppers
Copy link
Member

Description

Our Mono.Android-Tests project crashes on startup with:

05-05 15:19:34.429  4929  4929 F mono-rt : [ERROR] FATAL UNHANDLED EXCEPTION: System.ArgumentException: Type provided must be an Enum. Arg_ParamName_Name, enumType
05-05 15:19:34.429  4929  4929 F mono-rt :    at System.Enum.InternalGetUnderlyingType(RuntimeType enumType)
05-05 15:19:34.429  4929  4929 F mono-rt :    at System.RuntimeType.GetEnumUnderlyingType()
05-05 15:19:34.429  4929  4929 F mono-rt :    at System.Enum.GetUnderlyingType(Type enumType)
05-05 15:19:34.429  4929  4929 F mono-rt :    at Java.Interop.JniRuntime.JniTypeManager.GetUnderlyingType(Type , Int32& )
05-05 15:19:34.429  4929  4929 F mono-rt :    at Java.Interop.JniRuntime.JniTypeManager.GetTypeSignature(Type )
05-05 15:19:34.429  4929  4929 F mono-rt :    at Java.Interop.JniPeerMembers.JniInstanceMethods..ctor(Type )
05-05 15:19:34.429  4929  4929 F mono-rt :    at Java.Interop.JniPeerMembers.JniInstanceMethods.GetConstructorsForType(Type )
05-05 15:19:34.429  4929  4929 F mono-rt :    at Java.Interop.JniPeerMembers.JniInstanceMethods.StartCreateInstance(String , Type , JniArgumentValue* )
05-05 15:19:34.429  4929  4929 F mono-rt :    at Java.Lang.Error..ctor(String )
05-05 15:19:34.429  4929  4929 F mono-rt :    at Android.Runtime.JavaProxyThrowable..ctor(Exception )
05-05 15:19:34.429  4929  4929 F mono-rt :    at Java.Lang.Throwable.FromException(Exception )
05-05 15:19:34.429  4929  4929 F mono-rt :    at Android.Runtime.AndroidEnvironment.UnhandledException(Exception e)
05-05 15:19:34.429  4929  4929 F mono-rt :    at Android.Runtime.JNINativeWrapper.Wrap_JniMarshal_PPL_V(_JniMarshal_PPL_V , IntPtr , IntPtr , IntPtr )

This looks oddly familiar to a related issue in mono/mono: dotnet/android#1842

Looking at the underlying code, I think it throws here:

https://github.com/xamarin/java.interop/blob/61cdb40dcc8b33d400aef3fd12e0aeee72f71e35/src/Java.Interop/Java.Interop/JniRuntime.JniTypeManager.cs#L151-L152

Reproduction Steps

Build a .NET 7 Release app with -p:EnableLLVM=true.

Expected behavior

Use of -p:EnableLLVM=true on Android doesn't crash on startup.

Actual behavior

Use of -p:EnableLLVM=true crashes on startup on Android.

Regression?

This project appears to be working in .NET 6.

Known Workarounds

Disable LLVM.

Configuration

.NET 7.0.100-preview.4.22174.1 c7afae6936bf80239aa93bfd3b6f68513d9876d4
.NET runtime 7.0.0-preview.4.22172.7 c5d40c9

Other information

No response

@dotnet-issue-labeler
Copy link

I couldn't figure out the best area label to add to this issue. If you have write-permissions please help me learn by adding exactly one area label.

@ghost ghost added the untriaged New issue has not been triaged by the area owner label May 5, 2022
jonpryor pushed a commit to dotnet/android that referenced this issue May 5, 2022
Context: dotnet/runtime#56989
Context: dotnet/runtime#68734
Context: dotnet/runtime#68914
Context: dotnet/runtime#68701

Changes: dotnet/installer@04e40fa...c7afae6

	% git diff --shortstat 04e40fa9...c7afae69
	 98 files changed, 1788 insertions(+), 1191 deletions(-)

Changes: dotnet/runtime@a21b9a2...c5d40c9

	% git diff --shortstat a21b9a2d...c5d40c9e
	 28347 files changed, 1609359 insertions(+), 1066473 deletions(-)

Changes: dotnet/linker@01c4f59...04c49c9

	% git diff --shortstat 01c4f590...04c49c9d
	 577 files changed, 28039 insertions(+), 10835 deletions(-)

Updates to build with the .NET 7 SDK and use the runtime specified by
the SDK.  We no longer use different SDK & runtime versions.

This produces a 7.0.100 Android workload.

After this is merged we should be able to enable Maestro to consume
future .NET 7 builds from dotnet/installer/main.

~~ Known Issues ~~

AOT+LLVM crashes on startup:

  * dotnet/runtime#68914

Xamarin.Build.Download hits a breaking change with `ZipEntry`:

  * dotnet/runtime#68734
  * xamarin/XamarinComponents#1368

illink outputs different MVIDs per architecture:

  * dotnet/linker#2203
  * dotnet/runtime#67660

Size of `libmonosgen-2.0.so` regressed:

  * dotnet/runtime#68330
  * dotnet/runtime#68354

Newer .NET 7 builds crash on startup:

  * dotnet/runtime#68701
  * This is worked around by *disabling* lazy loading of AOT'd
    assemblies 6dc426f.
  * TODO: re-enable once we get a fixed .NET 7 runtime.

TODO: We can't yet push to the `dotnet7` feed. Working on this.

Co-authored-by: Jonathan Peppers <jonathan.peppers@microsoft.com>
Co-authored-by: Peter Collins <pecolli@microsoft.com>
@steveisok
Copy link
Member

/cc @SamMonoRT

@SamMonoRT SamMonoRT added this to the 7.0.0 milestone May 10, 2022
@ghost ghost removed the untriaged New issue has not been triaged by the area owner label May 10, 2022
@SamMonoRT
Copy link
Member

cc @vargaz - please can you take a look.
@jonathanpeppers - do we need any related fix backported for MAUI release ?

@jonathanpeppers
Copy link
Member Author

@SamMonoRT this issue is only happening in .NET 7.

It looks like the same test is passing in .NET 6.

@vargaz
Copy link
Contributor

vargaz commented May 10, 2022

Which type is passed to the method ?

@jonpryor
Copy link
Member

jonpryor commented May 11, 2022

What's "odd" about this stack trace is that it makes no sense.

It starts with the JavaProxyThrowable constructor, which chains to the Java.Lang.Error constructor, which does:

var __r = _members.InstanceMethods.StartCreateInstance (__id, ((object) this).GetType (), __args);

i.e. the System.Type instance will be typeof(JavaProxyThrowable).

We then hit JniPeerMembers.JniInstanceMethods.StartCreateInstance(): https://github.com/xamarin/java.interop/blob/3fcce7466324c6564874fe275eac222a8970078c/src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceMethods.cs#L97-L112

which calls GetConstructorsForType(): https://github.com/xamarin/java.interop/blob/3fcce7466324c6564874fe275eac222a8970078c/src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceMethods.cs#L70-L82

  • declaringType is typeof(JavaProxyThrowable).
  • DeclaringType is typeof(Error)
  • We thus hit new JniInstanceMethods(typeof(JavaProxyThrowable))

This brings us to the JniInstaceMethods(Type) constructor: https://github.com/xamarin/java.interop/blob/3fcce7466324c6564874fe275eac222a8970078c/src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceMethods.cs#L17-L29

Which calls GetTypeSignature(typeof(JavaProxyThrowable)): https://github.com/xamarin/java.interop/blob/3fcce7466324c6564874fe275eac222a8970078c/src/Java.Interop/Java.Interop/JniRuntime.JniTypeManager.cs#L51

Which calls GetUnderlyingType(typeof(JavaProxyThrowable), out int _): https://github.com/xamarin/java.interop/blob/3fcce7466324c6564874fe275eac222a8970078c/src/Java.Interop/Java.Interop/JniRuntime.JniTypeManager.cs#L140-L155

Which is where things make no sense: statically, we know type is typeof(JavaProxyThrowable), yet here:

https://github.com/xamarin/java.interop/blob/3fcce7466324c6564874fe275eac222a8970078c/src/Java.Interop/Java.Interop/JniRuntime.JniTypeManager.cs#L151-L152

typeof(JavaProxyThrowable).IsEnum must be true in order for us to hit Enum.GetUnderlyingType(typeof(JavaProxyThrowable)), but it shouldn't be true!

Thus, throwing the ArgumentException is correct behavior.

The bug is that typeof(JavaProxyThrowable).IsEnum is true when it should be false!

(Or AOT+LLVM inlining mumble something?)

@jonathanpeppers
Copy link
Member Author

I made some smaller repros here, building xamarin-android/main. I used dotnet new android, and set -p:AndroidPackageFormat=apk, -p:AndroidUseAssemblyStore=false, and -p:AndroidEnableAssemblyCompression=false, to simplify things and make the IL readable if needed.

-p:EnableLLVM=false, these launch successfully:

-p:EnableLLVM=true, these fail:

(this one might be a different issue, it has a different crash)

I added more logging in xamarin-android to see the exception:

05-12 11:42:37.629  2490  2490 I MonoDroid: Android.Runtime.JavaProxyThrowable: Exception_WasThrown, Android.Runtime.JavaProxyThrowable
05-12 11:42:37.629  2490  2490 I MonoDroid:   --- End of managed Android.Runtime.JavaProxyThrowable stack trace ---
05-12 11:42:37.629  2490  2490 I MonoDroid: android.runtime.JavaProxyThrowable: System.NotSupportedException: Unable to activate instance of type foo.MainActivity from native handle 0x7ffd8e98a214 (key_handle 0x18e6dce).
05-12 11:42:37.629  2490  2490 I MonoDroid:  ---> System.MissingMethodException: No constructor found for foo.MainActivity::.ctor(System.IntPtr, Android.Runtime.JniHandleOwnership)
05-12 11:42:37.630  2490  2490 I MonoDroid:  ---> Java.Interop.JavaLocationException: Exception_WasThrown, Java.Interop.JavaLocationException

We believe that Type.IsInterface and Type.IsEnum might be returning values wrong for types.

@vargaz
Copy link
Contributor

vargaz commented May 19, 2022

Which aotprofile file is used to AOT these apps ?

@jonathanpeppers
Copy link
Member Author

It is this one, which was recorded using .NET 6:

https://github.com/xamarin/xamarin-android/blob/b250c04b09a2b725336ae6d6c5693e0b9f37e4cc/src/Xamarin.Android.Build.Tasks/dotnet.aotprofile

Working on another one, but it looks like I have to build a newer libmono-profiler-aot.so.

@ghost
Copy link

ghost commented May 19, 2022

Tagging subscribers to 'arch-android': @steveisok, @akoeplinger
See info in area-owners.md if you want to be subscribed.

Issue Details

Description

Our Mono.Android-Tests project crashes on startup with:

05-05 15:19:34.429  4929  4929 F mono-rt : [ERROR] FATAL UNHANDLED EXCEPTION: System.ArgumentException: Type provided must be an Enum. Arg_ParamName_Name, enumType
05-05 15:19:34.429  4929  4929 F mono-rt :    at System.Enum.InternalGetUnderlyingType(RuntimeType enumType)
05-05 15:19:34.429  4929  4929 F mono-rt :    at System.RuntimeType.GetEnumUnderlyingType()
05-05 15:19:34.429  4929  4929 F mono-rt :    at System.Enum.GetUnderlyingType(Type enumType)
05-05 15:19:34.429  4929  4929 F mono-rt :    at Java.Interop.JniRuntime.JniTypeManager.GetUnderlyingType(Type , Int32& )
05-05 15:19:34.429  4929  4929 F mono-rt :    at Java.Interop.JniRuntime.JniTypeManager.GetTypeSignature(Type )
05-05 15:19:34.429  4929  4929 F mono-rt :    at Java.Interop.JniPeerMembers.JniInstanceMethods..ctor(Type )
05-05 15:19:34.429  4929  4929 F mono-rt :    at Java.Interop.JniPeerMembers.JniInstanceMethods.GetConstructorsForType(Type )
05-05 15:19:34.429  4929  4929 F mono-rt :    at Java.Interop.JniPeerMembers.JniInstanceMethods.StartCreateInstance(String , Type , JniArgumentValue* )
05-05 15:19:34.429  4929  4929 F mono-rt :    at Java.Lang.Error..ctor(String )
05-05 15:19:34.429  4929  4929 F mono-rt :    at Android.Runtime.JavaProxyThrowable..ctor(Exception )
05-05 15:19:34.429  4929  4929 F mono-rt :    at Java.Lang.Throwable.FromException(Exception )
05-05 15:19:34.429  4929  4929 F mono-rt :    at Android.Runtime.AndroidEnvironment.UnhandledException(Exception e)
05-05 15:19:34.429  4929  4929 F mono-rt :    at Android.Runtime.JNINativeWrapper.Wrap_JniMarshal_PPL_V(_JniMarshal_PPL_V , IntPtr , IntPtr , IntPtr )

This looks oddly familiar to a related issue in mono/mono: dotnet/android#1842

Looking at the underlying code, I think it throws here:

https://github.com/xamarin/java.interop/blob/61cdb40dcc8b33d400aef3fd12e0aeee72f71e35/src/Java.Interop/Java.Interop/JniRuntime.JniTypeManager.cs#L151-L152

Reproduction Steps

Build a .NET 7 Release app with -p:EnableLLVM=true.

Expected behavior

Use of -p:EnableLLVM=true on Android doesn't crash on startup.

Actual behavior

Use of -p:EnableLLVM=true crashes on startup on Android.

Regression?

This project appears to be working in .NET 6.

Known Workarounds

Disable LLVM.

Configuration

.NET 7.0.100-preview.4.22174.1 c7afae6936bf80239aa93bfd3b6f68513d9876d4
.NET runtime 7.0.0-preview.4.22172.7 c5d40c9

Other information

No response

Author: jonathanpeppers
Assignees: vargaz
Labels:

os-android, area-Codegen-LLVM-mono

Milestone: 7.0.0

jonathanpeppers added a commit to jonathanpeppers/xamarin-android that referenced this issue May 24, 2022
Context: dotnet/runtime#68914

We found that the issue with LLVM only occurs when using Profiled AOT.

This is enabled by default for `Release` builds in .NET 6+, so let's
try using `-p:AndroidEnableProfiledAot=false`.

This at least gets some of our LLVM test coverage running again.
jonathanpeppers added a commit to jonathanpeppers/xamarin-android that referenced this issue May 25, 2022
Context: dotnet/runtime#68914

We found that the issue with LLVM only occurs when using Profiled AOT.

This is enabled by default for `Release` builds in .NET 6+, so let's
try using `-p:AndroidEnableProfiledAot=false`.

This at least gets some of our LLVM test coverage running again.

When testing this, we started getting a crash:

    05-24 15:11:36.809  5024  5024 F DEBUG   : Abort message: 'Internal p/invoke symbol 'java-interop @ java_interop_jvm_list' (hash: 0x58c48fc8b89cb484) not found in compile-time map.'

It appeared that `Android.App.Application.cctor` was running before
`JNIEnv.Initialize()` is complete:

    05-25 11:12:33.177 10812 10812 D Mono    : AOT: FOUND method Android.Runtime.JNIEnv/<>c:.cctor () [0x78e0d04608 - 0x78e0d04678 0x78e0d6f8a0]
    05-25 11:12:33.177 10812 10812 D Mono    : Running class .cctor for Java.Interop.Tools.TypeNameMappings.JavaNativeTypeManager from 'Mono.Android.dll'
    05-25 11:12:33.177 10812 10812 D Mono    : AOT: FOUND method Java.Interop.Tools.TypeNameMappings.JavaNativeTypeManager:.cctor () [0x78e0d31d24 - 0x78e0d31d64 0x78e0d6f8a0]
    05-25 11:12:33.177 10812 10812 D Mono    : Running class .cctor for Android.App.Application from 'Mono.Android.dll'

By adding an explicit static constructor to `Android.App.Application`,
this appears to have workaround this issue.
jonathanpeppers added a commit to jonathanpeppers/xamarin-android that referenced this issue May 26, 2022
Context: dotnet/runtime#68914

We found that the issue with LLVM only occurs when using Profiled AOT.

This is enabled by default for `Release` builds in .NET 6+, so let's
try using `-p:AndroidEnableProfiledAot=false`.

This at least gets some of our LLVM test coverage running again.

When testing this, we started getting a crash:

    05-24 15:11:36.809  5024  5024 F DEBUG   : Abort message: 'Internal p/invoke symbol 'java-interop @ java_interop_jvm_list' (hash: 0x58c48fc8b89cb484) not found in compile-time map.'

It appeared that `Android.App.Application.cctor` was running before
`JNIEnv.Initialize()` is complete:

    05-25 11:12:33.177 10812 10812 D Mono    : AOT: FOUND method Android.Runtime.JNIEnv/<>c:.cctor () [0x78e0d04608 - 0x78e0d04678 0x78e0d6f8a0]
    05-25 11:12:33.177 10812 10812 D Mono    : Running class .cctor for Java.Interop.Tools.TypeNameMappings.JavaNativeTypeManager from 'Mono.Android.dll'
    05-25 11:12:33.177 10812 10812 D Mono    : AOT: FOUND method Java.Interop.Tools.TypeNameMappings.JavaNativeTypeManager:.cctor () [0x78e0d31d24 - 0x78e0d31d64 0x78e0d6f8a0]
    05-25 11:12:33.177 10812 10812 D Mono    : Running class .cctor for Android.App.Application from 'Mono.Android.dll'

By adding an explicit static constructor to `Android.App.Application`,
this appears to have workaround this issue.
jonathanpeppers added a commit to jonathanpeppers/xamarin-android that referenced this issue Jul 19, 2022
Context: dotnet/runtime#68914

We found that the issue with LLVM only occurs when using Profiled AOT.

This is enabled by default for `Release` builds in .NET 6+, so let's
try using `-p:AndroidEnableProfiledAot=false`.

This at least gets some of our LLVM test coverage running again.

When testing this, we started getting a crash:

    05-24 15:11:36.809  5024  5024 F DEBUG   : Abort message: 'Internal p/invoke symbol 'java-interop @ java_interop_jvm_list' (hash: 0x58c48fc8b89cb484) not found in compile-time map.'

It appeared that `Android.App.Application.cctor` was running before
`JNIEnv.Initialize()` is complete:

    05-25 11:12:33.177 10812 10812 D Mono    : AOT: FOUND method Android.Runtime.JNIEnv/<>c:.cctor () [0x78e0d04608 - 0x78e0d04678 0x78e0d6f8a0]
    05-25 11:12:33.177 10812 10812 D Mono    : Running class .cctor for Java.Interop.Tools.TypeNameMappings.JavaNativeTypeManager from 'Mono.Android.dll'
    05-25 11:12:33.177 10812 10812 D Mono    : AOT: FOUND method Java.Interop.Tools.TypeNameMappings.JavaNativeTypeManager:.cctor () [0x78e0d31d24 - 0x78e0d31d64 0x78e0d6f8a0]
    05-25 11:12:33.177 10812 10812 D Mono    : Running class .cctor for Android.App.Application from 'Mono.Android.dll'

By adding an explicit static constructor to `Android.App.Application`,
this appears to have workaround this issue.
@fanyang-mono
Copy link
Member

#73304 is also a Enum related failure. I wonder if they could have the same root cause.

@jonathanpeppers
Copy link
Member Author

#73304 is also a Enum related failure.

I think #73304 might mean "enumeration", it's this error message after the linker removed the text for it:

<data name="InvalidOperation_EnumEnded" xml:space="preserve">
<value>Enumeration already finished.</value>
</data>

I think this is if you use an Enumerator yourself and try to move past the end?

@lambdageek
Copy link
Member

lambdageek commented Aug 17, 2022

Based on the description here #68914 (comment) I'm trying to make a sample for dotnet/runtime that has the same behavior, and trying to compile it with LLVM.

Starting from this:

using System;
using System.Runtime.CompilerServices;

public class Base {
    public Base () {
        Work (((object)this).GetType());
    }
    [MethodImpl(MethodImplOptions.NoInlining)]
    public static void Work(Type t) {
        Console.WriteLine ($"is enum: {t} - {t.IsEnum}");
    }
}

public class Child : Base {
    public Child() : base () { }
}


public class Program {
    public static void Main () {
        bool isMono = typeof(object).Assembly.GetType("Mono.RuntimeStructs") != null;
        Console.WriteLine($"Hello World {(isMono ? "from Mono!" : "from CoreCLR!")}");
        var _ = new Child ();
    }
}

Currently having issues building the runtime with LLVM support

Doesn't repro on osx-arm64 desktop with llvmonly or with fullAOT with llvm. going to try android

@lambdageek
Copy link
Member

lambdageek commented Aug 17, 2022

Nope, my naive repro doesn't ever return True for IsEnum in the dotnet/runtime Android sample

@jonathanpeppers
Copy link
Member Author

@lambdageek this particular issue, I haven't seen last time I tested LLVM. Is it possible some change since May solved this one?

I've more recently hit these two:

@lambdageek lambdageek removed their assignment Aug 17, 2022
@lambdageek
Copy link
Member

Removing blocking-release and moving to 8.0 - unless you think we can close it, @jonathanpeppers ?

@lambdageek lambdageek modified the milestones: 7.0.0, 8.0.0 Aug 17, 2022
@jonathanpeppers
Copy link
Member Author

Let's just close it, and I can reopen if we hit this again.

@BrzVlad
Copy link
Member

BrzVlad commented Aug 30, 2022

I stumbled across an identical stacktrace while hitting various issues on android + llvmaot. #74796 was fixing it for me.

@ghost ghost locked as resolved and limited conversation to collaborators Sep 29, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

9 participants