diff --git a/Classes/BITChannel.h b/Classes/BITChannel.h index 3e247f1a27..0fad80f818 100644 --- a/Classes/BITChannel.h +++ b/Classes/BITChannel.h @@ -40,6 +40,7 @@ FOUNDATION_EXPORT char *BITSafeJsonEventsString; - (BOOL)enqueueTelemetryItem:(BITTelemetryData *)item; @end + NS_ASSUME_NONNULL_END #endif /* HOCKEYSDK_FEATURE_METRICS */ diff --git a/Classes/BITChannel.m b/Classes/BITChannel.m index be3490c2b4..4b036c3b2e 100644 --- a/Classes/BITChannel.m +++ b/Classes/BITChannel.m @@ -162,27 +162,26 @@ NS_ASSUME_NONNULL_BEGIN // Since we can't persist every event right away, we write it to a simple C string. // This can then be written to disk by a signal handler in case of a crash. - bit_appendStringToSafeJsonStream(string, &(BITSafeJsonEventsString)); + BITSafeJsonEventsString = bit_jsonStreamByAppendingJsonString(BITSafeJsonEventsString, string); _dataItemCount += 1; } } -void bit_appendStringToSafeJsonStream(NSString *string, char **jsonString) { - if (jsonString == NULL) { return; } +char * bit_jsonStreamByAppendingJsonString(char *json_stream, NSString *jsonString) { + if ((json_stream == NULL) || !json_stream) { - if (!string) { return; } - - if (*jsonString == NULL || strlen(*jsonString) == 0) { - bit_resetSafeJsonStream(jsonString); + return strdup(""); } - if (string.length == 0) { return; } + if (!jsonString || (jsonString.length == 0)) { + return json_stream; + } - char *new_string = NULL; - // Concatenate old string with new JSON string and add a comma. - asprintf(&new_string, "%s%.*s\n", *jsonString, (int)MIN(string.length, (NSUInteger)INT_MAX), string.UTF8String); - free(*jsonString); - *jsonString = new_string; + + char *concatenated_string = NULL; + // Concatenate old string with new JSON string and add a new line. + asprintf(&concatenated_string, "%s%.*s\n", json_stream, (int)MIN(jsonString.length, (NSUInteger)INT_MAX), jsonString.UTF8String); + return concatenated_string; } void bit_resetSafeJsonStream(char **string) { diff --git a/Classes/BITChannelPrivate.h b/Classes/BITChannelPrivate.h index 080e3a381d..20bf567b6a 100644 --- a/Classes/BITChannelPrivate.h +++ b/Classes/BITChannelPrivate.h @@ -73,16 +73,16 @@ NS_ASSUME_NONNULL_BEGIN /** * A C function that serializes a given dictionary to JSON and appends it to a char string * - * @param dictionary A dictionary which will be serialized to JSON and then appended to the string. - * @param string The C string which the dictionary's JSON representation will be appended to. + * @param existing_json_stream A C string containing JSON items in the JSON Stream format. + * @param jsonString A NSString object containing a valid JSON item. */ -void bit_appendStringToSafeJsonStream(NSString *string, char *__nonnull*__nonnull jsonStream); +char * bit_jsonStreamByAppendingJsonString(char *existing_json_stream, NSString *jsonString); /** -* Reset BITSafeJsonEventsString so we can start appending JSON dictionaries. -* -* @param string The string that will be reset. -*/ + * Reset BITSafeJsonEventsString so we can start appending JSON dictionaries. + * + * @param string The string that will be reset. + */ void bit_resetSafeJsonStream(char *__nonnull*__nonnull jsonStream); /** diff --git a/Classes/BITCrashManager.m b/Classes/BITCrashManager.m index 8ab7b65dfe..a951849439 100644 --- a/Classes/BITCrashManager.m +++ b/Classes/BITCrashManager.m @@ -28,7 +28,7 @@ * OTHER DEALINGS IN THE SOFTWARE. */ -#import "HockeySDK.h" +#import "HockeySDKFeatureConfig.h" #if HOCKEYSDK_FEATURE_CRASH_REPORTER @@ -39,13 +39,20 @@ #import "BITHockeyHelper.h" #import "BITHockeyAppClient.h" +#import "BITCrashManager.h" +#import "BITCrashManagerPrivate.h" #import "BITCrashAttachment.h" #import "BITHockeyBaseManagerPrivate.h" -#import "BITCrashManagerPrivate.h" #import "BITCrashReportTextFormatter.h" #import "BITCrashDetailsPrivate.h" #import "BITCrashCXXExceptionHandler.h" +#if HOCKEYSDK_FEATURE_METRICS +#import "BITMetricsManagerPrivate.h" +#import "BITChannel.h" +#import "BITPersistencePrivate.h" +#endif + #include // stores the set of crashreports that have been approved but aren't sent yet @@ -81,16 +88,40 @@ static NSString *const kBITFakeCrashDeviceModel = @"BITFakeCrashDeviceModel"; static NSString *const kBITFakeCrashAppBinaryUUID = @"BITFakeCrashAppBinaryUUID"; static NSString *const kBITFakeCrashReport = @"BITFakeCrashAppString"; +#if HOCKEYSDK_FEATURE_METRICS +static char const *BITSaveEventsFilePath; +#endif static BITCrashManagerCallbacks bitCrashCallbacks = { .context = NULL, .handleSignal = NULL }; -// proxy implementation for PLCrashReporter to keep our interface stable while this can change +#if HOCKEYSDK_FEATURE_METRICS +static void bit_save_events_callback(siginfo_t *info, ucontext_t *uap, void *context) { + // Try to get a file descriptor with our pre-filled path + int fd = open(BITSaveEventsFilePath, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (fd < 0) { + return; + } + + size_t len = strlen(BITSafeJsonEventsString); + if (len > 0) { + // Simply write the whole string to disk + write(fd, BITSafeJsonEventsString, len); + } + close(fd); +} +#endif + +// Proxy implementation for PLCrashReporter to keep our interface stable while this can change static void plcr_post_crash_callback (siginfo_t *info, ucontext_t *uap, void *context) { - if (bitCrashCallbacks.handleSignal != NULL) +#if HOCKEYSDK_FEATURE_METRICS + bit_save_events_callback(info, uap, context); +#endif + if (bitCrashCallbacks.handleSignal != NULL) { bitCrashCallbacks.handleSignal(context); + } } static PLCrashReporterCallbacks plCrashCallbacks = { @@ -99,7 +130,6 @@ static PLCrashReporterCallbacks plCrashCallbacks = { .handleSignal = plcr_post_crash_callback }; - // Temporary class until PLCR catches up // We trick PLCR with an Objective-C exception. // @@ -154,8 +184,6 @@ static void uncaught_cxx_exception_handler(const BITCrashUncaughtCXXExceptionInf NSString *_settingsFile; NSString *_analyzerInProgressFile; NSFileManager *_fileManager; - - PLCrashReporterCallbacks *_crashCallBacks; BOOL _crashIdenticalCurrentVersion; @@ -183,7 +211,6 @@ static void uncaught_cxx_exception_handler(const BITCrashUncaughtCXXExceptionInf _plCrashReporter = nil; _exceptionHandler = nil; - _crashCallBacks = nil; _crashIdenticalCurrentVersion = YES; @@ -657,17 +684,18 @@ static void uncaught_cxx_exception_handler(const BITCrashUncaughtCXXExceptionInf return useremail; } - -#pragma mark - Public - +#pragma mark - CrashCallbacks /** * Set the callback for PLCrashReporter * * @param callbacks BITCrashManagerCallbacks instance */ -- (void)setCrashCallbacks: (BITCrashManagerCallbacks *) callbacks { +- (void)setCrashCallbacks:(BITCrashManagerCallbacks *)callbacks { if (!callbacks) return; + if (_isSetup) { + BITHockeyLog(@"CrashCallbacks need to be configured before calling startManager!"); + } // set our proxy callback struct bitCrashCallbacks.context = callbacks->context; @@ -675,10 +703,17 @@ static void uncaught_cxx_exception_handler(const BITCrashUncaughtCXXExceptionInf // set the PLCrashReporterCallbacks struct plCrashCallbacks.context = callbacks->context; - - _crashCallBacks = &plCrashCallbacks; } +#if HOCKEYSDK_FEATURE_METRICS +- (void)configDefaultCrashCallback { + BITMetricsManager *metricsManager = [BITHockeyManager sharedHockeyManager].metricsManager; + BITPersistence *persistence = metricsManager.persistence; + BITSaveEventsFilePath = strdup([persistence fileURLForType:BITPersistenceTypeTelemetry].UTF8String); +} +#endif + +#pragma mark - Public - (void)setAlertViewHandler:(BITCustomAlertViewHandler)alertViewHandler{ _alertViewHandler = alertViewHandler; @@ -1146,10 +1181,11 @@ static void uncaught_cxx_exception_handler(const BITCrashUncaughtCXXExceptionInf // can't break this NSError *error = NULL; - // set any user defined callbacks, hopefully the users knows what they do - if (_crashCallBacks) { - [self.plCrashReporter setCrashCallbacks:_crashCallBacks]; - } +#if HOCKEYSDK_FEATURE_METRICS + [self configDefaultCrashCallback]; +#endif + // Set plCrashReporter callback which contains our default callback and potentially user defined callbacks + [self.plCrashReporter setCrashCallbacks:&plCrashCallbacks]; // Enable the Crash Reporter if (![self.plCrashReporter enableCrashReporterAndReturnError: &error]) diff --git a/Support/HockeySDKTests/BITChannelTests.m b/Support/HockeySDKTests/BITChannelTests.m index 54e939c9ad..ed37cd847c 100644 --- a/Support/HockeySDKTests/BITChannelTests.m +++ b/Support/HockeySDKTests/BITChannelTests.m @@ -31,7 +31,6 @@ BITTelemetryContext *mockContext = mock(BITTelemetryContext.class); _sut = [[BITChannel alloc]initWithTelemetryContext:mockContext persistence:_mockPersistence]; - BITSafeJsonEventsString = NULL; } #pragma mark - Setup Tests @@ -88,34 +87,46 @@ - (void)testAppendStringToSafeJsonStream { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wnonnull" - bit_appendStringToSafeJsonStream(nil, 0); + BITSafeJsonEventsString = bit_jsonStreamByAppendingJsonString(0, nil); #pragma clang diagnostic pop - XCTAssertTrue(BITSafeJsonEventsString == NULL); + XCTAssertEqual(strcmp(BITSafeJsonEventsString, ""), 0); - BITSafeJsonEventsString = NULL; #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wnonnull" - bit_appendStringToSafeJsonStream(nil, &BITSafeJsonEventsString); + BITSafeJsonEventsString = bit_jsonStreamByAppendingJsonString(NULL, nil); #pragma clang diagnostic pop - XCTAssertTrue(BITSafeJsonEventsString == NULL); + XCTAssertEqual(strcmp(BITSafeJsonEventsString, ""), 0); + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wnonnull" + BITSafeJsonEventsString = bit_jsonStreamByAppendingJsonString(nil, nil); +#pragma clang diagnostic pop + XCTAssertEqual(strcmp(BITSafeJsonEventsString, ""), 0); - bit_appendStringToSafeJsonStream(@"", &BITSafeJsonEventsString); +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wnonnull" + BITSafeJsonEventsString = bit_jsonStreamByAppendingJsonString(NULL, @""); +#pragma clang diagnostic pop XCTAssertEqual(strcmp(BITSafeJsonEventsString,""), 0); - bit_appendStringToSafeJsonStream(@"{\"Key1\":\"Value1\"}", &BITSafeJsonEventsString); + BITSafeJsonEventsString = bit_jsonStreamByAppendingJsonString("", @"{\"Key1\":\"Value1\"}"); XCTAssertEqual(strcmp(BITSafeJsonEventsString,"{\"Key1\":\"Value1\"}\n"), 0); } - (void)testResetSafeJsonStream { + BITSafeJsonEventsString = NULL; bit_resetSafeJsonStream(&BITSafeJsonEventsString); XCTAssertEqual(strcmp(BITSafeJsonEventsString,""), 0); - BITSafeJsonEventsString = NULL; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wnonnull" + bit_resetSafeJsonStream(NULL); +#pragma clang diagnostic pop + #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wnonnull" bit_resetSafeJsonStream(nil); #pragma clang diagnostic pop - XCTAssertEqual(BITSafeJsonEventsString, NULL); BITSafeJsonEventsString = strdup("test string"); bit_resetSafeJsonStream(&BITSafeJsonEventsString);