From 6cd406da2503830b4afdcbbda46b8e1abfa6bffd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Feb 2025 16:03:48 +0100 Subject: [PATCH 1/4] Bump actions/create-github-app-token from 1.11.2 to 1.11.3 (#4151) Bumps [actions/create-github-app-token](https://github.com/actions/create-github-app-token) from 1.11.2 to 1.11.3. - [Release notes](https://github.com/actions/create-github-app-token/releases) - [Commits](https://github.com/actions/create-github-app-token/compare/136412a57a7081aa63c935a2cc2918f76c34f514...67e27a7eb7db372a1c61a7f9bdab8699e9ee57f7) --- updated-dependencies: - dependency-name: actions/create-github-app-token dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6b473ad5ed..5f8ddf9f10 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -19,7 +19,7 @@ jobs: steps: - name: Get auth token id: token - uses: actions/create-github-app-token@136412a57a7081aa63c935a2cc2918f76c34f514 # v1.11.2 + uses: actions/create-github-app-token@67e27a7eb7db372a1c61a7f9bdab8699e9ee57f7 # v1.11.3 with: app-id: ${{ vars.SENTRY_RELEASE_BOT_CLIENT_ID }} private-key: ${{ secrets.SENTRY_RELEASE_BOT_PRIVATE_KEY }} From 33f1664aa648300e04e98a274ed1acfe14336857 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Feb 2025 15:25:40 +0000 Subject: [PATCH 2/4] Bump github/codeql-action from 3.28.8 to 3.28.9 (#4149) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.28.8 to 3.28.9. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/dd746615b3b9d728a6a37ca2045b68ca76d4841a...9e8d0789d4a0fa9ceb6b1738f7e269594bdd67f0) --- updated-dependencies: - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql-analysis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 98cc67159a..7652d0ff64 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -40,7 +40,7 @@ jobs: cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }} - name: Initialize CodeQL - uses: github/codeql-action/init@dd746615b3b9d728a6a37ca2045b68ca76d4841a # pin@v2 + uses: github/codeql-action/init@9e8d0789d4a0fa9ceb6b1738f7e269594bdd67f0 # pin@v2 with: languages: 'java' @@ -49,4 +49,4 @@ jobs: ./gradlew buildForCodeQL - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@dd746615b3b9d728a6a37ca2045b68ca76d4841a # pin@v2 + uses: github/codeql-action/analyze@9e8d0789d4a0fa9ceb6b1738f7e269594bdd67f0 # pin@v2 From f4162ef9982e0e4bd860c1ecd415fc61fa885b05 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Feb 2025 15:29:57 +0000 Subject: [PATCH 3/4] Bump gradle/actions from 6962c6c931ff9effc947259cc1b9c6edba90b9d3 to aa23778d2dc6f6556fcc7164e99babbd8c3134e4 (#4150) Bumps [gradle/actions](https://github.com/gradle/actions) from 6962c6c931ff9effc947259cc1b9c6edba90b9d3 to aa23778d2dc6f6556fcc7164e99babbd8c3134e4. - [Release notes](https://github.com/gradle/actions/releases) - [Commits](https://github.com/gradle/actions/compare/6962c6c931ff9effc947259cc1b9c6edba90b9d3...aa23778d2dc6f6556fcc7164e99babbd8c3134e4) --- updated-dependencies: - dependency-name: gradle/actions dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/agp-matrix.yml | 2 +- .github/workflows/build.yml | 2 +- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/enforce-license-compliance.yml | 2 +- .github/workflows/generate-javadocs.yml | 2 +- .github/workflows/integration-tests-benchmarks.yml | 4 ++-- .github/workflows/integration-tests-ui-critical.yml | 2 +- .github/workflows/integration-tests-ui.yml | 2 +- .github/workflows/release-build.yml | 2 +- .github/workflows/system-tests-backend.yml | 2 +- 10 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/agp-matrix.yml b/.github/workflows/agp-matrix.yml index 75253e7251..99dd460a56 100644 --- a/.github/workflows/agp-matrix.yml +++ b/.github/workflows/agp-matrix.yml @@ -39,7 +39,7 @@ jobs: java-version: '17' - name: Setup Gradle - uses: gradle/actions/setup-gradle@6962c6c931ff9effc947259cc1b9c6edba90b9d3 # pin@v3 + uses: gradle/actions/setup-gradle@aa23778d2dc6f6556fcc7164e99babbd8c3134e4 # pin@v3 with: gradle-home-cache-cleanup: true cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }} diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b83f9de0b1..7de498e3e5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -30,7 +30,7 @@ jobs: java-version: '17' - name: Setup Gradle - uses: gradle/actions/setup-gradle@6962c6c931ff9effc947259cc1b9c6edba90b9d3 # pin@v3 + uses: gradle/actions/setup-gradle@aa23778d2dc6f6556fcc7164e99babbd8c3134e4 # pin@v3 with: gradle-home-cache-cleanup: true cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }} diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 7652d0ff64..39dfbdb090 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -34,7 +34,7 @@ jobs: java-version: '17' - name: Setup Gradle - uses: gradle/actions/setup-gradle@6962c6c931ff9effc947259cc1b9c6edba90b9d3 # pin@v3 + uses: gradle/actions/setup-gradle@aa23778d2dc6f6556fcc7164e99babbd8c3134e4 # pin@v3 with: gradle-home-cache-cleanup: true cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }} diff --git a/.github/workflows/enforce-license-compliance.yml b/.github/workflows/enforce-license-compliance.yml index f7e1aad059..6b793131f3 100644 --- a/.github/workflows/enforce-license-compliance.yml +++ b/.github/workflows/enforce-license-compliance.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Setup Gradle - uses: gradle/actions/setup-gradle@6962c6c931ff9effc947259cc1b9c6edba90b9d3 # pin@v3 + uses: gradle/actions/setup-gradle@aa23778d2dc6f6556fcc7164e99babbd8c3134e4 # pin@v3 with: gradle-home-cache-cleanup: true diff --git a/.github/workflows/generate-javadocs.yml b/.github/workflows/generate-javadocs.yml index 286df36dfe..62c8c30181 100644 --- a/.github/workflows/generate-javadocs.yml +++ b/.github/workflows/generate-javadocs.yml @@ -20,7 +20,7 @@ jobs: java-version: '17' - name: Setup Gradle - uses: gradle/actions/setup-gradle@6962c6c931ff9effc947259cc1b9c6edba90b9d3 # pin@v3 + uses: gradle/actions/setup-gradle@aa23778d2dc6f6556fcc7164e99babbd8c3134e4 # pin@v3 with: gradle-home-cache-cleanup: true diff --git a/.github/workflows/integration-tests-benchmarks.yml b/.github/workflows/integration-tests-benchmarks.yml index 8d0e0381d6..de40849718 100644 --- a/.github/workflows/integration-tests-benchmarks.yml +++ b/.github/workflows/integration-tests-benchmarks.yml @@ -38,7 +38,7 @@ jobs: java-version: '17' - name: Setup Gradle - uses: gradle/actions/setup-gradle@6962c6c931ff9effc947259cc1b9c6edba90b9d3 # pin@v3 + uses: gradle/actions/setup-gradle@aa23778d2dc6f6556fcc7164e99babbd8c3134e4 # pin@v3 with: gradle-home-cache-cleanup: true cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }} @@ -89,7 +89,7 @@ jobs: java-version: '17' - name: Setup Gradle - uses: gradle/actions/setup-gradle@6962c6c931ff9effc947259cc1b9c6edba90b9d3 # pin@v3 + uses: gradle/actions/setup-gradle@aa23778d2dc6f6556fcc7164e99babbd8c3134e4 # pin@v3 with: gradle-home-cache-cleanup: true cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }} diff --git a/.github/workflows/integration-tests-ui-critical.yml b/.github/workflows/integration-tests-ui-critical.yml index 917d34732f..05cd811f5a 100644 --- a/.github/workflows/integration-tests-ui-critical.yml +++ b/.github/workflows/integration-tests-ui-critical.yml @@ -36,7 +36,7 @@ jobs: java-version: '17' - name: Setup Gradle - uses: gradle/actions/setup-gradle@6962c6c931ff9effc947259cc1b9c6edba90b9d3 # pin@v3 + uses: gradle/actions/setup-gradle@aa23778d2dc6f6556fcc7164e99babbd8c3134e4 # pin@v3 with: gradle-home-cache-cleanup: true cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }} diff --git a/.github/workflows/integration-tests-ui.yml b/.github/workflows/integration-tests-ui.yml index b50420533a..f62408e7f4 100644 --- a/.github/workflows/integration-tests-ui.yml +++ b/.github/workflows/integration-tests-ui.yml @@ -33,7 +33,7 @@ jobs: java-version: '17' - name: Setup Gradle - uses: gradle/actions/setup-gradle@6962c6c931ff9effc947259cc1b9c6edba90b9d3 # pin@v3 + uses: gradle/actions/setup-gradle@aa23778d2dc6f6556fcc7164e99babbd8c3134e4 # pin@v3 with: gradle-home-cache-cleanup: true cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }} diff --git a/.github/workflows/release-build.yml b/.github/workflows/release-build.yml index cbf6997405..975ab2ff1a 100644 --- a/.github/workflows/release-build.yml +++ b/.github/workflows/release-build.yml @@ -26,7 +26,7 @@ jobs: java-version: '17' - name: Setup Gradle - uses: gradle/actions/setup-gradle@6962c6c931ff9effc947259cc1b9c6edba90b9d3 # pin@v3 + uses: gradle/actions/setup-gradle@aa23778d2dc6f6556fcc7164e99babbd8c3134e4 # pin@v3 with: gradle-home-cache-cleanup: true diff --git a/.github/workflows/system-tests-backend.yml b/.github/workflows/system-tests-backend.yml index c5d364375e..aed5fd2864 100644 --- a/.github/workflows/system-tests-backend.yml +++ b/.github/workflows/system-tests-backend.yml @@ -56,7 +56,7 @@ jobs: java-version: '17' - name: Setup Gradle - uses: gradle/actions/setup-gradle@6962c6c931ff9effc947259cc1b9c6edba90b9d3 # pin@v3 + uses: gradle/actions/setup-gradle@aa23778d2dc6f6556fcc7164e99babbd8c3134e4 # pin@v3 with: gradle-home-cache-cleanup: true From 5e31a6b9243794a70f7b6c333c1efa4baa0154ba Mon Sep 17 00:00:00 2001 From: Giancarlo Buenaflor Date: Tue, 11 Feb 2025 14:22:15 +0100 Subject: [PATCH 4/4] feat(android-ndk): add api for getting debug images by addresses (#4089) * Add fetching debug images by addresses * update test * clean up * update test * update comments * remove file * fix imports * update docs * revert doc * use hashset * Format code * update naming * apiDump * Improve Nullability, consider case of null imageSize * Add fetching debug images by addresses update test clean up update test update comments remove file fix imports update docs revert doc use hashset Format code update naming apiDump Improve Nullability, consider case of null imageSize Update tests, ensure code_file and debug_file are set * Fix test * Update javadoc * Update Changelog --------- Co-authored-by: Sentry Github Bot Co-authored-by: Markus Hintersteiner --- CHANGELOG.md | 1 + .../api/sentry-android-core.api | 1 + .../android/core/IDebugImagesLoader.java | 4 + .../android/core/NoOpDebugImagesLoader.java | 6 + .../android/core/SentryAndroidOptionsTest.kt | 2 + sentry-android-ndk/api/sentry-android-ndk.api | 1 + .../sentry/android/ndk/DebugImagesLoader.java | 93 ++++++++++++++- .../android/ndk/DebugImagesLoaderTest.kt | 110 +++++++++++++++++- 8 files changed, 214 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b6e1d8b5a..199bf7caa3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ - Add split apks info to the `App` context ([#3193](https://github.com/getsentry/sentry-java/pull/3193)) - Expose new `withSentryObservableEffect` method overload that accepts `SentryNavigationListener` as a parameter ([#4143](https://github.com/getsentry/sentry-java/pull/4143)) - This allows sharing the same `SentryNavigationListener` instance across fragments and composables to preserve the trace +- (Internal) Add API to filter native debug images based on stacktrace addresses ([#4089](https://github.com/getsentry/sentry-java/pull/4089)) ### Fixes diff --git a/sentry-android-core/api/sentry-android-core.api b/sentry-android-core/api/sentry-android-core.api index 8cabe0fd8e..ff982b51f6 100644 --- a/sentry-android-core/api/sentry-android-core.api +++ b/sentry-android-core/api/sentry-android-core.api @@ -212,6 +212,7 @@ public abstract class io/sentry/android/core/EnvelopeFileObserverIntegration : i public abstract interface class io/sentry/android/core/IDebugImagesLoader { public abstract fun clearDebugImages ()V public abstract fun loadDebugImages ()Ljava/util/List; + public abstract fun loadDebugImagesForAddresses (Ljava/util/Set;)Ljava/util/Set; } public final class io/sentry/android/core/InternalSentrySdk { diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/IDebugImagesLoader.java b/sentry-android-core/src/main/java/io/sentry/android/core/IDebugImagesLoader.java index 902f7efc2b..7b98147aab 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/IDebugImagesLoader.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/IDebugImagesLoader.java @@ -2,6 +2,7 @@ import io.sentry.protocol.DebugImage; import java.util.List; +import java.util.Set; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; @@ -11,5 +12,8 @@ public interface IDebugImagesLoader { @Nullable List loadDebugImages(); + @Nullable + Set loadDebugImagesForAddresses(Set addresses); + void clearDebugImages(); } diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/NoOpDebugImagesLoader.java b/sentry-android-core/src/main/java/io/sentry/android/core/NoOpDebugImagesLoader.java index 70451972a7..193b734219 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/NoOpDebugImagesLoader.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/NoOpDebugImagesLoader.java @@ -2,6 +2,7 @@ import io.sentry.protocol.DebugImage; import java.util.List; +import java.util.Set; import org.jetbrains.annotations.Nullable; final class NoOpDebugImagesLoader implements IDebugImagesLoader { @@ -19,6 +20,11 @@ public static NoOpDebugImagesLoader getInstance() { return null; } + @Override + public @Nullable Set loadDebugImagesForAddresses(Set addresses) { + return null; + } + @Override public void clearDebugImages() {} } diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/SentryAndroidOptionsTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/SentryAndroidOptionsTest.kt index efef393dc7..d3e8e9a239 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/SentryAndroidOptionsTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/SentryAndroidOptionsTest.kt @@ -182,6 +182,8 @@ class SentryAndroidOptionsTest { private class CustomDebugImagesLoader : IDebugImagesLoader { override fun loadDebugImages(): List? = null + override fun loadDebugImagesForAddresses(addresses: Set?): Set? = null + override fun clearDebugImages() {} } } diff --git a/sentry-android-ndk/api/sentry-android-ndk.api b/sentry-android-ndk/api/sentry-android-ndk.api index 155a368b11..eb5f48a9bd 100644 --- a/sentry-android-ndk/api/sentry-android-ndk.api +++ b/sentry-android-ndk/api/sentry-android-ndk.api @@ -10,6 +10,7 @@ public final class io/sentry/android/ndk/DebugImagesLoader : io/sentry/android/c public fun (Lio/sentry/android/core/SentryAndroidOptions;Lio/sentry/ndk/NativeModuleListLoader;)V public fun clearDebugImages ()V public fun loadDebugImages ()Ljava/util/List; + public fun loadDebugImagesForAddresses (Ljava/util/Set;)Ljava/util/Set; } public final class io/sentry/android/ndk/NdkScopeObserver : io/sentry/ScopeObserverAdapter { diff --git a/sentry-android-ndk/src/main/java/io/sentry/android/ndk/DebugImagesLoader.java b/sentry-android-ndk/src/main/java/io/sentry/android/ndk/DebugImagesLoader.java index 1257325091..602981c1d0 100644 --- a/sentry-android-ndk/src/main/java/io/sentry/android/ndk/DebugImagesLoader.java +++ b/sentry-android-ndk/src/main/java/io/sentry/android/ndk/DebugImagesLoader.java @@ -10,7 +10,9 @@ import io.sentry.util.AutoClosableReentrantLock; import io.sentry.util.Objects; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.VisibleForTesting; @@ -25,7 +27,7 @@ public final class DebugImagesLoader implements IDebugImagesLoader { private final @NotNull NativeModuleListLoader moduleListLoader; - private static @Nullable List debugImages; + private static volatile @Nullable List debugImages; /** we need to lock it because it could be called from different threads */ protected static final @NotNull AutoClosableReentrantLock debugImagesLock = @@ -54,6 +56,8 @@ public DebugImagesLoader( debugImages = new ArrayList<>(debugImagesArr.length); for (io.sentry.ndk.DebugImage d : debugImagesArr) { final DebugImage debugImage = new DebugImage(); + debugImage.setCodeFile(d.getCodeFile()); + debugImage.setDebugFile(d.getDebugFile()); debugImage.setUuid(d.getUuid()); debugImage.setType(d.getType()); debugImage.setDebugId(d.getDebugId()); @@ -75,7 +79,92 @@ public DebugImagesLoader( return debugImages; } - /** Clears the caching of debug images on sentry-native and here. */ + /** + * Loads debug images for the given set of addresses. + * + * @param addresses Set of memory addresses to find debug images for + * @return Set of matching debug images, or null if debug images couldn't be loaded + */ + public @Nullable Set loadDebugImagesForAddresses( + final @NotNull Set addresses) { + try (final @NotNull ISentryLifecycleToken ignored = debugImagesLock.acquire()) { + final @Nullable List allDebugImages = loadDebugImages(); + if (allDebugImages == null) { + return null; + } + if (addresses.isEmpty()) { + return null; + } + + final Set referencedImages = filterImagesByAddresses(allDebugImages, addresses); + if (referencedImages.isEmpty()) { + options + .getLogger() + .log( + SentryLevel.WARNING, + "No debug images found for any of the %d addresses.", + addresses.size()); + return null; + } + + return referencedImages; + } + } + + /** + * Finds all debug image containing the given addresses. Assumes that the images are sorted by + * address, which should always be true on Linux/Android and Windows platforms + * + * @return All matching debug images or null if none are found + */ + private @NotNull Set filterImagesByAddresses( + final @NotNull List images, final @NotNull Set addresses) { + final Set result = new HashSet<>(); + + for (int i = 0; i < images.size(); i++) { + final @NotNull DebugImage image = images.get(i); + final @Nullable DebugImage nextDebugImage = + (i + 1) < images.size() ? images.get(i + 1) : null; + final @Nullable String nextDebugImageAddress = + nextDebugImage != null ? nextDebugImage.getImageAddr() : null; + + for (final @NotNull String rawAddress : addresses) { + try { + final long address = Long.parseLong(rawAddress.replace("0x", ""), 16); + + final @Nullable String imageAddress = image.getImageAddr(); + if (imageAddress != null) { + try { + final long imageStart = Long.parseLong(imageAddress.replace("0x", ""), 16); + final long imageEnd; + + final @Nullable Long imageSize = image.getImageSize(); + if (imageSize != null) { + imageEnd = imageStart + imageSize; + } else if (nextDebugImageAddress != null) { + imageEnd = Long.parseLong(nextDebugImageAddress.replace("0x", ""), 16); + } else { + imageEnd = Long.MAX_VALUE; + } + if (address >= imageStart && address < imageEnd) { + result.add(image); + // once image is added we can skip the remaining addresses and go straight to the + // next + // image + break; + } + } catch (NumberFormatException e) { + // ignored, invalid debug image address + } + } + } catch (NumberFormatException e) { + // ignored, invalid address supplied + } + } + } + return result; + } + @Override public void clearDebugImages() { try (final @NotNull ISentryLifecycleToken ignored = debugImagesLock.acquire()) { diff --git a/sentry-android-ndk/src/test/java/io/sentry/android/ndk/DebugImagesLoaderTest.kt b/sentry-android-ndk/src/test/java/io/sentry/android/ndk/DebugImagesLoaderTest.kt index 927ce98c3b..912a2911c5 100644 --- a/sentry-android-ndk/src/test/java/io/sentry/android/ndk/DebugImagesLoaderTest.kt +++ b/sentry-android-ndk/src/test/java/io/sentry/android/ndk/DebugImagesLoaderTest.kt @@ -6,6 +6,7 @@ import org.mockito.kotlin.mock import org.mockito.kotlin.verify import org.mockito.kotlin.whenever import kotlin.test.Test +import kotlin.test.assertEquals import kotlin.test.assertNotNull import kotlin.test.assertNull import kotlin.test.assertTrue @@ -16,11 +17,13 @@ class DebugImagesLoaderTest { val options = SentryAndroidOptions() fun getSut(): DebugImagesLoader { - return DebugImagesLoader(options, nativeLoader) + val loader = DebugImagesLoader(options, nativeLoader) + loader.clearDebugImages() + return loader } } - private val fixture = Fixture() + private var fixture = Fixture() @Test fun `get images returns image list`() { @@ -77,4 +80,107 @@ class DebugImagesLoaderTest { assertNull(sut.cachedDebugImages) } + + @Test + fun `find images by address`() { + val sut = fixture.getSut() + + val image1 = io.sentry.ndk.DebugImage().apply { + imageAddr = "0x1000" + imageSize = 0x1000L + } + + val image2 = io.sentry.ndk.DebugImage().apply { + imageAddr = "0x2000" + imageSize = 0x1000L + } + + val image3 = io.sentry.ndk.DebugImage().apply { + imageAddr = "0x3000" + imageSize = 0x1000L + } + + whenever(fixture.nativeLoader.loadModuleList()).thenReturn(arrayOf(image1, image2, image3)) + + val result = sut.loadDebugImagesForAddresses( + setOf("0x1500", "0x2500") + ) + + assertNotNull(result) + assertEquals(2, result.size) + assertTrue(result.any { it.imageAddr == image1.imageAddr }) + assertTrue(result.any { it.imageAddr == image2.imageAddr }) + } + + @Test + fun `find images with invalid addresses are not added to the result`() { + val sut = fixture.getSut() + + val image1 = io.sentry.ndk.DebugImage().apply { + imageAddr = "0x1000" + imageSize = 0x1000L + } + + val image2 = io.sentry.ndk.DebugImage().apply { + imageAddr = "0x2000" + imageSize = 0x1000L + } + + whenever(fixture.nativeLoader.loadModuleList()).thenReturn(arrayOf(image1, image2)) + + val hexAddresses = setOf("0xINVALID", "0x1500") + val result = sut.loadDebugImagesForAddresses(hexAddresses) + + assertEquals(1, result!!.size) + } + + @Test + fun `find images by address returns null if result is empty`() { + val sut = fixture.getSut() + + val image1 = io.sentry.ndk.DebugImage().apply { + imageAddr = "0x1000" + imageSize = 0x1000L + } + + val image2 = io.sentry.ndk.DebugImage().apply { + imageAddr = "0x2000" + imageSize = 0x1000L + } + + whenever(fixture.nativeLoader.loadModuleList()).thenReturn(arrayOf(image1, image2)) + + val hexAddresses = setOf("0x100", "0x10500") + val result = sut.loadDebugImagesForAddresses(hexAddresses) + + assertNull(result) + } + + @Test + fun `invalid image adresses are ignored for loadDebugImagesForAddresses`() { + val sut = fixture.getSut() + + val image1 = io.sentry.ndk.DebugImage().apply { + imageAddr = "0xNotANumber" + imageSize = 0x1000L + } + + val image2 = io.sentry.ndk.DebugImage().apply { + imageAddr = "0x2000" + imageSize = null + } + + val image3 = io.sentry.ndk.DebugImage().apply { + imageAddr = "0x5000" + imageSize = 0x1000L + } + + whenever(fixture.nativeLoader.loadModuleList()).thenReturn(arrayOf(image1, image2, image3)) + + val hexAddresses = setOf("0x100", "0x2000", "0x2000", "0x5000") + val result = sut.loadDebugImagesForAddresses(hexAddresses) + + assertNotNull(result) + assertEquals(2, result.size) + } }