diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 7d72210a275..b2fdeac6ec1 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -22,7 +22,7 @@ jobs: uses: actions/checkout@v3 - name: Initialize CodeQL - uses: github/codeql-action/init@04df1262e6247151b5ac09cd2c303ac36ad3f62b # pin@v2 + uses: github/codeql-action/init@d186a2a36cc67bfa1b860e6170d37fb9634742c7 # pin@v2 with: languages: ${{ matrix.language }} @@ -35,4 +35,4 @@ jobs: -destination platform="iOS Simulator,OS=latest,name=iPhone 11 Pro" - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@04df1262e6247151b5ac09cd2c303ac36ad3f62b # pin@v2 + uses: github/codeql-action/analyze@d186a2a36cc67bfa1b860e6170d37fb9634742c7 # pin@v2 diff --git a/.github/workflows/saucelabs-UI-tests.yml b/.github/workflows/saucelabs-UI-tests.yml index aa42e663feb..3ae2a310a2a 100644 --- a/.github/workflows/saucelabs-UI-tests.yml +++ b/.github/workflows/saucelabs-UI-tests.yml @@ -33,7 +33,7 @@ jobs: xcode: '13.4.1' - runs-on: macos-12 - xcode: '14.1' + xcode: '14.2' steps: - uses: actions/checkout@v3 @@ -93,6 +93,9 @@ jobs: fail-fast: false matrix: include: + - xcode: '14.2' + suite: 'iOS-16' + - xcode: '13.4.1' suite: 'iOS-15' @@ -106,6 +109,9 @@ jobs: - xcode: '13.4.1' suite: 'iOS-13' + - xcode: '13.4.1' + suite: 'iOS-12' + steps: - uses: actions/checkout@v3 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index eadba7c2c9a..69600cec962 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -81,13 +81,6 @@ jobs: # we get the error 'XCTest/XCTest.h' not found. Setting ENABLE_TESTING_SEARCH_PATH=YES # doesn't work. include: - # iOS 12.4 - - runs-on: macos-11 - platform: 'iOS' - xcode: '13.2.1' - test-destination-os: '12.4' - # This job needs to install the simulator which can take a couple of minutes - timeout-minutes: 25 # iOS 13.7 - runs-on: macos-11 @@ -130,6 +123,13 @@ jobs: xcode: '13.4.1' test-destination-os: 'latest' timeout-minutes: 15 + + # macOS 13 + - runs-on: macos-12 + platform: 'macOS' + xcode: '14.2' + test-destination-os: 'latest' + timeout-minutes: 15 # Catalyst. We only test the latest version, as # the risk something breaking on Catalyst and not @@ -156,6 +156,13 @@ jobs: test-destination-os: 'latest' timeout-minutes: 15 + # tvOS 16 + - runs-on: macos-12 + platform: 'tvOS' + xcode: '14.2' + test-destination-os: 'latest' + timeout-minutes: 15 + steps: - uses: actions/checkout@v3 - uses: actions/download-artifact@v3 @@ -167,14 +174,6 @@ jobs: - run: ./scripts/ci-select-xcode.sh ${{matrix.xcode}} - # GH action images don't have an iOS 12.4 simulator. Therefore we have to download and install the simulator manually. - - name: Install iOS 12.4 simulator - if: ${{ matrix.platform == 'iOS' && matrix.test-destination-os == '12.4'}} - run: | - gem install xcode-install - xcversion simulators --install='iOS 12.4' - xcrun simctl create custom-test-device "iPhone 8" "com.apple.CoreSimulator.SimRuntime.iOS-12-4" - # Workaround with a symlink pointed out in: https://github.com/actions/virtual-environments/issues/551#issuecomment-637344435 - name: Prepare iOS 13.7 simulator if: ${{ matrix.platform == 'iOS' && matrix.test-destination-os == '13.7'}} @@ -326,38 +325,6 @@ jobs: ~/Library/Logs/scan/*.log ./fastlane/test_output/** - # macos-11 doesn't have a simulator for iOS 12 - ui-tests-swift-ios-12: - name: UI Tests on iOS 12 Simulator - runs-on: macos-11 - strategy: - matrix: - target: ['ios_swift', 'ios_objc', 'tvos_swift'] - - steps: - - uses: actions/checkout@v3 - - # GH action images don't have an iOS 12.4 simulator. Therefore we have to download and install the simulator manually. - - name: Install iOS 12.4 simulator - run: | - gem install xcode-install - xcversion simulators --install='iOS 12.4' - xcrun simctl create custom-test-device "iPhone 8" "com.apple.CoreSimulator.SimRuntime.iOS-12-4" - - # GitHub Actions sometimes fail to launch the UI tests. Therefore we retry - - name: Run Fastlane - run: for i in {1..2}; do fastlane ui_tests_${{matrix.target}} device:"iPhone 8 (12.4)" address_sanitizer:false && break ; done - shell: sh - - - name: Archiving Raw Test Logs - uses: actions/upload-artifact@v3 - if: ${{ failure() || cancelled() }} - with: - name: raw-uitest-output-${{matrix.target}}-ios-12 - path: | - ~/Library/Logs/scan/*.log - ./fastlane/test_output/** - ui-tests-address-sanitizer: name: UI Tests with Address Sanitizer runs-on: macos-12 diff --git a/.sauce/config.yml b/.sauce/config.yml index c1b73083197..0271736022f 100644 --- a/.sauce/config.yml +++ b/.sauce/config.yml @@ -12,6 +12,11 @@ xcuitest: testApp: ./DerivedData/Build/Products/Test-iphoneos/iOS-SwiftUITests-Runner.app suites: + + - name: "iOS-16" + devices: + - name: "iPhone.*" + platformVersion: "16.4" - name: "iOS-15" devices: @@ -36,7 +41,7 @@ suites: - name: "iOS-12" devices: - name: "iPhone.*" - platformVersion: "12.5.5" + platformVersion: "12.5.7" - name: "iOS-11" devices: diff --git a/CHANGELOG.md b/CHANGELOG.md index 399776acefa..77a9b8ee8f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # Changelog +## 8.5.0 + +### Features + +- feat: Core data operation in the main thread (#2879) + +### Fixes + +- Crash when serializing invalid objects (#2858) +- Don't send screenshots with either width or height of 0 (#2876) +- GPU frame alignment with stack traces in profiles (#2856) + ## 8.4.0 ### Features @@ -11,7 +23,7 @@ ### Fixes - Correctly track and send GPU frame render data in profiles (#2823) -- Xcode 14.3 compiling issue regarding functions declaration with no prototype (#2852) +- Xcode 14.3 compiling issue regarding functions declaration with no prototype (#2852) ## 8.3.3 diff --git a/Gemfile.lock b/Gemfile.lock index df2e4186902..2f16633abb0 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -224,7 +224,7 @@ GEM nap (1.1.0) naturally (2.2.1) netrc (0.11.0) - nokogiri (1.14.2) + nokogiri (1.14.3) mini_portile2 (~> 2.8.0) racc (~> 1.4) optparse (0.1.1) diff --git a/Sentry.podspec b/Sentry.podspec index 37dcbb00f09..d9e36231188 100644 --- a/Sentry.podspec +++ b/Sentry.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "Sentry" - s.version = "8.4.0" + s.version = "8.5.0" s.summary = "Sentry client for cocoa" s.homepage = "https://github.com/getsentry/sentry-cocoa" s.license = "mit" @@ -27,7 +27,7 @@ Pod::Spec.new do |s| } s.default_subspecs = ['Core'] - s.dependency "SentryPrivate", "8.4.0" + s.dependency "SentryPrivate", "8.5.0" s.subspec 'Core' do |sp| sp.source_files = "Sources/Sentry/**/*.{h,hpp,m,mm,c,cpp}", diff --git a/Sentry.xcodeproj/project.pbxproj b/Sentry.xcodeproj/project.pbxproj index 175c4555124..3853002daee 100644 --- a/Sentry.xcodeproj/project.pbxproj +++ b/Sentry.xcodeproj/project.pbxproj @@ -42,7 +42,6 @@ 0A2D8D9828997887008720F6 /* NSLocale+Sentry.h in Headers */ = {isa = PBXBuildFile; fileRef = 0A2D8D9728997887008720F6 /* NSLocale+Sentry.h */; }; 0A2D8DA8289BC905008720F6 /* SentryViewHierarchy.h in Headers */ = {isa = PBXBuildFile; fileRef = 0A2D8DA6289BC905008720F6 /* SentryViewHierarchy.h */; }; 0A2D8DA9289BC905008720F6 /* SentryViewHierarchy.m in Sources */ = {isa = PBXBuildFile; fileRef = 0A2D8DA7289BC905008720F6 /* SentryViewHierarchy.m */; }; - 0A4EDEA928D3461B00FA67CB /* SentryPerformanceTracker+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 0A4EDEA828D3461B00FA67CB /* SentryPerformanceTracker+Private.h */; }; 0A5370A128A3EC2400B2DCDE /* SentryViewHierarchyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A5370A028A3EC2400B2DCDE /* SentryViewHierarchyTests.swift */; }; 0A56DA5F28ABA01B00C400D5 /* SentryTransactionContext+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 0A56DA5E28ABA01B00C400D5 /* SentryTransactionContext+Private.h */; }; 0A6EEADD28A657970076B469 /* UIViewRecursiveDescriptionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A6EEADC28A657970076B469 /* UIViewRecursiveDescriptionTests.swift */; }; @@ -77,7 +76,12 @@ 15E0A8ED240F2CB000F044E3 /* SentrySerialization.m in Sources */ = {isa = PBXBuildFile; fileRef = 15E0A8EC240F2CB000F044E3 /* SentrySerialization.m */; }; 15E0A8F0240F638200F044E3 /* SentrySerializationNilTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 15E0A8EF240F638200F044E3 /* SentrySerializationNilTests.m */; }; 15E0A8F22411A45A00F044E3 /* SentrySession.m in Sources */ = {isa = PBXBuildFile; fileRef = 15E0A8F12411A45A00F044E3 /* SentrySession.m */; }; + 33042A0D29DAF79A00C60085 /* SentryExtraContextProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 33042A0C29DAF79A00C60085 /* SentryExtraContextProvider.m */; }; + 33042A1729DC2C4300C60085 /* SentryExtraContextProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33042A1629DC2C4300C60085 /* SentryExtraContextProviderTests.swift */; }; 627E7589299F6FE40085504D /* SentryInternalDefines.h in Headers */ = {isa = PBXBuildFile; fileRef = 627E7588299F6FE40085504D /* SentryInternalDefines.h */; }; + 62885DA729E946B100554F38 /* TestConncurrentModifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62885DA629E946B100554F38 /* TestConncurrentModifications.swift */; }; + 62E081A929ED4260000F69FC /* SentryBreadcrumbDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 62E081A829ED4260000F69FC /* SentryBreadcrumbDelegate.h */; }; + 62E081AB29ED4322000F69FC /* SentryBreadcrumbTestDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62E081AA29ED4322000F69FC /* SentryBreadcrumbTestDelegate.swift */; }; 62F226B729A37C120038080D /* SentryBooleanSerialization.m in Sources */ = {isa = PBXBuildFile; fileRef = 62F226B629A37C120038080D /* SentryBooleanSerialization.m */; }; 630435FE1EBCA9D900C4D3FA /* SentryNSURLRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 630435FC1EBCA9D900C4D3FA /* SentryNSURLRequest.h */; }; 630435FF1EBCA9D900C4D3FA /* SentryNSURLRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 630435FD1EBCA9D900C4D3FA /* SentryNSURLRequest.m */; }; @@ -625,6 +629,7 @@ 8453421228BE855D00C22EEC /* SentrySampleDecision.m in Sources */ = {isa = PBXBuildFile; fileRef = 8453421128BE855D00C22EEC /* SentrySampleDecision.m */; }; 8453421628BE8A9500C22EEC /* SentrySpanStatus.m in Sources */ = {isa = PBXBuildFile; fileRef = 8453421528BE8A9500C22EEC /* SentrySpanStatus.m */; }; 8454CF8D293EAF9A006AC140 /* SentryMetricProfiler.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8454CF8B293EAF9A006AC140 /* SentryMetricProfiler.mm */; }; + 84A5D75B29D5170700388BFA /* TimeInterval+Sentry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84A5D75A29D5170700388BFA /* TimeInterval+Sentry.swift */; }; 84A888FD28D9B11700C51DFD /* SentryProfiler+Test.h in Headers */ = {isa = PBXBuildFile; fileRef = 84A888FC28D9B11700C51DFD /* SentryProfiler+Test.h */; }; 84A8891C28DBD28900C51DFD /* SentryDevice.h in Headers */ = {isa = PBXBuildFile; fileRef = 84A8891A28DBD28900C51DFD /* SentryDevice.h */; }; 84A8891D28DBD28900C51DFD /* SentryDevice.mm in Sources */ = {isa = PBXBuildFile; fileRef = 84A8891B28DBD28900C51DFD /* SentryDevice.mm */; }; @@ -763,6 +768,8 @@ D8ACE3CD2762187D00F5A213 /* SentryNSDataSwizzling.h in Headers */ = {isa = PBXBuildFile; fileRef = D8ACE3CA2762187D00F5A213 /* SentryNSDataSwizzling.h */; }; D8ACE3CE2762187D00F5A213 /* SentryNSDataTracker.h in Headers */ = {isa = PBXBuildFile; fileRef = D8ACE3CB2762187D00F5A213 /* SentryNSDataTracker.h */; }; D8ACE3CF2762187D00F5A213 /* SentryFileIOTrackingIntegration.h in Headers */ = {isa = PBXBuildFile; fileRef = D8ACE3CC2762187D00F5A213 /* SentryFileIOTrackingIntegration.h */; }; + D8B088B629C9E3FF00213258 /* SentryTracerConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = D8B088B429C9E3FF00213258 /* SentryTracerConfiguration.h */; }; + D8B088B729C9E3FF00213258 /* SentryTracerConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = D8B088B529C9E3FF00213258 /* SentryTracerConfiguration.m */; }; D8B76B062808066D000A58C4 /* SentryScreenshotIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8B76B042808060E000A58C4 /* SentryScreenshotIntegrationTests.swift */; }; D8B76B0828081461000A58C4 /* TestSentryScreenShot.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8B76B0728081461000A58C4 /* TestSentryScreenShot.swift */; }; D8BBD32728FD9FC00011F850 /* SentrySwift.h in Headers */ = {isa = PBXBuildFile; fileRef = D8BBD32628FD9FBF0011F850 /* SentrySwift.h */; }; @@ -885,7 +892,6 @@ 0A2D8D9728997887008720F6 /* NSLocale+Sentry.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "NSLocale+Sentry.h"; path = "include/NSLocale+Sentry.h"; sourceTree = ""; }; 0A2D8DA6289BC905008720F6 /* SentryViewHierarchy.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryViewHierarchy.h; path = include/SentryViewHierarchy.h; sourceTree = ""; }; 0A2D8DA7289BC905008720F6 /* SentryViewHierarchy.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryViewHierarchy.m; sourceTree = ""; }; - 0A4EDEA828D3461B00FA67CB /* SentryPerformanceTracker+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "SentryPerformanceTracker+Private.h"; path = "include/SentryPerformanceTracker+Private.h"; sourceTree = ""; }; 0A5370A028A3EC2400B2DCDE /* SentryViewHierarchyTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryViewHierarchyTests.swift; sourceTree = ""; }; 0A56DA5E28ABA01B00C400D5 /* SentryTransactionContext+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "SentryTransactionContext+Private.h"; path = "include/SentryTransactionContext+Private.h"; sourceTree = ""; }; 0A6EEADC28A657970076B469 /* UIViewRecursiveDescriptionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIViewRecursiveDescriptionTests.swift; sourceTree = ""; }; @@ -920,7 +926,13 @@ 15E0A8EC240F2CB000F044E3 /* SentrySerialization.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SentrySerialization.m; sourceTree = ""; }; 15E0A8EF240F638200F044E3 /* SentrySerializationNilTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentrySerializationNilTests.m; sourceTree = ""; }; 15E0A8F12411A45A00F044E3 /* SentrySession.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SentrySession.m; sourceTree = ""; }; + 33042A0B29DAF5F400C60085 /* SentryExtraContextProvider.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SentryExtraContextProvider.h; sourceTree = ""; }; + 33042A0C29DAF79A00C60085 /* SentryExtraContextProvider.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryExtraContextProvider.m; sourceTree = ""; }; + 33042A1629DC2C4300C60085 /* SentryExtraContextProviderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryExtraContextProviderTests.swift; sourceTree = ""; }; 627E7588299F6FE40085504D /* SentryInternalDefines.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryInternalDefines.h; path = include/SentryInternalDefines.h; sourceTree = ""; }; + 62885DA629E946B100554F38 /* TestConncurrentModifications.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestConncurrentModifications.swift; sourceTree = ""; }; + 62E081A829ED4260000F69FC /* SentryBreadcrumbDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryBreadcrumbDelegate.h; path = include/SentryBreadcrumbDelegate.h; sourceTree = ""; }; + 62E081AA29ED4322000F69FC /* SentryBreadcrumbTestDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryBreadcrumbTestDelegate.swift; sourceTree = ""; }; 62F226B629A37C120038080D /* SentryBooleanSerialization.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryBooleanSerialization.m; sourceTree = ""; }; 62F226B829A37C270038080D /* SentryBooleanSerialization.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SentryBooleanSerialization.h; sourceTree = ""; }; 630435FC1EBCA9D900C4D3FA /* SentryNSURLRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SentryNSURLRequest.h; path = include/SentryNSURLRequest.h; sourceTree = ""; }; @@ -1530,6 +1542,7 @@ 849472802971C107002603DE /* SentrySystemWrapperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentrySystemWrapperTests.swift; sourceTree = ""; }; 849472822971C2CD002603DE /* SentryNSProcessInfoWrapperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryNSProcessInfoWrapperTests.swift; sourceTree = ""; }; 849472842971C41A002603DE /* SentryNSTimerWrapperTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryNSTimerWrapperTest.swift; sourceTree = ""; }; + 84A5D75A29D5170700388BFA /* TimeInterval+Sentry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TimeInterval+Sentry.swift"; sourceTree = ""; }; 84A888FC28D9B11700C51DFD /* SentryProfiler+Test.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "SentryProfiler+Test.h"; path = "Sources/Sentry/include/SentryProfiler+Test.h"; sourceTree = SOURCE_ROOT; }; 84A8891A28DBD28900C51DFD /* SentryDevice.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryDevice.h; path = include/SentryDevice.h; sourceTree = ""; }; 84A8891B28DBD28900C51DFD /* SentryDevice.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = SentryDevice.mm; sourceTree = ""; }; @@ -1673,6 +1686,8 @@ D8ACE3CA2762187D00F5A213 /* SentryNSDataSwizzling.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SentryNSDataSwizzling.h; path = include/SentryNSDataSwizzling.h; sourceTree = ""; }; D8ACE3CB2762187D00F5A213 /* SentryNSDataTracker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SentryNSDataTracker.h; path = include/SentryNSDataTracker.h; sourceTree = ""; }; D8ACE3CC2762187D00F5A213 /* SentryFileIOTrackingIntegration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SentryFileIOTrackingIntegration.h; path = include/SentryFileIOTrackingIntegration.h; sourceTree = ""; }; + D8B088B429C9E3FF00213258 /* SentryTracerConfiguration.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryTracerConfiguration.h; path = include/SentryTracerConfiguration.h; sourceTree = ""; }; + D8B088B529C9E3FF00213258 /* SentryTracerConfiguration.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryTracerConfiguration.m; sourceTree = ""; }; D8B76B042808060E000A58C4 /* SentryScreenshotIntegrationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryScreenshotIntegrationTests.swift; sourceTree = ""; }; D8B76B0728081461000A58C4 /* TestSentryScreenShot.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestSentryScreenShot.swift; sourceTree = ""; }; D8BBD32628FD9FBF0011F850 /* SentrySwift.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SentrySwift.h; path = include/SentrySwift.h; sourceTree = ""; }; @@ -2104,6 +2119,8 @@ 844EDC75294144DB00C86F34 /* SentrySystemWrapper.mm */, 844EDCE32947DC3100C86F34 /* SentryNSTimerWrapper.h */, 844EDCE42947DC3100C86F34 /* SentryNSTimerWrapper.m */, + 33042A0B29DAF5F400C60085 /* SentryExtraContextProvider.h */, + 33042A0C29DAF79A00C60085 /* SentryExtraContextProvider.m */, ); name = Helper; sourceTree = ""; @@ -2709,6 +2726,7 @@ 84A8892028DBD8D600C51DFD /* SentryDeviceTests.mm */, D85790282976A69F00C6AC1F /* TestDebugImageProvider.swift */, 8431EE5A29ADB8EA00D8DC56 /* SentryTimeTests.m */, + 33042A1629DC2C4300C60085 /* SentryExtraContextProviderTests.swift */, ); path = Helper; sourceTree = ""; @@ -2722,6 +2740,7 @@ A839D89924864BA8003B7AFD /* SentrySystemEventBreadcrumbs.m */, 639889B51EDECFA800EA7442 /* SentryBreadcrumbTracker.h */, 639889B61EDECFA800EA7442 /* SentryBreadcrumbTracker.m */, + 62E081A829ED4260000F69FC /* SentryBreadcrumbDelegate.h */, ); name = Breadcrumbs; sourceTree = ""; @@ -2822,7 +2841,6 @@ 8EAE9804261E87120073B6B3 /* SentryUIViewControllerPerformanceTracker.m */, 8EAE9809261E9F530073B6B3 /* SentryPerformanceTracker.h */, 8EBF870726140D37001A6853 /* SentryPerformanceTracker.m */, - 0A4EDEA828D3461B00FA67CB /* SentryPerformanceTracker+Private.h */, D8BFE37029A3782F002E73F3 /* SentryTimeToDisplayTracker.h */, D8BFE37129A3782F002E73F3 /* SentryTimeToDisplayTracker.m */, ); @@ -2852,6 +2870,7 @@ 7BE0DC30272ABCEC004FA8B7 /* SentryAutoBreadcrumbTrackingIntegration+Test.h */, 7BE0DC28272A9E1C004FA8B7 /* SentryBreadcrumbTrackerTests.swift */, A811D866248E2770008A41EA /* SentrySystemEventBreadcrumbsTest.swift */, + 62E081AA29ED4322000F69FC /* SentryBreadcrumbTestDelegate.swift */, ); path = Breadcrumbs; sourceTree = ""; @@ -2954,6 +2973,7 @@ 7BF536D224BEF240004FA6A2 /* TestUtils */ = { isa = PBXGroup; children = ( + 62885DA629E946B100554F38 /* TestConncurrentModifications.swift */, 7BF536D324BEF255004FA6A2 /* SentryAssertions.swift */, 7B6D98EC24C703F8005502FA /* Async.swift */, 7BF9EF712722A84800B5BBEF /* SentryClassRegistrator.h */, @@ -3073,6 +3093,7 @@ isa = PBXGroup; children = ( 7BD47B4C268F0B080076A663 /* ClearTestState.swift */, + 84A5D75A29D5170700388BFA /* TimeInterval+Sentry.swift */, 7B30B68126527C55006B2752 /* TestDisplayLinkWrapper.swift */, 8E25C97425F8511A00DC215B /* TestRandom.swift */, 7BE3C7762445E50A00A38442 /* TestCurrentDateProvider.swift */, @@ -3132,6 +3153,8 @@ 8E133FA025E72DEF00ABD0BF /* SentrySamplingContext.m */, 8E4E7C7B25DAB287006AB9E2 /* SentryTracer.h */, 8E4E7C8125DAB2A5006AB9E2 /* SentryTracer.m */, + D8B088B429C9E3FF00213258 /* SentryTracerConfiguration.h */, + D8B088B529C9E3FF00213258 /* SentryTracerConfiguration.m */, 84AF45A429A7FFA500FBB177 /* SentryTracerConcurrency.h */, 84AF45A529A7FFA500FBB177 /* SentryTracerConcurrency.mm */, 8E8C57A525EEFC42001CEEFA /* SentryTracesSampler.h */, @@ -3366,7 +3389,6 @@ 63AA76991EB9C1C200D153DE /* SentryDefines.h in Headers */, D86B6835294348A400B8B1FC /* SentryAttachment+Private.h in Headers */, 84AF45A629A7FFA500FBB177 /* SentryTracerConcurrency.h in Headers */, - 0A4EDEA928D3461B00FA67CB /* SentryPerformanceTracker+Private.h in Headers */, 7B2A70DB27D607CF008B0D15 /* SentryThreadWrapper.h in Headers */, 8EAE980B261E9F530073B6B3 /* SentryPerformanceTracker.h in Headers */, 63FE718520DA4C1100CDBAE8 /* SentryCrashC.h in Headers */, @@ -3482,6 +3504,7 @@ 63BE85701ECEC6DE00DC44F5 /* NSDate+SentryExtras.h in Headers */, 63FE709520DA4C1000CDBAE8 /* SentryCrashReportFilterBasic.h in Headers */, 844EDCE52947DC3100C86F34 /* SentryNSTimerWrapper.h in Headers */, + D8B088B629C9E3FF00213258 /* SentryTracerConfiguration.h in Headers */, 63FE70A120DA4C1000CDBAE8 /* SentryCrashCString.h in Headers */, 7B63459D280EBA6300CFA05A /* SentryUIEventTracker.h in Headers */, 7B7D873424864C6600D2ECFF /* SentryCrashDefaultMachineContextWrapper.h in Headers */, @@ -3512,6 +3535,7 @@ 7B3B83722833832B0001FDEB /* SentrySpanOperations.h in Headers */, 7BF9EF722722A84800B5BBEF /* SentryClassRegistrator.h in Headers */, 63FE715520DA4C1100CDBAE8 /* SentryCrashStackCursor_MachineContext.h in Headers */, + 62E081A929ED4260000F69FC /* SentryBreadcrumbDelegate.h in Headers */, 15360CF02433A16D00112302 /* SentryInstallation.h in Headers */, 63FE714720DA4C1100CDBAE8 /* SentryCrashMachineContext.h in Headers */, 7BA61CAB247BA98100C130A8 /* SentryDebugImageProvider.h in Headers */, @@ -3835,6 +3859,7 @@ 7B3B473825D6CC7E00D01640 /* SentryNSError.m in Sources */, D8ACE3C82762187200F5A213 /* SentryNSDataTracker.m in Sources */, 7BE3C77D2446112C00A38442 /* SentryRateLimitParser.m in Sources */, + D8B088B729C9E3FF00213258 /* SentryTracerConfiguration.m in Sources */, 8ECC674A25C23A20000E2BF6 /* SentryTransactionContext.mm in Sources */, 03BCC38C27E1C01A003232C7 /* SentryTime.mm in Sources */, A8F17B342902870300990B25 /* SentryHttpStatusCodeRange.m in Sources */, @@ -3885,6 +3910,7 @@ 630435FF1EBCA9D900C4D3FA /* SentryNSURLRequest.m in Sources */, 7B5CAF7727F5A68C00ED0DB6 /* SentryNSURLRequestBuilder.m in Sources */, 639FCFA11EBC804600778193 /* SentryException.m in Sources */, + 33042A0D29DAF79A00C60085 /* SentryExtraContextProvider.m in Sources */, 7BA61CAD247BAA0B00C130A8 /* SentryDebugImageProvider.m in Sources */, 63FE70E720DA4C1000CDBAE8 /* SentryCrashMonitor.c in Sources */, 84354E1229BF944900CDBB8B /* SentryProfileTimeseries.mm in Sources */, @@ -4103,12 +4129,14 @@ 7B6D1263265F7CC600C9BE4B /* PrivateSentrySDKOnlyTests.swift in Sources */, 7BB42EF124F3B7B700D7B39A /* SentrySession+Equality.m in Sources */, 7BF9EF8B2722D58700B5BBEF /* SentryInitializeForGettingSubclassesNotCalled.m in Sources */, + 33042A1729DC2C4300C60085 /* SentryExtraContextProviderTests.swift in Sources */, D8137D54272B53070082656C /* TestSentrySpan.m in Sources */, 7BECF432261463E600D9826E /* SentryMechanismMetaTests.swift in Sources */, 7BE8E8462593313500C4DA1F /* SentryAttachment+Equality.m in Sources */, 63FE721F20DA66EC00CDBAE8 /* SentryCrashSignalInfo_Tests.m in Sources */, 0ADC33F128D9BE940078D980 /* TestSentryUIDeviceWrapper.swift in Sources */, 63FE721420DA66EC00CDBAE8 /* SentryCrashMemory_Tests.m in Sources */, + 62885DA729E946B100554F38 /* TestConncurrentModifications.swift in Sources */, 63FE720520DA66EC00CDBAE8 /* FileBasedTestCase.m in Sources */, 0A6EEADD28A657970076B469 /* UIViewRecursiveDescriptionTests.swift in Sources */, 63EED6C32237989300E02400 /* SentryOptionsTest.m in Sources */, @@ -4121,6 +4149,7 @@ 7BE912AF272166DD00E49E62 /* SentryNoOpSpanTests.swift in Sources */, 7B56D73524616E5600B842DA /* SentryConcurrentRateLimitsDictionaryTests.swift in Sources */, 7B7D8730248648AD00D2ECFF /* SentryStacktraceBuilderTests.swift in Sources */, + 62E081AB29ED4322000F69FC /* SentryBreadcrumbTestDelegate.swift in Sources */, D8751FA5274743710032F4DE /* SentryNSURLSessionTaskSearchTests.swift in Sources */, D86F419827C8FEFA00490520 /* SentryCoreDataMiddleware+Extension.swift in Sources */, 7B944FB32469C02900A10721 /* TestHub.swift in Sources */, @@ -4295,6 +4324,7 @@ 8431F01629B2851500D8DC56 /* TestSentryNSProcessInfoWrapper.swift in Sources */, 84B7FA4229B28CDE00AD93B1 /* TestCurrentDateProvider.swift in Sources */, 84B7FA3F29B28BAD00AD93B1 /* TestTransport.swift in Sources */, + 84A5D75B29D5170700388BFA /* TimeInterval+Sentry.swift in Sources */, 8431F01929B2852D00D8DC56 /* Invocation.swift in Sources */, 84B7FA4629B2935F00AD93B1 /* ClearTestState.swift in Sources */, 8431F01529B2851500D8DC56 /* TestSentryNSTimerWrapper.swift in Sources */, diff --git a/Sentry.xcodeproj/xcshareddata/xcschemes/Sentry.xcscheme b/Sentry.xcodeproj/xcshareddata/xcschemes/Sentry.xcscheme index 378ddf22970..2dd6a7be9d5 100644 --- a/Sentry.xcodeproj/xcshareddata/xcschemes/Sentry.xcscheme +++ b/Sentry.xcodeproj/xcshareddata/xcschemes/Sentry.xcscheme @@ -76,9 +76,6 @@ - - diff --git a/SentryPrivate.podspec b/SentryPrivate.podspec index 55c5e7998d1..01b0fb283db 100644 --- a/SentryPrivate.podspec +++ b/SentryPrivate.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "SentryPrivate" - s.version = "8.4.0" + s.version = "8.5.0" s.summary = "Sentry Private Library." s.homepage = "https://github.com/getsentry/sentry-cocoa" s.license = "mit" diff --git a/SentrySwiftUI.podspec b/SentrySwiftUI.podspec index 637d7c6ab42..29349fe1ef3 100644 --- a/SentrySwiftUI.podspec +++ b/SentrySwiftUI.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "SentrySwiftUI" - s.version = "8.4.0" + s.version = "8.5.0" s.summary = "Sentry client for SwiftUI" s.homepage = "https://github.com/getsentry/sentry-cocoa" s.license = "mit" @@ -19,5 +19,5 @@ Pod::Spec.new do |s| s.watchos.framework = 'WatchKit' s.source_files = "Sources/SentrySwiftUI/**/*.{swift,h,m}" - s.dependency 'Sentry', "8.4.0" + s.dependency 'Sentry', "8.5.0" end diff --git a/SentryTestUtils/ClearTestState.swift b/SentryTestUtils/ClearTestState.swift index 14f72adc610..6a87a432493 100644 --- a/SentryTestUtils/ClearTestState.swift +++ b/SentryTestUtils/ClearTestState.swift @@ -34,5 +34,9 @@ class TestCleanup: NSObject { SentrySwizzleWrapper.sharedInstance.removeAllCallbacks() SentryTracer.resetAppStartMeasurementRead() + +#if os(iOS) || os(macOS) || targetEnvironment(macCatalyst) + SentryTracer.resetConcurrencyTracking() +#endif } } diff --git a/SentryTestUtils/TestClient.swift b/SentryTestUtils/TestClient.swift index fb037f8ca9d..f6d885c91e7 100644 --- a/SentryTestUtils/TestClient.swift +++ b/SentryTestUtils/TestClient.swift @@ -15,7 +15,7 @@ public class TestClient: SentryClient { // Without this override we get a fatal error: use of unimplemented initializer // see https://stackoverflow.com/questions/28187261/ios-swift-fatal-error-use-of-unimplemented-initializer-init - public override init(options: Options, transportAdapter: SentryTransportAdapter, fileManager: SentryFileManager, deleteOldEnvelopeItems: Bool, threadInspector: SentryThreadInspector, random: SentryRandomProtocol, crashWrapper: SentryCrashWrapper, deviceWrapper: SentryUIDeviceWrapper, locale: Locale, timezone: TimeZone) { + public override init(options: Options, transportAdapter: SentryTransportAdapter, fileManager: SentryFileManager, deleteOldEnvelopeItems: Bool, threadInspector: SentryThreadInspector, random: SentryRandomProtocol, locale: Locale, timezone: TimeZone, extraContextProvider: SentryExtraContextProvider) { super.init( options: options, transportAdapter: transportAdapter, @@ -23,10 +23,9 @@ public class TestClient: SentryClient { deleteOldEnvelopeItems: false, threadInspector: threadInspector, random: random, - crashWrapper: crashWrapper, - deviceWrapper: deviceWrapper, locale: locale, - timezone: timezone + timezone: timezone, + extraContextProvider: extraContextProvider ) } diff --git a/SentryTestUtils/TestCurrentDateProvider.swift b/SentryTestUtils/TestCurrentDateProvider.swift index e2f325689ed..f99af35bea2 100644 --- a/SentryTestUtils/TestCurrentDateProvider.swift +++ b/SentryTestUtils/TestCurrentDateProvider.swift @@ -2,9 +2,9 @@ import Foundation @objc public class TestCurrentDateProvider: NSObject, CurrentDateProvider { - - private var internalDate = Date(timeIntervalSinceReferenceDate: 0) - + public static let defaultStartingDate = Date(timeIntervalSinceReferenceDate: 0) + private var internalDate = defaultStartingDate + private var internalSystemTime: UInt64 = 0 public var driftTimeForEveryRead = false public func date() -> Date { @@ -17,10 +17,27 @@ public class TestCurrentDateProvider: NSObject, CurrentDateProvider { return internalDate } + + /// Reset the date provider to its default starting date. + public func reset() { + setDate(date: TestCurrentDateProvider.defaultStartingDate) + internalSystemTime = 0 + } public func setDate(date: Date) { internalDate = date } + + /// Advance the current date by the specified number of seconds. + public func advance(by seconds: TimeInterval) { + setDate(date: date().addingTimeInterval(seconds)) + internalSystemTime += seconds.toNanoSeconds() + } + + public func advanceBy(nanoseconds: UInt64) { + setDate(date: date().addingTimeInterval(TimeInterval(nanoseconds) / 1e9)) + internalSystemTime += nanoseconds + } public var internalDispatchNow = DispatchTime.now() public func dispatchTimeNow() -> dispatch_time_t { @@ -31,4 +48,8 @@ public class TestCurrentDateProvider: NSObject, CurrentDateProvider { public func timezoneOffset() -> Int { return timezoneOffsetValue } + + public func systemTime() -> UInt64 { + return internalSystemTime + } } diff --git a/SentryTestUtils/TestDisplayLinkWrapper.swift b/SentryTestUtils/TestDisplayLinkWrapper.swift index 3ce7f56df14..b2c0636b612 100644 --- a/SentryTestUtils/TestDisplayLinkWrapper.swift +++ b/SentryTestUtils/TestDisplayLinkWrapper.swift @@ -1,77 +1,109 @@ import Foundation #if os(iOS) || os(tvOS) || targetEnvironment(macCatalyst) +/// The smallest magnitude of time that is significant to how frames are classified as normal/slow/frozen. +let timeEpsilon = 0.001 + +public enum GPUFrame { + case normal + case slow + case frozen +} + +public enum FrameRate: UInt64 { + case low = 60 + case high = 120 + + public var tickDuration: CFTimeInterval { + return 1 / CFTimeInterval(self.rawValue) + } +} + public class TestDisplayLinkWrapper: SentryDisplayLinkWrapper { - public var target: AnyObject! public var selector: Selector! - var internalTimestamp = 0.0 - var internalActualFrameRate = 60.0 + public var currentFrameRate: FrameRate = .low let frozenFrameThreshold = 0.7 - - var frameDuration: Double { - return 1.0 / internalActualFrameRate - } - - private var slowFrameThreshold: CFTimeInterval { - return 1 / (Double(internalActualFrameRate) - 1.0) + public var dateProvider: TestCurrentDateProvider + + public init(dateProvider: TestCurrentDateProvider = TestCurrentDateProvider()) { + self.dateProvider = dateProvider } - + public override func link(withTarget target: Any, selector sel: Selector) { self.target = target as AnyObject self.selector = sel } + + public override var timestamp: CFTimeInterval { + return dateProvider.systemTime().toTimeInterval() + } + + public override var targetTimestamp: CFTimeInterval { + return dateProvider.systemTime().toTimeInterval() + currentFrameRate.tickDuration + } + + public override func invalidate() { + target = nil + selector = nil + } public func call() { _ = target.perform(selector) } - public override var timestamp: CFTimeInterval { - return internalTimestamp - } - - public func changeFrameRate(_ newFrameRate: Double) { - internalActualFrameRate = newFrameRate + public func changeFrameRate(_ newFrameRate: FrameRate) { + currentFrameRate = newFrameRate } public func normalFrame() { - internalTimestamp += frameDuration + dateProvider.advance(by: currentFrameRate.tickDuration) call() } - public func slowFrame() { - internalTimestamp += slowFrameThreshold + 0.001 + public func fastestSlowFrame() -> CFTimeInterval { + let duration: Double = slowFrameThreshold(currentFrameRate.rawValue) + timeEpsilon + dateProvider.advance(by: duration) call() + return duration } - - public func almostFrozenFrame() { - internalTimestamp += frozenFrameThreshold + + public func middlingSlowFrame() -> CFTimeInterval { + let duration: Double = (frozenFrameThreshold - (slowFrameThreshold(currentFrameRate.rawValue) + timeEpsilon)) / 2.0 + dateProvider.advance(by: duration) call() + return duration } - public func frozenFrame() { - internalTimestamp += frozenFrameThreshold + 0.001 + public func slowestSlowFrame() -> CFTimeInterval { + dateProvider.advance(by: frozenFrameThreshold) call() + return frozenFrameThreshold } - - public override var targetTimestamp: CFTimeInterval { - return internalTimestamp + frameDuration + + public func fastestFrozenFrame() -> CFTimeInterval { + let duration: Double = frozenFrameThreshold + timeEpsilon + dateProvider.advance(by: duration) + call() + return duration } - - public override func invalidate() { - target = nil - selector = nil + + /// There's no upper bound for a frozen frame, except maybe for the watchdog time limit. + /// - parameter extraTime: the additional time to add to the frozen frame threshold when simulating a frozen frame. + public func slowerFrozenFrame(extraTime: TimeInterval = 0.1) { + dateProvider.advance(by: frozenFrameThreshold + extraTime) + call() } public func givenFrames(_ slow: Int, _ frozen: Int, _ normal: Int) { self.call() for _ in 0.. Void)? + + struct InvocationInfo { + var target: NSObject + var selector: Selector + } + var invocationInfo: InvocationInfo? } - public lazy var overrides = Overrides() + public var overrides = Overrides() public override func scheduledTimer(withTimeInterval interval: TimeInterval, repeats: Bool, block: @escaping (Timer) -> Void) -> Timer { - let timer = TestTimer() + let timer = Timer.scheduledTimer(withTimeInterval: TimeInterval.infinity, repeats: repeats, block: block) overrides.timer = timer overrides.block = block return timer } + public override func scheduledTimer(withTimeInterval ti: TimeInterval, target aTarget: Any, selector aSelector: Selector, userInfo: Any?, repeats yesOrNo: Bool) -> Timer { + let timer = Timer.scheduledTimer(timeInterval: ti, target: aTarget, selector: aSelector, userInfo: userInfo, repeats: yesOrNo) + //swiftlint:disable force_cast + overrides.invocationInfo = Overrides.InvocationInfo(target: aTarget as! NSObject, selector: aSelector) + //swiftlint:enable force_cast + return timer + } + public func fire() { - overrides.block?(overrides.timer) + if let block = overrides.block { + block(overrides.timer) + } else if let invocationInfo = overrides.invocationInfo { + try! Invocation(target: invocationInfo.target, selector: invocationInfo.selector).invoke() + } } } diff --git a/SentryTestUtils/TimeInterval+Sentry.swift b/SentryTestUtils/TimeInterval+Sentry.swift new file mode 100644 index 00000000000..c75691d562e --- /dev/null +++ b/SentryTestUtils/TimeInterval+Sentry.swift @@ -0,0 +1,11 @@ +public extension TimeInterval { + func toNanoSeconds() -> UInt64 { + return UInt64(self * Double(NSEC_PER_SEC)) + } +} + +public extension UInt64 { + func toTimeInterval() -> TimeInterval { + return Double(self) / Double(NSEC_PER_SEC) + } +} diff --git a/Sources/Configuration/Sentry.xcconfig b/Sources/Configuration/Sentry.xcconfig index 6ca539ff207..61103651a8a 100644 --- a/Sources/Configuration/Sentry.xcconfig +++ b/Sources/Configuration/Sentry.xcconfig @@ -2,6 +2,6 @@ PRODUCT_NAME = Sentry INFOPLIST_FILE = Sources/Sentry/Info.plist PRODUCT_BUNDLE_IDENTIFIER = io.sentry.Sentry -CURRENT_PROJECT_VERSION = 8.4.0 +CURRENT_PROJECT_VERSION = 8.5.0 MODULEMAP_FILE = $(SRCROOT)/Sources/Sentry/Sentry.modulemap diff --git a/Sources/Configuration/SentryPrivate.xcconfig b/Sources/Configuration/SentryPrivate.xcconfig index 4ec32be3fad..e5cf62409c8 100644 --- a/Sources/Configuration/SentryPrivate.xcconfig +++ b/Sources/Configuration/SentryPrivate.xcconfig @@ -1,3 +1,3 @@ PRODUCT_NAME = SentryPrivate MACH_O_TYPE = staticlib -CURRENT_PROJECT_VERSION = 8.4.0 +CURRENT_PROJECT_VERSION = 8.5.0 diff --git a/Sources/Sentry/PrivateSentrySDKOnly.m b/Sources/Sentry/PrivateSentrySDKOnly.m index c60549fe3df..1dcbac8ca64 100644 --- a/Sources/Sentry/PrivateSentrySDKOnly.m +++ b/Sources/Sentry/PrivateSentrySDKOnly.m @@ -1,6 +1,7 @@ #import "PrivateSentrySDKOnly.h" #import "SentryClient.h" #import "SentryDebugImageProvider.h" +#import "SentryExtraContextProvider.h" #import "SentryHub+Private.h" #import "SentryInstallation.h" #import "SentryMeta.h" @@ -100,6 +101,11 @@ + (NSString *)getSdkVersionString return SentryMeta.versionString; } ++ (NSDictionary *)getExtraContext +{ + return [[SentryExtraContextProvider sharedInstance] getExtraContext]; +} + #if SENTRY_HAS_UIKIT + (BOOL)framesTrackingMeasurementHybridSDKMode diff --git a/Sources/Sentry/SentryAppStartTracker.m b/Sources/Sentry/SentryAppStartTracker.m index 801c067d188..3b351d1aabb 100644 --- a/Sources/Sentry/SentryAppStartTracker.m +++ b/Sources/Sentry/SentryAppStartTracker.m @@ -67,6 +67,7 @@ - (instancetype)initWithCurrentDateProvider:(id)curre self.wasInBackground = NO; self.didFinishLaunchingTimestamp = [currentDateProvider date]; self.enablePreWarmedAppStartTracing = enablePreWarmedAppStartTracing; + self.isRunning = NO; } return self; } @@ -116,6 +117,8 @@ - (void)start # if SENTRY_HAS_UIKIT [self.appStateManager start]; # endif + + self.isRunning = YES; } - (void)buildAppStartMeasurement @@ -208,8 +211,8 @@ - (void)buildAppStartMeasurement SentrySDK.appStartMeasurement = appStartMeasurement; }; - // With only running this once we know that the process is a new one when the following - // code is executed. +// With only running this once we know that the process is a new one when the following +// code is executed. // We need to make sure the block runs on each test instead of only once # if TEST block(); @@ -285,6 +288,10 @@ - (void)stop [NSNotificationCenter.defaultCenter removeObserver:self name:UIApplicationDidEnterBackgroundNotification object:nil]; + +# if TEST + self.isRunning = NO; +# endif } - (void)dealloc diff --git a/Sources/Sentry/SentryAutoBreadcrumbTrackingIntegration.m b/Sources/Sentry/SentryAutoBreadcrumbTrackingIntegration.m index 6a054907b46..2171743e317 100644 --- a/Sources/Sentry/SentryAutoBreadcrumbTrackingIntegration.m +++ b/Sources/Sentry/SentryAutoBreadcrumbTrackingIntegration.m @@ -53,7 +53,7 @@ - (void)installWithOptions:(nonnull SentryOptions *)options systemEventBreadcrumbs:(SentrySystemEventBreadcrumbs *)systemEventBreadcrumbs { self.breadcrumbTracker = breadcrumbTracker; - [self.breadcrumbTracker start]; + [self.breadcrumbTracker startWithDelegate:self]; if (options.enableSwizzling) { [self.breadcrumbTracker startSwizzle]; diff --git a/Sources/Sentry/SentryBreadcrumbTracker.m b/Sources/Sentry/SentryBreadcrumbTracker.m index dfc635fb04b..f2cccb57d7d 100644 --- a/Sources/Sentry/SentryBreadcrumbTracker.m +++ b/Sources/Sentry/SentryBreadcrumbTracker.m @@ -1,5 +1,6 @@ #import "SentryBreadcrumbTracker.h" #import "SentryBreadcrumb.h" +#import "SentryBreadcrumbDelegate.h" #import "SentryClient.h" #import "SentryDefines.h" #import "SentryHub.h" @@ -25,6 +26,7 @@ SentryBreadcrumbTracker () @property (nonatomic, strong) SentrySwizzleWrapper *swizzleWrapper; +@property (nonatomic, weak) id delegate; @end @@ -38,8 +40,9 @@ - (instancetype)initWithSwizzleWrapper:(SentrySwizzleWrapper *)swizzleWrapper return self; } -- (void)start +- (void)startWithDelegate:(id)delegate { + _delegate = delegate; [self addEnabledCrumb]; [self trackApplicationUIKitNotifications]; } @@ -57,6 +60,7 @@ - (void)stop #if SENTRY_HAS_UIKIT [self.swizzleWrapper removeSwizzleSendActionForKey:SentryBreadcrumbTrackerSwizzleSendAction]; #endif + _delegate = nil; } - (void)trackApplicationUIKitNotifications @@ -81,15 +85,13 @@ - (void)trackApplicationUIKitNotifications object:nil queue:nil usingBlock:^(NSNotification *notification) { - if (nil != [SentrySDK.currentHub getClient]) { - SentryBreadcrumb *crumb = - [[SentryBreadcrumb alloc] initWithLevel:kSentryLevelWarning - category:@"device.event"]; - crumb.type = @"system"; - crumb.data = @ { @"action" : @"LOW_MEMORY" }; - crumb.message = @"Low memory"; - [SentrySDK addBreadcrumb:crumb]; - } + SentryBreadcrumb *crumb = + [[SentryBreadcrumb alloc] initWithLevel:kSentryLevelWarning + category:@"device.event"]; + crumb.type = @"system"; + crumb.data = @ { @"action" : @"LOW_MEMORY" }; + crumb.message = @"Low memory"; + [self.delegate addBreadcrumb:crumb]; }]; #endif @@ -124,12 +126,10 @@ - (void)addBreadcrumbWithType:(NSString *)type withDataKey:(NSString *)key withDataValue:(NSString *)value { - if (nil != [SentrySDK.currentHub getClient]) { - SentryBreadcrumb *crumb = [[SentryBreadcrumb alloc] initWithLevel:level category:category]; - crumb.type = type; - crumb.data = @{ key : value }; - [SentrySDK addBreadcrumb:crumb]; - } + SentryBreadcrumb *crumb = [[SentryBreadcrumb alloc] initWithLevel:level category:category]; + crumb.type = type; + crumb.data = @{ key : value }; + [self.delegate addBreadcrumb:crumb]; } - (void)addEnabledCrumb @@ -138,7 +138,7 @@ - (void)addEnabledCrumb category:@"started"]; crumb.type = @"debug"; crumb.message = @"Breadcrumb Tracking"; - [SentrySDK addBreadcrumb:crumb]; + [self.delegate addBreadcrumb:crumb]; } #if SENTRY_HAS_UIKIT @@ -165,8 +165,7 @@ - (void)swizzleSendAction #if SENTRY_HAS_UIKIT [self.swizzleWrapper swizzleSendAction:^(NSString *action, id target, id sender, UIEvent *event) { - if ([SentrySDK.currentHub getClient] == nil || - [SentryBreadcrumbTracker avoidSender:sender forTarget:target action:action]) { + if ([SentryBreadcrumbTracker avoidSender:sender forTarget:target action:action]) { return; } @@ -182,7 +181,7 @@ - (void)swizzleSendAction crumb.type = @"user"; crumb.message = action; crumb.data = data; - [SentrySDK addBreadcrumb:crumb]; + [self.delegate addBreadcrumb:crumb]; } forKey:SentryBreadcrumbTrackerSwizzleSendAction]; diff --git a/Sources/Sentry/SentryClient.m b/Sources/Sentry/SentryClient.m index c9c962a3d17..4281f5b9d3a 100644 --- a/Sources/Sentry/SentryClient.m +++ b/Sources/Sentry/SentryClient.m @@ -8,7 +8,6 @@ #import "SentryCrashDefaultMachineContextWrapper.h" #import "SentryCrashIntegration.h" #import "SentryCrashStackEntryMapper.h" -#import "SentryCrashWrapper.h" #import "SentryDebugImageProvider.h" #import "SentryDefaultCurrentDateProvider.h" #import "SentryDependencyContainer.h" @@ -18,6 +17,7 @@ #import "SentryEnvelopeItemType.h" #import "SentryEvent.h" #import "SentryException.h" +#import "SentryExtraContextProvider.h" #import "SentryFileManager.h" #import "SentryGlobalEventProcessor.h" #import "SentryHub+Private.h" @@ -31,7 +31,6 @@ #import "SentryMessage.h" #import "SentryMeta.h" #import "SentryNSError.h" -#import "SentryNSProcessInfoWrapper.h" #import "SentryOptions+Private.h" #import "SentrySDK+Private.h" #import "SentryScope+Private.h" @@ -43,7 +42,6 @@ #import "SentryTransport.h" #import "SentryTransportAdapter.h" #import "SentryTransportFactory.h" -#import "SentryUIDeviceWrapper.h" #import "SentryUser.h" #import "SentryUserFeedback.h" #import "SentryWatchdogTerminationTracker.h" @@ -60,11 +58,9 @@ @property (nonatomic, strong) SentryTransportAdapter *transportAdapter; @property (nonatomic, strong) SentryDebugImageProvider *debugImageProvider; @property (nonatomic, strong) id random; -@property (nonatomic, strong) SentryCrashWrapper *crashWrapper; -@property (nonatomic, strong) SentryUIDeviceWrapper *deviceWrapper; @property (nonatomic, strong) NSLocale *locale; @property (nonatomic, strong) NSTimeZone *timezone; -@property (nonatomic, strong) SentryNSProcessInfoWrapper *processInfoWrapper; +@property (nonatomic, strong) SentryExtraContextProvider *extraContextProvider; @end @@ -123,19 +119,10 @@ - (instancetype)initWithOptions:(SentryOptions *)options transportAdapter:(SentryTransportAdapter *)transportAdapter { - SentryInAppLogic *inAppLogic = - [[SentryInAppLogic alloc] initWithInAppIncludes:options.inAppIncludes - inAppExcludes:options.inAppExcludes]; - SentryCrashStackEntryMapper *crashStackEntryMapper = - [[SentryCrashStackEntryMapper alloc] initWithInAppLogic:inAppLogic]; - SentryStacktraceBuilder *stacktraceBuilder = - [[SentryStacktraceBuilder alloc] initWithCrashStackEntryMapper:crashStackEntryMapper]; - id machineContextWrapper = - [[SentryCrashDefaultMachineContextWrapper alloc] init]; SentryThreadInspector *threadInspector = - [[SentryThreadInspector alloc] initWithStacktraceBuilder:stacktraceBuilder - andMachineContextWrapper:machineContextWrapper]; - SentryUIDeviceWrapper *deviceWrapper = [[SentryUIDeviceWrapper alloc] init]; + [[SentryThreadInspector alloc] initWithOptions:options]; + + SentryExtraContextProvider *extraContextProvider = [SentryExtraContextProvider sharedInstance]; return [self initWithOptions:options transportAdapter:transportAdapter @@ -143,10 +130,9 @@ - (instancetype)initWithOptions:(SentryOptions *)options deleteOldEnvelopeItems:deleteOldEnvelopeItems threadInspector:threadInspector random:[SentryDependencyContainer sharedInstance].random - crashWrapper:[SentryCrashWrapper sharedInstance] - deviceWrapper:deviceWrapper locale:[NSLocale autoupdatingCurrentLocale] - timezone:[NSCalendar autoupdatingCurrentCalendar].timeZone]; + timezone:[NSCalendar autoupdatingCurrentCalendar].timeZone + extraContextProvider:extraContextProvider]; } - (instancetype)initWithOptions:(SentryOptions *)options @@ -155,10 +141,9 @@ - (instancetype)initWithOptions:(SentryOptions *)options deleteOldEnvelopeItems:(BOOL)deleteOldEnvelopeItems threadInspector:(SentryThreadInspector *)threadInspector random:(id)random - crashWrapper:(SentryCrashWrapper *)crashWrapper - deviceWrapper:(SentryUIDeviceWrapper *)deviceWrapper locale:(NSLocale *)locale timezone:(NSTimeZone *)timezone + extraContextProvider:(SentryExtraContextProvider *)extraContentProvider { if (self = [super init]) { _isEnabled = YES; @@ -167,13 +152,11 @@ - (instancetype)initWithOptions:(SentryOptions *)options self.fileManager = fileManager; self.threadInspector = threadInspector; self.random = random; - self.crashWrapper = crashWrapper; self.debugImageProvider = [SentryDependencyContainer sharedInstance].debugImageProvider; self.locale = locale; self.timezone = timezone; self.attachmentProcessors = [[NSMutableArray alloc] init]; - self.deviceWrapper = deviceWrapper; - self.processInfoWrapper = [[SentryNSProcessInfoWrapper alloc] init]; + self.extraContextProvider = extraContentProvider; if (deleteOldEnvelopeItems) { [fileManager deleteOldEnvelopeItems]; @@ -509,6 +492,7 @@ - (void)close { _isEnabled = NO; [self flush:self.options.shutdownTimeInterval]; + SENTRY_LOG_DEBUG(@"Closed the Client."); } - (SentryEvent *_Nullable)prepareEvent:(SentryEvent *)event @@ -791,36 +775,17 @@ - (void)applyCultureContextToEvent:(SentryEvent *)event - (void)applyExtraDeviceContextToEvent:(SentryEvent *)event { - [self - modifyContext:event - key:@"device" - block:^(NSMutableDictionary *device) { - device[SentryDeviceContextFreeMemoryKey] = @(self.crashWrapper.freeMemorySize); - device[@"free_storage"] = @(self.crashWrapper.freeStorageSize); - device[@"processor_count"] = @([self.processInfoWrapper processorCount]); - -#if TARGET_OS_IOS - if (self.deviceWrapper.orientation != UIDeviceOrientationUnknown) { - device[@"orientation"] - = UIDeviceOrientationIsPortrait(self.deviceWrapper.orientation) - ? @"portrait" - : @"landscape"; - } - - if (self.deviceWrapper.isBatteryMonitoringEnabled) { - device[@"charging"] - = self.deviceWrapper.batteryState == UIDeviceBatteryStateCharging - ? @(YES) - : @(NO); - device[@"battery_level"] = @((int)(self.deviceWrapper.batteryLevel * 100)); - } -#endif - }]; + NSDictionary *extraContext = [[self extraContextProvider] getExtraContext]; + [self modifyContext:event + key:@"device" + block:^(NSMutableDictionary *device) { + [device addEntriesFromDictionary:extraContext[@"device"]]; + }]; [self modifyContext:event key:@"app" block:^(NSMutableDictionary *app) { - app[SentryDeviceContextAppMemoryKey] = @(self.crashWrapper.appMemorySize); + [app addEntriesFromDictionary:extraContext[@"app"]]; }]; } diff --git a/Sources/Sentry/SentryCoreDataTracker.m b/Sources/Sentry/SentryCoreDataTracker.m index 98567e4a56d..0d3ee19f749 100644 --- a/Sources/Sentry/SentryCoreDataTracker.m +++ b/Sources/Sentry/SentryCoreDataTracker.m @@ -1,20 +1,30 @@ #import "SentryCoreDataTracker.h" +#import "SentryFrame.h" #import "SentryHub+Private.h" +#import "SentryInternalDefines.h" #import "SentryLog.h" +#import "SentryNSProcessInfoWrapper.h" #import "SentryPredicateDescriptor.h" #import "SentrySDK+Private.h" #import "SentryScope+Private.h" #import "SentrySpanProtocol.h" +#import "SentryStacktrace.h" +#import "SentryThreadInspector.h" @implementation SentryCoreDataTracker { SentryPredicateDescriptor *predicateDescriptor; + SentryThreadInspector *_threadInspector; + SentryNSProcessInfoWrapper *_processInfoWrapper; } -- (instancetype)init +- (instancetype)initWithThreadInspector:(SentryThreadInspector *)threadInspector + processInfoWrapper:(SentryNSProcessInfoWrapper *)processInfoWrapper; { if (self = [super init]) { predicateDescriptor = [[SentryPredicateDescriptor alloc] init]; + _threadInspector = threadInspector; + _processInfoWrapper = processInfoWrapper; } return self; } @@ -47,8 +57,9 @@ - (NSArray *)managedObjectContext:(NSManagedObjectContext *)context NSArray *result = original(request, error); if (fetchSpan) { - [fetchSpan setDataValue:[NSNumber numberWithInteger:result.count] forKey:@"read_count"]; + [self mainThreadExtraInfo:fetchSpan]; + [fetchSpan setDataValue:[NSNumber numberWithInteger:result.count] forKey:@"read_count"]; [fetchSpan finishWithStatus:result == nil ? kSentrySpanStatusInternalError : kSentrySpanStatusOk]; @@ -91,6 +102,7 @@ - (BOOL)managedObjectContext:(NSManagedObjectContext *)context BOOL result = original(error); if (fetchSpan) { + [self mainThreadExtraInfo:fetchSpan]; [fetchSpan finishWithStatus:result ? kSentrySpanStatusOk : kSentrySpanStatusInternalError]; SENTRY_LOG_DEBUG(@"SentryCoreDataTracker automatically finished span with status: %@", @@ -100,6 +112,20 @@ - (BOOL)managedObjectContext:(NSManagedObjectContext *)context return result; } +- (void)mainThreadExtraInfo:(SentrySpan *)span +{ + BOOL isMainThread = [NSThread isMainThread]; + + [span setDataValue:@(isMainThread) forKey:BLOCKED_MAIN_THREAD]; + + if (!isMainThread) { + return; + } + + SentryStacktrace *stackTrace = [_threadInspector stacktraceForCurrentThreadAsyncUnsafe]; + [span setFrames:stackTrace.frames]; +} + - (NSString *)descriptionForOperations: (NSDictionary *> *)operations inContext:(NSManagedObjectContext *)context diff --git a/Sources/Sentry/SentryCoreDataTrackingIntegration.m b/Sources/Sentry/SentryCoreDataTrackingIntegration.m index 5691c672229..937e2d6bb39 100644 --- a/Sources/Sentry/SentryCoreDataTrackingIntegration.m +++ b/Sources/Sentry/SentryCoreDataTrackingIntegration.m @@ -1,9 +1,12 @@ #import "SentryCoreDataTrackingIntegration.h" #import "SentryCoreDataSwizzling.h" #import "SentryCoreDataTracker.h" +#import "SentryDependencyContainer.h" #import "SentryLog.h" #import "SentryNSDataSwizzling.h" +#import "SentryNSProcessInfoWrapper.h" #import "SentryOptions.h" +#import "SentryThreadInspector.h" @interface SentryCoreDataTrackingIntegration () @@ -20,7 +23,9 @@ - (BOOL)installWithOptions:(SentryOptions *)options return NO; } - self.tracker = [[SentryCoreDataTracker alloc] init]; + self.tracker = [[SentryCoreDataTracker alloc] + initWithThreadInspector:[[SentryThreadInspector alloc] initWithOptions:options] + processInfoWrapper:[SentryDependencyContainer.sharedInstance processInfoWrapper]]; [SentryCoreDataSwizzling.sharedInstance startWithMiddleware:self.tracker]; return YES; diff --git a/Sources/Sentry/SentryCurrentDate.m b/Sources/Sentry/SentryCurrentDate.m index bed5c86e865..21468fe90cb 100644 --- a/Sources/Sentry/SentryCurrentDate.m +++ b/Sources/Sentry/SentryCurrentDate.m @@ -22,6 +22,14 @@ + (NSDate *)date return [currentDateProvider date]; } ++ (uint64_t)systemTime +{ + if (currentDateProvider == nil) { + currentDateProvider = [SentryDefaultCurrentDateProvider sharedInstance]; + } + return [currentDateProvider systemTime]; +} + + (dispatch_time_t)dispatchTimeNow { if (currentDateProvider == nil) { diff --git a/Sources/Sentry/SentryDefaultCurrentDateProvider.m b/Sources/Sentry/SentryDefaultCurrentDateProvider.m index 6e86349bbc1..e56a654be3d 100644 --- a/Sources/Sentry/SentryDefaultCurrentDateProvider.m +++ b/Sources/Sentry/SentryDefaultCurrentDateProvider.m @@ -1,4 +1,5 @@ #import "SentryDefaultCurrentDateProvider.h" +#import "SentryTime.h" NS_ASSUME_NONNULL_BEGIN @@ -32,6 +33,11 @@ - (NSInteger)timezoneOffset return [NSTimeZone localTimeZone].secondsFromGMT; } +- (uint64_t)systemTime +{ + return getAbsoluteTime(); +} + @end NS_ASSUME_NONNULL_END diff --git a/Sources/Sentry/SentryDependencyContainer.m b/Sources/Sentry/SentryDependencyContainer.m index 77c0f6f051a..58b802dcd7e 100644 --- a/Sources/Sentry/SentryDependencyContainer.m +++ b/Sources/Sentry/SentryDependencyContainer.m @@ -1,6 +1,7 @@ #import "SentryANRTracker.h" #import "SentryDefaultCurrentDateProvider.h" #import "SentryDispatchQueueWrapper.h" +#import "SentryNSProcessInfoWrapper.h" #import "SentryUIApplication.h" #import #import @@ -212,6 +213,18 @@ - (SentryANRTracker *)getANRTracker:(NSTimeInterval)timeout return _anrTracker; } +- (SentryNSProcessInfoWrapper *)processInfoWrapper +{ + if (_processInfoWrapper == nil) { + @synchronized(sentryDependencyContainerLock) { + if (_processInfoWrapper == nil) { + _processInfoWrapper = [[SentryNSProcessInfoWrapper alloc] init]; + } + } + } + return _processInfoWrapper; +} + #if SENTRY_HAS_METRIC_KIT - (SentryMXManager *)metricKitManager { diff --git a/Sources/Sentry/SentryEnvelope.m b/Sources/Sentry/SentryEnvelope.m index ba290b064fe..5018dc9bb47 100644 --- a/Sources/Sentry/SentryEnvelope.m +++ b/Sources/Sentry/SentryEnvelope.m @@ -63,10 +63,9 @@ - (instancetype)initWithHeader:(SentryEnvelopeItemHeader *)header data:(NSData * - (instancetype)initWithEvent:(SentryEvent *)event { - NSError *error; - NSData *json = [SentrySerialization dataWithJSONObject:[event serialize] error:&error]; + NSData *json = [SentrySerialization dataWithJSONObject:[event serialize]]; - if (nil != error) { + if (nil == json) { // We don't know what caused the serialization to fail. SentryEvent *errorEvent = [[SentryEvent alloc] initWithLevel:kSentryLevelWarning]; @@ -83,7 +82,7 @@ - (instancetype)initWithEvent:(SentryEvent *)event // We accept the risk that this simple serialization fails. Therefore we ignore the // error on purpose. - json = [SentrySerialization dataWithJSONObject:[errorEvent serialize] error:nil]; + json = [SentrySerialization dataWithJSONObject:[errorEvent serialize]]; } // event.type can be nil and the server infers error if there's a stack trace, otherwise diff --git a/Sources/Sentry/SentryExtraContextProvider.h b/Sources/Sentry/SentryExtraContextProvider.h new file mode 100644 index 00000000000..fbf1d3f1b3e --- /dev/null +++ b/Sources/Sentry/SentryExtraContextProvider.h @@ -0,0 +1,23 @@ +#import "SentryCrashWrapper.h" +#import "SentryNSProcessInfoWrapper.h" +#import "SentryUIDeviceWrapper.h" +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + * Provider of dynamic context data that we need to read at the time of an exception. + */ +@interface SentryExtraContextProvider : NSObject + ++ (instancetype)sharedInstance; + +- (instancetype)initWithCrashWrapper:(SentryCrashWrapper *)crashWrapper + deviceWrapper:(SentryUIDeviceWrapper *)deviceWrapper + processInfoWrapper:(SentryNSProcessInfoWrapper *)processInfoWrapper; + +- (NSDictionary *)getExtraContext; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Sources/Sentry/SentryExtraContextProvider.m b/Sources/Sentry/SentryExtraContextProvider.m new file mode 100644 index 00000000000..1ed102cf6dd --- /dev/null +++ b/Sources/Sentry/SentryExtraContextProvider.m @@ -0,0 +1,81 @@ +#import "SentryExtraContextProvider.h" +#import "SentryCrashIntegration.h" +#import "SentryCrashWrapper.h" +#import "SentryDependencyContainer.h" +#import "SentryNSProcessInfoWrapper.h" +#import "SentryUIDeviceWrapper.h" + +@interface +SentryExtraContextProvider () + +@property (nonatomic, strong) SentryCrashWrapper *crashWrapper; +@property (nonatomic, strong) SentryUIDeviceWrapper *deviceWrapper; +@property (nonatomic, strong) SentryNSProcessInfoWrapper *processInfoWrapper; + +@end + +@implementation SentryExtraContextProvider + ++ (instancetype)sharedInstance +{ + static SentryExtraContextProvider *instance = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ instance = [[self alloc] init]; }); + return instance; +} + +- (instancetype)init +{ + return + [self initWithCrashWrapper:[SentryCrashWrapper sharedInstance] + deviceWrapper:[[SentryUIDeviceWrapper alloc] init] + processInfoWrapper:[SentryDependencyContainer.sharedInstance processInfoWrapper]]; +} + +- (instancetype)initWithCrashWrapper:(id)crashWrapper + deviceWrapper:(id)deviceWrapper + processInfoWrapper:(id)processInfoWrapper +{ + if (self = [super init]) { + self.crashWrapper = crashWrapper; + self.deviceWrapper = deviceWrapper; + self.processInfoWrapper = processInfoWrapper; + } + return self; +} + +- (NSDictionary *)getExtraContext +{ + return @{ @"device" : [self getExtraDeviceContext], @"app" : [self getExtraAppContext] }; +} + +- (NSDictionary *)getExtraDeviceContext +{ + NSMutableDictionary *extraDeviceContext = [[NSMutableDictionary alloc] init]; + + extraDeviceContext[SentryDeviceContextFreeMemoryKey] = @(self.crashWrapper.freeMemorySize); + extraDeviceContext[@"free_storage"] = @(self.crashWrapper.freeStorageSize); + extraDeviceContext[@"processor_count"] = @([self.processInfoWrapper processorCount]); + +#if TARGET_OS_IOS + if (self.deviceWrapper.orientation != UIDeviceOrientationUnknown) { + extraDeviceContext[@"orientation"] + = UIDeviceOrientationIsPortrait(self.deviceWrapper.orientation) ? @"portrait" + : @"landscape"; + } + + if (self.deviceWrapper.isBatteryMonitoringEnabled) { + extraDeviceContext[@"charging"] + = self.deviceWrapper.batteryState == UIDeviceBatteryStateCharging ? @(YES) : @(NO); + extraDeviceContext[@"battery_level"] = @((int)(self.deviceWrapper.batteryLevel * 100)); + } +#endif + return extraDeviceContext; +} + +- (NSDictionary *)getExtraAppContext +{ + return @{ SentryDeviceContextAppMemoryKey : @(self.crashWrapper.appMemorySize) }; +} + +@end diff --git a/Sources/Sentry/SentryFileManager.m b/Sources/Sentry/SentryFileManager.m index a49104b897a..128d8b980d7 100644 --- a/Sources/Sentry/SentryFileManager.m +++ b/Sources/Sentry/SentryFileManager.m @@ -318,7 +318,7 @@ - (void)storeCrashedSession:(SentrySession *)session - (void)storeSession:(SentrySession *)session sessionFilePath:(NSString *)sessionFilePath { - NSData *sessionData = [SentrySerialization dataWithSession:session error:nil]; + NSData *sessionData = [SentrySerialization dataWithSession:session]; SENTRY_LOG_DEBUG(@"Writing session: %@", sessionFilePath); @synchronized(self.currentSessionFilePath) { if (![self writeData:sessionData toPath:sessionFilePath]) { @@ -444,7 +444,7 @@ - (BOOL)writeData:(NSData *)data toPath:(NSString *)path - (NSString *)storeDictionary:(NSDictionary *)dictionary toPath:(NSString *)path { - NSData *saveData = [SentrySerialization dataWithJSONObject:dictionary error:nil]; + NSData *saveData = [SentrySerialization dataWithJSONObject:dictionary]; return nil != saveData ? [self storeData:saveData toUniqueJSONPath:path] : path; // TODO: Should we return null instead? Whoever is using this // return value is being tricked. @@ -452,12 +452,10 @@ - (NSString *)storeDictionary:(NSDictionary *)dictionary toPath:(NSString *)path - (void)storeAppState:(SentryAppState *)appState { - NSError *error = nil; - NSData *data = [SentrySerialization dataWithJSONObject:[appState serialize] error:&error]; + NSData *data = [SentrySerialization dataWithJSONObject:[appState serialize]]; - if (error != nil) { - SENTRY_LOG_ERROR( - @"Failed to store app state, because of an error in serialization: %@", error); + if (data == nil) { + SENTRY_LOG_ERROR(@"Failed to store app state, because of an error in serialization"); return; } diff --git a/Sources/Sentry/SentryFramesTracker.m b/Sources/Sentry/SentryFramesTracker.m index c57cadc2078..60400e9284f 100644 --- a/Sources/Sentry/SentryFramesTracker.m +++ b/Sources/Sentry/SentryFramesTracker.m @@ -1,6 +1,8 @@ #import "SentryFramesTracker.h" #import "SentryCompiler.h" +#import "SentryCurrentDate.h" #import "SentryDisplayLinkWrapper.h" +#import "SentryLog.h" #import "SentryProfiler.h" #import "SentryProfilingConditionals.h" #import "SentryTime.h" @@ -40,6 +42,14 @@ @end +CFTimeInterval +slowFrameThreshold(uint64_t actualFramesPerSecond) +{ + // Most frames take just a few microseconds longer than the optimal calculated duration. + // Therefore we subtract one, because otherwise almost all frames would be slow. + return 1.0 / (actualFramesPerSecond - 1.0); +} + @implementation SentryFramesTracker { /** @@ -111,7 +121,7 @@ - (void)start - (void)displayLinkCallback { CFTimeInterval thisFrameTimestamp = self.displayLinkWrapper.timestamp; - uint64_t thisFrameSystemTimestamp = getAbsoluteTime(); + uint64_t thisFrameSystemTimestamp = SentryCurrentDate.systemTime; if (self.previousFrameTimestamp == SentryPreviousFrameInitialValue) { self.previousFrameTimestamp = thisFrameTimestamp; @@ -127,12 +137,12 @@ - (void)displayLinkCallback // need to check the frame rate for every callback. // targetTimestamp is only available on iOS 10.0 and tvOS 10.0 and above. We use a fallback of // 60 fps. - double actualFramesPerSecond = 60.0; + uint64_t currentFrameRate = 60; if (UNLIKELY((self.displayLinkWrapper.targetTimestamp == self.displayLinkWrapper.timestamp))) { - actualFramesPerSecond = 60.0; + currentFrameRate = 60; } else { - actualFramesPerSecond - = 1 / (self.displayLinkWrapper.targetTimestamp - self.displayLinkWrapper.timestamp); + currentFrameRate = (uint64_t)round( + (1 / (self.displayLinkWrapper.targetTimestamp - self.displayLinkWrapper.timestamp))); } # if SENTRY_TARGET_PROFILING_SUPPORTED @@ -142,40 +152,37 @@ - (void)displayLinkCallback BOOL shouldRecordFrameRates = [SentryProfiler isRunning]; # endif // defined(TEST) || defined(TESTCI) BOOL hasNoFrameRatesYet = self.frameRateTimestamps.count == 0; - BOOL frameRateSignificantlyChanged - = fabs(self.frameRateTimestamps.lastObject[@"frame_rate"].doubleValue - - actualFramesPerSecond) - > 1e-10f; // these may be a small fraction off of a whole number of frames per second, so - // allow some small epsilon difference + uint64_t previousFrameRate + = self.frameRateTimestamps.lastObject[@"value"].unsignedLongLongValue; + BOOL frameRateChanged = previousFrameRate != currentFrameRate; BOOL shouldRecordNewFrameRate - = shouldRecordFrameRates && (hasNoFrameRatesYet || frameRateSignificantlyChanged); + = shouldRecordFrameRates && (hasNoFrameRatesYet || frameRateChanged); if (shouldRecordNewFrameRate) { - [self.frameRateTimestamps addObject:@{ - @"timestamp" : @(thisFrameSystemTimestamp), - @"frame_rate" : @(actualFramesPerSecond), - }]; + SENTRY_LOG_DEBUG(@"Recording new frame rate at %llu.", thisFrameSystemTimestamp); + [self recordTimestamp:thisFrameSystemTimestamp + value:@(currentFrameRate) + array:self.frameRateTimestamps]; } # endif // SENTRY_TARGET_PROFILING_SUPPORTED - // Most frames take just a few microseconds longer than the optimal calculated duration. - // Therefore we subtract one, because otherwise almost all frames would be slow. - CFTimeInterval slowFrameThreshold = 1 / (actualFramesPerSecond - 1); - CFTimeInterval frameDuration = thisFrameTimestamp - self.previousFrameTimestamp; - if (frameDuration > slowFrameThreshold && frameDuration <= SentryFrozenFrameThreshold) { + if (frameDuration > slowFrameThreshold(currentFrameRate) + && frameDuration <= SentryFrozenFrameThreshold) { atomic_fetch_add_explicit(&_slowFrames, 1, SentryFramesMemoryOrder); # if SENTRY_TARGET_PROFILING_SUPPORTED - [self recordTimestampStart:@(self.previousFrameSystemTimestamp) - end:@(thisFrameSystemTimestamp) - array:self.slowFrameTimestamps]; + SENTRY_LOG_DEBUG(@"Capturing slow frame starting at %llu.", thisFrameSystemTimestamp); + [self recordTimestamp:thisFrameSystemTimestamp + value:@(thisFrameSystemTimestamp - self.previousFrameSystemTimestamp) + array:self.slowFrameTimestamps]; # endif // SENTRY_TARGET_PROFILING_SUPPORTED } else if (frameDuration > SentryFrozenFrameThreshold) { atomic_fetch_add_explicit(&_frozenFrames, 1, SentryFramesMemoryOrder); # if SENTRY_TARGET_PROFILING_SUPPORTED - [self recordTimestampStart:@(self.previousFrameSystemTimestamp) - end:@(thisFrameSystemTimestamp) - array:self.frozenFrameTimestamps]; + SENTRY_LOG_DEBUG(@"Capturing frozen frame starting at %llu.", thisFrameSystemTimestamp); + [self recordTimestamp:thisFrameSystemTimestamp + value:@(thisFrameSystemTimestamp - self.previousFrameSystemTimestamp) + array:self.frozenFrameTimestamps]; # endif // SENTRY_TARGET_PROFILING_SUPPORTED } @@ -198,14 +205,14 @@ - (void)reportNewFrame } # if SENTRY_TARGET_PROFILING_SUPPORTED -- (void)recordTimestampStart:(NSNumber *)start end:(NSNumber *)end array:(NSMutableArray *)array +- (void)recordTimestamp:(uint64_t)timestamp value:(NSNumber *)value array:(NSMutableArray *)array { BOOL shouldRecord = [SentryProfiler isRunning]; # if defined(TEST) || defined(TESTCI) shouldRecord = YES; # endif if (shouldRecord) { - [array addObject:@{ @"start_timestamp" : start, @"end_timestamp" : end }]; + [array addObject:@{ @"timestamp" : @(timestamp), @"value" : value }]; } } # endif // SENTRY_TARGET_PROFILING_SUPPORTED diff --git a/Sources/Sentry/SentryHub.m b/Sources/Sentry/SentryHub.m index 92a1f389e99..026c223db32 100644 --- a/Sources/Sentry/SentryHub.m +++ b/Sources/Sentry/SentryHub.m @@ -309,16 +309,6 @@ - (SentryId *)captureEvent:(SentryEvent *)event operation:operation]]; } -- (id)startTransactionWithName:(NSString *)name - nameSource:(SentryTransactionNameSource)source - operation:(NSString *)operation -{ - return [self - startTransactionWithContext:[[SentryTransactionContext alloc] initWithName:name - nameSource:source - operation:operation]]; -} - - (id)startTransactionWithName:(NSString *)name operation:(NSString *)operation bindToScope:(BOOL)bindToScope @@ -330,18 +320,6 @@ - (SentryId *)captureEvent:(SentryEvent *)event bindToScope:bindToScope]; } -- (id)startTransactionWithName:(NSString *)name - nameSource:(SentryTransactionNameSource)source - operation:(NSString *)operation - bindToScope:(BOOL)bindToScope -{ - return - [self startTransactionWithContext:[[SentryTransactionContext alloc] initWithName:name - nameSource:source - operation:operation] - bindToScope:bindToScope]; -} - - (id)startTransactionWithContext:(SentryTransactionContext *)transactionContext { return [self startTransactionWithContext:transactionContext customSamplingContext:@{}]; @@ -369,9 +347,8 @@ - (SentryId *)captureEvent:(SentryEvent *)event { return [self startTransactionWithContext:transactionContext bindToScope:bindToScope - waitForChildren:NO customSamplingContext:customSamplingContext - timerWrapper:nil]; + configuration:[SentryTracerConfiguration defaultConfiguration]]; } - (SentryTransactionContext *)transactionContext:(SentryTransactionContext *)context @@ -388,41 +365,10 @@ - (SentryTransactionContext *)transactionContext:(SentryTransactionContext *)con parentSampled:context.parentSampled]; } -- (id)startTransactionWithContext:(SentryTransactionContext *)transactionContext - bindToScope:(BOOL)bindToScope - waitForChildren:(BOOL)waitForChildren - customSamplingContext:(NSDictionary *)customSamplingContext - timerWrapper:(nullable SentryNSTimerWrapper *)timerWrapper -{ - SentrySamplingContext *samplingContext = - [[SentrySamplingContext alloc] initWithTransactionContext:transactionContext - customSamplingContext:customSamplingContext]; - - SentryTracesSamplerDecision *samplerDecision = [_tracesSampler sample:samplingContext]; - transactionContext = [self transactionContext:transactionContext - withSampled:samplerDecision.decision]; - transactionContext.sampleRate = samplerDecision.sampleRate; - - SentryProfilesSamplerDecision *profilesSamplerDecision = - [_profilesSampler sample:samplingContext tracesSamplerDecision:samplerDecision]; - - id tracer = [[SentryTracer alloc] initWithTransactionContext:transactionContext - hub:self - profilesSamplerDecision:profilesSamplerDecision - waitForChildren:waitForChildren - timerWrapper:timerWrapper]; - - if (bindToScope) - self.scope.span = tracer; - - return tracer; -} - - (SentryTracer *)startTransactionWithContext:(SentryTransactionContext *)transactionContext bindToScope:(BOOL)bindToScope customSamplingContext:(NSDictionary *)customSamplingContext - idleTimeout:(NSTimeInterval)idleTimeout - dispatchQueueWrapper:(SentryDispatchQueueWrapper *)dispatchQueueWrapper + configuration:(SentryTracerConfiguration *)configuration { SentrySamplingContext *samplingContext = [[SentrySamplingContext alloc] initWithTransactionContext:transactionContext @@ -436,11 +382,12 @@ - (SentryTracer *)startTransactionWithContext:(SentryTransactionContext *)transa SentryProfilesSamplerDecision *profilesSamplerDecision = [_profilesSampler sample:samplingContext tracesSamplerDecision:samplerDecision]; + configuration.profilesSamplerDecision = profilesSamplerDecision; + SentryTracer *tracer = [[SentryTracer alloc] initWithTransactionContext:transactionContext hub:self - profilesSamplerDecision:profilesSamplerDecision - idleTimeout:idleTimeout - dispatchQueueWrapper:dispatchQueueWrapper]; + configuration:configuration]; + if (bindToScope) self.scope.span = tracer; @@ -693,6 +640,7 @@ - (void)flush:(NSTimeInterval)timeout - (void)close { [_client close]; + SENTRY_LOG_DEBUG(@"Closed the Hub."); } @end diff --git a/Sources/Sentry/SentryMeta.m b/Sources/Sentry/SentryMeta.m index 027ff89943f..d5ad6f3231f 100644 --- a/Sources/Sentry/SentryMeta.m +++ b/Sources/Sentry/SentryMeta.m @@ -5,7 +5,7 @@ @implementation SentryMeta // Don't remove the static keyword. If you do the compiler adds the constant name to the global // symbol table and it might clash with other constants. When keeping the static keyword the // compiler replaces all occurrences with the value. -static NSString *versionString = @"8.4.0"; +static NSString *versionString = @"8.5.0"; static NSString *sdkName = @"sentry.cocoa"; + (NSString *)versionString diff --git a/Sources/Sentry/SentryMetricProfiler.mm b/Sources/Sentry/SentryMetricProfiler.mm index 294379105ad..678479f94db 100644 --- a/Sources/Sentry/SentryMetricProfiler.mm +++ b/Sources/Sentry/SentryMetricProfiler.mm @@ -2,6 +2,7 @@ #if SENTRY_TARGET_PROFILING_SUPPORTED +# import "SentryCurrentDate.h" # import "SentryEvent+Private.h" # import "SentryLog.h" # import "SentryNSProcessInfoWrapper.h" @@ -47,7 +48,7 @@ @implementation SentryMetricReading [absoluteTimestampValues enumerateObjectsUsingBlock:^( SentryMetricReading *_Nonnull reading, NSUInteger idx, BOOL *_Nonnull stop) { // if the metric reading wasn't recorded until the transaction ended, don't include it - if (orderedChronologically(transaction.endSystemTime, reading.absoluteTimestamp)) { + if (!orderedChronologically(reading.absoluteTimestamp, transaction.endSystemTime)) { return; } @@ -200,7 +201,7 @@ - (SentryMetricReading *)metricReadingForValue:(NSNumber *)value { const auto reading = [[SentryMetricReading alloc] init]; reading.value = value; - reading.absoluteTimestamp = getAbsoluteTime(); + reading.absoluteTimestamp = SentryCurrentDate.systemTime; return reading; } diff --git a/Sources/Sentry/SentryNSDataSwizzling.m b/Sources/Sentry/SentryNSDataSwizzling.m index a54d6459364..c61c4fd7993 100644 --- a/Sources/Sentry/SentryNSDataSwizzling.m +++ b/Sources/Sentry/SentryNSDataSwizzling.m @@ -2,6 +2,7 @@ #import "SentryCrashDefaultMachineContextWrapper.h" #import "SentryCrashMachineContextWrapper.h" #import "SentryCrashStackEntryMapper.h" +#import "SentryDependencyContainer.h" #import "SentryInAppLogic.h" #import "SentryNSDataTracker.h" #import "SentryNSProcessInfoWrapper.h" @@ -32,8 +33,8 @@ + (SentryNSDataSwizzling *)shared - (void)startWithOptions:(SentryOptions *)options { self.dataTracker = [[SentryNSDataTracker alloc] - initWithThreadInspector:[self buildThreadInspectorForOptions:options] - processInfoWrapper:[[SentryNSProcessInfoWrapper alloc] init]]; + initWithThreadInspector:[[SentryThreadInspector alloc] initWithOptions:options] + processInfoWrapper:[SentryDependencyContainer.sharedInstance processInfoWrapper]]; [self.dataTracker enable]; [SentryNSDataSwizzling swizzleNSData]; } @@ -43,21 +44,6 @@ - (void)stop [self.dataTracker disable]; } -- (SentryThreadInspector *)buildThreadInspectorForOptions:(SentryOptions *)options -{ - SentryInAppLogic *inAppLogic = - [[SentryInAppLogic alloc] initWithInAppIncludes:options.inAppIncludes - inAppExcludes:options.inAppExcludes]; - SentryCrashStackEntryMapper *crashStackEntryMapper = - [[SentryCrashStackEntryMapper alloc] initWithInAppLogic:inAppLogic]; - SentryStacktraceBuilder *stacktraceBuilder = - [[SentryStacktraceBuilder alloc] initWithCrashStackEntryMapper:crashStackEntryMapper]; - id machineContextWrapper = - [[SentryCrashDefaultMachineContextWrapper alloc] init]; - return [[SentryThreadInspector alloc] initWithStacktraceBuilder:stacktraceBuilder - andMachineContextWrapper:machineContextWrapper]; -} - // SentrySwizzleInstanceMethod declaration shadows a local variable. The swizzling is working // fine and we accept this warning. #pragma clang diagnostic push diff --git a/Sources/Sentry/SentryNSDataTracker.m b/Sources/Sentry/SentryNSDataTracker.m index 100179076ba..0880e159e5f 100644 --- a/Sources/Sentry/SentryNSDataTracker.m +++ b/Sources/Sentry/SentryNSDataTracker.m @@ -5,6 +5,7 @@ #import "SentryFileManager.h" #import "SentryFrame.h" #import "SentryHub+Private.h" +#import "SentryInternalDefines.h" #import "SentryLog.h" #import "SentryNSProcessInfoWrapper.h" #import "SentryOptions.h" @@ -187,7 +188,7 @@ - (void)mainThreadExtraInfo:(id)span { BOOL isMainThread = [NSThread isMainThread]; - [span setDataValue:@(isMainThread) forKey:@"blocked_main_thread"]; + [span setDataValue:@(isMainThread) forKey:BLOCKED_MAIN_THREAD]; if (!isMainThread) { return; @@ -207,7 +208,7 @@ - (void)mainThreadExtraInfo:(id)span // and only the 'main' frame remains in the stack // therefore, there is nothing to do about it // and we should not report it as an issue. - [span setDataValue:@(NO) forKey:@"blocked_main_thread"]; + [span setDataValue:@(NO) forKey:BLOCKED_MAIN_THREAD]; } else { [((SentrySpan *)span) setFrames:frames]; } diff --git a/Sources/Sentry/SentryNSProcessInfoWrapper.mm b/Sources/Sentry/SentryNSProcessInfoWrapper.mm index 5d672f6c7e3..d92bc3bace4 100644 --- a/Sources/Sentry/SentryNSProcessInfoWrapper.mm +++ b/Sources/Sentry/SentryNSProcessInfoWrapper.mm @@ -2,6 +2,14 @@ @implementation SentryNSProcessInfoWrapper ++ (SentryNSProcessInfoWrapper *)shared +{ + static SentryNSProcessInfoWrapper *instance = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ instance = [[self alloc] init]; }); + return instance; +} + - (NSString *)processDirectoryPath { return NSBundle.mainBundle.bundlePath; diff --git a/Sources/Sentry/SentryNSTimerWrapper.m b/Sources/Sentry/SentryNSTimerWrapper.m index 5e6af70ad1b..6a9ffef47c5 100644 --- a/Sources/Sentry/SentryNSTimerWrapper.m +++ b/Sources/Sentry/SentryNSTimerWrapper.m @@ -9,4 +9,17 @@ - (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)interval return [NSTimer scheduledTimerWithTimeInterval:interval repeats:repeats block:block]; } +- (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti + target:(id)aTarget + selector:(SEL)aSelector + userInfo:(nullable id)userInfo + repeats:(BOOL)yesOrNo +{ + return [NSTimer scheduledTimerWithTimeInterval:ti + target:aTarget + selector:aSelector + userInfo:userInfo + repeats:yesOrNo]; +} + @end diff --git a/Sources/Sentry/SentryNSURLRequest.m b/Sources/Sentry/SentryNSURLRequest.m index 7cb40d722a1..a02c06d3ff8 100644 --- a/Sources/Sentry/SentryNSURLRequest.m +++ b/Sources/Sentry/SentryNSURLRequest.m @@ -29,14 +29,9 @@ - (_Nullable instancetype)initStoreRequestWithDsn:(SentryDsn *)dsn didFailWithError:(NSError *_Nullable *_Nullable)error { NSDictionary *serialized = [event serialize]; - NSData *jsonData = [SentrySerialization dataWithJSONObject:serialized error:error]; + NSData *jsonData = [SentrySerialization dataWithJSONObject:serialized]; if (nil == jsonData) { - if (error) { - // TODO: We're possibly overriding an error set by the actual - // code that failed ^ - *error = NSErrorFromSentryError( - kSentryErrorJsonConversionError, @"Event cannot be converted to JSON"); - } + SENTRY_LOG_ERROR(@"Event cannot be converted to JSON"); return nil; } diff --git a/Sources/Sentry/SentryNetworkTracker.m b/Sources/Sentry/SentryNetworkTracker.m index d0b0967c300..2df26e0683b 100644 --- a/Sources/Sentry/SentryNetworkTracker.m +++ b/Sources/Sentry/SentryNetworkTracker.m @@ -22,6 +22,15 @@ #import "SentryTracer.h" #import +/** + * WARNING: We had issues in the past with this code on older iOS versions. We don't run unit tests + * on all the iOS versions our SDK supports. When adding this comment on April 12th, 2023, we + * decided to remove running unit tests on iOS 12 simulators. Check the develop-docs decision log + * for more information https://github.com/getsentry/sentry-cocoa/blob/main/develop-docs/README.md. + * Back then, the code worked correctly on all iOS versions. Please evaluate if your changes could + * break on specific iOS versions to ensure it works properly when modifying this file. If they + * could, please add UI tests and run them on older iOS versions. + */ @interface SentryNetworkTracker () diff --git a/Sources/Sentry/SentryPerformanceTracker.m b/Sources/Sentry/SentryPerformanceTracker.m index 9751eb47aa8..9c049b6f751 100644 --- a/Sources/Sentry/SentryPerformanceTracker.m +++ b/Sources/Sentry/SentryPerformanceTracker.m @@ -39,13 +39,6 @@ - (instancetype)init return self; } -- (SentrySpanId *)startSpanWithName:(NSString *)name operation:(NSString *)operation -{ - return [self startSpanWithName:name - nameSource:kSentryTransactionNameSourceCustom - operation:operation]; -} - - (SentrySpanId *)startSpanWithName:(NSString *)name nameSource:(SentryTransactionNameSource)source operation:(NSString *)operation @@ -79,11 +72,15 @@ - (SentrySpanId *)startSpanWithName:(NSString *)name } SENTRY_LOG_DEBUG(@"Creating new transaction bound to scope: %d", bindToScope); - newSpan = [SentrySDK.currentHub startTransactionWithContext:context - bindToScope:bindToScope - waitForChildren:YES - customSamplingContext:@{} - timerWrapper:nil]; + + newSpan = [SentrySDK.currentHub + startTransactionWithContext:context + bindToScope:bindToScope + customSamplingContext:@{} + configuration:[SentryTracerConfiguration configurationWithBlock:^( + SentryTracerConfiguration *configuration) { + configuration.waitForChildren = YES; + }]]; [(SentryTracer *)newSpan setDelegate:self]; }]; @@ -103,16 +100,6 @@ - (SentrySpanId *)startSpanWithName:(NSString *)name return spanId; } -- (void)measureSpanWithDescription:(NSString *)description - operation:(NSString *)operation - inBlock:(void (^)(void))block -{ - [self measureSpanWithDescription:description - nameSource:kSentryTransactionNameSourceCustom - operation:operation - inBlock:block]; -} - - (void)measureSpanWithDescription:(NSString *)description nameSource:(SentryTransactionNameSource)source operation:(NSString *)operation @@ -129,18 +116,6 @@ - (void)measureSpanWithDescription:(NSString *)description [self finishSpan:spanId]; } -- (void)measureSpanWithDescription:(NSString *)description - operation:(NSString *)operation - parentSpanId:(SentrySpanId *)parentSpanId - inBlock:(void (^)(void))block -{ - [self measureSpanWithDescription:description - nameSource:kSentryTransactionNameSourceCustom - operation:operation - parentSpanId:parentSpanId - inBlock:block]; -} - - (void)measureSpanWithDescription:(NSString *)description nameSource:(SentryTransactionNameSource)source operation:(NSString *)operation diff --git a/Sources/Sentry/SentryPerformanceTrackingIntegration.m b/Sources/Sentry/SentryPerformanceTrackingIntegration.m index 632fdb4a527..d525f2a60cb 100644 --- a/Sources/Sentry/SentryPerformanceTrackingIntegration.m +++ b/Sources/Sentry/SentryPerformanceTrackingIntegration.m @@ -1,5 +1,6 @@ #import "SentryPerformanceTrackingIntegration.h" #import "SentryDefaultObjCRuntimeWrapper.h" +#import "SentryDependencyContainer.h" #import "SentryDispatchQueueWrapper.h" #import "SentryLog.h" #import "SentryNSProcessInfoWrapper.h" @@ -40,7 +41,7 @@ - (BOOL)installWithOptions:(SentryOptions *)options dispatchQueue:dispatchQueue objcRuntimeWrapper:[SentryDefaultObjCRuntimeWrapper sharedInstance] subClassFinder:subClassFinder - processInfoWrapper:[[SentryNSProcessInfoWrapper alloc] init]]; + processInfoWrapper:[SentryDependencyContainer.sharedInstance processInfoWrapper]]; [self.swizzling start]; SentryUIViewControllerPerformanceTracker.shared.enableWaitForFullDisplay diff --git a/Sources/Sentry/SentryProfiler.mm b/Sources/Sentry/SentryProfiler.mm index aefc844d118..34e465b7130 100644 --- a/Sources/Sentry/SentryProfiler.mm +++ b/Sources/Sentry/SentryProfiler.mm @@ -162,7 +162,8 @@ SentryProfiler *_Nullable _gCurrentProfiler; SentryNSProcessInfoWrapper *_gCurrentProcessInfoWrapper; SentrySystemWrapper *_gCurrentSystemWrapper; -SentryNSTimerWrapper *_gCurrentTimerWrapper; +SentryNSTimerWrapper *_gMetricTimerWrapper; +SentryNSTimerWrapper *_gTimeoutTimerWrapper; # if SENTRY_HAS_UIKIT SentryFramesTracker *_gCurrentFramesTracker; # endif // SENTRY_HAS_UIKIT @@ -193,73 +194,32 @@ * didn't occur within the profile time. */ NSArray * -processFrameRenders(SentryFrameInfoTimeSeries *frameInfo, SentryTransaction *transaction) +sliceGPUData(SentryFrameInfoTimeSeries *frameInfo, SentryTransaction *transaction) { - auto relativeFrameInfo = [NSMutableArray array]; + auto slicedGPUEntries = [NSMutableArray array]; [frameInfo enumerateObjectsUsingBlock:^( - NSDictionary *_Nonnull obj, NSUInteger idx, BOOL *_Nonnull stop) { - const auto frameRenderStart = obj[@"start_timestamp"].unsignedLongLongValue; - - if (!orderedChronologically(transaction.startSystemTime, frameRenderStart)) { - SENTRY_LOG_DEBUG(@"GPU frame render started before profile start, will not report it."); - return; - } - const auto frameRenderEnd = obj[@"end_timestamp"].unsignedLongLongValue; - if (orderedChronologically(transaction.endSystemTime, frameRenderEnd)) { - SENTRY_LOG_DEBUG(@"Frame render finished after transaction finished, won't record."); - return; - } - const auto relativeFrameRenderStart - = getDurationNs(transaction.startSystemTime, frameRenderStart); - const auto relativeFrameRenderEnd - = getDurationNs(transaction.startSystemTime, frameRenderEnd); - - // this probably won't happen, but doesn't hurt to have one last defensive check before - // calling getDurationNs - if (!orderedChronologically(relativeFrameRenderStart, relativeFrameRenderEnd)) { - SENTRY_LOG_WARN( - @"Computed relative start and end timestamps are not chronologically ordered."); - return; - } - const auto frameRenderDurationNs - = getDurationNs(relativeFrameRenderStart, relativeFrameRenderEnd); - - [relativeFrameInfo addObject:@{ - @"elapsed_since_start_ns" : serializedUnsigned64BitInteger(relativeFrameRenderStart), - @"value" : @(frameRenderDurationNs), - }]; - }]; - return relativeFrameInfo; -} - -/** - * Convert the data structure that records timestamps for GPU frame rate info from - * SentryFramesTracker to the structure expected for profiling metrics. - */ -NSArray * -processFrameRates(SentryFrameInfoTimeSeries *frameRates, SentryTransaction *transaction) -{ - auto relativeFrameRates = [NSMutableArray array]; - [frameRates enumerateObjectsUsingBlock:^( NSDictionary *_Nonnull obj, NSUInteger idx, BOOL *_Nonnull stop) { const auto timestamp = obj[@"timestamp"].unsignedLongLongValue; - const auto refreshRate = obj[@"frame_rate"]; if (!orderedChronologically(transaction.startSystemTime, timestamp)) { + SENTRY_LOG_DEBUG(@"GPU info recorded (%llu) before transaction start (%llu), " + @"will not report it.", + timestamp, transaction.startSystemTime); return; } - if (orderedChronologically(transaction.endSystemTime, timestamp)) { + + if (!orderedChronologically(timestamp, transaction.endSystemTime)) { + SENTRY_LOG_DEBUG(@"GPU info recorded after transaction finished, won't record."); return; } - const auto relativeTimestamp = getDurationNs(transaction.startSystemTime, timestamp); - [relativeFrameRates addObject:@ { + [slicedGPUEntries addObject:@ { @"elapsed_since_start_ns" : serializedUnsigned64BitInteger(relativeTimestamp), - @"value" : refreshRate, + @"value" : obj[@"value"], }]; }]; - return relativeFrameRates; + return slicedGPUEntries; } # endif // SENTRY_HAS_UIKIT @@ -341,12 +301,15 @@ + (void)startWithHub:(SentryHub *)hub [_gCurrentProfiler start]; + if (_gTimeoutTimerWrapper == nil) { + _gTimeoutTimerWrapper = [[SentryNSTimerWrapper alloc] init]; + } _gCurrentProfiler->_timeoutTimer = - [NSTimer scheduledTimerWithTimeInterval:kSentryProfilerTimeoutInterval - target:self - selector:@selector(timeoutAbort) - userInfo:nil - repeats:NO]; + [_gTimeoutTimerWrapper scheduledTimerWithTimeInterval:kSentryProfilerTimeoutInterval + target:self + selector:@selector(timeoutAbort) + userInfo:nil + repeats:NO]; # if SENTRY_HAS_UIKIT [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(backgroundAbort) @@ -425,21 +388,21 @@ + (SentryEnvelopeItem *)createProfilingEnvelopeItemForTransaction:(SentryTransac const auto metrics = [_gCurrentProfiler->_metricProfiler serializeForTransaction:transaction]; # if SENTRY_HAS_UIKIT - const auto slowFrames = processFrameRenders( - _gCurrentFramesTracker.currentFrames.slowFrameTimestamps, transaction); + const auto slowFrames + = sliceGPUData(_gCurrentFramesTracker.currentFrames.slowFrameTimestamps, transaction); if (slowFrames.count > 0) { metrics[@"slow_frame_renders"] = @{ @"unit" : @"nanosecond", @"values" : slowFrames }; } - const auto frozenFrames = processFrameRenders( - _gCurrentFramesTracker.currentFrames.frozenFrameTimestamps, transaction); + const auto frozenFrames + = sliceGPUData(_gCurrentFramesTracker.currentFrames.frozenFrameTimestamps, transaction); if (frozenFrames.count > 0) { metrics[@"frozen_frame_renders"] = @{ @"unit" : @"nanosecond", @"values" : frozenFrames }; } if (slowFrames.count > 0 || frozenFrames.count > 0) { - const auto frameRates = processFrameRates( - _gCurrentFramesTracker.currentFrames.frameRateTimestamps, transaction); + const auto frameRates + = sliceGPUData(_gCurrentFramesTracker.currentFrames.frameRateTimestamps, transaction); if (frameRates.count > 0) { metrics[@"screen_frame_rates"] = @{ @"unit" : @"hz", @"values" : frameRates }; } @@ -477,10 +440,16 @@ + (void)useProcessInfoWrapper:(SentryNSProcessInfoWrapper *)processInfoWrapper _gCurrentProcessInfoWrapper = processInfoWrapper; } -+ (void)useTimerWrapper:(SentryNSTimerWrapper *)timerWrapper ++ (void)useMetricTimerWrapper:(SentryNSTimerWrapper *)timerWrapper +{ + std::lock_guard l(_gProfilerLock); + _gMetricTimerWrapper = timerWrapper; +} + ++ (void)useTimeoutTimerWrapper:(SentryNSTimerWrapper *)timerWrapper { std::lock_guard l(_gProfilerLock); - _gCurrentTimerWrapper = timerWrapper; + _gTimeoutTimerWrapper = timerWrapper; } # if SENTRY_HAS_UIKIT @@ -543,10 +512,10 @@ - (void)startMetricProfiler _gCurrentSystemWrapper = [[SentrySystemWrapper alloc] init]; } if (_gCurrentProcessInfoWrapper == nil) { - _gCurrentProcessInfoWrapper = [[SentryNSProcessInfoWrapper alloc] init]; + _gCurrentProcessInfoWrapper = [SentryDependencyContainer.sharedInstance processInfoWrapper]; } - if (_gCurrentTimerWrapper == nil) { - _gCurrentTimerWrapper = [[SentryNSTimerWrapper alloc] init]; + if (_gMetricTimerWrapper == nil) { + _gMetricTimerWrapper = [[SentryNSTimerWrapper alloc] init]; } # if SENTRY_HAS_UIKIT if (_gCurrentFramesTracker == nil) { @@ -556,7 +525,7 @@ - (void)startMetricProfiler _metricProfiler = [[SentryMetricProfiler alloc] initWithProcessInfoWrapper:_gCurrentProcessInfoWrapper systemWrapper:_gCurrentSystemWrapper - timerWrapper:_gCurrentTimerWrapper]; + timerWrapper:_gMetricTimerWrapper]; [_metricProfiler start]; } @@ -655,8 +624,20 @@ - (void)start SENTRY_LOG_WARN(@"Profiler instance no longer exists, cannot process next sample."); return; } + + // in test, we'll overwrite the sample's timestamp to one mocked by SentryCurrentDate etal. + // Doing this in a unified way between tests and production required extensive changes to + // the C++ layer, so we opted for this solution to avoid any potential breakages or + // performance hits there. +# if defined(TEST) || defined(TESTCI) + Backtrace backtraceCopy = backtrace; + backtraceCopy.absoluteTimestamp = SentryCurrentDate.systemTime; + processBacktrace(backtraceCopy, threadMetadata, queueMetadata, samples, stacks, frames, + frameIndexLookup, stackIndexLookup); +# else processBacktrace(backtrace, threadMetadata, queueMetadata, samples, stacks, frames, frameIndexLookup, stackIndexLookup); +# endif // defined(TEST) || defined(TESTCI) }, kSentryProfilerFrequencyHz); _profiler->startSampling(); @@ -742,10 +723,9 @@ + (NSDictionary *)serializeInfoForTransaction:(SentryTransaction *)transaction + (SentryEnvelopeItem *)envelopeItemForProfileData:(NSMutableDictionary *)profile profileID:(SentryId *)profileID { - NSError *error = nil; - const auto JSONData = [SentrySerialization dataWithJSONObject:profile error:&error]; + const auto JSONData = [SentrySerialization dataWithJSONObject:profile]; if (JSONData == nil) { - SENTRY_LOG_DEBUG(@"Failed to encode profile to JSON: %@", error); + SENTRY_LOG_DEBUG(@"Failed to encode profile to JSON."); return nil; } diff --git a/Sources/Sentry/SentrySDK.m b/Sources/Sentry/SentrySDK.m index 474c7d42003..2be5d6fa68e 100644 --- a/Sources/Sentry/SentrySDK.m +++ b/Sources/Sentry/SentrySDK.m @@ -182,37 +182,14 @@ + (SentryId *)captureEvent:(SentryEvent *)event withScope:(SentryScope *)scope + (id)startTransactionWithName:(NSString *)name operation:(NSString *)operation { - return [self startTransactionWithName:name - nameSource:kSentryTransactionNameSourceCustom - operation:operation]; + return [SentrySDK.currentHub startTransactionWithName:name operation:operation]; } + (id)startTransactionWithName:(NSString *)name - nameSource:(SentryTransactionNameSource)source - operation:(NSString *)operation -{ - return [SentrySDK.currentHub startTransactionWithName:name - nameSource:source - operation:operation]; -} - -+ (id)startTransactionWithName:(NSString *)name - operation:(NSString *)operation - bindToScope:(BOOL)bindToScope -{ - return [self startTransactionWithName:name - nameSource:kSentryTransactionNameSourceCustom - operation:operation - bindToScope:bindToScope]; -} - -+ (id)startTransactionWithName:(NSString *)name - nameSource:(SentryTransactionNameSource)source operation:(NSString *)operation bindToScope:(BOOL)bindToScope { return [SentrySDK.currentHub startTransactionWithName:name - nameSource:source operation:operation bindToScope:bindToScope]; } @@ -398,16 +375,18 @@ + (void)flush:(NSTimeInterval)timeout */ + (void)close { + SENTRY_LOG_DEBUG(@"Starting to close SDK."); // pop the hub and unset SentryHub *hub = SentrySDK.currentHub; - // uninstall all the integrations + // Uninstall all the integrations for (NSObject *integration in hub.installedIntegrations) { if ([integration respondsToSelector:@selector(uninstall)]) { [integration uninstall]; } } [hub removeAllIntegrations]; + SENTRY_LOG_DEBUG(@"Uninstalled all integrations."); #if SENTRY_HAS_UIKIT // force the AppStateManager to unsubscribe, see diff --git a/Sources/Sentry/SentryScreenshot.m b/Sources/Sentry/SentryScreenshot.m index d4b1cb21f0e..91bb2e90e2b 100644 --- a/Sources/Sentry/SentryScreenshot.m +++ b/Sources/Sentry/SentryScreenshot.m @@ -1,4 +1,5 @@ #import "SentryScreenshot.h" +#import "SentryCompiler.h" #import "SentryDependencyContainer.h" #import "SentryDispatchQueueWrapper.h" #import "SentryUIApplication.h" @@ -43,11 +44,20 @@ - (void)saveScreenShots:(NSString *)imagesDirectoryPath NSMutableArray *result = [NSMutableArray arrayWithCapacity:windows.count]; for (UIWindow *window in windows) { - UIGraphicsBeginImageContext(window.frame.size); + CGSize size = window.frame.size; + if (size.width == 0 || size.height == 0) { + // avoid API errors reported as e.g.: + // [Graphics] Invalid size provided to UIGraphicsBeginImageContext(): size={0, 0}, + // scale=1.000000 + continue; + } + UIGraphicsBeginImageContext(size); if ([window drawViewHierarchyInRect:window.bounds afterScreenUpdates:false]) { UIImage *img = UIGraphicsGetImageFromCurrentImageContext(); - if (img.size.width > 0 || img.size.height > 0) { + // this shouldn't happen now that we discard windows with either 0 height or 0 width, + // but still, we shouldn't send any images with either one. + if (LIKELY(img.size.width > 0 && img.size.height > 0)) { NSData *bytes = UIImagePNGRepresentation(img); if (bytes && bytes.length > 0) { [result addObject:bytes]; diff --git a/Sources/Sentry/SentrySerialization.m b/Sources/Sentry/SentrySerialization.m index 2a423b53c15..850c63a69a7 100644 --- a/Sources/Sentry/SentrySerialization.m +++ b/Sources/Sentry/SentrySerialization.m @@ -16,35 +16,17 @@ @implementation SentrySerialization + (NSData *_Nullable)dataWithJSONObject:(NSDictionary *)dictionary - error:(NSError *_Nullable *_Nullable)error { -// We'll do this whether we're handling an exception or library error -#define SENTRY_HANDLE_ERROR(__sentry_error) \ - SENTRY_LOG_ERROR(@"Invalid JSON: %@", __sentry_error); \ - *error = __sentry_error; \ - return nil; - - NSData *data = nil; - -#if defined(DEBUG) || defined(TEST) || defined(TESTCI) - @try { -#else - if ([NSJSONSerialization isValidJSONObject:dictionary]) { -#endif - data = [NSJSONSerialization dataWithJSONObject:dictionary options:0 error:error]; -#if defined(DEBUG) || defined(TEST) || defined(TESTCI) - } @catch (NSException *exception) { - if (error) { - SENTRY_HANDLE_ERROR(NSErrorFromSentryErrorWithException( - kSentryErrorJsonConversionError, @"Event cannot be converted to JSON", exception)); - } + if (![NSJSONSerialization isValidJSONObject:dictionary]) { + SENTRY_LOG_ERROR(@"Dictionary is not a valid JSON object."); + return nil; } -#else - } else if (error) { - SENTRY_HANDLE_ERROR(NSErrorFromSentryErrorWithUnderlyingError( - kSentryErrorJsonConversionError, @"Event cannot be converted to JSON", *error)); + + NSError *error = nil; + NSData *data = [NSJSONSerialization dataWithJSONObject:dictionary options:0 error:&error]; + if (error) { + SENTRY_LOG_ERROR(@"Internal error while serializing JSON: %@", error); } -#endif return data; } @@ -69,13 +51,9 @@ + (NSData *_Nullable)dataWithEnvelope:(SentryEnvelope *)envelope [serializedData setValue:[traceContext serialize] forKey:@"trace"]; } - NSData *header = [SentrySerialization dataWithJSONObject:serializedData error:error]; + NSData *header = [SentrySerialization dataWithJSONObject:serializedData]; if (nil == header) { SENTRY_LOG_ERROR(@"Envelope header cannot be converted to JSON."); - if (error) { - *error = NSErrorFromSentryError( - kSentryErrorJsonConversionError, @"Envelope header cannot be converted to JSON"); - } return nil; } [envelopeData appendData:header]; @@ -84,14 +62,9 @@ + (NSData *_Nullable)dataWithEnvelope:(SentryEnvelope *)envelope [envelopeData appendData:[@"\n" dataUsingEncoding:NSUTF8StringEncoding]]; NSDictionary *serializedItemHeaderData = [envelope.items[i].header serialize]; - NSData *itemHeader = [SentrySerialization dataWithJSONObject:serializedItemHeaderData - error:error]; + NSData *itemHeader = [SentrySerialization dataWithJSONObject:serializedItemHeaderData]; if (nil == itemHeader) { SENTRY_LOG_ERROR(@"Envelope item header cannot be converted to JSON."); - if (error) { - *error = NSErrorFromSentryError(kSentryErrorJsonConversionError, - @"Envelope item header cannot be converted to JSON"); - } return nil; } [envelopeData appendData:itemHeader]; @@ -305,9 +278,8 @@ + (SentryEnvelope *_Nullable)envelopeWithData:(NSData *)data } + (NSData *_Nullable)dataWithSession:(SentrySession *)session - error:(NSError *_Nullable *_Nullable)error { - return [self dataWithJSONObject:[session serialize] error:error]; + return [self dataWithJSONObject:[session serialize]]; } + (SentrySession *_Nullable)sessionWithData:(NSData *)sessionData diff --git a/Sources/Sentry/SentrySpan.m b/Sources/Sentry/SentrySpan.m index 4b6f44e76aa..4e3331261bf 100644 --- a/Sources/Sentry/SentrySpan.m +++ b/Sources/Sentry/SentrySpan.m @@ -146,7 +146,7 @@ - (void)finishWithStatus:(SentrySpanStatus)status if (self.timestamp == nil) { self.timestamp = [SentryCurrentDate date]; SENTRY_LOG_DEBUG(@"Setting span timestamp: %@ at system time %llu", self.timestamp, - (unsigned long long)getAbsoluteTime()); + (unsigned long long)SentryCurrentDate.systemTime); } if (self.tracer == nil) { SENTRY_LOG_DEBUG( diff --git a/Sources/Sentry/SentrySpanContext.m b/Sources/Sentry/SentrySpanContext.m index 42bb43773b0..dbed024ac2d 100644 --- a/Sources/Sentry/SentrySpanContext.m +++ b/Sources/Sentry/SentrySpanContext.m @@ -9,7 +9,7 @@ @implementation SentrySpanContext - (instancetype)initWithOperation:(NSString *)operation { - return [self initWithOperation:operation sampled:NO]; + return [self initWithOperation:operation sampled:kSentrySampleDecisionUndecided]; } - (instancetype)initWithOperation:(NSString *)operation sampled:(SentrySampleDecision)sampled diff --git a/Sources/Sentry/SentryStacktraceBuilder.m b/Sources/Sentry/SentryStacktraceBuilder.m index 97d40419a52..bac08039954 100644 --- a/Sources/Sentry/SentryStacktraceBuilder.m +++ b/Sources/Sentry/SentryStacktraceBuilder.m @@ -41,7 +41,8 @@ - (SentryStacktrace *)retrieveStacktraceFromCursor:(SentryCrashStackCursor)stack continue; } if (stackCursor.symbolicate(&stackCursor)) { - [frames addObject:[self.crashStackEntryMapper mapStackEntryWithCursor:stackCursor]]; + frame = [self.crashStackEntryMapper mapStackEntryWithCursor:stackCursor]; + [frames addObject:frame]; } } sentrycrash_async_backtrace_decref(stackCursor.async_caller); diff --git a/Sources/Sentry/SentrySystemEventBreadcrumbs.m b/Sources/Sentry/SentrySystemEventBreadcrumbs.m index e2fac3d4f12..656c1ec881b 100644 --- a/Sources/Sentry/SentrySystemEventBreadcrumbs.m +++ b/Sources/Sentry/SentrySystemEventBreadcrumbs.m @@ -1,5 +1,6 @@ #import "SentrySystemEventBreadcrumbs.h" #import "SentryBreadcrumb.h" +#import "SentryBreadcrumbDelegate.h" #import "SentryCurrentDateProvider.h" #import "SentryDependencyContainer.h" #import "SentryLog.h" @@ -12,7 +13,7 @@ @interface SentrySystemEventBreadcrumbs () -@property (nonatomic, weak) id delegate; +@property (nonatomic, weak) id delegate; @property (nonatomic, strong) SentryFileManager *fileManager; @property (nonatomic, strong) id currentDateProvider; @property (nonatomic, strong) SentryNSNotificationCenterWrapper *notificationCenterWrapper; @@ -32,7 +33,7 @@ - (instancetype)initWithFileManager:(SentryFileManager *)fileManager return self; } -- (void)startWithDelegate:(id)delegate +- (void)startWithDelegate:(id)delegate { #if TARGET_OS_IOS UIDevice *currentDevice = [UIDevice currentDevice]; @@ -73,7 +74,7 @@ - (void)dealloc /** * Only used for testing, call startWithDelegate instead. */ -- (void)startWithDelegate:(id)delegate +- (void)startWithDelegate:(id)delegate currentDevice:(nullable UIDevice *)currentDevice { _delegate = delegate; diff --git a/Sources/Sentry/SentryThreadInspector.m b/Sources/Sentry/SentryThreadInspector.m index e6829a24673..8e1b66dc235 100644 --- a/Sources/Sentry/SentryThreadInspector.m +++ b/Sources/Sentry/SentryThreadInspector.m @@ -1,8 +1,12 @@ #import "SentryThreadInspector.h" +#import "SentryCrashDefaultMachineContextWrapper.h" #import "SentryCrashStackCursor.h" #include "SentryCrashStackCursor_MachineContext.h" +#import "SentryCrashStackEntryMapper.h" #include "SentryCrashSymbolicator.h" #import "SentryFrame.h" +#import "SentryInAppLogic.h" +#import "SentryOptions.h" #import "SentryStacktrace.h" #import "SentryStacktraceBuilder.h" #import "SentryThread.h" @@ -59,6 +63,21 @@ - (id)initWithStacktraceBuilder:(SentryStacktraceBuilder *)stacktraceBuilder return self; } +- (instancetype)initWithOptions:(SentryOptions *)options +{ + SentryInAppLogic *inAppLogic = + [[SentryInAppLogic alloc] initWithInAppIncludes:options.inAppIncludes + inAppExcludes:options.inAppExcludes]; + SentryCrashStackEntryMapper *crashStackEntryMapper = + [[SentryCrashStackEntryMapper alloc] initWithInAppLogic:inAppLogic]; + SentryStacktraceBuilder *stacktraceBuilder = + [[SentryStacktraceBuilder alloc] initWithCrashStackEntryMapper:crashStackEntryMapper]; + id machineContextWrapper = + [[SentryCrashDefaultMachineContextWrapper alloc] init]; + return [self initWithStacktraceBuilder:stacktraceBuilder + andMachineContextWrapper:machineContextWrapper]; +} + - (SentryStacktrace *)stacktraceForCurrentThreadAsyncUnsafe { return [self.stacktraceBuilder buildStacktraceForCurrentThreadAsyncUnsafe]; diff --git a/Sources/Sentry/SentryTracer.m b/Sources/Sentry/SentryTracer.m index ff73c082018..b043649af85 100644 --- a/Sources/Sentry/SentryTracer.m +++ b/Sources/Sentry/SentryTracer.m @@ -54,10 +54,9 @@ * @c -[finish] doesn't necessarily lead to finishing the tracer, because it could still wait for * child spans to finish if @c waitForChildren is @c YES . */ @property (nonatomic) BOOL wasFinishCalled; -@property (nonatomic) NSTimeInterval idleTimeout; -@property (nonatomic, nullable, strong) SentryDispatchQueueWrapper *dispatchQueueWrapper; -@property (nonatomic, nullable, strong) SentryNSTimerWrapper *timerWrapper; @property (nonatomic, nullable, strong) NSTimer *deadlineTimer; +@property (nonnull, strong) SentryTracerConfiguration *configuration; + #if SENTRY_TARGET_PROFILING_SUPPORTED @property (nonatomic) BOOL isProfiling; @property (nonatomic) uint64_t startSystemTime; @@ -67,7 +66,6 @@ @implementation SentryTracer { /** Wether the tracer should wait for child spans to finish before finishing itself. */ - BOOL _waitForChildren; SentryTraceContext *_traceContext; SentryAppStartMeasurement *appStartMeasurement; NSMutableDictionary *_measurements; @@ -77,7 +75,6 @@ @implementation SentryTracer { NSDate *_originalStartTimestamp; #if SENTRY_HAS_UIKIT - NSUInteger initTotalFrames; NSUInteger initSlowFrames; NSUInteger initFrozenFrames; @@ -100,113 +97,60 @@ - (instancetype)initWithTransactionContext:(SentryTransactionContext *)transacti { return [self initWithTransactionContext:transactionContext hub:hub - profilesSamplerDecision:nil - waitForChildren:NO - timerWrapper:nil]; + configuration:SentryTracerConfiguration.defaultConfiguration]; } - (instancetype)initWithTransactionContext:(SentryTransactionContext *)transactionContext hub:(nullable SentryHub *)hub - waitForChildren:(BOOL)waitForChildren + configuration:(SentryTracerConfiguration *)configuration; { - return [self initWithTransactionContext:transactionContext - hub:hub - profilesSamplerDecision:nil - waitForChildren:waitForChildren - idleTimeout:0.0 - dispatchQueueWrapper:nil - timerWrapper:nil]; -} + if (!(self = [super initWithContext:transactionContext])) { + return nil; + } -- (instancetype)initWithTransactionContext:(SentryTransactionContext *)transactionContext - hub:(nullable SentryHub *)hub - profilesSamplerDecision: - (nullable SentryProfilesSamplerDecision *)profilesSamplerDecision - waitForChildren:(BOOL)waitForChildren - timerWrapper:(nullable SentryNSTimerWrapper *)timerWrapper -{ - return [self initWithTransactionContext:transactionContext - hub:hub - profilesSamplerDecision:profilesSamplerDecision - waitForChildren:waitForChildren - idleTimeout:0.0 - dispatchQueueWrapper:nil - timerWrapper:timerWrapper]; -} + _configuration = configuration; -- (instancetype)initWithTransactionContext:(SentryTransactionContext *)transactionContext - hub:(nullable SentryHub *)hub - profilesSamplerDecision: - (nullable SentryProfilesSamplerDecision *)profilesSamplerDecision - idleTimeout:(NSTimeInterval)idleTimeout - dispatchQueueWrapper:(SentryDispatchQueueWrapper *)dispatchQueueWrapper -{ - return [self initWithTransactionContext:transactionContext - hub:hub - profilesSamplerDecision:profilesSamplerDecision - waitForChildren:YES - idleTimeout:idleTimeout - dispatchQueueWrapper:dispatchQueueWrapper - timerWrapper:nil]; -} - -- (instancetype) - initWithTransactionContext:(SentryTransactionContext *)transactionContext - hub:(nullable SentryHub *)hub - profilesSamplerDecision:(nullable SentryProfilesSamplerDecision *)profilesSamplerDecision - waitForChildren:(BOOL)waitForChildren - idleTimeout:(NSTimeInterval)idleTimeout - dispatchQueueWrapper:(nullable SentryDispatchQueueWrapper *)dispatchQueueWrapper - timerWrapper:(nullable SentryNSTimerWrapper *)timerWrapper -{ - if (self = [super initWithContext:transactionContext]) { - self.transactionContext = transactionContext; - _children = [[NSMutableArray alloc] init]; - self.hub = hub; - self.wasFinishCalled = NO; - _waitForChildren = waitForChildren; - _measurements = [[NSMutableDictionary alloc] init]; - self.finishStatus = kSentrySpanStatusUndefined; - self.idleTimeout = idleTimeout; - self.dispatchQueueWrapper = dispatchQueueWrapper; - - if (timerWrapper == nil) { - self.timerWrapper = [[SentryNSTimerWrapper alloc] init]; - } else { - self.timerWrapper = timerWrapper; - } + self.transactionContext = transactionContext; + _children = [[NSMutableArray alloc] init]; + self.hub = hub; + self.wasFinishCalled = NO; + _measurements = [[NSMutableDictionary alloc] init]; + self.finishStatus = kSentrySpanStatusUndefined; - appStartMeasurement = [self getAppStartMeasurement]; + if (_configuration.timerWrapper == nil) { + _configuration.timerWrapper = [[SentryNSTimerWrapper alloc] init]; + } - if ([self hasIdleTimeout]) { - [self dispatchIdleTimeout]; - } + appStartMeasurement = [self getAppStartMeasurement]; - if ([self isAutoGeneratedTransaction]) { - [self startDeadlineTimer]; - } + if ([self hasIdleTimeout]) { + [self dispatchIdleTimeout]; + } + + if ([self isAutoGeneratedTransaction]) { + [self startDeadlineTimer]; + } #if SENTRY_HAS_UIKIT - // Store current amount of frames at the beginning to be able to calculate the amount of - // frames at the end of the transaction. - SentryFramesTracker *framesTracker = [SentryFramesTracker sharedInstance]; - if (framesTracker.isRunning) { - SentryScreenFrames *currentFrames = framesTracker.currentFrames; - initTotalFrames = currentFrames.total; - initSlowFrames = currentFrames.slow; - initFrozenFrames = currentFrames.frozen; - } + // Store current amount of frames at the beginning to be able to calculate the amount of + // frames at the end of the transaction. + SentryFramesTracker *framesTracker = [SentryFramesTracker sharedInstance]; + if (framesTracker.isRunning) { + SentryScreenFrames *currentFrames = framesTracker.currentFrames; + initTotalFrames = currentFrames.total; + initSlowFrames = currentFrames.slow; + initFrozenFrames = currentFrames.frozen; + } #endif // SENTRY_HAS_UIKIT #if SENTRY_TARGET_PROFILING_SUPPORTED - if (profilesSamplerDecision.decision == kSentrySampleDecisionYes) { - _isProfiling = YES; - _startSystemTime = getAbsoluteTime(); - [SentryProfiler startWithHub:hub]; - trackTracerWithID(self.traceId); - } -#endif // SENTRY_TARGET_PROFILING_SUPPORTED + if (_configuration.profilesSamplerDecision.decision == kSentrySampleDecisionYes) { + _isProfiling = YES; + _startSystemTime = SentryCurrentDate.systemTime; + [SentryProfiler startWithHub:hub]; + trackTracerWithID(self.traceId); } +#endif // SENTRY_TARGET_PROFILING_SUPPORTED return self; } @@ -219,40 +163,42 @@ - (nullable SentryTracer *)tracer - (void)dispatchIdleTimeout { if (_idleTimeoutBlock != nil) { - [self.dispatchQueueWrapper dispatchCancel:_idleTimeoutBlock]; + [_configuration.dispatchQueueWrapper dispatchCancel:_idleTimeoutBlock]; } __weak SentryTracer *weakSelf = self; - _idleTimeoutBlock = [self.dispatchQueueWrapper createDispatchBlock:^{ + _idleTimeoutBlock = [_configuration.dispatchQueueWrapper createDispatchBlock:^{ if (weakSelf == nil) { SENTRY_LOG_DEBUG(@"WeakSelf is nil. Not doing anything."); return; } [weakSelf finishInternal]; }]; + if (_idleTimeoutBlock == NULL) { - SENTRY_LOG_WARN(@"Couln't create idle time out block. Can't schedule idle timeout. " + SENTRY_LOG_WARN(@"Couldn't create idle time out block. Can't schedule idle timeout. " @"Finishing transaction"); // If the transaction has no children, the SDK will discard it. [self finishInternal]; } else { - [self.dispatchQueueWrapper dispatchAfter:self.idleTimeout block:_idleTimeoutBlock]; + [_configuration.dispatchQueueWrapper dispatchAfter:_configuration.idleTimeout + block:_idleTimeoutBlock]; } } - (BOOL)hasIdleTimeout { - return self.idleTimeout > 0 && self.dispatchQueueWrapper != nil; + return _configuration.idleTimeout > 0 && _configuration.dispatchQueueWrapper != nil; } - (BOOL)isAutoGeneratedTransaction { - return self.waitForChildren || [self hasIdleTimeout]; + return _configuration.waitForChildren || [self hasIdleTimeout]; } - (void)cancelIdleTimeout { if ([self hasIdleTimeout]) { - [self.dispatchQueueWrapper dispatchCancel:_idleTimeoutBlock]; + [_configuration.dispatchQueueWrapper dispatchCancel:_idleTimeoutBlock]; } } @@ -260,14 +206,14 @@ - (void)startDeadlineTimer { __weak SentryTracer *weakSelf = self; self.deadlineTimer = - [self.timerWrapper scheduledTimerWithTimeInterval:SENTRY_AUTO_TRANSACTION_DEADLINE - repeats:NO - block:^(NSTimer *_Nonnull timer) { - if (weakSelf == nil) { - return; - } - [weakSelf deadlineTimerFired]; - }]; + [_configuration.timerWrapper scheduledTimerWithTimeInterval:SENTRY_AUTO_TRANSACTION_DEADLINE + repeats:NO + block:^(NSTimer *_Nonnull timer) { + if (weakSelf == nil) { + return; + } + [weakSelf deadlineTimerFired]; + }]; } - (void)deadlineTimerFired @@ -413,14 +359,13 @@ - (void)setMeasurement:(NSString *)name value:(NSNumber *)value unit:(SentryMeas - (void)finish { - SENTRY_LOG_DEBUG( - @"-[SentryTracer finish] for trace ID %@", _traceContext.traceId.sentryIdString); [self finishWithStatus:kSentrySpanStatusOk]; } - (void)finishWithStatus:(SentrySpanStatus)status { - SENTRY_LOG_DEBUG(@"Finished trace %@", self.traceContext.traceId.sentryIdString); + SENTRY_LOG_DEBUG(@"Finished trace with traceID: %@ and status: %@", self.traceId.sentryIdString, + nameForSentrySpanStatus(status)); self.wasFinishCalled = YES; _finishStatus = status; [self canBeFinished]; @@ -456,7 +401,7 @@ - (void)canBeFinished - (BOOL)hasUnfinishedChildSpansToWaitFor { - if (!_waitForChildren) { + if (!self.configuration.waitForChildren) { return NO; } @@ -524,7 +469,7 @@ - (void)finishInternal }]; @synchronized(_children) { - if (self.idleTimeout > 0.0 && _children.count == 0) { + if (_configuration.idleTimeout > 0.0 && _children.count == 0) { SENTRY_LOG_DEBUG(@"Was waiting for timeout for UI event trace but it had no children, " @"will not keep transaction."); return; @@ -616,7 +561,7 @@ - (SentryTransaction *)toTransaction transaction.transaction = self.transactionContext.name; #if SENTRY_TARGET_PROFILING_SUPPORTED transaction.startSystemTime = self.startSystemTime; - transaction.endSystemTime = getAbsoluteTime(); + transaction.endSystemTime = SentryCurrentDate.systemTime; #endif // SENTRY_TARGET_PROFILING_SUPPORTED NSMutableArray *framesOfAllSpans = [NSMutableArray array]; @@ -850,6 +795,16 @@ - (NSDate *)originalStartTimestamp return _startTimeChanged ? _originalStartTimestamp : self.startTimestamp; } +#if SENTRY_TARGET_PROFILING_SUPPORTED && (defined(TEST) || defined(TESTCI)) +// this just calls through to SentryTracerConcurrency.resetConcurrencyTracking(). we have to do this +// through SentryTracer because SentryTracerConcurrency cannot be included in test targets via ObjC +// bridging headers because it contains C++. ++ (void)resetConcurrencyTracking +{ + resetConcurrencyTracking(); +} +#endif // SENTRY_TARGET_PROFILING_SUPPORTED && (defined(TEST) || defined(TESTCI)) + @end NS_ASSUME_NONNULL_END diff --git a/Sources/Sentry/SentryTracerConcurrency.mm b/Sources/Sentry/SentryTracerConcurrency.mm index bb3ee5f9c5d..7c4fd052338 100644 --- a/Sources/Sentry/SentryTracerConcurrency.mm +++ b/Sources/Sentry/SentryTracerConcurrency.mm @@ -38,4 +38,13 @@ } } +# if defined(TEST) || defined(TESTCI) +void +resetConcurrencyTracking() +{ + std::lock_guard l(_gStateLock); + [_gInFlightTraceIDs removeAllObjects]; +} +# endif // defined(TEST) || defined(TESTCI) + #endif // SENTRY_TARGET_PROFILING_SUPPORTED diff --git a/Sources/Sentry/SentryTracerConfiguration.m b/Sources/Sentry/SentryTracerConfiguration.m new file mode 100644 index 00000000000..c14ec504f4b --- /dev/null +++ b/Sources/Sentry/SentryTracerConfiguration.m @@ -0,0 +1,28 @@ +#import "SentryTracerConfiguration.h" + +@implementation SentryTracerConfiguration + ++ (SentryTracerConfiguration *)defaultConfiguration +{ + return [[SentryTracerConfiguration alloc] init]; +} + ++ (SentryTracerConfiguration *)configurationWithBlock:(void (^)(SentryTracerConfiguration *))block +{ + SentryTracerConfiguration *result = [[SentryTracerConfiguration alloc] init]; + + block(result); + + return result; +} + +- (instancetype)init +{ + if (self = [super init]) { + self.idleTimeout = 0; + self.waitForChildren = NO; + } + return self; +} + +@end diff --git a/Sources/Sentry/SentryTransaction.m b/Sources/Sentry/SentryTransaction.m index b17eba33d9d..bf1ef01fbed 100644 --- a/Sources/Sentry/SentryTransaction.m +++ b/Sources/Sentry/SentryTransaction.m @@ -85,7 +85,7 @@ - (instancetype)initWithTrace:(SentryTracer *)trace children:(NSArray)currentDateProvider dispatchQueueWrapper:(SentryDispatchQueueWrapper *)dispatchQueueWrapper appStateManager:(SentryAppStateManager *)appStateManager diff --git a/Sources/Sentry/include/SentryAutoBreadcrumbTrackingIntegration.h b/Sources/Sentry/include/SentryAutoBreadcrumbTrackingIntegration.h index 7c2ca028161..bd8e8819608 100644 --- a/Sources/Sentry/include/SentryAutoBreadcrumbTrackingIntegration.h +++ b/Sources/Sentry/include/SentryAutoBreadcrumbTrackingIntegration.h @@ -1,6 +1,6 @@ #import "SentryBaseIntegration.h" +#import "SentryBreadcrumbDelegate.h" #import "SentryIntegrationProtocol.h" -#import "SentrySystemEventBreadcrumbs.h" NS_ASSUME_NONNULL_BEGIN @@ -8,7 +8,7 @@ NS_ASSUME_NONNULL_BEGIN * This automatically adds breadcrumbs for different user actions. */ @interface SentryAutoBreadcrumbTrackingIntegration - : SentryBaseIntegration + : SentryBaseIntegration @end diff --git a/Sources/Sentry/include/SentryBreadcrumbDelegate.h b/Sources/Sentry/include/SentryBreadcrumbDelegate.h new file mode 100644 index 00000000000..b5db2045a03 --- /dev/null +++ b/Sources/Sentry/include/SentryBreadcrumbDelegate.h @@ -0,0 +1,11 @@ +#import "SentryInternalDefines.h" + +NS_ASSUME_NONNULL_BEGIN + +@protocol SentryBreadcrumbDelegate + +- (void)addBreadcrumb:(SentryBreadcrumb *)crumb; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Sources/Sentry/include/SentryBreadcrumbTracker.h b/Sources/Sentry/include/SentryBreadcrumbTracker.h index 0029bddcacc..8a9b7ffc247 100644 --- a/Sources/Sentry/include/SentryBreadcrumbTracker.h +++ b/Sources/Sentry/include/SentryBreadcrumbTracker.h @@ -4,12 +4,14 @@ NS_ASSUME_NONNULL_BEGIN @class SentrySwizzleWrapper; +@protocol SentryBreadcrumbDelegate; + @interface SentryBreadcrumbTracker : NSObject SENTRY_NO_INIT - (instancetype)initWithSwizzleWrapper:(SentrySwizzleWrapper *)swizzleWrapper; -- (void)start; +- (void)startWithDelegate:(id)delegate; - (void)startSwizzle; - (void)stop; diff --git a/Sources/Sentry/include/SentryCoreDataTracker.h b/Sources/Sentry/include/SentryCoreDataTracker.h index a6967e1ae96..773e56aeffb 100644 --- a/Sources/Sentry/include/SentryCoreDataTracker.h +++ b/Sources/Sentry/include/SentryCoreDataTracker.h @@ -7,7 +7,13 @@ NS_ASSUME_NONNULL_BEGIN static NSString *const SENTRY_COREDATA_FETCH_OPERATION = @"db.sql.query"; static NSString *const SENTRY_COREDATA_SAVE_OPERATION = @"db.sql.transaction"; +@class SentryThreadInspector, SentryNSProcessInfoWrapper; + @interface SentryCoreDataTracker : NSObject +SENTRY_NO_INIT + +- (instancetype)initWithThreadInspector:(SentryThreadInspector *)threadInspector + processInfoWrapper:(SentryNSProcessInfoWrapper *)processInfoWrapper; @end diff --git a/Sources/Sentry/include/SentryCurrentDate.h b/Sources/Sentry/include/SentryCurrentDate.h index 26c6afea515..839c6960474 100644 --- a/Sources/Sentry/include/SentryCurrentDate.h +++ b/Sources/Sentry/include/SentryCurrentDate.h @@ -12,6 +12,8 @@ NS_SWIFT_NAME(CurrentDate) + (NSDate *)date; ++ (uint64_t)systemTime; + + (dispatch_time_t)dispatchTimeNow; + (void)setCurrentDateProvider:(nullable id)currentDateProvider; diff --git a/Sources/Sentry/include/SentryCurrentDateProvider.h b/Sources/Sentry/include/SentryCurrentDateProvider.h index 74dae8dd2f1..cc7417ef250 100644 --- a/Sources/Sentry/include/SentryCurrentDateProvider.h +++ b/Sources/Sentry/include/SentryCurrentDateProvider.h @@ -11,6 +11,8 @@ NS_SWIFT_NAME(CurrentDateProvider) - (NSInteger)timezoneOffset; +- (uint64_t)systemTime; + @end NS_ASSUME_NONNULL_END diff --git a/Sources/Sentry/include/SentryDependencyContainer.h b/Sources/Sentry/include/SentryDependencyContainer.h index ad5905fd98a..49d34bd8f9a 100644 --- a/Sources/Sentry/include/SentryDependencyContainer.h +++ b/Sources/Sentry/include/SentryDependencyContainer.h @@ -4,7 +4,7 @@ @class SentryAppStateManager, SentryCrashWrapper, SentryThreadWrapper, SentrySwizzleWrapper, SentryDispatchQueueWrapper, SentryDebugImageProvider, SentryANRTracker, - SentryNSNotificationCenterWrapper, SentryMXManager; + SentryNSNotificationCenterWrapper, SentryMXManager, SentryNSProcessInfoWrapper; #if SENTRY_HAS_UIKIT @class SentryScreenshot, SentryUIApplication, SentryViewHierarchy; @@ -32,6 +32,7 @@ SENTRY_NO_INIT @property (nonatomic, strong) SentryNSNotificationCenterWrapper *notificationCenterWrapper; @property (nonatomic, strong) SentryDebugImageProvider *debugImageProvider; @property (nonatomic, strong) SentryANRTracker *anrTracker; +@property (nonatomic, strong) SentryNSProcessInfoWrapper *processInfoWrapper; #if SENTRY_HAS_UIKIT @property (nonatomic, strong) SentryScreenshot *screenshot; diff --git a/Sources/Sentry/include/SentryHub+Private.h b/Sources/Sentry/include/SentryHub+Private.h index 21692e7d22c..622911d03cc 100644 --- a/Sources/Sentry/include/SentryHub+Private.h +++ b/Sources/Sentry/include/SentryHub+Private.h @@ -1,7 +1,8 @@ #import "SentryHub.h" +#import "SentryTracer.h" @class SentryEnvelopeItem, SentryId, SentryScope, SentryTransaction, SentryDispatchQueueWrapper, - SentryEnvelope, SentryTracer, SentryNSTimerWrapper; + SentryEnvelope, SentryNSTimerWrapper; NS_ASSUME_NONNULL_BEGIN @@ -24,26 +25,10 @@ SentryHub (Private) - (void)closeCachedSessionWithTimestamp:(NSDate *_Nullable)timestamp; -- (id)startTransactionWithName:(NSString *)name - nameSource:(SentryTransactionNameSource)source - operation:(NSString *)operation; - -- (id)startTransactionWithName:(NSString *)name - nameSource:(SentryTransactionNameSource)source - operation:(NSString *)operation - bindToScope:(BOOL)bindToScope; - -- (id)startTransactionWithContext:(SentryTransactionContext *)transactionContext - bindToScope:(BOOL)bindToScope - waitForChildren:(BOOL)waitForChildren - customSamplingContext:(NSDictionary *)customSamplingContext - timerWrapper:(nullable SentryNSTimerWrapper *)timerWrapper; - - (SentryTracer *)startTransactionWithContext:(SentryTransactionContext *)transactionContext bindToScope:(BOOL)bindToScope customSamplingContext:(NSDictionary *)customSamplingContext - idleTimeout:(NSTimeInterval)idleTimeout - dispatchQueueWrapper:(SentryDispatchQueueWrapper *)dispatchQueueWrapper; + configuration:(SentryTracerConfiguration *)configuration; - (SentryId *)captureEvent:(SentryEvent *)event withScope:(SentryScope *)scope diff --git a/Sources/Sentry/include/SentryInternalDefines.h b/Sources/Sentry/include/SentryInternalDefines.h index f18f232a38f..5b8ae123250 100644 --- a/Sources/Sentry/include/SentryInternalDefines.h +++ b/Sources/Sentry/include/SentryInternalDefines.h @@ -31,3 +31,5 @@ static NSString *const SentryDebugImageType = @"macho"; } \ (__cond_result); \ }) + +#define BLOCKED_MAIN_THREAD @"blocked_main_thread" diff --git a/Sources/Sentry/include/SentryNSTimerWrapper.h b/Sources/Sentry/include/SentryNSTimerWrapper.h index 0529b48e5ba..2e80fa83f84 100644 --- a/Sources/Sentry/include/SentryNSTimerWrapper.h +++ b/Sources/Sentry/include/SentryNSTimerWrapper.h @@ -8,6 +8,12 @@ NS_ASSUME_NONNULL_BEGIN repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block; +- (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti + target:(id)aTarget + selector:(SEL)aSelector + userInfo:(nullable id)userInfo + repeats:(BOOL)yesOrNo; + @end NS_ASSUME_NONNULL_END diff --git a/Sources/Sentry/include/SentryPerformanceTracker+Private.h b/Sources/Sentry/include/SentryPerformanceTracker+Private.h deleted file mode 100644 index d58ef221026..00000000000 --- a/Sources/Sentry/include/SentryPerformanceTracker+Private.h +++ /dev/null @@ -1,21 +0,0 @@ -#import "SentryPerformanceTracker.h" - -@interface -SentryPerformanceTracker (Private) - -- (SentrySpanId *)startSpanWithName:(NSString *)name - nameSource:(SentryTransactionNameSource)source - operation:(NSString *)operation; - -- (void)measureSpanWithDescription:(NSString *)description - nameSource:(SentryTransactionNameSource)source - operation:(NSString *)operation - inBlock:(void (^)(void))block; - -- (void)measureSpanWithDescription:(NSString *)description - nameSource:(SentryTransactionNameSource)source - operation:(NSString *)operation - parentSpanId:(SentrySpanId *)parentSpanId - inBlock:(void (^)(void))block; - -@end diff --git a/Sources/Sentry/include/SentryPerformanceTracker.h b/Sources/Sentry/include/SentryPerformanceTracker.h index 6f43d141d0b..e187521e0f3 100644 --- a/Sources/Sentry/include/SentryPerformanceTracker.h +++ b/Sources/Sentry/include/SentryPerformanceTracker.h @@ -21,10 +21,13 @@ NS_ASSUME_NONNULL_BEGIN * Starts a new span if no span is active, then bind it to the scope if no span is bound. * @note If there's an active span, starts a child of the active span. * @param name Span name. + * @param source the transaction name source. * @param operation Span operation. * @return The span id. */ -- (SentrySpanId *)startSpanWithName:(NSString *)name operation:(NSString *)operation; +- (SentrySpanId *)startSpanWithName:(NSString *)name + nameSource:(SentryTransactionNameSource)source + operation:(NSString *)operation; /** * Activate the span with @c spanId to create any call to @c startSpan as a child. @@ -37,10 +40,12 @@ NS_ASSUME_NONNULL_BEGIN /** * Measure the given @c block execution. * @param description The description of the span. + * @param source the transaction name source. * @param operation Span operation. * @param block Block to be measured. */ - (void)measureSpanWithDescription:(NSString *)description + nameSource:(SentryTransactionNameSource)source operation:(NSString *)operation inBlock:(void (^)(void))block; @@ -48,11 +53,13 @@ NS_ASSUME_NONNULL_BEGIN * Measure the given @c block execution adding it as a child of given parent span. * @note If @c parentSpanId does not exist this measurement is not performed. * @param description The description of the span. + * @param source the transaction name source. * @param operation Span operation. * @param parentSpanId Id of the span to use as parent. * @param block Block to be measured. */ - (void)measureSpanWithDescription:(NSString *)description + nameSource:(SentryTransactionNameSource)source operation:(NSString *)operation parentSpanId:(SentrySpanId *)parentSpanId inBlock:(void (^)(void))block; diff --git a/Sources/Sentry/include/SentryProfiler.h b/Sources/Sentry/include/SentryProfiler.h index 1a9116a06e6..2bfa6d16f26 100644 --- a/Sources/Sentry/include/SentryProfiler.h +++ b/Sources/Sentry/include/SentryProfiler.h @@ -22,7 +22,6 @@ NS_ASSUME_NONNULL_BEGIN SENTRY_EXTERN const int kSentryProfilerFrequencyHz; SENTRY_EXTERN NSString *const kTestStringConst; -FOUNDATION_EXPORT NSTimeInterval kSentryProfilerTimeoutInterval; SENTRY_EXTERN NSString *const kSentryProfilerSerializationKeySlowFrameRenders; SENTRY_EXTERN NSString *const kSentryProfilerSerializationKeyFrozenFrameRenders; diff --git a/Sources/Sentry/include/SentrySDK+Private.h b/Sources/Sentry/include/SentrySDK+Private.h index c50cc50ee4e..f4fbaeb2a4b 100644 --- a/Sources/Sentry/include/SentrySDK+Private.h +++ b/Sources/Sentry/include/SentrySDK+Private.h @@ -36,18 +36,6 @@ SentrySDK (Private) */ + (void)captureEnvelope:(SentryEnvelope *)envelope; -/** - * Start a transaction with a name and a name source. - */ -+ (id)startTransactionWithName:(NSString *)name - nameSource:(SentryTransactionNameSource)source - operation:(NSString *)operation; - -+ (id)startTransactionWithName:(NSString *)name - nameSource:(SentryTransactionNameSource)source - operation:(NSString *)operation - bindToScope:(BOOL)bindToScope; - @end NS_ASSUME_NONNULL_END diff --git a/Sources/Sentry/include/SentrySerialization.h b/Sources/Sentry/include/SentrySerialization.h index 4946fadddb5..562e7904eec 100644 --- a/Sources/Sentry/include/SentrySerialization.h +++ b/Sources/Sentry/include/SentrySerialization.h @@ -8,11 +8,9 @@ static int const SENTRY_BAGGAGE_MAX_SIZE = 8192; @interface SentrySerialization : NSObject -+ (NSData *_Nullable)dataWithJSONObject:(NSDictionary *)dictionary - error:(NSError *_Nullable *_Nullable)error; ++ (NSData *_Nullable)dataWithJSONObject:(NSDictionary *)dictionary; -+ (NSData *_Nullable)dataWithSession:(SentrySession *)session - error:(NSError *_Nullable *_Nullable)error; ++ (NSData *_Nullable)dataWithSession:(SentrySession *)session; + (NSDictionary *)decodeBaggage:(NSString *)baggage; + (NSString *)baggageEncodedDictionary:(NSDictionary *)dictionary; diff --git a/Sources/Sentry/include/SentrySystemEventBreadcrumbs.h b/Sources/Sentry/include/SentrySystemEventBreadcrumbs.h index 76d14a1ccf2..39f8aecd309 100644 --- a/Sources/Sentry/include/SentrySystemEventBreadcrumbs.h +++ b/Sources/Sentry/include/SentrySystemEventBreadcrumbs.h @@ -10,7 +10,7 @@ NS_ASSUME_NONNULL_BEGIN @class SentryNSNotificationCenterWrapper; -@protocol SentrySystemEventBreadcrumbsDelegate; +@protocol SentryBreadcrumbDelegate; @interface SentrySystemEventBreadcrumbs : NSObject SENTRY_NO_INIT @@ -19,10 +19,10 @@ SENTRY_NO_INIT andCurrentDateProvider:(id)currentDateProvider andNotificationCenterWrapper:(SentryNSNotificationCenterWrapper *)notificationCenterWrapper; -- (void)startWithDelegate:(id)delegate; +- (void)startWithDelegate:(id)delegate; #if TARGET_OS_IOS -- (void)startWithDelegate:(id)delegate +- (void)startWithDelegate:(id)delegate currentDevice:(nullable UIDevice *)currentDevice; - (void)timezoneEventTriggered; #endif @@ -31,10 +31,4 @@ SENTRY_NO_INIT @end -@protocol SentrySystemEventBreadcrumbsDelegate - -- (void)addBreadcrumb:(SentryBreadcrumb *)crumb; - -@end - NS_ASSUME_NONNULL_END diff --git a/Sources/Sentry/include/SentryThreadInspector.h b/Sources/Sentry/include/SentryThreadInspector.h index 77da2bf208f..216aac1ba46 100644 --- a/Sources/Sentry/include/SentryThreadInspector.h +++ b/Sources/Sentry/include/SentryThreadInspector.h @@ -2,7 +2,7 @@ #import "SentryDefines.h" #import -@class SentryThread, SentryStacktraceBuilder, SentryStacktrace; +@class SentryThread, SentryStacktraceBuilder, SentryStacktrace, SentryOptions; NS_ASSUME_NONNULL_BEGIN @@ -12,6 +12,8 @@ SENTRY_NO_INIT - (id)initWithStacktraceBuilder:(SentryStacktraceBuilder *)stacktraceBuilder andMachineContextWrapper:(id)machineContextWrapper; +- (instancetype)initWithOptions:(SentryOptions *)options; + - (nullable SentryStacktrace *)stacktraceForCurrentThreadAsyncUnsafe; /** diff --git a/Sources/Sentry/include/SentryTime.h b/Sources/Sentry/include/SentryTime.h index d236fc38dee..3985b60d12a 100644 --- a/Sources/Sentry/include/SentryTime.h +++ b/Sources/Sentry/include/SentryTime.h @@ -21,7 +21,8 @@ uint64_t getAbsoluteTime(void); * Check whether two timestamps provided as 64 bit unsigned integers are in normal * chronological order, as a convenience runtime check before using @c getDurationNs. * Equal timestamps are considered to be valid chronological order. - * @return @c true if @c b>=a, otherwise return @c false. + * @return @c true if @c a<=b, otherwise return @c false. + * @note Negating the return value implies @c a>b . */ bool orderedChronologically(uint64_t a, uint64_t b); diff --git a/Sources/Sentry/include/SentryTraceContext.h b/Sources/Sentry/include/SentryTraceContext.h index 3fd1b491928..607471c0083 100644 --- a/Sources/Sentry/include/SentryTraceContext.h +++ b/Sources/Sentry/include/SentryTraceContext.h @@ -50,7 +50,7 @@ NS_ASSUME_NONNULL_BEGIN /** * Sample rate used for this trace. */ -@property (nullable, nonatomic) NSString *sampleRate; +@property (nullable, nonatomic, strong) NSString *sampleRate; /** * Initializes a SentryTraceContext with given properties. diff --git a/Sources/Sentry/include/SentryTracer.h b/Sources/Sentry/include/SentryTracer.h index 300ea72a447..1a6fab6a8f8 100644 --- a/Sources/Sentry/include/SentryTracer.h +++ b/Sources/Sentry/include/SentryTracer.h @@ -1,5 +1,6 @@ #import "SentrySpan.h" #import "SentrySpanProtocol.h" +#import "SentryTracerConfiguration.h" #import NS_ASSUME_NONNULL_BEGIN @@ -31,13 +32,6 @@ static NSTimeInterval const SentryTracerDefaultTimeout = 3.0; @property (nullable, nonatomic, copy) void (^finishCallback)(SentryTracer *); -/** - * Indicates whether this tracer will be finished only if all children have been finished. - * If this property is @c YES and the finish function is called before all children are finished - * the tracer will automatically finish when the last child finishes. - */ -@property (readonly) BOOL waitForChildren; - /** * Retrieves a trace context from this tracer. */ @@ -72,46 +66,17 @@ static NSTimeInterval const SentryTracerDefaultTimeout = 3.0; hub:(nullable SentryHub *)hub; /** - * Init a @c SentryTracer with given transaction context, hub and whether the tracer should wait - * for all children to finish before it finishes. - * @param transactionContext Transaction context - * @param hub A hub to bind this transaction - * @param waitForChildren Whether this tracer should wait all children to finish. - */ -- (instancetype)initWithTransactionContext:(SentryTransactionContext *)transactionContext - hub:(nullable SentryHub *)hub - waitForChildren:(BOOL)waitForChildren; - -/** - * Init a @c SentryTracer with given transaction context, hub and whether the tracer should wait - * for all children to finish before it finishes. - * @param transactionContext Transaction context - * @param hub A hub to bind this transaction. - * @param profilesSamplerDecision Whether to sample a profile corresponding to this transaction. - * @param waitForChildren Whether this tracer should wait all children to finish. - * @param timerWrapper A wrapper around @c NSTimer, to make it testable. - */ -- (instancetype)initWithTransactionContext:(SentryTransactionContext *)transactionContext - hub:(nullable SentryHub *)hub - profilesSamplerDecision: - (nullable SentryProfilesSamplerDecision *)profilesSamplerDecision - waitForChildren:(BOOL)waitForChildren - timerWrapper:(nullable SentryNSTimerWrapper *)timerWrapper; - -/** - * Init a @c SentryTracer with given transaction context, hub and whether the tracer should wait - * for all children to finish before it finishes. + * Init a SentryTracer with given transaction context and hub and set other fields by default + * * @param transactionContext Transaction context * @param hub A hub to bind this transaction - * @param profilesSamplerDecision Whether to sample a profile corresponding to this transaction - * @param idleTimeout The idle time to wait until to finish the transaction. + * @param configuration Configuration on how SentryTracer will behave + * + * @return SentryTracer */ - (instancetype)initWithTransactionContext:(SentryTransactionContext *)transactionContext hub:(nullable SentryHub *)hub - profilesSamplerDecision: - (nullable SentryProfilesSamplerDecision *)profilesSamplerDecision - idleTimeout:(NSTimeInterval)idleTimeout - dispatchQueueWrapper:(SentryDispatchQueueWrapper *)dispatchQueueWrapper; + configuration:(SentryTracerConfiguration *)configuration; - (id)startChildWithParentId:(SentrySpanId *)parentId operation:(NSString *)operation diff --git a/Sources/Sentry/include/SentryTracerConcurrency.h b/Sources/Sentry/include/SentryTracerConcurrency.h index f4123810cb3..9710f5df41d 100644 --- a/Sources/Sentry/include/SentryTracerConcurrency.h +++ b/Sources/Sentry/include/SentryTracerConcurrency.h @@ -22,6 +22,10 @@ void trackTracerWithID(SentryId *traceID); */ void stopTrackingTracerWithID(SentryId *traceID, SentryConcurrentTransactionCleanupBlock cleanup); +# if defined(TEST) || defined(TESTCI) +void resetConcurrencyTracking(void); +# endif // defined(TEST) || defined(TESTCI) + SENTRY_EXTERN_C_END NS_ASSUME_NONNULL_END diff --git a/Sources/Sentry/include/SentryTracerConfiguration.h b/Sources/Sentry/include/SentryTracerConfiguration.h new file mode 100644 index 00000000000..25070ff20d3 --- /dev/null +++ b/Sources/Sentry/include/SentryTracerConfiguration.h @@ -0,0 +1,50 @@ +#import + +NS_ASSUME_NONNULL_BEGIN + +@class SentryNSTimerWrapper, SentryDispatchQueueWrapper, SentryProfilesSamplerDecision; + +@interface SentryTracerConfiguration : NSObject + +/** + * Return an instance of SentryTracerConfiguration with default values. + */ +@property (class, readonly) SentryTracerConfiguration *defaultConfiguration; + +/** + * Indicates whether the tracer will be finished only if all children have been finished. + * If this property is YES and the finish function is called before all children are finished + * the tracer will automatically finish when the last child finishes. + * + * Default is NO. + */ +@property (nonatomic) BOOL waitForChildren; + +/** + * A dispatch queue wrapper to intermediate between the tracer and dispatch calls. + */ +@property (nonatomic, strong, nullable) SentryDispatchQueueWrapper *dispatchQueueWrapper; + +/** + * Whether to sample a profile corresponding to this transaction + */ +@property (nonatomic, strong, nullable) SentryProfilesSamplerDecision *profilesSamplerDecision; + +/** + * The idle time to wait until to finish the transaction + * + * Default is 0 seconds + */ +@property (nonatomic) NSTimeInterval idleTimeout; + +/** + * A writer around NSTimer, to make it testable + */ +@property (nonatomic, strong, nullable) SentryNSTimerWrapper *timerWrapper; + ++ (SentryTracerConfiguration *)configurationWithBlock: + (void (^)(SentryTracerConfiguration *configuration))block; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Sources/Sentry/include/SentryTransaction.h b/Sources/Sentry/include/SentryTransaction.h index d12447d740b..5b9eadaae35 100644 --- a/Sources/Sentry/include/SentryTransaction.h +++ b/Sources/Sentry/include/SentryTransaction.h @@ -4,7 +4,7 @@ NS_ASSUME_NONNULL_BEGIN -@class SentryTracer, SentryTransactionContext; +@class SentryTracer; NS_SWIFT_NAME(Transaction) @interface SentryTransaction : SentryEvent diff --git a/Sources/SentryCrash/Installations/SentryCrashInstallation+Private.h b/Sources/SentryCrash/Installations/SentryCrashInstallation+Private.h index ccb0605db57..196dcc5916d 100644 --- a/Sources/SentryCrash/Installations/SentryCrashInstallation+Private.h +++ b/Sources/SentryCrash/Installations/SentryCrashInstallation+Private.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashReportFieldProperties.h // @@ -26,31 +27,6 @@ #import "SentryCrashInstallation.h" -/** Implement a property to be used as a "key". */ -#define IMPLEMENT_REPORT_KEY_PROPERTY(NAME, NAMEUPPER) \ - @synthesize NAME##Key = _##NAME##Key; \ - -(void)set##NAMEUPPER##Key : (NSString *)value \ - { \ - _##NAME##Key; \ - _##NAME##Key = value; \ - [self reportFieldForProperty:@ #NAME setKey:value]; \ - } - -/** Implement a property to be used as a "value". */ -#define IMPLEMENT_REPORT_VALUE_PROPERTY(NAME, NAMEUPPER, TYPE) \ - @synthesize NAME = _##NAME; \ - -(void)set##NAMEUPPER : (TYPE)value \ - { \ - _##NAME; \ - _##NAME = value; \ - [self reportFieldForProperty:@ #NAME setValue:value]; \ - } - -/** Implement a standard report property (with key and value properties) */ -#define IMPLEMENT_REPORT_PROPERTY(NAME, NAMEUPPER, TYPE) \ - IMPLEMENT_REPORT_VALUE_PROPERTY(NAME, NAMEUPPER, TYPE) \ - IMPLEMENT_REPORT_KEY_PROPERTY(NAME, NAMEUPPER) - typedef struct { const char *key; const char *value; @@ -71,20 +47,6 @@ SentryCrashInstallation () */ - (id)initWithRequiredProperties:(NSArray *)requiredProperties; -/** Set the key to be used for the specified report property. - * - * @param propertyName The name of the property. - * @param key The key to use. - */ -- (void)reportFieldForProperty:(NSString *)propertyName setKey:(id)key; - -/** Set the value of the specified report property. - * - * @param propertyName The name of the property. - * @param value The value to set. - */ -- (void)reportFieldForProperty:(NSString *)propertyName setValue:(id)value; - /** Create a new sink. Subclasses must implement this. */ - (id)sink; diff --git a/Sources/SentryCrash/Installations/SentryCrashInstallation.h b/Sources/SentryCrash/Installations/SentryCrashInstallation.h index 4eba18e1f67..cfddcfe4d6c 100644 --- a/Sources/SentryCrash/Installations/SentryCrashInstallation.h +++ b/Sources/SentryCrash/Installations/SentryCrashInstallation.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashInstallation.h // diff --git a/Sources/SentryCrash/Installations/SentryCrashInstallation.m b/Sources/SentryCrash/Installations/SentryCrashInstallation.m index 763613f371c..31f33665703 100644 --- a/Sources/SentryCrash/Installations/SentryCrashInstallation.m +++ b/Sources/SentryCrash/Installations/SentryCrashInstallation.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashInstallation.m // @@ -193,31 +194,6 @@ - (CrashHandlerData *)g_crashHandlerData return g_crashHandlerData; } -- (SentryCrashInstReportField *)reportFieldForProperty:(NSString *)propertyName -{ - SentryCrashInstReportField *field = [self.fields objectForKey:propertyName]; - if (field == nil) { - field = [SentryCrashInstReportField fieldWithIndex:self.nextFieldIndex]; - self.nextFieldIndex++; - self.crashHandlerData->reportFieldsCount = self.nextFieldIndex; - self.crashHandlerData->reportFields[field.index] = field.field; - [self.fields setObject:field forKey:propertyName]; - } - return field; -} - -- (void)reportFieldForProperty:(NSString *)propertyName setKey:(id)key -{ - SentryCrashInstReportField *field = [self reportFieldForProperty:propertyName]; - field.key = key; -} - -- (void)reportFieldForProperty:(NSString *)propertyName setValue:(id)value -{ - SentryCrashInstReportField *field = [self reportFieldForProperty:propertyName]; - field.value = value; -} - - (NSError *)validateProperties { NSMutableString *errors = [NSMutableString string]; diff --git a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor.c b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor.c index 055b19fcf31..080720bf20d 100644 --- a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor.c +++ b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor.c @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashMonitor.c // diff --git a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor.h b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor.h index 0c8e3d17680..8e4c9259a71 100644 --- a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor.h +++ b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashMonitor.h // diff --git a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitorContext.h b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitorContext.h index 7de0b426370..08eec503539 100644 --- a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitorContext.h +++ b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitorContext.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashMonitorContext.h // diff --git a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitorType.c b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitorType.c index 86481cc3b18..b14aead36d9 100644 --- a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitorType.c +++ b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitorType.c @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashMonitorType.c // diff --git a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitorType.h b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitorType.h index 7a388a42098..e38dd9b097f 100644 --- a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitorType.h +++ b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitorType.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashMonitorType.h // diff --git a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_AppState.c b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_AppState.c index 19c6a830392..0ed2e122597 100644 --- a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_AppState.c +++ b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_AppState.c @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashMonitor_AppState.c // diff --git a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_AppState.h b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_AppState.h index fe1235586dd..e00da4e74d0 100644 --- a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_AppState.h +++ b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_AppState.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashMonitor_AppState.h // diff --git a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_CPPException.cpp b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_CPPException.cpp index f24617fcf7d..e7d13d427ce 100644 --- a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_CPPException.cpp +++ b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_CPPException.cpp @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashMonitor_CPPException.c // diff --git a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_CPPException.h b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_CPPException.h index 7b4d71d3a45..39908fb7cb4 100644 --- a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_CPPException.h +++ b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_CPPException.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashMonitor_CPPException.h // diff --git a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_MachException.c b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_MachException.c index ad9fbf88392..6176dd84a9d 100644 --- a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_MachException.c +++ b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_MachException.c @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashMonitor_MachException.c // diff --git a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_MachException.h b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_MachException.h index 013db661ef8..e72d29422c8 100644 --- a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_MachException.h +++ b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_MachException.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashMonitor_MachException.h // diff --git a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_NSException.h b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_NSException.h index c6a3f2974cf..76280ba508a 100644 --- a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_NSException.h +++ b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_NSException.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashMonitor_NSException.h // diff --git a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_NSException.m b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_NSException.m index 91573c5057d..4d1a658c796 100644 --- a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_NSException.m +++ b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_NSException.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashMonitor_NSException.m // diff --git a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_Signal.c b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_Signal.c index 382976344a9..a73c73668cd 100644 --- a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_Signal.c +++ b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_Signal.c @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashMonitor_Signal.c // diff --git a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_Signal.h b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_Signal.h index 5c3802f38ff..f03f3fdb329 100644 --- a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_Signal.h +++ b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_Signal.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashMonitor_Signal.h // diff --git a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_System.h b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_System.h index 9b0bb7ddd8a..8fbe326b0ef 100644 --- a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_System.h +++ b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_System.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashMonitor_System.h // diff --git a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_System.m b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_System.m index aecb5680acf..84f225539fe 100644 --- a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_System.m +++ b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_System.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashMonitor_System.m // diff --git a/Sources/SentryCrash/Recording/SentryCrash.h b/Sources/SentryCrash/Recording/SentryCrash.h index 1ba6b775956..c7188e94e15 100644 --- a/Sources/SentryCrash/Recording/SentryCrash.h +++ b/Sources/SentryCrash/Recording/SentryCrash.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrash.h // diff --git a/Sources/SentryCrash/Recording/SentryCrash.m b/Sources/SentryCrash/Recording/SentryCrash.m index 368a2873ead..de82c959fec 100644 --- a/Sources/SentryCrash/Recording/SentryCrash.m +++ b/Sources/SentryCrash/Recording/SentryCrash.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrash.m // diff --git a/Sources/SentryCrash/Recording/SentryCrashC.c b/Sources/SentryCrash/Recording/SentryCrashC.c index b058957dfc3..ff21ab1ffcf 100644 --- a/Sources/SentryCrash/Recording/SentryCrashC.c +++ b/Sources/SentryCrash/Recording/SentryCrashC.c @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashC.c // diff --git a/Sources/SentryCrash/Recording/SentryCrashC.h b/Sources/SentryCrash/Recording/SentryCrashC.h index eeba4f817e0..9601289b7a6 100644 --- a/Sources/SentryCrash/Recording/SentryCrashC.h +++ b/Sources/SentryCrash/Recording/SentryCrashC.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashC.h // diff --git a/Sources/SentryCrash/Recording/SentryCrashCachedData.c b/Sources/SentryCrash/Recording/SentryCrashCachedData.c index 66e21255679..5302eb41b51 100644 --- a/Sources/SentryCrash/Recording/SentryCrashCachedData.c +++ b/Sources/SentryCrash/Recording/SentryCrashCachedData.c @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashCachedData.c // diff --git a/Sources/SentryCrash/Recording/SentryCrashCachedData.h b/Sources/SentryCrash/Recording/SentryCrashCachedData.h index d0f35cbaedf..f6a89f95d82 100644 --- a/Sources/SentryCrash/Recording/SentryCrashCachedData.h +++ b/Sources/SentryCrash/Recording/SentryCrashCachedData.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashCachedData.h // diff --git a/Sources/SentryCrash/Recording/SentryCrashDoctor.h b/Sources/SentryCrash/Recording/SentryCrashDoctor.h index af7aab96a09..09734fbadbf 100644 --- a/Sources/SentryCrash/Recording/SentryCrashDoctor.h +++ b/Sources/SentryCrash/Recording/SentryCrashDoctor.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashDoctor.h // SentryCrash diff --git a/Sources/SentryCrash/Recording/SentryCrashDoctor.m b/Sources/SentryCrash/Recording/SentryCrashDoctor.m index e9a1ea785ab..759d856ddc3 100644 --- a/Sources/SentryCrash/Recording/SentryCrashDoctor.m +++ b/Sources/SentryCrash/Recording/SentryCrashDoctor.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashDoctor.m // SentryCrash diff --git a/Sources/SentryCrash/Recording/SentryCrashReport.c b/Sources/SentryCrash/Recording/SentryCrashReport.c index 41c73794032..07500c2fcaa 100644 --- a/Sources/SentryCrash/Recording/SentryCrashReport.c +++ b/Sources/SentryCrash/Recording/SentryCrashReport.c @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashReport.m // diff --git a/Sources/SentryCrash/Recording/SentryCrashReport.h b/Sources/SentryCrash/Recording/SentryCrashReport.h index fa06ae182aa..1eb0aeb8965 100644 --- a/Sources/SentryCrash/Recording/SentryCrashReport.h +++ b/Sources/SentryCrash/Recording/SentryCrashReport.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashReport.h // diff --git a/Sources/SentryCrash/Recording/SentryCrashReportFields.h b/Sources/SentryCrash/Recording/SentryCrashReportFields.h index cef8cb36d00..8736d2b6ea0 100644 --- a/Sources/SentryCrash/Recording/SentryCrashReportFields.h +++ b/Sources/SentryCrash/Recording/SentryCrashReportFields.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashReportFields.h // diff --git a/Sources/SentryCrash/Recording/SentryCrashReportFixer.c b/Sources/SentryCrash/Recording/SentryCrashReportFixer.c index 0a1fe65494a..feccd2b3e1c 100644 --- a/Sources/SentryCrash/Recording/SentryCrashReportFixer.c +++ b/Sources/SentryCrash/Recording/SentryCrashReportFixer.c @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashReportFixer.c // diff --git a/Sources/SentryCrash/Recording/SentryCrashReportFixer.h b/Sources/SentryCrash/Recording/SentryCrashReportFixer.h index bc259043078..ad2e77b09d4 100644 --- a/Sources/SentryCrash/Recording/SentryCrashReportFixer.h +++ b/Sources/SentryCrash/Recording/SentryCrashReportFixer.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashReportFixer.c // diff --git a/Sources/SentryCrash/Recording/SentryCrashReportStore.c b/Sources/SentryCrash/Recording/SentryCrashReportStore.c index 997064a6fed..f7bba6bb23b 100644 --- a/Sources/SentryCrash/Recording/SentryCrashReportStore.c +++ b/Sources/SentryCrash/Recording/SentryCrashReportStore.c @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashReportStore.c // diff --git a/Sources/SentryCrash/Recording/SentryCrashReportStore.h b/Sources/SentryCrash/Recording/SentryCrashReportStore.h index 24ced7d9811..7b101368b4b 100644 --- a/Sources/SentryCrash/Recording/SentryCrashReportStore.h +++ b/Sources/SentryCrash/Recording/SentryCrashReportStore.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashReportStore.h // diff --git a/Sources/SentryCrash/Recording/SentryCrashReportVersion.h b/Sources/SentryCrash/Recording/SentryCrashReportVersion.h index 651bfd863f7..4f7e8fe4b8d 100644 --- a/Sources/SentryCrash/Recording/SentryCrashReportVersion.h +++ b/Sources/SentryCrash/Recording/SentryCrashReportVersion.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashReportVersion.h // diff --git a/Sources/SentryCrash/Recording/SentryCrashReportWriter.h b/Sources/SentryCrash/Recording/SentryCrashReportWriter.h index 4febb0bf91f..2cacc25add5 100644 --- a/Sources/SentryCrash/Recording/SentryCrashReportWriter.h +++ b/Sources/SentryCrash/Recording/SentryCrashReportWriter.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashReportWriter.h // diff --git a/Sources/SentryCrash/Recording/SentryCrashSystemCapabilities.h b/Sources/SentryCrash/Recording/SentryCrashSystemCapabilities.h index 16bf1d83e43..aeae47c3c19 100644 --- a/Sources/SentryCrash/Recording/SentryCrashSystemCapabilities.h +++ b/Sources/SentryCrash/Recording/SentryCrashSystemCapabilities.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashSystemCapabilities.h // diff --git a/Sources/SentryCrash/Recording/Tools/NSError+SentrySimpleConstructor.h b/Sources/SentryCrash/Recording/Tools/NSError+SentrySimpleConstructor.h index bd3c5c5b180..ff6ac7808c5 100644 --- a/Sources/SentryCrash/Recording/Tools/NSError+SentrySimpleConstructor.h +++ b/Sources/SentryCrash/Recording/Tools/NSError+SentrySimpleConstructor.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // NSError+SimpleConstructor.h // diff --git a/Sources/SentryCrash/Recording/Tools/NSError+SentrySimpleConstructor.m b/Sources/SentryCrash/Recording/Tools/NSError+SentrySimpleConstructor.m index 37feed23691..e9b6c6144d3 100644 --- a/Sources/SentryCrash/Recording/Tools/NSError+SentrySimpleConstructor.m +++ b/Sources/SentryCrash/Recording/Tools/NSError+SentrySimpleConstructor.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // NSError+SimpleConstructor.m // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashCPU.c b/Sources/SentryCrash/Recording/Tools/SentryCrashCPU.c index cd4d147ffc5..52201766d04 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashCPU.c +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashCPU.c @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashCPU.h // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashCPU.h b/Sources/SentryCrash/Recording/Tools/SentryCrashCPU.h index 982006f3b50..1ec4a85d3b6 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashCPU.h +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashCPU.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashCPU.h // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashCPU_Apple.h b/Sources/SentryCrash/Recording/Tools/SentryCrashCPU_Apple.h index 42cd748a15f..dddb2911635 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashCPU_Apple.h +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashCPU_Apple.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashCPU_Apple.h // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashCPU_arm.c b/Sources/SentryCrash/Recording/Tools/SentryCrashCPU_arm.c index 90bb24551b7..3a4a71878e5 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashCPU_arm.c +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashCPU_arm.c @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashCPU_arm.c // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashCPU_arm64.c b/Sources/SentryCrash/Recording/Tools/SentryCrashCPU_arm64.c index 4cd9939b248..d244494ddfb 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashCPU_arm64.c +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashCPU_arm64.c @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashCPU_arm64_Apple.c // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashCPU_x86_32.c b/Sources/SentryCrash/Recording/Tools/SentryCrashCPU_x86_32.c index 36acd112d89..c95ac6275fa 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashCPU_x86_32.c +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashCPU_x86_32.c @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashCPU_x86_32.c // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashCPU_x86_64.c b/Sources/SentryCrash/Recording/Tools/SentryCrashCPU_x86_64.c index 27f1e62cba5..aaca4b24b07 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashCPU_x86_64.c +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashCPU_x86_64.c @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashCPU_x86_64.c // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashDebug.c b/Sources/SentryCrash/Recording/Tools/SentryCrashDebug.c index ef956a4a4c6..649f4e350cc 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashDebug.c +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashDebug.c @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashDebug.c // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashDebug.h b/Sources/SentryCrash/Recording/Tools/SentryCrashDebug.h index c4aeaa26e15..f29c6e634cb 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashDebug.h +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashDebug.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashDebug.h // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashDynamicLinker.c b/Sources/SentryCrash/Recording/Tools/SentryCrashDynamicLinker.c index 573d22a8e9f..4706b6de389 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashDynamicLinker.c +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashDynamicLinker.c @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashDynamicLinker.c // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashDynamicLinker.h b/Sources/SentryCrash/Recording/Tools/SentryCrashDynamicLinker.h index 185d92e144b..7f7bfda8432 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashDynamicLinker.h +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashDynamicLinker.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashDynamicLinker.h // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashFileUtils.c b/Sources/SentryCrash/Recording/Tools/SentryCrashFileUtils.c index 500b7300d29..0380419b773 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashFileUtils.c +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashFileUtils.c @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashFileUtils.c // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashFileUtils.h b/Sources/SentryCrash/Recording/Tools/SentryCrashFileUtils.h index 1218e645c45..596c48b0c15 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashFileUtils.h +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashFileUtils.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashFileUtils.h // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashID.c b/Sources/SentryCrash/Recording/Tools/SentryCrashID.c index be519cb5f60..5f98467c500 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashID.c +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashID.c @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashID.c // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashID.h b/Sources/SentryCrash/Recording/Tools/SentryCrashID.h index bce2d5cd2ce..3ca996f9f76 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashID.h +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashID.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashID.h // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashJSONCodec.c b/Sources/SentryCrash/Recording/Tools/SentryCrashJSONCodec.c index 450047c8a26..53d4c38be5e 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashJSONCodec.c +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashJSONCodec.c @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashJSONCodec.c // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashJSONCodec.h b/Sources/SentryCrash/Recording/Tools/SentryCrashJSONCodec.h index 6cda5ad513a..035fad33454 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashJSONCodec.h +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashJSONCodec.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashJSONCodec.h // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashJSONCodecObjC.h b/Sources/SentryCrash/Recording/Tools/SentryCrashJSONCodecObjC.h index ebfa2d4929f..c8fb49ae598 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashJSONCodecObjC.h +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashJSONCodecObjC.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashJSONCodecObjC.h // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashJSONCodecObjC.m b/Sources/SentryCrash/Recording/Tools/SentryCrashJSONCodecObjC.m index 0e81e382995..e9d935bc7b1 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashJSONCodecObjC.m +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashJSONCodecObjC.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashJSONCodecObjC.m // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashLogger.c b/Sources/SentryCrash/Recording/Tools/SentryCrashLogger.c index b6edb9b5496..0b921437107 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashLogger.c +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashLogger.c @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashLogger.c // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashLogger.h b/Sources/SentryCrash/Recording/Tools/SentryCrashLogger.h index 5d0427cb173..61bfbb4b48e 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashLogger.h +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashLogger.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashLogger.h // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashMachineContext.c b/Sources/SentryCrash/Recording/Tools/SentryCrashMachineContext.c index 447f4194199..7fb4063bf5f 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashMachineContext.c +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashMachineContext.c @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashMachineContext.c // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashMachineContext.h b/Sources/SentryCrash/Recording/Tools/SentryCrashMachineContext.h index fb494eeff25..6e3cb9e1f98 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashMachineContext.h +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashMachineContext.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashMachineContext.h // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashMachineContext_Apple.h b/Sources/SentryCrash/Recording/Tools/SentryCrashMachineContext_Apple.h index 15c2d057512..fcf1cde44a2 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashMachineContext_Apple.h +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashMachineContext_Apple.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashMachineContext_Apple.h // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashMemory.c b/Sources/SentryCrash/Recording/Tools/SentryCrashMemory.c index 5a64d1ce3c2..763a9b1fe29 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashMemory.c +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashMemory.c @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashMemory.c // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashMemory.h b/Sources/SentryCrash/Recording/Tools/SentryCrashMemory.h index b9c9a5d6fed..06cb2a9870d 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashMemory.h +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashMemory.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashMemory.h // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashObjC.c b/Sources/SentryCrash/Recording/Tools/SentryCrashObjC.c index f5017a9951b..ee1ef1ffdd6 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashObjC.c +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashObjC.c @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashObjC.c // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashObjC.h b/Sources/SentryCrash/Recording/Tools/SentryCrashObjC.h index 70d5f58218b..2374029db38 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashObjC.h +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashObjC.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashObjC.h // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashSignalInfo.c b/Sources/SentryCrash/Recording/Tools/SentryCrashSignalInfo.c index c8c5069fce6..8d27ed32597 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashSignalInfo.c +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashSignalInfo.c @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashSignalInfo.c // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashSignalInfo.h b/Sources/SentryCrash/Recording/Tools/SentryCrashSignalInfo.h index 5965a1b0cb0..63ac46678e7 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashSignalInfo.h +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashSignalInfo.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashSignalInfo.h // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashStackCursor.c b/Sources/SentryCrash/Recording/Tools/SentryCrashStackCursor.c index 409ab9ed575..b4d79d572a3 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashStackCursor.c +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashStackCursor.c @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashStackCursor.h // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashStackCursor.h b/Sources/SentryCrash/Recording/Tools/SentryCrashStackCursor.h index 3a05a64ad62..f2bac8c3d87 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashStackCursor.h +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashStackCursor.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashStackCursor.h // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashStackCursor_Backtrace.c b/Sources/SentryCrash/Recording/Tools/SentryCrashStackCursor_Backtrace.c index b8b349ccacd..34057a64432 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashStackCursor_Backtrace.c +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashStackCursor_Backtrace.c @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashStackCursor_Backtrace.c // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashStackCursor_Backtrace.h b/Sources/SentryCrash/Recording/Tools/SentryCrashStackCursor_Backtrace.h index 1110e9e47df..0b60ebb76d9 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashStackCursor_Backtrace.h +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashStackCursor_Backtrace.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashStackCursor_Backtrace.h // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashStackCursor_MachineContext.c b/Sources/SentryCrash/Recording/Tools/SentryCrashStackCursor_MachineContext.c index 768c5225eb1..77bb3749564 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashStackCursor_MachineContext.c +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashStackCursor_MachineContext.c @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashStackCursor_MachineContext.c // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashStackCursor_MachineContext.h b/Sources/SentryCrash/Recording/Tools/SentryCrashStackCursor_MachineContext.h index abd597c55d7..a76013b360f 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashStackCursor_MachineContext.h +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashStackCursor_MachineContext.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashStackCursor_MachineContext.h // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashStackCursor_SelfThread.c b/Sources/SentryCrash/Recording/Tools/SentryCrashStackCursor_SelfThread.c index 3afd3fd7e93..68ae0a401fe 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashStackCursor_SelfThread.c +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashStackCursor_SelfThread.c @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashStackCursor_SelfThread.c // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashStackCursor_SelfThread.h b/Sources/SentryCrash/Recording/Tools/SentryCrashStackCursor_SelfThread.h index c5cc261eeae..46b4d679bca 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashStackCursor_SelfThread.h +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashStackCursor_SelfThread.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashStackCursor_SelfThread.h // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashString.c b/Sources/SentryCrash/Recording/Tools/SentryCrashString.c index d46e3355b83..3252ffe4ed0 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashString.c +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashString.c @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashString.m // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashString.h b/Sources/SentryCrash/Recording/Tools/SentryCrashString.h index 426c9cbdf8e..cd5b2731459 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashString.h +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashString.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashString.h // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashSymbolicator.c b/Sources/SentryCrash/Recording/Tools/SentryCrashSymbolicator.c index d57237551f3..0f151a13158 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashSymbolicator.c +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashSymbolicator.c @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashSymbolicator.c // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashSymbolicator.h b/Sources/SentryCrash/Recording/Tools/SentryCrashSymbolicator.h index a57d54f1ed8..915cdddbe86 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashSymbolicator.h +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashSymbolicator.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashSymbolicator.h // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashSysCtl.c b/Sources/SentryCrash/Recording/Tools/SentryCrashSysCtl.c index 989ec2728bd..7a6265837f4 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashSysCtl.c +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashSysCtl.c @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashSysCtl.m // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashSysCtl.h b/Sources/SentryCrash/Recording/Tools/SentryCrashSysCtl.h index 6e699de6be1..faf3398845c 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashSysCtl.h +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashSysCtl.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashSysCtl.h // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashThread.c b/Sources/SentryCrash/Recording/Tools/SentryCrashThread.c index 3472673f28d..db829421433 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashThread.c +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashThread.c @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashThread.c // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashThread.h b/Sources/SentryCrash/Recording/Tools/SentryCrashThread.h index 656d12c378e..7244aeb297c 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashThread.h +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashThread.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashThread.h // diff --git a/Sources/SentryCrash/Reporting/Filters/SentryCrashReportFilter.h b/Sources/SentryCrash/Reporting/Filters/SentryCrashReportFilter.h index 9934ff4fb16..de106c9f054 100644 --- a/Sources/SentryCrash/Reporting/Filters/SentryCrashReportFilter.h +++ b/Sources/SentryCrash/Reporting/Filters/SentryCrashReportFilter.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashReportFilter.h // diff --git a/Sources/SentryCrash/Reporting/Filters/SentryCrashReportFilterBasic.h b/Sources/SentryCrash/Reporting/Filters/SentryCrashReportFilterBasic.h index c3626342b6a..e9ccd5aaf9d 100644 --- a/Sources/SentryCrash/Reporting/Filters/SentryCrashReportFilterBasic.h +++ b/Sources/SentryCrash/Reporting/Filters/SentryCrashReportFilterBasic.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashReportFilterBasic.h // diff --git a/Sources/SentryCrash/Reporting/Filters/SentryCrashReportFilterBasic.m b/Sources/SentryCrash/Reporting/Filters/SentryCrashReportFilterBasic.m index 17c7c66a625..37f738aa560 100644 --- a/Sources/SentryCrash/Reporting/Filters/SentryCrashReportFilterBasic.m +++ b/Sources/SentryCrash/Reporting/Filters/SentryCrashReportFilterBasic.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashReportFilterBasic.m // diff --git a/Sources/SentryCrash/Reporting/Filters/Tools/Container+SentryDeepSearch.h b/Sources/SentryCrash/Reporting/Filters/Tools/Container+SentryDeepSearch.h index c7948fbdcf4..f822d1e877b 100644 --- a/Sources/SentryCrash/Reporting/Filters/Tools/Container+SentryDeepSearch.h +++ b/Sources/SentryCrash/Reporting/Filters/Tools/Container+SentryDeepSearch.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // Container+DeepSearch // diff --git a/Sources/SentryCrash/Reporting/Filters/Tools/Container+SentryDeepSearch.m b/Sources/SentryCrash/Reporting/Filters/Tools/Container+SentryDeepSearch.m index 881a0dc9b55..1f025de48ce 100644 --- a/Sources/SentryCrash/Reporting/Filters/Tools/Container+SentryDeepSearch.m +++ b/Sources/SentryCrash/Reporting/Filters/Tools/Container+SentryDeepSearch.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // Container+DeepSearch // diff --git a/Sources/SentryCrash/Reporting/Filters/Tools/SentryCrashVarArgs.h b/Sources/SentryCrash/Reporting/Filters/Tools/SentryCrashVarArgs.h index cb6e5f7fd23..722d9c5de67 100644 --- a/Sources/SentryCrash/Reporting/Filters/Tools/SentryCrashVarArgs.h +++ b/Sources/SentryCrash/Reporting/Filters/Tools/SentryCrashVarArgs.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashVarArgs.h // @@ -70,26 +71,3 @@ typedef void (^SentryCrashVA_Block)(id entry); #define sentrycrashva_list_to_nsarray(FIRST_ARG_NAME, ARRAY_NAME) \ NSMutableArray *ARRAY_NAME = [NSMutableArray array]; \ sentrycrashva_iterate_list(FIRST_ARG_NAME, ^(id entry) { [ARRAY_NAME addObject:entry]; }) - -/** - * Convert a variable argument list into a dictionary, interpreting the vararg - * list as object, key, object, key, ... - * An autoreleased NSMutableDictionary will be created in the current scope with - * the specified name. - * - * @param FIRST_ARG_NAME The name of the first argument in the vararg list. - * @param DICT_NAME The name of the dictionary to create in the current scope. - */ -#define sentrycrashva_list_to_nsdictionary(FIRST_ARG_NAME, DICT_NAME) \ - NSMutableDictionary *DICT_NAME = [NSMutableDictionary dictionary]; \ - { \ - __block id sentrycrashva_object = nil; \ - sentrycrashva_iterate_list(FIRST_ARG_NAME, ^(id entry) { \ - if (sentrycrashva_object == nil) { \ - sentrycrashva_object = entry; \ - } else { \ - [DICT_NAME setObject:sentrycrashva_object forKey:entry]; \ - sentrycrashva_object = nil; \ - } \ - }); \ - } diff --git a/Sources/SentryCrash/Reporting/Tools/SentryCrashCString.h b/Sources/SentryCrash/Reporting/Tools/SentryCrashCString.h index e5d8da87e92..dec6c52f429 100644 --- a/Sources/SentryCrash/Reporting/Tools/SentryCrashCString.h +++ b/Sources/SentryCrash/Reporting/Tools/SentryCrashCString.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashCString.h // diff --git a/Sources/SentryCrash/Reporting/Tools/SentryCrashCString.m b/Sources/SentryCrash/Reporting/Tools/SentryCrashCString.m index af11161cffa..d41aec28810 100644 --- a/Sources/SentryCrash/Reporting/Tools/SentryCrashCString.m +++ b/Sources/SentryCrash/Reporting/Tools/SentryCrashCString.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashCString.m // diff --git a/Tests/SentryProfilerTests/SentryProfilerSwiftTests.swift b/Tests/SentryProfilerTests/SentryProfilerSwiftTests.swift index 7c579ceba8b..2144d86211b 100644 --- a/Tests/SentryProfilerTests/SentryProfilerSwiftTests.swift +++ b/Tests/SentryProfilerTests/SentryProfilerSwiftTests.swift @@ -27,21 +27,43 @@ class SentryProfilerSwiftTests: XCTestCase { lazy var systemWrapper = TestSentrySystemWrapper() lazy var processInfoWrapper = TestSentryNSProcessInfoWrapper() - lazy var timerWrapper = TestSentryNSTimerWrapper() + lazy var metricTimerWrapper = TestSentryNSTimerWrapper() + lazy var timeoutTimerWrapper = TestSentryNSTimerWrapper() let currentDateProvider = TestCurrentDateProvider() #if !os(macOS) - lazy var displayLinkWrapper = TestDisplayLinkWrapper() + lazy var displayLinkWrapper = TestDisplayLinkWrapper(dateProvider: currentDateProvider) lazy var framesTracker = SentryFramesTracker(displayLinkWrapper: displayLinkWrapper) #endif - func newTransaction(testingAppLaunchSpans: Bool = false) -> Span { - currentDateProvider.setDate(date: currentDateProvider.date().addingTimeInterval(2)) + init() { + CurrentDate.setCurrentDateProvider(currentDateProvider) + options.profilesSampleRate = 1.0 + options.tracesSampleRate = 1.0 + + SentryProfiler.useSystemWrapper(systemWrapper) + SentryProfiler.useProcessInfoWrapper(processInfoWrapper) + SentryProfiler.useMetricTimerWrapper(metricTimerWrapper) + SentryProfiler.useTimeoutTimerWrapper(timeoutTimerWrapper) + + systemWrapper.overrides.cpuUsagePerCore = mockCPUUsages.map { NSNumber(value: $0) } + processInfoWrapper.overrides.processorCount = UInt(mockCPUUsages.count) + systemWrapper.overrides.memoryFootprintBytes = mockMemoryFootprint + +#if !os(macOS) + SentryProfiler.useFramesTracker(framesTracker) + framesTracker.start() + displayLinkWrapper.call() +#endif + } + + /// Advance the mock date provider, start a new transaction and return its handle. + func newTransaction(testingAppLaunchSpans: Bool = false) throws -> SentryTracer { if testingAppLaunchSpans { - return hub.startTransaction(name: transactionName, operation: SentrySpanOperationUILoad) + return try XCTUnwrap(hub.startTransaction(name: transactionName, operation: SentrySpanOperationUILoad) as? SentryTracer) } - return hub.startTransaction(name: transactionName, operation: transactionOperation) + return try XCTUnwrap(hub.startTransaction(name: transactionName, operation: transactionOperation) as? SentryTracer) } // mocking @@ -49,70 +71,115 @@ class SentryProfilerSwiftTests: XCTestCase { let mockCPUUsages = [12.4, 63.5, 1.4, 4.6] let mockMemoryFootprint: SentryRAMBytes = 123_455 let mockUsageReadingsPerBatch = 2 - let mockSlowFramesPerBatch = 2 - let mockFrozenFramesPerBatch = 1 +#if !os(macOS) // SentryFramesTracker starts assuming a frame rate of 60 Hz and will only log an update if it changes, so the first value here needs to be different for it to register. - let mockFrameRateChangesPerBatch: [Double] = [120.0, 60.0, 120.0, 60.0] - - func mockMetricsSubsystems() { - SentryProfiler.useSystemWrapper(systemWrapper) - SentryProfiler.useProcessInfoWrapper(processInfoWrapper) - SentryProfiler.useTimerWrapper(timerWrapper) - #if !os(macOS) - SentryProfiler.useFramesTracker(framesTracker) - #endif - } + let mockFrameRateChangesPerBatch: [FrameRate] = [.high, .low, .high, .low] +#endif - func prepareMetricsMocks() { - systemWrapper.overrides.cpuUsagePerCore = mockCPUUsages.map { NSNumber(value: $0) } - processInfoWrapper.overrides.processorCount = UInt(mockCPUUsages.count) +#if !os(macOS) + // Absolute timestamps must be adjusted per span when asserting + var expectedSlowFrames = [[String: Any]]() + var expectedFrozenFrames = [[String: Any]]() + var expectedFrameRateChanges = [[String: Any]]() - systemWrapper.overrides.memoryFootprintBytes = mockMemoryFootprint + func resetGPUExpectations() { + expectedSlowFrames = [[String: Any]]() + expectedFrozenFrames = [[String: Any]]() + expectedFrameRateChanges = [[String: Any]]() } +#endif - func gatherMockedMetrics() { + func gatherMockedMetrics(span: Span) throws { // clear out any errors that might've been set in previous calls systemWrapper.overrides.cpuUsageError = nil systemWrapper.overrides.memoryFootprintError = nil // gather mock cpu usages and memory footprints for _ in 0.. /// transaction A |---------------------------------------------------| /// profiler A |---------------------------x <- timeout /// transaction B |-------| /// profiler B |-------| <- normal finish /// ``` func testConcurrentSpansWithTimeout() throws { - let originalTimeoutInterval = kSentryProfilerTimeoutInterval - kSentryProfilerTimeoutInterval = 1 - - let spanA = fixture.newTransaction() - + let spanA = try fixture.newTransaction() + fixture.currentDateProvider.advanceBy(nanoseconds: 1.toNanoSeconds()) forceProfilerSample() + fixture.currentDateProvider.advanceBy(nanoseconds: 30.toNanoSeconds()) + fixture.timeoutTimerWrapper.fire() - // cause spanA profiler to time out - let exp = expectation(description: "spanA times out") - DispatchQueue.main.asyncAfter(deadline: .now() + 2) { - exp.fulfill() - } - waitForExpectations(timeout: 3) - - let spanB = fixture.newTransaction() + fixture.currentDateProvider.advanceBy(nanoseconds: 0.5.toNanoSeconds()) + let spanB = try fixture.newTransaction() + fixture.currentDateProvider.advanceBy(nanoseconds: 0.5.toNanoSeconds()) forceProfilerSample() spanB.finish() @@ -261,8 +292,6 @@ class SentryProfilerSwiftTests: XCTestCase { spanA.finish() try self.assertValidProfileData() - - kSentryProfilerTimeoutInterval = originalTimeoutInterval } func testProfileTimeoutTimer() throws { @@ -299,86 +328,69 @@ class SentryProfilerSwiftTests: XCTestCase { try performTest(transactionEnvironment: expectedEnvironment) } - func testStartTransaction_NotSamplingProfileUsingEnableProfiling() { - assertProfilesSampler(expectedDecision: .no) { options in + func testStartTransaction_NotSamplingProfileUsingEnableProfiling() throws { + try assertProfilesSampler(expectedDecision: .no) { options in options.enableProfiling_DEPRECATED_TEST_ONLY = false } } - func testStartTransaction_SamplingProfileUsingEnableProfiling() { - assertProfilesSampler(expectedDecision: .yes) { options in + func testStartTransaction_SamplingProfileUsingEnableProfiling() throws { + try assertProfilesSampler(expectedDecision: .yes) { options in options.enableProfiling_DEPRECATED_TEST_ONLY = true } } - func testStartTransaction_NotSamplingProfileUsingSampleRate() { - assertProfilesSampler(expectedDecision: .no) { options in + func testStartTransaction_NotSamplingProfileUsingSampleRate() throws { + try assertProfilesSampler(expectedDecision: .no) { options in options.profilesSampleRate = 0.49 } } - func testStartTransaction_SamplingProfileUsingSampleRate() { - assertProfilesSampler(expectedDecision: .yes) { options in + func testStartTransaction_SamplingProfileUsingSampleRate() throws { + try assertProfilesSampler(expectedDecision: .yes) { options in options.profilesSampleRate = 0.5 } } - func testStartTransaction_SamplingProfileUsingProfilesSampler() { - assertProfilesSampler(expectedDecision: .yes) { options in + func testStartTransaction_SamplingProfileUsingProfilesSampler() throws { + try assertProfilesSampler(expectedDecision: .yes) { options in options.profilesSampler = { _ in return 0.51 } } } - func testStartTransaction_WhenProfilesSampleRateAndProfilesSamplerNil() { - assertProfilesSampler(expectedDecision: .no) { options in + func testStartTransaction_WhenProfilesSampleRateAndProfilesSamplerNil() throws { + try assertProfilesSampler(expectedDecision: .no) { options in options.profilesSampleRate = nil options.profilesSampler = { _ in return nil } } } - func testStartTransaction_WhenProfilesSamplerOutOfRange_TooBig() { - assertProfilesSampler(expectedDecision: .no) { options in + func testStartTransaction_WhenProfilesSamplerOutOfRange_TooBig() throws { + try assertProfilesSampler(expectedDecision: .no) { options in options.profilesSampler = { _ in return 1.01 } } } - func testStartTransaction_WhenProfilesSamplersOutOfRange_TooSmall() { - assertProfilesSampler(expectedDecision: .no) { options in + func testStartTransaction_WhenProfilesSamplersOutOfRange_TooSmall() throws { + try assertProfilesSampler(expectedDecision: .no) { options in options.profilesSampler = { _ in return -0.01 } } } } private extension SentryProfilerSwiftTests { - enum TestError: Error { - case unexpectedProfileDeserializationType - case unexpectedMeasurementsDeserializationType - case noEnvelopeCaptured - case noProfileEnvelopeItem - case malformedMetricValueEntry - case noMetricsReported - case noMetricValuesFound - } - func getLatestProfileData() throws -> Data { - guard let envelope = try XCTUnwrap(self.fixture.client).captureEventWithScopeInvocations.last else { - throw(TestError.noEnvelopeCaptured) - } + let envelope = try XCTUnwrap(self.fixture.client?.captureEventWithScopeInvocations.last) XCTAssertEqual(1, envelope.additionalEnvelopeItems.count) - guard let profileItem = envelope.additionalEnvelopeItems.first else { - throw(TestError.noProfileEnvelopeItem) - } + let profileItem = try XCTUnwrap(envelope.additionalEnvelopeItems.first) XCTAssertEqual("profile", profileItem.header.type) return profileItem.data } func getLatestTransaction() throws -> Transaction { - guard let envelope = try XCTUnwrap(self.fixture.client).captureEventWithScopeInvocations.last else { - throw(TestError.noEnvelopeCaptured) - } - + let envelope = try XCTUnwrap(self.fixture.client?.captureEventWithScopeInvocations.last) return try XCTUnwrap(envelope.event as? Transaction) } @@ -399,43 +411,23 @@ private extension SentryProfilerSwiftTests { SentrySDK.setAppStartMeasurement(appStartMeasurement) } - let originalTimeoutInterval = kSentryProfilerTimeoutInterval - if shouldTimeOut { - kSentryProfilerTimeoutInterval = 1 - } - let span = fixture.newTransaction(testingAppLaunchSpans: testingAppLaunchSpans) - + let span = try fixture.newTransaction(testingAppLaunchSpans: testingAppLaunchSpans) forceProfilerSample() - - let exp = expectation(description: "profiler should finish") + fixture.currentDateProvider.advance(by: 31) if shouldTimeOut { - DispatchQueue.main.asyncAfter(deadline: .now() + 2) { - span.finish() - exp.fulfill() - } - } else { - span.finish() - exp.fulfill() + fixture.timeoutTimerWrapper.fire() } - waitForExpectations(timeout: 10) + span.finish() try self.assertValidProfileData(transactionEnvironment: transactionEnvironment, shouldTimeout: shouldTimeOut) - - if shouldTimeOut { - kSentryProfilerTimeoutInterval = originalTimeoutInterval - } } func assertMetricsPayload(metricsBatches: Int = 1) throws { let profileData = try self.getLatestProfileData() let transaction = try getLatestTransaction() - guard let profile = try JSONSerialization.jsonObject(with: profileData) as? [String: Any] else { - throw TestError.unexpectedProfileDeserializationType - } - guard let measurements = profile["measurements"] as? [String: Any] else { - throw TestError.unexpectedMeasurementsDeserializationType - } + let profile = try XCTUnwrap(JSONSerialization.jsonObject(with: profileData) as? [String: Any]) + let measurements = try XCTUnwrap(profile["measurements"] as? [String: Any]) let expectedUsageReadings = fixture.mockUsageReadingsPerBatch * metricsBatches @@ -447,30 +439,57 @@ private extension SentryProfilerSwiftTests { try assertMetricValue(measurements: measurements, key: kSentryMetricProfilerSerializationKeyMemoryFootprint, numberOfReadings: expectedUsageReadings, expectedValue: fixture.mockMemoryFootprint, transaction: transaction) #if !os(macOS) - // can't elide the value argument, because assertMetricValue is generic and the parameter type won't be able to be inferred, so we provide a dummy variable/value - let dummyValue: UInt64? = nil - try assertMetricValue(measurements: measurements, key: kSentryProfilerSerializationKeySlowFrameRenders, numberOfReadings: fixture.mockSlowFramesPerBatch * metricsBatches, expectedValue: dummyValue, transaction: transaction) - try assertMetricValue(measurements: measurements, key: kSentryProfilerSerializationKeyFrozenFrameRenders, numberOfReadings: fixture.mockFrozenFramesPerBatch * metricsBatches, expectedValue: dummyValue, transaction: transaction) - - // need to add 1 frame rate reading because there is always an initial one for the frame rate when the frame tracker starts - let expectedFrameRateReadings = 1 + fixture.mockFrameRateChangesPerBatch.count * metricsBatches - try assertMetricValue(measurements: measurements, key: kSentryProfilerSerializationKeyFrameRates, numberOfReadings: expectedFrameRateReadings, expectedValue: dummyValue, transaction: transaction) + try assertMetricEntries(measurements: measurements, key: kSentryProfilerSerializationKeySlowFrameRenders, expectedEntries: fixture.expectedSlowFrames, transaction: transaction) + try assertMetricEntries(measurements: measurements, key: kSentryProfilerSerializationKeyFrozenFrameRenders, expectedEntries: fixture.expectedFrozenFrames, transaction: transaction) + try assertMetricEntries(measurements: measurements, key: kSentryProfilerSerializationKeyFrameRates, expectedEntries: fixture.expectedFrameRateChanges, transaction: transaction) #endif } - func assertMetricValue(measurements: [String: Any], key: String, numberOfReadings: Int, expectedValue: T?, transaction: Transaction) throws { - guard let metricContainer = measurements[key] as? [String: Any] else { - throw TestError.noMetricsReported + func sortedByTimestamps(_ entries: [[String: Any]]) -> [[String: Any]] { + entries.sorted { a, b in + UInt64(a["elapsed_since_start_ns"] as! String)! < UInt64(b["elapsed_since_start_ns"] as! String)! + } + } + + func printTimestamps(entries: [[String: Any]]) -> [NSString] { + entries.reduce(into: [NSString](), { partialResult, entry in + partialResult.append(entry["elapsed_since_start_ns"] as! NSString) + }) + } + + func assertMetricEntries(measurements: [String: Any], key: String, expectedEntries: [[String: Any]], transaction: Transaction) throws { + let metricContainer = try XCTUnwrap(measurements[key] as? [String: Any]) + let actualEntries = try XCTUnwrap(metricContainer["values"] as? [[String: Any]]) + let sortedActualEntries = sortedByTimestamps(actualEntries) + let sortedExpectedEntries = sortedByTimestamps(expectedEntries) + + guard actualEntries.count == expectedEntries.count else { + XCTFail("Wrong number of values under \(key). expected: \(printTimestamps(entries: sortedExpectedEntries)); actual: \(printTimestamps(entries: sortedActualEntries)); transaction start time: \(transaction.startSystemTime)") + return } - guard let values = metricContainer["values"] as? [[String: Any]] else { - throw TestError.malformedMetricValueEntry + + for i in 0..(measurements: [String: Any], key: String, numberOfReadings: Int, expectedValue: T? = nil, transaction: Transaction) throws { + let metricContainer = try XCTUnwrap(measurements[key] as? [String: Any]) + let values = try XCTUnwrap(metricContainer["values"] as? [[String: Any]]) XCTAssertEqual(values.count, numberOfReadings, "Wrong number of values under \(key)") if let expectedValue = expectedValue { - guard let actualValue = values[0]["value"] as? T else { - throw TestError.noMetricValuesFound - } + let actualValue = try XCTUnwrap(values[0]["value"] as? T) XCTAssertEqual(actualValue, expectedValue, "Wrong value for \(key)") let timestamp = try XCTUnwrap(values[0]["elapsed_since_start_ns"] as? NSString) @@ -596,7 +615,7 @@ private extension SentryProfilerSwiftTests { } } - func assertProfilesSampler(expectedDecision: SentrySampleDecision, options: (Options) -> Void) { + func assertProfilesSampler(expectedDecision: SentrySampleDecision, options: (Options) -> Void) throws { let fixtureOptions = fixture.options fixtureOptions.tracesSampleRate = 1.0 fixtureOptions.profilesSampleRate = nil @@ -607,7 +626,8 @@ private extension SentryProfilerSwiftTests { case .yes: return NSNumber(value: 1) @unknown default: - fatalError("Unexpected value for sample decision") + XCTFail("Unexpected value for sample decision") + return NSNumber(value: 0) } } options(fixtureOptions) @@ -615,37 +635,23 @@ private extension SentryProfilerSwiftTests { let hub = fixture.hub Dynamic(hub).tracesSampler.random = TestRandom(value: 1.0) - let span = fixture.newTransaction() - let exp = expectation(description: "Span finishes") - DispatchQueue.global().asyncAfter(deadline: .now() + 2.0) { - span.finish() - - guard let client = self.fixture.client else { - XCTFail("Expected a valid test client to exist") - return - } + let span = try fixture.newTransaction() + forceProfilerSample() + fixture.currentDateProvider.advance(by: 5) + span.finish() - switch expectedDecision { - case .undecided, .no: - guard let event = client.captureEventWithScopeInvocations.first else { - XCTFail("Expected to capture at least 1 event, but without a profile") - return - } - XCTAssertEqual(0, event.additionalEnvelopeItems.count) - case .yes: - guard let event = client.captureEventWithScopeInvocations.first else { - XCTFail("Expected to capture at least 1 event with a profile") - return - } - XCTAssertEqual(1, event.additionalEnvelopeItems.count) - @unknown default: - fatalError("Unexpected value for sample decision") - } + let client = try XCTUnwrap(self.fixture.client) - exp.fulfill() + switch expectedDecision { + case .undecided, .no: + let event = try XCTUnwrap(client.captureEventWithScopeInvocations.first) + XCTAssertEqual(0, event.additionalEnvelopeItems.count) + case .yes: + let event = try XCTUnwrap(client.captureEventWithScopeInvocations.first) + XCTAssertEqual(1, event.additionalEnvelopeItems.count) + @unknown default: + XCTFail("Unexpected value for sample decision") } - - waitForExpectations(timeout: 3) } } #endif // os(iOS) || os(macOS) || targetEnvironment(macCatalyst) diff --git a/Tests/SentryTests/Helper/SentryExtraContextProviderTests.swift b/Tests/SentryTests/Helper/SentryExtraContextProviderTests.swift new file mode 100644 index 00000000000..8c8267bd3a6 --- /dev/null +++ b/Tests/SentryTests/Helper/SentryExtraContextProviderTests.swift @@ -0,0 +1,67 @@ +import SentryTestUtils +import XCTest + +final class SentryExtraContextProviderTests: XCTestCase { + + private class Fixture { + let crashWrapper = TestSentryCrashWrapper.sharedInstance() + let deviceWrapper = TestSentryUIDeviceWrapper() + let processWrapper = TestSentryNSProcessInfoWrapper() + + func getSut() -> SentryExtraContextProvider { + return SentryExtraContextProvider( + crashWrapper: crashWrapper, + deviceWrapper: deviceWrapper, + processInfoWrapper: processWrapper) + } + } + + private var fixture: Fixture! + + override func setUp() { + super.setUp() + fixture = Fixture() + } + + func testExtraCrashInfo() throws { + let sut = fixture.getSut() + fixture.crashWrapper.internalFreeMemorySize = 123_456 + fixture.crashWrapper.internalAppMemorySize = 234_567 + fixture.crashWrapper.internalFreeStorageSize = 345_678 + + let actualContext = sut.getExtraContext() + let device = actualContext["device"] as? [String: Any] + let app = actualContext["app"] as? [String: Any] + + XCTAssertEqual(device?["free_memory"] as? UInt64, fixture.crashWrapper.internalFreeMemorySize) + XCTAssertEqual(app?["app_memory"] as? UInt64, fixture.crashWrapper.internalAppMemorySize) + XCTAssertEqual(device?["free_storage"] as? UInt64, fixture.crashWrapper.internalFreeStorageSize) + } + + func testExtraDeviceInfo() { +#if os(iOS) + let sut = fixture.getSut() + fixture.deviceWrapper.internalOrientation = .landscapeLeft + fixture.deviceWrapper.internalBatteryState = .full + fixture.deviceWrapper.internalBatteryLevel = 0.44 + + let actualContext = sut.getExtraContext() + let device = actualContext["device"] as? [String: Any] + + XCTAssertEqual(device?["orientation"] as? String, "landscape") + XCTAssertEqual(device?["charging"] as? Bool, false) + XCTAssertEqual(device?["battery_level"] as? UInt, 44) +#endif + } + + func testExtraProcessInfo() { + let sut = fixture.getSut() + fixture.processWrapper.overrides.processorCount = 12 + + let actualContext = sut.getExtraContext() + let device = actualContext["device"] as? [String: Any] + + XCTAssertEqual(device?["processor_count"] as? UInt, fixture.processWrapper.overrides.processorCount) + } + +} diff --git a/Tests/SentryTests/Helper/SentryFileManagerTests.swift b/Tests/SentryTests/Helper/SentryFileManagerTests.swift index 74c05d913ad..8f35cb14a72 100644 --- a/Tests/SentryTests/Helper/SentryFileManagerTests.swift +++ b/Tests/SentryTests/Helper/SentryFileManagerTests.swift @@ -375,21 +375,6 @@ class SentryFileManagerTests: XCTestCase { XCTAssertEqual(0, fixture.delegate.envelopeItemsDeleted.count) } - /** - * We need to deserialize every envelope and check if it contains a session. - */ - func testMigrateSessionInit_WorstCasePerformance() { - sut.store(fixture.sessionEnvelope) - sut.store(fixture.sessionUpdateEnvelope) - for _ in 0...(fixture.maxCacheItems - 3) { - sut.store(TestConstants.envelope) - } - - measure { - sut.store(TestConstants.envelope) - } - } - func testGetAllEnvelopesAreSortedByDateAscending() { givenMaximumEnvelopes() diff --git a/Tests/SentryTests/Helper/SentryProfiler+SwiftTest.h b/Tests/SentryTests/Helper/SentryProfiler+SwiftTest.h index 35d8aae8b25..9e050c8b14a 100644 --- a/Tests/SentryTests/Helper/SentryProfiler+SwiftTest.h +++ b/Tests/SentryTests/Helper/SentryProfiler+SwiftTest.h @@ -21,7 +21,11 @@ SentryProfiler () + (void)useProcessInfoWrapper:(SentryNSProcessInfoWrapper *)processInfoWrapper NS_SWIFT_NAME(useProcessInfoWrapper(_:)); -+ (void)useTimerWrapper:(SentryNSTimerWrapper *)timerWrapper NS_SWIFT_NAME(useTimerWrapper(_:)); ++ (void)useMetricTimerWrapper:(SentryNSTimerWrapper *)timerWrapper + NS_SWIFT_NAME(useMetricTimerWrapper(_:)); + ++ (void)useTimeoutTimerWrapper:(SentryNSTimerWrapper *)timerWrapper + NS_SWIFT_NAME(useTimeoutTimerWrapper(_:)); # if SENTRY_HAS_UIKIT + (void)useFramesTracker:(SentryFramesTracker *)framesTracker NS_SWIFT_NAME(useFramesTracker(_:)); diff --git a/Tests/SentryTests/Helper/SentrySerializationTests.swift b/Tests/SentryTests/Helper/SentrySerializationTests.swift index edba69ed866..85b02eab7db 100644 --- a/Tests/SentryTests/Helper/SentrySerializationTests.swift +++ b/Tests/SentryTests/Helper/SentrySerializationTests.swift @@ -12,18 +12,7 @@ class SentrySerializationTests: XCTestCase { "valid object": "hi, i'm a valid object", "invalid object": NSDate() ] - - var data: Data? - let exp = expectation(description: "Should throw error") - do { - data = try SentrySerialization.data(withJSONObject: json) - } catch { - exp.fulfill() - //Depending of the iOS version, the underlying type of NSDate may change. - //Knowing that we have an error is enough. - XCTAssertTrue(error.localizedDescription.starts(with: "Event cannot be converted to JSON (Invalid type in JSON write")) - } - waitForExpectations(timeout: 1) + let data = SentrySerialization.data(withJSONObject: json) XCTAssertNil(data) } @@ -198,9 +187,9 @@ class SentrySerializationTests: XCTestCase { let dict = SentrySession(releaseName: "1.0.0").serialize() let session = SentrySession(jsonObject: dict)! - let data = try SentrySerialization.data(with: session) + let data = SentrySerialization.data(with: session) - XCTAssertNotNil(SentrySerialization.session(with: data)) + XCTAssertNotNil(SentrySerialization.session(with: data!)) } func testSerializeSessionWithNoReleaseName() throws { @@ -208,7 +197,7 @@ class SentrySerializationTests: XCTestCase { dict["attrs"] = nil // Remove release name let session = SentrySession(jsonObject: dict)! - let data = try SentrySerialization.data(with: session) + let data = SentrySerialization.data(with: session)! XCTAssertNil(SentrySerialization.session(with: data)) } @@ -217,7 +206,7 @@ class SentrySerializationTests: XCTestCase { let dict = SentrySession(releaseName: "").serialize() let session = SentrySession(jsonObject: dict)! - let data = try SentrySerialization.data(with: session) + let data = SentrySerialization.data(with: session)! XCTAssertNil(SentrySerialization.session(with: data)) } @@ -225,7 +214,7 @@ class SentrySerializationTests: XCTestCase { func testSerializeSessionWithGarbageInDict() throws { var dict = SentrySession(releaseName: "").serialize() dict["started"] = "20" - let data = try SentrySerialization.data(withJSONObject: dict) + let data = SentrySerialization.data(withJSONObject: dict)! XCTAssertNil(SentrySerialization.session(with: data)) } @@ -252,7 +241,7 @@ class SentrySerializationTests: XCTestCase { func testAppStateWithValidData_ReturnsValidAppState() throws { let appState = TestData.appState - let appStateData = try SentrySerialization.data(withJSONObject: appState.serialize()) + let appStateData = SentrySerialization.data(withJSONObject: appState.serialize())! let actual = SentrySerialization.appState(with: appStateData) diff --git a/Tests/SentryTests/Helper/UrlSessionDelegateSpy.swift b/Tests/SentryTests/Helper/UrlSessionDelegateSpy.swift index dc71b34cfc3..5c4aabf2b75 100644 --- a/Tests/SentryTests/Helper/UrlSessionDelegateSpy.swift +++ b/Tests/SentryTests/Helper/UrlSessionDelegateSpy.swift @@ -5,5 +5,13 @@ class UrlSessionDelegateSpy: NSObject, URLSessionDelegate { func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) { delegateCallback() + + /* + Fixes error in tests: + + 2023-04-06 23:47:38.040259-0800 xctest[76215:8787183] [API] API MISUSE: NSURLSession delegate SentryTests.UrlSessionDelegateSpy: (0x12b124d90) + 2023-04-06 23:47:38.040521-0800 xctest[76215:8787183] [API] API MISUSE: didReceiveChallenge:completionHandler: completion handler not called + */ + completionHandler(.performDefaultHandling, nil) } } diff --git a/Tests/SentryTests/Integrations/Breadcrumbs/SentryAutoBreadcrumbTrackingIntegrationTests.swift b/Tests/SentryTests/Integrations/Breadcrumbs/SentryAutoBreadcrumbTrackingIntegrationTests.swift index 1fd70d82f2f..e86d79ebd42 100644 --- a/Tests/SentryTests/Integrations/Breadcrumbs/SentryAutoBreadcrumbTrackingIntegrationTests.swift +++ b/Tests/SentryTests/Integrations/Breadcrumbs/SentryAutoBreadcrumbTrackingIntegrationTests.swift @@ -93,9 +93,9 @@ class SentryAutoBreadcrumbTrackingIntegrationTests: XCTestCase { private class SentryTestBreadcrumbTracker: SentryBreadcrumbTracker { - let startInvocations = Invocations() - override func start() { - startInvocations.record(Void()) + let startInvocations = Invocations() + override func start(with delegate: SentryBreadcrumbDelegate) { + startInvocations.record(delegate) } let startSwizzleInvocations = Invocations() @@ -107,8 +107,8 @@ private class SentryTestBreadcrumbTracker: SentryBreadcrumbTracker { private class SentryTestSystemEventBreadcrumbs: SentrySystemEventBreadcrumbs { - let startWithdelegateInvocations = Invocations() - override func start(with delegate: SentrySystemEventBreadcrumbsDelegate) { + let startWithdelegateInvocations = Invocations() + override func start(with delegate: SentryBreadcrumbDelegate) { startWithdelegateInvocations.record(delegate) } } diff --git a/Tests/SentryTests/Integrations/Breadcrumbs/SentryBreadcrumbTestDelegate.swift b/Tests/SentryTests/Integrations/Breadcrumbs/SentryBreadcrumbTestDelegate.swift new file mode 100644 index 00000000000..50325fa2834 --- /dev/null +++ b/Tests/SentryTests/Integrations/Breadcrumbs/SentryBreadcrumbTestDelegate.swift @@ -0,0 +1,10 @@ +import Foundation +import SentryTestUtils + +class SentryBreadcrumbTestDelegate: NSObject, SentryBreadcrumbDelegate { + + var addCrumbInvocations = Invocations() + func add(_ crumb: Breadcrumb) { + addCrumbInvocations.record(crumb) + } +} diff --git a/Tests/SentryTests/Integrations/Breadcrumbs/SentryBreadcrumbTrackerTests.swift b/Tests/SentryTests/Integrations/Breadcrumbs/SentryBreadcrumbTrackerTests.swift index 7a383c989f4..ad16b700317 100644 --- a/Tests/SentryTests/Integrations/Breadcrumbs/SentryBreadcrumbTrackerTests.swift +++ b/Tests/SentryTests/Integrations/Breadcrumbs/SentryBreadcrumbTrackerTests.swift @@ -3,20 +3,17 @@ import XCTest class SentryBreadcrumbTrackerTests: XCTestCase { - private var scope: Scope! + private var delegate: SentryBreadcrumbTestDelegate! override func setUp() { super.setUp() - scope = Scope() - let client = TestClient(options: Options()) - let hub = TestHub(client: client, andScope: scope) - SentrySDK.setCurrentHub(hub) + delegate = SentryBreadcrumbTestDelegate() } override func tearDown() { super.tearDown() - clearTestState() + delegate = nil } #if os(iOS) || os(tvOS) || targetEnvironment(macCatalyst) @@ -25,7 +22,7 @@ class SentryBreadcrumbTrackerTests: XCTestCase { let swizzleWrapper = SentrySwizzleWrapper.sharedInstance let sut = SentryBreadcrumbTracker(swizzleWrapper: swizzleWrapper) - sut.start() + sut.start(with: delegate) sut.startSwizzle() sut.stop() @@ -34,8 +31,13 @@ class SentryBreadcrumbTrackerTests: XCTestCase { } func testSwizzlingStarted_ViewControllerAppears_AddsUILifeCycleBreadcrumb() { + let scope = Scope() + let client = TestClient(options: Options()) + let hub = TestHub(client: client, andScope: scope) + SentrySDK.setCurrentHub(hub) + let sut = SentryBreadcrumbTracker(swizzleWrapper: SentrySwizzleWrapper.sharedInstance) - sut.start() + sut.start(with: delegate) sut.startSwizzle() let viewController = UIViewController() @@ -45,9 +47,9 @@ class SentryBreadcrumbTrackerTests: XCTestCase { let crumbs = Dynamic(scope).breadcrumbArray.asArray as? [Breadcrumb] - XCTAssertEqual(2, crumbs?.count) + XCTAssertEqual(1, crumbs?.count) - let lifeCycleCrumb = crumbs?[1] + let lifeCycleCrumb = crumbs?[0] XCTAssertEqual("navigation", lifeCycleCrumb?.type) XCTAssertEqual("ui.lifecycle", lifeCycleCrumb?.category) XCTAssertEqual("false", lifeCycleCrumb?.data?["beingPresented"] as? String) @@ -55,6 +57,8 @@ class SentryBreadcrumbTrackerTests: XCTestCase { XCTAssertEqual("test title", lifeCycleCrumb?.data?["title"] as? String) XCTAssertEqual("false", lifeCycleCrumb?.data?["beingPresented"] as? String) XCTAssertEqual("UINavigationController", lifeCycleCrumb?.data?["parentViewController"] as? String) + + clearTestState() } func testExtractDataFrom_View() { diff --git a/Tests/SentryTests/Integrations/Breadcrumbs/SentrySystemEventBreadcrumbsTest.swift b/Tests/SentryTests/Integrations/Breadcrumbs/SentrySystemEventBreadcrumbsTest.swift index 1a185fcc009..99560b62f8e 100644 --- a/Tests/SentryTests/Integrations/Breadcrumbs/SentrySystemEventBreadcrumbsTest.swift +++ b/Tests/SentryTests/Integrations/Breadcrumbs/SentrySystemEventBreadcrumbsTest.swift @@ -9,7 +9,7 @@ class SentrySystemEventBreadcrumbsTest: XCTestCase { private class Fixture { let options: Options - let delegate = SentrySystemEventBreadcrumbTestDelegate() + let delegate = SentryBreadcrumbTestDelegate() let fileManager: TestFileManager var currentDateProvider = TestCurrentDateProvider() let notificationCenterWrapper = TestNSNotificationCenterWrapper() @@ -282,11 +282,3 @@ class SentrySystemEventBreadcrumbsTest: XCTestCase { #endif } - -class SentrySystemEventBreadcrumbTestDelegate: NSObject, SentrySystemEventBreadcrumbsDelegate { - - var addCrumbInvocations = Invocations() - func add(_ crumb: Breadcrumb) { - addCrumbInvocations.record(crumb) - } -} diff --git a/Tests/SentryTests/Integrations/Performance/AppStartTracking/SentryAppStartTrackingIntegrationTests.swift b/Tests/SentryTests/Integrations/Performance/AppStartTracking/SentryAppStartTrackingIntegrationTests.swift index 5a6f8f74883..04146f54c97 100644 --- a/Tests/SentryTests/Integrations/Performance/AppStartTracking/SentryAppStartTrackingIntegrationTests.swift +++ b/Tests/SentryTests/Integrations/Performance/AppStartTracking/SentryAppStartTrackingIntegrationTests.swift @@ -19,6 +19,11 @@ class SentryAppStartTrackingIntegrationTests: NotificationCenterTestCase { private var fixture: Fixture! private var sut: SentryAppStartTrackingIntegration! + + override class func setUp() { + super.setUp() + SentryLog.configure(true, diagnosticLevel: .debug) + } override func setUp() { super.setUp() @@ -35,26 +40,35 @@ class SentryAppStartTrackingIntegrationTests: NotificationCenterTestCase { sut.stop() } - func testAppStartMeasuringEnabledAndSampleRate_DoesUpdatesAppState() { + func testAppStartMeasuringEnabledAndSampleRate_properlySetupTracker() throws { sut.install(with: fixture.options) + + let tracker = try XCTUnwrap(Dynamic(sut).tracker.asObject as? SentryAppStartTracker, "SentryAppStartTrackingIntegration should have a tracker") + try assertTrackerSetupAndRunning(tracker) + } + + func testUnistall_stopsTracker() throws { + sut.install(with: fixture.options) + + let tracker = try XCTUnwrap(Dynamic(sut).tracker.asObject as? SentryAppStartTracker, "SentryAppStartTrackingIntegration should have a tracker") + try assertTrackerSetupAndRunning(tracker) + sut.uninstall() - uiWindowDidBecomeVisible() - - XCTAssertNotNil(SentrySDK.getAppStartMeasurement()) + let isRunning = Dynamic(tracker).isRunning.asBool ?? true + XCTAssertFalse(isRunning, "AppStartTracking should not be running") } - func testNoSampleRate_DoesNotUpdatesAppState() { + func testNoSampleRate_noTracker() { let options = fixture.options options.tracesSampleRate = 0.0 options.tracesSampler = nil sut.install(with: options) - - uiWindowDidBecomeVisible() - - XCTAssertNil(SentrySDK.getAppStartMeasurement()) + + let tracker = Dynamic(sut).tracker.asAnyObject as? SentryAppStartTracker + XCTAssertNil(tracker) } - func testHybridSDKModeEnabled_DoesUpdatesAppState() { + func testHybridSDKModeEnabled_properlySetupTracker() throws { PrivateSentrySDKOnly.appStartMeasurementHybridSDKMode = true let options = fixture.options @@ -62,30 +76,27 @@ class SentryAppStartTrackingIntegrationTests: NotificationCenterTestCase { options.tracesSampler = nil sut.install(with: options) - uiWindowDidBecomeVisible() - - XCTAssertNotNil(SentrySDK.getAppStartMeasurement()) + let tracker = try XCTUnwrap(Dynamic(sut).tracker.asObject as? SentryAppStartTracker, "SentryAppStartTrackingIntegration should have a tracker") + try assertTrackerSetupAndRunning(tracker) } - func testOnlyAppStartMeasuringEnabled_DoesNotUpdatesAppState() { + func testOnlyAppStartMeasuringEnabled_noTracker() { let options = fixture.options options.tracesSampleRate = 0.0 options.tracesSampler = nil sut.install(with: options) - uiWindowDidBecomeVisible() - - XCTAssertNil(SentrySDK.getAppStartMeasurement()) + let tracker = Dynamic(sut).tracker.asAnyObject as? SentryAppStartTracker + XCTAssertNil(tracker) } - func testAutoPerformanceTrackingDisabled_DoesNotUpdatesAppState() { + func testAutoPerformanceTrackingDisabled_noTracker() { let options = fixture.options options.enableAutoPerformanceTracing = false sut.install(with: options) - uiWindowDidBecomeVisible() - - XCTAssertNil(SentrySDK.getAppStartMeasurement()) + let tracker = Dynamic(sut).tracker.asAnyObject as? SentryAppStartTracker + XCTAssertNil(tracker) } func test_PerformanceTrackingDisabled() { @@ -95,6 +106,22 @@ class SentryAppStartTrackingIntegrationTests: NotificationCenterTestCase { XCTAssertFalse(result) } + + func assertTrackerSetupAndRunning(_ tracker: SentryAppStartTracker) throws { + let dateProvider = Dynamic(tracker).currentDate.asObject as? DefaultCurrentDateProvider + + XCTAssertEqual(dateProvider, DefaultCurrentDateProvider.sharedInstance()) + + _ = try XCTUnwrap(Dynamic(tracker).dispatchQueue.asAnyObject as? SentryDispatchQueueWrapper, "Tracker does not have a dispatch queue.") + + let appStateManager = Dynamic(tracker).appStateManager.asObject as? SentryAppStateManager + + XCTAssertEqual(appStateManager, SentryDependencyContainer.sharedInstance().appStateManager) + + _ = try XCTUnwrap(Dynamic(tracker).sysctl.asObject as? SentrySysctl, "Tracker does not have a Sysctl") + + XCTAssertTrue(tracker.isRunning, "AppStartTracking should be running") + } } #endif diff --git a/Tests/SentryTests/Integrations/Performance/CoreData/SentryCoreDataTrackerTest.swift b/Tests/SentryTests/Integrations/Performance/CoreData/SentryCoreDataTrackerTest.swift index f63ac81b84c..2236b2d310d 100644 --- a/Tests/SentryTests/Integrations/Performance/CoreData/SentryCoreDataTrackerTest.swift +++ b/Tests/SentryTests/Integrations/Performance/CoreData/SentryCoreDataTrackerTest.swift @@ -6,9 +6,18 @@ class SentryCoreDataTrackerTests: XCTestCase { private class Fixture { let context = TestNSManagedObjectContext() + let threadInspector = TestThreadInspector.instance + let imageProvider = TestDebugImageProvider() func getSut() -> SentryCoreDataTracker { - return SentryCoreDataTracker() + imageProvider.debugImages = [TestData.debugImage] + SentryDependencyContainer.sharedInstance().debugImageProvider = imageProvider + + threadInspector.allThreads = [TestData.thread2] + let processInfoWrapper = TestSentryNSProcessInfoWrapper() + processInfoWrapper.overrides.processDirectoryPath = "sentrytest" + + return SentryCoreDataTracker(threadInspector: threadInspector, processInfoWrapper: processInfoWrapper) } func testEntity() -> TestEntity { @@ -46,6 +55,17 @@ class SentryCoreDataTrackerTests: XCTestCase { let fetch = NSFetchRequest(entityName: "TestEntity") assertRequest(fetch, expectedDescription: "SELECT 'TestEntity'") } + + func testFetchRequestBackgroundThread() { + let expect = expectation(description: "Operation in background thread") + DispatchQueue.global(qos: .default).async { + let fetch = NSFetchRequest(entityName: "TestEntity") + self.assertRequest(fetch, expectedDescription: "SELECT 'TestEntity'", mainThread: false) + expect.fulfill() + } + + wait(for: [expect], timeout: 0.1) + } func test_FetchRequest_WithPredicate() { let fetch = NSFetchRequest(entityName: "TestEntity") @@ -82,6 +102,17 @@ class SentryCoreDataTrackerTests: XCTestCase { fixture.context.inserted = [fixture.testEntity()] assertSave("INSERTED 1 'TestEntity'") } + + func testSaveBackgroundThread() { + let expect = expectation(description: "Operation in background thread") + DispatchQueue.global(qos: .default).async { + self.fixture.context.inserted = [self.fixture.testEntity()] + self.assertSave("INSERTED 1 'TestEntity'", mainThread: false) + expect.fulfill() + } + + wait(for: [expect], timeout: 0.1) + } func test_Save_2Insert_1Entity() { fixture.context.inserted = [fixture.testEntity(), fixture.testEntity()] @@ -229,7 +260,7 @@ class SentryCoreDataTrackerTests: XCTestCase { XCTAssertEqual(transaction.children.count, 0) } - func assertSave(_ expectedDescription: String) { + func assertSave(_ expectedDescription: String, mainThread: Bool = true) { let sut = fixture.getSut() let transaction = startTransaction() @@ -237,13 +268,27 @@ class SentryCoreDataTrackerTests: XCTestCase { try? sut.saveManagedObjectContext(fixture.context) { _ in return true } - - XCTAssertEqual(transaction.children.count, 1) - XCTAssertEqual(transaction.children[0].operation, SENTRY_COREDATA_SAVE_OPERATION) - XCTAssertEqual(transaction.children[0].spanDescription, expectedDescription) + + guard let dbSpan = try? XCTUnwrap(transaction.children.first) else { + XCTFail("Span for DB operation don't exist.") + return + } + + XCTAssertEqual(dbSpan.operation, SENTRY_COREDATA_SAVE_OPERATION) + XCTAssertEqual(dbSpan.spanDescription, expectedDescription) + XCTAssertEqual(dbSpan.data["blocked_main_thread"] as? Bool ?? false, mainThread) + + if mainThread { + guard let frames = (dbSpan as? SentrySpan)?.frames else { + XCTFail("File IO Span in the main thread has no frames") + return + } + XCTAssertEqual(frames.first, TestData.mainFrame) + XCTAssertEqual(frames.last, TestData.testFrame) + } } - func assertRequest(_ fetch: NSFetchRequest, expectedDescription: String) { + func assertRequest(_ fetch: NSFetchRequest, expectedDescription: String, mainThread: Bool = true) { let transaction = startTransaction() let sut = fixture.getSut() @@ -254,12 +299,28 @@ class SentryCoreDataTrackerTests: XCTestCase { let result = try? sut.fetchManagedObjectContext(context, request: fetch) { _, _ in return [someEntity] } - + + guard let dbSpan = try? XCTUnwrap(transaction.children.first) else { + XCTFail("Span for DB operation don't exist.") + return + } + XCTAssertEqual(result?.count, 1) - XCTAssertEqual(transaction.children.count, 1) - XCTAssertEqual(transaction.children[0].operation, SENTRY_COREDATA_FETCH_OPERATION) - XCTAssertEqual(transaction.children[0].spanDescription, expectedDescription) - XCTAssertEqual(transaction.children[0].data["read_count"] as? Int, 1) + XCTAssertEqual(dbSpan.operation, SENTRY_COREDATA_FETCH_OPERATION) + XCTAssertEqual(dbSpan.spanDescription, expectedDescription) + XCTAssertEqual(dbSpan.data["read_count"] as? Int, 1) + XCTAssertEqual(dbSpan.data["blocked_main_thread"] as? Bool ?? false, mainThread) + + if mainThread { + guard let frames = (dbSpan as? SentrySpan)?.frames else { + XCTFail("File IO Span in the main thread has no frames") + return + } + XCTAssertEqual(frames.first, TestData.mainFrame) + XCTAssertEqual(frames.last, TestData.testFrame) + } else { + XCTAssertNil((dbSpan as? SentrySpan)?.frames) + } } private func startTransaction() -> SentryTracer { diff --git a/Tests/SentryTests/Integrations/Performance/FramesTracking/SentryFramesTracker+TestInit.h b/Tests/SentryTests/Integrations/Performance/FramesTracking/SentryFramesTracker+TestInit.h index 73078665b5b..682921307ba 100644 --- a/Tests/SentryTests/Integrations/Performance/FramesTracking/SentryFramesTracker+TestInit.h +++ b/Tests/SentryTests/Integrations/Performance/FramesTracking/SentryFramesTracker+TestInit.h @@ -1,8 +1,12 @@ +#import "SentryDefines.h" #import "SentryFramesTracker.h" NS_ASSUME_NONNULL_BEGIN #if SENTRY_HAS_UIKIT +SENTRY_EXTERN double slowFrameThreshold(uint64_t actualFramesPerSecond); +SENTRY_EXTERN CFTimeInterval const SentryFrozenFrameThreshold; + @interface SentryFramesTracker (TestInit) diff --git a/Tests/SentryTests/Integrations/Performance/FramesTracking/SentryFramesTrackerTests.swift b/Tests/SentryTests/Integrations/Performance/FramesTracking/SentryFramesTrackerTests.swift index 080d75e9746..24d51cf3aa0 100644 --- a/Tests/SentryTests/Integrations/Performance/FramesTracking/SentryFramesTrackerTests.swift +++ b/Tests/SentryTests/Integrations/Performance/FramesTracking/SentryFramesTrackerTests.swift @@ -46,9 +46,9 @@ class SentryFramesTrackerTests: XCTestCase { sut.start() fixture.displayLinkWrapper.call() - fixture.displayLinkWrapper.slowFrame() + _ = fixture.displayLinkWrapper.fastestSlowFrame() fixture.displayLinkWrapper.normalFrame() - fixture.displayLinkWrapper.almostFrozenFrame() + _ = fixture.displayLinkWrapper.slowestSlowFrame() try assert(slow: 2, frozen: 0, total: 3) } @@ -58,8 +58,8 @@ class SentryFramesTrackerTests: XCTestCase { sut.start() fixture.displayLinkWrapper.call() - fixture.displayLinkWrapper.slowFrame() - fixture.displayLinkWrapper.frozenFrame() + _ = fixture.displayLinkWrapper.fastestSlowFrame() + _ = fixture.displayLinkWrapper.fastestFrozenFrame() try assert(slow: 1, frozen: 1, total: 2) } @@ -69,9 +69,9 @@ class SentryFramesTrackerTests: XCTestCase { sut.start() fixture.displayLinkWrapper.call() - fixture.displayLinkWrapper.slowFrame() - fixture.displayLinkWrapper.changeFrameRate(120.0) - fixture.displayLinkWrapper.frozenFrame() + _ = fixture.displayLinkWrapper.fastestSlowFrame() + fixture.displayLinkWrapper.changeFrameRate(.high) + _ = fixture.displayLinkWrapper.fastestFrozenFrame() try assert(slow: 1, frozen: 1, total: 2, frameRates: 2) } @@ -92,8 +92,8 @@ class SentryFramesTrackerTests: XCTestCase { let frames: UInt = 600_000 for _ in 0 ..< frames { fixture.displayLinkWrapper.normalFrame() - fixture.displayLinkWrapper.slowFrame() - fixture.displayLinkWrapper.frozenFrame() + _ = fixture.displayLinkWrapper.fastestSlowFrame() + _ = fixture.displayLinkWrapper.fastestFrozenFrame() } group.wait() @@ -186,10 +186,9 @@ private extension SentryFramesTrackerTests { #if os(iOS) || os(macOS) || targetEnvironment(macCatalyst) func assertProfilingData(slow: UInt? = nil, frozen: UInt? = nil, frameRates: UInt? = nil) throws { - func assertStartAndEndOrdering(frame: [String: NSNumber]) throws { - let start = try XCTUnwrap(frame["start_timestamp"], "Expected a start timestamp for the frame.") - let end = try XCTUnwrap(frame["end_timestamp"], "Expected an end timestamp for the frame.") - XCTAssert(start.compare(end) != .orderedDescending) + func assertFrameInfo(frame: [String: NSNumber]) throws { + XCTAssertNotNil(frame["timestamp"], "Expected a timestamp for the frame.") + XCTAssertNotNil(frame["value"], "Expected a duration value for the frame.") } let currentFrames = fixture.sut.currentFrames @@ -197,13 +196,13 @@ private extension SentryFramesTrackerTests { if let slow = slow { XCTAssertEqual(currentFrames.slowFrameTimestamps.count, Int(slow)) for frame in currentFrames.slowFrameTimestamps { - try assertStartAndEndOrdering(frame: frame) + try assertFrameInfo(frame: frame) } } if let frozen = frozen { XCTAssertEqual(currentFrames.frozenFrameTimestamps.count, Int(frozen)) for frame in currentFrames.frozenFrameTimestamps { - try assertStartAndEndOrdering(frame: frame) + try assertFrameInfo(frame: frame) } } if let frameRates = frameRates { diff --git a/Tests/SentryTests/Integrations/Performance/IO/SentryFileIOTrackingIntegrationObjCTests.m b/Tests/SentryTests/Integrations/Performance/IO/SentryFileIOTrackingIntegrationObjCTests.m index faa185dcd2a..cd88dd47457 100644 --- a/Tests/SentryTests/Integrations/Performance/IO/SentryFileIOTrackingIntegrationObjCTests.m +++ b/Tests/SentryTests/Integrations/Performance/IO/SentryFileIOTrackingIntegrationObjCTests.m @@ -1,3 +1,4 @@ +#import "SentryByteCountFormatter.h" #import "SentryNSDataTracker.h" #import "SentryOptions.h" #import "SentrySDK.h" @@ -218,10 +219,9 @@ - (void)assertTransactionForOperation:(NSString *)operation block:(void (^)(void if ([operation isEqualToString:SENTRY_FILE_READ_OPERATION]) { XCTAssertEqualObjects(ioSpan.spanDescription, filename); } else { - NSString *expectedString = [NSString - stringWithFormat:@"%@ (%@)", filename, - [NSByteCountFormatter stringFromByteCount:someData.length - countStyle:NSByteCountFormatterCountStyleBinary]]; + NSString *expectedString = + [NSString stringWithFormat:@"%@ (%@)", filename, + [SentryByteCountFormatter bytesCountDescription:someData.length]]; XCTAssertEqualObjects(ioSpan.spanDescription, expectedString); } diff --git a/Tests/SentryTests/Integrations/Performance/IO/SentryNSDataTrackerTests.swift b/Tests/SentryTests/Integrations/Performance/IO/SentryNSDataTrackerTests.swift index 892f1ec6ebc..1702e725aad 100644 --- a/Tests/SentryTests/Integrations/Performance/IO/SentryNSDataTrackerTests.swift +++ b/Tests/SentryTests/Integrations/Performance/IO/SentryNSDataTrackerTests.swift @@ -316,7 +316,7 @@ class SentryNSDataTrackerTests: XCTestCase { if operation == SENTRY_FILE_READ_OPERATION { XCTAssertEqual(span?.spanDescription, lastComponent) } else { - let bytesDescription = ByteCountFormatter.string(fromByteCount: Int64(size), countStyle: .binary) + let bytesDescription = SentryByteCountFormatter.bytesCountDescription( UInt(size)) XCTAssertEqual(span?.spanDescription ?? "", "\(lastComponent) (\(bytesDescription))") } } diff --git a/Tests/SentryTests/Integrations/Performance/Network/SentryNetworkTrackerTests.swift b/Tests/SentryTests/Integrations/Performance/Network/SentryNetworkTrackerTests.swift index 8b24de0ada8..d567a7a42e1 100644 --- a/Tests/SentryTests/Integrations/Performance/Network/SentryNetworkTrackerTests.swift +++ b/Tests/SentryTests/Integrations/Performance/Network/SentryNetworkTrackerTests.swift @@ -155,7 +155,7 @@ class SentryNetworkTrackerTests: XCTestCase { let tracer = SentryTracer(transactionContext: TransactionContext(name: SentryNetworkTrackerTests.transactionName, operation: SentryNetworkTrackerTests.transactionOperation), hub: nil, - waitForChildren: true) + configuration: SentryTracerConfiguration(block: { $0.waitForChildren = true })) tracer.finish() @@ -172,8 +172,7 @@ class SentryNetworkTrackerTests: XCTestCase { let task = createDataTask() let tracer = SentryTracer(transactionContext: TransactionContext(name: SentryNetworkTrackerTests.transactionName, operation: SentryNetworkTrackerTests.transactionOperation), - hub: nil, - waitForChildren: true) + hub: nil, configuration: SentryTracerConfiguration(block: { $0.waitForChildren = true })) fixture.scope.span = tracer sut.urlSessionTaskResume(task) @@ -626,8 +625,6 @@ class SentryNetworkTrackerTests: XCTestCase { sut.urlSessionTask(task, setState: .completed) - fixture.hub.group.wait() - guard let envelope = self.fixture.hub.capturedEventsWithScopes.first else { XCTFail("Expected to capture 1 event") return @@ -657,8 +654,6 @@ class SentryNetworkTrackerTests: XCTestCase { sut.urlSessionTask(task, setState: .completed) - fixture.hub.group.wait() - guard let envelope = self.fixture.hub.capturedEventsWithScopes.first else { XCTFail("Expected to capture 1 event") return @@ -678,8 +673,6 @@ class SentryNetworkTrackerTests: XCTestCase { sut.urlSessionTask(task, setState: .completed) - fixture.hub.group.wait() - guard let envelope = self.fixture.hub.capturedEventsWithScopes.first else { XCTFail("Expected to capture 1 event") return diff --git a/Tests/SentryTests/Integrations/Performance/SentryPerformanceTrackerTests.swift b/Tests/SentryTests/Integrations/Performance/SentryPerformanceTrackerTests.swift index b4bda6d7347..f4753aa6b46 100644 --- a/Tests/SentryTests/Integrations/Performance/SentryPerformanceTrackerTests.swift +++ b/Tests/SentryTests/Integrations/Performance/SentryPerformanceTrackerTests.swift @@ -47,7 +47,7 @@ class SentryPerformanceTrackerTests: XCTestCase { let scopeSpan = fixture.scope.span XCTAssert(scopeSpan === transaction) - XCTAssertTrue(transaction.waitForChildren) + XCTAssertTrue(Dynamic(transaction).configuration.waitForChildren.asBool ?? false) XCTAssertEqual(transaction.transactionContext.name, fixture.someTransaction) XCTAssertEqual(transaction.transactionContext.nameSource, .custom) } @@ -155,7 +155,7 @@ class SentryPerformanceTrackerTests: XCTestCase { let expect = expectation(description: "Callback Expectation") - sut.measureSpan(withDescription: fixture.someTransaction, operation: fixture.someOperation) { + sut.measureSpan(withDescription: fixture.someTransaction, nameSource: .custom, operation: fixture.someOperation) { let spanId = sut.activeSpanId()! span = sut.getSpan(spanId) @@ -175,7 +175,7 @@ class SentryPerformanceTrackerTests: XCTestCase { let expect = expectation(description: "Callback Expectation") - sut.measureSpan(withDescription: fixture.someTransaction, operation: fixture.someOperation, parentSpanId: SpanId()) { + sut.measureSpan(withDescription: fixture.someTransaction, nameSource: .custom, operation: fixture.someOperation, parentSpanId: SpanId()) { expect.fulfill() } @@ -186,7 +186,7 @@ class SentryPerformanceTrackerTests: XCTestCase { func testNotSampled() { fixture.client.options.tracesSampleRate = 0 let sut = fixture.getSut() - let spanId = sut.startSpan(withName: fixture.someTransaction, operation: fixture.someOperation) + let spanId = sut.startSpan(withName: fixture.someTransaction, nameSource: .custom, operation: fixture.someOperation) let span = sut.getSpan(spanId) XCTAssertEqual(span!.sampled, .no) @@ -195,7 +195,7 @@ class SentryPerformanceTrackerTests: XCTestCase { func testSampled() { fixture.client.options.tracesSampleRate = 1 let sut = fixture.getSut() - let spanId = sut.startSpan(withName: fixture.someTransaction, operation: fixture.someOperation) + let spanId = sut.startSpan(withName: fixture.someTransaction, nameSource: .custom, operation: fixture.someOperation) let span = sut.getSpan(spanId) XCTAssertEqual(span!.sampled, .yes) @@ -273,7 +273,7 @@ class SentryPerformanceTrackerTests: XCTestCase { let activeSpans = Dynamic(sut).activeSpanStack as NSMutableArray? activeSpans?.add(TestSentrySpan()) - let spanId = sut.startSpan(withName: fixture.someTransaction, operation: fixture.someOperation) + let spanId = sut.startSpan(withName: fixture.someTransaction, nameSource: .custom, operation: fixture.someOperation) XCTAssertEqual(spanId, SpanId.empty) } @@ -342,7 +342,7 @@ class SentryPerformanceTrackerTests: XCTestCase { } private func startSpan(tracker: SentryPerformanceTracker) -> SpanId { - return tracker.startSpan(withName: fixture.someTransaction, operation: fixture.someOperation) + return tracker.startSpan(withName: fixture.someTransaction, nameSource: .custom, operation: fixture.someOperation) } } diff --git a/Tests/SentryTests/Integrations/Performance/UIViewController/SentryTimeToDisplayTrackerTest.swift b/Tests/SentryTests/Integrations/Performance/UIViewController/SentryTimeToDisplayTrackerTest.swift index 4c1d3673975..c7f8260d907 100644 --- a/Tests/SentryTests/Integrations/Performance/UIViewController/SentryTimeToDisplayTrackerTest.swift +++ b/Tests/SentryTests/Integrations/Performance/UIViewController/SentryTimeToDisplayTrackerTest.swift @@ -234,11 +234,14 @@ class SentryTimeToDisplayTrackerTest: XCTestCase { XCTAssertEqual(sut.fullDisplaySpan?.timestamp, sut.initialDisplaySpan?.timestamp) } - func testReportFullyDisplayed_afterFinishingTracer_withWaitForChildren() { + func testReportFullyDisplayed_afterFinishingTracer_withWaitForChildren() throws { fixture.dateProvider.setDate(date: Date(timeIntervalSince1970: 9)) - let hub = TestHub(client: SentryClient(options: Options()), andScope: nil) - let tracer = SentryTracer(transactionContext: TransactionContext(operation: "Test Operation"), hub: hub, waitForChildren: true) + let options = Options() + let hub = TestHub(client: SentryClient(options: options, fileManager: try TestFileManager(options: options), deleteOldEnvelopeItems: false), andScope: nil) + let tracer = SentryTracer(transactionContext: TransactionContext(operation: "Test Operation"), hub: hub, configuration: SentryTracerConfiguration(block: { config in + config.waitForChildren = true + })) let sut = fixture.getSut(for: UIViewController(), waitForFullDisplay: true) sut.start(for: tracer) diff --git a/Tests/SentryTests/Integrations/Performance/UIViewController/SentryUIViewControllerPerformanceTrackerTests.swift b/Tests/SentryTests/Integrations/Performance/UIViewController/SentryUIViewControllerPerformanceTrackerTests.swift index dc62f9ad7d1..919e6b76ed7 100644 --- a/Tests/SentryTests/Integrations/Performance/UIViewController/SentryUIViewControllerPerformanceTrackerTests.swift +++ b/Tests/SentryTests/Integrations/Performance/UIViewController/SentryUIViewControllerPerformanceTrackerTests.swift @@ -350,7 +350,7 @@ class SentryUIViewControllerPerformanceTrackerTests: XCTestCase { sut.viewControllerLoadView(viewController) { transactionSpan = self.getStack(tracker).first lastSpan = self.getStack(tracker).last - customSpanId = tracker.startSpan(withName: self.spanName, operation: self.spanOperation) + customSpanId = tracker.startSpan(withName: self.spanName, nameSource: .custom, operation: self.spanOperation) } let unwrappedLastSpan = try XCTUnwrap(lastSpan) XCTAssertTrue(unwrappedLastSpan.isFinished) diff --git a/Tests/SentryTests/Integrations/SentryCrash/SentryCrashIntegrationTests.swift b/Tests/SentryTests/Integrations/SentryCrash/SentryCrashIntegrationTests.swift index dc1f6a4f347..1ef469ae058 100644 --- a/Tests/SentryTests/Integrations/SentryCrash/SentryCrashIntegrationTests.swift +++ b/Tests/SentryTests/Integrations/SentryCrash/SentryCrashIntegrationTests.swift @@ -272,7 +272,7 @@ class SentryCrashIntegrationTests: NotificationCenterTestCase { api?.pointee.setEnabled(true) let transport = TestTransport() - let client = SentryClient(options: fixture.options) + let client = SentryClient(options: fixture.options, fileManager: try TestFileManager(options: fixture.options), deleteOldEnvelopeItems: false) Dynamic(client).transportAdapter = TestTransportAdapter(transport: transport, options: fixture.options) hub.bindClient(client) diff --git a/Tests/SentryTests/Integrations/Session/SentrySessionGeneratorTests.swift b/Tests/SentryTests/Integrations/Session/SentrySessionGeneratorTests.swift index a30afb2fb6b..340611c524b 100644 --- a/Tests/SentryTests/Integrations/Session/SentrySessionGeneratorTests.swift +++ b/Tests/SentryTests/Integrations/Session/SentrySessionGeneratorTests.swift @@ -38,7 +38,7 @@ class SentrySessionGeneratorTests: NotificationCenterTestCase { return name != "SentryAutoSessionTrackingIntegration" } - fileManager = try! SentryFileManager(options: options, andCurrentDateProvider: DefaultCurrentDateProvider.sharedInstance()) + fileManager = try! SentryFileManager(options: options, andCurrentDateProvider: DefaultCurrentDateProvider.sharedInstance(), dispatchQueueWrapper: TestSentryDispatchQueueWrapper()) fileManager.deleteCurrentSession() fileManager.deleteCrashedSession() diff --git a/Tests/SentryTests/Integrations/WatchdogTerminations/SentryWatchdogTerminationsIntegrationTests.swift b/Tests/SentryTests/Integrations/WatchdogTerminations/SentryWatchdogTerminationsIntegrationTests.swift index 09cd94d38d5..1bae051838d 100644 --- a/Tests/SentryTests/Integrations/WatchdogTerminations/SentryWatchdogTerminationsIntegrationTests.swift +++ b/Tests/SentryTests/Integrations/WatchdogTerminations/SentryWatchdogTerminationsIntegrationTests.swift @@ -18,7 +18,7 @@ class SentryWatchdogTerminationIntegrationTests: XCTestCase { crashWrapper = TestSentryCrashWrapper.sharedInstance() SentryDependencyContainer.sharedInstance().crashWrapper = crashWrapper - SentryDependencyContainer.sharedInstance().fileManager = try! SentryFileManager(options: options, andCurrentDateProvider: currentDate) + SentryDependencyContainer.sharedInstance().fileManager = try! SentryFileManager(options: options, andCurrentDateProvider: currentDate, dispatchQueueWrapper: TestSentryDispatchQueueWrapper()) let hub = SentryHub(client: client, andScope: nil, andCrashWrapper: crashWrapper, andCurrentDateProvider: currentDate) SentrySDK.setCurrentHub(hub) diff --git a/Tests/SentryTests/Integrations/WatchdogTerminations/SentryWatchdogTerminationsScopeObserverTests.swift b/Tests/SentryTests/Integrations/WatchdogTerminations/SentryWatchdogTerminationsScopeObserverTests.swift index 003773e57f6..840dbb2b5dc 100644 --- a/Tests/SentryTests/Integrations/WatchdogTerminations/SentryWatchdogTerminationsScopeObserverTests.swift +++ b/Tests/SentryTests/Integrations/WatchdogTerminations/SentryWatchdogTerminationsScopeObserverTests.swift @@ -18,7 +18,7 @@ class SentryWatchdogTerminationScopeObserverTests: XCTestCase { options = Options() options.dsn = SentryWatchdogTerminationScopeObserverTests.dsn - fileManager = try! SentryFileManager(options: options, andCurrentDateProvider: currentDate) + fileManager = try! SentryFileManager(options: options, andCurrentDateProvider: currentDate, dispatchQueueWrapper: TestSentryDispatchQueueWrapper()) } func getSut() -> SentryWatchdogTerminationScopeObserver { diff --git a/Tests/SentryTests/Protocol/SentryBreadcrumbTests.swift b/Tests/SentryTests/Protocol/SentryBreadcrumbTests.swift index 08e824e1e79..be90f2b3a16 100644 --- a/Tests/SentryTests/Protocol/SentryBreadcrumbTests.swift +++ b/Tests/SentryTests/Protocol/SentryBreadcrumbTests.swift @@ -19,7 +19,7 @@ class SentryBreadcrumbTests: XCTestCase { breadcrumb.category = category breadcrumb.type = "user" breadcrumb.message = "Click something" - breadcrumb.data = ["some": ["data": "data", "date": date]] + breadcrumb.data = ["some": ["data": "data", "date": date] as [String: Any]] } var dateAs8601String: String { diff --git a/Tests/SentryTests/Protocol/SentryEnvelopeTests.swift b/Tests/SentryTests/Protocol/SentryEnvelopeTests.swift index de1809034c4..00c1889f110 100644 --- a/Tests/SentryTests/Protocol/SentryEnvelopeTests.swift +++ b/Tests/SentryTests/Protocol/SentryEnvelopeTests.swift @@ -177,7 +177,7 @@ class SentryEnvelopeTests: XCTestCase { let event = fixture.event let envelope = SentryEnvelope(event: event) - let expectedData = try SentrySerialization.data(withJSONObject: event.serialize()) + let expectedData = SentrySerialization.data(withJSONObject: event.serialize())! XCTAssertEqual(event.eventId, envelope.header.eventId) XCTAssertEqual(1, envelope.items.count) @@ -222,7 +222,7 @@ class SentryEnvelopeTests: XCTestCase { XCTAssertEqual("user_report", item?.header.type) XCTAssertNotNil(item?.data) - let expectedData = try SentrySerialization.data(withJSONObject: userFeedback.serialize()) + let expectedData = SentrySerialization.data(withJSONObject: userFeedback.serialize())! let actual = String(data: item?.data ?? Data(), encoding: .utf8)?.sorted() let expected = String(data: expectedData, encoding: .utf8)?.sorted() diff --git a/Tests/SentryTests/Protocol/SentrySdkInfoTests.swift b/Tests/SentryTests/Protocol/SentrySdkInfoTests.swift index e124c7ab2c0..6080e19fd3e 100644 --- a/Tests/SentryTests/Protocol/SentrySdkInfoTests.swift +++ b/Tests/SentryTests/Protocol/SentrySdkInfoTests.swift @@ -100,7 +100,7 @@ class SentrySdkInfoTests: XCTestCase { } func testInitWithDict_AllNil() { - let dict = ["sdk": [ "name": nil, "version": nil]] + let dict = ["sdk": [ "name": nil, "version": nil] as [String: Any?]] assertEmptySdkInfo(actual: SentrySdkInfo(dict: dict)) } diff --git a/Tests/SentryTests/Protocol/TestData.swift b/Tests/SentryTests/Protocol/TestData.swift index c2a6b0eba11..3209afc21f1 100644 --- a/Tests/SentryTests/Protocol/TestData.swift +++ b/Tests/SentryTests/Protocol/TestData.swift @@ -14,7 +14,7 @@ class TestData { } } static let sdk = ["name": SentryMeta.sdkName, "version": SentryMeta.versionString] - static let context = ["context": ["c": "a", "date": timestamp]] + static let context: [String: [String: Any]] = ["context": ["c": "a", "date": timestamp]] static var crumb: Breadcrumb { let crumb = Breadcrumb() @@ -22,7 +22,7 @@ class TestData { crumb.timestamp = timestamp crumb.type = "user" crumb.message = "Clicked something" - crumb.data = ["some": ["data": "data", "date": timestamp]] + crumb.data = ["some": ["data": "data", "date": timestamp] as [String: Any]] return crumb } @@ -65,7 +65,7 @@ class TestData { user.segment = "segmentA" user.name = "User" user.geo = geo - user.data = ["some": ["data": "data", "date": timestamp]] + user.data = ["some": ["data": "data", "date": timestamp] as [String: Any]] return user } diff --git a/Tests/SentryTests/SentryClient+TestInit.h b/Tests/SentryTests/SentryClient+TestInit.h index 61ad03da9e7..27e1213ab18 100644 --- a/Tests/SentryTests/SentryClient+TestInit.h +++ b/Tests/SentryTests/SentryClient+TestInit.h @@ -1,3 +1,4 @@ +#import "SentryExtraContextProvider.h" #import "SentryRandom.h" #import "SentryTransport.h" #import @@ -28,10 +29,9 @@ SentryClient () deleteOldEnvelopeItems:(BOOL)deleteOldEnvelopeItems threadInspector:(SentryThreadInspector *)threadInspector random:(id)random - crashWrapper:(SentryCrashWrapper *)crashWrapper - deviceWrapper:(SentryUIDeviceWrapper *)deviceWrapper locale:(NSLocale *)locale - timezone:(NSTimeZone *)timezone; + timezone:(NSTimeZone *)timezone + extraContextProvider:(SentryExtraContextProvider *)extraContextProvider; @end diff --git a/Tests/SentryTests/SentryClientTests.swift b/Tests/SentryTests/SentryClientTests.swift index 4d91e2d19c1..bfd35cc227b 100644 --- a/Tests/SentryTests/SentryClientTests.swift +++ b/Tests/SentryTests/SentryClientTests.swift @@ -30,6 +30,8 @@ class SentryClientTest: XCTestCase { let transaction: Transaction let crashWrapper = TestSentryCrashWrapper.sharedInstance() let deviceWrapper = TestSentryUIDeviceWrapper() + let processWrapper = TestSentryNSProcessInfoWrapper() + let extraContentProvider: SentryExtraContextProvider let locale = Locale(identifier: "en_US") let timezone = TimeZone(identifier: "Europe/Vienna")! let queue = DispatchQueue(label: "SentryHubTests", qos: .utility, attributes: [.concurrent]) @@ -50,7 +52,7 @@ class SentryClientTest: XCTestCase { let options = Options() options.dsn = SentryClientTest.dsn - fileManager = try! SentryFileManager(options: options, andCurrentDateProvider: TestCurrentDateProvider()) + fileManager = try! SentryFileManager(options: options, andCurrentDateProvider: TestCurrentDateProvider(), dispatchQueueWrapper: TestSentryDispatchQueueWrapper()) transaction = Transaction(trace: trace, children: []) @@ -60,6 +62,8 @@ class SentryClientTest: XCTestCase { crashWrapper.internalFreeMemorySize = 123_456 crashWrapper.internalAppMemorySize = 234_567 crashWrapper.internalFreeStorageSize = 345_678 + + extraContentProvider = SentryExtraContextProvider(crashWrapper: crashWrapper, deviceWrapper: deviceWrapper, processInfoWrapper: processWrapper) } func getSut(configureOptions: (Options) -> Void = { _ in }) -> SentryClient { @@ -77,10 +81,9 @@ class SentryClientTest: XCTestCase { deleteOldEnvelopeItems: false, threadInspector: threadInspector, random: random, - crashWrapper: crashWrapper, - deviceWrapper: deviceWrapper, locale: locale, - timezone: timezone + timezone: timezone, + extraContextProvider: extraContentProvider ) } catch { XCTFail("Options could not be created") @@ -610,9 +613,7 @@ class SentryClientTest: XCTestCase { func testCaptureEvent_AddCurrentMemoryStorageAndCPUCoreCount() { let sut = fixture.getSut() - let testProcessWrapper = TestSentryNSProcessInfoWrapper() - testProcessWrapper.overrides.processorCount = 12 - Dynamic(sut).processInfoWrapper = testProcessWrapper + fixture.processWrapper.overrides.processorCount = 12 sut.capture(event: TestData.event) @@ -627,7 +628,7 @@ class SentryClientTest: XCTestCase { XCTAssertEqual(eventFreeStorage, 345_678) let cpuCoreCount = actual.context?["device"]?["processor_count"] as? UInt - XCTAssertEqual(testProcessWrapper.processorCount, cpuCoreCount) + XCTAssertEqual(fixture.processWrapper.processorCount, cpuCoreCount) } } diff --git a/Tests/SentryTests/SentryCrash/Container+DeepSearch_Tests.m b/Tests/SentryTests/SentryCrash/Container+DeepSearch_Tests.m index b2126776931..1636cd1d2bf 100644 --- a/Tests/SentryTests/SentryCrash/Container+DeepSearch_Tests.m +++ b/Tests/SentryTests/SentryCrash/Container+DeepSearch_Tests.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // Container+DeepSearch_Tests.m // diff --git a/Tests/SentryTests/SentryCrash/FileBasedTestCase.h b/Tests/SentryTests/SentryCrash/FileBasedTestCase.h index 50c9b82ad37..7c60f3bc4be 100644 --- a/Tests/SentryTests/SentryCrash/FileBasedTestCase.h +++ b/Tests/SentryTests/SentryCrash/FileBasedTestCase.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // FileBasedTestCase.h // diff --git a/Tests/SentryTests/SentryCrash/FileBasedTestCase.m b/Tests/SentryTests/SentryCrash/FileBasedTestCase.m index cfc70abde2d..cbdd20caf80 100644 --- a/Tests/SentryTests/SentryCrash/FileBasedTestCase.m +++ b/Tests/SentryTests/SentryCrash/FileBasedTestCase.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // FileBasedTestCase.m // diff --git a/Tests/SentryTests/SentryCrash/NSError+SimpleConstructor_Tests.m b/Tests/SentryTests/SentryCrash/NSError+SimpleConstructor_Tests.m index 57a1b7f6817..49b47e7b329 100644 --- a/Tests/SentryTests/SentryCrash/NSError+SimpleConstructor_Tests.m +++ b/Tests/SentryTests/SentryCrash/NSError+SimpleConstructor_Tests.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // NSError+SimpleConstructor_Tests.m // diff --git a/Tests/SentryTests/SentryCrash/RFC3339UTFString_Tests.m b/Tests/SentryTests/SentryCrash/RFC3339UTFString_Tests.m index 1797edd76da..f0bd6f32683 100644 --- a/Tests/SentryTests/SentryCrash/RFC3339UTFString_Tests.m +++ b/Tests/SentryTests/SentryCrash/RFC3339UTFString_Tests.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // RFC3339DateTool_Tests.m // diff --git a/Tests/SentryTests/SentryCrash/SentryCrashCPU_Tests.m b/Tests/SentryTests/SentryCrash/SentryCrashCPU_Tests.m index 9541d30b493..ac777d231fb 100644 --- a/Tests/SentryTests/SentryCrash/SentryCrashCPU_Tests.m +++ b/Tests/SentryTests/SentryCrash/SentryCrashCPU_Tests.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashCPU_Tests.m // diff --git a/Tests/SentryTests/SentryCrash/SentryCrashCString_Tests.m b/Tests/SentryTests/SentryCrash/SentryCrashCString_Tests.m index 7c582c52762..eb7042b6fc0 100644 --- a/Tests/SentryTests/SentryCrash/SentryCrashCString_Tests.m +++ b/Tests/SentryTests/SentryCrash/SentryCrashCString_Tests.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashCString_Tests.m // diff --git a/Tests/SentryTests/SentryCrash/SentryCrashCachedData_Tests.m b/Tests/SentryTests/SentryCrash/SentryCrashCachedData_Tests.m index 0e8d2610470..0999a63393f 100644 --- a/Tests/SentryTests/SentryCrash/SentryCrashCachedData_Tests.m +++ b/Tests/SentryTests/SentryCrash/SentryCrashCachedData_Tests.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashCachedData_Tests.m // diff --git a/Tests/SentryTests/SentryCrash/SentryCrashDynamicLinker_Tests.m b/Tests/SentryTests/SentryCrash/SentryCrashDynamicLinker_Tests.m index 6cff4b6796f..0ba13504d64 100644 --- a/Tests/SentryTests/SentryCrash/SentryCrashDynamicLinker_Tests.m +++ b/Tests/SentryTests/SentryCrash/SentryCrashDynamicLinker_Tests.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashDynamicLinker_Tests.m // diff --git a/Tests/SentryTests/SentryCrash/SentryCrashFileUtils_Tests.m b/Tests/SentryTests/SentryCrash/SentryCrashFileUtils_Tests.m index 419ff6c953f..fb5f5197b4e 100644 --- a/Tests/SentryTests/SentryCrash/SentryCrashFileUtils_Tests.m +++ b/Tests/SentryTests/SentryCrash/SentryCrashFileUtils_Tests.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashFileUtils_Tests.m // diff --git a/Tests/SentryTests/SentryCrash/SentryCrashJSONCodec_Tests.m b/Tests/SentryTests/SentryCrash/SentryCrashJSONCodec_Tests.m index bdccc7a5386..0bc90ad5b1b 100644 --- a/Tests/SentryTests/SentryCrash/SentryCrashJSONCodec_Tests.m +++ b/Tests/SentryTests/SentryCrash/SentryCrashJSONCodec_Tests.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashJSONCodec_Tests.m // diff --git a/Tests/SentryTests/SentryCrash/SentryCrashLogger_Tests.m b/Tests/SentryTests/SentryCrash/SentryCrashLogger_Tests.m index f3277efd71e..9c846fbb5c6 100644 --- a/Tests/SentryTests/SentryCrash/SentryCrashLogger_Tests.m +++ b/Tests/SentryTests/SentryCrash/SentryCrashLogger_Tests.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashLogger_Tests.m // diff --git a/Tests/SentryTests/SentryCrash/SentryCrashMach_Tests.m b/Tests/SentryTests/SentryCrash/SentryCrashMach_Tests.m index aa2b0b602ad..f55c2981c2d 100644 --- a/Tests/SentryTests/SentryCrash/SentryCrashMach_Tests.m +++ b/Tests/SentryTests/SentryCrash/SentryCrashMach_Tests.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashMach_Tests.m // diff --git a/Tests/SentryTests/SentryCrash/SentryCrashMemory_Tests.m b/Tests/SentryTests/SentryCrash/SentryCrashMemory_Tests.m index 6bc8d249988..9f082ae37f9 100644 --- a/Tests/SentryTests/SentryCrash/SentryCrashMemory_Tests.m +++ b/Tests/SentryTests/SentryCrash/SentryCrashMemory_Tests.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // sentrycrashmemory_Tests.m // diff --git a/Tests/SentryTests/SentryCrash/SentryCrashMonitor_AppState_Tests.m b/Tests/SentryTests/SentryCrash/SentryCrashMonitor_AppState_Tests.m index 584634bec2b..7e541b6fd4e 100644 --- a/Tests/SentryTests/SentryCrash/SentryCrashMonitor_AppState_Tests.m +++ b/Tests/SentryTests/SentryCrash/SentryCrashMonitor_AppState_Tests.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashMonitor_AppState_Tests.m // diff --git a/Tests/SentryTests/SentryCrash/SentryCrashMonitor_NSException_Tests.m b/Tests/SentryTests/SentryCrash/SentryCrashMonitor_NSException_Tests.m index b6d52eb380a..1746654b5bd 100644 --- a/Tests/SentryTests/SentryCrash/SentryCrashMonitor_NSException_Tests.m +++ b/Tests/SentryTests/SentryCrash/SentryCrashMonitor_NSException_Tests.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashMonitor_NSException_Tests.m // diff --git a/Tests/SentryTests/SentryCrash/SentryCrashMonitor_Signal_Tests.m b/Tests/SentryTests/SentryCrash/SentryCrashMonitor_Signal_Tests.m index b730beac311..63a12832136 100644 --- a/Tests/SentryTests/SentryCrash/SentryCrashMonitor_Signal_Tests.m +++ b/Tests/SentryTests/SentryCrash/SentryCrashMonitor_Signal_Tests.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashMonitor_Signal_Tests.m // diff --git a/Tests/SentryTests/SentryCrash/SentryCrashMonitor_Tests.m b/Tests/SentryTests/SentryCrash/SentryCrashMonitor_Tests.m index e474911b12a..81301ed7a62 100644 --- a/Tests/SentryTests/SentryCrash/SentryCrashMonitor_Tests.m +++ b/Tests/SentryTests/SentryCrash/SentryCrashMonitor_Tests.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashMonitor_Tests.m // diff --git a/Tests/SentryTests/SentryCrash/SentryCrashObjC_Tests.m b/Tests/SentryTests/SentryCrash/SentryCrashObjC_Tests.m index cb941d53d4b..b15fb0114f3 100644 --- a/Tests/SentryTests/SentryCrash/SentryCrashObjC_Tests.m +++ b/Tests/SentryTests/SentryCrash/SentryCrashObjC_Tests.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashObjC_Tests.m // diff --git a/Tests/SentryTests/SentryCrash/SentryCrashReportFilter_Tests.m b/Tests/SentryTests/SentryCrash/SentryCrashReportFilter_Tests.m index f590179ec84..9294cf81f2e 100644 --- a/Tests/SentryTests/SentryCrash/SentryCrashReportFilter_Tests.m +++ b/Tests/SentryTests/SentryCrash/SentryCrashReportFilter_Tests.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashReportFilter_Tests.m // diff --git a/Tests/SentryTests/SentryCrash/SentryCrashReportStore_Tests.m b/Tests/SentryTests/SentryCrash/SentryCrashReportStore_Tests.m index 8cb62e67fac..c5010c675d7 100644 --- a/Tests/SentryTests/SentryCrash/SentryCrashReportStore_Tests.m +++ b/Tests/SentryTests/SentryCrash/SentryCrashReportStore_Tests.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashReportStore_Tests.m // diff --git a/Tests/SentryTests/SentryCrash/SentryCrashSignalInfo_Tests.m b/Tests/SentryTests/SentryCrash/SentryCrashSignalInfo_Tests.m index a628e4596b3..fc0a0ca344c 100644 --- a/Tests/SentryTests/SentryCrash/SentryCrashSignalInfo_Tests.m +++ b/Tests/SentryTests/SentryCrash/SentryCrashSignalInfo_Tests.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashSignalInfo_Tests.m // diff --git a/Tests/SentryTests/SentryCrash/SentryCrashString_Tests.m b/Tests/SentryTests/SentryCrash/SentryCrashString_Tests.m index 3bd95891e6f..bd604fe2345 100644 --- a/Tests/SentryTests/SentryCrash/SentryCrashString_Tests.m +++ b/Tests/SentryTests/SentryCrash/SentryCrashString_Tests.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashString_Tests.m // diff --git a/Tests/SentryTests/SentryCrash/SentryCrashSysCtl_Tests.m b/Tests/SentryTests/SentryCrash/SentryCrashSysCtl_Tests.m index b864e262967..2c498a55f5e 100644 --- a/Tests/SentryTests/SentryCrash/SentryCrashSysCtl_Tests.m +++ b/Tests/SentryTests/SentryCrash/SentryCrashSysCtl_Tests.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashSysCtl_Tests.m // diff --git a/Tests/SentryTests/SentryCrash/TestThread.h b/Tests/SentryTests/SentryCrash/TestThread.h index 6252d668090..e3242cba13b 100644 --- a/Tests/SentryTests/SentryCrash/TestThread.h +++ b/Tests/SentryTests/SentryCrash/TestThread.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // TestThread.h // diff --git a/Tests/SentryTests/SentryCrash/TestThread.m b/Tests/SentryTests/SentryCrash/TestThread.m index 348008697fa..8151051e973 100644 --- a/Tests/SentryTests/SentryCrash/TestThread.m +++ b/Tests/SentryTests/SentryCrash/TestThread.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // TestThread.m // diff --git a/Tests/SentryTests/SentryCrash/XCTestCase+SentryCrash.h b/Tests/SentryTests/SentryCrash/XCTestCase+SentryCrash.h index 64e91823427..b54bff692bd 100644 --- a/Tests/SentryTests/SentryCrash/XCTestCase+SentryCrash.h +++ b/Tests/SentryTests/SentryCrash/XCTestCase+SentryCrash.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SenTestCase+SentryCrash.h // diff --git a/Tests/SentryTests/SentryCrash/XCTestCase+SentryCrash.m b/Tests/SentryTests/SentryCrash/XCTestCase+SentryCrash.m index 50065d1e3ff..9973e1bc555 100644 --- a/Tests/SentryTests/SentryCrash/XCTestCase+SentryCrash.m +++ b/Tests/SentryTests/SentryCrash/XCTestCase+SentryCrash.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SenTestCase+SentryCrash.m // diff --git a/Tests/SentryTests/SentryHubTests.swift b/Tests/SentryTests/SentryHubTests.swift index fb354d61e19..a249d5cbe78 100644 --- a/Tests/SentryTests/SentryHubTests.swift +++ b/Tests/SentryTests/SentryHubTests.swift @@ -33,7 +33,7 @@ class SentryHubTests: XCTestCase { event = Event() event.message = SentryMessage(formatted: message) - fileManager = try! SentryFileManager(options: options, andCurrentDateProvider: currentDateProvider) + fileManager = try! SentryFileManager(options: options, andCurrentDateProvider: currentDateProvider, dispatchQueueWrapper: TestSentryDispatchQueueWrapper()) CurrentDate.setCurrentDateProvider(currentDateProvider) @@ -176,8 +176,8 @@ class SentryHubTests: XCTestCase { XCTAssertEqual(crumbMessage, scopeBreadcrumbs?.first?["message"] as? String) } - func testAddUserToTheScope() { - let client = SentryClient(options: fixture.options) + func testAddUserToTheScope() throws { + let client = SentryClient(options: fixture.options, fileManager: try TestFileManager(options: fixture.options), deleteOldEnvelopeItems: false) let hub = SentryHub(client: client, andScope: Scope()) let user = User() diff --git a/Tests/SentryTests/SentrySDKTests.swift b/Tests/SentryTests/SentrySDKTests.swift index eb615f4e929..18dbe41dc41 100644 --- a/Tests/SentryTests/SentrySDKTests.swift +++ b/Tests/SentryTests/SentrySDKTests.swift @@ -31,6 +31,8 @@ class SentrySDKTests: XCTestCase { } let message = "message" + let operation = "ui.load" + let transactionName = "Load Main Screen" init() { CurrentDate.setCurrentDateProvider(currentDate) @@ -344,10 +346,23 @@ class SentrySDKTests: XCTestCase { func testStartTransaction() { givenSdkWithHub() - let span = SentrySDK.startTransaction(name: "Some Transaction", operation: "Operations", bindToScope: true) + let transaction = SentrySDK.startTransaction(name: fixture.transactionName, operation: fixture.operation) + + assertTransaction(transaction: transaction) + + XCTAssertNil(SentrySDK.span) + } + + func testStartTransaction_WithBindToScope() { + givenSdkWithHub() + + let transaction = SentrySDK.startTransaction(name: fixture.transactionName, operation: fixture.operation, bindToScope: true) + + assertTransaction(transaction: transaction) + let newSpan = SentrySDK.span - XCTAssert(span === newSpan) + XCTAssert(transaction === newSpan) } func testInstallIntegrations() { @@ -561,13 +576,13 @@ class SentrySDKTests: XCTestCase { XCTAssertFalse(client?.isEnabled ?? true) } - func testClose_CallsFlushCorrectlyOnTransport() { + func testClose_CallsFlushCorrectlyOnTransport() throws { SentrySDK.start { options in options.dsn = SentrySDKTests.dsnAsString } let transport = TestTransport() - let client = SentryClient(options: fixture.options) + let client = SentryClient(options: fixture.options, fileManager: try TestFileManager(options: fixture.options), deleteOldEnvelopeItems: false) Dynamic(client).transportAdapter = TestTransportAdapter(transport: transport, options: fixture.options) SentrySDK.currentHub().bindClient(client) SentrySDK.close() @@ -575,13 +590,13 @@ class SentrySDKTests: XCTestCase { XCTAssertEqual(Options().shutdownTimeInterval, transport.flushInvocations.first) } - func testFlush_CallsFlushCorrectlyOnTransport() { + func testFlush_CallsFlushCorrectlyOnTransport() throws { SentrySDK.start { options in options.dsn = SentrySDKTests.dsnAsString } let transport = TestTransport() - let client = SentryClient(options: fixture.options) + let client = SentryClient(options: fixture.options, fileManager: try TestFileManager(options: fixture.options), deleteOldEnvelopeItems: false) Dynamic(client).transportAdapter = TestTransportAdapter(transport: transport, options: fixture.options) SentrySDK.currentHub().bindClient(client) @@ -591,7 +606,7 @@ class SentrySDKTests: XCTestCase { XCTAssertEqual(flushTimeout, transport.flushInvocations.first) } - func testSetpAppStartMeasurementConcurrently_() { + func testSetAppStartMeasurementConcurrently() { func setAppStartMeasurement(_ queue: DispatchQueue, _ i: Int) { group.enter() queue.async { @@ -695,6 +710,13 @@ class SentrySDKTests: XCTestCase { XCTAssertEqual(fixture.scope, hubScope) } + private func assertTransaction(transaction: Span) { + XCTAssertEqual(fixture.operation, transaction.operation) + let tracer = transaction as! SentryTracer + XCTAssertEqual(fixture.transactionName, tracer.traceContext.transaction) + XCTAssertEqual(.custom, tracer.transactionContext.nameSource) + } + private func advanceTime(bySeconds: TimeInterval) { fixture.currentDate.setDate(date: fixture.currentDate.date().addingTimeInterval(bySeconds)) } diff --git a/Tests/SentryTests/SentryScopeSwiftTests.swift b/Tests/SentryTests/SentryScopeSwiftTests.swift index 89ef6fc1296..80067fce97a 100644 --- a/Tests/SentryTests/SentryScopeSwiftTests.swift +++ b/Tests/SentryTests/SentryScopeSwiftTests.swift @@ -29,14 +29,14 @@ class SentryScopeSwiftTests: XCTestCase { user.email = "user@sentry.io" user.username = "user123" user.ipAddress = ipAddress - user.data = ["some": ["data": "data", "date": date]] + user.data = ["some": ["data": "data", "date": date] as [String: Any]] breadcrumb = Breadcrumb() breadcrumb.level = SentryLevel.info breadcrumb.timestamp = date breadcrumb.type = "user" breadcrumb.message = "Clicked something" - breadcrumb.data = ["some": ["data": "data", "date": date]] + breadcrumb.data = ["some": ["data": "data", "date": date] as [String: Any]] scope = Scope(maxBreadcrumbs: maxBreadcrumbs) scope.setUser(user) @@ -317,28 +317,13 @@ class SentryScopeSwiftTests: XCTestCase { // With this test we test if modifications from multiple threads don't lead to a crash. func testModifyingFromMultipleThreads() { - let queue = DispatchQueue(label: "SentryScopeTests", qos: .userInteractive, attributes: [.concurrent, .initiallyInactive]) - let group = DispatchGroup() - let scope = fixture.scope - for _ in 0...2 { - group.enter() - queue.async { - - // The number is kept small for the CI to not take too long. - // If you really want to test this increase to 100_000 or so. - for _ in 0...10 { - // Simulate some real world modifications of the user - self.modifyScope(scope: scope) - } - - group.leave() - } - } - - queue.activate() - group.waitWithTimeout(timeout: 500) + // The number is kept small for the CI to not take too long. + // If you really want to test this increase to 100_000 or so. + testConcurrentModifications(asyncWorkItems: 2, writeLoopCount: 10, writeWork: { _ in + self.modifyScope(scope: scope) + }) } func testScopeObserver_setUser() { diff --git a/Tests/SentryTests/SentryScreenShotTests.swift b/Tests/SentryTests/SentryScreenShotTests.swift index f9cd101f613..eaacf748623 100644 --- a/Tests/SentryTests/SentryScreenShotTests.swift +++ b/Tests/SentryTests/SentryScreenShotTests.swift @@ -82,18 +82,42 @@ class SentryScreenShotTests: XCTestCase { XCTAssertEqual(image?.size.width, 10) XCTAssertEqual(image?.size.height, 10) } - + func test_ZeroSizeScreenShot_GetsDiscarded() { let testWindow = TestWindow(frame: CGRect(x: 0, y: 0, width: 0, height: 0)) fixture.uiApplication.windows = [testWindow] - + guard let data = self.fixture.sut.appScreenshots() else { XCTFail("Could not make window screenshot") return } - + XCTAssertEqual(0, data.count, "No screenshot should be taken, cause the image has zero size.") } + + func test_ZeroWidthScreenShot_GetsDiscarded() { + let testWindow = TestWindow(frame: CGRect(x: 0, y: 0, width: 0, height: 1_000)) + fixture.uiApplication.windows = [testWindow] + + guard let data = self.fixture.sut.appScreenshots() else { + XCTFail("Could not make window screenshot") + return + } + + XCTAssertEqual(0, data.count, "No screenshot should be taken, cause the image has zero width.") + } + + func test_ZeroHeightScreenShot_GetsDiscarded() { + let testWindow = TestWindow(frame: CGRect(x: 0, y: 0, width: 1_000, height: 0)) + fixture.uiApplication.windows = [testWindow] + + guard let data = self.fixture.sut.appScreenshots() else { + XCTFail("Could not make window screenshot") + return + } + + XCTAssertEqual(0, data.count, "No screenshot should be taken, cause the image has zero height.") + } class TestSentryUIApplication: SentryUIApplication { private var _windows: [UIWindow]? diff --git a/Tests/SentryTests/SentryTests-Bridging-Header.h b/Tests/SentryTests/SentryTests-Bridging-Header.h index 9b42fc83605..012cbcaff01 100644 --- a/Tests/SentryTests/SentryTests-Bridging-Header.h +++ b/Tests/SentryTests/SentryTests-Bridging-Header.h @@ -22,6 +22,7 @@ #import "SentryAutoSessionTrackingIntegration.h" #import "SentryBaggage.h" #import "SentryBooleanSerialization.h" +#import "SentryBreadcrumbDelegate.h" #import "SentryBreadcrumbTracker.h" #import "SentryByteCountFormatter.h" #import "SentryClassRegistrator.h" @@ -185,11 +186,13 @@ #import "URLSessionTaskMock.h" @import SentryPrivate; #import "SentryEnvelopeAttachmentHeader.h" +#import "SentryExtraContextProvider.h" #import "SentryMeasurementValue.h" #import "SentryNSProcessInfoWrapper.h" #import "SentryPerformanceTracker+Testing.h" #import "SentrySpanOperations.h" #import "SentryTimeToDisplayTracker.h" +#import "SentryTracerConfiguration.h" #import "TestSentryViewHierarchy.h" #if SENTRY_HAS_UIKIT # import "MockUIScene.h" diff --git a/Tests/SentryTests/SentryViewHierarchyTests.swift b/Tests/SentryTests/SentryViewHierarchyTests.swift index f55f1318ce9..8dfef6bb3b6 100644 --- a/Tests/SentryTests/SentryViewHierarchyTests.swift +++ b/Tests/SentryTests/SentryViewHierarchyTests.swift @@ -4,7 +4,6 @@ import XCTest #if os(iOS) || os(tvOS) || targetEnvironment(macCatalyst) class SentryViewHierarchyTests: XCTestCase { private class Fixture { - let uiApplication = TestSentryUIApplication() var sut: SentryViewHierarchy { @@ -16,10 +15,26 @@ class SentryViewHierarchyTests: XCTestCase { override func setUp() { super.setUp() + fixture = Fixture() SentryDependencyContainer.sharedInstance().application = fixture.uiApplication } + override func setUpWithError() throws { + try super.setUpWithError() + + /** + * This test is making iOS 13 simulator hang in GH workflow, + * thats why we need to check for iOS 13 or later. + * By testing this in the other versions of iOS we guarantee the behavior + * mean while, running an iOS 12 sample with Saucelabs ensures this feature + * is not crashing the app. + */ + guard #available(iOS 13, *) else { + throw XCTSkip("Skipping for iOS < 13") + } + } + override func tearDown() { super.tearDown() clearTestState() @@ -164,7 +179,7 @@ class SentryViewHierarchyTests: XCTestCase { ex.fulfill() XCTAssertTrue(Thread.isMainThread) } - + let dispatch = DispatchQueue(label: "background") dispatch.async { let _ = sut.fetch() diff --git a/Tests/SentryTests/State/TestHub.swift b/Tests/SentryTests/State/TestHub.swift index a4bc4ea5695..c66c452063f 100644 --- a/Tests/SentryTests/State/TestHub.swift +++ b/Tests/SentryTests/State/TestHub.swift @@ -1,9 +1,7 @@ import Foundation +import SentryTestUtils class TestHub: SentryHub { - - let group = DispatchGroup() - let queue = DispatchQueue(label: "TestHub", attributes: .concurrent) var startSessionInvocations: Int = 0 var closeCachedSessionInvocations: Int = 0 @@ -23,30 +21,27 @@ class TestHub: SentryHub { endSessionTimestamp = timestamp } - var sentCrashEvents: [Event] = [] + var sentCrashEvents = Invocations() override func captureCrash(_ event: Event) { - sentCrashEvents.append(event) + sentCrashEvents.record(event) } - var sentCrashEventsWithScope: [(event: Event, scope: Scope)] = [] + var sentCrashEventsWithScope = Invocations<(event: Event, scope: Scope)>() override func captureCrash(_ event: Event, with scope: Scope) { - sentCrashEventsWithScope.append((event, scope)) + sentCrashEventsWithScope.record((event, scope)) } - var capturedEventsWithScopes: [(event: Event, scope: Scope, additionalEnvelopeItems: [SentryEnvelopeItem])] = [] + var capturedEventsWithScopes = Invocations<(event: Event, scope: Scope, additionalEnvelopeItems: [SentryEnvelopeItem])>() override func capture(event: Event, scope: Scope, additionalEnvelopeItems: [SentryEnvelopeItem]) -> SentryId { - group.enter() - queue.async(flags: .barrier) { - self.capturedEventsWithScopes.append((event, scope, additionalEnvelopeItems)) - self.group.leave() - } + + self.capturedEventsWithScopes.record((event, scope, additionalEnvelopeItems)) return event.eventId } - var capturedTransactionsWithScope: [(transaction: [String: Any], scope: Scope)] = [] + var capturedTransactionsWithScope = Invocations<(transaction: [String: Any], scope: Scope)>() override func capture(_ transaction: Transaction, with scope: Scope) -> SentryId { - capturedTransactionsWithScope.append((transaction.serialize(), scope)) + capturedTransactionsWithScope.record((transaction.serialize(), scope)) return super.capture(transaction, with: scope) } } diff --git a/Tests/SentryTests/TestLogOutput.swift b/Tests/SentryTests/TestLogOutput.swift index af9c6e1777c..c5cbc2b8d12 100644 --- a/Tests/SentryTests/TestLogOutput.swift +++ b/Tests/SentryTests/TestLogOutput.swift @@ -1,9 +1,40 @@ import Foundation class TestLogOutput: SentryLogOutput { - var loggedMessages: [String] = [] + + private let queue = DispatchQueue(label: "TestLogOutput", attributes: .concurrent) + + private var _loggedMessages: [String] = [] + + var callSuperWhenLogging = true + + var loggedMessages: [String] { + get { + queue.sync { + return _loggedMessages + } + } + } + override func log(_ message: String) { - super.log(message) - loggedMessages.append(message) + if callSuperWhenLogging { + super.log(message) + } + queue.async(flags: .barrier) { + self._loggedMessages.append(message) + } + } +} + +class TestLogOutPutTests: XCTestCase { + + func testLoggingFromMulitpleThreads() { + let sut = TestLogOutput() + sut.callSuperWhenLogging = false + testConcurrentModifications(writeWork: { i in + sut.log("Some message \(i)") + }, readWork: { + XCTAssertNotNil(sut.loggedMessages) + }) } } diff --git a/Tests/SentryTests/TestUtils/TestConncurrentModifications.swift b/Tests/SentryTests/TestUtils/TestConncurrentModifications.swift new file mode 100644 index 00000000000..d0dff19460e --- /dev/null +++ b/Tests/SentryTests/TestUtils/TestConncurrentModifications.swift @@ -0,0 +1,23 @@ +import Foundation + +func testConcurrentModifications(asyncWorkItems: Int = 5, writeLoopCount: Int = 1_000, writeWork: @escaping (Int) -> Void, readWork: @escaping () -> Void = {}) { + let queue = DispatchQueue(label: "testConcurrentModifications", qos: .userInteractive, attributes: [.concurrent, .initiallyInactive]) + let group = DispatchGroup() + + for _ in 0.. UInt64 { - return UInt64(self * Double(NSEC_PER_SEC)) - } -} - -extension UInt64 { - func toTimeInterval() -> TimeInterval { - return Double(self) / Double(NSEC_PER_SEC) - } -} - extension XCTest { func contentsOfResource(_ resource: String, ofType: String = "json") throws -> Data { let path = Bundle(for: type(of: self)).path(forResource: "Resources/\(resource)", ofType: "json") diff --git a/Tests/SentryTests/Transaction/SentryTracer+Test.h b/Tests/SentryTests/Transaction/SentryTracer+Test.h index 01a4b2e1173..3444b3f8b46 100644 --- a/Tests/SentryTests/Transaction/SentryTracer+Test.h +++ b/Tests/SentryTests/Transaction/SentryTracer+Test.h @@ -1,4 +1,4 @@ - +#import "SentryProfilingConditionals.h" #import "SentryTracer.h" NS_ASSUME_NONNULL_BEGIN @@ -10,6 +10,10 @@ SentryTracer (Test) - (void)updateStartTime:(NSDate *)startTime; +#if SENTRY_TARGET_PROFILING_SUPPORTED && (defined(TEST) || defined(TESTCI)) ++ (void)resetConcurrencyTracking; +#endif // SENTRY_TARGET_PROFILING_SUPPORTED && (defined(TEST) || defined(TESTCI)) + @end NS_ASSUME_NONNULL_END diff --git a/Tests/SentryTests/Transaction/SentryTracerObjCTests.m b/Tests/SentryTests/Transaction/SentryTracerObjCTests.m index 495fc004e9c..8378315cfb6 100644 --- a/Tests/SentryTests/Transaction/SentryTracerObjCTests.m +++ b/Tests/SentryTests/Transaction/SentryTracerObjCTests.m @@ -27,11 +27,14 @@ - (void)testSpanFinishesAfterTracerReleased_NoCrash_TracerIsNil SentryHub *hub = [[SentryHub alloc] initWithClient:nil andScope:nil]; SentryTransactionContext *context = [[SentryTransactionContext alloc] initWithOperation:@""]; - SentryTracer *tracer = [[SentryTracer alloc] initWithTransactionContext:context - hub:hub - profilesSamplerDecision:nil - waitForChildren:YES - timerWrapper:nil]; + SentryTracer *tracer = [[SentryTracer alloc] + initWithTransactionContext:context + hub:hub + configuration:[SentryTracerConfiguration configurationWithBlock:^( + SentryTracerConfiguration *configuration) { + configuration.waitForChildren = YES; + }]]; + [tracer finish]; child = [tracer startChildWithOperation:@"child"]; } @@ -55,17 +58,23 @@ - (void)testConcurrentTracerProfiling [[SentryProfilesSamplerDecision alloc] initWithDecision:kSentrySampleDecisionYes forSampleRate:@1]; - SentryTracer *tracer1 = [[SentryTracer alloc] initWithTransactionContext:context1 - hub:hub - profilesSamplerDecision:decision - waitForChildren:YES - timerWrapper:nil]; - - SentryTracer *tracer2 = [[SentryTracer alloc] initWithTransactionContext:context2 - hub:hub - profilesSamplerDecision:decision - waitForChildren:YES - timerWrapper:nil]; + SentryTracer *tracer1 = [[SentryTracer alloc] + initWithTransactionContext:context1 + hub:hub + configuration:[SentryTracerConfiguration configurationWithBlock:^( + SentryTracerConfiguration *configuration) { + configuration.profilesSamplerDecision = decision; + configuration.waitForChildren = YES; + }]]; + + SentryTracer *tracer2 = [[SentryTracer alloc] + initWithTransactionContext:context2 + hub:hub + configuration:[SentryTracerConfiguration configurationWithBlock:^( + SentryTracerConfiguration *configuration) { + configuration.profilesSamplerDecision = decision; + configuration.waitForChildren = YES; + }]]; // force some samples to be taken by the profiler NSMutableString *string = [NSMutableString string]; diff --git a/Tests/SentryTests/Transaction/SentryTracerTests.swift b/Tests/SentryTests/Transaction/SentryTracerTests.swift index 10094c91636..ebfd7a99a27 100644 --- a/Tests/SentryTests/Transaction/SentryTracerTests.swift +++ b/Tests/SentryTests/Transaction/SentryTracerTests.swift @@ -88,9 +88,11 @@ class SentryTracerTests: XCTestCase { let tracer = hub.startTransaction( with: transactionContext, bindToScope: false, - waitForChildren: waitForChildren, customSamplingContext: [:], - timerWrapper: timerWrapper) as! SentryTracer + configuration: SentryTracerConfiguration(block: { + $0.waitForChildren = waitForChildren + $0.timerWrapper = self.timerWrapper + })) return tracer } @@ -99,8 +101,11 @@ class SentryTracerTests: XCTestCase { with: transactionContext, bindToScope: false, customSamplingContext: [:], - idleTimeout: idleTimeout, - dispatchQueueWrapper: dispatchQueueWrapper + configuration: SentryTracerConfiguration(block: { + $0.idleTimeout = idleTimeout + $0.dispatchQueueWrapper = dispatchQueueWrapper + $0.waitForChildren = true + }) ) return tracer } @@ -118,7 +123,7 @@ class SentryTracerTests: XCTestCase { clearTestState() } - func testFinish_WithChildren_WaitsForAllChildren() { + func testFinish_WithChildren_WaitsForAllChildren() throws { let sut = fixture.getSut() let child = sut.startChild(operation: fixture.transactionOperation) sut.finish() @@ -139,7 +144,7 @@ class SentryTracerTests: XCTestCase { assertOneTransactionCaptured(sut) - let serialization = getSerializedTransaction() + let serialization = try getSerializedTransaction() let spans = serialization["spans"]! as! [[String: Any]] let tracerTimestamp: NSDate = sut.timestamp! as NSDate @@ -204,7 +209,7 @@ class SentryTracerTests: XCTestCase { let sut = fixture.getSut() sut.finish() - XCTAssertEqual(fixture.timerWrapper.overrides.timer.invalidateCount, 1) + XCTAssertFalse(fixture.timerWrapper.overrides.timer.isValid) } func testFinish_CheckDefaultStatus() { @@ -276,7 +281,7 @@ class SentryTracerTests: XCTestCase { } func testFinish_WithoutHub_DoesntCaptureTransaction() { - let sut = SentryTracer(transactionContext: fixture.transactionContext, hub: nil, waitForChildren: false) + let sut = SentryTracer(transactionContext: fixture.transactionContext, hub: nil) sut.finish() @@ -598,7 +603,6 @@ class SentryTracerTests: XCTestCase { let sut = fixture.getSut() advanceTime(bySeconds: 1) sut.finish() - fixture.hub.group.wait() XCTAssertEqual(1, fixture.hub.capturedEventsWithScopes.count) @@ -618,16 +622,14 @@ class SentryTracerTests: XCTestCase { advanceTime(bySeconds: 0.5) secondTransaction.finish() - fixture.hub.group.wait() XCTAssertEqual(1, fixture.hub.capturedEventsWithScopes.count) let serializedSecondTransaction = fixture.hub.capturedEventsWithScopes.first!.event.serialize() XCTAssertNil(serializedSecondTransaction["measurements"]) firstTransaction.finish() - fixture.hub.group.wait() XCTAssertEqual(2, fixture.hub.capturedEventsWithScopes.count) - assertAppStartMeasurementOn(transaction: fixture.hub.capturedEventsWithScopes[1].event as! Transaction, appStartMeasurement: appStartMeasurement) + assertAppStartMeasurementOn(transaction: fixture.hub.capturedEventsWithScopes.invocations[1].event as! Transaction, appStartMeasurement: appStartMeasurement) } func testAddUnknownAppStartMeasurement_NotPutOnNextTransaction() { @@ -642,7 +644,6 @@ class SentryTracerTests: XCTestCase { )) fixture.getSut().finish() - fixture.hub.group.wait() assertAppStartMeasurementNotPutOnTransaction() } @@ -689,7 +690,6 @@ class SentryTracerTests: XCTestCase { let sut = fixture.hub.startTransaction(transactionContext: TransactionContext(name: "custom", operation: "custom")) as! SentryTracer sut.finish() - fixture.hub.group.wait() XCTAssertNotNil(SentrySDK.getAppStartMeasurement()) @@ -713,7 +713,6 @@ class SentryTracerTests: XCTestCase { let sut = fixture.getSut() advanceTime(bySeconds: 1.0) sut.finish() - fixture.hub.group.wait() assertAppStartMeasurementNotPutOnTransaction() } @@ -727,7 +726,6 @@ class SentryTracerTests: XCTestCase { let sut = fixture.getSut() advanceTime(bySeconds: 1.0) sut.finish() - fixture.hub.group.wait() assertAppStartMeasurementNotPutOnTransaction() } @@ -739,7 +737,6 @@ class SentryTracerTests: XCTestCase { let sut = fixture.getSut() sut.finish() - fixture.hub.group.wait() assertAppStartMeasurementNotPutOnTransaction() } @@ -755,7 +752,6 @@ class SentryTracerTests: XCTestCase { childSpan.setMeasurement(name: name, value: value, unit: unit) childSpan.finish() sut.finish() - fixture.hub.group.wait() XCTAssertEqual(1, fixture.hub.capturedEventsWithScopes.count) let serializedTransaction = fixture.hub.capturedEventsWithScopes.first?.event.serialize() @@ -834,7 +830,7 @@ class SentryTracerTests: XCTestCase { XCTAssertTrue(sutOnScope === fixture.hub.scope.span) } - func testFinishAsync() { + func testFinishAsync() throws { let sut = fixture.getSut() let child = sut.startChild(operation: fixture.transactionOperation) sut.finish() @@ -866,7 +862,7 @@ class SentryTracerTests: XCTestCase { assertOneTransactionCaptured(sut) - let spans = getSerializedTransaction()["spans"]! as! [[String: Any]] + let spans = try getSerializedTransaction()["spans"]! as! [[String: Any]] XCTAssertEqual(spans.count, children * (grandchildren + 1) + 1) } @@ -888,11 +884,9 @@ class SentryTracerTests: XCTestCase { queue.activate() group.wait() - fixture.hub.group.wait() - XCTAssertEqual(transactions, fixture.hub.capturedEventsWithScopes.count) - let transactionsWithAppStartMeasurement = fixture.hub.capturedEventsWithScopes.filter { pair in + let transactionsWithAppStartMeasurement = fixture.hub.capturedEventsWithScopes.invocations.filter { pair in let serializedTransaction = pair.event.serialize() let measurements = serializedTransaction["measurements"] as? [String: [String: Int]] return measurements == ["app_start_warm": ["value": 500]] @@ -901,7 +895,7 @@ class SentryTracerTests: XCTestCase { XCTAssertEqual(1, transactionsWithAppStartMeasurement.count) } - func testAddingSpansOnDifferentThread_WhileFinishing_DoesNotCrash() { + func testAddingSpansOnDifferentThread_WhileFinishing_DoesNotCrash() throws { let sut = fixture.getSut(waitForChildren: false) let children = 1_000 @@ -918,7 +912,7 @@ class SentryTracerTests: XCTestCase { group.enter() queue.async { let child = sut.startChild(operation: self.fixture.transactionOperation) - Dynamic(child).frames = [] + Dynamic(child).frames = [] as [Frame] child.finish() group.leave() } @@ -938,7 +932,7 @@ class SentryTracerTests: XCTestCase { queue.activate() group.wait() - let spans = getSerializedTransaction()["spans"]! as! [[String: Any]] + let spans = try getSerializedTransaction()["spans"]! as! [[String: Any]] XCTAssertGreaterThanOrEqual(spans.count, children) } @@ -965,8 +959,6 @@ class SentryTracerTests: XCTestCase { sut.finish() - fixture.hub.group.wait() - XCTAssertEqual(1, fixture.hub.capturedEventsWithScopes.count) let serializedTransaction = fixture.hub.capturedEventsWithScopes.first!.event.serialize() let measurements = serializedTransaction["measurements"] as? [String: [String: Int]] @@ -1008,10 +1000,9 @@ class SentryTracerTests: XCTestCase { fixture.currentDateProvider.internalDispatchNow = newNow } - private func getSerializedTransaction() -> [String: Any] { - guard let transaction = fixture.hub.capturedEventsWithScopes.first?.event else { - fatalError("Event must not be nil.") - } + private func getSerializedTransaction() throws -> [String: Any] { + let transaction = try XCTUnwrap( fixture.hub.capturedEventsWithScopes.first?.event) + return transaction.serialize() } @@ -1019,16 +1010,13 @@ class SentryTracerTests: XCTestCase { let sut = fixture.getSut() sut.updateStartTime(fixture.appStartEnd.addingTimeInterval(startTimestamp)) sut.finish() - fixture.hub.group.wait() } private func assertTransactionNotCaptured(_ tracer: SentryTracer) { - fixture.hub.group.wait() XCTAssertEqual(0, fixture.hub.capturedEventsWithScopes.count) } private func assertOneTransactionCaptured(_ tracer: SentryTracer) { - fixture.hub.group.wait() XCTAssertTrue(tracer.isFinished) XCTAssertEqual(1, fixture.hub.capturedEventsWithScopes.count) } @@ -1111,8 +1099,6 @@ class SentryTracerTests: XCTestCase { } private func assertNoMeasurementsAdded() { - fixture.hub.group.wait() - XCTAssertEqual(1, fixture.hub.capturedEventsWithScopes.count) let serializedTransaction = fixture.hub.capturedEventsWithScopes.first?.event.serialize() XCTAssertNil(serializedTransaction?["measurements"]) diff --git a/Tests/SentryTests/Transaction/SentryTransactionTests.swift b/Tests/SentryTests/Transaction/SentryTransactionTests.swift index bfec9fd24ef..bd777b1c65a 100644 --- a/Tests/SentryTests/Transaction/SentryTransactionTests.swift +++ b/Tests/SentryTests/Transaction/SentryTransactionTests.swift @@ -169,4 +169,13 @@ class SentryTransactionTests: XCTestCase { let actualTransactionInfo = actual["transaction_info"] as? [String: String] XCTAssertEqual(actualTransactionInfo?["source"], "component") } + + func testSerialize_TransactionName() { + let scope = Scope() + let transaction = fixture.getTransactionWith(scope: scope) + let actual = transaction.serialize() + + let actualTransaction = actual["transaction"] as? String + XCTAssertEqual(actualTransaction, fixture.transactionName) + } } diff --git a/develop-docs/README.md b/develop-docs/README.md index 1307f1a278c..4782dd8afd7 100644 --- a/develop-docs/README.md +++ b/develop-docs/README.md @@ -130,11 +130,13 @@ Contributors: @marandaneto, @brustolin and @philipphofmann We decided not to use the `NSRange` type for the `failedRequestStatusCodes` property of the `SentryNetworkTracker` class because it's not compatible with the specification, which requires the type to be a range of `from` -> `to` integers. The `NSRange` type is a range of `location` -> `length` integers. We decided to use a custom type instead of `NSRange` to avoid confusion. The custom type is called `SentryHttpStatusCodeRange`. -### Manually installing iOS 12 simulators +### Manually installing iOS 12 simulators Date: October 21st 2022 Contributors: @philipphofmann +We reverted this decision with [remove running unit tests on iOS 12 simulators](#remove-ios-12-simulators). + GH actions will remove the macOS-10.15 image, which contains an iOS 12 simulator on 12/1/22; see https://github.com/actions/runner-images/issues/5583. Neither the [macOS-11](https://github.com/actions/runner-images/blob/main/images/macos/macos-11-Readme.md#installed-sdks) nor the [macOS-12](https://github.com/actions/runner-images/blob/main/images/macos/macos-12-Readme.md#installed-sdks) image contains an iOS 12 simulator. GH @@ -177,33 +179,49 @@ platform-specific framework bundles only works with Xcode 12. Carthage has encouraged its users [to use XCFrameworks](https://github.com/Carthage/Carthage/tree/a91d086ceaffef65c4a4a761108f3f32c519940c#getting-started) since version 0.37.0, released in January 2021. Therefore, it's acceptable to use XCFrameworks for Carthage users. -## Remove the permissions feature +### Remove the permissions feature Date: December 14, 2022 We [removed](https://github.com/getsentry/sentry-cocoa/pull/2529) the permissions feature that we added in [7.24.0](https://github.com/getsentry/sentry-cocoa/releases/tag/7.24.0). Multiple people reported getting denied in app review because of permission access without the corresponding Info.plist entry: see [#2528](https://github.com/getsentry/sentry-cocoa/issues/2528) and [2065](https://github.com/getsentry/sentry-cocoa/issues/2065). -## Rename master to main +### Rename master to main Date: January 16th, 2023 Contributors: @kahest, @brustolin and @philipphofmann With 8.0.0, we rename the default branch from `master` to `main`. We will keep the `master` branch for backwards compatibility for package managers pointing to the `master` branch. -## SentrySwiftUI version +### SentrySwiftUI version Date: January 18th, 2023 Contributors: @brustolin and @philipphofmann We release experimental SentrySwiftUI cocoa package with the version 8.0.0 because all podspecs file in a repo need to have the same version. -## Tracking package managers +### Tracking package managers To be able to identify the package manager(PM) being used by the user, we need that the PM identify itself. Luckily all of the 3 PMs we support do this in some way, mostly by exposing a compiler directive (SPM, COCOA) or a build setting (CARTHAGE). With this information we can create a conditional compilation that injects the name of the PM. You can find this in `SentrySDKInfo.m`. -## Usage of `__has_include` +### Usage of `__has_include` Some private headers add a dependency of a public header, when those private headers are used in a sample project, or referenced from a hybrid SDK, it is treated as part of the project using it, therefore, if it points to a header that is not part of said project, a compilation error will occur. To solve this we make use of `__has_include` to try to point to the SDK version of the header, or to fallback to the direct reference when compiling the SDK. + +### Remove running unit tests on iOS 12 simulators + +Date: April 12th 2023 +Contributors: @philipphofmann + +We use [`xcode-install`](https://github.com/xcpretty/xcode-install) to install some older iOS simulators for test runs [here](https://github.com/getsentry/sentry-cocoa/blob/ff5c1d83bf601bbcd0f5f1070c3abf05310881bd/.github/workflows/test.yml#L174) and [here](https://github.com/getsentry/sentry-cocoa/blob/ff5c1d83bf601bbcd0f5f1070c3abf05310881bd/.github/workflows/test.yml#L343). That project is being sunset, so we would have to find an alternative. + +Installing the simulator can take up to 15 minutes, so the current solution slows CI and sometimes leads to timeouts. +We want our CI to be fast and reliable. Instead of running the unit tests on iOS 12, we run UI tests on an iPhone with iOS 12, +which reduces the risk of breaking users on iOS 12. Our unit tests should primarily focus on business logic and shouldn't depend +on specific iOS versions. If we have functionality that risks breaking on older iOS versions, we should write UI tests instead. +For the swizzling of UIViewControllers and NSURLSession, we have UI tests running on iOS 12. Therefore, dropping running unit +tests on iOS 12 simulators is acceptable. This decision reverts [manually installing iOS 12 simulators](#ios-12-simulators). + +Related to [GH-2862](https://github.com/getsentry/sentry-cocoa/issues/2862) and diff --git a/scripts/no-changes-in-high-risk-files.sh b/scripts/no-changes-in-high-risk-files.sh index 714f165415a..38718a2a98f 100755 --- a/scripts/no-changes-in-high-risk-files.sh +++ b/scripts/no-changes-in-high-risk-files.sh @@ -1,10 +1,11 @@ #!/bin/bash set -euo pipefail -# To update the sha run shasum -a 256 ./Sources/Sentry/SentryNSURLSessionTaskSearch.m and copy the result in EXPECTED. +# To update the sha run the command in ACTUAL and copy the result in EXPECTED. -ACTUAL=$(shasum -a 256 ./Sources/Sentry/SentryNSURLSessionTaskSearch.m) -EXPECTED="819d5ca5e3db2ac23c859b14c149b7f0754d3ae88bea1dba92c18f49a81da0e1 ./Sources/Sentry/SentryNSURLSessionTaskSearch.m" +ACTUAL=$(shasum -a 256 ./Sources/Sentry/SentryNSURLSessionTaskSearch.m ./Sources/Sentry/SentryNetworkTracker.m) +EXPECTED="819d5ca5e3db2ac23c859b14c149b7f0754d3ae88bea1dba92c18f49a81da0e1 ./Sources/Sentry/SentryNSURLSessionTaskSearch.m +58d5414b4f0a4c821b20fc1a16f88bda3116401e905b7bc1d18af828be75e431 ./Sources/Sentry/SentryNetworkTracker.m" if [ "$ACTUAL" = "$EXPECTED" ]; then echo "No changes in high risk files."