Skip to content

Commit

Permalink
feat: NativeAOT iOS support (#2820)
Browse files Browse the repository at this point in the history
Co-authored-by: Stefan Jandl <[email protected]>
  • Loading branch information
vaind and bitsandfoxes authored Nov 16, 2023
1 parent 0873db3 commit 0d643ff
Show file tree
Hide file tree
Showing 10 changed files with 85 additions and 22 deletions.
12 changes: 10 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,17 @@ Additionally, we're dropping support for some of the old target frameworks, plea

#### Native AOT

Native AOT publishing for compilation support for .NET 7+ has been added to Sentry, Sentry.Serilog, Sentry.Profiling, Sentry.OpenTelemetry and Sentry.NLog. There are some functional differences when publishing Native AOT:
Native AOT publishing support for .NET 8 has been added to Sentry for the following platforms:

- `StackTraceMode.Enhanced` is ignored because it's not available when publishing Native AOT. The mechanism to generate these ehanced stack traces relies heavily on reflection which isn't compatible with trimming.
- Windows
- Linux
- macOS
- Mac Catalyst
- iOS

There are some functional differences when publishing Native AOT:

- `StackTraceMode.Enhanced` is ignored because it's not available when publishing Native AOT. The mechanism to generate these enhanced stack traces relies heavily on reflection which isn't compatible with trimming.
- Reflection cannot be leveraged for JSON Serialization and you may need to use `SentryOptions.AddJsonSerializerContext` to supply a serialization context for types that you'd like to send to Sentry (e.g. in the `Span.Context`). ([#2732](https://github.com/getsentry/sentry-dotnet/pull/2732), [#2793](https://github.com/getsentry/sentry-dotnet/pull/2793))
- WinUI applications: when publishing Native AOT, Sentry isn't able to automatically register an unhandled exception handler because that relies on reflection. You'll need to [register the unhandled event handler manually](https://github.com/getsentry/sentry-dotnet/issues/2778) instead.

Expand Down
12 changes: 1 addition & 11 deletions samples/Sentry.Samples.Ios/Sentry.Samples.Ios.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<ImplicitUsings>true</ImplicitUsings>
<SupportedOSPlatformVersion>11.0</SupportedOSPlatformVersion>
<SelfContained>true</SelfContained>
<PublishAot>true</PublishAot>
</PropertyGroup>

<!--
Expand All @@ -19,17 +20,6 @@
<ProjectReference Include="..\..\src\Sentry\Sentry.csproj" />
</ItemGroup>

<!--
Use the arm64 runtime when building on arm64 Macs.
See https://github.com/xamarin/xamarin-macios/issues/17841
-->
<PropertyGroup>
<OSArchitecture>$([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture)</OSArchitecture>
<!-- Switch to this when running on an actual device -->
<!-- <RuntimeIdentifier Condition="'$(OSArchitecture)' == 'Arm64'">ios-arm64</RuntimeIdentifier>-->
<RuntimeIdentifier Condition="'$(OSArchitecture)' == 'Arm64' And ('$(_iOSRuntimeIdentifier)' == 'iossimulator-x64' Or ('$(_iOSRuntimeIdentifier)' == '' And '$(RuntimeIdentifier)' == ''))">iossimulator-arm64</RuntimeIdentifier>
</PropertyGroup>

<!--
To run on a device, you need to set the CodesignEntitlements property.
-->
Expand Down
2 changes: 2 additions & 0 deletions samples/Sentry.Samples.Maui/MauiProgram.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ public static MauiApp CreateMauiApp()
// By default, we will send the last 100 breadcrumbs with each event.
// If you want to see everything we can capture from MAUI, you may wish to use a larger value.
options.MaxBreadcrumbs = 1000;

options.Debug = true;
})

.ConfigureFonts(fonts =>
Expand Down
6 changes: 4 additions & 2 deletions samples/Sentry.Samples.Maui/Sentry.Samples.Maui.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
<SingleProject>true</SingleProject>
<ImplicitUsings>enable</ImplicitUsings>
<PublishReadyToRun>false</PublishReadyToRun>
<PublishAot Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios' or $([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'maccatalyst'">true</PublishAot>

<!-- Display name -->
<ApplicationTitle>Sentry.Samples.Maui</ApplicationTitle>
Expand Down Expand Up @@ -58,11 +59,12 @@
<TargetPlatformIdentifier>$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)'))</TargetPlatformIdentifier>

<RuntimeIdentifier Condition="'$(TargetPlatformIdentifier)' == 'android' And '$(OSArchitecture)' == 'Arm64'">android-arm64</RuntimeIdentifier>
<RuntimeIdentifier Condition="'$(TargetPlatformIdentifier)' == 'ios' And '$(OSArchitecture)' == 'Arm64'">iossimulator-arm64</RuntimeIdentifier>
<RuntimeIdentifier Condition="'$(TargetPlatformIdentifier)' == 'ios' And '$(_IsPublishing)' == 'true'">ios-arm64</RuntimeIdentifier>
<RuntimeIdentifier Condition="'$(TargetPlatformIdentifier)' == 'ios' And '$(OSArchitecture)' == 'Arm64' And '$(_IsPublishing)' != 'true'">iossimulator-arm64</RuntimeIdentifier>
<RuntimeIdentifier Condition="'$(TargetPlatformIdentifier)' == 'maccatalyst' And '$(OSArchitecture)' == 'Arm64'">maccatalyst-arm64</RuntimeIdentifier>

<RuntimeIdentifier Condition="'$(TargetPlatformIdentifier)' == 'android' And '$(OSArchitecture)' == 'x64'">android-x64</RuntimeIdentifier>
<RuntimeIdentifier Condition="'$(TargetPlatformIdentifier)' == 'ios' And '$(OSArchitecture)' == 'x64'">iossimulator-x64</RuntimeIdentifier>
<RuntimeIdentifier Condition="'$(TargetPlatformIdentifier)' == 'ios' And '$(OSArchitecture)' == 'x64' And '$(_IsPublishing)' != 'true'">iossimulator-x64</RuntimeIdentifier>
<RuntimeIdentifier Condition="'$(TargetPlatformIdentifier)' == 'maccatalyst' And '$(OSArchitecture)' == 'x64'">maccatalyst-x64</RuntimeIdentifier>
</PropertyGroup>

Expand Down
18 changes: 16 additions & 2 deletions src/Sentry/Internal/DebugStackTrace.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using Sentry.Internal.Extensions;
using Sentry.Extensibility;
using Sentry.Native;
using Sentry.Internal.ILSpy;
using Sentry.Protocol;

Expand All @@ -17,8 +16,11 @@ internal class DebugStackTrace : SentryStackTrace
private readonly Dictionary<Guid, int> _debugImageIndexByModule = new();
private const int DebugImageMissing = -1;
private bool _debugImagesMerged;

#if NET6_0_OR_GREATER
private Dictionary<long, DebugImage>? _nativeDebugImages;
private HashSet<long> _usedNativeDebugImages = new();
#endif

/*
* NOTE: While we could improve these regexes, doing so might break exception grouping on the backend.
Expand Down Expand Up @@ -220,6 +222,7 @@ private IEnumerable<SentryStackFrame> CreateFrames(StackTrace stackTrace, bool i
}
}

#if NET6_0_OR_GREATER
/// <summary>
/// Native AOT implementation of CreateFrame.
/// Native frames have only limited method information at runtime (and even that can be disabled).
Expand All @@ -237,7 +240,15 @@ private IEnumerable<SentryStackFrame> CreateFrames(StackTrace stackTrace, bool i
frame.ImageAddress = imageAddress;
frame.InstructionAddress = stackFrame.GetNativeIP();

_nativeDebugImages ??= C.LoadDebugImages(_options.DiagnosticLogger);
#if __ANDROID__
// TODO there will be support for NativeAOT in the future.
_nativeDebugImages ??= new();
#elif __IOS__ || MACCATALYST
_nativeDebugImages ??= Sentry.iOS.C.LoadDebugImages(_options.DiagnosticLogger);
#else
_nativeDebugImages ??= Sentry.Native.C.LoadDebugImages(_options.DiagnosticLogger);
#endif

if (!_usedNativeDebugImages.Contains(imageAddress) && _nativeDebugImages.TryGetValue(imageAddress, out var debugImage))
{
_usedNativeDebugImages.Add(imageAddress);
Expand All @@ -261,6 +272,7 @@ internal static SentryStackFrame ParseNativeAOTToString(string info)
}
return frame;
}
#endif

/// <summary>
/// Default the implementation of CreateFrame.
Expand Down Expand Up @@ -352,7 +364,9 @@ internal static SentryStackFrame ParseNativeAOTToString(string info)
internal SentryStackFrame? CreateFrame(IStackFrame stackFrame)
{
var frame = TryCreateManagedFrame(stackFrame);
#if NET6_0_OR_GREATER
frame ??= TryCreateNativeAOTFrame(stackFrame);
#endif
if (frame is null)
{
return null;
Expand Down
38 changes: 38 additions & 0 deletions src/Sentry/Platforms/iOS/CFunctions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using Sentry.Extensibility;
using Sentry.Internal.Extensions;
using Sentry.Protocol;

namespace Sentry.iOS;

internal static class C
{
internal static Dictionary<long, DebugImage> LoadDebugImages(IDiagnosticLogger? logger)
{
logger?.LogDebug("Collecting a list of native debug images.");
var result = new Dictionary<long, DebugImage>();
try
{
var cList = SentryCocoaHybridSdk.DebugImages;
logger?.LogDebug("There are {0} native debug images, parsing the information.", cList.Length);
foreach (var cItem in cList)
{
if (cItem.ImageAddress?.ParseHexAsLong() is { } imageAddress)
{
result.Add(imageAddress, new DebugImage()
{
CodeFile = cItem.CodeFile,
ImageAddress = imageAddress,
ImageSize = cItem.ImageSize?.LongValue,
DebugId = cItem.DebugID,
Type = cItem.Type,
});
}
}
}
catch (Exception e)
{
logger?.LogWarning("Error loading the list of debug images", e);
}
return result;
}
}
3 changes: 2 additions & 1 deletion src/Sentry/Sentry.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
<!-- Platform-specific props included here -->
<Import Project="Platforms\Android\Sentry.Android.props" Condition="'$(TargetPlatformIdentifier)' == 'android'" />
<Import Project="Platforms\iOS\Sentry.iOS.props" Condition="'$(TargetPlatformIdentifier)' == 'ios' Or '$(TargetPlatformIdentifier)' == 'maccatalyst'" />
<Import Project="Platforms\Native\Sentry.Native.targets"/>
<Import Project="Platforms\Native\Sentry.Native.targets"
Condition="'$(TargetPlatformIdentifier)' != 'android' and '$(TargetPlatformIdentifier)' != 'ios' and '$(TargetPlatformIdentifier)' != 'maccatalyst'"/>

<PropertyGroup Condition="'$(FrameworkSupportsAot)' == 'true'">
<IsAotCompatible>true</IsAotCompatible>
Expand Down
7 changes: 6 additions & 1 deletion src/Sentry/buildTransitive/Sentry.targets
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,12 @@
<!-- Native AOT publishing
While '_IsPublishing' looks a bit "private", it's also OK if it suddenly stops working and this step runs anyway.
See https://github.com/dotnet/sdk/issues/26324#issuecomment-1169236993 -->
<PropertyGroup Condition="'$(PublishDir)' != '' and '$(PublishAot)' == 'true' and '$(_IsPublishing)' == 'true'">
<PropertyGroup Condition="
'$(PublishDir)' != ''
and '$(PublishAot)' == 'true'
and '$(_IsPublishing)' == 'true'
and $([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) != 'ios'
and $([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) != 'maccatalyst'">
<SentryCLIUploadNativeAOT>true</SentryCLIUploadNativeAOT>
<SentryCLIUploadDirectory>$(PublishDir)</SentryCLIUploadDirectory>
<!-- We must run after "_CopyAotSymbols" (not "Publish"), because that is the one that actually copies debug symbols to the publish directory. -->
Expand Down
6 changes: 4 additions & 2 deletions test/Sentry.Tests/Internals/DebugStackTraceTests.verify.cs
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ void CheckStackTraceIsUnchanged(SentryStackTrace stackTrace)
}
}

#if NET6_0_OR_GREATER
[Fact]
public void ParseNativeAOTToString()
{
Expand All @@ -214,8 +215,8 @@ public void ParseNativeAOTToString()
Assert.Null(frame.Package);
}

// TODO: Create integration test to test this behaviour when publishing AOT apps
// See https://github.com/getsentry/sentry-dotnet/issues/2772
// TODO: Create integration test to test this behaviour when publishing AOT apps
// See https://github.com/getsentry/sentry-dotnet/issues/2772
[Fact]
public Task CreateFrame_ForNativeAOT()
{
Expand All @@ -230,6 +231,7 @@ public Task CreateFrame_ForNativeAOT()

return VerifyJson(frame.ToJsonString());
}
#endif

private class InjectableDebugStackTrace : DebugStackTrace
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ public void ConfigureAppFrame_NativeAOTWithoutMethodInfo_InAppIsNull()
Assert.Null(sut.InApp);
}

#if NET6_0_OR_GREATER
[Fact]
public void ConfigureAppFrame_NativeAOTWithoutMethodInfo_InAppIsSet()
{
Expand All @@ -244,5 +245,5 @@ public void ConfigureAppFrame_NativeAOTWithoutMethodInfo_InAppIsSet()
sut.ConfigureAppFrame(new());
Assert.True(sut.InApp);
}

#endif
}

0 comments on commit 0d643ff

Please sign in to comment.