diff --git a/React/Base/RCTBatchedBridge.m b/React/Base/RCTBatchedBridge.m index 65db38432..8ec30748c 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(); @@ -591,6 +595,9 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithBundleURL:(__unused NSURL *)bundleUR _modulesByName_DEPRECATED = nil; _frameUpdateObservers = nil; + if (_flowIDMap != NULL) { + CFRelease(_flowIDMap); + } }]; }); } @@ -793,15 +800,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); @@ -835,6 +849,11 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithBundleURL:(__unused NSURL *)bundleUR @autoreleasepool { for (NSNumber *indexObj in calls) { NSUInteger index = indexObj.unsignedIntegerValue; + if (callID != -1) { + 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] @@ -853,6 +872,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 91675efa5..649771656 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 a586fd07e..25ed23e21 100644 --- a/React/Executors/RCTJSCExecutor.m +++ b/React/Executors/RCTJSCExecutor.m @@ -283,6 +283,20 @@ static void RCTInstallJSCProfiler(RCTBridge *bridge, JSContextRef context) CFDictionaryRemoveValue(cookieMap, (const void *)cookie); }; +#ifndef __clang_analyzer__ + weakBridge.flowIDMap = CFDictionaryCreateMutable(NULL, 0, NULL, NULL); +#endif + context[@"nativeTraceBeginAsyncFlow"] = ^(__unused uint64_t tag, __unused NSString *name, int64_t cookie) { + int64_t newCookie = [_RCTProfileBeginFlowEvent() longLongValue]; + CFDictionarySetValue(weakBridge.flowIDMap, (const void *)cookie, (const void *)newCookie); + }; + + context[@"nativeTraceEndAsyncFlow"] = ^(__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); + }; + context[@"nativeTraceBeginSection"] = ^(NSNumber *tag, NSString *profileName){ static int profileCounter = 1; if (!profileName) { diff --git a/React/Profiler/RCTProfile.h b/React/Profiler/RCTProfile.h index 1cd721ab7..7b888dc6a 100644 --- a/React/Profiler/RCTProfile.h +++ b/React/Profiler/RCTProfile.h @@ -190,7 +190,7 @@ RCT_EXTERN void RCTProfileRegisterCallbacks(RCTProfileCallbacks *); #define _RCTProfileBeginFlowEvent() @0 #define RCTProfileEndFlowEvent() -#define _RCTProfileEndFlowEvent() +#define _RCTProfileEndFlowEvent(...) #define RCTProfileIsProfiling(...) NO #define RCTProfileInit(...)