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
This commit is contained in:
Pieter De Baets
2016-07-19 07:16:32 -07:00
committed by Facebook Github Bot 4
parent 46417dd26a
commit 63e0e6c7e4
2 changed files with 57 additions and 30 deletions

View File

@@ -311,12 +311,15 @@ static NSThread *newJavaScriptThread(void)
- (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 @@ static void installBasicSynchronousHooksOnContext(JSContext *context)
{
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 @@ static void installBasicSynchronousHooksOnContext(JSContext *context)
}
}
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,19 +726,23 @@ static void installBasicSynchronousHooksOnContext(JSContext *context)
}
}
[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,
@@ -739,6 +750,8 @@ static NSData *loadPossiblyBundledApplicationScript(NSData *script, NSURL *sourc
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 @@ static NSData *loadPossiblyBundledApplicationScript(NSData *script, NSURL *sourc
[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 NSError *executeApplicationScript(NSData *script, NSURL *sourceURL, RCTJS
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 @@ static NSError *executeApplicationScript(NSData *script, NSURL *sourceURL, RCTJS
}
__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);
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 (onComplete) {
NSError *error = [NSError errorWithDomain:RCTErrorDomain code:2 userInfo:@{NSLocalizedDescriptionKey: errorDesc}];
onComplete(error);
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)

View File

@@ -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) \
^{ \