Introduce JSC profiler API
Summary: Extract JSC profiler API which can now be used even if profiler is unavailable. public Reviewed By: tadeuzagallo Differential Revision: D2749217 fb-gh-sync-id: 1ffa6f37323ea0ddbda3fdacfdf8a9b360185b2e
This commit is contained in:
parent
f946d14fcc
commit
0fe50055c4
|
@ -21,25 +21,10 @@
|
|||
#import "RCTProfile.h"
|
||||
#import "RCTPerformanceLogger.h"
|
||||
#import "RCTUtils.h"
|
||||
|
||||
#ifndef RCT_JSC_PROFILER
|
||||
#if RCT_DEV
|
||||
#define RCT_JSC_PROFILER 1
|
||||
#else
|
||||
#define RCT_JSC_PROFILER 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if RCT_JSC_PROFILER
|
||||
#include <dlfcn.h>
|
||||
#import "RCTJSCProfiler.h"
|
||||
|
||||
static NSString *const RCTJSCProfilerEnabledDefaultsKey = @"RCTJSCProfilerEnabled";
|
||||
|
||||
#ifndef RCT_JSC_PROFILER_DYLIB
|
||||
#define RCT_JSC_PROFILER_DYLIB [[[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:@"RCTJSCProfiler.ios%zd", [[[UIDevice currentDevice] systemVersion] integerValue]] ofType:@"dylib" inDirectory:@"RCTJSCProfiler"] UTF8String]
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@interface RCTJavaScriptContext : NSObject <RCTInvalidating>
|
||||
|
||||
@property (nonatomic, strong, readonly) JSContext *context;
|
||||
|
@ -219,51 +204,19 @@ static JSValueRef RCTNativeTraceEndSection(JSContextRef context, __unused JSObje
|
|||
|
||||
static void RCTInstallJSCProfiler(RCTBridge *bridge, JSContextRef context)
|
||||
{
|
||||
#if RCT_JSC_PROFILER
|
||||
void *JSCProfiler = dlopen(RCT_JSC_PROFILER_DYLIB, RTLD_NOW);
|
||||
if (JSCProfiler != NULL) {
|
||||
void (*nativeProfilerStart)(JSContextRef, const char *) =
|
||||
(__typeof__(nativeProfilerStart))dlsym(JSCProfiler, "nativeProfilerStart");
|
||||
void (*nativeProfilerEnd)(JSContextRef, const char *, const char *) =
|
||||
(__typeof__(nativeProfilerEnd))dlsym(JSCProfiler, "nativeProfilerEnd");
|
||||
|
||||
if (nativeProfilerStart != NULL && nativeProfilerEnd != NULL) {
|
||||
void (*nativeProfilerEnableBytecode)(void) =
|
||||
(__typeof__(nativeProfilerEnableBytecode))dlsym(JSCProfiler, "nativeProfilerEnableBytecode");
|
||||
|
||||
if (nativeProfilerEnableBytecode != NULL) {
|
||||
nativeProfilerEnableBytecode();
|
||||
}
|
||||
|
||||
static BOOL isProfiling = NO;
|
||||
|
||||
if (isProfiling) {
|
||||
nativeProfilerStart(context, "profile");
|
||||
}
|
||||
|
||||
[bridge.devMenu addItem:[RCTDevMenuItem toggleItemWithKey:RCTJSCProfilerEnabledDefaultsKey title:@"Start Profiling" selectedTitle:@"Stop Profiling" handler:^(BOOL shouldStart) {
|
||||
|
||||
if (shouldStart == isProfiling) {
|
||||
return;
|
||||
}
|
||||
|
||||
isProfiling = shouldStart;
|
||||
|
||||
if (RCTJSCProfilerIsSupported()) {
|
||||
[bridge.devMenu addItem:[RCTDevMenuItem toggleItemWithKey:RCTJSCProfilerEnabledDefaultsKey title:@"Start Profiling" selectedTitle:@"Stop Profiling" handler:^(BOOL shouldStart) {
|
||||
if (shouldStart != RCTJSCProfilerIsProfiling(context)) {
|
||||
if (shouldStart) {
|
||||
nativeProfilerStart(context, "profile");
|
||||
RCTJSCProfilerStart(context);
|
||||
} else {
|
||||
NSString *outputFile = [NSTemporaryDirectory() stringByAppendingPathComponent:@"cpu_profile.json"];
|
||||
nativeProfilerEnd(context, "profile", outputFile.UTF8String);
|
||||
NSData *profileData = [NSData dataWithContentsOfFile:outputFile
|
||||
options:NSDataReadingMappedIfSafe
|
||||
error:NULL];
|
||||
|
||||
NSString *outputFile = RCTJSCProfilerStop(context);
|
||||
NSData *profileData = [NSData dataWithContentsOfFile:outputFile options:NSDataReadingMappedIfSafe error:NULL];
|
||||
RCTProfileSendResult(bridge, @"cpu-profile", profileData);
|
||||
}
|
||||
}]];
|
||||
}
|
||||
}
|
||||
}]];
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
/**
|
||||
* 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 <JavaScriptCore/JavaScriptCore.h>
|
||||
|
||||
#import "RCTDefines.h"
|
||||
|
||||
/** The API is not thread-safe. */
|
||||
|
||||
/** The context is not retained. */
|
||||
RCT_EXTERN void RCTJSCProfilerStart(JSContextRef ctx);
|
||||
/** Returns a file path containing the profiler data. */
|
||||
RCT_EXTERN NSString *RCTJSCProfilerStop(JSContextRef ctx);
|
||||
|
||||
RCT_EXTERN BOOL RCTJSCProfilerIsProfiling(JSContextRef ctx);
|
||||
RCT_EXTERN BOOL RCTJSCProfilerIsSupported(void);
|
|
@ -0,0 +1,137 @@
|
|||
/**
|
||||
* 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 "RCTJSCProfiler.h"
|
||||
#import "RCTLog.h"
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#ifndef RCT_JSC_PROFILER
|
||||
#if RCT_DEV
|
||||
#define RCT_JSC_PROFILER 1
|
||||
#else
|
||||
#define RCT_JSC_PROFILER 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if RCT_JSC_PROFILER
|
||||
|
||||
#include <dlfcn.h>
|
||||
|
||||
#ifndef RCT_JSC_PROFILER_DYLIB
|
||||
#define RCT_JSC_PROFILER_DYLIB [[[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:@"RCTJSCProfiler.ios%zd", [[[UIDevice currentDevice] systemVersion] integerValue]] ofType:@"dylib" inDirectory:@"RCTJSCProfiler"] UTF8String]
|
||||
#endif
|
||||
|
||||
static const char *const JSCProfileName = "profile";
|
||||
|
||||
typedef void (*JSCProfilerStartFunctionType)(JSContextRef, const char *);
|
||||
typedef void (*JSCProfilerEndFunctionType)(JSContextRef, const char *, const char *);
|
||||
typedef void (*JSCProfilerEnableFunctionType)(void);
|
||||
|
||||
static NSMutableDictionary<NSValue *, NSNumber *> *RCTJSCProfilerStateMap;
|
||||
|
||||
static JSCProfilerStartFunctionType RCTNativeProfilerStart = NULL;
|
||||
static JSCProfilerEndFunctionType RCTNativeProfilerEnd = NULL;
|
||||
|
||||
NS_INLINE NSValue *RCTJSContextRefKey(JSContextRef ref) {
|
||||
return [NSValue valueWithPointer:ref];
|
||||
}
|
||||
|
||||
static void RCTJSCProfilerStateInit()
|
||||
{
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
RCTJSCProfilerStateMap = [NSMutableDictionary new];
|
||||
|
||||
void *JSCProfiler = dlopen(RCT_JSC_PROFILER_DYLIB, RTLD_NOW);
|
||||
|
||||
RCTNativeProfilerStart = (JSCProfilerStartFunctionType)dlsym(JSCProfiler, "nativeProfilerStart");
|
||||
RCTNativeProfilerEnd = (JSCProfilerEndFunctionType)dlsym(JSCProfiler, "nativeProfilerEnd");
|
||||
JSCProfilerEnableFunctionType enableBytecode = (__typeof__(enableBytecode))dlsym(JSCProfiler, "nativeProfilerEnableBytecode");
|
||||
|
||||
if (RCTNativeProfilerStart && RCTNativeProfilerEnd && enableBytecode) {
|
||||
enableBytecode();
|
||||
RCTLogInfo(@"JSC profiler is available.");
|
||||
} else {
|
||||
RCTNativeProfilerStart = NULL;
|
||||
RCTNativeProfilerEnd = NULL;
|
||||
RCTLogInfo(@"JSC profiler is not supported.");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void RCTJSCProfilerStart(JSContextRef ctx)
|
||||
{
|
||||
#if RCT_JSC_PROFILER
|
||||
if (ctx != NULL) {
|
||||
if (RCTJSCProfilerIsSupported()) {
|
||||
NSValue *key = RCTJSContextRefKey(ctx);
|
||||
BOOL isProfiling = [RCTJSCProfilerStateMap[key] boolValue];
|
||||
if (!isProfiling) {
|
||||
RCTLogInfo(@"Starting JSC profiler for context: %p", ctx);
|
||||
RCTJSCProfilerStateMap[key] = @YES;
|
||||
RCTNativeProfilerStart(ctx, JSCProfileName);
|
||||
} else {
|
||||
RCTLogWarn(@"Trying to start JSC profiler on a context which is already profiled.");
|
||||
}
|
||||
} else {
|
||||
RCTLogWarn(@"Cannot start JSC profiler as it's not supported.");
|
||||
}
|
||||
} else {
|
||||
RCTLogWarn(@"Trying to start JSC profiler for NULL context.");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
NSString *RCTJSCProfilerStop(JSContextRef ctx)
|
||||
{
|
||||
NSString *outputFile = nil;
|
||||
#if RCT_JSC_PROFILER
|
||||
if (ctx != NULL) {
|
||||
RCTJSCProfilerStateInit();
|
||||
NSValue *key = RCTJSContextRefKey(ctx);
|
||||
BOOL isProfiling = [RCTJSCProfilerStateMap[key] boolValue];
|
||||
if (isProfiling) {
|
||||
NSString *filename = [NSString stringWithFormat:@"cpu_profile_%ld.json", (long)CFAbsoluteTimeGetCurrent()];
|
||||
outputFile = [NSTemporaryDirectory() stringByAppendingPathComponent:filename];
|
||||
RCTNativeProfilerEnd(ctx, JSCProfileName, outputFile.UTF8String);
|
||||
RCTLogInfo(@"Stopped JSC profiler for context: %p", ctx);
|
||||
} else {
|
||||
RCTLogWarn(@"Trying to stop JSC profiler on a context which is not being profiled.");
|
||||
}
|
||||
[RCTJSCProfilerStateMap removeObjectForKey:key];
|
||||
} else {
|
||||
RCTLogWarn(@"Trying to stop JSC profiler for NULL context.");
|
||||
}
|
||||
#endif
|
||||
return outputFile;
|
||||
}
|
||||
|
||||
BOOL RCTJSCProfilerIsProfiling(JSContextRef ctx)
|
||||
{
|
||||
BOOL isProfiling = NO;
|
||||
#if RCT_JSC_PROFILER
|
||||
if (ctx != NULL) {
|
||||
RCTJSCProfilerStateInit();
|
||||
isProfiling = [RCTJSCProfilerStateMap[RCTJSContextRefKey(ctx)] boolValue];
|
||||
}
|
||||
#endif
|
||||
return isProfiling;
|
||||
}
|
||||
|
||||
BOOL RCTJSCProfilerIsSupported(void)
|
||||
{
|
||||
BOOL isSupported = NO;
|
||||
#if RCT_JSC_PROFILER
|
||||
RCTJSCProfilerStateInit();
|
||||
isSupported = (RCTNativeProfilerStart != NULL);
|
||||
#endif
|
||||
return isSupported;
|
||||
}
|
Loading…
Reference in New Issue