diff --git a/Crashlytics/Crashlytics/Components/FIRCLSContext.h b/Crashlytics/Crashlytics/Components/FIRCLSContext.h
index f3656087e81..bdb43418c69 100644
--- a/Crashlytics/Crashlytics/Components/FIRCLSContext.h
+++ b/Crashlytics/Crashlytics/Components/FIRCLSContext.h
@@ -33,6 +33,13 @@
__BEGIN_DECLS
+#ifdef __OBJC__
+@class FIRCLSInternalReport;
+@class FIRCLSSettings;
+@class FIRCLSInstallIdentifierModel;
+@class FIRCLSFileManager;
+#endif
+
typedef struct {
volatile bool initialized;
volatile bool debuggerAttached;
@@ -90,10 +97,18 @@ typedef struct {
uint32_t maxKeyValues;
} FIRCLSContextInitData;
-bool FIRCLSContextInitialize(const FIRCLSContextInitData* initData);
+#ifdef __OBJC__
+bool FIRCLSContextInitialize(FIRCLSInternalReport* report,
+ FIRCLSSettings* settings,
+ FIRCLSInstallIdentifierModel* installIDModel,
+ FIRCLSFileManager* fileManager);
// Re-writes the metadata file on the current thread
-void FIRCLSContextUpdateMetadata(const FIRCLSContextInitData* initData);
+void FIRCLSContextUpdateMetadata(FIRCLSInternalReport* report,
+ FIRCLSSettings* settings,
+ FIRCLSInstallIdentifierModel* installIDModel,
+ FIRCLSFileManager* fileManager);
+#endif
void FIRCLSContextBaseInit(void);
void FIRCLSContextBaseDeinit(void);
diff --git a/Crashlytics/Crashlytics/Components/FIRCLSContext.m b/Crashlytics/Crashlytics/Components/FIRCLSContext.m
index 501de89b458..26d46c72ec9 100644
--- a/Crashlytics/Crashlytics/Components/FIRCLSContext.m
+++ b/Crashlytics/Crashlytics/Components/FIRCLSContext.m
@@ -17,12 +17,16 @@
#include
#include
+#import "FIRCLSFileManager.h"
+#import "FIRCLSInstallIdentifierModel.h"
+#import "FIRCLSInternalReport.h"
+#import "FIRCLSSettings.h"
+
#include "FIRCLSApplication.h"
#include "FIRCLSCrashedMarkerFile.h"
#include "FIRCLSDefines.h"
#include "FIRCLSFeatures.h"
#include "FIRCLSGlobals.h"
-#include "FIRCLSInternalReport.h"
#include "FIRCLSProcess.h"
#include "FIRCLSUtility.h"
@@ -45,7 +49,61 @@
static const char* FIRCLSContextAppendToRoot(NSString* root, NSString* component);
static void FIRCLSContextAllocate(FIRCLSContext* context);
-bool FIRCLSContextInitialize(const FIRCLSContextInitData* initData) {
+FIRCLSContextInitData FIRCLSContextBuildInitData(FIRCLSInternalReport* report,
+ FIRCLSSettings* settings,
+ FIRCLSInstallIdentifierModel* installIDModel,
+ FIRCLSFileManager* fileManager) {
+ // Because we need to start the crash reporter right away,
+ // it starts up either with default settings, or cached settings
+ // from the last time they were fetched
+
+ FIRCLSContextInitData initData;
+
+ memset(&initData, 0, sizeof(FIRCLSContextInitData));
+
+ initData.customBundleId = nil;
+ initData.installId = [installIDModel.installID UTF8String];
+ initData.sessionId = [[report identifier] UTF8String];
+ initData.rootPath = [[report path] UTF8String];
+ initData.previouslyCrashedFileRootPath = [[fileManager rootPath] UTF8String];
+ initData.errorsEnabled = [settings errorReportingEnabled];
+ initData.customExceptionsEnabled = [settings customExceptionsEnabled];
+ initData.maxCustomExceptions = [settings maxCustomExceptions];
+ initData.maxErrorLogSize = [settings errorLogBufferSize];
+ initData.maxLogSize = [settings logBufferSize];
+ initData.maxKeyValues = [settings maxCustomKeys];
+
+ // If this is set, then we could attempt to do a synchronous submission for certain kinds of
+ // events (exceptions). This is a very cool feature, but adds complexity to the backend. For now,
+ // we're going to leave this disabled. It does work in the exception case, but will ultimtely
+ // result in the following crash to be discared. Usually that crash isn't interesting. But, if it
+ // was, we'd never have a chance to see it.
+ initData.delegate = nil;
+
+#if CLS_MACH_EXCEPTION_SUPPORTED
+ __block exception_mask_t mask = 0;
+
+ // TODO(b/141241224) This if statement was hardcoded to no, so this block was never run
+ // FIRCLSSignalEnumerateHandledSignals(^(int idx, int signal) {
+ // if ([self.delegate ensureDeliveryOfUnixSignal:signal]) {
+ // mask |= FIRCLSMachExceptionMaskForSignal(signal);
+ // }
+ // });
+
+ initData.machExceptionMask = mask;
+#endif
+
+ return initData;
+}
+
+bool FIRCLSContextInitialize(FIRCLSInternalReport* report,
+ FIRCLSSettings* settings,
+ FIRCLSInstallIdentifierModel* installIDModel,
+ FIRCLSFileManager* fileManager) {
+ FIRCLSContextInitData initDataObj =
+ FIRCLSContextBuildInitData(report, settings, installIDModel, fileManager);
+ FIRCLSContextInitData* initData = &initDataObj;
+
if (!initData) {
return false;
}
@@ -197,7 +255,14 @@ bool FIRCLSContextInitialize(const FIRCLSContextInitData* initData) {
return true;
}
-void FIRCLSContextUpdateMetadata(const FIRCLSContextInitData* initData) {
+void FIRCLSContextUpdateMetadata(FIRCLSInternalReport* report,
+ FIRCLSSettings* settings,
+ FIRCLSInstallIdentifierModel* installIDModel,
+ FIRCLSFileManager* fileManager) {
+ FIRCLSContextInitData initDataObj =
+ FIRCLSContextBuildInitData(report, settings, installIDModel, fileManager);
+ FIRCLSContextInitData* initData = &initDataObj;
+
NSString* rootPath = [NSString stringWithUTF8String:initData->rootPath];
const char* metaDataPath =
diff --git a/Crashlytics/Crashlytics/Controllers/FIRCLSReportManager.m b/Crashlytics/Crashlytics/Controllers/FIRCLSReportManager.m
index 10e2fd9be32..fafb2ca119d 100644
--- a/Crashlytics/Crashlytics/Controllers/FIRCLSReportManager.m
+++ b/Crashlytics/Crashlytics/Controllers/FIRCLSReportManager.m
@@ -445,9 +445,7 @@ - (void)checkAndRotateInstallUUIDIfNeededWithReport:(FIRCLSInternalReport *)repo
return;
}
- FIRCLSContextInitData initData = [self initializeContextInitData:report];
-
- FIRCLSContextUpdateMetadata(&initData);
+ FIRCLSContextUpdateMetadata(report, self.settings, self.installIDModel, self->_fileManager);
}];
}
@@ -473,53 +471,18 @@ - (void)startNetworkRequestsWithToken:(FIRCLSDataCollectionToken *)token
[self handleContentsInOtherReportingDirectoriesWithToken:token];
}
-- (FIRCLSContextInitData)initializeContextInitData:(FIRCLSInternalReport *)report {
- FIRCLSContextInitData initData;
-
- memset(&initData, 0, sizeof(FIRCLSContextInitData));
-
- // Because we need to start the crash reporter right away,
- // it starts up either with default settings, or cached settings
- // from the last time they were fetched
- FIRCLSSettings *settings = self.settings;
-
- initData.customBundleId = NULL;
- initData.installId = [self.installIDModel.installID UTF8String];
- initData.sessionId = [[report identifier] UTF8String];
- initData.rootPath = [[report path] UTF8String];
- initData.previouslyCrashedFileRootPath = [[_fileManager rootPath] UTF8String];
-#if CLS_MACH_EXCEPTION_SUPPORTED
- initData.machExceptionMask = [self machExceptionMask];
-#endif
- initData.errorsEnabled = [settings errorReportingEnabled];
- initData.customExceptionsEnabled = [settings customExceptionsEnabled];
- initData.maxCustomExceptions = [settings maxCustomExceptions];
- initData.maxErrorLogSize = [settings errorLogBufferSize];
- initData.maxLogSize = [settings logBufferSize];
- initData.maxKeyValues = [settings maxCustomKeys];
-
- return initData;
-}
-
- (BOOL)startCrashReporterWithProfilingMark:(FIRCLSProfileMark)mark
report:(FIRCLSInternalReport *)report {
if (!report) {
return NO;
}
- FIRCLSContextInitData initData = [self initializeContextInitData:report];
-
- // If this is set, then we could attempt to do a synchronous submission for certain kinds of
- // events (exceptions). This is a very cool feature, but adds complexity to the backend. For now,
- // we're going to leave this disabled. It does work in the exception case, but will ultimtely
- // result in the following crash to be discared. Usually that crash isn't interesting. But, if it
- // was, we'd never have a chance to see it.
- initData.delegate = NULL;
-
- if (![self installCrashReportingHandlers:&initData]) {
+ if (!FIRCLSContextInitialize(report, self.settings, self.installIDModel, _fileManager)) {
return NO;
}
+ [self setupStateNotifications];
+
[self registerAnalyticsEventListener];
[self crashReportingSetupCompleted:mark];
@@ -569,33 +532,8 @@ - (FIRCLSReportUploader *)uploader {
return _uploader;
}
-#if CLS_MACH_EXCEPTION_SUPPORTED
-- (exception_mask_t)machExceptionMask {
- __block exception_mask_t mask = 0;
-
- // TODO(b/141241224) This if statement was hardcoded to no, so this block was never run
- // FIRCLSSignalEnumerateHandledSignals(^(int idx, int signal) {
- // if ([self.delegate ensureDeliveryOfUnixSignal:signal]) {
- // mask |= FIRCLSMachExceptionMaskForSignal(signal);
- // }
- // });
-
- return mask;
-}
-#endif
-
#pragma mark - Reporting Lifecycle
-- (BOOL)installCrashReportingHandlers:(FIRCLSContextInitData *)initData {
- if (!FIRCLSContextInitialize(initData)) {
- return NO;
- }
-
- [self setupStateNotifications];
-
- return YES;
-}
-
- (FIRCLSInternalReport *)setupCurrentReport:(NSString *)executionIdentifier {
[self createLaunchFailureMarker];
diff --git a/Crashlytics/Crashlytics/Controllers/FIRCLSReportManager_Private.h b/Crashlytics/Crashlytics/Controllers/FIRCLSReportManager_Private.h
index 4940855990e..f7f139740ac 100644
--- a/Crashlytics/Crashlytics/Controllers/FIRCLSReportManager_Private.h
+++ b/Crashlytics/Crashlytics/Controllers/FIRCLSReportManager_Private.h
@@ -15,6 +15,8 @@
#import "FIRCLSReportManager.h"
#import "FIRCLSReportUploader.h"
+@class FIRCLSInstallIdentifierModel;
+
@interface FIRCLSReportManager ()
@property(nonatomic, strong) NSOperationQueue *operationQueue;
diff --git a/Crashlytics/Crashlytics/FIRCrashlytics.m b/Crashlytics/Crashlytics/FIRCrashlytics.m
index 1d01a6c6217..20650e531a8 100644
--- a/Crashlytics/Crashlytics/FIRCrashlytics.m
+++ b/Crashlytics/Crashlytics/FIRCrashlytics.m
@@ -307,11 +307,8 @@ - (void)recordError:(NSError *)error {
FIRCLSUserLoggingRecordError(error, nil);
}
-- (void)recordCustomExceptionName:(NSString *)name
- reason:(NSString *)reason
- frameArray:(NSArray *)frameArray {
- FIRCLSExceptionRecord(FIRCLSExceptionTypeCustom, [[name copy] UTF8String],
- [[reason copy] UTF8String], [frameArray copy], NO);
+- (void)recordExceptionModel:(FIRExceptionModel *)exceptionModel {
+ FIRCLSExceptionRecordModel(exceptionModel);
}
@end
diff --git a/Crashlytics/Crashlytics/FIRExceptionModel.m b/Crashlytics/Crashlytics/FIRExceptionModel.m
new file mode 100644
index 00000000000..f0c4697a869
--- /dev/null
+++ b/Crashlytics/Crashlytics/FIRExceptionModel.m
@@ -0,0 +1,42 @@
+// Copyright 2020 Google
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#import "FIRExceptionModel.h"
+
+@interface FIRExceptionModel ()
+
+@property(nonatomic, copy) NSString *name;
+@property(nonatomic, copy) NSString *reason;
+
+@end
+
+@implementation FIRExceptionModel
+
+- (instancetype)initWithName:(NSString *)name reason:(NSString *)reason {
+ self = [super init];
+ if (!self) {
+ return nil;
+ }
+
+ _name = [name copy];
+ _reason = [reason copy];
+
+ return self;
+}
+
++ (instancetype)exceptionModelWithName:(NSString *)name reason:(NSString *)reason {
+ return [[FIRExceptionModel alloc] initWithName:name reason:reason];
+}
+
+@end
diff --git a/Crashlytics/Crashlytics/FIRStackFrame.m b/Crashlytics/Crashlytics/FIRStackFrame.m
new file mode 100644
index 00000000000..30449146506
--- /dev/null
+++ b/Crashlytics/Crashlytics/FIRStackFrame.m
@@ -0,0 +1,94 @@
+// Copyright 2020 Google
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#import "FIRStackFrame_Private.h"
+
+@interface FIRStackFrame ()
+
+@property(nonatomic, copy, nullable) NSString *symbol;
+@property(nonatomic, copy, nullable) NSString *rawSymbol;
+@property(nonatomic, copy, nullable) NSString *library;
+@property(nonatomic, copy, nullable) NSString *fileName;
+@property(nonatomic, assign) uint32_t lineNumber;
+@property(nonatomic, assign) uint64_t offset;
+@property(nonatomic, assign) uint64_t address;
+
+@property(nonatomic, assign) BOOL isSymbolicated;
+
+@end
+
+@implementation FIRStackFrame
+
+#pragma mark - Public Methods
+
+- (instancetype)initWithSymbol:(NSString *)symbol file:(NSString *)file line:(NSInteger)line {
+ self = [super init];
+ if (!self) {
+ return nil;
+ }
+
+ _symbol = [symbol copy];
+ _fileName = [file copy];
+ _lineNumber = (uint32_t)line;
+
+ _isSymbolicated = true;
+
+ return self;
+}
+
++ (instancetype)stackFrameWithSymbol:(NSString *)symbol file:(NSString *)file line:(NSInteger)line {
+ return [[FIRStackFrame alloc] initWithSymbol:symbol file:file line:line];
+}
+
+#pragma mark - Internal Methods
+
++ (instancetype)stackFrame {
+ return [[self alloc] init];
+}
+
++ (instancetype)stackFrameWithAddress:(NSUInteger)address {
+ FIRStackFrame *frame = [self stackFrame];
+
+ [frame setAddress:address];
+
+ return frame;
+}
+
++ (instancetype)stackFrameWithSymbol:(NSString *)symbol {
+ FIRStackFrame *frame = [self stackFrame];
+
+ frame.symbol = symbol;
+ frame.rawSymbol = symbol;
+
+ return frame;
+}
+
+#pragma mark - Overrides
+
+- (NSString *)description {
+ if (self.isSymbolicated) {
+ return [NSString
+ stringWithFormat:@"{%@ - %@:%u}", [self fileName], [self symbol], [self lineNumber]];
+ }
+
+ if (self.fileName) {
+ return [NSString stringWithFormat:@"{[0x%llx] %@ - %@:%u}", [self address], [self fileName],
+ [self symbol], [self lineNumber]];
+ }
+
+ return [NSString
+ stringWithFormat:@"{[0x%llx + %u] %@}", [self address], [self lineNumber], [self symbol]];
+}
+
+@end
diff --git a/Crashlytics/Crashlytics/Handlers/FIRCLSException.h b/Crashlytics/Crashlytics/Handlers/FIRCLSException.h
index 7287d9eeeee..61dac77c321 100644
--- a/Crashlytics/Crashlytics/Handlers/FIRCLSException.h
+++ b/Crashlytics/Crashlytics/Handlers/FIRCLSException.h
@@ -19,7 +19,8 @@
#ifdef __OBJC__
#import
-@class FIRCLSStackFrame;
+@class FIRStackFrame;
+@class FIRExceptionModel;
#endif
#define CLS_EXCEPTION_STRING_LENGTH_MAX (1024 * 16)
@@ -60,11 +61,12 @@ void FIRCLSExceptionRaiseTestObjCException(void) __attribute((noreturn));
void FIRCLSExceptionRaiseTestCppException(void) __attribute((noreturn));
#ifdef __OBJC__
+void FIRCLSExceptionRecordModel(FIRExceptionModel* exceptionModel);
void FIRCLSExceptionRecordNSException(NSException* exception);
void FIRCLSExceptionRecord(FIRCLSExceptionType type,
const char* name,
const char* reason,
- NSArray* frames,
+ NSArray* frames,
BOOL attemptDelivery);
#endif
diff --git a/Crashlytics/Crashlytics/Handlers/FIRCLSException.mm b/Crashlytics/Crashlytics/Handlers/FIRCLSException.mm
index 1eeecb60e94..6f209904571 100644
--- a/Crashlytics/Crashlytics/Handlers/FIRCLSException.mm
+++ b/Crashlytics/Crashlytics/Handlers/FIRCLSException.mm
@@ -16,13 +16,15 @@
#include "FIRCLSException.h"
+#import "FIRExceptionModel_Private.h"
+#import "FIRStackFrame_Private.h"
+
#include "FIRCLSApplication.h"
#include "FIRCLSFile.h"
#include "FIRCLSGlobals.h"
#include "FIRCLSHandler.h"
#import "FIRCLSLogger.h"
#include "FIRCLSProcess.h"
-#import "FIRCLSStackFrame.h"
#import "FIRCLSUserLogging.h"
#import "FIRCLSUtility.h"
@@ -76,6 +78,14 @@ void FIRCLSExceptionInitialize(FIRCLSExceptionReadOnlyContext *roContext,
rwContext->customExceptionCount = 0;
}
+void FIRCLSExceptionRecordModel(FIRExceptionModel *exceptionModel) {
+ const char *name = [[exceptionModel.name copy] UTF8String];
+ const char *reason = [[exceptionModel.reason copy] UTF8String];
+
+ FIRCLSExceptionRecord(FIRCLSExceptionTypeCustom, name, reason, [exceptionModel.stackTrace copy],
+ NO);
+}
+
void FIRCLSExceptionRecordNSException(NSException *exception) {
FIRCLSSDKLog("Recording an NSException\n");
@@ -93,14 +103,14 @@ void FIRCLSExceptionRecordNSException(NSException *exception) {
NSMutableArray *frames = [NSMutableArray new];
for (NSNumber *address in returnAddresses) {
- [frames addObject:[FIRCLSStackFrame stackFrameWithAddress:[address unsignedIntegerValue]]];
+ [frames addObject:[FIRStackFrame stackFrameWithAddress:[address unsignedIntegerValue]]];
}
FIRCLSExceptionRecord(FIRCLSExceptionTypeObjectiveC, [name UTF8String], [reason UTF8String],
frames, YES);
}
-static void FIRCLSExceptionRecordFrame(FIRCLSFile *file, FIRCLSStackFrame *frame) {
+static void FIRCLSExceptionRecordFrame(FIRCLSFile *file, FIRStackFrame *frame) {
FIRCLSFileWriteHashStart(file);
FIRCLSFileWriteHashEntryUint64(file, "pc", [frame address]);
@@ -150,7 +160,7 @@ void FIRCLSExceptionWrite(FIRCLSFile *file,
FIRCLSExceptionType type,
const char *name,
const char *reason,
- NSArray *frames) {
+ NSArray *frames) {
FIRCLSFileWriteSectionStart(file, "exception");
FIRCLSFileWriteHashStart(file);
@@ -164,7 +174,7 @@ void FIRCLSExceptionWrite(FIRCLSFile *file,
FIRCLSFileWriteHashKey(file, "frames");
FIRCLSFileWriteArrayStart(file);
- for (FIRCLSStackFrame *frame in frames) {
+ for (FIRStackFrame *frame in frames) {
FIRCLSExceptionRecordFrame(file, frame);
}
@@ -179,7 +189,7 @@ void FIRCLSExceptionWrite(FIRCLSFile *file,
void FIRCLSExceptionRecord(FIRCLSExceptionType type,
const char *name,
const char *reason,
- NSArray *frames,
+ NSArray *frames,
BOOL attemptDelivery) {
if (!FIRCLSContextIsInitialized()) {
return;
diff --git a/Crashlytics/Crashlytics/Models/FIRCLSStackFrame.m b/Crashlytics/Crashlytics/Models/FIRCLSStackFrame.m
deleted file mode 100644
index 8227e4ff463..00000000000
--- a/Crashlytics/Crashlytics/Models/FIRCLSStackFrame.m
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright 2019 Google
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#import "FIRCLSStackFrame.h"
-
-@implementation FIRCLSStackFrame
-
-+ (instancetype)stackFrame {
- return [[self alloc] init];
-}
-
-+ (instancetype)stackFrameWithAddress:(NSUInteger)address {
- FIRCLSStackFrame* frame = [self stackFrame];
-
- [frame setAddress:address];
-
- return frame;
-}
-
-+ (instancetype)stackFrameWithSymbol:(NSString*)symbol {
- FIRCLSStackFrame* frame = [self stackFrame];
-
- frame.symbol = symbol;
- frame.rawSymbol = symbol;
-
- return frame;
-}
-
-- (NSString*)description {
- if ([self fileName]) {
- return [NSString stringWithFormat:@"{[0x%llx] %@ - %@:%u}", [self address], [self fileName],
- [self symbol], [self lineNumber]];
- }
-
- return [NSString
- stringWithFormat:@"{[0x%llx + %u] %@}", [self address], [self lineNumber], [self symbol]];
-}
-
-@end
diff --git a/Crashlytics/Crashlytics/Models/FIRCLSSymbolResolver.h b/Crashlytics/Crashlytics/Models/FIRCLSSymbolResolver.h
index 85adc85094d..a18e60467e7 100644
--- a/Crashlytics/Crashlytics/Models/FIRCLSSymbolResolver.h
+++ b/Crashlytics/Crashlytics/Models/FIRCLSSymbolResolver.h
@@ -14,13 +14,13 @@
#import
-@class FIRCLSStackFrame;
+@class FIRStackFrame;
@interface FIRCLSSymbolResolver : NSObject
- (BOOL)loadBinaryImagesFromFile:(NSString *)path;
-- (FIRCLSStackFrame *)frameForAddress:(uint64_t)address;
-- (BOOL)updateStackFrame:(FIRCLSStackFrame *)frame;
+- (FIRStackFrame *)frameForAddress:(uint64_t)address;
+- (BOOL)updateStackFrame:(FIRStackFrame *)frame;
@end
diff --git a/Crashlytics/Crashlytics/Models/FIRCLSSymbolResolver.m b/Crashlytics/Crashlytics/Models/FIRCLSSymbolResolver.m
index a96dd70aa38..522a14ec595 100644
--- a/Crashlytics/Crashlytics/Models/FIRCLSSymbolResolver.m
+++ b/Crashlytics/Crashlytics/Models/FIRCLSSymbolResolver.m
@@ -19,7 +19,7 @@
#include "FIRCLSBinaryImage.h"
#include "FIRCLSFile.h"
#import "FIRCLSLogger.h"
-#import "FIRCLSStackFrame.h"
+#import "FIRStackFrame_Private.h"
@interface FIRCLSSymbolResolver () {
NSMutableArray* _binaryImages;
@@ -107,8 +107,8 @@ - (BOOL)fillInImageDetails:(FIRCLSBinaryImageDetails*)details forUUID:(NSString*
return FIRCLSBinaryImageFindImageForUUID([uuid UTF8String], details);
}
-- (FIRCLSStackFrame*)frameForAddress:(uint64_t)address {
- FIRCLSStackFrame* frame = [FIRCLSStackFrame stackFrameWithAddress:(NSUInteger)address];
+- (FIRStackFrame*)frameForAddress:(uint64_t)address {
+ FIRStackFrame* frame = [FIRStackFrame stackFrameWithAddress:(NSUInteger)address];
if (![self updateStackFrame:frame]) {
return nil;
@@ -117,7 +117,7 @@ - (FIRCLSStackFrame*)frameForAddress:(uint64_t)address {
return frame;
}
-- (BOOL)updateStackFrame:(FIRCLSStackFrame*)frame {
+- (BOOL)updateStackFrame:(FIRStackFrame*)frame {
uint64_t address = [frame address];
if (address == 0) {
return NO;
diff --git a/Crashlytics/Crashlytics/Operations/Reports/FIRCLSProcessReportOperation.m b/Crashlytics/Crashlytics/Operations/Reports/FIRCLSProcessReportOperation.m
index 3c5ab8a1ba9..12ce601d684 100644
--- a/Crashlytics/Crashlytics/Operations/Reports/FIRCLSProcessReportOperation.m
+++ b/Crashlytics/Crashlytics/Operations/Reports/FIRCLSProcessReportOperation.m
@@ -18,9 +18,9 @@
#import "FIRCLSFile.h"
#import "FIRCLSInternalReport.h"
#import "FIRCLSSerializeSymbolicatedFramesOperation.h"
-#import "FIRCLSStackFrame.h"
#import "FIRCLSSymbolResolver.h"
#import "FIRCLSSymbolicationOperation.h"
+#import "FIRStackFrame_Private.h"
@implementation FIRCLSProcessReportOperation
@@ -64,7 +64,7 @@ - (NSArray *)threadArrayFromFile:(NSString *)path {
NSMutableArray *frameArray = [NSMutableArray array];
for (NSNumber *pc in [threadDetails objectForKey:@"stacktrace"]) {
- FIRCLSStackFrame *frame = [FIRCLSStackFrame stackFrameWithAddress:[pc unsignedIntegerValue]];
+ FIRStackFrame *frame = [FIRStackFrame stackFrameWithAddress:[pc unsignedIntegerValue]];
[frameArray addObject:frame];
}
diff --git a/Crashlytics/Crashlytics/Operations/Symbolication/FIRCLSDemangleOperation.mm b/Crashlytics/Crashlytics/Operations/Symbolication/FIRCLSDemangleOperation.mm
index 182f7a01849..364a6ed6ddb 100644
--- a/Crashlytics/Crashlytics/Operations/Symbolication/FIRCLSDemangleOperation.mm
+++ b/Crashlytics/Crashlytics/Operations/Symbolication/FIRCLSDemangleOperation.mm
@@ -13,7 +13,7 @@
// limitations under the License.
#include "FIRCLSDemangleOperation.h"
-#include "FIRCLSStackFrame.h"
+#include "FIRStackFrame_Private.h"
#import
@@ -84,7 +84,7 @@ - (NSString *)demangleSymbol:(const char *)symbol {
}
- (void)main {
- [self enumerateFramesWithBlock:^(FIRCLSStackFrame *frame) {
+ [self enumerateFramesWithBlock:^(FIRStackFrame *frame) {
NSString *demangedSymbol = [self demangleSymbol:[[frame rawSymbol] UTF8String]];
if (demangedSymbol) {
diff --git a/Crashlytics/Crashlytics/Operations/Symbolication/FIRCLSSerializeSymbolicatedFramesOperation.m b/Crashlytics/Crashlytics/Operations/Symbolication/FIRCLSSerializeSymbolicatedFramesOperation.m
index 6b2b79b7466..b8701d261a7 100644
--- a/Crashlytics/Crashlytics/Operations/Symbolication/FIRCLSSerializeSymbolicatedFramesOperation.m
+++ b/Crashlytics/Crashlytics/Operations/Symbolication/FIRCLSSerializeSymbolicatedFramesOperation.m
@@ -16,7 +16,7 @@
#import "FIRCLSFile.h"
#import "FIRCLSLogger.h"
-#import "FIRCLSStackFrame.h"
+#import "FIRStackFrame_Private.h"
@implementation FIRCLSSerializeSymbolicatedFramesOperation
@@ -37,7 +37,7 @@ - (void)main {
for (NSArray *frameArray in self.threadArray) {
FIRCLSFileWriteArrayStart(&file);
- for (FIRCLSStackFrame *frame in frameArray) {
+ for (FIRStackFrame *frame in frameArray) {
FIRCLSFileWriteHashStart(&file);
FIRCLSFileWriteHashEntryString(&file, "symbol", [[frame symbol] UTF8String]);
diff --git a/Crashlytics/Crashlytics/Operations/Symbolication/FIRCLSSymbolicationOperation.m b/Crashlytics/Crashlytics/Operations/Symbolication/FIRCLSSymbolicationOperation.m
index e2988b2d4b8..d15005bbeef 100644
--- a/Crashlytics/Crashlytics/Operations/Symbolication/FIRCLSSymbolicationOperation.m
+++ b/Crashlytics/Crashlytics/Operations/Symbolication/FIRCLSSymbolicationOperation.m
@@ -14,13 +14,12 @@
#import "FIRCLSSymbolicationOperation.h"
-#import "FIRCLSStackFrame.h"
#import "FIRCLSSymbolResolver.h"
@implementation FIRCLSSymbolicationOperation
- (void)main {
- [self enumerateFramesWithBlock:^(FIRCLSStackFrame *frame) {
+ [self enumerateFramesWithBlock:^(FIRStackFrame *frame) {
[self.symbolResolver updateStackFrame:frame];
}];
}
diff --git a/Crashlytics/Crashlytics/Operations/Symbolication/FIRCLSThreadArrayOperation.h b/Crashlytics/Crashlytics/Operations/Symbolication/FIRCLSThreadArrayOperation.h
index e223523ffff..0c2a1df54b2 100644
--- a/Crashlytics/Crashlytics/Operations/Symbolication/FIRCLSThreadArrayOperation.h
+++ b/Crashlytics/Crashlytics/Operations/Symbolication/FIRCLSThreadArrayOperation.h
@@ -14,12 +14,12 @@
#import
-@class FIRCLSStackFrame;
+@class FIRStackFrame;
@interface FIRCLSThreadArrayOperation : NSOperation
@property(nonatomic, strong) NSArray *threadArray;
-- (void)enumerateFramesWithBlock:(void (^)(FIRCLSStackFrame *frame))block;
+- (void)enumerateFramesWithBlock:(void (^)(FIRStackFrame *frame))block;
@end
diff --git a/Crashlytics/Crashlytics/Operations/Symbolication/FIRCLSThreadArrayOperation.m b/Crashlytics/Crashlytics/Operations/Symbolication/FIRCLSThreadArrayOperation.m
index 96c73277626..3f7509e7e3e 100644
--- a/Crashlytics/Crashlytics/Operations/Symbolication/FIRCLSThreadArrayOperation.m
+++ b/Crashlytics/Crashlytics/Operations/Symbolication/FIRCLSThreadArrayOperation.m
@@ -14,13 +14,11 @@
#import "FIRCLSThreadArrayOperation.h"
-#import "FIRCLSStackFrame.h"
-
@implementation FIRCLSThreadArrayOperation
-- (void)enumerateFramesWithBlock:(void (^)(FIRCLSStackFrame *frame))block {
+- (void)enumerateFramesWithBlock:(void (^)(FIRStackFrame *frame))block {
for (NSArray *frameArray in self.threadArray) {
- for (FIRCLSStackFrame *frame in frameArray) {
+ for (FIRStackFrame *frame in frameArray) {
block(frame);
if ([self isCancelled]) {
diff --git a/Crashlytics/Crashlytics/Private/FIRExceptionModel_Private.h b/Crashlytics/Crashlytics/Private/FIRExceptionModel_Private.h
new file mode 100644
index 00000000000..7cd161a9568
--- /dev/null
+++ b/Crashlytics/Crashlytics/Private/FIRExceptionModel_Private.h
@@ -0,0 +1,33 @@
+// Copyright 2020 Google
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef FIRExceptionModel_Private_h
+#define FIRExceptionModel_Private_h
+
+#import
+
+#import "FIRExceptionModel.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface FIRExceptionModel (Private)
+
+@property(nonatomic, copy) NSString *name;
+@property(nonatomic, copy) NSString *reason;
+
+@end
+
+NS_ASSUME_NONNULL_END
+
+#endif /* FIRExceptionModel_Private_h */
diff --git a/Crashlytics/Crashlytics/Models/FIRCLSStackFrame.h b/Crashlytics/Crashlytics/Private/FIRStackFrame_Private.h
similarity index 69%
rename from Crashlytics/Crashlytics/Models/FIRCLSStackFrame.h
rename to Crashlytics/Crashlytics/Private/FIRStackFrame_Private.h
index 24ef1cc8bc5..d4c0a44c9b3 100644
--- a/Crashlytics/Crashlytics/Models/FIRCLSStackFrame.h
+++ b/Crashlytics/Crashlytics/Private/FIRStackFrame_Private.h
@@ -1,4 +1,4 @@
-// Copyright 2019 Google
+// Copyright 2020 Google
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -14,18 +14,18 @@
#import
+#import "FIRStackFrame.h"
+
NS_ASSUME_NONNULL_BEGIN
/**
- *
- * This class is used in conjunction with -[Crashlytics
- * recordCustomExceptionName:reason:frameArray:] to record information about non-ObjC/C++
- * exceptions. All information included here will be displayed in the Crashlytics UI, and can
- * influence crash grouping. Be particularly careful with the use of the address property. If set,
- * Crashlytics will attempt symbolication and could overwrite other properities in the process.
- *
+ * This class is used in conjunction with recordExceptionModel to record information about
+ * non-ObjC/C++ exceptions. All information included here will be displayed in the Crashlytics UI,
+ * and can influence crash grouping. Be particularly careful with the use of the address property.
+ *If set, Crashlytics will attempt symbolication and could overwrite other properities in the
+ *process.
**/
-@interface FIRCLSStackFrame : NSObject
+@interface FIRStackFrame (Private)
+ (instancetype)stackFrame;
+ (instancetype)stackFrameWithAddress:(NSUInteger)address;
diff --git a/Crashlytics/Crashlytics/Public/FIRCrashlytics.h b/Crashlytics/Crashlytics/Public/FIRCrashlytics.h
index c78a592de49..9f651537b17 100644
--- a/Crashlytics/Crashlytics/Public/FIRCrashlytics.h
+++ b/Crashlytics/Crashlytics/Public/FIRCrashlytics.h
@@ -14,6 +14,8 @@
#import
+#import "FIRExceptionModel.h"
+
#if __has_include()
#warning "FirebaseCrashlytics and Crashlytics are not compatible \
in the same app because including multiple crash reporters can \
@@ -95,7 +97,6 @@ NS_SWIFT_NAME(Crashlytics)
- (void)setUserID:(NSString *)userID;
/**
- *
* Records a non-fatal event described by an NSError object. The events are
* grouped and displayed similarly to crashes. Keep in mind that this method can be expensive.
* The total number of NSErrors that can be recorded during your app's life-cycle is limited by a
@@ -106,6 +107,18 @@ NS_SWIFT_NAME(Crashlytics)
*/
- (void)recordError:(NSError *)error NS_SWIFT_NAME(record(error:));
+/**
+ * Records an Exception Model described by an FIRExceptionModel object. The events are
+ * grouped and displayed similarly to crashes. Keep in mind that this method can be expensive.
+ * The total number of FIRExceptionModels that can be recorded during your app's life-cycle is
+ * limited by a fixed-size circular buffer. If the buffer is overrun, the oldest data is dropped.
+ * Exception Models are relayed to Crashlytics on a subsequent launch of your application.
+ *
+ * @param exceptionModel Instance of the FIRExceptionModel to be recorded
+ */
+- (void)recordExceptionModel:(FIRExceptionModel *)exceptionModel
+ NS_SWIFT_NAME(record(exceptionModel:));
+
/**
* Returns whether the app crashed during the previous execution.
*/
diff --git a/Crashlytics/Crashlytics/Public/FIRExceptionModel.h b/Crashlytics/Crashlytics/Public/FIRExceptionModel.h
new file mode 100644
index 00000000000..a0ee1579eed
--- /dev/null
+++ b/Crashlytics/Crashlytics/Public/FIRExceptionModel.h
@@ -0,0 +1,57 @@
+// Copyright 2020 Google
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#import
+
+#import "FIRStackFrame.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ * The Firebase Crashlytics Exception Model provides a way to report custom exceptions
+ * to Crashlytics that came from a runtime environment outside of the native
+ * platform Crashlytics is running in.
+ */
+NS_SWIFT_NAME(ExceptionModel)
+@interface FIRExceptionModel : NSObject
+
+/** :nodoc: */
+- (instancetype)init NS_UNAVAILABLE;
+
+/**
+ * Initializes an Exception Model model with the given required fields.
+ *
+ * @param name - typically the type of the Exception class
+ * @param reason - the human-readable reason the issue occurred
+ */
+- (instancetype)initWithName:(NSString *)name reason:(NSString *)reason;
+
+/**
+ * Creates an Exception Model model with the given required fields.
+ *
+ * @param name - typically the type of the Exception class
+ * @param reason - the human-readable reason the issue occurred
+ */
++ (instancetype)exceptionModelWithName:(NSString *)name
+ reason:(NSString *)reason NS_SWIFT_UNAVAILABLE("");
+
+/**
+ * A list of Stack Frames that make up the stack trace. The order of the stack trace is top-first,
+ * so typically the "main" function is the last element in this list.
+ */
+@property(nonatomic, copy) NSArray *stackTrace;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/Crashlytics/Crashlytics/Public/FIRStackFrame.h b/Crashlytics/Crashlytics/Public/FIRStackFrame.h
new file mode 100644
index 00000000000..ef9746fbb46
--- /dev/null
+++ b/Crashlytics/Crashlytics/Public/FIRStackFrame.h
@@ -0,0 +1,53 @@
+// Copyright 2020 Google
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#import
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ * The Firebase Crashlytics Stack Frame provides a way to construct the lines of
+ * a stack trace for reporting along with a recorded Exception Model.
+ */
+NS_SWIFT_NAME(StackFrame)
+@interface FIRStackFrame : NSObject
+
+/** :nodoc: */
+- (instancetype)init NS_UNAVAILABLE;
+
+/**
+ * Initializes a symbolicated Stack Frame with the given required fields. Symbolicated
+ * Stack Frames will appear in the Crashlytics dashboard as reported in these fields.
+ *
+ * @param symbol - The function or method name
+ * @param file - the file where the exception occurred
+ * @param line - the line number
+ */
+- (instancetype)initWithSymbol:(NSString *)symbol file:(NSString *)file line:(NSInteger)line;
+
+/**
+ * Creates a symbolicated Stack Frame with the given required fields. Symbolicated
+ * Stack Frames will appear in the Crashlytics dashboard as reported in these fields. *
+ *
+ * @param symbol - The function or method name
+ * @param file - the file where the exception occurred
+ * @param line - the line number
+ */
++ (instancetype)stackFrameWithSymbol:(NSString *)symbol
+ file:(NSString *)file
+ line:(NSInteger)line NS_SWIFT_UNAVAILABLE("");
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/Crashlytics/Crashlytics/Public/FirebaseCrashlytics.h b/Crashlytics/Crashlytics/Public/FirebaseCrashlytics.h
index d85680b0059..9022811ba1c 100644
--- a/Crashlytics/Crashlytics/Public/FirebaseCrashlytics.h
+++ b/Crashlytics/Crashlytics/Public/FirebaseCrashlytics.h
@@ -15,3 +15,5 @@
*/
#import "FIRCrashlytics.h"
+#import "FIRExceptionModel.h"
+#import "FIRStackFrame.h"
diff --git a/Crashlytics/UnitTests/FIRCLSDemangleOperationTests.m b/Crashlytics/UnitTests/FIRCLSDemangleOperationTests.m
index 8deb8d29e94..eb1232a61b3 100644
--- a/Crashlytics/UnitTests/FIRCLSDemangleOperationTests.m
+++ b/Crashlytics/UnitTests/FIRCLSDemangleOperationTests.m
@@ -17,7 +17,7 @@
#import
#import
-#import "FIRCLSStackFrame.h"
+#import "FIRStackFrame_Private.h"
@interface FIRCLSDemangleOperationTests : XCTestCase
@@ -57,9 +57,9 @@ - (void)testDemangleCppSymbolsWithBlockInvoke {
- (void)testOperation {
NSMutableArray *frameArray = [[NSMutableArray alloc] init];
- [frameArray addObject:[FIRCLSStackFrame stackFrameWithSymbol:@"_Z7monitorP8NSStringlS0_"]];
- [frameArray addObject:[FIRCLSStackFrame stackFrameWithSymbol:@"_ZN9wikipedia7article6formatEv"]];
- [frameArray addObject:[FIRCLSStackFrame stackFrameWithSymbol:@"unmangledSymbol"]];
+ [frameArray addObject:[FIRStackFrame stackFrameWithSymbol:@"_Z7monitorP8NSStringlS0_"]];
+ [frameArray addObject:[FIRStackFrame stackFrameWithSymbol:@"_ZN9wikipedia7article6formatEv"]];
+ [frameArray addObject:[FIRStackFrame stackFrameWithSymbol:@"unmangledSymbol"]];
FIRCLSDemangleOperation *op = [[FIRCLSDemangleOperation alloc] init];
[op setThreadArray:@[ frameArray ]];
diff --git a/Crashlytics/UnitTests/FIRCLSProcessReportOperationTests.m b/Crashlytics/UnitTests/FIRCLSProcessReportOperationTests.m
index ec56707c959..dafa36651d7 100644
--- a/Crashlytics/UnitTests/FIRCLSProcessReportOperationTests.m
+++ b/Crashlytics/UnitTests/FIRCLSProcessReportOperationTests.m
@@ -21,7 +21,7 @@
#import "FIRCLSFileManager.h"
#import "FIRCLSInternalReport.h"
#import "FIRCLSMockSymbolResolver.h"
-#import "FIRCLSStackFrame.h"
+#import "FIRStackFrame_Private.h"
@interface FIRCLSProcessReportOperationTests : XCTestCase
@@ -70,9 +70,9 @@ - (void)testExceptionSymbolication {
// Setup a resolver that will work for the contents of the file
FIRCLSMockSymbolResolver *resolver = [[FIRCLSMockSymbolResolver alloc] init];
- FIRCLSStackFrame *frame = nil;
+ FIRStackFrame *frame = nil;
- frame = [FIRCLSStackFrame stackFrameWithSymbol:@"testSymbolA"];
+ frame = [FIRStackFrame stackFrameWithSymbol:@"testSymbolA"];
[frame setLibrary:@"libA"];
[frame setOffset:10];
diff --git a/Crashlytics/UnitTests/FIRCLSSymbolicationOperationTests.m b/Crashlytics/UnitTests/FIRCLSSymbolicationOperationTests.m
index 9a893336a17..276013d5b28 100644
--- a/Crashlytics/UnitTests/FIRCLSSymbolicationOperationTests.m
+++ b/Crashlytics/UnitTests/FIRCLSSymbolicationOperationTests.m
@@ -18,7 +18,7 @@
#import
#import "FIRCLSMockSymbolResolver.h"
-#import "FIRCLSStackFrame.h"
+#import "FIRStackFrame_Private.h"
@interface FIRCLSSymbolicationOperationTests : XCTestCase
@@ -37,23 +37,23 @@ - (void)tearDown {
- (void)testOperation {
FIRCLSMockSymbolResolver* resolver = [[FIRCLSMockSymbolResolver alloc] init];
- FIRCLSStackFrame* frame = nil;
+ FIRStackFrame* frame = nil;
- frame = [FIRCLSStackFrame stackFrameWithSymbol:@"testSymbolA"];
+ frame = [FIRStackFrame stackFrameWithSymbol:@"testSymbolA"];
[frame setLibrary:@"libA"];
[frame setOffset:10];
[resolver addMockFrame:frame atAddress:100];
- frame = [FIRCLSStackFrame stackFrameWithSymbol:@"testSymbolB"];
+ frame = [FIRStackFrame stackFrameWithSymbol:@"testSymbolB"];
[frame setLibrary:@"libB"];
[frame setOffset:20];
[resolver addMockFrame:frame atAddress:200];
NSMutableArray* frameArray = [[NSMutableArray alloc] init];
- [frameArray addObject:[FIRCLSStackFrame stackFrameWithAddress:100]];
- [frameArray addObject:[FIRCLSStackFrame stackFrameWithAddress:200]];
+ [frameArray addObject:[FIRStackFrame stackFrameWithAddress:100]];
+ [frameArray addObject:[FIRStackFrame stackFrameWithAddress:200]];
FIRCLSSymbolicationOperation* op = [[FIRCLSSymbolicationOperation alloc] init];
@@ -65,10 +65,10 @@ - (void)testOperation {
XCTAssertEqual([frameArray count], 2, @"");
XCTAssertEqualObjects([frameArray[0] symbol], @"testSymbolA", @"");
XCTAssertEqualObjects([frameArray[0] library], @"libA", @"");
- XCTAssertEqual([((FIRCLSStackFrame*)frameArray[0]) offset], 10, @"");
+ XCTAssertEqual([((FIRStackFrame*)frameArray[0]) offset], 10, @"");
XCTAssertEqualObjects([frameArray[1] symbol], @"testSymbolB", @"");
XCTAssertEqualObjects([frameArray[1] library], @"libB", @"");
- XCTAssertEqual([((FIRCLSStackFrame*)frameArray[1]) offset], 20, @"");
+ XCTAssertEqual([((FIRStackFrame*)frameArray[1]) offset], 20, @"");
}
@end
diff --git a/Crashlytics/UnitTests/FIRExceptionModelTests.m b/Crashlytics/UnitTests/FIRExceptionModelTests.m
new file mode 100644
index 00000000000..1b3e93b74ec
--- /dev/null
+++ b/Crashlytics/UnitTests/FIRExceptionModelTests.m
@@ -0,0 +1,80 @@
+// Copyright 2020 Google
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#import
+
+#import "FIRExceptionModel_Private.h"
+#import "FIRStackFrame_Private.h"
+
+@interface FIRExceptionModelTests : XCTestCase
+
+@end
+
+@implementation FIRExceptionModelTests
+
+- (void)testBasicOwnership {
+ NSArray *stackTrace = @[
+ [FIRStackFrame stackFrameWithSymbol:@"CrashyFunc" file:@"AppLib.m" line:504],
+ [FIRStackFrame stackFrameWithSymbol:@"ApplicationMain" file:@"AppleLib" line:1],
+ [FIRStackFrame stackFrameWithSymbol:@"main()" file:@"main.m" line:201],
+ ];
+ NSString *name = @"FIRExceptionModelTestsCrash";
+ NSString *reason = @"Programmer made an error";
+
+ FIRExceptionModel *model = [FIRExceptionModel exceptionModelWithName:name reason:reason];
+ model.stackTrace = stackTrace;
+
+ name = @"NewName";
+ reason = nil;
+ stackTrace = @[];
+
+ XCTAssertEqualObjects(model.name, @"FIRExceptionModelTestsCrash");
+ XCTAssertEqualObjects(model.reason, @"Programmer made an error");
+ XCTAssertEqual(model.stackTrace.count, 3);
+ XCTAssertEqualObjects(model.stackTrace[0].symbol, @"CrashyFunc");
+ XCTAssertEqualObjects(model.stackTrace[2].fileName, @"main.m");
+}
+
+- (void)testMutableArrayOwnership {
+ NSMutableArray *stackTrace = [[NSMutableArray alloc] initWithArray:@[
+ [FIRStackFrame stackFrameWithSymbol:@"CrashyFunc" file:@"AppLib.m" line:504],
+ [FIRStackFrame stackFrameWithSymbol:@"ApplicationMain" file:@"AppleLib" line:1],
+ [FIRStackFrame stackFrameWithSymbol:@"main()" file:@"main.m" line:201],
+ ]];
+ NSString *name = @"FIRExceptionModelTestsCrash";
+ NSString *reason = @"Programmer made an error";
+
+ FIRExceptionModel *model = [FIRExceptionModel exceptionModelWithName:name reason:reason];
+ model.stackTrace = stackTrace;
+
+ stackTrace[0].symbol = @"NewSymbol";
+
+ FIRStackFrame *newFrame = [FIRStackFrame stackFrameWithSymbol:@"NewMain"
+ file:@"below_main.m"
+ line:300];
+ [stackTrace addObject:newFrame];
+ [stackTrace insertObject:newFrame atIndex:1];
+
+ XCTAssertEqual(model.stackTrace.count, 3);
+
+ // Modifying underlying frames in the stack trace will be reflected in the Exception Model's copy
+ // because we only shallow copy the array and not the contents.
+ XCTAssertEqualObjects(model.stackTrace[0].symbol, @"NewSymbol");
+
+ // Inserted frames into the mutable array after the fact do not impact the array passed to the
+ // Exception Model.
+ XCTAssertEqualObjects(model.stackTrace[1].symbol, @"ApplicationMain");
+}
+
+@end
diff --git a/Crashlytics/UnitTests/FIRRecordExceptionModelTests.m b/Crashlytics/UnitTests/FIRRecordExceptionModelTests.m
new file mode 100644
index 00000000000..f8bfaf2238c
--- /dev/null
+++ b/Crashlytics/UnitTests/FIRRecordExceptionModelTests.m
@@ -0,0 +1,99 @@
+// Copyright 2020 Google
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#import
+
+#import "FIRExceptionModel.h"
+#import "FIRStackFrame.h"
+
+#import "FABMockApplicationIdentifierModel.h"
+#import "FIRCLSContext.h"
+#import "FIRCLSInstallIdentifierModel.h"
+#import "FIRCLSInternalReport.h"
+#import "FIRCLSMockFileManager.h"
+#import "FIRCLSMockSettings.h"
+#import "FIRMockInstallations.h"
+
+#define TEST_BUNDLE_ID (@"com.crashlytics.test")
+
+@interface FIRRecordExceptionModelTests : XCTestCase
+
+@property(nonatomic, strong) FIRCLSMockFileManager *fileManager;
+@property(nonatomic, strong) FIRCLSMockSettings *mockSettings;
+@property(nonatomic, strong) NSString *reportPath;
+
+@end
+
+@implementation FIRRecordExceptionModelTests
+
+- (void)setUp {
+ self.fileManager = [[FIRCLSMockFileManager alloc] init];
+ [self.fileManager setPathNamespace:TEST_BUNDLE_ID];
+
+ FABMockApplicationIdentifierModel *appIDModel = [[FABMockApplicationIdentifierModel alloc] init];
+ self.mockSettings = [[FIRCLSMockSettings alloc] initWithFileManager:self.fileManager
+ appIDModel:appIDModel];
+
+ FIRMockInstallations *iid = [[FIRMockInstallations alloc] initWithFID:@"test_instance_id"];
+
+ FIRCLSInstallIdentifierModel *installIDModel =
+ [[FIRCLSInstallIdentifierModel alloc] initWithInstallations:iid];
+
+ NSString *name = @"exception_model_report";
+ self.reportPath = [self.fileManager.rootPath stringByAppendingPathComponent:name];
+ [self.fileManager createDirectoryAtPath:self.reportPath];
+
+ FIRCLSInternalReport *report =
+ [[FIRCLSInternalReport alloc] initWithPath:self.reportPath
+ executionIdentifier:@"TEST_EXECUTION_IDENTIFIER"];
+
+ FIRCLSContextInitialize(report, self.mockSettings, installIDModel, self.fileManager);
+}
+
+- (void)tearDown {
+ [[NSFileManager defaultManager] removeItemAtPath:self.fileManager.rootPath error:nil];
+}
+
+- (void)testWrittenCLSRecordFile {
+ NSArray *stackTrace = @[
+ [FIRStackFrame stackFrameWithSymbol:@"CrashyFunc" file:@"AppLib.m" line:504],
+ [FIRStackFrame stackFrameWithSymbol:@"ApplicationMain" file:@"AppleLib" line:1],
+ [FIRStackFrame stackFrameWithSymbol:@"main()" file:@"main.m" line:201],
+ ];
+ NSString *name = @"FIRExceptionModelTestsCrash";
+ NSString *reason = @"Programmer made an error";
+
+ FIRExceptionModel *exceptionModel = [FIRExceptionModel exceptionModelWithName:name reason:reason];
+ exceptionModel.stackTrace = stackTrace;
+
+ FIRCLSExceptionRecordModel(exceptionModel);
+
+ NSData *data = [NSData
+ dataWithContentsOfFile:[self.reportPath
+ stringByAppendingPathComponent:@"custom_exception_a.clsrecord"]];
+ NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
+ NSDictionary *exception = json[@"exception"];
+ NSArray *frames = exception[@"frames"];
+ XCTAssertEqualObjects(exception[@"name"],
+ @"464952457863657074696f6e4d6f64656c54657374734372617368");
+ XCTAssertEqualObjects(exception[@"reason"], @"50726f6772616d6d6572206d61646520616e206572726f72");
+ XCTAssertEqual(frames.count, 3);
+ XCTAssertEqualObjects(frames[2][@"file"], @"6d61696e2e6d");
+ XCTAssertEqual([frames[2][@"line"] intValue], 201);
+ XCTAssertEqual([frames[2][@"offset"] intValue], 0);
+ XCTAssertEqual([frames[2][@"pc"] intValue], 0);
+ XCTAssertEqualObjects(frames[2][@"symbol"], @"6d61696e2829");
+}
+
+@end
diff --git a/Crashlytics/UnitTests/FIRStackFrameTests.m b/Crashlytics/UnitTests/FIRStackFrameTests.m
new file mode 100644
index 00000000000..7270609c71d
--- /dev/null
+++ b/Crashlytics/UnitTests/FIRStackFrameTests.m
@@ -0,0 +1,62 @@
+// Copyright 2020 Google
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#import
+
+#import "FIRStackFrame_Private.h"
+
+@interface FIRStackFrameTests : XCTestCase
+
+@end
+
+@implementation FIRStackFrameTests
+
+- (void)testBasicSymbolicatedCheck {
+ FIRStackFrame *stackFrame = [FIRStackFrame stackFrameWithSymbol:@"SYMBOL"
+ file:@"FILE"
+ line:54321];
+ XCTAssertEqualObjects(stackFrame.symbol, @"SYMBOL");
+ XCTAssertEqualObjects(stackFrame.fileName, @"FILE");
+ XCTAssertEqual(stackFrame.lineNumber, 54321);
+}
+
+- (void)testOwnership {
+ NSString *symbol = @"SYMBOL";
+ NSString *file = @"FILE";
+ FIRStackFrame *stackFrame = [FIRStackFrame stackFrameWithSymbol:symbol file:file line:54321];
+ symbol = @"NEW_SYMBOL";
+ file = nil;
+ XCTAssertEqualObjects(stackFrame.symbol, @"SYMBOL");
+ XCTAssertEqualObjects(stackFrame.fileName, @"FILE");
+ XCTAssertEqual(stackFrame.lineNumber, 54321);
+}
+
+- (void)testIntUIntConversion {
+ FIRStackFrame *stackFrame = [FIRStackFrame stackFrameWithSymbol:@"SYMBOL" file:@"FILE" line:100];
+ XCTAssertEqual(stackFrame.lineNumber, 100);
+
+ FIRStackFrame *stackFrame2 = [FIRStackFrame stackFrameWithSymbol:@"SYMBOL"
+ file:@"FILE"
+ line:-100];
+ XCTAssertEqual(stackFrame2.lineNumber, 4294967196);
+}
+
+- (void)testDescription {
+ FIRStackFrame *stackFrame = [FIRStackFrame stackFrameWithSymbol:@"FIRStackFrameTests"
+ file:@"testDescription"
+ line:35];
+ XCTAssertEqualObjects([stackFrame description], @"{testDescription - FIRStackFrameTests:35}");
+}
+
+@end
diff --git a/Crashlytics/UnitTests/Mocks/FIRCLSMockSymbolResolver.h b/Crashlytics/UnitTests/Mocks/FIRCLSMockSymbolResolver.h
index 2612803b217..fab3ee4f883 100644
--- a/Crashlytics/UnitTests/Mocks/FIRCLSMockSymbolResolver.h
+++ b/Crashlytics/UnitTests/Mocks/FIRCLSMockSymbolResolver.h
@@ -16,6 +16,6 @@
@interface FIRCLSMockSymbolResolver : FIRCLSSymbolResolver
-- (void)addMockFrame:(FIRCLSStackFrame *)frame atAddress:(uint64_t)address;
+- (void)addMockFrame:(FIRStackFrame *)frame atAddress:(uint64_t)address;
@end
diff --git a/Crashlytics/UnitTests/Mocks/FIRCLSMockSymbolResolver.m b/Crashlytics/UnitTests/Mocks/FIRCLSMockSymbolResolver.m
index 1173f890cb3..62d6b7a9551 100644
--- a/Crashlytics/UnitTests/Mocks/FIRCLSMockSymbolResolver.m
+++ b/Crashlytics/UnitTests/Mocks/FIRCLSMockSymbolResolver.m
@@ -14,7 +14,7 @@
#import "FIRCLSMockSymbolResolver.h"
-#import "FIRCLSStackFrame.h"
+#import "FIRStackFrame_Private.h"
@interface FIRCLSMockSymbolResolver () {
NSMutableDictionary *_frames;
@@ -35,20 +35,20 @@ - (instancetype)init {
return self;
}
-- (void)addMockFrame:(FIRCLSStackFrame *)frame atAddress:(uint64_t)address {
+- (void)addMockFrame:(FIRStackFrame *)frame atAddress:(uint64_t)address {
[_frames setObject:frame forKey:@(address)];
}
-- (BOOL)updateStackFrame:(FIRCLSStackFrame *)frame {
- FIRCLSStackFrame *matchedFrame = [_frames objectForKey:@([frame address])];
+- (BOOL)updateStackFrame:(FIRStackFrame *)frame {
+ FIRStackFrame *matchedFrame = [_frames objectForKey:@(frame.address)];
if (!matchedFrame) {
return NO;
}
- [frame setSymbol:[matchedFrame symbol]];
- [frame setLibrary:[matchedFrame library]];
- [frame setOffset:[matchedFrame offset]];
+ [frame setSymbol:matchedFrame.symbol];
+ [frame setLibrary:matchedFrame.library];
+ [frame setOffset:matchedFrame.offset];
return YES;
}