diff --git a/Classes/BITCrashManager.h b/Classes/BITCrashManager.h index 64f695d60f..37ac20711c 100644 --- a/Classes/BITCrashManager.h +++ b/Classes/BITCrashManager.h @@ -166,6 +166,38 @@ typedef NS_ENUM(NSUInteger, BITCrashManagerStatus) { @property (nonatomic, assign, getter=isMachExceptionHandlerEnabled) BOOL enableMachExceptionHandler; +/** + * Enables heuristics to detect the app getting killed while being in the foreground + * + * This allows it to get a crash report if the app got killed while being in the foreground + * because of now of the following reasons: + * - The main thread was blocked for too long + * - The app took too long to start up + * - The app tried to allocate too much memory. If iOS did send a memory warning before killing the app because of this reason, `didReceiveMemoryWarningInLastSession` returns `YES`. + * - Permitted background duration if main thread is running in an endless loop + * - App failed to resume in time if main thread is running in an endless loop + * + * The following kills can _NOT_ be detected: + * - Terminating the app takes too long + * - Permitted background duration too long for all other cases + * - App failed to resume in time for all other cases + * - possibly more cases + * + * Crash reports triggered by this mechanisms do _NOT_ contain any stack traces since the time of the kill + * cannot be intercepted and hence no stack trace of the time of the kill event can't be gathered. + * + * Default: _NO_ + * + * @warning This is a heuristic and it _MAY_ report false positives! + * + * @see wasKilledInLastSession + * @see didReceiveMemoryWarningInLastSession + * @see [Apple Technical Note TN2151](https://developer.apple.com/library/ios/technotes/tn2151/_index.html) + * @see [Apple Technical Q&A QA1693](https://developer.apple.com/library/ios/qa/qa1693/_index.html) + */ +@property (nonatomic, assign, getter = isAppKillDetectionWhileInForegroundEnabled) BOOL enableAppKillDetectionWhileInForeground; + + /** * Set the callbacks that will be executed prior to program termination after a crash has occurred * @@ -224,6 +256,48 @@ typedef NS_ENUM(NSUInteger, BITCrashManagerStatus) { @property (nonatomic, readonly) BOOL didCrashInLastSession; +/** + Indicates if the app was killed while being in foreground from the iOS + + If `enableDectionAppKillWhileInForeground` is enabled, use this on startup to check if the + app starts the first time after it was killed by iOS in the previous session. + + This can happen if it consumed too much memory or the watchdog killed the app because it + took too long to startup or blocks the main thread for too long, or other reasons. See Apple + documentation: https://developer.apple.com/library/ios/qa/qa1693/_index.html + + See `enableDectionAppKillWhileInForeground` for more details about which kind of kills can be detected. + + @warning This property only has a correct value, once `[BITHockeyManager startManager]` was + invoked! In addition, it is automatically disabled while a debugger session is active! + + @see enableAppKillDetectionWhileInForeground + @see didReceiveMemoryWarningInLastSession + */ +@property (nonatomic, readonly) BOOL wasKilledInLastSession; + + +/** + Indicates if the app did receive a low memory warning in the last session + + It may happen that low memory warning where send but couldn't be logged, since iOS + killed the app before updating the flag in the filesystem did complete. + + This property may be true in case of low memory kills, but it doesn't have to be! Apps + can also be killed without the app ever receiving a low memory warning. + + Also the app could have received a low memory warning, but the reason for being killed was + actually different. + + @warning This property only has a correct value, once `[BITHockeyManager startManager]` was + invoked! + + @see enableAppKillDetectionWhileInForeground + @see wasKilledInLastSession + */ +@property (nonatomic, readonly) BOOL didReceiveMemoryWarningInLastSession; + + /** Provides the time between startup and crash in seconds diff --git a/Classes/BITCrashManager.m b/Classes/BITCrashManager.m index fa1c8bcac9..76f2a4c811 100644 --- a/Classes/BITCrashManager.m +++ b/Classes/BITCrashManager.m @@ -55,6 +55,19 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; +NSString *const kBITAppWentIntoBackgroundSafely = @"BITAppWentIntoBackgroundSafely"; +NSString *const kBITAppDidReceiveLowMemoryNotification = @"BITAppDidReceiveLowMemoryNotification"; +NSString *const kBITAppVersion = @"BITAppVersion"; +NSString *const kBITAppOSVersion = @"BITAppOSVersion"; +NSString *const kBITAppUUIDs = @"BITAppUUIDs"; + +NSString *const kBITFakeCrashUUID = @"BITFakeCrashUUID"; +NSString *const kBITFakeCrashAppVersion = @"BITFakeCrashAppVersion"; +NSString *const kBITFakeCrashAppBundleIdentifier = @"BITFakeCrashAppBundleIdentifier"; +NSString *const kBITFakeCrashOSVersion = @"BITFakeCrashOSVersion"; +NSString *const kBITFakeCrashDeviceModel = @"BITFakeCrashDeviceModel"; +NSString *const kBITFakeCrashAppBinaryUUID = @"BITFakeCrashAppBinaryUUID"; +NSString *const kBITFakeCrashReport = @"BITFakeCrashAppString"; @interface BITCrashManager () @@ -83,7 +96,13 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; BOOL _sendingInProgress; BOOL _isSetup; + BOOL _didLogLowMemoryWarning; + id _appDidBecomeActiveObserver; + id _appWillTerminateObserver; + id _appDidEnterBackgroundObserver; + id _appWillEnterForegroundObserver; + id _appDidReceiveLowMemoryWarningObserver; id _networkDidBecomeReachableObserver; } @@ -105,6 +124,7 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; _didCrashInLastSession = NO; _timeintervalCrashInLastSessionOccured = -1; + _didLogLowMemoryWarning = NO; _approvedCrashReports = [[NSMutableDictionary alloc] init]; @@ -287,20 +307,116 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; [strongSelf triggerDelayedProcessing]; }]; } + + if (nil == _appWillTerminateObserver) { + _appWillTerminateObserver = [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillTerminateNotification + object:nil + queue:NSOperationQueue.mainQueue + usingBlock:^(NSNotification *note) { + typeof(self) strongSelf = weakSelf; + [strongSelf leavingAppSafely]; + }]; + } + + if (nil == _appDidEnterBackgroundObserver) { + _appDidEnterBackgroundObserver = [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidEnterBackgroundNotification + object:nil + queue:NSOperationQueue.mainQueue + usingBlock:^(NSNotification *note) { + typeof(self) strongSelf = weakSelf; + [strongSelf leavingAppSafely]; + }]; + } + + if (nil == _appWillEnterForegroundObserver) { + _appWillEnterForegroundObserver = [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillEnterForegroundNotification + object:nil + queue:NSOperationQueue.mainQueue + usingBlock:^(NSNotification *note) { + typeof(self) strongSelf = weakSelf; + [strongSelf appEnteredForeground]; + }]; + } + + if (nil == _appDidReceiveLowMemoryWarningObserver) { + _appDidReceiveLowMemoryWarningObserver = [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidReceiveMemoryWarningNotification + object:nil + queue:NSOperationQueue.mainQueue + usingBlock:^(NSNotification *note) { + // we only need to log this once + if (!_didLogLowMemoryWarning) { + [[NSUserDefaults standardUserDefaults] setBool:YES forKey:kBITAppDidReceiveLowMemoryNotification]; + _didLogLowMemoryWarning = YES; + } + }]; + } } - (void) unregisterObservers { - if(_appDidBecomeActiveObserver) { - [[NSNotificationCenter defaultCenter] removeObserver:_appDidBecomeActiveObserver]; - _appDidBecomeActiveObserver = nil; - } + [self unregisterObserver:_appDidBecomeActiveObserver]; + [self unregisterObserver:_appWillTerminateObserver]; + [self unregisterObserver:_appDidEnterBackgroundObserver]; + [self unregisterObserver:_appWillEnterForegroundObserver]; + [self unregisterObserver:_appDidReceiveLowMemoryWarningObserver]; - if(_networkDidBecomeReachableObserver) { - [[NSNotificationCenter defaultCenter] removeObserver:_networkDidBecomeReachableObserver]; - _networkDidBecomeReachableObserver = nil; + [self unregisterObserver:_networkDidBecomeReachableObserver]; +} + +- (void) unregisterObserver:(id)observer { + if (observer) { + [[NSNotificationCenter defaultCenter] removeObserver:observer]; + observer = nil; } } +- (void)leavingAppSafely { + if (self.isAppKillDetectionWhileInForegroundEnabled) + [[NSUserDefaults standardUserDefaults] setBool:YES forKey:kBITAppWentIntoBackgroundSafely]; +} + +- (void)appEnteredForeground { + // we disable kill detection while the debugger is running, since we'd get only false positives if the app is terminated by the user using the debugger + if (self.isDebuggerAttached) { + [[NSUserDefaults standardUserDefaults] setBool:YES forKey:kBITAppWentIntoBackgroundSafely]; + } else if (self.isAppKillDetectionWhileInForegroundEnabled) { + [[NSUserDefaults standardUserDefaults] setBool:NO forKey:kBITAppWentIntoBackgroundSafely]; + + static dispatch_once_t predAppData; + + dispatch_once(&predAppData, ^{ + id bundleVersion = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]; + if (bundleVersion && [bundleVersion isKindOfClass:[NSString class]]) + [[NSUserDefaults standardUserDefaults] setObject:bundleVersion forKey:kBITAppVersion]; + [[NSUserDefaults standardUserDefaults] setObject:[[UIDevice currentDevice] systemVersion] forKey:kBITAppOSVersion]; + + NSString *uuidString =[NSString stringWithFormat:@"%@", + [self deviceArchitecture], + [self executableUUID] + ]; + + [[NSUserDefaults standardUserDefaults] setObject:uuidString forKey:kBITAppUUIDs]; + }); + } +} + +- (NSString *)deviceArchitecture { + NSString *archName = @"???"; + + size_t size; + cpu_type_t type; + cpu_subtype_t subtype; + size = sizeof(type); + if (sysctlbyname("hw.cputype", &type, &size, NULL, 0)) + return archName; + + size = sizeof(subtype); + if (sysctlbyname("hw.cpusubtype", &subtype, &size, NULL, 0)) + return archName; + + archName = [BITCrashReportTextFormatter bit_archNameFromCPUType:type subType:subtype] ?: @"???"; + + return archName; +} /** * Get the userID from the delegate which should be stored with the crash report @@ -437,6 +553,35 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; } } +/** + * Write a meta file for a new crash report + * + * @param filename the crash reports temp filename + */ +- (void)storeMetaDataForCrashReportFilename:(NSString *)filename { + NSError *error = NULL; + NSMutableDictionary *metaDict = [NSMutableDictionary dictionaryWithCapacity:4]; + NSString *applicationLog = @""; + NSString *errorString = nil; + + [self addStringValueToKeychain:[self userNameForCrashReport] forKey:[NSString stringWithFormat:@"%@.%@", filename, kBITCrashMetaUserName]]; + [self addStringValueToKeychain:[self userEmailForCrashReport] forKey:[NSString stringWithFormat:@"%@.%@", filename, kBITCrashMetaUserEmail]]; + [self addStringValueToKeychain:[self userIDForCrashReport] forKey:[NSString stringWithFormat:@"%@.%@", filename, kBITCrashMetaUserID]]; + + if (self.delegate != nil && [self.delegate respondsToSelector:@selector(applicationLogForCrashManager:)]) { + applicationLog = [self.delegate applicationLogForCrashManager:self] ?: @""; + } + [metaDict setObject:applicationLog forKey:kBITCrashMetaApplicationLog]; + + NSData *plist = [NSPropertyListSerialization dataFromPropertyList:(id)metaDict + format:NSPropertyListBinaryFormat_v1_0 + errorDescription:&errorString]; + if (plist) { + [plist writeToFile:[_crashesDir stringByAppendingPathComponent: [filename stringByAppendingPathExtension:@"meta"]] atomically:YES]; + } else { + BITHockeyLog(@"ERROR: Writing crash meta data failed. %@", error); + } +} #pragma mark - PLCrashReporter @@ -481,28 +626,7 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; [crashData writeToFile:[_crashesDir stringByAppendingPathComponent: cacheFilename] atomically:YES]; - // write the meta file - NSMutableDictionary *metaDict = [NSMutableDictionary dictionaryWithCapacity:4]; - NSString *applicationLog = @""; - NSString *errorString = nil; - - [self addStringValueToKeychain:[self userNameForCrashReport] forKey:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserName]]; - [self addStringValueToKeychain:[self userEmailForCrashReport] forKey:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserEmail]]; - [self addStringValueToKeychain:[self userIDForCrashReport] forKey:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserID]]; - - if (self.delegate != nil && [self.delegate respondsToSelector:@selector(applicationLogForCrashManager:)]) { - applicationLog = [self.delegate applicationLogForCrashManager:self] ?: @""; - } - [metaDict setObject:applicationLog forKey:kBITCrashMetaApplicationLog]; - - NSData *plist = [NSPropertyListSerialization dataFromPropertyList:(id)metaDict - format:NSPropertyListBinaryFormat_v1_0 - errorDescription:&errorString]; - if (plist) { - [plist writeToFile:[NSString stringWithFormat:@"%@.meta", [_crashesDir stringByAppendingPathComponent: cacheFilename]] atomically:YES]; - } else { - BITHockeyLog(@"ERROR: Writing crash meta data failed. %@", error); - } + [self storeMetaDataForCrashReportFilename:cacheFilename]; } } } @@ -734,10 +858,103 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; _isSetup = YES; }); } + + if ([[NSUserDefaults standardUserDefaults] valueForKey:kBITAppDidReceiveLowMemoryNotification]) + _didReceiveMemoryWarningInLastSession = [[NSUserDefaults standardUserDefaults] boolForKey:kBITAppDidReceiveLowMemoryNotification]; + + if (!_didCrashInLastSession && self.isAppKillDetectionWhileInForegroundEnabled) { + BOOL didAppSwitchToBackgroundSafely = YES; + + if ([[NSUserDefaults standardUserDefaults] valueForKey:kBITAppWentIntoBackgroundSafely]) + didAppSwitchToBackgroundSafely = [[NSUserDefaults standardUserDefaults] boolForKey:kBITAppWentIntoBackgroundSafely]; + + if (!didAppSwitchToBackgroundSafely) { + NSLog(@"AppHasBeenKilled!"); + + [self createCrashReportForAppKill]; + + _wasKilledInLastSession = YES; + _didCrashInLastSession = YES; + } + } + [self appEnteredForeground]; + [[NSUserDefaults standardUserDefaults] setBool:NO forKey:kBITAppDidReceiveLowMemoryNotification]; + [[NSUserDefaults standardUserDefaults] synchronize]; [self triggerDelayedProcessing]; } +/** + * Creates a fake crash report because the app was killed while being in foreground + */ +- (void)createCrashReportForAppKill { + NSString *fakeReportUUID = bit_UUID(); + + NSString *fakeReportAppVersion = [[NSUserDefaults standardUserDefaults] objectForKey:kBITAppVersion]; + if (!fakeReportAppVersion) + return; + + NSString *fakeReportOSVersion = [[NSUserDefaults standardUserDefaults] objectForKey:kBITAppOSVersion] ?: [[UIDevice currentDevice] systemVersion]; + NSString *fakeReportAppBundleIdentifier = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleIdentifier"]; + NSString *fakeReportDeviceModel = [self getDevicePlatform] ?: @"Unknown"; + NSString *fakeReportAppUUIDs = [[NSUserDefaults standardUserDefaults] objectForKey:kBITAppUUIDs] ?: @""; + + NSMutableString *fakeReportString = [NSMutableString string]; + + [fakeReportString appendFormat:@"Incident Identifier: %@\n", fakeReportUUID]; + [fakeReportString appendFormat:@"CrashReporter Key: %@\n", bit_appAnonID() ?: @"???"]; + [fakeReportString appendFormat:@"Hardware Model: %@\n", fakeReportDeviceModel]; + [fakeReportString appendFormat:@"Identifier: %@\n", fakeReportAppBundleIdentifier]; + [fakeReportString appendFormat:@"Version: %@\n", fakeReportAppVersion]; + [fakeReportString appendString:@"Code Type: ARM\n"]; + [fakeReportString appendString:@"\n"]; + + NSLocale *enUSPOSIXLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]; + NSDateFormatter *rfc3339Formatter = [[NSDateFormatter alloc] init]; + [rfc3339Formatter setLocale:enUSPOSIXLocale]; + [rfc3339Formatter setDateFormat:@"yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'"]; + [rfc3339Formatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]]; + + // we use the current date, since we don't know when the kill actually happened + [fakeReportString appendFormat:@"Date/Time: %@\n", [rfc3339Formatter stringFromDate:[NSDate date]]]; + [fakeReportString appendFormat:@"OS Version: %@\n", fakeReportOSVersion]; + [fakeReportString appendString:@"Report Version: 104\n"]; + [fakeReportString appendString:@"\n"]; + [fakeReportString appendString:@"Exception Type: SIGKILL\n"]; + [fakeReportString appendString:@"Exception Codes: 00000020 at 0x8badf00d\n"]; + [fakeReportString appendString:@"\n"]; + [fakeReportString appendString:@"Application Specific Information:\n"]; + [fakeReportString appendString:@"The application was killed by the iOS watchdog."]; + if (self.didReceiveMemoryWarningInLastSession) { + [fakeReportString appendString:@" The app received at least one Low Memory Warning."]; + } + [fakeReportString appendString:@"\n"]; + + NSString *fakeReportFilename = [NSString stringWithFormat: @"%.0f", [NSDate timeIntervalSinceReferenceDate]]; + + NSString *errorString = nil; + + NSMutableDictionary *rootObj = [NSMutableDictionary dictionaryWithCapacity:2]; + [rootObj setObject:fakeReportUUID forKey:kBITFakeCrashUUID]; + [rootObj setObject:fakeReportAppVersion forKey:kBITFakeCrashAppVersion]; + [rootObj setObject:fakeReportAppBundleIdentifier forKey:kBITFakeCrashAppBundleIdentifier]; + [rootObj setObject:fakeReportOSVersion forKey:kBITFakeCrashOSVersion]; + [rootObj setObject:fakeReportDeviceModel forKey:kBITFakeCrashDeviceModel]; + [rootObj setObject:fakeReportAppUUIDs forKey:kBITFakeCrashAppBinaryUUID]; + [rootObj setObject:fakeReportString forKey:kBITFakeCrashReport]; + + NSData *plist = [NSPropertyListSerialization dataFromPropertyList:(id)rootObj + format:NSPropertyListBinaryFormat_v1_0 + errorDescription:&errorString]; + if (plist) { + if ([plist writeToFile:[_crashesDir stringByAppendingPathComponent:[fakeReportFilename stringByAppendingPathExtension:@"fake"]] atomically:YES]) { + [self storeMetaDataForCrashReportFilename:fakeReportFilename]; + } + } else { + BITHockeyLog(@"ERROR: Writing fake crash report. %@", errorString); + } +} + /** * Send all approved crash reports * @@ -755,13 +972,49 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; NSData *crashData = [NSData dataWithContentsOfFile:filename]; if ([crashData length] > 0) { - BITPLCrashReport *report = [[BITPLCrashReport alloc] initWithData:crashData error:&error]; + BITPLCrashReport *report = nil; + NSString *crashUUID = @""; + NSString *installString = nil; + NSString *crashLogString = nil; + NSString *appBundleIdentifier = nil; + NSString *appBundleVersion = nil; + NSString *osVersion = nil; + NSString *deviceModel = nil; + NSString *appBinaryUUIDs = nil; + NSString *metaFilename = nil; + + NSString *errorString = nil; + NSPropertyListFormat format; + + if ([[cacheFilename pathExtension] isEqualToString:@"fake"]) { + NSDictionary *fakeReportDict = (NSDictionary *)[NSPropertyListSerialization + propertyListFromData:crashData + mutabilityOption:NSPropertyListMutableContainersAndLeaves + format:&format + errorDescription:&errorString]; + + crashLogString = [fakeReportDict objectForKey:kBITFakeCrashReport]; + crashUUID = [fakeReportDict objectForKey:kBITFakeCrashUUID]; + appBundleIdentifier = [fakeReportDict objectForKey:kBITFakeCrashAppBundleIdentifier]; + appBundleVersion = [fakeReportDict objectForKey:kBITFakeCrashAppVersion]; + appBinaryUUIDs = [fakeReportDict objectForKey:kBITFakeCrashAppBinaryUUID]; + deviceModel = [fakeReportDict objectForKey:kBITFakeCrashDeviceModel]; + osVersion = [fakeReportDict objectForKey:kBITFakeCrashOSVersion]; + + metaFilename = [cacheFilename stringByReplacingOccurrencesOfString:@".fake" withString:@".meta"]; + if ([appBundleVersion compare:[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]] == NSOrderedSame) { + _crashIdenticalCurrentVersion = YES; + } + + } else { + report = [[BITPLCrashReport alloc] initWithData:crashData error:&error]; + } - if (report == nil) { + if (report == nil && crashLogString == nil) { BITHockeyLog(@"WARNING: Could not parse crash report"); // we cannot do anything with this report, so delete it [_fileManager removeItemAtPath:filename error:&error]; - [_fileManager removeItemAtPath:[NSString stringWithFormat:@"%@.meta", filename] error:&error]; + [_fileManager removeItemAtPath:[NSString stringWithFormat:@"%@.meta", metaFilename] error:&error]; [self removeKeyFromKeychain:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserName]]; [self removeKeyFromKeychain:[NSString stringWithFormat:@"%@.%@", cacheFilename, kBITCrashMetaUserEmail]]; @@ -769,16 +1022,23 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; continue; } - NSString *crashUUID = @""; - if (report.uuidRef != NULL) { - crashUUID = (NSString *) CFBridgingRelease(CFUUIDCreateString(NULL, report.uuidRef)); + if (report) { + if (report.uuidRef != NULL) { + crashUUID = (NSString *) CFBridgingRelease(CFUUIDCreateString(NULL, report.uuidRef)); + } + metaFilename = [filename stringByAppendingPathExtension:@"meta"]; + crashLogString = [BITCrashReportTextFormatter stringValueForCrashReport:report crashReporterKey:installString]; + appBundleIdentifier = report.applicationInfo.applicationIdentifier; + appBundleVersion = report.applicationInfo.applicationVersion; + osVersion = report.systemInfo.operatingSystemVersion; + deviceModel = [self getDevicePlatform]; + appBinaryUUIDs = [self extractAppUUIDs:report]; + if ([report.applicationInfo.applicationVersion compare:[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]] == NSOrderedSame) { + _crashIdenticalCurrentVersion = YES; + } } - NSString *installString = bit_appAnonID() ?: @""; - NSString *crashLogString = [BITCrashReportTextFormatter stringValueForCrashReport:report crashReporterKey:installString]; - if ([report.applicationInfo.applicationVersion compare:[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]] == NSOrderedSame) { - _crashIdenticalCurrentVersion = YES; - } + installString = bit_appAnonID() ?: @""; if (crashes == nil) { crashes = [NSMutableString string]; @@ -790,10 +1050,7 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; NSString *applicationLog = @""; NSString *description = @""; - NSString *errorString = nil; - NSPropertyListFormat format; - - NSData *plist = [NSData dataWithContentsOfFile:[filename stringByAppendingString:@".meta"]]; + NSData *plist = [NSData dataWithContentsOfFile:[_crashesDir stringByAppendingPathComponent:metaFilename]]; if (plist) { NSDictionary *metaDict = (NSDictionary *)[NSPropertyListSerialization propertyListFromData:plist @@ -815,12 +1072,12 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus"; [crashes appendFormat:@"%s%@%@%@%@%@%@%@%@%@%@%@", [[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleExecutable"] UTF8String], - [self extractAppUUIDs:report], - report.applicationInfo.applicationIdentifier, - report.systemInfo.operatingSystemVersion, - [self getDevicePlatform], + appBinaryUUIDs, + appBundleIdentifier, + osVersion, + deviceModel, [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"], - report.applicationInfo.applicationVersion, + appBundleVersion, crashUUID, [crashLogString stringByReplacingOccurrencesOfString:@"]]>" withString:@"]]" @"]]>" options:NSLiteralSearch range:NSMakeRange(0,crashLogString.length)], userid, diff --git a/Classes/BITCrashReportTextFormatter.h b/Classes/BITCrashReportTextFormatter.h index 13ab19f964..5ddef4a22d 100644 --- a/Classes/BITCrashReportTextFormatter.h +++ b/Classes/BITCrashReportTextFormatter.h @@ -49,5 +49,6 @@ + (NSString *)stringValueForCrashReport:(PLCrashReport *)report crashReporterKey:(NSString *)crashReporterKey; + (NSArray *)arrayOfAppUUIDsForCrashReport:(PLCrashReport *)report; ++ (NSString *)bit_archNameFromCPUType:(uint64_t)cpuType subType:(uint64_t)subType; @end diff --git a/Classes/BITCrashReportTextFormatter.m b/Classes/BITCrashReportTextFormatter.m index 6094affc23..c8b060d34c 100644 --- a/Classes/BITCrashReportTextFormatter.m +++ b/Classes/BITCrashReportTextFormatter.m @@ -464,66 +464,73 @@ static NSInteger bit_binaryImageSort(id binary1, id binary2, void *context) { { NSString *archName = @"???"; if (imageInfo.codeType != nil && imageInfo.codeType.typeEncoding == PLCrashReportProcessorTypeEncodingMach) { - switch (imageInfo.codeType.type) { - case CPU_TYPE_ARM: - /* Apple includes subtype for ARM binaries. */ - switch (imageInfo.codeType.subtype) { - case CPU_SUBTYPE_ARM_V6: - archName = @"armv6"; - break; - - case CPU_SUBTYPE_ARM_V7: - archName = @"armv7"; - break; - - case CPU_SUBTYPE_ARM_V7S: - archName = @"armv7s"; - break; - - default: - archName = @"arm-unknown"; - break; - } - break; - - case CPU_TYPE_ARM64: - /* Apple includes subtype for ARM64 binaries. */ - switch (imageInfo.codeType.subtype) { - case CPU_SUBTYPE_ARM_ALL: - archName = @"arm64"; - break; - - case CPU_SUBTYPE_ARM_V8: - archName = @"arm64"; - break; - - default: - archName = @"arm64-unknown"; - break; - } - break; - - case CPU_TYPE_X86: - archName = @"i386"; - break; - - case CPU_TYPE_X86_64: - archName = @"x86_64"; - break; - - case CPU_TYPE_POWERPC: - archName = @"powerpc"; - break; - - default: - // Use the default archName value (initialized above). - break; - } + archName = [BITCrashReportTextFormatter bit_archNameFromCPUType:imageInfo.codeType.type subType:imageInfo.codeType.subtype]; } return archName; } ++ (NSString *)bit_archNameFromCPUType:(uint64_t)cpuType subType:(uint64_t)subType { + NSString *archName = @"???"; + switch (cpuType) { + case CPU_TYPE_ARM: + /* Apple includes subtype for ARM binaries. */ + switch (subType) { + case CPU_SUBTYPE_ARM_V6: + archName = @"armv6"; + break; + + case CPU_SUBTYPE_ARM_V7: + archName = @"armv7"; + break; + + case CPU_SUBTYPE_ARM_V7S: + archName = @"armv7s"; + break; + + default: + archName = @"arm-unknown"; + break; + } + break; + + case CPU_TYPE_ARM64: + /* Apple includes subtype for ARM64 binaries. */ + switch (subType) { + case CPU_SUBTYPE_ARM_ALL: + archName = @"arm64"; + break; + + case CPU_SUBTYPE_ARM_V8: + archName = @"arm64"; + break; + + default: + archName = @"arm64-unknown"; + break; + } + break; + + case CPU_TYPE_X86: + archName = @"i386"; + break; + + case CPU_TYPE_X86_64: + archName = @"x86_64"; + break; + + case CPU_TYPE_POWERPC: + archName = @"powerpc"; + break; + + default: + // Use the default archName value (initialized above). + break; + } + + return archName; +} + /** * Format a stack frame for display in a thread backtrace.