Add support for JSC's sampling profiler on iOS for RN
Reviewed By: javache Differential Revision: D4107919 fbshipit-source-id: ecfe2cacdb78b857e461f7006b29e4d1fe1a1862
This commit is contained in:
parent
c2a55baf80
commit
9fc6204efc
|
@ -31,6 +31,7 @@
|
||||||
#import "RCTSourceCode.h"
|
#import "RCTSourceCode.h"
|
||||||
#import "RCTJSCWrapper.h"
|
#import "RCTJSCWrapper.h"
|
||||||
#import "RCTJSCErrorHandling.h"
|
#import "RCTJSCErrorHandling.h"
|
||||||
|
#import "JSCSamplingProfiler.h"
|
||||||
|
|
||||||
NSString *const RCTJSCThreadName = @"com.facebook.react.JavaScript";
|
NSString *const RCTJSCThreadName = @"com.facebook.react.JavaScript";
|
||||||
NSString *const RCTJavaScriptContextCreatedNotification = @"RCTJavaScriptContextCreatedNotification";
|
NSString *const RCTJavaScriptContextCreatedNotification = @"RCTJavaScriptContextCreatedNotification";
|
||||||
|
@ -417,6 +418,36 @@ static NSThread *newJavaScriptThread(void)
|
||||||
[weakBridge.flowIDMapLock unlock];
|
[weakBridge.flowIDMapLock unlock];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Add toggles for JSC's sampling profiler, if the profiler is enabled
|
||||||
|
if (self->_jscWrapper->JSSamplingProfilerEnabled()) {
|
||||||
|
// Mark this thread as the main JS thread before starting profiling.
|
||||||
|
self->_jscWrapper->JSStartSamplingProfilingOnMainJSCThread(context.JSGlobalContextRef);
|
||||||
|
|
||||||
|
// Allow to toggle the sampling profiler through RN's dev menu
|
||||||
|
__weak JSContext *weakContext = self->_context.context;
|
||||||
|
[self->_bridge.devMenu addItem:[RCTDevMenuItem buttonItemWithTitle:@"Start / Stop JS Sampling Profiler" handler:^{
|
||||||
|
// JSPokeSamplingProfiler() toggles the profiling process
|
||||||
|
JSValueRef jsResult = self->_jscWrapper->JSPokeSamplingProfiler(weakContext.JSGlobalContextRef);
|
||||||
|
|
||||||
|
if (!self->_jscWrapper->JSValueIsNull(weakContext.JSGlobalContextRef, jsResult)) {
|
||||||
|
NSString *results = [[self->_jscWrapper->JSValue valueWithJSValueRef:jsResult inContext:weakContext] toObject];
|
||||||
|
JSCSamplingProfiler *profilerModule = [self->_bridge moduleForClass:[JSCSamplingProfiler class]];
|
||||||
|
[profilerModule operationCompletedWithResults:results];
|
||||||
|
}
|
||||||
|
}]];
|
||||||
|
|
||||||
|
// Allow for the profiler to be poked from JS code as well
|
||||||
|
// (see SamplingProfiler.js for an example of how it could be used with the JSCSamplingProfiler module).
|
||||||
|
context[@"pokeSamplingProfiler"] = ^(NSDictionary *){
|
||||||
|
RCTJSCExecutor *strongSelf = weakSelf;
|
||||||
|
if (!strongSelf.isValid) {
|
||||||
|
return @{};
|
||||||
|
}
|
||||||
|
JSValueRef result = strongSelf->_jscWrapper->JSPokeSamplingProfiler(weakContext.JSGlobalContextRef);
|
||||||
|
return (NSDictionary *)[[strongSelf->_jscWrapper->JSValue valueWithJSValueRef:result inContext:weakContext] toObject];
|
||||||
|
};
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if RCT_DEV
|
#if RCT_DEV
|
||||||
|
|
|
@ -28,13 +28,15 @@ typedef JSStringRef (*JSPropertyNameArrayGetNameAtIndexFuncType)(JSPropertyNameA
|
||||||
typedef void (*JSPropertyNameArrayReleaseFuncType)(JSPropertyNameArrayRef);
|
typedef void (*JSPropertyNameArrayReleaseFuncType)(JSPropertyNameArrayRef);
|
||||||
typedef JSValueRef (*JSValueMakeFromJSONStringFuncType)(JSContextRef, JSStringRef);
|
typedef JSValueRef (*JSValueMakeFromJSONStringFuncType)(JSContextRef, JSStringRef);
|
||||||
typedef JSValueRef (*JSObjectCallAsFunctionFuncType)(JSContextRef, JSObjectRef, JSObjectRef, size_t, const JSValueRef *, JSValueRef *);
|
typedef JSValueRef (*JSObjectCallAsFunctionFuncType)(JSContextRef, JSObjectRef, JSObjectRef, size_t, const JSValueRef *, JSValueRef *);
|
||||||
typedef JSValueRef (*JSValueMakeNullFuncType)(JSContextRef);
|
typedef JSValueRef (*JSValueRefWithJSContextRefFuncType)(JSContextRef);
|
||||||
typedef JSStringRef (*JSValueCreateJSONStringFuncType)(JSContextRef, JSValueRef, unsigned, JSValueRef *);
|
typedef JSStringRef (*JSValueCreateJSONStringFuncType)(JSContextRef, JSValueRef, unsigned, JSValueRef *);
|
||||||
typedef bool (*JSValueIsUndefinedFuncType)(JSContextRef, JSValueRef);
|
typedef bool (*JSValueIsUndefinedFuncType)(JSContextRef, JSValueRef);
|
||||||
typedef bool (*JSValueIsNullFuncType)(JSContextRef, JSValueRef);
|
typedef bool (*JSValueIsNullFuncType)(JSContextRef, JSValueRef);
|
||||||
typedef JSObjectRef (*JSValueToObjectFuncType)(JSContextRef, JSValueRef, JSValueRef *);
|
typedef JSObjectRef (*JSValueToObjectFuncType)(JSContextRef, JSValueRef, JSValueRef *);
|
||||||
typedef JSValueRef (*JSEvaluateScriptFuncType)(JSContextRef, JSStringRef, JSObjectRef, JSStringRef, int, JSValueRef *);
|
typedef JSValueRef (*JSEvaluateScriptFuncType)(JSContextRef, JSStringRef, JSObjectRef, JSStringRef, int, JSValueRef *);
|
||||||
typedef JSValueRef (*JSEvaluateBytecodeBundleFuncType)(JSContextRef, JSObjectRef, int, JSStringRef, JSValueRef *);
|
typedef JSValueRef (*JSEvaluateBytecodeBundleFuncType)(JSContextRef, JSObjectRef, int, JSStringRef, JSValueRef *);
|
||||||
|
typedef bool (*JSSamplingProfilerEnabledFuncType)();
|
||||||
|
typedef void (*JSStartSamplingProfilingOnMainJSCThreadFuncType)(JSGlobalContextRef);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* JSNoBytecodeFileFormatVersion
|
* JSNoBytecodeFileFormatVersion
|
||||||
|
@ -60,7 +62,7 @@ typedef struct RCTJSCWrapper {
|
||||||
JSPropertyNameArrayReleaseFuncType JSPropertyNameArrayRelease;
|
JSPropertyNameArrayReleaseFuncType JSPropertyNameArrayRelease;
|
||||||
JSValueMakeFromJSONStringFuncType JSValueMakeFromJSONString;
|
JSValueMakeFromJSONStringFuncType JSValueMakeFromJSONString;
|
||||||
JSObjectCallAsFunctionFuncType JSObjectCallAsFunction;
|
JSObjectCallAsFunctionFuncType JSObjectCallAsFunction;
|
||||||
JSValueMakeNullFuncType JSValueMakeNull;
|
JSValueRefWithJSContextRefFuncType JSValueMakeNull;
|
||||||
JSValueCreateJSONStringFuncType JSValueCreateJSONString;
|
JSValueCreateJSONStringFuncType JSValueCreateJSONString;
|
||||||
JSValueIsUndefinedFuncType JSValueIsUndefined;
|
JSValueIsUndefinedFuncType JSValueIsUndefined;
|
||||||
JSValueIsNullFuncType JSValueIsNull;
|
JSValueIsNullFuncType JSValueIsNull;
|
||||||
|
@ -68,6 +70,9 @@ typedef struct RCTJSCWrapper {
|
||||||
JSEvaluateScriptFuncType JSEvaluateScript;
|
JSEvaluateScriptFuncType JSEvaluateScript;
|
||||||
JSEvaluateBytecodeBundleFuncType JSEvaluateBytecodeBundle;
|
JSEvaluateBytecodeBundleFuncType JSEvaluateBytecodeBundle;
|
||||||
voidWithNoParamsFuncType configureJSCForIOS;
|
voidWithNoParamsFuncType configureJSCForIOS;
|
||||||
|
JSSamplingProfilerEnabledFuncType JSSamplingProfilerEnabled;
|
||||||
|
JSValueRefWithJSContextRefFuncType JSPokeSamplingProfiler;
|
||||||
|
JSStartSamplingProfilingOnMainJSCThreadFuncType JSStartSamplingProfilingOnMainJSCThread;
|
||||||
const int32_t JSBytecodeFileFormatVersion;
|
const int32_t JSBytecodeFileFormatVersion;
|
||||||
Class JSContext;
|
Class JSContext;
|
||||||
Class JSValue;
|
Class JSValue;
|
||||||
|
|
|
@ -24,12 +24,15 @@ assert(false);\
|
||||||
}
|
}
|
||||||
|
|
||||||
UNIMPLEMENTED_SYSTEM_JSC_FUNCTION(JSEvaluateBytecodeBundle)
|
UNIMPLEMENTED_SYSTEM_JSC_FUNCTION(JSEvaluateBytecodeBundle)
|
||||||
|
UNIMPLEMENTED_SYSTEM_JSC_FUNCTION(JSPokeSamplingProfiler)
|
||||||
|
UNIMPLEMENTED_SYSTEM_JSC_FUNCTION(JSStartSamplingProfilingOnMainJSCThread)
|
||||||
|
|
||||||
#undef UNIMPLEMENTED_SYSTEM_JSC_FUNCTION
|
#undef UNIMPLEMENTED_SYSTEM_JSC_FUNCTION
|
||||||
|
|
||||||
// A no-op function, to replace void functions that do no exist in the system JSC
|
// A no-op function, to replace void functions that do no exist in the system JSC
|
||||||
// with a function that does nothing.
|
// with a function that does nothing.
|
||||||
static void noOpSystemJSCFunc(void *args...){ }
|
static void noOpSystemJSCFunc(void *args...){ }
|
||||||
|
static bool alwaysFalseSystemJSCFunc(void *args...){ return false; }
|
||||||
|
|
||||||
void __attribute__((visibility("hidden"),weak)) RCTCustomJSCInit(__unused void *handle) {
|
void __attribute__((visibility("hidden"),weak)) RCTCustomJSCInit(__unused void *handle) {
|
||||||
return;
|
return;
|
||||||
|
@ -85,6 +88,9 @@ static RCTJSCWrapper *RCTSetUpSystemLibraryPointers()
|
||||||
.JSBytecodeFileFormatVersion = JSNoBytecodeFileFormatVersion,
|
.JSBytecodeFileFormatVersion = JSNoBytecodeFileFormatVersion,
|
||||||
.JSEvaluateBytecodeBundle = (JSEvaluateBytecodeBundleFuncType)UnimplementedJSEvaluateBytecodeBundle,
|
.JSEvaluateBytecodeBundle = (JSEvaluateBytecodeBundleFuncType)UnimplementedJSEvaluateBytecodeBundle,
|
||||||
.configureJSCForIOS = (voidWithNoParamsFuncType)noOpSystemJSCFunc,
|
.configureJSCForIOS = (voidWithNoParamsFuncType)noOpSystemJSCFunc,
|
||||||
|
.JSSamplingProfilerEnabled = (JSSamplingProfilerEnabledFuncType)alwaysFalseSystemJSCFunc,
|
||||||
|
.JSPokeSamplingProfiler = (JSValueRefWithJSContextRefFuncType)UnimplementedJSPokeSamplingProfiler,
|
||||||
|
.JSStartSamplingProfilingOnMainJSCThread = (JSStartSamplingProfilingOnMainJSCThreadFuncType)UnimplementedJSStartSamplingProfilingOnMainJSCThread,
|
||||||
.JSContext = [JSContext class],
|
.JSContext = [JSContext class],
|
||||||
.JSValue = [JSValue class],
|
.JSValue = [JSValue class],
|
||||||
};
|
};
|
||||||
|
@ -114,7 +120,7 @@ static RCTJSCWrapper *RCTSetUpCustomLibraryPointers()
|
||||||
.JSPropertyNameArrayRelease = (JSPropertyNameArrayReleaseFuncType)dlsym(libraryHandle, "JSPropertyNameArrayRelease"),
|
.JSPropertyNameArrayRelease = (JSPropertyNameArrayReleaseFuncType)dlsym(libraryHandle, "JSPropertyNameArrayRelease"),
|
||||||
.JSValueMakeFromJSONString = (JSValueMakeFromJSONStringFuncType)dlsym(libraryHandle, "JSValueMakeFromJSONString"),
|
.JSValueMakeFromJSONString = (JSValueMakeFromJSONStringFuncType)dlsym(libraryHandle, "JSValueMakeFromJSONString"),
|
||||||
.JSObjectCallAsFunction = (JSObjectCallAsFunctionFuncType)dlsym(libraryHandle, "JSObjectCallAsFunction"),
|
.JSObjectCallAsFunction = (JSObjectCallAsFunctionFuncType)dlsym(libraryHandle, "JSObjectCallAsFunction"),
|
||||||
.JSValueMakeNull = (JSValueMakeNullFuncType)dlsym(libraryHandle, "JSValueMakeNull"),
|
.JSValueMakeNull = (JSValueRefWithJSContextRefFuncType)dlsym(libraryHandle, "JSValueMakeNull"),
|
||||||
.JSValueCreateJSONString = (JSValueCreateJSONStringFuncType)dlsym(libraryHandle, "JSValueCreateJSONString"),
|
.JSValueCreateJSONString = (JSValueCreateJSONStringFuncType)dlsym(libraryHandle, "JSValueCreateJSONString"),
|
||||||
.JSValueIsUndefined = (JSValueIsUndefinedFuncType)dlsym(libraryHandle, "JSValueIsUndefined"),
|
.JSValueIsUndefined = (JSValueIsUndefinedFuncType)dlsym(libraryHandle, "JSValueIsUndefined"),
|
||||||
.JSValueIsNull = (JSValueIsNullFuncType)dlsym(libraryHandle, "JSValueIsNull"),
|
.JSValueIsNull = (JSValueIsNullFuncType)dlsym(libraryHandle, "JSValueIsNull"),
|
||||||
|
@ -122,6 +128,9 @@ static RCTJSCWrapper *RCTSetUpCustomLibraryPointers()
|
||||||
.JSEvaluateScript = (JSEvaluateScriptFuncType)dlsym(libraryHandle, "JSEvaluateScript"),
|
.JSEvaluateScript = (JSEvaluateScriptFuncType)dlsym(libraryHandle, "JSEvaluateScript"),
|
||||||
.JSEvaluateBytecodeBundle = (JSEvaluateBytecodeBundleFuncType)dlsym(libraryHandle, "JSEvaluateBytecodeBundle"),
|
.JSEvaluateBytecodeBundle = (JSEvaluateBytecodeBundleFuncType)dlsym(libraryHandle, "JSEvaluateBytecodeBundle"),
|
||||||
.configureJSCForIOS = (voidWithNoParamsFuncType)dlsym(libraryHandle, "configureJSCForIOS"),
|
.configureJSCForIOS = (voidWithNoParamsFuncType)dlsym(libraryHandle, "configureJSCForIOS"),
|
||||||
|
.JSSamplingProfilerEnabled = (JSSamplingProfilerEnabledFuncType)dlsym(libraryHandle, "JSSamplingProfilerEnabled"),
|
||||||
|
.JSPokeSamplingProfiler = (JSValueRefWithJSContextRefFuncType)dlsym(libraryHandle, "JSPokeSamplingProfiler"),
|
||||||
|
.JSStartSamplingProfilingOnMainJSCThread = (JSStartSamplingProfilingOnMainJSCThreadFuncType)dlsym(libraryHandle, "JSStartSamplingProfilingOnMainJSCThread"),
|
||||||
.JSBytecodeFileFormatVersion = *(const int32_t *)dlsym(libraryHandle, "JSBytecodeFileFormatVersion"),
|
.JSBytecodeFileFormatVersion = *(const int32_t *)dlsym(libraryHandle, "JSBytecodeFileFormatVersion"),
|
||||||
.JSContext = (__bridge Class)dlsym(libraryHandle, "OBJC_CLASS_$_JSContext"),
|
.JSContext = (__bridge Class)dlsym(libraryHandle, "OBJC_CLASS_$_JSContext"),
|
||||||
.JSValue = (__bridge Class)dlsym(libraryHandle, "OBJC_CLASS_$_JSValue"),
|
.JSValue = (__bridge Class)dlsym(libraryHandle, "OBJC_CLASS_$_JSValue"),
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2015-present, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
#import "RCTBridgeModule.h"
|
||||||
|
|
||||||
|
@interface JSCSamplingProfiler : NSObject <RCTBridgeModule>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Receives a JSON string containing the result of a JSC CPU Profiling run,
|
||||||
|
* and sends them to the packager to be symbolicated and saved to disk.
|
||||||
|
* It is safe to call this method from any thread.
|
||||||
|
*/
|
||||||
|
- (void)operationCompletedWithResults:(NSString *)results;
|
||||||
|
|
||||||
|
@end
|
|
@ -0,0 +1,58 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2015-present, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import "JSCSamplingProfiler.h"
|
||||||
|
|
||||||
|
#import "RCTBridge.h"
|
||||||
|
#import "RCTLog.h"
|
||||||
|
|
||||||
|
@implementation JSCSamplingProfiler
|
||||||
|
|
||||||
|
@synthesize methodQueue = _methodQueue;
|
||||||
|
@synthesize bridge = _bridge;
|
||||||
|
|
||||||
|
RCT_EXPORT_MODULE(JSCSamplingProfiler);
|
||||||
|
|
||||||
|
#ifdef RCT_PROFILE
|
||||||
|
RCT_EXPORT_METHOD(operationComplete:(int)token result:(id)profileData error:(id)error)
|
||||||
|
{
|
||||||
|
if (error) {
|
||||||
|
RCTLogError(@"JSC Sampling profiler ended with error: %@", error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a POST request with all of the datas
|
||||||
|
NSURL *url = [NSURL URLWithString:@"/jsc-profile" relativeToURL:self.bridge.bundleURL];
|
||||||
|
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url
|
||||||
|
cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData
|
||||||
|
timeoutInterval:60];
|
||||||
|
[request setHTTPMethod:@"POST"];
|
||||||
|
[request setHTTPBody:[profileData dataUsingEncoding:NSUTF8StringEncoding]];
|
||||||
|
|
||||||
|
// Send the request
|
||||||
|
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:nil];
|
||||||
|
|
||||||
|
if (connection) {
|
||||||
|
RCTLogInfo(@"JSC CPU Profile data sent successfully.");
|
||||||
|
} else {
|
||||||
|
RCTLogWarn(@"JSC CPU Profile data failed to send.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)operationCompletedWithResults:(NSString *)results
|
||||||
|
{
|
||||||
|
// Send the results to the packager, using the module's queue.
|
||||||
|
dispatch_async(self.methodQueue, ^{
|
||||||
|
[self operationComplete:0 result:results error:nil];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
@end
|
Loading…
Reference in New Issue