FIRCrashlytics.m 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  1. // Copyright 2019 Google
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. #include <stdatomic.h>
  15. #if __has_include(<FBLPromises/FBLPromises.h>)
  16. #import <FBLPromises/FBLPromises.h>
  17. #else
  18. #import "FBLPromises.h"
  19. #endif
  20. #include "Crashlytics/Crashlytics/Components/FIRCLSCrashedMarkerFile.h"
  21. #include "Crashlytics/Crashlytics/Components/FIRCLSGlobals.h"
  22. #import "Crashlytics/Crashlytics/Components/FIRCLSHost.h"
  23. #include "Crashlytics/Crashlytics/Components/FIRCLSUserLogging.h"
  24. #import "Crashlytics/Crashlytics/DataCollection/FIRCLSDataCollectionArbiter.h"
  25. #import "Crashlytics/Crashlytics/DataCollection/FIRCLSDataCollectionToken.h"
  26. #import "Crashlytics/Crashlytics/FIRCLSUserDefaults/FIRCLSUserDefaults.h"
  27. #include "Crashlytics/Crashlytics/Handlers/FIRCLSException.h"
  28. #import "Crashlytics/Crashlytics/Helpers/FIRCLSDefines.h"
  29. #include "Crashlytics/Crashlytics/Helpers/FIRCLSUtility.h"
  30. #import "Crashlytics/Crashlytics/Models/FIRCLSExecutionIdentifierModel.h"
  31. #import "Crashlytics/Crashlytics/Models/FIRCLSFileManager.h"
  32. #import "Crashlytics/Crashlytics/Models/FIRCLSSettings.h"
  33. #import "Crashlytics/Crashlytics/Settings/Models/FIRCLSApplicationIdentifierModel.h"
  34. #import "Crashlytics/Crashlytics/Helpers/FIRCLSLogger.h"
  35. #import "Crashlytics/Shared/FIRCLSByteUtility.h"
  36. #import "Crashlytics/Shared/FIRCLSConstants.h"
  37. #import "Crashlytics/Shared/FIRCLSFABHost.h"
  38. #import "Crashlytics/Crashlytics/Controllers/FIRCLSAnalyticsManager.h"
  39. #import "Crashlytics/Crashlytics/Controllers/FIRCLSContextManager.h"
  40. #import "Crashlytics/Crashlytics/Controllers/FIRCLSExistingReportManager.h"
  41. #import "Crashlytics/Crashlytics/Controllers/FIRCLSManagerData.h"
  42. #import "Crashlytics/Crashlytics/Controllers/FIRCLSNotificationManager.h"
  43. #import "Crashlytics/Crashlytics/Controllers/FIRCLSReportManager.h"
  44. #import "Crashlytics/Crashlytics/Controllers/FIRCLSReportUploader.h"
  45. #import "Crashlytics/Crashlytics/Controllers/FIRCLSRolloutsPersistenceManager.h"
  46. #import "Crashlytics/Crashlytics/Private/FIRCLSExistingReportManager_Private.h"
  47. #import "Crashlytics/Crashlytics/Private/FIRCLSOnDemandModel_Private.h"
  48. #import "Crashlytics/Crashlytics/Private/FIRExceptionModel_Private.h"
  49. #import "FirebaseCore/Extension/FirebaseCoreInternal.h"
  50. #import "FirebaseInstallations/Source/Library/Private/FirebaseInstallationsInternal.h"
  51. #import "Interop/Analytics/Public/FIRAnalyticsInterop.h"
  52. #import <GoogleDataTransport/GoogleDataTransport.h>
  53. @import FirebaseSessions;
  54. @import FirebaseRemoteConfigInterop;
  55. #if SWIFT_PACKAGE
  56. @import FirebaseCrashlyticsSwift;
  57. #elif __has_include(<FirebaseCrashlytics/FirebaseCrashlytics-Swift.h>)
  58. #import <FirebaseCrashlytics/FirebaseCrashlytics-Swift.h>
  59. #elif __has_include("FirebaseCrashlytics-Swift.h")
  60. // If frameworks are not available, fall back to importing the header as it
  61. // should be findable from a header search path pointing to the build
  62. // directory. See #12611 for more context.
  63. #import "FirebaseCrashlytics-Swift.h"
  64. #endif
  65. #if TARGET_OS_IPHONE
  66. #import <UIKit/UIKit.h>
  67. #endif
  68. FIRCLSContext _firclsContext;
  69. dispatch_queue_t _firclsLoggingQueue;
  70. dispatch_queue_t _firclsBinaryImageQueue;
  71. dispatch_queue_t _firclsExceptionQueue;
  72. static atomic_bool _hasInitializedInstance;
  73. NSString *const FIRCLSGoogleTransportMappingID = @"1206";
  74. /// Empty protocol to register with FirebaseCore's component system.
  75. @protocol FIRCrashlyticsInstanceProvider <NSObject>
  76. @end
  77. @interface FIRCrashlytics () <FIRLibrary,
  78. FIRCrashlyticsInstanceProvider,
  79. FIRSessionsSubscriber,
  80. FIRRolloutsStateSubscriber>
  81. @property(nonatomic) BOOL didPreviouslyCrash;
  82. @property(nonatomic, copy) NSString *googleAppID;
  83. @property(nonatomic) FIRCLSDataCollectionArbiter *dataArbiter;
  84. @property(nonatomic) FIRCLSFileManager *fileManager;
  85. @property(nonatomic) FIRCLSReportManager *reportManager;
  86. @property(nonatomic) FIRCLSReportUploader *reportUploader;
  87. @property(nonatomic, strong) FIRCLSExistingReportManager *existingReportManager;
  88. @property(nonatomic, strong) FIRCLSAnalyticsManager *analyticsManager;
  89. @property(nonatomic, strong) FIRCLSRemoteConfigManager *remoteConfigManager;
  90. // Dependencies common to each of the Controllers
  91. @property(nonatomic, strong) FIRCLSManagerData *managerData;
  92. @end
  93. @implementation FIRCrashlytics
  94. #pragma mark - Singleton Support
  95. - (instancetype)initWithApp:(FIRApp *)app
  96. appInfo:(NSDictionary *)appInfo
  97. installations:(FIRInstallations *)installations
  98. analytics:(id<FIRAnalyticsInterop>)analytics
  99. sessions:(id<FIRSessionsProvider>)sessions
  100. remoteConfig:(id<FIRRemoteConfigInterop>)remoteConfig {
  101. self = [super init];
  102. if (self) {
  103. bool expectedCalled = NO;
  104. if (!atomic_compare_exchange_strong(&_hasInitializedInstance, &expectedCalled, YES)) {
  105. FIRCLSErrorLog(@"Cannot instantiate more than one instance of Crashlytics.");
  106. return nil;
  107. }
  108. NSLog(@"[Firebase/Crashlytics] Version %@", FIRCLSSDKVersion());
  109. FIRCLSDeveloperLog("Crashlytics", @"Running on %@, %@ (%@)", FIRCLSHostModelInfo(),
  110. FIRCLSHostOSDisplayVersion(), FIRCLSHostOSBuildVersion());
  111. GDTCORTransport *googleTransport =
  112. [[GDTCORTransport alloc] initWithMappingID:FIRCLSGoogleTransportMappingID
  113. transformers:nil
  114. target:kGDTCORTargetCSH];
  115. _fileManager = [[FIRCLSFileManager alloc] init];
  116. _googleAppID = app.options.googleAppID;
  117. _dataArbiter = [[FIRCLSDataCollectionArbiter alloc] initWithApp:app withAppInfo:appInfo];
  118. FIRCLSApplicationIdentifierModel *appModel = [[FIRCLSApplicationIdentifierModel alloc] init];
  119. FIRCLSSettings *settings = [[FIRCLSSettings alloc] initWithFileManager:_fileManager
  120. appIDModel:appModel];
  121. FIRCLSOnDemandModel *onDemandModel =
  122. [[FIRCLSOnDemandModel alloc] initWithFIRCLSSettings:settings fileManager:_fileManager];
  123. _managerData = [[FIRCLSManagerData alloc] initWithGoogleAppID:_googleAppID
  124. googleTransport:googleTransport
  125. installations:installations
  126. analytics:analytics
  127. fileManager:_fileManager
  128. dataArbiter:_dataArbiter
  129. settings:settings
  130. onDemandModel:onDemandModel];
  131. if (sessions) {
  132. FIRCLSDebugLog(@"Registering Sessions SDK subscription for session data");
  133. // Subscription should be made after the DataCollectionArbiter
  134. // is initialized so that the Sessions SDK can immediately get
  135. // the data collection state.
  136. //
  137. // It should also be made after managerData is initialized so
  138. // that the ContextManager can accept data
  139. [sessions registerWithSubscriber:self];
  140. }
  141. _reportUploader = [[FIRCLSReportUploader alloc] initWithManagerData:_managerData];
  142. _existingReportManager =
  143. [[FIRCLSExistingReportManager alloc] initWithManagerData:_managerData
  144. reportUploader:_reportUploader];
  145. _analyticsManager = [[FIRCLSAnalyticsManager alloc] initWithAnalytics:analytics];
  146. _reportManager = [[FIRCLSReportManager alloc] initWithManagerData:_managerData
  147. existingReportManager:_existingReportManager
  148. analyticsManager:_analyticsManager];
  149. _didPreviouslyCrash = [_fileManager didCrashOnPreviousExecution];
  150. // Process did crash during previous execution
  151. if (_didPreviouslyCrash) {
  152. // Delete the crash file marker in the background ensure start up is as fast as possible
  153. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
  154. NSString *crashedMarkerFileFullPath = [[self.fileManager rootPath]
  155. stringByAppendingPathComponent:[NSString
  156. stringWithUTF8String:FIRCLSCrashedMarkerFileName]];
  157. [self.fileManager removeItemAtPath:crashedMarkerFileFullPath];
  158. });
  159. }
  160. [[[_reportManager startWithProfiling] then:^id _Nullable(NSNumber *_Nullable value) {
  161. if (![value boolValue]) {
  162. FIRCLSErrorLog(@"Crash reporting could not be initialized");
  163. }
  164. return value;
  165. }] catch:^void(NSError *error) {
  166. FIRCLSErrorLog(@"Crash reporting failed to initialize with error: %@", error);
  167. }];
  168. // RemoteConfig subscription should be made after session report directory created.
  169. if (remoteConfig) {
  170. FIRCLSDebugLog(@"Registering RemoteConfig SDK subscription for rollouts data");
  171. FIRCLSRolloutsPersistenceManager *persistenceManager =
  172. [[FIRCLSRolloutsPersistenceManager alloc]
  173. initWithFileManager:_fileManager
  174. andQueue:dispatch_queue_create(
  175. "com.google.firebase.FIRCLSRolloutsPersistence",
  176. DISPATCH_QUEUE_SERIAL)];
  177. _remoteConfigManager =
  178. [[FIRCLSRemoteConfigManager alloc] initWithRemoteConfig:remoteConfig
  179. persistenceDelegate:persistenceManager];
  180. [remoteConfig registerRolloutsStateSubscriber:self for:FIRRemoteConfigConstants.FIRNamespaceGoogleMobilePlatform];
  181. }
  182. }
  183. return self;
  184. }
  185. + (void)load {
  186. [FIRApp registerInternalLibrary:(Class<FIRLibrary>)self withName:@"firebase-crashlytics"];
  187. [FIRSessionsDependencies addDependencyWithName:FIRSessionsSubscriberNameCrashlytics];
  188. }
  189. + (NSArray<FIRComponent *> *)componentsToRegister {
  190. FIRComponentCreationBlock creationBlock =
  191. ^id _Nullable(FIRComponentContainer *container, BOOL *isCacheable) {
  192. if (!container.app.isDefaultApp) {
  193. FIRCLSErrorLog(@"Crashlytics must be used with the default Firebase app.");
  194. return nil;
  195. }
  196. id<FIRAnalyticsInterop> analytics = FIR_COMPONENT(FIRAnalyticsInterop, container);
  197. id<FIRSessionsProvider> sessions = FIR_COMPONENT(FIRSessionsProvider, container);
  198. id<FIRRemoteConfigInterop> remoteConfig = FIR_COMPONENT(FIRRemoteConfigInterop, container);
  199. FIRInstallations *installations = [FIRInstallations installationsWithApp:container.app];
  200. *isCacheable = YES;
  201. return [[FIRCrashlytics alloc] initWithApp:container.app
  202. appInfo:NSBundle.mainBundle.infoDictionary
  203. installations:installations
  204. analytics:analytics
  205. sessions:sessions
  206. remoteConfig:remoteConfig];
  207. };
  208. FIRComponent *component =
  209. [FIRComponent componentWithProtocol:@protocol(FIRCrashlyticsInstanceProvider)
  210. instantiationTiming:FIRInstantiationTimingEagerInDefaultApp
  211. creationBlock:creationBlock];
  212. return @[ component ];
  213. }
  214. + (instancetype)crashlytics {
  215. // The container will return the same instance since isCacheable is set
  216. FIRApp *defaultApp = [FIRApp defaultApp]; // Missing configure will be logged here.
  217. // Get the instance from the `FIRApp`'s container. This will create a new instance the
  218. // first time it is called, and since `isCacheable` is set in the component creation
  219. // block, it will return the existing instance on subsequent calls.
  220. id<FIRCrashlyticsInstanceProvider> instance =
  221. FIR_COMPONENT(FIRCrashlyticsInstanceProvider, defaultApp.container);
  222. // In the component creation block, we return an instance of `FIRCrashlytics`. Cast it and
  223. // return it.
  224. return (FIRCrashlytics *)instance;
  225. }
  226. - (void)setCrashlyticsCollectionEnabled:(BOOL)enabled {
  227. [self.dataArbiter setCrashlyticsCollectionEnabled:enabled];
  228. }
  229. - (BOOL)isCrashlyticsCollectionEnabled {
  230. return [self.dataArbiter isCrashlyticsCollectionEnabled];
  231. }
  232. #pragma mark - API: didCrashDuringPreviousExecution
  233. - (BOOL)didCrashDuringPreviousExecution {
  234. return self.didPreviouslyCrash;
  235. }
  236. - (void)processDidCrashDuringPreviousExecution {
  237. NSString *crashedMarkerFileName = [NSString stringWithUTF8String:FIRCLSCrashedMarkerFileName];
  238. NSString *crashedMarkerFileFullPath =
  239. [[self.fileManager rootPath] stringByAppendingPathComponent:crashedMarkerFileName];
  240. self.didPreviouslyCrash = [self.fileManager fileExistsAtPath:crashedMarkerFileFullPath];
  241. if (self.didPreviouslyCrash) {
  242. // Delete the crash file marker in the background ensure start up is as fast as possible
  243. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
  244. [self.fileManager removeItemAtPath:crashedMarkerFileFullPath];
  245. });
  246. }
  247. }
  248. #pragma mark - API: Logging
  249. - (void)log:(NSString *)msg {
  250. FIRCLSLog(@"%@", msg);
  251. }
  252. - (void)logWithFormat:(NSString *)format, ... {
  253. va_list args;
  254. va_start(args, format);
  255. [self logWithFormat:format arguments:args];
  256. va_end(args);
  257. }
  258. - (void)logWithFormat:(NSString *)format arguments:(va_list)args {
  259. [self log:[[NSString alloc] initWithFormat:format arguments:args]];
  260. }
  261. #pragma mark - API: Accessors
  262. - (void)checkForUnsentReportsWithCompletion:(void (^)(BOOL))completion {
  263. [[self.reportManager checkForUnsentReports]
  264. then:^id _Nullable(FIRCrashlyticsReport *_Nullable value) {
  265. completion(value ? true : false);
  266. return nil;
  267. }];
  268. }
  269. - (void)checkAndUpdateUnsentReportsWithCompletion:
  270. (void (^)(FIRCrashlyticsReport *_Nonnull))completion {
  271. [[self.reportManager checkForUnsentReports]
  272. then:^id _Nullable(FIRCrashlyticsReport *_Nullable value) {
  273. completion(value);
  274. return nil;
  275. }];
  276. }
  277. - (void)sendUnsentReports {
  278. [self.reportManager sendUnsentReports];
  279. }
  280. - (void)deleteUnsentReports {
  281. [self.reportManager deleteUnsentReports];
  282. }
  283. #pragma mark - API: setUserID
  284. - (void)setUserID:(nullable NSString *)userID {
  285. FIRCLSUserLoggingRecordInternalKeyValue(FIRCLSUserIdentifierKey, userID);
  286. }
  287. #pragma mark - API: setCustomValue
  288. - (void)setCustomValue:(nullable id)value forKey:(NSString *)key {
  289. FIRCLSUserLoggingRecordUserKeyValue(key, value);
  290. }
  291. - (void)setCustomKeysAndValues:(NSDictionary *)keysAndValues {
  292. FIRCLSUserLoggingRecordUserKeysAndValues(keysAndValues);
  293. }
  294. #pragma mark - API: Development Platform
  295. // These two methods are deprecated by our own API, so
  296. // its ok to implement them
  297. #pragma clang diagnostic push
  298. #pragma clang diagnostic ignored "-Wdeprecated-implementations"
  299. + (void)setDevelopmentPlatformName:(NSString *)name {
  300. [[self crashlytics] setDevelopmentPlatformName:name];
  301. }
  302. + (void)setDevelopmentPlatformVersion:(NSString *)version {
  303. [[self crashlytics] setDevelopmentPlatformVersion:version];
  304. }
  305. #pragma clang diagnostic pop
  306. - (NSString *)developmentPlatformName {
  307. FIRCLSErrorLog(@"developmentPlatformName is write-only");
  308. return nil;
  309. }
  310. - (void)setDevelopmentPlatformName:(NSString *)developmentPlatformName {
  311. FIRCLSUserLoggingRecordInternalKeyValue(FIRCLSDevelopmentPlatformNameKey,
  312. developmentPlatformName);
  313. }
  314. - (NSString *)developmentPlatformVersion {
  315. FIRCLSErrorLog(@"developmentPlatformVersion is write-only");
  316. return nil;
  317. }
  318. - (void)setDevelopmentPlatformVersion:(NSString *)developmentPlatformVersion {
  319. FIRCLSUserLoggingRecordInternalKeyValue(FIRCLSDevelopmentPlatformVersionKey,
  320. developmentPlatformVersion);
  321. }
  322. #pragma mark - API: Errors and Exceptions
  323. - (void)recordError:(NSError *)error {
  324. [self recordError:error userInfo:nil];
  325. }
  326. - (void)recordError:(NSError *)error userInfo:(NSDictionary<NSString *, id> *)userInfo {
  327. NSString *rolloutsInfoJSON = [_remoteConfigManager getRolloutAssignmentsEncodedJsonString];
  328. FIRCLSUserLoggingRecordError(error, userInfo, rolloutsInfoJSON);
  329. }
  330. - (void)recordExceptionModel:(FIRExceptionModel *)exceptionModel {
  331. NSString *rolloutsInfoJSON = [_remoteConfigManager getRolloutAssignmentsEncodedJsonString];
  332. FIRCLSExceptionRecordModel(exceptionModel, rolloutsInfoJSON);
  333. }
  334. - (void)recordOnDemandExceptionModel:(FIRExceptionModel *)exceptionModel {
  335. [self.managerData.onDemandModel
  336. recordOnDemandExceptionIfQuota:exceptionModel
  337. withDataCollectionEnabled:[self.dataArbiter isCrashlyticsCollectionEnabled]
  338. usingExistingReportManager:self.existingReportManager];
  339. }
  340. #pragma mark - FIRSessionsSubscriber
  341. - (void)onSessionChanged:(FIRSessionDetails *_Nonnull)session {
  342. FIRCLSDebugLog(@"Session ID changed: %@", session.sessionId.copy);
  343. [self.managerData.contextManager setAppQualitySessionId:session.sessionId.copy];
  344. }
  345. - (BOOL)isDataCollectionEnabled {
  346. return self.dataArbiter.isCrashlyticsCollectionEnabled;
  347. }
  348. - (FIRSessionsSubscriberName)sessionsSubscriberName {
  349. return FIRSessionsSubscriberNameCrashlytics;
  350. }
  351. #pragma mark - FIRRolloutsStateSubscriber
  352. - (void)rolloutsStateDidChange:(FIRRolloutsState *_Nonnull)rolloutsState {
  353. if (!_remoteConfigManager) {
  354. FIRCLSDebugLog(@"rolloutsStateDidChange gets called without init the rc manager.");
  355. return;
  356. }
  357. NSString *currentReportID = _managerData.executionIDModel.executionID;
  358. [_remoteConfigManager updateRolloutsStateWithRolloutsState:rolloutsState
  359. reportID:currentReportID];
  360. }
  361. @end