Skip to content

Commit f5f8fa5

Browse files
authored
Make Crashlytics context Init unblocking main (#14754)
1 parent 70b3f93 commit f5f8fa5

10 files changed

+104
-81
lines changed

Crashlytics/CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
# Unreleased
2+
- [fixed] Improved startup time by putting some initialization steps on a background. (#13675, #13232)
3+
14
# 11.9.0
25
- [fixed] Made on-demand fatal recording thread suspension configurable through setting to improve performance and avoid audio glitch on Unity. Change is for framework only.
36

Crashlytics/Crashlytics/Components/FIRCLSContext.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ __BEGIN_DECLS
3939
@class FIRCLSInstallIdentifierModel;
4040
@class FIRCLSFileManager;
4141
@class FIRCLSContextInitData;
42+
@class FBLPromise;
4243
#endif
4344

4445
typedef struct {
@@ -82,7 +83,8 @@ typedef struct {
8283
FIRCLSAllocatorRef allocator;
8384
} FIRCLSContext;
8485
#ifdef __OBJC__
85-
bool FIRCLSContextInitialize(FIRCLSContextInitData* initData, FIRCLSFileManager* fileManager);
86+
FBLPromise* FIRCLSContextInitialize(FIRCLSContextInitData* initData,
87+
FIRCLSFileManager* fileManager);
8688
FIRCLSContextInitData* FIRCLSContextBuildInitData(FIRCLSInternalReport* report,
8789
FIRCLSSettings* settings,
8890
FIRCLSFileManager* fileManager,

Crashlytics/Crashlytics/Components/FIRCLSContext.m

+6-9
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,6 @@
4646
// We need enough space here for the context, plus storage for strings.
4747
#define CLS_MINIMUM_READABLE_SIZE (sizeof(FIRCLSReadOnlyContext) + 4096 * 4)
4848

49-
static const int64_t FIRCLSContextInitWaitTime = 5LL * NSEC_PER_SEC;
50-
5149
static const char* FIRCLSContextAppendToRoot(NSString* root, NSString* component);
5250
static void FIRCLSContextAllocate(FIRCLSContext* context);
5351

@@ -76,7 +74,8 @@
7674
return initData;
7775
}
7876

79-
bool FIRCLSContextInitialize(FIRCLSContextInitData* initData, FIRCLSFileManager* fileManager) {
77+
FBLPromise* FIRCLSContextInitialize(FIRCLSContextInitData* initData,
78+
FIRCLSFileManager* fileManager) {
8079
if (!initData) {
8180
return false;
8281
}
@@ -102,6 +101,8 @@ bool FIRCLSContextInitialize(FIRCLSContextInitData* initData, FIRCLSFileManager*
102101
// some values that aren't tied to particular subsystem
103102
_firclsContext.readonly->debuggerAttached = FIRCLSProcessDebuggerAttached();
104103

104+
__block FBLPromise* initPromise = [FBLPromise pendingPromise];
105+
105106
dispatch_group_async(group, queue, ^{
106107
FIRCLSHostInitialize(&_firclsContext.readonly->host);
107108
});
@@ -220,14 +221,10 @@ bool FIRCLSContextInitialize(FIRCLSContextInitData* initData, FIRCLSFileManager*
220221
if (!FIRCLSAllocatorProtect(_firclsContext.allocator)) {
221222
FIRCLSSDKLog("Error: Memory protection failed\n");
222223
}
224+
[initPromise fulfill:nil];
223225
});
224226

225-
if (dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, FIRCLSContextInitWaitTime)) !=
226-
0) {
227-
FIRCLSSDKLog("Error: Delayed initialization\n");
228-
}
229-
230-
return true;
227+
return initPromise;
231228
}
232229

233230
void FIRCLSContextBaseInit(void) {

Crashlytics/Crashlytics/Controllers/FIRCLSContextManager.h

+3-3
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,9 @@ NS_ASSUME_NONNULL_BEGIN
3434
/// a new Session ID.
3535
@property(nonatomic, copy) NSString *appQualitySessionId;
3636

37-
- (BOOL)setupContextWithReport:(FIRCLSInternalReport *)report
38-
settings:(FIRCLSSettings *)settings
39-
fileManager:(FIRCLSFileManager *)fileManager;
37+
- (FBLPromise *)setupContextWithReport:(FIRCLSInternalReport *)report
38+
settings:(FIRCLSSettings *)settings
39+
fileManager:(FIRCLSFileManager *)fileManager;
4040

4141
@end
4242

Crashlytics/Crashlytics/Controllers/FIRCLSContextManager.m

+3-3
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,9 @@ - (instancetype)init {
4040
return self;
4141
}
4242

43-
- (BOOL)setupContextWithReport:(FIRCLSInternalReport *)report
44-
settings:(FIRCLSSettings *)settings
45-
fileManager:(FIRCLSFileManager *)fileManager {
43+
- (FBLPromise *)setupContextWithReport:(FIRCLSInternalReport *)report
44+
settings:(FIRCLSSettings *)settings
45+
fileManager:(FIRCLSFileManager *)fileManager {
4646
_report = report;
4747
_settings = settings;
4848
_fileManager = fileManager;

Crashlytics/Crashlytics/Controllers/FIRCLSReportManager.m

+58-48
Original file line numberDiff line numberDiff line change
@@ -289,15 +289,26 @@ - (FBLPromise *)deleteUnsentReports {
289289

290290
BOOL launchFailure = [self.launchMarker checkForAndCreateLaunchMarker];
291291

292-
FIRCLSInternalReport *report = [self setupCurrentReport:executionIdentifier];
292+
__block FIRCLSInternalReport *report = [self setupCurrentReport:executionIdentifier];
293293
if (!report) {
294294
FIRCLSErrorLog(@"Unable to setup a new report");
295295
}
296296

297-
if (![self startCrashReporterWithProfilingReport:report]) {
298-
FIRCLSErrorLog(@"Unable to start crash reporter");
299-
report = nil;
300-
}
297+
FBLPromise<NSNumber *> *reportProfilingPromise;
298+
reportProfilingPromise =
299+
[[self startCrashReporterWithProfilingReport:report] then:^id _Nullable(id _Nullable value) {
300+
if ([value isEqual:@NO]) {
301+
FIRCLSErrorLog(@"Unable to start crash reporter");
302+
report = nil;
303+
return [FBLPromise resolvedWith:@NO];
304+
}
305+
306+
// empty for disabled start-up time
307+
dispatch_async(FIRCLSGetLoggingQueue(), ^{
308+
FIRCLSUserLoggingWriteInternalKeyValue(FIRCLSStartTimeKey, @"");
309+
});
310+
return [FBLPromise resolvedWith:@YES];
311+
}];
301312

302313
#if CLS_METRICKIT_SUPPORTED
303314
if (@available(iOS 15, *)) {
@@ -317,9 +328,12 @@ - (FBLPromise *)deleteUnsentReports {
317328
[self beginSettingsWithToken:dataCollectionToken];
318329

319330
// Wait for MetricKit data to be available, then continue to send reports and resolve promise.
320-
promise = [[self waitForMetricKitData]
331+
promise = [[reportProfilingPromise onQueue:_dispatchQueue
332+
then:^id _Nullable(id _Nullable value) {
333+
return [self waitForMetricKitData];
334+
}]
321335
onQueue:_dispatchQueue
322-
then:^id _Nullable(id _Nullable metricKitValue) {
336+
then:^id _Nullable(id _Nullable value) {
323337
[self beginReportUploadsWithToken:dataCollectionToken blockingSend:launchFailure];
324338

325339
// If data collection is enabled, the SDK will not notify the user
@@ -335,36 +349,33 @@ - (FBLPromise *)deleteUnsentReports {
335349

336350
// Wait for an action to get sent, either from processReports: or automatic data collection,
337351
// and for MetricKit data to be available.
338-
promise = [[FBLPromise all:@[ [self waitForReportAction], [self waitForMetricKitData] ]]
352+
promise = [[reportProfilingPromise
339353
onQueue:_dispatchQueue
340-
then:^id _Nullable(NSArray *_Nullable wrappedActionAndData) {
341-
// Process the actions for the reports on disk.
342-
FIRCLSReportAction action = [[wrappedActionAndData firstObject] reportActionValue];
343-
344-
if (action == FIRCLSReportActionSend) {
345-
FIRCLSDebugLog(@"Sending unsent reports.");
346-
FIRCLSDataCollectionToken *dataCollectionToken =
347-
[FIRCLSDataCollectionToken validToken];
348-
349-
[self beginSettingsWithToken:dataCollectionToken];
350-
351-
[self beginReportUploadsWithToken:dataCollectionToken blockingSend:NO];
352-
353-
} else if (action == FIRCLSReportActionDelete) {
354-
FIRCLSDebugLog(@"Deleting unsent reports.");
355-
[self.existingReportManager deleteUnsentReports];
356-
} else {
357-
FIRCLSErrorLog(@"Unknown report action: %d", action);
358-
}
359-
return @(report != nil);
360-
}];
361-
}
362-
363-
if (report != nil) {
364-
// empty for disabled start-up time
365-
dispatch_async(FIRCLSGetLoggingQueue(), ^{
366-
FIRCLSUserLoggingWriteInternalKeyValue(FIRCLSStartTimeKey, @"");
367-
});
354+
then:^id _Nullable(id _Nullable value) {
355+
return [FBLPromise all:@[ [self waitForReportAction], [self waitForMetricKitData] ]];
356+
}] onQueue:_dispatchQueue
357+
then:^id _Nullable(NSArray *_Nullable wrappedActionAndData) {
358+
// Process the actions for the reports on disk.
359+
FIRCLSReportAction action =
360+
[[wrappedActionAndData firstObject] reportActionValue];
361+
362+
if (action == FIRCLSReportActionSend) {
363+
FIRCLSDebugLog(@"Sending unsent reports.");
364+
FIRCLSDataCollectionToken *dataCollectionToken =
365+
[FIRCLSDataCollectionToken validToken];
366+
367+
[self beginSettingsWithToken:dataCollectionToken];
368+
369+
[self beginReportUploadsWithToken:dataCollectionToken blockingSend:NO];
370+
371+
} else if (action == FIRCLSReportActionDelete) {
372+
FIRCLSDebugLog(@"Deleting unsent reports.");
373+
[self.existingReportManager deleteUnsentReports];
374+
} else {
375+
FIRCLSErrorLog(@"Unknown report action: %d", action);
376+
}
377+
return @(report != nil);
378+
}];
368379
}
369380

370381
// To make the code more predictable and therefore testable, don't resolve the startup promise
@@ -412,24 +423,23 @@ - (void)beginReportUploadsWithToken:(FIRCLSDataCollectionToken *)token
412423
}
413424
}
414425

415-
- (BOOL)startCrashReporterWithProfilingReport:(FIRCLSInternalReport *)report {
426+
- (FBLPromise<NSNumber *> *)startCrashReporterWithProfilingReport:(FIRCLSInternalReport *)report {
416427
if (!report) {
417-
return NO;
418-
}
419-
420-
if (![self.contextManager setupContextWithReport:report
421-
settings:self.settings
422-
fileManager:_fileManager]) {
423-
return NO;
428+
return [FBLPromise resolvedWith:@NO];
424429
}
425430

426-
[self.notificationManager registerNotificationListener];
431+
return [[self.contextManager setupContextWithReport:report
432+
settings:self.settings
433+
fileManager:_fileManager]
434+
then:^id _Nullable(id _Nullable value) {
435+
[self.notificationManager registerNotificationListener];
427436

428-
[self.analyticsManager registerAnalyticsListener];
437+
[self.analyticsManager registerAnalyticsListener];
429438

430-
[self crashReportingSetupCompleted];
439+
[self crashReportingSetupCompleted];
431440

432-
return YES;
441+
return [FBLPromise resolvedWith:@YES];
442+
}];
433443
}
434444

435445
- (void)crashReportingSetupCompleted {

Crashlytics/UnitTests/FIRCLSContextManagerTests.m

+12-9
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,10 @@ - (void)tearDown {
6868
}
6969

7070
- (void)test_notSettingSessionID_protoHasNilSessionID {
71-
[self.contextManager setupContextWithReport:self.report
72-
settings:self.mockSettings
73-
fileManager:self.fileManager];
71+
FBLPromiseAwait([self.contextManager setupContextWithReport:self.report
72+
settings:self.mockSettings
73+
fileManager:self.fileManager],
74+
nil);
7475

7576
FIRCLSReportAdapter *adapter = [[FIRCLSReportAdapter alloc] initWithPath:self.report.path
7677
googleAppId:@"TestGoogleAppID"
@@ -84,9 +85,10 @@ - (void)test_notSettingSessionID_protoHasNilSessionID {
8485
- (void)test_settingSessionIDMultipleTimes_protoHasLastSessionID {
8586
[self.contextManager setAppQualitySessionId:TestContextSessionID];
8687

87-
[self.contextManager setupContextWithReport:self.report
88-
settings:self.mockSettings
89-
fileManager:self.fileManager];
88+
FBLPromiseAwait([self.contextManager setupContextWithReport:self.report
89+
settings:self.mockSettings
90+
fileManager:self.fileManager],
91+
nil);
9092

9193
[self.contextManager setAppQualitySessionId:TestContextSessionID2];
9294

@@ -101,9 +103,10 @@ - (void)test_settingSessionIDMultipleTimes_protoHasLastSessionID {
101103
}
102104

103105
- (void)test_settingSessionIDOutOfOrder_protoHasLastSessionID {
104-
[self.contextManager setupContextWithReport:self.report
105-
settings:self.mockSettings
106-
fileManager:self.fileManager];
106+
FBLPromiseAwait([self.contextManager setupContextWithReport:self.report
107+
settings:self.mockSettings
108+
fileManager:self.fileManager],
109+
nil);
107110

108111
[self.contextManager setAppQualitySessionId:TestContextSessionID];
109112

Crashlytics/UnitTests/FIRCLSOnDemandModelTests.m

+4-3
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,10 @@ - (void)setUp {
9898
executionIdentifier:@"TEST_EXECUTION_IDENTIFIER"];
9999

100100
FIRCLSContextManager *contextManager = [[FIRCLSContextManager alloc] init];
101-
[contextManager setupContextWithReport:report
102-
settings:self.mockSettings
103-
fileManager:self.fileManager];
101+
FBLPromiseAwait([contextManager setupContextWithReport:report
102+
settings:self.mockSettings
103+
fileManager:self.fileManager],
104+
nil);
104105
}
105106

106107
- (void)tearDown {

Crashlytics/UnitTests/FIRRecordExceptionModelTests.m

+4-3
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,10 @@ - (void)setUp {
5454
executionIdentifier:@"TEST_EXECUTION_IDENTIFIER"];
5555

5656
FIRCLSContextManager *contextManager = [[FIRCLSContextManager alloc] init];
57-
[contextManager setupContextWithReport:report
58-
settings:self.mockSettings
59-
fileManager:self.fileManager];
57+
FBLPromiseAwait([contextManager setupContextWithReport:report
58+
settings:self.mockSettings
59+
fileManager:self.fileManager],
60+
nil);
6061
}
6162

6263
- (void)tearDown {

Crashlytics/UnitTests/Mocks/FIRCLSMockReportManager.m

+8-2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
#if __has_include(<FBLPromises/FBLPromises.h>)
16+
#import <FBLPromises/FBLPromises.h>
17+
#else
18+
#import "FBLPromises.h"
19+
#endif
20+
1521
#import "Crashlytics/UnitTests/Mocks/FIRCLSMockReportManager.h"
1622

1723
#import "Crashlytics/Crashlytics/Components/FIRCLSContext.h"
@@ -22,10 +28,10 @@
2228

2329
@implementation FIRCLSMockReportManager
2430

25-
- (BOOL)startCrashReporterWithProfilingReport:(FIRCLSInternalReport *)report {
31+
- (FBLPromise<NSNumber *> *)startCrashReporterWithProfilingReport:(FIRCLSInternalReport *)report {
2632
NSLog(@"Crash Reporting system disabled for testing");
2733

28-
return YES;
34+
return [FBLPromise resolvedWith:@YES];
2935
}
3036

3137
- (BOOL)installCrashReportingHandlers:(FIRCLSContextInitData *)initData {

0 commit comments

Comments
 (0)