diff --git a/ReactCommon/turbomodule/core/platform/ios/RCTTurboModule.h b/ReactCommon/turbomodule/core/platform/ios/RCTTurboModule.h index 13505799164..827bffb78ef 100644 --- a/ReactCommon/turbomodule/core/platform/ios/RCTTurboModule.h +++ b/ReactCommon/turbomodule/core/platform/ios/RCTTurboModule.h @@ -22,6 +22,101 @@ ((RCTTurboModuleEnabled() && [(klass) conformsToProtocol:@protocol(RCTTurboModule)])) #define RCT_IS_TURBO_MODULE_INSTANCE(module) RCT_IS_TURBO_MODULE_CLASS([(module) class]) +typedef int MethodCallId; + +/** + * This interface exists to allow the application to collect performance + * metrics of the TurboModule system. By implementing each function, you can + * hook into various stages of TurboModule creation and method dispatch (both async and sync). + * + * Note: + * - TurboModule async method invocations can interleave, so methodCallId should be used as a unique id for a method + * call. + */ +@protocol RCTTurboModulePerformanceLogger +// Create TurboModule JS Object +- (void)createTurboModuleStart:(const char *)moduleName; +- (void)createTurboModuleEnd:(const char *)moduleName; +- (void)createTurboModuleCacheHit:(const char *)moduleName; +- (void)getCppTurboModuleFromTMMDelegateStart:(const char *)moduleName; +- (void)getCppTurboModuleFromTMMDelegateEnd:(const char *)moduleName; +- (void)getTurboModuleFromRCTTurboModuleStart:(const char *)moduleName; +- (void)getTurboModuleFromRCTTurboModuleEnd:(const char *)moduleName; +- (void)getTurboModuleFromRCTCxxModuleStart:(const char *)moduleName; +- (void)getTurboModuleFromRCTCxxModuleEnd:(const char *)moduleName; +- (void)getTurboModuleFromTMMDelegateStart:(const char *)moduleName; +- (void)getTurboModuleFromTMMDelegateEnd:(const char *)moduleName; + +// Create RCTTurboModule object +- (void)createRCTTurboModuleStart:(const char *)moduleName; +- (void)createRCTTurboModuleEnd:(const char *)moduleName; +- (void)createRCTTurboModuleCacheHit:(const char *)moduleName; +- (void)getRCTTurboModuleClassStart:(const char *)moduleName; +- (void)getRCTTurboModuleClassEnd:(const char *)moduleName; +- (void)getRCTTurboModuleInstanceStart:(const char *)moduleName; +- (void)getRCTTurboModuleInstanceEnd:(const char *)moduleName; +- (void)setupRCTTurboModuleDispatch:(const char *)moduleName; +- (void)setupRCTTurboModuleStart:(const char *)moduleName; +- (void)setupRCTTurboModuleEnd:(const char *)moduleName; +- (void)attachRCTBridgeToRCTTurboModuleStart:(const char *)moduleName; +- (void)attachRCTBridgeToRCTTurboModuleEnd:(const char *)moduleName; +- (void)attachMethodQueueToRCTTurboModuleStart:(const char *)moduleName; +- (void)attachMethodQueueToRCTTurboModuleEnd:(const char *)moduleName; +- (void)registerRCTTurboModuleForFrameUpdatesStart:(const char *)moduleName; +- (void)registerRCTTurboModuleForFrameUpdatesEnd:(const char *)moduleName; +- (void)dispatchDidInitializeModuleNotificationForRCTTurboModuleStart:(const char *)moduleName; +- (void)dispatchDidInitializeModuleNotificationForRCTTurboModuleEnd:(const char *)moduleName; + +// Sync method invocation +- (void)syncMethodCallStart:(const char *)moduleName + methodName:(const char *)methodName + methodCallId:(MethodCallId)methodCallId; +- (void)syncMethodCallEnd:(const char *)moduleName + methodName:(const char *)methodName + methodCallId:(MethodCallId)methodCallId; +- (void)syncMethodCallArgumentConversionStart:(const char *)moduleName + methodName:(const char *)methodName + methodCallId:(MethodCallId)methodCallId; +- (void)syncMethodCallArgumentConversionEnd:(const char *)moduleName + methodName:(const char *)methodName + methodCallId:(MethodCallId)methodCallId; +- (void)syncRCTTurboModuleMethodCallStart:(const char *)moduleName + methodName:(const char *)methodName + methodCallId:(MethodCallId)methodCallId; +- (void)syncRCTTurboModuleMethodCallEnd:(const char *)moduleName + methodName:(const char *)methodName + methodCallId:(MethodCallId)methodCallId; +- (void)syncMethodCallReturnConversionStart:(const char *)moduleName + methodName:(const char *)methodName + methodCallId:(MethodCallId)methodCallId; +- (void)syncMethodCallReturnConversionEnd:(const char *)moduleName + methodName:(const char *)methodName + methodCallId:(MethodCallId)methodCallId; + +// Async method invocation +- (void)asyncMethodCallStart:(const char *)moduleName + methodName:(const char *)methodName + methodCallId:(MethodCallId)methodCallId; +- (void)asyncMethodCallEnd:(const char *)moduleName + methodName:(const char *)methodName + methodCallId:(MethodCallId)methodCallId; +- (void)asyncMethodCallArgumentConversionStart:(const char *)moduleName + methodName:(const char *)methodName + methodCallId:(MethodCallId)methodCallId; +- (void)asyncMethodCallArgumentConversionEnd:(const char *)moduleName + methodName:(const char *)methodName + methodCallId:(MethodCallId)methodCallId; +- (void)asyncRCTTurboModuleMethodCallDispatch:(const char *)moduleName + methodName:(const char *)methodName + methodCallId:(MethodCallId)methodCallId; +- (void)asyncRCTTurboModuleMethodCallStart:(const char *)moduleName + methodName:(const char *)methodName + methodCallId:(MethodCallId)methodCallId; +- (void)asyncRCTTurboModuleMethodCallEnd:(const char *)moduleName + methodName:(const char *)methodName + methodCallId:(MethodCallId)methodCallId; +@end + namespace facebook { namespace react { @@ -44,6 +139,11 @@ class JSI_EXPORT ObjCTurboModule : public TurboModule { id instance_; + /** + * TODO(T63718299): Move this into the ObjCTurboModule constructor + */ + void setRCTTurboModulePerformanceLogger(id performanceLogger); + protected: void setMethodArgConversionSelector(NSString *methodName, int argIndex, NSString *fnName); @@ -55,23 +155,38 @@ class JSI_EXPORT ObjCTurboModule : public TurboModule { NSMutableDictionary *methodArgConversionSelectors_; NSDictionary *> *methodArgumentTypeNames_; NSString *getArgumentTypeName(NSString *methodName, int argIndex); + id performanceLogger_; + + /** + * Required for performance logging async method invocations. + * This field is static because two nth async method calls from different + * TurboModules can interleave, and should therefore be treated as two distinct calls. + */ + static MethodCallId methodCallId_; + + static MethodCallId getNewMethodCallId(); NSInvocation *getMethodInvocation( jsi::Runtime &runtime, - TurboModuleMethodValueKind valueKind, - const id module, - std::shared_ptr jsInvoker, - const std::string &methodName, + TurboModuleMethodValueKind returnType, + const char *methodName, SEL selector, const jsi::Value *args, size_t count, - NSMutableArray *retainedObjectsForInvocation); + NSMutableArray *retainedObjectsForInvocation, + MethodCallId methodCallId); + jsi::Value performMethodInvocation( + jsi::Runtime &runtime, + TurboModuleMethodValueKind returnType, + const char *methodName, + NSInvocation *inv, + NSMutableArray *retainedObjectsForInvocation, + MethodCallId methodCallId); BOOL hasMethodArgConversionSelector(NSString *methodName, int argIndex); SEL getMethodArgConversionSelector(NSString *methodName, int argIndex); - using PromiseInvocationBlock = - void (^)(jsi::Runtime &rt, RCTPromiseResolveBlock resolveWrapper, RCTPromiseRejectBlock rejectWrapper); + using PromiseInvocationBlock = void (^)(RCTPromiseResolveBlock resolveWrapper, RCTPromiseRejectBlock rejectWrapper); jsi::Value createPromise(jsi::Runtime &runtime, std::shared_ptr jsInvoker, PromiseInvocationBlock invoke); }; diff --git a/ReactCommon/turbomodule/core/platform/ios/RCTTurboModule.mm b/ReactCommon/turbomodule/core/platform/ios/RCTTurboModule.mm index 04cf1bf7e93..ee39e0174e2 100644 --- a/ReactCommon/turbomodule/core/platform/ios/RCTTurboModule.mm +++ b/ReactCommon/turbomodule/core/platform/ios/RCTTurboModule.mm @@ -303,15 +303,13 @@ jsi::Value ObjCTurboModule::createPromise( rejectWasCalled = YES; }; - invokeCopy(rt, resolveBlock, rejectBlock); + invokeCopy(resolveBlock, rejectBlock); return jsi::Value::undefined(); }); return Promise.callAsConstructor(runtime, fn); } -namespace { - /** * Perform method invocation on a specific queue as configured by the module class. * This serves as a backward-compatible support for RCTBridgeModule's methodQueue API. @@ -321,40 +319,64 @@ namespace { * - ObjC module methods will be always be called from JS thread. * They may decide to dispatch to a different queue as needed. */ -jsi::Value performMethodInvocation( +jsi::Value ObjCTurboModule::performMethodInvocation( jsi::Runtime &runtime, + TurboModuleMethodValueKind returnType, + const char *methodName, NSInvocation *inv, - TurboModuleMethodValueKind valueKind, - const id module, - std::shared_ptr jsInvoker, - NSMutableArray *retainedObjectsForInvocation) + NSMutableArray *retainedObjectsForInvocation, + MethodCallId methodCallId) { __block id result; jsi::Runtime *rt = &runtime; + __weak id weakModule = instance_; + id performanceLogger = performanceLogger_; + const char *moduleName = name_.c_str(); + const bool isSync = returnType != VoidKind && returnType != PromiseKind; + void (^block)() = ^{ - [inv invokeWithTarget:module]; + if (!weakModule) { + return; + } + + id strongModule = weakModule; + + if (isSync) { + [performanceLogger syncRCTTurboModuleMethodCallStart:moduleName methodName:methodName methodCallId:methodCallId]; + } else { + [performanceLogger asyncRCTTurboModuleMethodCallStart:moduleName methodName:methodName methodCallId:methodCallId]; + } + + [inv invokeWithTarget:strongModule]; [retainedObjectsForInvocation removeAllObjects]; - if (valueKind == VoidKind) { + if (returnType == VoidKind) { + [performanceLogger asyncRCTTurboModuleMethodCallEnd:moduleName methodName:methodName methodCallId:methodCallId]; return; } void *rawResult; [inv getReturnValue:&rawResult]; result = (__bridge id)rawResult; + [performanceLogger syncRCTTurboModuleMethodCallEnd:moduleName methodName:methodName methodCallId:methodCallId]; }; // Backward-compatibility layer for calling module methods on specific queue. dispatch_queue_t methodQueue = NULL; - if ([module conformsToProtocol:@protocol(RCTBridgeModule)] && [module respondsToSelector:@selector(methodQueue)]) { - methodQueue = [module performSelector:@selector(methodQueue)]; + if ([instance_ conformsToProtocol:@protocol(RCTBridgeModule)] && + [instance_ respondsToSelector:@selector(methodQueue)]) { + methodQueue = [instance_ performSelector:@selector(methodQueue)]; } if (methodQueue == NULL || methodQueue == RCTJSThread) { // This is the default mode of execution: on JS thread. block(); } else if (methodQueue == dispatch_get_main_queue()) { - if (valueKind == VoidKind) { + if (returnType == VoidKind) { // Void methods are treated as async for now, so there's no need to block here. + + [performanceLogger_ asyncRCTTurboModuleMethodCallDispatch:moduleName + methodName:methodName + methodCallId:methodCallId]; RCTExecuteOnMainQueue(block); } else { // This is not ideal, but provides the simplest mechanism for now. @@ -363,7 +385,10 @@ jsi::Value performMethodInvocation( RCTUnsafeExecuteOnMainQueueSync(block); } } else { - if (valueKind == VoidKind) { + if (returnType == VoidKind) { + [performanceLogger_ asyncRCTTurboModuleMethodCallDispatch:moduleName + methodName:methodName + methodCallId:methodCallId]; dispatch_async(methodQueue, block); } else { dispatch_sync(methodQueue, block); @@ -372,33 +397,48 @@ jsi::Value performMethodInvocation( // VoidKind can't be null // PromiseKind, and FunctionKind must throw errors always - if (valueKind != VoidKind && valueKind != PromiseKind && valueKind != FunctionKind && + if (returnType != VoidKind && returnType != PromiseKind && returnType != FunctionKind && (result == (id)kCFNull || result == nil)) { return jsi::Value::null(); } + jsi::Value returnValue = jsi::Value::undefined(); + [performanceLogger_ syncMethodCallReturnConversionStart:moduleName methodName:methodName methodCallId:methodCallId]; + // TODO: Re-use value conversion logic from existing impl, if possible. - switch (valueKind) { - case VoidKind: - return jsi::Value::undefined(); - case BooleanKind: - return convertNSNumberToJSIBoolean(*rt, (NSNumber *)result); - case NumberKind: - return convertNSNumberToJSINumber(*rt, (NSNumber *)result); - case StringKind: - return convertNSStringToJSIString(*rt, (NSString *)result); - case ObjectKind: - return convertNSDictionaryToJSIObject(*rt, (NSDictionary *)result); - case ArrayKind: - return convertNSArrayToJSIArray(*rt, (NSArray *)result); + switch (returnType) { + case VoidKind: { + break; + } + case BooleanKind: { + returnValue = convertNSNumberToJSIBoolean(*rt, (NSNumber *)result); + break; + } + case NumberKind: { + returnValue = convertNSNumberToJSINumber(*rt, (NSNumber *)result); + break; + } + case StringKind: { + returnValue = convertNSStringToJSIString(*rt, (NSString *)result); + break; + } + case ObjectKind: { + returnValue = convertNSDictionaryToJSIObject(*rt, (NSDictionary *)result); + break; + } + case ArrayKind: { + returnValue = convertNSArrayToJSIArray(*rt, (NSArray *)result); + break; + } case FunctionKind: throw std::runtime_error("convertInvocationResultToJSIValue: FunctionKind is not supported yet."); case PromiseKind: throw std::runtime_error("convertInvocationResultToJSIValue: PromiseKind wasn't handled properly."); } -} -} // namespace + [performanceLogger_ syncMethodCallReturnConversionEnd:moduleName methodName:methodName methodCallId:methodCallId]; + return returnValue; +} /** * Given a method name, and an argument index, return type of that argument. @@ -460,15 +500,28 @@ NSString *ObjCTurboModule::getArgumentTypeName(NSString *methodName, int argInde NSInvocation *ObjCTurboModule::getMethodInvocation( jsi::Runtime &runtime, - TurboModuleMethodValueKind valueKind, - const id module, - std::shared_ptr jsInvoker, - const std::string &methodName, + TurboModuleMethodValueKind returnType, + const char *methodName, SEL selector, const jsi::Value *args, size_t count, - NSMutableArray *retainedObjectsForInvocation) + NSMutableArray *retainedObjectsForInvocation, + MethodCallId methodCallId) { + const bool isSync = returnType != VoidKind && returnType != PromiseKind; + const char *moduleName = name_.c_str(); + const id module = instance_; + + if (isSync) { + [performanceLogger_ syncMethodCallArgumentConversionStart:moduleName + methodName:methodName + methodCallId:methodCallId]; + } else { + [performanceLogger_ asyncMethodCallArgumentConversionStart:moduleName + methodName:methodName + methodCallId:methodCallId]; + } + NSInvocation *inv = [NSInvocation invocationWithMethodSignature:[[module class] instanceMethodSignatureForSelector:selector]]; [inv setSelector:selector]; @@ -516,10 +569,10 @@ NSInvocation *ObjCTurboModule::getMethodInvocation( /** * Convert arg to ObjC objects. */ - id objCArg = convertJSIValueToObjCObject(runtime, *arg, jsInvoker); + id objCArg = convertJSIValueToObjCObject(runtime, *arg, jsInvoker_); if (objCArg) { - NSString *methodNameNSString = @(methodName.c_str()); + NSString *methodNameNSString = @(methodName); /** * Convert objects using RCTConvert. @@ -570,6 +623,14 @@ NSInvocation *ObjCTurboModule::getMethodInvocation( } } + if (isSync) { + [performanceLogger_ syncMethodCallArgumentConversionEnd:moduleName methodName:methodName methodCallId:methodCallId]; + } else { + [performanceLogger_ asyncMethodCallArgumentConversionEnd:moduleName + methodName:methodName + methodCallId:methodCallId]; + } + return inv; } @@ -581,35 +642,62 @@ ObjCTurboModule::ObjCTurboModule( { } +void ObjCTurboModule::setRCTTurboModulePerformanceLogger(id performanceLogger) +{ + performanceLogger_ = performanceLogger; +} + +MethodCallId ObjCTurboModule::methodCallId_{0}; + +MethodCallId ObjCTurboModule::getNewMethodCallId() +{ + return methodCallId_++; +} + jsi::Value ObjCTurboModule::invokeObjCMethod( jsi::Runtime &runtime, - TurboModuleMethodValueKind valueKind, - const std::string &methodName, + TurboModuleMethodValueKind returnType, + const std::string &methodNameStr, SEL selector, const jsi::Value *args, size_t count) { - NSMutableArray *retainedObjectsForInvocation = [NSMutableArray arrayWithCapacity:count + 2]; - NSInvocation *inv = getMethodInvocation( - runtime, valueKind, instance_, jsInvoker_, methodName, selector, args, count, retainedObjectsForInvocation); + MethodCallId methodCallId = getNewMethodCallId(); + const bool isSync = returnType != VoidKind && returnType != PromiseKind; + const char *moduleName = name_.c_str(); + const char *methodName = methodNameStr.c_str(); - if (valueKind == PromiseKind) { - // Promise return type is special cased today, i.e. it needs extra 2 function args for resolve() and reject(), to - // be passed to the actual ObjC++ class method. - return createPromise( - runtime, - jsInvoker_, - ^(jsi::Runtime &rt, RCTPromiseResolveBlock resolveBlock, RCTPromiseRejectBlock rejectBlock) { - [inv setArgument:(void *)&resolveBlock atIndex:count + 2]; - [inv setArgument:(void *)&rejectBlock atIndex:count + 3]; - [retainedObjectsForInvocation addObject:resolveBlock]; - [retainedObjectsForInvocation addObject:rejectBlock]; - // The return type becomes void in the ObjC side. - performMethodInvocation(rt, inv, VoidKind, instance_, jsInvoker_, retainedObjectsForInvocation); - }); + if (isSync) { + [performanceLogger_ syncMethodCallStart:moduleName methodName:methodName methodCallId:methodCallId]; + } else { + [performanceLogger_ asyncMethodCallStart:moduleName methodName:methodName methodCallId:methodCallId]; } - return performMethodInvocation(runtime, inv, valueKind, instance_, jsInvoker_, retainedObjectsForInvocation); + NSMutableArray *retainedObjectsForInvocation = [NSMutableArray arrayWithCapacity:count + 2]; + NSInvocation *inv = getMethodInvocation( + runtime, returnType, methodName, selector, args, count, retainedObjectsForInvocation, methodCallId); + + jsi::Value returnValue = returnType == PromiseKind + ? createPromise( + runtime, + jsInvoker_, + ^(RCTPromiseResolveBlock resolveBlock, RCTPromiseRejectBlock rejectBlock) { + [inv setArgument:(void *)&resolveBlock atIndex:count + 2]; + [inv setArgument:(void *)&rejectBlock atIndex:count + 3]; + [retainedObjectsForInvocation addObject:resolveBlock]; + [retainedObjectsForInvocation addObject:rejectBlock]; + // The return type becomes void in the ObjC side. + performMethodInvocation(runtime, VoidKind, methodName, inv, retainedObjectsForInvocation, methodCallId); + }) + : performMethodInvocation(runtime, returnType, methodName, inv, retainedObjectsForInvocation, methodCallId); + + if (isSync) { + [performanceLogger_ syncMethodCallEnd:moduleName methodName:methodName methodCallId:methodCallId]; + } else { + [performanceLogger_ asyncMethodCallEnd:moduleName methodName:methodName methodCallId:methodCallId]; + } + + return returnValue; } BOOL ObjCTurboModule::hasMethodArgConversionSelector(NSString *methodName, int argIndex) diff --git a/ReactCommon/turbomodule/core/platform/ios/RCTTurboModuleManager.h b/ReactCommon/turbomodule/core/platform/ios/RCTTurboModuleManager.h index 2adeb6bbbc3..f3c3ffb5cea 100644 --- a/ReactCommon/turbomodule/core/platform/ios/RCTTurboModuleManager.h +++ b/ReactCommon/turbomodule/core/platform/ios/RCTTurboModuleManager.h @@ -42,6 +42,11 @@ delegate:(id)delegate jsInvoker:(std::shared_ptr)jsInvoker; +- (instancetype)initWithBridge:(RCTBridge *)bridge + delegate:(id)delegate + jsInvoker:(std::shared_ptr)jsInvoker + performanceLogger:(id)performanceLogger; + - (void)installJSBindingWithRuntime:(facebook::jsi::Runtime *)runtime; - (void)invalidate; diff --git a/ReactCommon/turbomodule/core/platform/ios/RCTTurboModuleManager.mm b/ReactCommon/turbomodule/core/platform/ios/RCTTurboModuleManager.mm index 8553041e814..c9bf5caeb68 100644 --- a/ReactCommon/turbomodule/core/platform/ios/RCTTurboModuleManager.mm +++ b/ReactCommon/turbomodule/core/platform/ios/RCTTurboModuleManager.mm @@ -37,6 +37,7 @@ static Class getFallbackClassFromName(const char *name) @implementation RCTTurboModuleManager { jsi::Runtime *_runtime; std::shared_ptr _jsInvoker; + id _performanceLogger; __weak id _delegate; __weak RCTBridge *_bridge; /** @@ -65,12 +66,21 @@ static Class getFallbackClassFromName(const char *name) - (instancetype)initWithBridge:(RCTBridge *)bridge delegate:(id)delegate jsInvoker:(std::shared_ptr)jsInvoker +{ + return [self initWithBridge:bridge delegate:delegate jsInvoker:jsInvoker performanceLogger:nil]; +} + +- (instancetype)initWithBridge:(RCTBridge *)bridge + delegate:(id)delegate + jsInvoker:(std::shared_ptr)jsInvoker + performanceLogger:(id)performanceLogger { if (self = [super init]) { _jsInvoker = jsInvoker; _delegate = delegate; _bridge = bridge; _invalidating = false; + _performanceLogger = performanceLogger; // Necessary to allow NativeModules to lookup TurboModules [bridge setRCTTurboModuleLookupDelegate:self]; @@ -114,6 +124,7 @@ static Class getFallbackClassFromName(const char *name) { auto turboModuleLookup = _turboModuleCache.find(moduleName); if (turboModuleLookup != _turboModuleCache.end()) { + [_performanceLogger createTurboModuleCacheHit:moduleName]; return turboModuleLookup->second; } @@ -122,7 +133,9 @@ static Class getFallbackClassFromName(const char *name) * Pure C++ modules get priority. */ if ([_delegate respondsToSelector:@selector(getTurboModule:jsInvoker:)]) { + [_performanceLogger getCppTurboModuleFromTMMDelegateStart:moduleName]; auto turboModule = [_delegate getTurboModule:moduleName jsInvoker:_jsInvoker]; + [_performanceLogger getCppTurboModuleFromTMMDelegateEnd:moduleName]; if (turboModule != nullptr) { _turboModuleCache.insert({moduleName, turboModule}); return turboModule; @@ -132,7 +145,9 @@ static Class getFallbackClassFromName(const char *name) /** * Step 2: Look for platform-specific modules. */ + [_performanceLogger createRCTTurboModuleStart:moduleName]; id module = [self provideRCTTurboModule:moduleName]; + [_performanceLogger createRCTTurboModuleEnd:moduleName]; // If we request that a TurboModule be created, its respective ObjC class must exist // If the class doesn't exist, then provideRCTTurboModule returns nil @@ -145,7 +160,9 @@ static Class getFallbackClassFromName(const char *name) // If RCTTurboModule supports creating its own C++ TurboModule object, // allow it to do so. if ([module respondsToSelector:@selector(getTurboModuleWithJsInvoker:)]) { + [_performanceLogger getTurboModuleFromRCTTurboModuleStart:moduleName]; auto turboModule = [module getTurboModuleWithJsInvoker:_jsInvoker]; + [_performanceLogger getTurboModuleFromRCTTurboModuleEnd:moduleName]; assert(turboModule != nullptr); _turboModuleCache.insert({moduleName, turboModule}); return turboModule; @@ -158,7 +175,9 @@ static Class getFallbackClassFromName(const char *name) if ([moduleClass isSubclassOfClass:RCTCxxModule.class]) { // Use TurboCxxModule compat class to wrap the CxxModule instance. // This is only for migration convenience, despite less performant. + [_performanceLogger getTurboModuleFromRCTCxxModuleStart:moduleName]; auto turboModule = std::make_shared([((RCTCxxModule *)module) createModule], _jsInvoker); + [_performanceLogger getTurboModuleFromRCTCxxModuleEnd:moduleName]; _turboModuleCache.insert({moduleName, turboModule}); return turboModule; } @@ -166,7 +185,9 @@ static Class getFallbackClassFromName(const char *name) /** * Step 2d: Return an exact sub-class of ObjC TurboModule */ + [_performanceLogger getTurboModuleFromTMMDelegateStart:moduleName]; auto turboModule = [_delegate getTurboModule:moduleName instance:module jsInvoker:_jsInvoker]; + [_performanceLogger getTurboModuleFromTMMDelegateEnd:moduleName]; if (turboModule != nullptr) { _turboModuleCache.insert({moduleName, turboModule}); } @@ -191,6 +212,7 @@ static Class getFallbackClassFromName(const char *name) auto rctTurboModuleCacheLookup = _rctTurboModuleCache.find(moduleName); if (rctTurboModuleCacheLookup != _rctTurboModuleCache.end()) { + [_performanceLogger createRCTTurboModuleCacheHit:moduleName]; return rctTurboModuleCacheLookup->second; } @@ -202,6 +224,8 @@ static Class getFallbackClassFromName(const char *name) /** * Step 2a: Resolve platform-specific class. */ + [_performanceLogger getRCTTurboModuleClassStart:moduleName]; + if ([_delegate respondsToSelector:@selector(getModuleClassFromName:)]) { moduleClass = [_delegate getModuleClassFromName:moduleName]; } @@ -210,6 +234,8 @@ static Class getFallbackClassFromName(const char *name) moduleClass = getFallbackClassFromName(moduleName); } + [_performanceLogger getRCTTurboModuleClassEnd:moduleName]; + if (![moduleClass conformsToProtocol:@protocol(RCTTurboModule)]) { return nil; } @@ -217,12 +243,16 @@ static Class getFallbackClassFromName(const char *name) /** * Step 2b: Ask hosting application/delegate to instantiate this class */ + [_performanceLogger getRCTTurboModuleInstanceStart:moduleName]; + if ([_delegate respondsToSelector:@selector(getModuleInstanceFromClass:)]) { module = [_delegate getModuleInstanceFromClass:moduleClass]; } else { module = [moduleClass new]; } + [_performanceLogger getRCTTurboModuleInstanceEnd:moduleName]; + if ([module respondsToSelector:@selector(setTurboModuleLookupDelegate:)]) { [module setTurboModuleLookupDelegate:self]; } @@ -232,12 +262,15 @@ static Class getFallbackClassFromName(const char *name) __weak id weakModule = (id)module; __weak RCTBridge *weakBridge = _bridge; + id performanceLogger = _performanceLogger; auto setupTurboModule = ^{ if (!weakModule) { return; } + [performanceLogger setupRCTTurboModuleStart:moduleName]; + id strongModule = weakModule; RCTBridge *strongBridge = weakBridge; @@ -251,6 +284,8 @@ static Class getFallbackClassFromName(const char *name) * NativeModule. */ if ([strongModule respondsToSelector:@selector(bridge)] && strongBridge) { + [performanceLogger attachRCTBridgeToRCTTurboModuleStart:moduleName]; + /** * Just because a NativeModule has the `bridge` method, it doesn't mean * that it has synthesized the bridge in its implementation. Therefore, @@ -275,6 +310,8 @@ static Class getFallbackClassFromName(const char *name) "or provide your own setter method.", RCTBridgeModuleNameForClass(strongModule)); } + + [performanceLogger attachRCTBridgeToRCTTurboModuleEnd:moduleName]; } /** @@ -283,6 +320,8 @@ static Class getFallbackClassFromName(const char *name) * `@synthesize methodQueue = _methodQueue` */ if ([strongModule respondsToSelector:@selector(methodQueue)]) { + [performanceLogger attachMethodQueueToRCTTurboModuleStart:moduleName]; + dispatch_queue_t methodQueue = [strongModule performSelector:@selector(methodQueue)]; if (!methodQueue) { NSString *moduleClassName = NSStringFromClass(strongModule.class); @@ -299,6 +338,8 @@ static Class getFallbackClassFromName(const char *name) moduleClassName); } } + + [performanceLogger attachMethodQueueToRCTTurboModuleEnd:moduleName]; } /** @@ -309,8 +350,10 @@ static Class getFallbackClassFromName(const char *name) * rollout. */ if (strongBridge) { + [performanceLogger registerRCTTurboModuleForFrameUpdatesStart:moduleName]; RCTModuleData *data = [[RCTModuleData alloc] initWithModuleInstance:strongModule bridge:strongBridge]; [strongBridge registerModuleForFrameUpdates:strongModule withModuleData:data]; + [performanceLogger registerRCTTurboModuleForFrameUpdatesEnd:moduleName]; } /** @@ -319,10 +362,14 @@ static Class getFallbackClassFromName(const char *name) * TODO(T41180176): Investigate whether we can delete this after TM * rollout. */ + [performanceLogger dispatchDidInitializeModuleNotificationForRCTTurboModuleStart:moduleName]; [[NSNotificationCenter defaultCenter] postNotificationName:RCTDidInitializeModuleNotification object:strongBridge userInfo:@{@"module" : module, @"bridge" : RCTNullIfNil([strongBridge parentBridge])}]; + [performanceLogger dispatchDidInitializeModuleNotificationForRCTTurboModuleEnd:moduleName]; + + [performanceLogger setupRCTTurboModuleEnd:moduleName]; }; if ([[module class] respondsToSelector:@selector(requiresMainQueueSetup)] && @@ -334,6 +381,7 @@ static Class getFallbackClassFromName(const char *name) * * TODO(T63807674): Investigate the right migration plan off of this */ + [_performanceLogger setupRCTTurboModuleDispatch:moduleName]; RCTUnsafeExecuteOnMainQueueSync(setupTurboModule); } else { setupTurboModule(); @@ -354,7 +402,9 @@ static Class getFallbackClassFromName(const char *name) __weak __typeof(self) weakSelf = self; react::TurboModuleBinding::install( - *_runtime, [weakSelf](const std::string &name) -> std::shared_ptr { + *_runtime, + [weakSelf, + performanceLogger = _performanceLogger](const std::string &name) -> std::shared_ptr { if (!weakSelf) { return nullptr; } @@ -367,6 +417,8 @@ static Class getFallbackClassFromName(const char *name) [strongSelf->_bridge.performanceLogger markStartForTag:RCTPLTurboModuleSetup]; } + [performanceLogger createTurboModuleStart:moduleName]; + /** * By default, all TurboModules are long-lived. * Additionally, if a TurboModule with the name `name` isn't found, then we @@ -374,6 +426,16 @@ static Class getFallbackClassFromName(const char *name) */ auto turboModule = [strongSelf provideTurboModule:moduleName]; + /** + * TODO(T63718299): Move this setter into the ObjCTurboModule constructor + */ + if (std::shared_ptr objCTurboModule = + std::dynamic_pointer_cast(turboModule)) { + objCTurboModule->setRCTTurboModulePerformanceLogger(performanceLogger); + }; + + [performanceLogger createTurboModuleEnd:moduleName]; + if (moduleWasNotInitialized && [strongSelf moduleIsInitialized:moduleName]) { [strongSelf->_bridge.performanceLogger markStopForTag:RCTPLTurboModuleSetup]; [strongSelf notifyAboutTurboModuleSetup:moduleName];