mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-23 06:35:51 +00:00
Add enableDectionAppKillWhileInForeground option to BITCrashManager
This option implements basic heuristics to detect if the app got killed by the iOS watchdog while running in foreground, which can only happen if: - The app tried to allocate too much memory - Main thread doesn't respond for some time It is not possible to detect all cases where such kills can occur.
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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:@"<uuid type=\"app\" arch=\"%@\">%@</uuid>",
|
||||
[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];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -735,9 +859,102 @@ NSString *const kBITCrashManagerStatus = @"BITCrashManagerStatus";
|
||||
});
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (report == 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 && 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) {
|
||||
if (report.uuidRef != NULL) {
|
||||
crashUUID = (NSString *) CFBridgingRelease(CFUUIDCreateString(NULL, report.uuidRef));
|
||||
}
|
||||
NSString *installString = bit_appAnonID() ?: @"";
|
||||
NSString *crashLogString = [BITCrashReportTextFormatter stringValueForCrashReport:report crashReporterKey:installString];
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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:@"<crash><applicationname>%s</applicationname><uuids>%@</uuids><bundleidentifier>%@</bundleidentifier><systemversion>%@</systemversion><platform>%@</platform><senderversion>%@</senderversion><version>%@</version><uuid>%@</uuid><log><![CDATA[%@]]></log><userid>%@</userid><username>%@</username><contact>%@</contact><installstring>%@</installstring><description><![CDATA[%@]]></description></crash>",
|
||||
[[[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:@"]]" @"]]><![CDATA[" @">" options:NSLiteralSearch range:NSMakeRange(0,crashLogString.length)],
|
||||
userid,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -464,10 +464,18 @@ static NSInteger bit_binaryImageSort(id binary1, id binary2, void *context) {
|
||||
{
|
||||
NSString *archName = @"???";
|
||||
if (imageInfo.codeType != nil && imageInfo.codeType.typeEncoding == PLCrashReportProcessorTypeEncodingMach) {
|
||||
switch (imageInfo.codeType.type) {
|
||||
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 (imageInfo.codeType.subtype) {
|
||||
switch (subType) {
|
||||
case CPU_SUBTYPE_ARM_V6:
|
||||
archName = @"armv6";
|
||||
break;
|
||||
@@ -488,7 +496,7 @@ static NSInteger bit_binaryImageSort(id binary1, id binary2, void *context) {
|
||||
|
||||
case CPU_TYPE_ARM64:
|
||||
/* Apple includes subtype for ARM64 binaries. */
|
||||
switch (imageInfo.codeType.subtype) {
|
||||
switch (subType) {
|
||||
case CPU_SUBTYPE_ARM_ALL:
|
||||
archName = @"arm64";
|
||||
break;
|
||||
@@ -519,7 +527,6 @@ static NSInteger bit_binaryImageSort(id binary1, id binary2, void *context) {
|
||||
// Use the default archName value (initialized above).
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return archName;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user