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

fix: [iOS / Native AOT] - prevent use of unsupported marshalling behaviour #3729

Open
wants to merge 27 commits into
base: main
Choose a base branch
from

Conversation

bricefriha
Copy link
Contributor

@bricefriha bricefriha commented Nov 5, 2024

This PR aims to fix #3280, preventing the use of unsupported marshalling behaviour UnwindNativeCode for Native AOT by disabling Marshal Exceptions when the code is compiled via native AOT (as advised here).

Testing

Testing these changes is more tricky than usual since we did find a way to run this from Unit Tests.

The best way to do this is to: [on a MAC with xcode installed]

  • Clone the sentry-dotnet repository (make sure you don't do it on a directory that is linked to iCloud)
  • Git checkout fix/#3680
  • From the open the terminal and move to the sample directory: cd /Samples/Sentry.Samples.Maui
  • Plug a physical device via USB (if your device is not in developer mode: open xcode, it will setup the device)
  • then run `dotnet publish -c Release --framework net8.0-ios /t:Run /p:_DeviceName={DeviceID}

If you don't know the ID of your device (DeviceID):
either run xcrun xctrace list devices, or head to Window -> Devices and Simulators -> device id is under Identifier

  • wait until the app opens on your device
  • terminate the process
  • Open the app again on your phone
  • Once open press the "Throw Unhandled .NET Exception (Crash)"
  • Head to Sentry, you should see the event popping up

Known error while testing (and their workaround)

  • error : strip exited with code 13: this is a known issue with Xcode 16 (now fixed on Xcode16.2 beta) If you get this error, add the following line to the .csproj of the maui project:
<MtouchNoSymbolStrip Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">True</MtouchNoSymbolStrip>

@bricefriha bricefriha changed the title Fix: [iOS / Native AOT] - prevent use unsupported marshalling behaviour Fix: [iOS / Native AOT] - prevent use of unsupported marshalling behaviour Nov 5, 2024
@bricefriha bricefriha changed the title Fix: [iOS / Native AOT] - prevent use of unsupported marshalling behaviour fix: [iOS / Native AOT] - prevent use of unsupported marshalling behaviour Nov 5, 2024
CHANGELOG.md Outdated Show resolved Hide resolved
src/Sentry/Platforms/Cocoa/SentryOptions.cs Outdated Show resolved Hide resolved
src/Sentry/Platforms/Cocoa/SentrySdk.cs Outdated Show resolved Hide resolved
src/Sentry/Platforms/Cocoa/SentrySdk.cs Outdated Show resolved Hide resolved
@jamescrosswell
Copy link
Collaborator

Also it was enabled by default in .NET for a reason: https://github.com/xamarin/xamarin-macios/wiki/.NET-release-notes-Xcode-13.3#exception-marshalling-is-enabled-by-default

That link is interesting... it seems to imply this setting could be controlled at build time, rather than runtime (what we're doing)... so it might be possible to set this automatically to the appropriate value via our transitive build script rather than waiting until runtime.

If we are going to do it at runtime, it might be possible to pick an appropriate value automatically based on whether or not the user's application is compiled AOT or not (rather than hoping they configure the right value via the native options). We have an AotHelper that might be used to detect whether the app is AOT compiled or not, but I'm a bit concerned that the current implementation of this picks up apps that are trimmed but not AOT... so if we can do it at compile time that would be better:
https://github.com/getsentry/sentry-dotnet/blob/9f36d22f3b216351d5957b74e831e8b92636e6ba/src/Sentry/Internal/AotHelper.cs#L15-L14

@bricefriha
Copy link
Contributor Author

bricefriha commented Nov 7, 2024

@jamescrosswell

We have an AotHelper that might be used to detect whether the app is AOT compiled or not, but I'm a bit concerned that the current implementation of this picks up apps that are trimmed but not AOT...

Looking at the implementation of AotHelper .IsAOT, I got the same impression. Should we do !(!RuntimeFeature.IsDynamicCodeCompiled && !RuntimeFeature.IsDynamicCodeSupported) instead then? dotnet/runtime#76739 (comment)

so it might be possible to set this automatically to the appropriate value via our transitive build script rather than waiting until runtime.

I like this idea

@bricefriha bricefriha marked this pull request as ready for review November 8, 2024 20:05
@jamescrosswell
Copy link
Collaborator

  • then run `dotnet publish -c Release --framework net8.0-ios /t:Run /p:_DeviceName={DeviceID}

There are a few more hoops to jump through before you can do this right? You have to setup a provisioning profile for the app in the apple developer portal first?

I'm just waiting for Apple to renew my annual membership to the developer portal so I can test.

@bricefriha
Copy link
Contributor Author

There are a few more hoops to jump through before you can do this right? You have to setup a provisioning profile for the app in the apple developer portal first?

maybe? I'm not sure. I think Xcode creates a temporary one

you can also use a simulator I think, btw

@bruno-garcia
Copy link
Member

is this waiting on validation? Can I help? or we put an alpha out and as folks reporting the issue?

@bricefriha
Copy link
Contributor Author

bricefriha commented Nov 18, 2024

is this waiting on validation? Can I help?

Yes @jamescrosswell is going to run some tests to validate this.

or we put an alpha out and as folks reporting the issue?

I think it would be a good idea so James doesn't have to jump through hoops to test this, and I can also create a repo.
However, I think James wanted to compare the behaviours of this branch with the one of the main branch

@jamescrosswell
Copy link
Collaborator

Yes @jamescrosswell is going to run some tests to validate this.

Sorry for the delay. I've been trying to resolve #3764 (which is blocking this PR and anything else that targets main).

@jamescrosswell
Copy link
Collaborator

jamescrosswell commented Nov 20, 2024

@bricefriha I finally carved out some time to setup a provisioning profile etc. so that I can test this. At the moment, I'm not able to publish this to test though - I'm running into trim analysis errors like:

  Sentry.Samples.Maui net9.0-ios failed with 4 error(s) (23.1s) → bin/Release/net9.0-ios/ios-arm64/Sentry.Samples.Maui.dll
    /code/sentry-dotnet/samples/Sentry.Samples.Maui/obj/Release/net9.0-ios/ios-arm64/Microsoft.Maui.Controls.SourceGen/Microsoft.Maui.Controls.SourceGen.CodeBehindGenerator/Resources_Styles_Styles.xaml.sg.cs(33): Trim analysis error IL2026: __XamlGeneratedCode__.__Type68A1B57D17E9473E.InitializeComponent(): Using member 'Microsoft.Maui.Controls.Xaml.OnPlatformExtension.Default.get' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. The OnPlatformExtension is not trim safe. Use OnPlatform<T> instead.
    /code/sentry-dotnet/samples/Sentry.Samples.Maui/obj/Release/net9.0-ios/ios-arm64/Microsoft.Maui.Controls.SourceGen/Microsoft.Maui.Controls.SourceGen.CodeBehindGenerator/Resources_Styles_Styles.xaml.sg.cs(33): Trim analysis error IL2026: __XamlGeneratedCode__.__Type68A1B57D17E9473E.InitializeComponent(): Using member 'Microsoft.Maui.Controls.Xaml.OnPlatformExtension.Default.set' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. The OnPlatformExtension is not trim safe. Use OnPlatform<T> instead.
    /code/sentry-dotnet/samples/Sentry.Samples.Maui/obj/Release/net9.0-ios/ios-arm64/linked/System.Text.Json.dll : error IL3053: Assembly 'System.Text.Json' produced AOT analysis warnings.

This may be unrelated to your PR and more related to the fact that we bumped to net9.0... it's coming from the source generators, which is a bit odd. We might be referencing the wrong version of something.

It does, however, still prevent me from being able to test this 😞

@bricefriha
Copy link
Contributor Author

bricefriha commented Nov 20, 2024

This may be unrelated to your PR and more related to the fact that we bumped to net9.0... it's coming from the source generators, which is a bit odd. We might be referencing the wrong version of something.

That shouldn't be related because you should at least be able to publish the app entirely. I would try again with .net8.0 (for troubleshooting sake)

@bricefriha
Copy link
Contributor Author

bricefriha commented Nov 21, 2024

Sentry.Samples.Maui net9.0-ios failed with 4 error(s) (23.1s) → bin/Release/net9.0-ios/ios-arm64/Sentry.Samples.Maui.dll /code/sentry-dotnet/samples/Sentry.Samples.Maui/obj/Release/net9.0-ios/ios-arm64/Microsoft.Maui.Controls.SourceGen/Microsoft.Maui.Controls.SourceGen.CodeBehindGenerator/Resources_Styles_Styles.xaml.sg.cs(33): Trim analysis error IL2026: __XamlGeneratedCode__.__Type68A1B57D17E9473E.InitializeComponent(): Using member 'Microsoft.Maui.Controls.Xaml.OnPlatformExtension.Default.get' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. The OnPlatformExtension is not trim safe. Use OnPlatform<T> instead. /code/sentry-dotnet/samples/Sentry.Samples.Maui/obj/Release/net9.0-ios/ios-arm64/Microsoft.Maui.Controls.SourceGen/Microsoft.Maui.Controls.SourceGen.CodeBehindGenerator/Resources_Styles_Styles.xaml.sg.cs(33): Trim analysis error IL2026: __XamlGeneratedCode__.__Type68A1B57D17E9473E.InitializeComponent(): Using member 'Microsoft.Maui.Controls.Xaml.OnPlatformExtension.Default.set' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. The OnPlatformExtension is not trim safe. Use OnPlatform<T> instead. /code/sentry-dotnet/samples/Sentry.Samples.Maui/obj/Release/net9.0-ios/ios-arm64/linked/System.Text.Json.dll : error IL3053: Assembly 'System.Text.Json' produced AOT analysis warnings.

it sounds like something we have to remove /bin /obj file and rebuild to get it fixed. but I'll dig deeper on my side

Update

I experience the same issue now with .net9

@jamescrosswell
Copy link
Collaborator

jamescrosswell commented Dec 3, 2024

Still having difficulty testing this, unfortunately... due to challenges publishing an AOT compiled application to iOS.

When targeting net8.0 I ran into this:

And I couldn't get publishing going using Xcode 16.2 Beta 3. I think we might need to wait for the official 16.2 release as there's only so much time I can burn on fiddling with tooling that is constantly changing.

When targeting net9.0 I ran into this:

Eventually, we're going to resolve one/both of the above though and we can finally test and release this fix.

@jamescrosswell jamescrosswell self-requested a review December 4, 2024 21:12
@jamescrosswell
Copy link
Collaborator

Still having difficulty testing this, unfortunately... due to challenges publishing an AOT compiled application to iOS.

When targeting net8.0 I ran into this:

This has been addressed now with the release of Xcode 16.2.

When targeting net9.0 I ran into this:

Currently blocked by a bug in System.Text.Json:

Copy link
Collaborator

@jamescrosswell jamescrosswell left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bricefriha I'm not sure if you want to finish this PR or not.

I managed to test it by ignoring the trim warnings (setting <NoWarn>$(NoWarn);IL3053</NoWarn> in the csproj file).

However I didn't see any noticeable difference between the stack traces with MarshalManagedExceptionMode.UnwindNativeCode and MarshalManagedExceptionMode.Disabled.

I tested targeting net8.0-ios and net9.0-ios. The stack traces between those two targets were slightly different but neither was impacted by changing the MarshalManagedExceptionMode.

-->
<Target Name="_SentryMarshalExceptionManagement">
<PropertyGroup Condition="'$(PublishAot)' == 'true'">
<MtouchExtraArgs>marshal-managed-exceptions:disable</MtouchExtraArgs>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<MtouchExtraArgs>marshal-managed-exceptions:disable</MtouchExtraArgs>
<MtouchExtraArgs>--marshal-managed-exceptions:disable</MtouchExtraArgs>

I think there's a typo here right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Possibly a typo. It worked either way, In my experience

@bricefriha
Copy link
Contributor Author

@jamescrosswell Thanks for testing this.

However I didn't see any noticeable difference between the stack traces with MarshalManagedExceptionMode.UnwindNativeCode and MarshalManagedExceptionMode.Disabled.

I tested targeting net8.0-ios and net9.0-ios. The stack traces between those two targets were slightly different but neither was impacted by changing the MarshalManagedExceptionMode.

Did you experience a crash on launch?

@jamescrosswell
Copy link
Collaborator

Did you experience a crash on launch?

Yes, but that's because I followed Tipa's advice to throw an exception in AppDelegate.FinishedLaunching. The stack traces all looked like this for me:
image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[iOS / Native AOT] Bad stack trace due to unsupported marshalling behavior
4 participants