diff --git a/React/Base/RCTBatchedBridge.m b/React/Base/RCTBatchedBridge.m index 87e1fb8f7..6ca0fad9c 100644 --- a/React/Base/RCTBatchedBridge.m +++ b/React/Base/RCTBatchedBridge.m @@ -35,7 +35,8 @@ typedef NS_ENUM(NSUInteger, RCTBridgeFields) { RCTBridgeFieldRequestModuleIDs = 0, RCTBridgeFieldMethodIDs, - RCTBridgeFieldParamss, + RCTBridgeFieldParams, + RCTBridgeFieldCallID, }; RCT_EXTERN NSArray *RCTGetModuleClasses(void); @@ -65,6 +66,9 @@ RCT_EXTERN NSArray *RCTGetModuleClasses(void); NSUInteger _asyncInitializedModules; } +@synthesize flowID = _flowID; +@synthesize flowIDMap = _flowIDMap; + - (instancetype)initWithParentBridge:(RCTBridge *)bridge { RCTAssertMainThread(); @@ -606,6 +610,9 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithBundleURL:(__unused NSURL *)bundleUR _frameUpdateObservers = nil; _pendingCalls = nil; + if (_flowIDMap != NULL) { + CFRelease(_flowIDMap); + } }]; }); } @@ -812,15 +819,22 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithBundleURL:(__unused NSURL *)bundleUR - (void)handleBuffer:(NSArray *)buffer { NSArray *requestsArray = [RCTConvert NSArray:buffer]; - if (RCT_DEBUG && requestsArray.count <= RCTBridgeFieldParamss) { + + if (RCT_DEBUG && requestsArray.count <= RCTBridgeFieldParams) { RCTLogError(@"Buffer should contain at least %tu sub-arrays. Only found %tu", - RCTBridgeFieldParamss + 1, requestsArray.count); + RCTBridgeFieldParams + 1, requestsArray.count); return; } NSArray *moduleIDs = [RCTConvert NSNumberArray:requestsArray[RCTBridgeFieldRequestModuleIDs]]; NSArray *methodIDs = [RCTConvert NSNumberArray:requestsArray[RCTBridgeFieldMethodIDs]]; - NSArray *paramsArrays = [RCTConvert NSArrayArray:requestsArray[RCTBridgeFieldParamss]]; + NSArray *paramsArrays = [RCTConvert NSArrayArray:requestsArray[RCTBridgeFieldParams]]; + + int64_t callID = -1; + + if (requestsArray.count > 3) { + callID = [requestsArray[RCTBridgeFieldCallID] longLongValue]; + } if (RCT_DEBUG && (moduleIDs.count != methodIDs.count || moduleIDs.count != paramsArrays.count)) { RCTLogError(@"Invalid data message - all must be length: %zd", moduleIDs.count); @@ -853,6 +867,11 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithBundleURL:(__unused NSURL *)bundleUR @autoreleasepool { for (NSNumber *indexObj in calls) { NSUInteger index = indexObj.unsignedIntegerValue; + if (callID != -1 && _flowIDMap != NULL) { + int64_t newFlowID = (int64_t)CFDictionaryGetValue(_flowIDMap, (const void *)(_flowID + index)); + _RCTProfileEndFlowEvent(@(newFlowID)); + CFDictionaryRemoveValue(_flowIDMap, (const void *)(_flowID + index)); + } [self _handleRequestNumber:index moduleID:[moduleIDs[index] integerValue] methodID:[methodIDs[index] integerValue] @@ -871,6 +890,8 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithBundleURL:(__unused NSURL *)bundleUR dispatch_async(queue, block); } } + + _flowID = callID; } - (void)partialBatchDidFlush diff --git a/React/Base/RCTBridge+Private.h b/React/Base/RCTBridge+Private.h index 8e673a6b0..eedda8cd8 100644 --- a/React/Base/RCTBridge+Private.h +++ b/React/Base/RCTBridge+Private.h @@ -13,6 +13,10 @@ @interface RCTBridge () +// Used for the profiler flow events between JS and native +@property (nonatomic, assign) int64_t flowID; +@property (nonatomic, assign) CFMutableDictionaryRef flowIDMap; + + (instancetype)currentBridge; + (void)setCurrentBridge:(RCTBridge *)bridge; diff --git a/React/Executors/RCTJSCExecutor.m b/React/Executors/RCTJSCExecutor.m index a1489d35e..f96d2992a 100644 --- a/React/Executors/RCTJSCExecutor.m +++ b/React/Executors/RCTJSCExecutor.m @@ -314,6 +314,21 @@ static void RCTInstallJSCProfiler(RCTBridge *bridge, JSContextRef context) RCT_PROFILE_END_EVENT(tag.longLongValue, @"console", nil); }]; + __weak RCTBridge *weakBridge = _bridge; +#ifndef __clang_analyzer__ + weakBridge.flowIDMap = CFDictionaryCreateMutable(NULL, 0, NULL, NULL); +#endif + [self addSynchronousHookWithName:@"nativeTraceBeginAsyncFlow" usingBlock:^(__unused uint64_t tag, __unused NSString *name, int64_t cookie) { + int64_t newCookie = [_RCTProfileBeginFlowEvent() longLongValue]; + CFDictionarySetValue(weakBridge.flowIDMap, (const void *)cookie, (const void *)newCookie); + }]; + + [self addSynchronousHookWithName:@"nativeTraceEndAsyncFlow" usingBlock:^(__unused uint64_t tag, __unused NSString *name, int64_t cookie) { + int64_t newCookie = (int64_t)CFDictionaryGetValue(weakBridge.flowIDMap, (const void *)cookie); + _RCTProfileEndFlowEvent(@(newCookie)); + CFDictionaryRemoveValue(weakBridge.flowIDMap, (const void *)cookie); + }]; + [self executeBlockOnJavaScriptQueue:^{ RCTInstallJSCProfiler(_bridge, self.context.ctx); }]; diff --git a/React/Profiler/RCTProfile.h b/React/Profiler/RCTProfile.h index 17b3bf27d..2212e2cb1 100644 --- a/React/Profiler/RCTProfile.h +++ b/React/Profiler/RCTProfile.h @@ -191,7 +191,7 @@ RCT_EXTERN void RCTProfileRegisterCallbacks(RCTProfileCallbacks *); #define _RCTProfileBeginFlowEvent() @0 #define RCTProfileEndFlowEvent() -#define _RCTProfileEndFlowEvent() +#define _RCTProfileEndFlowEvent(...) #define RCTProfileIsProfiling(...) NO #define RCTProfileInit(...)