@@ -177,6 +177,13 @@ @interface NSDictionary (BSGKSMerge)
177177- (NSDictionary *)BSG_mergedInto : (NSDictionary *)dest ;
178178@end
179179
180+ @interface RegisterErrorData : NSObject
181+ @property (nonatomic , strong ) NSString *errorClass;
182+ @property (nonatomic , strong ) NSString *errorMessage;
183+ + (instancetype )errorDataFromThreads : (NSArray *)threads ;
184+ - (instancetype )initWithClass : (NSString *_Nonnull)errorClass message : (NSString *_Nonnull)errorMessage NS_DESIGNATED_INITIALIZER;
185+ @end
186+
180187@interface BugsnagCrashReport ()
181188
182189/* *
@@ -217,10 +224,16 @@ - (instancetype)initWithKSReport:(NSDictionary *)report {
217224
218225 _error = [report valueForKeyPath: @" crash.error" ];
219226 _errorType = _error[BSGKeyType];
220- _errorClass = BSGParseErrorClass (_error, _errorType);
221- _errorMessage = BSGParseErrorMessage (report, _error, _errorType);
222- _binaryImages = report[@" binary_images" ];
223227 _threads = [report valueForKeyPath: @" crash.threads" ];
228+ RegisterErrorData *data = [RegisterErrorData errorDataFromThreads: _threads];
229+ if (data) {
230+ _errorClass = data.errorClass ;
231+ _errorMessage = data.errorMessage ;
232+ } else {
233+ _errorClass = BSGParseErrorClass (_error, _errorType);
234+ _errorMessage = BSGParseErrorMessage (report, _error, _errorType);
235+ }
236+ _binaryImages = report[@" binary_images" ];
224237 _breadcrumbs = BSGParseBreadcrumbs (report);
225238 _severity = BSGParseSeverity (
226239 [report valueForKeyPath: @" user.state.crash.severity" ]);
@@ -542,12 +555,6 @@ - (NSArray *)serializeThreadsWithException:(NSMutableDictionary *)exception {
542555 BOOL isCrashedThread = [thread[@" crashed" ] boolValue ];
543556
544557 if (isCrashedThread) {
545- NSString *errMsg = [self enhancedErrorMessageForThread: thread];
546-
547- if (errMsg) { // use enhanced error message (currently swift assertions)
548- BSGDictInsertIfNotNil (exception, errMsg, BSGKeyMessage);
549- }
550-
551558 NSUInteger seen = 0 ;
552559 NSMutableArray *stacktrace = [NSMutableArray array ];
553560
@@ -593,41 +600,47 @@ - (NSArray *)serializeThreadsWithException:(NSMutableDictionary *)exception {
593600 return bugsnagThreads;
594601}
595602
596- /* *
597- * Returns the enhanced error message for the thread, or nil if none exists.
598- *
599- * This relies very heavily on heuristics rather than any documented APIs.
600- */
601- - (NSString *)enhancedErrorMessageForThread : (NSDictionary *)thread {
602- NSDictionary *notableAddresses = thread[@" notable_addresses" ];
603- NSMutableArray *msgBuffer = [NSMutableArray new ];
604- BOOL hasReservedWord = NO ;
605-
606- if (notableAddresses) {
603+ - (NSString *_Nullable)enhancedErrorMessageForThread : (NSDictionary *_Nullable)thread {
604+ return [self errorMessage ];
605+ }
606+
607+ @end
608+
609+ @implementation RegisterErrorData
610+ + (instancetype )errorDataFromThreads : (NSArray *)threads {
611+ for (NSDictionary *thread in threads) {
612+ if (![thread[@" crashed" ] boolValue ]) {
613+ continue ;
614+ }
615+ NSDictionary *notableAddresses = thread[@" notable_addresses" ];
616+ NSMutableArray *interestingValues = [NSMutableArray new ];
617+ NSString *reservedWord = nil ;
618+
607619 for (NSString *key in notableAddresses) {
608- if (![key hasPrefix: @" stack" ]) { // skip stack frames, only use register values
609- NSDictionary *data = notableAddresses[key];
610- NSString *contentValue = data[@" value" ];
611-
612- hasReservedWord = hasReservedWord || [self isReservedWord: contentValue];
613-
620+ if ([key hasPrefix: @" stack" ]) { // skip stack frames, only use register values
621+ continue ;
622+ }
623+ NSDictionary *data = notableAddresses[key];
624+ if (![@" string" isEqualToString: data[BSGKeyType]]) {
625+ continue ;
626+ }
627+ NSString *contentValue = data[@" value" ];
628+
629+ if ([self isReservedWord: contentValue]) {
630+ reservedWord = contentValue;
631+ } else if (!([[contentValue componentsSeparatedByString: @" /" ] count ] > 2 )) {
614632 // must be a string that isn't a reserved word and isn't a filepath
615- if ([@" string" isEqualToString: data[BSGKeyType]]
616- && ![self isReservedWord: contentValue]
617- && !([[contentValue componentsSeparatedByString: @" /" ] count ] > 2 )) {
618-
619- [msgBuffer addObject: contentValue];
620- }
633+ [interestingValues addObject: contentValue];
621634 }
622635 }
623- [msgBuffer sortUsingSelector: @selector (localizedCaseInsensitiveCompare: )];
624- }
625-
626- if (hasReservedWord && [msgBuffer count ] > 0 ) { // needs to have a reserved word used + a message
627- return [msgBuffer componentsJoinedByString: @" | " ];
628- } else {
629- return nil ;
636+
637+ [interestingValues sortUsingSelector: @selector (localizedCaseInsensitiveCompare: )];
638+
639+ NSString *message = [interestingValues componentsJoinedByString: @" | " ];
640+ return [[RegisterErrorData alloc ] initWithClass: reservedWord
641+ message: message];
630642 }
643+ return nil ;
631644}
632645
633646/* *
@@ -637,9 +650,24 @@ - (NSString *)enhancedErrorMessageForThread:(NSDictionary *)thread {
637650 *
638651 * For assert, "assertion failed" will be in one of the registers.
639652 */
640- - (BOOL )isReservedWord : (NSString *)contentValue {
641- return [@" assertion failed" isEqualToString: contentValue]
642- || [@" fatal error" isEqualToString: contentValue];
653+ + (BOOL )isReservedWord : (NSString *)contentValue {
654+ return [@" assertion failed" caseInsensitiveCompare: contentValue] == NSOrderedSame
655+ || [@" fatal error" caseInsensitiveCompare: contentValue] == NSOrderedSame
656+ || [@" precondition failed" caseInsensitiveCompare: contentValue] == NSOrderedSame;
657+ }
658+
659+ - (instancetype )init {
660+ return [self initWithClass: @" Unknown" message: @" <unset>" ];
643661}
644662
663+ - (instancetype )initWithClass : (NSString *)errorClass message : (NSString *)errorMessage {
664+ if (errorClass.length == 0 ) {
665+ return nil ;
666+ }
667+ if (self = [super init ]) {
668+ _errorClass = errorClass;
669+ _errorMessage = errorMessage;
670+ }
671+ return self;
672+ }
645673@end
0 commit comments