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

Enable symbolication and source context for net9.0-android #4033

Draft
wants to merge 21 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
7e7e512
Ensure device tests run on net8.0 and net9.0
jamescrosswell Mar 13, 2025
d569cf0
Move existing reader to V1 directory
jamescrosswell Mar 13, 2025
f660e63
Bumped NSubstitute to versino 5.3.0
jamescrosswell Mar 17, 2025
0b36c8c
Update Sentry.Extensions.Logging.Tests.csproj
jamescrosswell Mar 17, 2025
b94434a
Merge branch 'main' into store-v2
jamescrosswell Mar 17, 2025
4e9183b
Update Sentry.Maui.Tests.csproj
jamescrosswell Mar 17, 2025
0c22666
Update device-tests-android.yml
jamescrosswell Mar 17, 2025
9420d86
Update CHANGELOG.md
jamescrosswell Mar 17, 2025
1291467
Minimal work required to add the store v2 files to the repo with attr…
jamescrosswell Mar 18, 2025
339f4ef
make members internal
jamescrosswell Mar 18, 2025
d18d462
Tests pass on net8.0 and net9.0
jamescrosswell Mar 19, 2025
1c6494e
Merge branch 'main' into store-v2
jamescrosswell Mar 19, 2025
75e15c7
Format code
getsentry-bot Mar 19, 2025
fc3e17a
Removed Xamarin.LibZipSharp dependency
jamescrosswell Mar 19, 2025
d618ac2
Removed dependency on System.IO.Hashing
jamescrosswell Mar 19, 2025
509482b
Format code
getsentry-bot Mar 19, 2025
956f1c6
Update AndroidAssemblyReaderTests.cs
jamescrosswell Mar 19, 2025
8b95555
Merge branch 'store-v2' of github.com:getsentry/sentry-dotnet into st…
jamescrosswell Mar 19, 2025
0a9580b
Revert "Merge branch 'store-v2' of github.com:getsentry/sentry-dotnet…
jamescrosswell Mar 19, 2025
bc58628
Removed junk comments added by dotnet format
jamescrosswell Mar 20, 2025
bbfa502
Format code
getsentry-bot Mar 20, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 23 additions & 9 deletions .github/workflows/device-tests-android.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,12 @@ on:

jobs:
build:
name: Build (${{ matrix.tfm }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
tfm: [net8.0, net9.0]
env:
DOTNET_CLI_TELEMETRY_OPTOUT: 1
DOTNET_NOLOGO: 1
Expand All @@ -32,25 +37,35 @@ jobs:
uses: ./.github/actions/buildnative

- name: Build Android Test App
run: pwsh ./scripts/device-test.ps1 android -Build
run: pwsh ./scripts/device-test.ps1 android -Build -Tfm ${{ matrix.tfm }}

- name: Upload Android Test App (net8.0)
if: matrix.tfm == 'net8.0'
uses: actions/upload-artifact@v4
with:
name: device-test-android-net8.0
if-no-files-found: error
path: test/Sentry.Maui.Device.TestApp/bin/Release/net8.0-android/android-x64/io.sentry.dotnet.maui.device.testapp-Signed.apk

- name: Upload Android Test App
- name: Upload Android Test App (net9.0)
if: matrix.tfm == 'net9.0'
uses: actions/upload-artifact@v4
with:
name: device-test-android
name: device-test-android-net9.0
if-no-files-found: error
path: test/Sentry.Maui.Device.TestApp/bin/Release/net8.0-android34.0/android-x64/io.sentry.dotnet.maui.device.testapp-Signed.apk
path: test/Sentry.Maui.Device.TestApp/bin/Release/net9.0-android/android-x64/io.sentry.dotnet.maui.device.testapp-Signed.apk

android:
needs: [build]
name: Run Android API-${{ matrix.api-level }} Test
name: Run Android API-${{ matrix.api-level }} Test (${{ matrix.tfm }})

# Requires a "larger runner", for nested virtualization support
runs-on: ubuntu-latest-4-cores

strategy:
fail-fast: false
matrix:
tfm: [net8.0, net9.0]
# We run against both an older and a newer API
api-level: [27, 33]
env:
Expand All @@ -70,15 +85,14 @@ jobs:
- name: Download test app artifact
uses: actions/download-artifact@v4
with:
name: device-test-android
name: device-test-android-${{ matrix.tfm }}
path: bin

- name: Setup Gradle
uses: gradle/actions/setup-gradle@94baf225fe0a508e581a564467443d0e2379123b # pin@v3

# Cached AVD setup per https://github.com/ReactiveCircus/android-emulator-runner/blob/main/README.md


- name: Run Tests
timeout-minutes: 40
uses: reactivecircus/android-emulator-runner@62dbb605bba737720e10b196cb4220d374026a6d # Tag: v2.33.0
Expand All @@ -92,11 +106,11 @@ jobs:
disk-size: 4096M
emulator-options: -no-snapshot-save -no-window -accel on -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
disable-animations: false
script: pwsh scripts/device-test.ps1 android -Run
script: pwsh scripts/device-test.ps1 android -Run -Tfm ${{ matrix.tfm }}

- name: Upload results
if: success() || failure()
uses: actions/upload-artifact@v4
with:
name: device-test-android-${{ matrix.api-level }}-results
name: device-test-android-${{ matrix.api-level }}-${{ matrix.tfm }}-results
path: test_output
9 changes: 5 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@

## Unreleased

### Fixes

- Target `net9.0` on Sentry.Google.Cloud.Functions to avoid conflict with Sentry.AspNetCore ([#4039](https://github.com/getsentry/sentry-dotnet/pull/4039))

### Features

- Exception.HResult is now included in the mechanism data for all exceptions ([#4029](https://github.com/getsentry/sentry-dotnet/pull/4029))

### Fixes

- Fixed symbolication and source context for net9.0-android ([#4033](https://github.com/getsentry/sentry-dotnet/pull/4033))
- Target `net9.0` on Sentry.Google.Cloud.Functions to avoid conflict with Sentry.AspNetCore ([#4039](https://github.com/getsentry/sentry-dotnet/pull/4039))

### Dependencies

- Bump CLI from v2.42.2 to v2.42.3 ([#4036](https://github.com/getsentry/sentry-dotnet/pull/4036))
Expand Down
11 changes: 11 additions & 0 deletions ELFPayloadError.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace Xamarin.Android.AssemblyStore;

enum ELFPayloadError
{
None,
NotELF,
LoadFailed,
NotSharedLibrary,
NotLittleEndian,
NoPayloadSection,
}
2 changes: 1 addition & 1 deletion benchmarks/Sentry.Benchmarks/Sentry.Benchmarks.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.13.12" />
<PackageReference Include="BenchmarkDotNet.Diagnostics.Windows" Version="0.13.12" />
<PackageReference Include="NSubstitute" Version="5.1.0" />
<PackageReference Include="NSubstitute" Version="5.3.0" />
</ItemGroup>

<ItemGroup>
Expand Down
18 changes: 11 additions & 7 deletions scripts/device-test.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ param(
[String] $Platform,

[Switch] $Build,
[Switch] $Run
[Switch] $Run,
[String] $Tfm
)

Set-StrictMode -Version latest
Expand All @@ -21,13 +22,16 @@ $CI = Test-Path env:CI
Push-Location $PSScriptRoot/..
try
{
$tfm = 'net8.0-'
if (!$Tfm)
{
$Tfm = 'net8.0'
}
$arch = (!$IsWindows -and $(uname -m) -eq 'arm64') ? 'arm64' : 'x64'
if ($Platform -eq 'android')
{
$tfm += 'android34.0'
$Tfm += '-android'
$group = 'android'
$buildDir = $CI ? 'bin' : "test/Sentry.Maui.Device.TestApp/bin/Release/$tfm/android-$arch"
$buildDir = $CI ? 'bin' : "test/Sentry.Maui.Device.TestApp/bin/Release/$Tfm/android-$arch"
$arguments = @(
'--app', "$buildDir/io.sentry.dotnet.maui.device.testapp-Signed.apk",
'--package-name', 'io.sentry.dotnet.maui.device.testapp',
Expand All @@ -43,11 +47,11 @@ try
}
elseif ($Platform -eq 'ios')
{
$tfm += 'ios17.0'
$Tfm += '-ios'
$group = 'apple'
# Always use x64 on iOS, since arm64 doesn't support JIT, which is required for tests using NSubstitute
$arch = 'x64'
$buildDir = "test/Sentry.Maui.Device.TestApp/bin/Release/$tfm/iossimulator-$arch"
$buildDir = "test/Sentry.Maui.Device.TestApp/bin/Release/$Tfm/iossimulator-$arch"
$envValue = $CI ? 'true' : 'false'
$arguments = @(
'--app', "$buildDir/Sentry.Maui.Device.TestApp.app",
Expand All @@ -60,7 +64,7 @@ try
if ($Build)
{
# We disable AOT for device tests: https://github.com/nsubstitute/NSubstitute/issues/834
dotnet build -f $tfm -c Release -p:EnableAot=false -p:NoSymbolStrip=true test/Sentry.Maui.Device.TestApp
dotnet build -f $Tfm -c Release -p:EnableAot=false -p:NoSymbolStrip=true test/Sentry.Maui.Device.TestApp
if ($LASTEXITCODE -ne 0)
{
throw 'Failed to build Sentry.Maui.Device.TestApp'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
using Sentry.Android.AssemblyReader.V1;
using Sentry.Android.AssemblyReader.V2;

namespace Sentry.Android.AssemblyReader;

// The "Old" app type - where each DLL is placed in the 'assemblies' directory as an individual file.
Expand Down Expand Up @@ -25,13 +28,8 @@ public AndroidAssemblyDirectoryReader(ZipArchive zip, IList<string> supportedAbi
Logger?.Invoke("Resolved assembly {0} in the APK at {1}", name, zipEntry.FullName);

// We need a seekable stream for the PEReader (or even to check whether the DLL is compressed), so make a copy.
var memStream = new MemoryStream((int)zipEntry.Length);
using (var zipStream = zipEntry.Open())
{
zipStream.CopyTo(memStream);
memStream.Position = 0;
}
return CreatePEReader(name, memStream);
var memStream = zipEntry.Extract();
return CreatePEReader(name, memStream, Logger);
}

private ZipArchiveEntry? FindAssembly(string name)
Expand Down
8 changes: 4 additions & 4 deletions src/Sentry.Android.AssemblyReader/AndroidAssemblyReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ public void Dispose()
ZipArchive.Dispose();
}

protected PEReader CreatePEReader(string assemblyName, MemoryStream inputStream)
internal static PEReader CreatePEReader(string assemblyName, MemoryStream inputStream, DebugLogger? logger)
{
var decompressedStream = TryDecompressLZ4(assemblyName, inputStream);
var decompressedStream = TryDecompressLZ4(assemblyName, inputStream, logger);

// Use the decompressed stream, or if null, i.e. it wasn't compressed, use the original.
return new PEReader(decompressedStream ?? inputStream);
Expand All @@ -35,7 +35,7 @@ protected PEReader CreatePEReader(string assemblyName, MemoryStream inputStream)
/// [rest: lz4 compressed payload]
/// </summary>
/// <seealso href="https://github.com/xamarin/xamarin-android/blob/c92702619f5fabcff0ed88e09160baf9edd70f41/tools/decompress-assemblies/main.cs#L26" />
private Stream? TryDecompressLZ4(string assemblyName, MemoryStream inputStream)
private static Stream? TryDecompressLZ4(string assemblyName, MemoryStream inputStream, DebugLogger? logger)
{
const uint compressedDataMagic = 0x5A4C4158; // 'XALZ', little-endian
const int payloadOffset = 12;
Expand All @@ -51,7 +51,7 @@ protected PEReader CreatePEReader(string assemblyName, MemoryStream inputStream)
Debug.Assert(inputStream.Position == payloadOffset);
var inputLength = (int)(inputStream.Length - payloadOffset);

Logger?.Invoke("Decompressing assembly ({0} bytes uncompressed) using LZ4", decompressedLength);
logger?.Invoke("Decompressing assembly ({0} bytes uncompressed) using LZ4", decompressedLength);

var outputStream = new MemoryStream(decompressedLength);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
using Sentry.Android.AssemblyReader.V1;
using Sentry.Android.AssemblyReader.V2;

namespace Sentry.Android.AssemblyReader;

/// <summary>
Expand All @@ -15,14 +18,23 @@ public static class AndroidAssemblyReaderFactory
public static IAndroidAssemblyReader Open(string apkPath, IList<string> supportedAbis, DebugLogger? logger = null)
{
logger?.Invoke("Opening APK: {0}", apkPath);
var zipArchive = ZipFile.Open(apkPath, ZipArchiveMode.Read);

// Try to read using the v2 store format
if (AndroidAssemblyStoreReaderV2.TryReadStore(apkPath, supportedAbis, logger, out var readerV2))
{
logger?.Invoke("APK uses AssemblyStore V2");
return readerV2;
}

// Try to read using the v1 store format
var zipArchive = ZipFile.Open(apkPath, ZipArchiveMode.Read);
if (zipArchive.GetEntry("assemblies/assemblies.manifest") is not null)
{
logger?.Invoke("APK uses AssemblyStore");
return new AndroidAssemblyStoreReader(zipArchive, supportedAbis, logger);
logger?.Invoke("APK uses AssemblyStore V1");
return new AndroidAssemblyStoreReaderV1(zipArchive, supportedAbis, logger);
}

// Finally, try to read from file system
logger?.Invoke("APK doesn't use AssemblyStore");
return new AndroidAssemblyDirectoryReader(zipArchive, supportedAbis, logger);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>netstandard2.0;net8.0;net9.0</TargetFrameworks>
<TargetFrameworks>net8.0;net9.0</TargetFrameworks>
<Description>.NET assembly reader for Android</Description>
</PropertyGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<PackageReference Include="System.Reflection.Metadata" Version="5.0.0" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="ELFSharp" Version="2.17.3" />
<PackageReference Include="K4os.Compression.LZ4" Version="1.3.5" />
</ItemGroup>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
namespace Sentry.Android.AssemblyReader;
namespace Sentry.Android.AssemblyReader.V1;

// See https://devblogs.microsoft.com/dotnet/performance-improvements-in-dotnet-maui/#single-file-assembly-stores
internal sealed class AndroidAssemblyStoreReader : AndroidAssemblyReader, IAndroidAssemblyReader
internal sealed class AndroidAssemblyStoreReaderV1 : AndroidAssemblyReader, IAndroidAssemblyReader
{
private readonly AssemblyStoreExplorer _explorer;

public AndroidAssemblyStoreReader(ZipArchive zip, IList<string> supportedAbis, DebugLogger? logger)
public AndroidAssemblyStoreReaderV1(ZipArchive zip, IList<string> supportedAbis, DebugLogger? logger)
: base(zip, supportedAbis, logger)
{
_explorer = new(zip, supportedAbis, logger);
Expand All @@ -29,7 +29,7 @@ public AndroidAssemblyStoreReader(ZipArchive zip, IList<string> supportedAbis, D
return null;
}

return CreatePEReader(name, stream);
return CreatePEReader(name, stream, Logger);
}

private AssemblyStoreAssembly? TryFindAssembly(string name)
Expand Down
28 changes: 28 additions & 0 deletions src/Sentry.Android.AssemblyReader/V2/ATTRIBUTION.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
Parts of the code in this subdirectory have been adapted from
https://github.com/dotnet/android/blob/5ebcb1dd1503648391e3c0548200495f634d90c6/tools/assembly-store-reader-mk2/assembly-store-reader.csproj

The original license is as follows:

The MIT License (MIT)

Copyright (c) .NET Foundation and Contributors

All rights reserved.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Loading