diff --git a/integration-test/ios.Tests.ps1 b/integration-test/ios.Tests.ps1 index 4be943d06c..9e88a60cd9 100644 --- a/integration-test/ios.Tests.ps1 +++ b/integration-test/ios.Tests.ps1 @@ -25,6 +25,9 @@ Describe 'iOS app (, , )' -ForEach @( Remove-Item -Path "$PSScriptRoot/mobile-app" -Recurse -Force -ErrorAction SilentlyContinue Copy-Item -Path "$PSScriptRoot/net9-maui" -Destination "$PSScriptRoot/mobile-app" -Recurse -Force + # clean up potential old copied build outputs that may target a different configuration or runtime + Remove-Item -Path "$PSScriptRoot/mobile-app/bin", "$PSScriptRoot/mobile-app/obj" ` + -Recurse -Force -ErrorAction SilentlyContinue Push-Location $PSScriptRoot/mobile-app $arch = [System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture.ToString().ToLower() @@ -107,6 +110,19 @@ Describe 'iOS app (, , )' -ForEach @( $result.Envelopes() | Should -HaveCount 1 } + It 'does not leak managed exception as NSException (, )' { + $result = Invoke-SentryServer { + param([string]$url) + RunIosApp -Dsn $url -TestArg "OnActivated" + RunIosApp -Dsn $url + } + + $result.HasErrors() | Should -BeFalse + $result.Envelopes() | Should -AnyElementMatch "`"type`":`"System.ApplicationException`"" + $result.Envelopes() | Should -Not -AnyElementMatch "`"type`":`"nsexception`"" + $result.Envelopes() | Should -HaveCount 1 + } + It 'captures native crash (, )' { $result = Invoke-SentryServer { param([string]$url) diff --git a/integration-test/net9-maui/App.xaml.cs b/integration-test/net9-maui/App.xaml.cs index c97382096c..2db25f2cea 100644 --- a/integration-test/net9-maui/App.xaml.cs +++ b/integration-test/net9-maui/App.xaml.cs @@ -61,6 +61,16 @@ protected override Window CreateWindow(IActivationState? activationState) return new Window(new AppShell()); } + public static void OnActivated() + { + testArg = System.Environment.GetEnvironmentVariable("SENTRY_TEST_ARG"); + + if (HasTestArg("OnActivated")) + { + throw new ApplicationException("This exception was thrown deliberately from AppDelegate.OnActivated."); + } + } + public static void OnAppearing() { testArg = System.Environment.GetEnvironmentVariable("SENTRY_TEST_ARG"); diff --git a/integration-test/net9-maui/Platforms/iOS/AppDelegate.cs b/integration-test/net9-maui/Platforms/iOS/AppDelegate.cs index 24f2626f1c..ae2426b6bb 100644 --- a/integration-test/net9-maui/Platforms/iOS/AppDelegate.cs +++ b/integration-test/net9-maui/Platforms/iOS/AppDelegate.cs @@ -1,4 +1,5 @@ using Foundation; +using UIKit; namespace Sentry.Maui.Device.IntegrationTestApp; @@ -6,4 +7,10 @@ namespace Sentry.Maui.Device.IntegrationTestApp; public class AppDelegate : MauiUIApplicationDelegate { protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); + + public override void OnActivated(UIApplication application) + { + base.OnActivated(application); + App.OnActivated(); + } } diff --git a/src/Sentry/Platforms/Cocoa/RuntimeMarshalManagedExceptionIntegration.cs b/src/Sentry/Platforms/Cocoa/RuntimeMarshalManagedExceptionIntegration.cs index df1444970b..7cef2e920d 100644 --- a/src/Sentry/Platforms/Cocoa/RuntimeMarshalManagedExceptionIntegration.cs +++ b/src/Sentry/Platforms/Cocoa/RuntimeMarshalManagedExceptionIntegration.cs @@ -54,9 +54,10 @@ internal void Handle(object sender, MarshalManagedExceptionEventArgs e) return; } + e.ExceptionMode = MarshalManagedExceptionMode.Abort; + // Otherwise the runtime will call abort() after we return — directly via - // xamarin_assertion_message, or indirectly via the uncaught-NSException handler for - // ThrowObjectiveCException. Tell SentryCrash to ignore that SIGABRT so we don't emit a + // xamarin_assertion_message. Tell SentryCrash to ignore that SIGABRT so we don't emit a // duplicate native crash for an exception we've already captured. See // https://github.com/dotnet/macios/blob/be8a2ca1057242f745ef58011a02ffe21326d180/runtime/runtime.m#L2285 const int SIGABRT = 6; diff --git a/test/Sentry.Tests/Platforms/iOS/RuntimeMarshalManagedExceptionIntegrationTests.cs b/test/Sentry.Tests/Platforms/iOS/RuntimeMarshalManagedExceptionIntegrationTests.cs index b0c12feaa0..9709e638b0 100644 --- a/test/Sentry.Tests/Platforms/iOS/RuntimeMarshalManagedExceptionIntegrationTests.cs +++ b/test/Sentry.Tests/Platforms/iOS/RuntimeMarshalManagedExceptionIntegrationTests.cs @@ -82,6 +82,18 @@ public void Handle_Mono_AbortingMode_IgnoresSigabrt(MarshalManagedExceptionMode _fixture.Runtime.Received(1).IgnoreNextSignal(6); } + [Fact] + public void Handle_ThrowObjectiveCException_ChangesModeToAbort() + { + var sut = _fixture.GetSut(); + sut.Register(_fixture.Hub, SentryOptions); + var args = new MarshalManagedExceptionEventArgs { Exception = new Exception(), ExceptionMode = MarshalManagedExceptionMode.ThrowObjectiveCException }; + + sut.Handle(this, args); + + Assert.Equal(MarshalManagedExceptionMode.Abort, args.ExceptionMode); + } + [Theory] [InlineData(MarshalManagedExceptionMode.Disable)] [InlineData(MarshalManagedExceptionMode.UnwindNativeCode)]