Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

handle unknown CPU archs, improve error messaging #235

Merged
merged 10 commits into from
Jan 24, 2018
6 changes: 2 additions & 4 deletions OSX/Bugsnag.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
8A2C8FDD1C6BC2C800846019 /* BugsnagSink.h in Headers */ = {isa = PBXBuildFile; fileRef = 8A2C8FCB1C6BC2C800846019 /* BugsnagSink.h */; };
8A2C8FDE1C6BC2C800846019 /* BugsnagSink.m in Sources */ = {isa = PBXBuildFile; fileRef = 8A2C8FCC1C6BC2C800846019 /* BugsnagSink.m */; };
8A2C8FEA1C6BC38900846019 /* BugsnagBreadcrumbsTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8A2C8FE01C6BC38200846019 /* BugsnagBreadcrumbsTest.m */; };
8A2C8FEB1C6BC38900846019 /* BugsnagCrashReportTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8A2C8FE11C6BC38200846019 /* BugsnagCrashReportTests.m */; };
8A2C8FEC1C6BC38900846019 /* BugsnagSinkTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8A2C8FE21C6BC38200846019 /* BugsnagSinkTests.m */; };
8A2C8FEE1C6BC38900846019 /* report.json in Resources */ = {isa = PBXBuildFile; fileRef = 8A2C8FE41C6BC38200846019 /* report.json */; };
8A2C8FF01C6BC3A200846019 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8A2C8FEF1C6BC3A200846019 /* SystemConfiguration.framework */; };
Expand All @@ -34,6 +33,7 @@
8A627CD91EC3B75200F7C04E /* BSGSerialization.h in Headers */ = {isa = PBXBuildFile; fileRef = 8A627CD71EC3B75200F7C04E /* BSGSerialization.h */; };
8A627CDA1EC3B75200F7C04E /* BSGSerialization.m in Sources */ = {isa = PBXBuildFile; fileRef = 8A627CD81EC3B75200F7C04E /* BSGSerialization.m */; };
8A87352C1C6D3B1600EDBD5B /* BSG_KSCrashReportWriter.h in Headers */ = {isa = PBXBuildFile; fileRef = 8A87352B1C6D3B1600EDBD5B /* BSG_KSCrashReportWriter.h */; settings = {ATTRIBUTES = (Public, ); }; };
8ACF0F752018136200173809 /* BugsnagCrashReportTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8A2C8FE11C6BC38200846019 /* BugsnagCrashReportTests.m */; };
8AD9FA891E086351002859A7 /* BugsnagConfigurationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8AD9FA851E0862DC002859A7 /* BugsnagConfigurationTests.m */; };
E72352C11F55924A00436528 /* BSGConnectivity.h in Headers */ = {isa = PBXBuildFile; fileRef = E72352BF1F55924A00436528 /* BSGConnectivity.h */; };
E72352C21F55924A00436528 /* BSGConnectivity.m in Sources */ = {isa = PBXBuildFile; fileRef = E72352C01F55924A00436528 /* BSGConnectivity.m */; };
Expand Down Expand Up @@ -930,13 +930,13 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
8ACF0F752018136200173809 /* BugsnagCrashReportTests.m in Sources */,
E7CE78CC1FD94E77001D07E0 /* KSSystemInfo_Tests.m in Sources */,
E7CE78C91FD94E77001D07E0 /* KSSignalInfo_Tests.m in Sources */,
E7CE78C61FD94E77001D07E0 /* KSMach_Tests.m in Sources */,
E7CE78D61FD94E9E001D07E0 /* FileBasedTestCase.m in Sources */,
E7CE78D51FD94E93001D07E0 /* XCTestCase+KSCrash.m in Sources */,
E7CE78CD1FD94E77001D07E0 /* KSZombie_Tests.m in Sources */,
8AD9FA881E08633F002859A7 /* BugsnagConfigurationSpec.m in Sources */,
E7CE78CF1FD94E77001D07E0 /* NSError+SimpleConstructor_Tests.m in Sources */,
E7CE78C81FD94E77001D07E0 /* KSSafeCollections_Tests.m in Sources */,
E7CE78BF1FD94E77001D07E0 /* KSCrashSentry_Signal_Tests.m in Sources */,
Expand All @@ -959,11 +959,9 @@
E7CE78BD1FD94E77001D07E0 /* KSCrashSentry_Deadlock_Tests.m in Sources */,
E7CE78C51FD94E77001D07E0 /* KSLogger_Tests.m in Sources */,
E7CE78C11FD94E77001D07E0 /* KSCrashState_Tests.m in Sources */,
8A2C8FEB1C6BC38900846019 /* BugsnagCrashReportTests.m in Sources */,
E7CE78C31FD94E77001D07E0 /* KSFileUtils_Tests.m in Sources */,
E7CE78BC1FD94E77001D07E0 /* KSCrashReportStore_Tests.m in Sources */,
E79148611FD82BB7003EFEBF /* BugsnagSessionTrackerTest.m in Sources */,
8A2C8FEB1C6BC38900846019 /* BugsnagCrashReportTests.m in Sources */,
E79148591FD82BAE003EFEBF /* BugsnagSessionTest.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down
2 changes: 1 addition & 1 deletion Source/BugsnagCrashReport.h
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,6 @@ __deprecated_msg("Use toJson: instead.");
/**
* Returns the enhanced error message for the thread, or nil if none exists.
*/
- (NSString *_Nullable)enhancedErrorMessageForThread:(NSDictionary *_Nullable)thread;
- (NSString *_Nullable)enhancedErrorMessageForThread:(NSDictionary *_Nullable)thread __deprecated;

@end
112 changes: 70 additions & 42 deletions Source/BugsnagCrashReport.m
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,13 @@ @interface NSDictionary (BSGKSMerge)
- (NSDictionary *)BSG_mergedInto:(NSDictionary *)dest;
@end

@interface RegisterErrorData : NSObject
@property (nonatomic, strong) NSString *errorClass;
@property (nonatomic, strong) NSString *errorMessage;
+ (instancetype)errorDataFromThreads:(NSArray *)threads;
- (instancetype)initWithClass:(NSString *_Nonnull)errorClass message:(NSString *_Nonnull)errorMessage NS_DESIGNATED_INITIALIZER;
@end

@interface BugsnagCrashReport ()

/**
Expand Down Expand Up @@ -217,10 +224,16 @@ - (instancetype)initWithKSReport:(NSDictionary *)report {

_error = [report valueForKeyPath:@"crash.error"];
_errorType = _error[BSGKeyType];
_errorClass = BSGParseErrorClass(_error, _errorType);
_errorMessage = BSGParseErrorMessage(report, _error, _errorType);
_binaryImages = report[@"binary_images"];
_threads = [report valueForKeyPath:@"crash.threads"];
RegisterErrorData *data = [RegisterErrorData errorDataFromThreads:_threads];
if (data) {
_errorClass = data.errorClass;
_errorMessage = data.errorMessage;
} else {
_errorClass = BSGParseErrorClass(_error, _errorType);
_errorMessage = BSGParseErrorMessage(report, _error, _errorType);
}
_binaryImages = report[@"binary_images"];
_breadcrumbs = BSGParseBreadcrumbs(report);
_severity = BSGParseSeverity(
[report valueForKeyPath:@"user.state.crash.severity"]);
Expand Down Expand Up @@ -542,12 +555,6 @@ - (NSArray *)serializeThreadsWithException:(NSMutableDictionary *)exception {
BOOL isCrashedThread = [thread[@"crashed"] boolValue];

if (isCrashedThread) {
NSString *errMsg = [self enhancedErrorMessageForThread:thread];

if (errMsg) { // use enhanced error message (currently swift assertions)
BSGDictInsertIfNotNil(exception, errMsg, BSGKeyMessage);
}

NSUInteger seen = 0;
NSMutableArray *stacktrace = [NSMutableArray array];

Expand Down Expand Up @@ -593,41 +600,47 @@ - (NSArray *)serializeThreadsWithException:(NSMutableDictionary *)exception {
return bugsnagThreads;
}

/**
* Returns the enhanced error message for the thread, or nil if none exists.
*
* This relies very heavily on heuristics rather than any documented APIs.
*/
- (NSString *)enhancedErrorMessageForThread:(NSDictionary *)thread {
NSDictionary *notableAddresses = thread[@"notable_addresses"];
NSMutableArray *msgBuffer = [NSMutableArray new];
BOOL hasReservedWord = NO;

if (notableAddresses) {
- (NSString *_Nullable)enhancedErrorMessageForThread:(NSDictionary *_Nullable)thread {
return [self errorMessage];
}

@end

@implementation RegisterErrorData
+ (instancetype)errorDataFromThreads:(NSArray *)threads {
for (NSDictionary *thread in threads) {
if (![thread[@"crashed"] boolValue]) {
continue;
}
NSDictionary *notableAddresses = thread[@"notable_addresses"];
NSMutableArray *interestingValues = [NSMutableArray new];
NSString *reservedWord = nil;

for (NSString *key in notableAddresses) {
if (![key hasPrefix:@"stack"]) { // skip stack frames, only use register values
NSDictionary *data = notableAddresses[key];
NSString *contentValue = data[@"value"];

hasReservedWord = hasReservedWord || [self isReservedWord:contentValue];

if ([key hasPrefix:@"stack"]) { // skip stack frames, only use register values
continue;
}
NSDictionary *data = notableAddresses[key];
if (![@"string" isEqualToString:data[BSGKeyType]]) {
continue;
}
NSString *contentValue = data[@"value"];

if ([self isReservedWord:contentValue]) {
reservedWord = contentValue;
} else if (!([[contentValue componentsSeparatedByString:@"/"] count] > 2)) {
// must be a string that isn't a reserved word and isn't a filepath
if ([@"string" isEqualToString:data[BSGKeyType]]
&& ![self isReservedWord:contentValue]
&& !([[contentValue componentsSeparatedByString:@"/"] count] > 2)) {

[msgBuffer addObject:contentValue];
}
[interestingValues addObject:contentValue];
}
}
[msgBuffer sortUsingSelector:@selector(localizedCaseInsensitiveCompare:)];
}

if (hasReservedWord && [msgBuffer count] > 0) { // needs to have a reserved word used + a message
return [msgBuffer componentsJoinedByString:@" | "];
} else {
return nil;

[interestingValues sortUsingSelector:@selector(localizedCaseInsensitiveCompare:)];

NSString *message = [interestingValues componentsJoinedByString:@" | "];
return [[RegisterErrorData alloc] initWithClass:reservedWord
message:message];
}
return nil;
}

/**
Expand All @@ -637,9 +650,24 @@ - (NSString *)enhancedErrorMessageForThread:(NSDictionary *)thread {
*
* For assert, "assertion failed" will be in one of the registers.
*/
- (BOOL)isReservedWord:(NSString *)contentValue {
return [@"assertion failed" isEqualToString:contentValue]
|| [@"fatal error" isEqualToString:contentValue];
+ (BOOL)isReservedWord:(NSString *)contentValue {
return [@"assertion failed" caseInsensitiveCompare:contentValue] == NSOrderedSame
|| [@"fatal error" caseInsensitiveCompare:contentValue] == NSOrderedSame
|| [@"precondition failed" caseInsensitiveCompare:contentValue] == NSOrderedSame;
}

- (instancetype)init {
return [self initWithClass:@"Unknown" message:@"<unset>"];
}

- (instancetype)initWithClass:(NSString *)errorClass message:(NSString *)errorMessage {
if (errorClass.length == 0) {
return nil;
}
if (self = [super init]) {
_errorClass = errorClass;
_errorMessage = errorMessage;
}
return self;
}
@end
30 changes: 24 additions & 6 deletions Source/KSCrash/Source/KSCrash/Recording/BSG_KSCrashDoctor.m
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
typedef enum {
BSG_CPUFamilyUnknown,
BSG_CPUFamilyArm,
BSG_CPUFamilyArm64,
BSG_CPUFamilyX86,
BSG_CPUFamilyX86_64
} BSG_CPUFamily;
Expand Down Expand Up @@ -170,15 +171,17 @@ - (NSDictionary *)errorReport:(NSDictionary *)report {
- (BSG_CPUFamily)cpuFamily:(NSDictionary *)report {
NSDictionary *system = [self systemReport:report];
NSString *cpuArch = system[@BSG_KSSystemField_CPUArch];
if ([cpuArch isEqualToString:@"arm64"]) {
return BSG_CPUFamilyArm64;
}
if ([cpuArch rangeOfString:@"arm"].location == 0) {
return BSG_CPUFamilyArm;
}
if ([cpuArch rangeOfString:@"i"].location == 0 &&
[cpuArch rangeOfString:@"86"].location == 2) {
return BSG_CPUFamilyX86;
}
if ([cpuArch rangeOfString:@"x86_64" options:NSCaseInsensitiveSearch]
.location != NSNotFound) {
if ([@[@"x86_64", @"x86"] containsObject:cpuArch]) {
return BSG_CPUFamilyX86_64;
}
return BSG_CPUFamilyUnknown;
Expand All @@ -199,6 +202,18 @@ - (NSString *)registerNameForFamily:(BSG_CPUFamily)family
return @"r3";
}
}
case BSG_CPUFamilyArm64: {
switch (index) {
case 0:
return @"x0";
case 1:
return @"x1";
case 2:
return @"x2";
case 3:
return @"x3";
}
}
case BSG_CPUFamilyX86: {
switch (index) {
case 0:
Expand Down Expand Up @@ -367,10 +382,13 @@ - (BSG_KSCrashDoctorFunctionCall *)lastFunctionCall:(NSDictionary *)report {
BSG_CPUFamily family = [self cpuFamily:report];
NSDictionary *registers =
[self basicRegistersFromThreadReport:crashedThread];
NSArray *regNames = @[[self registerNameForFamily:family paramIndex:0],
[self registerNameForFamily:family paramIndex:1],
[self registerNameForFamily:family paramIndex:2],
[self registerNameForFamily:family paramIndex:3]];
NSMutableArray *regNames = [NSMutableArray arrayWithCapacity:4];
for (int paramIndex = 0; paramIndex <= 3; paramIndex++) {
NSString *regName = [self registerNameForFamily:family paramIndex:paramIndex];
if (regName.length > 0) {
[regNames addObject:regName];
}
}
NSMutableArray *params = [NSMutableArray arrayWithCapacity:4];
for (NSString *regName in regNames) {
BSG_KSCrashDoctorParam *param = [[BSG_KSCrashDoctorParam alloc] init];
Expand Down
Loading