Skip to content

Commit

Permalink
[PLAT-6075] Parse Swift 5.4 fatal error messages (#1010)
Browse files Browse the repository at this point in the history
  • Loading branch information
nickdowell committed Feb 23, 2021
1 parent 0ca1ae6 commit 670a768
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 18 deletions.
40 changes: 24 additions & 16 deletions Bugsnag/Payload/BugsnagError.m
Original file line number Diff line number Diff line change
Expand Up @@ -127,17 +127,25 @@ + (BugsnagError *)errorFromJson:(NSDictionary *)json {
}

- (void)updateWithCrashInfoMessage:(NSString *)crashInfoMessage {
@try {
// Messages that match this pattern should override the errorClass (and errorMessage if there is enough information.)
NSString *pattern = @"^(Assertion failed|Fatal error|Precondition failed): ((.+): )?file .+, line \\d+\n$";
NSRegularExpression *regex = [[NSRegularExpression alloc] initWithPattern:pattern options:NSRegularExpressionCaseInsensitive error:nil];
NSArray<NSTextCheckingResult *> *matches = [regex matchesInString:crashInfoMessage options:0 range:NSMakeRange(0, crashInfoMessage.length)];
NSArray<NSString *> *patterns = @[
// From Swift 2.2: https://github.com/apple/swift/blob/swift-2.2-RELEASE/stdlib/public/stubs/Assert.cpp#L24-L39
@"^(assertion failed|fatal error|precondition failed): ((.+): )?file .+, line \\d+\n$",
// From Swift 4.1: https://github.com/apple/swift/commit/d03a575279cf5c523779ef68f8d7903f09ba901e
@"^(Assertion failed|Fatal error|Precondition failed): ((.+): )?file .+, line \\d+\n$",
// From Swift 5.4: https://github.com/apple/swift/commit/1a051719e3b1b7c37a856684dd037d482fef8e59
@"^.+:\\d+: (Assertion failed|Fatal error|Precondition failed)(: (.+))?\n$",
];

for (NSString *pattern in patterns) {
NSArray<NSTextCheckingResult *> *matches = nil;
@try {
NSRegularExpression *regex = [[NSRegularExpression alloc] initWithPattern:pattern options:0 error:nil];
matches = [regex matchesInString:crashInfoMessage options:0 range:NSMakeRange(0, crashInfoMessage.length)];
} @catch (NSException *exception) {
bsg_log_err(@"Exception thrown while parsing crash info message: %@", exception);
}
if (matches.count != 1 || matches[0].numberOfRanges != 4) {
if (!self.errorMessage.length) {
// It's better to fall back to the raw string than have an empty errorMessage.
self.errorMessage = crashInfoMessage;
}
return;
continue;
}
NSRange errorClassRange = [matches[0] rangeAtIndex:1];
if (errorClassRange.location != NSNotFound) {
Expand All @@ -147,12 +155,12 @@ - (void)updateWithCrashInfoMessage:(NSString *)crashInfoMessage {
if (errorMessageRange.location != NSNotFound) {
self.errorMessage = [crashInfoMessage substringWithRange:errorMessageRange];
}
} @catch (NSException *exception) {
bsg_log_err(@"Exception thrown while parsing crash info message: %@", exception);
if (!self.errorMessage.length) {
// It's better to fall back to the raw string than have an empty errorMessage.
self.errorMessage = crashInfoMessage;
}
return; //!OCLint
}

if (!self.errorMessage.length) {
// It's better to fall back to the raw string than have an empty errorMessage.
self.errorMessage = crashInfoMessage;
}
}

Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
Changelog
=========

## TBD

### Bug fixes

* Fix Swift 5.4 fatal error message parsing.
[1010](https://github.com/bugsnag/bugsnag-cocoa/pull/1010)

## 6.6.3 (2021-02-17)

### Bug fixes
Expand Down
56 changes: 54 additions & 2 deletions Tests/BugsnagErrorTest.m
Original file line number Diff line number Diff line change
Expand Up @@ -168,9 +168,9 @@ - (void)testUpdateWithCrashInfoMessage {

error.errorClass = nil;
error.errorMessage = nil;
[error updateWithCrashInfoMessage:@"Fatal error: This should NEVER happen: file bugsnag_example/AnotherClass.swift, line 24\n"];
[error updateWithCrashInfoMessage:@"Fatal error: A suffusion of yellow: file calc.swift, line 5\n"];
XCTAssertEqualObjects(error.errorClass, @"Fatal error");
XCTAssertEqualObjects(error.errorMessage, @"This should NEVER happen");
XCTAssertEqualObjects(error.errorMessage, @"A suffusion of yellow");

error.errorClass = nil;
error.errorMessage = nil;
Expand Down Expand Up @@ -253,4 +253,56 @@ - (void)testUpdateWithCrashInfoMessage {
XCTAssertEqualObjects(error.errorMessage, @"Expected message",);
}

- (void)testUpdateWithCrashInfoMessage_Swift54 {
BugsnagError *error = [[BugsnagError alloc] initWithErrorClass:@"" errorMessage:@"" errorType:BSGErrorTypeCocoa stacktrace:nil];

// Swift fatal errors with a message.
// The errorClass and errorMessage should be overwritten with values extracted from the crash info message.

error.errorClass = nil;
error.errorMessage = nil;
[error updateWithCrashInfoMessage:@"bugsnag_example/AnotherClass.swift:24: Assertion failed: This should NEVER happen\n"];
XCTAssertEqualObjects(error.errorClass, @"Assertion failed");
XCTAssertEqualObjects(error.errorMessage, @"This should NEVER happen");

error.errorClass = nil;
error.errorMessage = nil;
[error updateWithCrashInfoMessage:@"calc.swift:5: Fatal error: A suffusion of yellow\n"];
XCTAssertEqualObjects(error.errorClass, @"Fatal error");
XCTAssertEqualObjects(error.errorMessage, @"A suffusion of yellow");

error.errorClass = nil;
error.errorMessage = nil;
[error updateWithCrashInfoMessage:@"bugsnag_example/AnotherClass.swift:24: Precondition failed: : strange formatting 😱::\n"];
XCTAssertEqualObjects(error.errorClass, @"Precondition failed");
XCTAssertEqualObjects(error.errorMessage, @" : strange formatting 😱::");

// Swift fatal errors without a message.
// The errorClass should be overwritten but the errorMessage left as-is.

error.errorClass = nil;
error.errorMessage = nil;
[error updateWithCrashInfoMessage:@"bugsnag_example/AnotherClass.swift:24: Assertion failed\n"];
XCTAssertEqualObjects(error.errorClass, @"Assertion failed");
XCTAssertEqualObjects(error.errorMessage, nil);

error.errorClass = nil;
error.errorMessage = @"Expected message";
[error updateWithCrashInfoMessage:@"bugsnag_example/AnotherClass.swift:24: Assertion failed\n"];
XCTAssertEqualObjects(error.errorClass, @"Assertion failed");
XCTAssertEqualObjects(error.errorMessage, @"Expected message");

error.errorClass = nil;
error.errorMessage = @"Expected message";
[error updateWithCrashInfoMessage:@"bugsnag_example/AnotherClass.swift:24: Fatal error\n"];
XCTAssertEqualObjects(error.errorClass, @"Fatal error");
XCTAssertEqualObjects(error.errorMessage, @"Expected message");

error.errorClass = nil;
error.errorMessage = @"Expected message";
[error updateWithCrashInfoMessage:@"bugsnag_example/AnotherClass.swift:24: Precondition failed\n"];
XCTAssertEqualObjects(error.errorClass, @"Precondition failed");
XCTAssertEqualObjects(error.errorMessage, @"Expected message");
}

@end

0 comments on commit 670a768

Please sign in to comment.