Skip to content
Permalink
Browse files

Add more profiling markers to RCTJSCExecutor

Summary: Add some additional perf markers, and remove the usage of RCTProfileBlock as it completely breaks debugging / stepping in those blocks.

Reviewed By: majak

Differential Revision: D3579900

fbshipit-source-id: 8846dfc39b2448daa3669d5e1e8efb9096f183c5
  • Loading branch information...
javache authored and Facebook Github Bot 4 committed Jul 19, 2016
1 parent 46417dd commit 63e0e6c7e4f190591a3255636258c6c4311ab593
Showing with 58 additions and 31 deletions.
  1. +56 −31 React/Executors/RCTJSCExecutor.mm
  2. +2 −0 React/Profiler/RCTProfile.h
@@ -311,12 +311,15 @@ - (instancetype)init

- (instancetype)initWithUseCustomJSCLibrary:(BOOL)useCustomJSCLibrary
{
RCT_PROFILE_BEGIN_EVENT(0, @"-[RCTJSCExecutor init]", nil);

if (self = [super init]) {
_useCustomJSCLibrary = useCustomJSCLibrary;
_valid = YES;
_javaScriptThread = newJavaScriptThread();
}

RCT_PROFILE_END_EVENT(0, @"", nil);
return self;
}

@@ -635,30 +638,32 @@ - (void)_executeJSCall:(NSString *)method
{
RCTAssert(onComplete != nil, @"onComplete block should not be nil");
__weak RCTJSCExecutor *weakSelf = self;
[self executeBlockOnJavaScriptQueue:RCTProfileBlock((^{
[self executeBlockOnJavaScriptQueue:^{
RCTJSCExecutor *strongSelf = weakSelf;
if (!strongSelf || !strongSelf.isValid) {
return;
}
NSError *error;

JSValueRef errorJSRef = NULL;
JSValueRef resultJSRef = NULL;
RCT_PROFILE_BEGIN_EVENT(0, @"executeJSCall", @{@"method": method, @"args": arguments});

RCTJSCWrapper *jscWrapper = strongSelf->_jscWrapper;
JSGlobalContextRef ctx = strongSelf->_context.context.JSGlobalContextRef;
JSGlobalContextRef contextJSRef = jscWrapper->JSContextGetGlobalContext(ctx);
JSContext *context = strongSelf->_context.context;
JSObjectRef globalObjectJSRef = jscWrapper->JSContextGetGlobalObject(ctx);
JSGlobalContextRef ctx = context.JSGlobalContextRef;
JSGlobalContextRef contextJSRef = jscWrapper->JSContextGetGlobalContext(ctx);

// get the BatchedBridge object
JSValueRef errorJSRef = NULL;
JSValueRef batchedBridgeRef = strongSelf->_batchedBridgeRef;
if (!batchedBridgeRef) {
JSStringRef moduleNameJSStringRef = jscWrapper->JSStringCreateWithUTF8CString("__fbBatchedBridge");
JSObjectRef globalObjectJSRef = jscWrapper->JSContextGetGlobalObject(ctx);
batchedBridgeRef = jscWrapper->JSObjectGetProperty(contextJSRef, globalObjectJSRef, moduleNameJSStringRef, &errorJSRef);
jscWrapper->JSStringRelease(moduleNameJSStringRef);
strongSelf->_batchedBridgeRef = batchedBridgeRef;
}

NSError *error;
JSValueRef resultJSRef = NULL;
if (batchedBridgeRef != NULL && errorJSRef == NULL && !jscWrapper->JSValueIsUndefined(contextJSRef, batchedBridgeRef)) {
// get method
JSStringRef methodNameJSStringRef = jscWrapper->JSStringCreateWithCFString((__bridge CFStringRef)method);
@@ -682,22 +687,24 @@ - (void)_executeJSCall:(NSString *)method
}
}

id objcValue;
if (errorJSRef || error) {
if (!error) {
error = RCTNSErrorFromJSError(jscWrapper, contextJSRef, errorJSRef);
}
onComplete(nil, error);
} else {
id objcValue = nil;
// We often return `null` from JS when there is nothing for native side. [JSValue toValue]
// returns [NSNull null] in this case, which we don't want.
if (!jscWrapper->JSValueIsNull(contextJSRef, resultJSRef)) {
JSValue *result = [jscWrapper->JSValue valueWithJSValueRef:resultJSRef inContext:context];
objcValue = unwrapResult ? [result toObject] : result;
}
onComplete(objcValue, nil);
}
}), 0, @"js_call", (@{@"method": method, @"args": arguments}))];

RCT_PROFILE_END_EVENT(0, @"js_call", nil);

onComplete(objcValue, error);
}];
}

- (void)executeApplicationScript:(NSData *)script
@@ -719,26 +726,32 @@ - (void)executeApplicationScript:(NSData *)script
}
}

[self executeBlockOnJavaScriptQueue:RCTProfileBlock((^{
RCTProfileBeginFlowEvent();
[self executeBlockOnJavaScriptQueue:^{
RCTProfileEndFlowEvent();
if (!self.isValid) {
return;
}

if (isRAMBundle) {
registerNativeRequire(self.context.context, self);
}

NSError *error = executeApplicationScript(script, sourceURL, self->_jscWrapper, self->_performanceLogger,
self->_context.context.JSGlobalContextRef);
if (onComplete) {
onComplete(error);
}
}), 0, @"js_call", (@{ @"url": sourceURL.absoluteString }))];
}];
}

static NSData *loadPossiblyBundledApplicationScript(NSData *script, NSURL *sourceURL,
RCTPerformanceLogger *performanceLogger,
BOOL &isRAMBundle, RandomAccessBundleData &randomAccessBundle,
NSError **error)
{
RCT_PROFILE_BEGIN_EVENT(0, @"executeApplicationScript / prepare bundle", nil);

// The RAM bundle has a magic number in the 4 first bytes `(0xFB0BD1E5)`.
uint32_t magicNumber = 0;
[script getBytes:&magicNumber length:sizeof(magicNumber)];
@@ -753,16 +766,17 @@ - (void)executeApplicationScript:(NSData *)script
[performanceLogger setValue:0 forTag:RCTPLRAMNativeRequires];
[performanceLogger setValue:0 forTag:RCTPLRAMNativeRequiresCount];
[performanceLogger setValue:0 forTag:RCTPLRAMNativeRequiresSize];

return script;
} else {
// JSStringCreateWithUTF8CString expects a null terminated C string.
// RAM Bundling already provides a null terminated one.
NSMutableData *nullTerminatedScript = [NSMutableData dataWithCapacity:script.length + 1];
[nullTerminatedScript appendData:script];
[nullTerminatedScript appendBytes:"" length:1];
return nullTerminatedScript;
script = nullTerminatedScript;
}

RCT_PROFILE_END_EVENT(0, @"", nil);
return script;
}

static void registerNativeRequire(JSContext *context, RCTJSCExecutor *executor)
@@ -774,6 +788,8 @@ static void registerNativeRequire(JSContext *context, RCTJSCExecutor *executor)
static NSError *executeApplicationScript(NSData *script, NSURL *sourceURL, RCTJSCWrapper *jscWrapper,
RCTPerformanceLogger *performanceLogger, JSGlobalContextRef ctx)
{
RCT_PROFILE_BEGIN_EVENT(0, @"executeApplicationScript / execute script",
@{ @"url": sourceURL.absoluteString, @"size": @(script.length) });
[performanceLogger markStartForTag:RCTPLScriptExecution];
JSValueRef jsError = NULL;
JSStringRef execJSString = jscWrapper->JSStringCreateWithUTF8CString((const char *)script.bytes);
@@ -782,7 +798,9 @@ static void registerNativeRequire(JSContext *context, RCTJSCExecutor *executor)
jscWrapper->JSStringRelease(bundleURL);
jscWrapper->JSStringRelease(execJSString);
[performanceLogger markStopForTag:RCTPLScriptExecution];
return result ? nil : RCTNSErrorFromJSError(jscWrapper, ctx, jsError);
NSError *error = result ? nil : RCTNSErrorFromJSError(jscWrapper, ctx, jsError);
RCT_PROFILE_END_EVENT(0, @"js_call", nil);
return error;
}

- (void)executeBlockOnJavaScriptQueue:(dispatch_block_t)block
@@ -812,37 +830,44 @@ - (void)injectJSONText:(NSString *)script
}

__weak RCTJSCExecutor *weakSelf = self;
[self executeBlockOnJavaScriptQueue:RCTProfileBlock((^{
RCTProfileBeginFlowEvent();
[self executeBlockOnJavaScriptQueue:^{
RCTProfileEndFlowEvent();

RCTJSCExecutor *strongSelf = weakSelf;
if (!strongSelf || !strongSelf.isValid) {
return;
}

RCT_PROFILE_BEGIN_EVENT(0, @"injectJSONText", @{@"objectName": objectName});
RCTJSCWrapper *jscWrapper = strongSelf->_jscWrapper;
JSStringRef execJSString = jscWrapper->JSStringCreateWithCFString((__bridge CFStringRef)script);
JSGlobalContextRef ctx = strongSelf->_context.context.JSGlobalContextRef;
JSValueRef valueToInject = jscWrapper->JSValueMakeFromJSONString(ctx, execJSString);
jscWrapper->JSStringRelease(execJSString);

NSError *error;
if (!valueToInject) {
NSString *errorDesc = [NSString stringWithFormat:@"Can't make JSON value from script '%@'", script];
RCTLogError(@"%@", errorDesc);

if (onComplete) {
NSError *error = [NSError errorWithDomain:RCTErrorDomain code:2 userInfo:@{NSLocalizedDescriptionKey: errorDesc}];
onComplete(error);
NSString *errorMessage = [NSString stringWithFormat:@"Can't make JSON value from script '%@'", script];
error = [NSError errorWithDomain:RCTErrorDomain code:2 userInfo:@{NSLocalizedDescriptionKey: errorMessage}];
RCTLogError(@"%@", errorMessage);
} else {
JSObjectRef globalObject = jscWrapper->JSContextGetGlobalObject(ctx);
JSStringRef JSName = jscWrapper->JSStringCreateWithCFString((__bridge CFStringRef)objectName);
JSValueRef jsError = NULL;
jscWrapper->JSObjectSetProperty(ctx, globalObject, JSName, valueToInject, kJSPropertyAttributeNone, &jsError);
jscWrapper->JSStringRelease(JSName);

if (jsError) {
error = RCTNSErrorFromJSError(jscWrapper, ctx, jsError);
}
return;
}
RCT_PROFILE_END_EVENT(0, @"js_call,json_call", nil);

JSObjectRef globalObject = jscWrapper->JSContextGetGlobalObject(ctx);
JSStringRef JSName = jscWrapper->JSStringCreateWithCFString((__bridge CFStringRef)objectName);
jscWrapper->JSObjectSetProperty(ctx, globalObject, JSName, valueToInject, kJSPropertyAttributeNone, NULL);
jscWrapper->JSStringRelease(JSName);
if (onComplete) {
onComplete(nil);
onComplete(error);
}
}), 0, @"js_call,json_call", (@{@"objectName": objectName}))];
}];
}

static bool readRandomAccessModule(const RandomAccessBundleData &bundleData, size_t offset, size_t size, char *data)
@@ -132,6 +132,8 @@ RCT_EXTERN void RCTProfileImmediateEvent(uint64_t tag,
* self and _cmd to name this event for simplicity sake.
*
* NOTE: The block can't expect any argument
*
* DEPRECATED: this approach breaks debugging and stepping through instrumented block functions
*/
#define RCTProfileBlock(block, tag, category, arguments) \
^{ \

0 comments on commit 63e0e6c

Please sign in to comment.
You can’t perform that action at this time.