From 2c13d2ff4629f4911cffd0f45a177d80e74be846 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Tue, 27 May 2025 22:25:06 +1200 Subject: [PATCH 1/6] Fixes symbolication for net9.0-android applications in Release configuration Resolves #4209: - https://github.com/getsentry/sentry-dotnet/issues/4209#issuecomment-2911168352 --- .../V2/AndroidAssemblyDirectoryReaderV2.cs | 5 +++ .../V2/AndroidAssemblyStoreReaderV2.cs | 33 +++++++++++++++---- .../V2/MonoAndroidHelper.Basic.cs | 25 ++++++++++++++ src/Sentry/Internal/DebugStackTrace.cs | 1 + 4 files changed, 57 insertions(+), 7 deletions(-) diff --git a/src/Sentry.Android.AssemblyReader/V2/AndroidAssemblyDirectoryReaderV2.cs b/src/Sentry.Android.AssemblyReader/V2/AndroidAssemblyDirectoryReaderV2.cs index d89f81da79..6a99af6ba2 100644 --- a/src/Sentry.Android.AssemblyReader/V2/AndroidAssemblyDirectoryReaderV2.cs +++ b/src/Sentry.Android.AssemblyReader/V2/AndroidAssemblyDirectoryReaderV2.cs @@ -12,6 +12,7 @@ public AndroidAssemblyDirectoryReaderV2(string apkPath, IList supportedA Logger = logger; foreach (var abi in supportedAbis) { + logger?.Invoke("Adding {0} to supported architectures for Directory Reader", abi); SupportedArchitectures.Add(abi.AbiToDeviceArchitecture()); } _archiveAssemblyHelper = new ArchiveAssemblyHelper(apkPath, logger); @@ -25,11 +26,13 @@ public AndroidAssemblyDirectoryReaderV2(string apkPath, IList supportedA var stream = File.OpenRead(name); return new PEReader(stream); } + Logger?.Invoke("File {0} does not exist in the APK", name); foreach (var arch in SupportedArchitectures) { if (_archiveAssemblyHelper.ReadEntry($"assemblies/{name}", arch) is not { } memStream) { + Logger?.Invoke("Couldn't find entry {0} in the APK for the {1} architecture", name, arch); continue; } @@ -137,6 +140,7 @@ public ArchiveAssemblyHelper(string archivePath, DebugLogger? logger) var potentialEntries = TransformArchiveAssemblyPath(path, arch); if (potentialEntries == null || potentialEntries.Count == 0) { + _logger?.Invoke("No potential entries for path '{0}' with arch '{1}'", path, arch); return null; } @@ -145,6 +149,7 @@ public ArchiveAssemblyHelper(string archivePath, DebugLogger? logger) { if (zip.GetEntry(assemblyPath) is not { } entry) { + _logger?.Invoke("No entry found for path '{0}' in archive '{1}'", assemblyPath, _archivePath); continue; } diff --git a/src/Sentry.Android.AssemblyReader/V2/AndroidAssemblyStoreReaderV2.cs b/src/Sentry.Android.AssemblyReader/V2/AndroidAssemblyStoreReaderV2.cs index 73b170a0c5..f88e0ca237 100644 --- a/src/Sentry.Android.AssemblyReader/V2/AndroidAssemblyStoreReaderV2.cs +++ b/src/Sentry.Android.AssemblyReader/V2/AndroidAssemblyStoreReaderV2.cs @@ -13,16 +13,35 @@ private AndroidAssemblyStoreReaderV2(IList explorers, Deb public static bool TryReadStore(string inputFile, IList supportedAbis, DebugLogger? logger, [NotNullWhen(true)] out AndroidAssemblyStoreReaderV2? reader) { + List supportedExplorers = []; + + // First we check the base.apk for an assembly store var (explorers, errorMessage) = AssemblyStoreExplorer.Open(inputFile, logger); - if (errorMessage != null) + if (explorers is null) { - logger?.Invoke(errorMessage); - reader = null; - return false; - } + logger?.Invoke("Unable to read store information for {0}: {1}", inputFile, errorMessage); - List supportedExplorers = []; - if (explorers is not null) + // Check for assembly stores in any device specific APKs + foreach (var supportedAbi in supportedAbis) + { + var splitFilePath = inputFile.GetArchivePathForAbi(supportedAbi, logger); + if (!File.Exists(splitFilePath)) + { + logger?.Invoke("No split config detected at: '{0}'", splitFilePath); + continue; + } + (explorers, errorMessage) = AssemblyStoreExplorer.Open(splitFilePath, logger); + if (explorers is not null) + { + supportedExplorers.AddRange(explorers); // If the error is null then this is not null + } + else + { + logger?.Invoke("Unable to read store information for {0}: {1}", splitFilePath, errorMessage); + } + } + } + else { foreach (var explorer in explorers) { diff --git a/src/Sentry.Android.AssemblyReader/V2/MonoAndroidHelper.Basic.cs b/src/Sentry.Android.AssemblyReader/V2/MonoAndroidHelper.Basic.cs index fda2ff8ad1..d305ac9a3e 100644 --- a/src/Sentry.Android.AssemblyReader/V2/MonoAndroidHelper.Basic.cs +++ b/src/Sentry.Android.AssemblyReader/V2/MonoAndroidHelper.Basic.cs @@ -89,4 +89,29 @@ public static string MakeZipArchivePath(string part1, ICollection? pathP public const string MANGLED_ASSEMBLY_REGULAR_ASSEMBLY_MARKER = "lib_"; public const string MANGLED_ASSEMBLY_SATELLITE_ASSEMBLY_MARKER = "lib-"; public const string SATELLITE_CULTURE_END_MARKER_CHAR = "_"; + + /// + /// When an AAB file is deployed, the APK is split into multiple APKs so our modules can end up in a companion + /// architecture specific APK like split_config.arm64_v8a.apk. This method returns the path to that split_config APK + /// if it exists... otherwise we just return the original archive path. + /// + internal static string GetArchivePathForArchitecture(this string archivePath, AndroidTargetArch arch, DebugLogger? logger) + { + return ArchToAbiMap.TryGetValue(arch, out var abi) + ? GetArchivePathForAbi(archivePath, abi, logger) + : archivePath; + } + + /// + /// When an AAB file is deployed, the APK is split into multiple APKs so our modules can end up in a companion + /// architecture specific APK like split_config.arm64_v8a.apk. This method returns the path to that split_config APK + /// if it exists... otherwise we just return the original archive path. + /// + internal static string GetArchivePathForAbi(this string archivePath, string abi, DebugLogger? logger) + { + var basePath = Path.GetDirectoryName(archivePath) ?? string.Empty; + var abiPart = abi.Replace("-", "_"); + var splitFilePath = Path.Combine(basePath, $"split_config.{abiPart}.apk"); + return splitFilePath; + } } diff --git a/src/Sentry/Internal/DebugStackTrace.cs b/src/Sentry/Internal/DebugStackTrace.cs index 3f43297772..7c509d627f 100644 --- a/src/Sentry/Internal/DebugStackTrace.cs +++ b/src/Sentry/Internal/DebugStackTrace.cs @@ -249,6 +249,7 @@ private IEnumerable CreateFrames(StackTrace stackTrace, bool i return null; } + _options.LogDebug("Attempting to get debug image for native AOT Frame"); var imageAddress = stackFrame.GetNativeImageBase(); var frame = ParseNativeAOTToString(stackFrame.ToString()); frame.ImageAddress = imageAddress; From 102d413788a5e25e45a2d5c06eff969cda1631a7 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Wed, 28 May 2025 14:38:30 +1200 Subject: [PATCH 2/6] Update CHANGELOG.md --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d6408973d3..61c65a0e2a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## Unreleased + +### Fixes + +- Fixed symbolication for net9.0-android applications in Release config ([#4221](https://github.com/getsentry/sentry-dotnet/pull/4221)) + ## 5.8.1 ### Fixes From e4faacda79c3fd40ec1febf635bf253e2c9eaf3f Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Thu, 29 May 2025 20:42:42 +1200 Subject: [PATCH 3/6] Read assembly from device specific APKs when AndroidUseAssemblyStore is false --- .../V2/AndroidAssemblyDirectoryReaderV2.cs | 51 +++++++++++++++---- .../V2/MonoAndroidHelper.Basic.cs | 12 ----- 2 files changed, 40 insertions(+), 23 deletions(-) diff --git a/src/Sentry.Android.AssemblyReader/V2/AndroidAssemblyDirectoryReaderV2.cs b/src/Sentry.Android.AssemblyReader/V2/AndroidAssemblyDirectoryReaderV2.cs index 6a99af6ba2..c3ee3139c9 100644 --- a/src/Sentry.Android.AssemblyReader/V2/AndroidAssemblyDirectoryReaderV2.cs +++ b/src/Sentry.Android.AssemblyReader/V2/AndroidAssemblyDirectoryReaderV2.cs @@ -15,7 +15,7 @@ public AndroidAssemblyDirectoryReaderV2(string apkPath, IList supportedA logger?.Invoke("Adding {0} to supported architectures for Directory Reader", abi); SupportedArchitectures.Add(abi.AbiToDeviceArchitecture()); } - _archiveAssemblyHelper = new ArchiveAssemblyHelper(apkPath, logger); + _archiveAssemblyHelper = new ArchiveAssemblyHelper(apkPath, logger, supportedAbis); } public PEReader? TryReadAssembly(string name) @@ -59,8 +59,9 @@ internal class ArchiveAssemblyHelper private readonly string _archivePath; private readonly DebugLogger? _logger; + private readonly IList _supportedAbis; - public ArchiveAssemblyHelper(string archivePath, DebugLogger? logger) + public ArchiveAssemblyHelper(string archivePath, DebugLogger? logger, IList supportedAbis) { if (string.IsNullOrEmpty(archivePath)) { @@ -69,6 +70,7 @@ public ArchiveAssemblyHelper(string archivePath, DebugLogger? logger) _archivePath = archivePath; _logger = logger; + _supportedAbis = supportedAbis; } public MemoryStream? ReadEntry(string path, AndroidTargetArch arch = AndroidTargetArch.None, bool uncompressIfNecessary = false) @@ -144,21 +146,48 @@ public ArchiveAssemblyHelper(string archivePath, DebugLogger? logger) return null; } - using var zip = ZipFile.OpenRead(_archivePath); - foreach (var assemblyPath in potentialEntries) + // First we check the base.apk + if (ReadEntryFromApk(_archivePath) is {} baseEntry) { - if (zip.GetEntry(assemblyPath) is not { } entry) + _logger?.Invoke("Found entry '{0}' in base archive '{1}'", path, _archivePath); + return baseEntry; + } + + // Otherwise check in the device specific APKs + foreach (var supportedAbi in _supportedAbis) + { + var splitFilePath = _archivePath.GetArchivePathForAbi(supportedAbi, _logger); + if (!File.Exists(splitFilePath)) { - _logger?.Invoke("No entry found for path '{0}' in archive '{1}'", assemblyPath, _archivePath); - continue; + _logger?.Invoke("No split config detected at: '{0}'", splitFilePath); + } + else if (ReadEntryFromApk(splitFilePath) is { } splitEntry) + { + return splitEntry; } - - var ret = entry.Extract(); - ret.Flush(); - return ret; } + // Finally admit defeat return null; + + MemoryStream? ReadEntryFromApk(string archivePath) + { + using var zip = ZipFile.OpenRead(archivePath); + foreach (var assemblyPath in potentialEntries) + { + if (zip.GetEntry(assemblyPath) is not { } entry) + { + _logger?.Invoke("No entry found for path '{0}' in archive '{1}'", assemblyPath, archivePath); + continue; + } + + var ret = entry.Extract(); + ret.Flush(); + return ret; + } + + return null; + } } /// diff --git a/src/Sentry.Android.AssemblyReader/V2/MonoAndroidHelper.Basic.cs b/src/Sentry.Android.AssemblyReader/V2/MonoAndroidHelper.Basic.cs index d305ac9a3e..7836c6819f 100644 --- a/src/Sentry.Android.AssemblyReader/V2/MonoAndroidHelper.Basic.cs +++ b/src/Sentry.Android.AssemblyReader/V2/MonoAndroidHelper.Basic.cs @@ -90,18 +90,6 @@ public static string MakeZipArchivePath(string part1, ICollection? pathP public const string MANGLED_ASSEMBLY_SATELLITE_ASSEMBLY_MARKER = "lib-"; public const string SATELLITE_CULTURE_END_MARKER_CHAR = "_"; - /// - /// When an AAB file is deployed, the APK is split into multiple APKs so our modules can end up in a companion - /// architecture specific APK like split_config.arm64_v8a.apk. This method returns the path to that split_config APK - /// if it exists... otherwise we just return the original archive path. - /// - internal static string GetArchivePathForArchitecture(this string archivePath, AndroidTargetArch arch, DebugLogger? logger) - { - return ArchToAbiMap.TryGetValue(arch, out var abi) - ? GetArchivePathForAbi(archivePath, abi, logger) - : archivePath; - } - /// /// When an AAB file is deployed, the APK is split into multiple APKs so our modules can end up in a companion /// architecture specific APK like split_config.arm64_v8a.apk. This method returns the path to that split_config APK From 691aaa22b0b0a2b1fbdf07409f00b4631834c2ae Mon Sep 17 00:00:00 2001 From: Sentry Github Bot Date: Thu, 29 May 2025 08:56:02 +0000 Subject: [PATCH 4/6] Format code --- .../V2/AndroidAssemblyDirectoryReaderV2.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Sentry.Android.AssemblyReader/V2/AndroidAssemblyDirectoryReaderV2.cs b/src/Sentry.Android.AssemblyReader/V2/AndroidAssemblyDirectoryReaderV2.cs index c3ee3139c9..7c92c70a26 100644 --- a/src/Sentry.Android.AssemblyReader/V2/AndroidAssemblyDirectoryReaderV2.cs +++ b/src/Sentry.Android.AssemblyReader/V2/AndroidAssemblyDirectoryReaderV2.cs @@ -147,7 +147,7 @@ public ArchiveAssemblyHelper(string archivePath, DebugLogger? logger, IList Date: Thu, 29 May 2025 14:15:47 +0000 Subject: [PATCH 5/6] release: 5.8.2-beta.1 --- CHANGELOG.md | 2 +- Directory.Build.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 907034381b..c971cbd951 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## Unreleased +## 5.8.2-beta.1 ### Fixes diff --git a/Directory.Build.props b/Directory.Build.props index 3b46556d67..0a58397ec2 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,7 +1,7 @@ - 5.8.1 + 5.8.2-beta.1 13 true true From 2b57b5c50a26d72788457fcee2ad027176463141 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Fri, 30 May 2025 11:00:48 +1200 Subject: [PATCH 6/6] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c971cbd951..907034381b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## 5.8.2-beta.1 +## Unreleased ### Fixes