diff --git a/React/Base/RCTBatchedBridge.m b/React/Base/RCTBatchedBridge.m index 1c399ceb130..5e60f97a991 100644 --- a/React/Base/RCTBatchedBridge.m +++ b/React/Base/RCTBatchedBridge.m @@ -111,11 +111,6 @@ RCT_EXTERN NSArray *RCTGetModuleClasses(void); // Synchronously initialize all native modules that cannot be loaded lazily [self initModulesWithDispatchGroup:initModulesAndLoadSource]; - if (RCTProfileIsProfiling()) { - // Depends on moduleDataByID being loaded - RCTProfileHookModules(self); - } - __block NSString *config; dispatch_group_enter(initModulesAndLoadSource); dispatch_async(bridgeQueue, ^{ diff --git a/React/Base/RCTModuleData.m b/React/Base/RCTModuleData.m index a4f0b27ed4f..bf8bea10e20 100644 --- a/React/Base/RCTModuleData.m +++ b/React/Base/RCTModuleData.m @@ -98,6 +98,11 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init); "bridge.", _moduleClass); } } + + if (RCTProfileIsProfiling()) { + RCTProfileHookInstance(_instance); + } + // Bridge must be set before methodQueue is set up, as methodQueue // initialization requires it (View Managers get their queue by calling // self.bridge.uiManager.methodQueue) diff --git a/React/Modules/RCTUIManager.h b/React/Modules/RCTUIManager.h index 8681fe86fbc..bcf14366b6f 100644 --- a/React/Modules/RCTUIManager.h +++ b/React/Modules/RCTUIManager.h @@ -15,6 +15,11 @@ #import "RCTViewManager.h" #import "RCTRootView.h" +/** + * Default name for the UIManager queue + */ +RCT_EXTERN char *const RCTUIManagerQueueName; + /** * Posted right before re-render happens. This is a chance for views to invalidate their state so * next render cycle will pick up updated views and layout appropriately. diff --git a/React/Modules/RCTUIManager.m b/React/Modules/RCTUIManager.m index d161413357b..8f37df5f97f 100644 --- a/React/Modules/RCTUIManager.m +++ b/React/Modules/RCTUIManager.m @@ -46,6 +46,7 @@ static void RCTTraverseViewNodes(id view, void (^block)(id #import "RCTDefines.h" +#import "RCTAssert.h" /** * RCTProfile @@ -151,6 +152,11 @@ RCT_EXTERN void RCTProfileHookModules(RCTBridge *); */ RCT_EXTERN void RCTProfileUnhookModules(RCTBridge *); +/** + * Hook into all of a module's methods + */ +RCT_EXTERN void RCTProfileHookInstance(id instance); + /** * Send systrace or cpu profiling information to the packager * to present to the user @@ -217,6 +223,7 @@ RCT_EXTERN void RCTProfileHideControls(void); #define RCTProfileBlock(block, ...) block #define RCTProfileHookModules(...) +#define RCTProfileHookInstance(...) #define RCTProfileUnhookModules(...) #define RCTProfileSendResult(...) diff --git a/React/Profiler/RCTProfile.m b/React/Profiler/RCTProfile.m index 0c30d619020..6599faea3b4 100644 --- a/React/Profiler/RCTProfile.m +++ b/React/Profiler/RCTProfile.m @@ -214,7 +214,31 @@ void RCTProfileTrampolineEnd(void) RCT_PROFILE_END_EVENT(0, @"objc_call,modules,auto", nil); } -static void RCTProfileHookInstance(id instance) +static UIView *(*originalCreateView)(RCTComponentData *, SEL, NSNumber *); +static UIView *RCTProfileCreateView(RCTComponentData *self, SEL _cmd, NSNumber *tag) +{ + UIView *view = originalCreateView(self, _cmd, tag); + RCTProfileHookInstance(view); + return view; +} + +static void RCTProfileHookUIManager(RCTUIManager *uiManager) +{ + dispatch_async(dispatch_get_main_queue(), ^{ + for (id view in [uiManager valueForKey:@"viewRegistry"]) { + RCTProfileHookInstance([uiManager viewForReactTag:view]); + } + + Method createView = class_getInstanceMethod([RCTComponentData class], @selector(createViewWithTag:)); + + if (method_getImplementation(createView) != (IMP)RCTProfileCreateView) { + originalCreateView = (typeof(originalCreateView))method_getImplementation(createView); + method_setImplementation(createView, (IMP)RCTProfileCreateView); + } + }); +} + +void RCTProfileHookInstance(id instance) { Class moduleClass = object_getClass(instance); @@ -279,18 +303,10 @@ static void RCTProfileHookInstance(id instance) objc_registerClassPair(proxyClass); object_setClass(instance, proxyClass); -} -static UIView *(*originalCreateView)(RCTComponentData *, SEL, NSNumber *); - -RCT_EXTERN UIView *RCTProfileCreateView(RCTComponentData *self, SEL _cmd, NSNumber *tag); -UIView *RCTProfileCreateView(RCTComponentData *self, SEL _cmd, NSNumber *tag) -{ - UIView *view = originalCreateView(self, _cmd, tag); - - RCTProfileHookInstance(view); - - return view; + if (moduleClass == [RCTUIManager class]) { + RCTProfileHookUIManager((RCTUIManager *)instance); + } } void RCTProfileHookModules(RCTBridge *bridge) @@ -305,23 +321,13 @@ void RCTProfileHookModules(RCTBridge *bridge) #pragma clang diagnostic pop for (RCTModuleData *moduleData in [bridge valueForKey:@"moduleDataByID"]) { - [bridge dispatchBlock:^{ - RCTProfileHookInstance(moduleData.instance); - } queue:moduleData.methodQueue]; + // Only hook modules with an instance, to prevent initializing everything + if ([moduleData hasInstance]) { + [bridge dispatchBlock:^{ + RCTProfileHookInstance(moduleData.instance); + } queue:moduleData.methodQueue]; + } } - - dispatch_async(dispatch_get_main_queue(), ^{ - for (id view in [bridge.uiManager valueForKey:@"viewRegistry"]) { - RCTProfileHookInstance([bridge.uiManager viewForReactTag:view]); - } - - Method createView = class_getInstanceMethod([RCTComponentData class], @selector(createViewWithTag:)); - - if (method_getImplementation(createView) != (IMP)RCTProfileCreateView) { - originalCreateView = (typeof(originalCreateView))method_getImplementation(createView); - method_setImplementation(createView, (IMP)RCTProfileCreateView); - } - }); } static void RCTProfileUnhookInstance(id instance) @@ -337,17 +343,22 @@ void RCTProfileUnhookModules(RCTBridge *bridge) dispatch_group_enter(RCTProfileGetUnhookGroup()); - for (RCTModuleData *moduleData in [bridge valueForKey:@"moduleDataByID"]) { - RCTProfileUnhookInstance(moduleData.instance); + NSDictionary *moduleDataByID = [bridge valueForKey:@"moduleDataByID"]; + for (RCTModuleData *moduleData in moduleDataByID) { + if ([moduleData hasInstance]) { + RCTProfileUnhookInstance(moduleData.instance); + } } - dispatch_async(dispatch_get_main_queue(), ^{ - for (id view in [bridge.uiManager valueForKey:@"viewRegistry"]) { - RCTProfileUnhookInstance(view); - } + if ([bridge moduleIsInitialized:[RCTUIManager class]]) { + dispatch_async(dispatch_get_main_queue(), ^{ + for (id view in [bridge.uiManager valueForKey:@"viewRegistry"]) { + RCTProfileUnhookInstance(view); + } - dispatch_group_leave(RCTProfileGetUnhookGroup()); - }); + dispatch_group_leave(RCTProfileGetUnhookGroup()); + }); + } } #pragma mark - Private ObjC class only used for the vSYNC CADisplayLink target @@ -457,8 +468,7 @@ void RCTProfileInit(RCTBridge *bridge) // Set up thread ordering dispatch_async(RCTProfileGetQueue(), ^{ - NSString *shadowQueue = @(dispatch_queue_get_label([[bridge uiManager] methodQueue])); - NSArray *orderedThreads = @[@"JS async", RCTJSCThreadName, shadowQueue, @"main"]; + NSArray *orderedThreads = @[@"JS async", @"RCTPerformanceLogger", RCTJSCThreadName, @(RCTUIManagerQueueName), @"main"]; [orderedThreads enumerateObjectsUsingBlock:^(NSString *thread, NSUInteger idx, __unused BOOL *stop) { RCTProfileAddEvent(RCTProfileTraceEvents, @"ph": @"M", // metadata event