diff --git a/CHANGELOG.md b/CHANGELOG.md index 21ec493d326..d022abecfee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### Features + +- Read free_memory when the event is captured, not only at SDK startup (#1962) + ### Fixes - Remove Sentry keys from cached HTTP request headers (#1975) diff --git a/Sources/Sentry/SentryClient.m b/Sources/Sentry/SentryClient.m index a43164aab0d..9b0cebff1e5 100644 --- a/Sources/Sentry/SentryClient.m +++ b/Sources/Sentry/SentryClient.m @@ -5,6 +5,7 @@ #import "SentryCrashDefaultMachineContextWrapper.h" #import "SentryCrashIntegration.h" #import "SentryCrashStackEntryMapper.h" +#import "SentryCrashWrapper.h" #import "SentryDebugImageProvider.h" #import "SentryDefaultCurrentDateProvider.h" #import "SentryDependencyContainer.h" @@ -55,6 +56,7 @@ @property (nonatomic, strong) SentryThreadInspector *threadInspector; @property (nonatomic, strong) id random; @property (nonatomic, weak) id attachmentProcessor; +@property (nonatomic, strong) SentryCrashWrapper *crashWrapper; @end @@ -101,6 +103,8 @@ - (_Nullable instancetype)initWithOptions:(SentryOptions *)options options:options]; self.random = [SentryDependencyContainer sharedInstance].random; + + self.crashWrapper = [SentryCrashWrapper sharedInstance]; } return self; } @@ -111,6 +115,7 @@ - (instancetype)initWithOptions:(SentryOptions *)options fileManager:(SentryFileManager *)fileManager threadInspector:(SentryThreadInspector *)threadInspector random:(id)random + crashWrapper:(SentryCrashWrapper *)crashWrapper { self = [self initWithOptions:options]; @@ -118,6 +123,7 @@ - (instancetype)initWithOptions:(SentryOptions *)options self.fileManager = fileManager; self.threadInspector = threadInspector; self.random = random; + self.crashWrapper = crashWrapper; return self; } @@ -487,10 +493,13 @@ - (SentryEvent *_Nullable)prepareEvent:(SentryEvent *)event event = [scope applyToEvent:event maxBreadcrumb:self.options.maxBreadcrumbs]; - // Remove free_memory if OOM as free_memory stems from the current run and not of the time of - // the OOM. if ([self isOOM:event isCrashEvent:isCrashEvent]) { + // Remove free_memory if OOM as free_memory stems from the current run and not of + // the time of the OOM. [self removeFreeMemoryFromDeviceContext:event]; + } else { + // Store the actual free memory at the time of this event + [self storeFreeMemoryToDeviceContext:event]; } // With scope applied, before running callbacks run: @@ -638,7 +647,24 @@ - (BOOL)isOOM:(SentryEvent *)event isCrashEvent:(BOOL)isCrashEvent [exception.mechanism.type isEqualToString:SentryOutOfMemoryMechanismType]; } +- (void)storeFreeMemoryToDeviceContext:(SentryEvent *)event +{ + [self modifyDeviceContext:event + block:^(NSMutableDictionary *device) { + device[SentryDeviceContextFreeMemoryKey] = + @(self.crashWrapper.freeMemory); + }]; +} + - (void)removeFreeMemoryFromDeviceContext:(SentryEvent *)event +{ + [self modifyDeviceContext:event + block:^(NSMutableDictionary *device) { + [device removeObjectForKey:SentryDeviceContextFreeMemoryKey]; + }]; +} + +- (void)modifyDeviceContext:(SentryEvent *)event block:(void (^)(NSMutableDictionary *))block { if (nil == event.context || event.context.count == 0 || nil == event.context[@"device"]) { return; @@ -647,9 +673,8 @@ - (void)removeFreeMemoryFromDeviceContext:(SentryEvent *)event NSMutableDictionary *context = [[NSMutableDictionary alloc] initWithDictionary:event.context]; NSMutableDictionary *device = [[NSMutableDictionary alloc] initWithDictionary:context[@"device"]]; - [device removeObjectForKey:SentryDeviceContextFreeMemoryKey]; + block(device); context[@"device"] = device; - event.context = context; } diff --git a/Sources/Sentry/SentryCrashWrapper.m b/Sources/Sentry/SentryCrashWrapper.m index 0a91a6f336a..075e7d67c6c 100644 --- a/Sources/Sentry/SentryCrashWrapper.m +++ b/Sources/Sentry/SentryCrashWrapper.m @@ -1,6 +1,7 @@ #import "SentryCrashWrapper.h" #import "SentryCrash.h" #import "SentryCrashMonitor_AppState.h" +#import "SentryCrashMonitor_System.h" #import "SentryHook.h" #import #import @@ -69,6 +70,11 @@ - (NSDictionary *)systemInfo return sharedInfo; } +- (NSInteger)freeMemory +{ + return sentrycrashcm_system_freememory(); +} + @end NS_ASSUME_NONNULL_END diff --git a/Sources/Sentry/include/SentryCrashWrapper.h b/Sources/Sentry/include/SentryCrashWrapper.h index 2dbbf601a37..a728ecaf7c6 100644 --- a/Sources/Sentry/include/SentryCrashWrapper.h +++ b/Sources/Sentry/include/SentryCrashWrapper.h @@ -30,6 +30,8 @@ SENTRY_NO_INIT - (NSDictionary *)systemInfo; +- (NSInteger)freeMemory; + @end NS_ASSUME_NONNULL_END diff --git a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_System.h b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_System.h index b9a870f42bb..5c9c657cff8 100644 --- a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_System.h +++ b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_System.h @@ -39,6 +39,8 @@ SentryCrashMonitorAPI *sentrycrashcm_system_getAPI(void); bool sentrycrash_isSimulatorBuild(void); +uint64_t sentrycrashcm_system_freememory(void); + #ifdef __cplusplus } #endif diff --git a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_System.m b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_System.m index c4d2f202bf6..a623bc1b38a 100644 --- a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_System.m +++ b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_System.m @@ -199,6 +199,12 @@ return 0; } +uint64_t +sentrycrashcm_system_freememory(void) +{ + return freeMemory(); +} + static uint64_t usableMemory(void) { diff --git a/Tests/SentryTests/SentryClient+TestInit.h b/Tests/SentryTests/SentryClient+TestInit.h index b999516ca80..12ddda1f54b 100644 --- a/Tests/SentryTests/SentryClient+TestInit.h +++ b/Tests/SentryTests/SentryClient+TestInit.h @@ -14,7 +14,8 @@ SentryClient (TestInit) transportAdapter:(SentryTransportAdapter *)transportAdapter fileManager:(SentryFileManager *)fileManager threadInspector:(SentryThreadInspector *)threadInspector - random:(id)random; + random:(id)random + crashWrapper:(SentryCrashWrapper *)crashWrapper; @end diff --git a/Tests/SentryTests/SentryClientTests.swift b/Tests/SentryTests/SentryClientTests.swift index d7dfe566dad..6555e3dfad1 100644 --- a/Tests/SentryTests/SentryClientTests.swift +++ b/Tests/SentryTests/SentryClientTests.swift @@ -27,6 +27,7 @@ class SentryClientTest: XCTestCase { let trace = SentryTracer(transactionContext: TransactionContext(name: "SomeTransaction", operation: "SomeOperation"), hub: nil) let transaction: Transaction + let crashWrapper = TestSentryCrashWrapper.sharedInstance() init() { session = SentrySession(releaseName: "release") @@ -59,7 +60,7 @@ class SentryClientTest: XCTestCase { ]) configureOptions(options) - client = Client(options: options, transportAdapter: transportAdapter, fileManager: fileManager, threadInspector: threadInspector, random: random) + client = Client(options: options, transportAdapter: transportAdapter, fileManager: fileManager, threadInspector: threadInspector, random: random, crashWrapper: crashWrapper) } catch { XCTFail("Options could not be created") } @@ -497,6 +498,20 @@ class SentryClientTest: XCTestCase { } } + func testCaptureCrash_FreeMemory() { + let event = TestData.event + event.threads = nil + event.debugMeta = nil + + fixture.crashWrapper.internalFreeMemory = 123_456 + fixture.getSut().captureCrash(event, with: fixture.scope) + + assertLastSentEventWithAttachment { actual in + let eventFreeMemory = actual.context?["device"]?["free_memory"] as? Int + XCTAssertEqual(eventFreeMemory, 123_456) + } + } + func testCaptureErrorWithUserInfo() { let expectedValue = "val" let error = NSError(domain: "domain", code: 0, userInfo: ["key": expectedValue]) diff --git a/Tests/SentryTests/SentryCrash/TestSentryCrashWrapper.h b/Tests/SentryTests/SentryCrash/TestSentryCrashWrapper.h index ed1dbc0e929..9409f554bf3 100644 --- a/Tests/SentryTests/SentryCrash/TestSentryCrashWrapper.h +++ b/Tests/SentryTests/SentryCrash/TestSentryCrashWrapper.h @@ -25,6 +25,8 @@ SENTRY_NO_INIT @property (nonatomic, assign) BOOL closeCalled; +@property (nonatomic, assign) NSInteger internalFreeMemory; + @end NS_ASSUME_NONNULL_END diff --git a/Tests/SentryTests/SentryCrash/TestSentryCrashWrapper.m b/Tests/SentryTests/SentryCrash/TestSentryCrashWrapper.m index d7b26356226..a6998a70f09 100644 --- a/Tests/SentryTests/SentryCrash/TestSentryCrashWrapper.m +++ b/Tests/SentryTests/SentryCrash/TestSentryCrashWrapper.m @@ -14,6 +14,7 @@ + (instancetype)sharedInstance instance.internalIsApplicationInForeground = YES; instance.installAsyncHooksCalled = NO; instance.closeCalled = NO; + instance.internalFreeMemory = 0; return instance; } @@ -57,4 +58,9 @@ - (NSDictionary *)systemInfo return @{}; } +- (NSInteger)freeMemory +{ + return self.internalFreeMemory; +} + @end