From 2c13d2ff4629f4911cffd0f45a177d80e74be846 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Tue, 27 May 2025 22:25:06 +1200 Subject: [PATCH 1/3] 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/3] 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 2b4c64aaf5ffbf34249d471f7bcd9494d37dd04e Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Wed, 28 May 2025 21:26:58 +1200 Subject: [PATCH 3/3] WIP: Tests for the android-release-symbols branch --- .gitignore | 1 + ...Sentry.Android.AssemblyReader.Tests.csproj | 50 ++++++++++++++++--- .../device-spec.json | 11 ++++ 3 files changed, 55 insertions(+), 7 deletions(-) create mode 100644 test/Sentry.Android.AssemblyReader.Tests/device-spec.json diff --git a/.gitignore b/.gitignore index f8d880a8e7..df3a0121b9 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,7 @@ mono_crash.*.json test_output/ test/**/*.apk /tools/ +/test/Sentry.Android.AssemblyReader.Tests/tools *.log .sentry-native diff --git a/test/Sentry.Android.AssemblyReader.Tests/Sentry.Android.AssemblyReader.Tests.csproj b/test/Sentry.Android.AssemblyReader.Tests/Sentry.Android.AssemblyReader.Tests.csproj index 0bbb2b5b92..9c025d513a 100644 --- a/test/Sentry.Android.AssemblyReader.Tests/Sentry.Android.AssemblyReader.Tests.csproj +++ b/test/Sentry.Android.AssemblyReader.Tests/Sentry.Android.AssemblyReader.Tests.csproj @@ -41,22 +41,58 @@ + <_ConfigString>A=$(_Aot)-S=$(_Store)-C=$(_Compressed) - ..\AndroidTestApp\bin\$(TargetFramework)\$(_ConfigString)\com.companyname.AndroidTestApp-Signed.apk - TestAPKs\$(TargetFramework)-$(_ConfigString).apk + <_AndroidTestAppOutputDir>..\AndroidTestApp\bin\$(TargetFramework)\$(_ConfigString) + $(_AndroidTestAppOutputDir)\com.companyname.AndroidTestApp-Signed.apk - - + + + + + + TestAPKs\$(TargetFramework)-$(_ConfigString).apk + + + - True - False + <_ToolsFolder>tools + <_BundleToolVersion>1.18.1 + <_BundleToolFile>bundletool-$(_BundleToolVersion).jar + <_BundleToolPath>$(_ToolsFolder)\$(_BundleToolFile) + <_SourceAab>$(_AndroidTestAppOutputDir)\com.companyname.AndroidTestApp-Signed.aab + <_SplitApksZip>$(_AndroidTestAppOutputDir)\split_config.apks + <_SplitApksDir>$(_AndroidTestAppOutputDir)\apks - + + + + + + + <_ApksToExtract Include="$(_SplitApksZip)" /> + + + + + + <_SplitApks Include="$(_SplitApksDir)\splits\*.apk" /> + + + diff --git a/test/Sentry.Android.AssemblyReader.Tests/device-spec.json b/test/Sentry.Android.AssemblyReader.Tests/device-spec.json new file mode 100644 index 0000000000..1d955f5389 --- /dev/null +++ b/test/Sentry.Android.AssemblyReader.Tests/device-spec.json @@ -0,0 +1,11 @@ +{ + "supportedAbis": ["x86_64"], + "supportedLocales": ["en-US"], + "deviceFeatures": ["reqGlEsVersion\u003d0x30000", "android.hardware.audio.output", "android.hardware.biometrics.face", "android.hardware.bluetooth", "android.hardware.bluetooth_le", "android.hardware.camera", "android.hardware.camera.any", "android.hardware.camera.autofocus", "android.hardware.camera.capability.manual_post_processing", "android.hardware.camera.capability.manual_sensor", "android.hardware.camera.capability.raw", "android.hardware.camera.concurrent", "android.hardware.camera.flash", "android.hardware.camera.front", "android.hardware.camera.level.full", "android.hardware.faketouch", "android.hardware.fingerprint", "android.hardware.hardware_keystore\u003d300", "android.hardware.identity_credential\u003d202301", "android.hardware.keystore.app_attest_key", "android.hardware.location", "android.hardware.location.gps", "android.hardware.location.network", "android.hardware.microphone", "android.hardware.ram.normal", "android.hardware.reboot_escrow", "android.hardware.screen.landscape", "android.hardware.screen.portrait", "android.hardware.security.model.compatible", "android.hardware.sensor.accelerometer", "android.hardware.sensor.ambient_temperature", "android.hardware.sensor.barometer", "android.hardware.sensor.compass", "android.hardware.sensor.gyroscope", "android.hardware.sensor.light", "android.hardware.sensor.proximity", "android.hardware.sensor.relative_humidity", "android.hardware.telephony", "android.hardware.telephony.calling", "android.hardware.telephony.data", "android.hardware.telephony.gsm", "android.hardware.telephony.ims", "android.hardware.telephony.messaging", "android.hardware.telephony.radio.access", "android.hardware.telephony.subscription", "android.hardware.touchscreen", "android.hardware.touchscreen.multitouch", "android.hardware.touchscreen.multitouch.distinct", "android.hardware.touchscreen.multitouch.jazzhand", "android.hardware.vulkan.compute", "android.hardware.vulkan.level\u003d1", "android.hardware.vulkan.version\u003d4206592", "android.hardware.wifi", "android.hardware.wifi.direct", "android.hardware.wifi.passpoint", "android.software.activities_on_secondary_displays", "android.software.adoptable_storage", "android.software.app_enumeration", "android.software.app_widgets", "android.software.autofill", "android.software.backup", "android.software.cant_save_state", "android.software.companion_device_setup", "android.software.controls", "android.software.credentials", "android.software.cts", "android.software.device_admin", "android.software.device_lock", "android.software.erofs", "android.software.file_based_encryption", "android.software.home_screen", "android.software.incremental_delivery\u003d2", "android.software.input_methods", "android.software.ipsec_tunnel_migration", "android.software.ipsec_tunnels", "android.software.live_wallpaper", "android.software.managed_users", "android.software.midi", "android.software.opengles.deqp.level\u003d132645633", "android.software.picture_in_picture", "android.software.print", "android.software.secure_lock_screen", "android.software.securely_removes_users", "android.software.telecom", "android.software.verified_boot", "android.software.voice_recognizers", "android.software.vulkan.deqp.level\u003d132645633", "android.software.webview", "android.software.window_magnification", "com.google.android.apps.dialer.SUPPORTED", "com.google.android.feature.EXCHANGE_6_2", "com.google.android.feature.GOOGLE_BUILD", "com.google.android.feature.GOOGLE_EXPERIENCE", "com.google.android.feature.WELLBEING"], + "glExtensions": ["GL_EXT_debug_marker", "GL_EXT_robustness", "GL_OES_EGL_sync", "GL_OES_EGL_image", "GL_OES_EGL_image_external", "GL_OES_depth24", "GL_OES_depth32", "GL_OES_element_index_uint", "GL_OES_texture_float", "GL_OES_texture_float_linear", "GL_OES_compressed_paletted_texture", "GL_OES_compressed_ETC1_RGB8_texture", "GL_OES_depth_texture", "GL_OES_texture_npot", "GL_OES_rgb8_rgba8", "GL_EXT_color_buffer_float", "GL_EXT_color_buffer_half_float", "GL_EXT_texture_format_BGRA8888", "GL_APPLE_texture_format_BGRA8888", "ANDROID_EMU_CHECKSUM_HELPER_v1", "ANDROID_EMU_native_sync_v2", "ANDROID_EMU_native_sync_v3", "ANDROID_EMU_native_sync_v4", "ANDROID_EMU_dma_v1", "ANDROID_EMU_host_composition_v1", "ANDROID_EMU_host_composition_v2", "ANDROID_EMU_vulkan", "ANDROID_EMU_deferred_vulkan_commands", "ANDROID_EMU_vulkan_null_optional_strings", "ANDROID_EMU_vulkan_create_resources_with_requirements", "ANDROID_EMU_YUV_Cache", "ANDROID_EMU_vulkan_ignored_handles", "ANDROID_EMU_has_shared_slots_host_memory_allocator", "ANDROID_EMU_vulkan_free_memory_sync", "ANDROID_EMU_vulkan_shader_float16_int8", "ANDROID_EMU_vulkan_async_queue_submit", "ANDROID_EMU_vulkan_queue_submit_with_commands", "ANDROID_EMU_sync_buffer_data", "ANDROID_EMU_vulkan_async_qsri", "ANDROID_EMU_hwc_multi_configs", "GL_OES_EGL_image_external_essl3", "GL_OES_vertex_array_object", "ANDROID_EMU_host_side_tracing", "ANDROID_EMU_gles_max_version_3_0"], + "screenDensity": 420, + "sdkVersion": 35, + "sdkRuntime": { + "supported": true + } +}