[ReactNative] Add JavaScriptCore legacy profiler

This commit is contained in:
Tadeu Zagallo 2015-07-20 09:14:53 -07:00
Родитель 5db42643cf
Коммит 72f7a1be6f
5 изменённых файлов: 337 добавлений и 0 удалений

Просмотреть файл

@ -0,0 +1,25 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#import "JSContextRef.h"
extern "C" {
JSValueRef nativeProfilerStart(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef *exception);
JSValueRef nativeProfilerEnd(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef *exception);
}

Просмотреть файл

@ -0,0 +1,161 @@
//#include "config.h"
#include "JSCLegacyProfiler.h"
#include "APICast.h"
#include "LegacyProfiler.h"
#include "OpaqueJSString.h"
#include "JSProfilerPrivate.h"
#include "JSStringRef.h"
#include <yajl/yajl_gen.h>
#define GEN_AND_CHECK(expr) \
do { \
yajl_gen_status GEN_AND_CHECK_status = (expr); \
if (GEN_AND_CHECK_status != yajl_gen_status_ok) { \
return GEN_AND_CHECK_status; \
} \
} while (false)
static inline yajl_gen_status yajl_gen_cstring(yajl_gen gen, const char *str) {
return yajl_gen_string(gen, (const unsigned char*)str, strlen(str));
}
static yajl_gen_status append_children_array_json(yajl_gen gen, const JSC::ProfileNode *node);
static yajl_gen_status append_node_json(yajl_gen gen, const JSC::ProfileNode *node);
static yajl_gen_status append_root_json(yajl_gen gen, const JSC::Profile *profile) {
GEN_AND_CHECK(yajl_gen_map_open(gen));
GEN_AND_CHECK(yajl_gen_cstring(gen, "rootNodes"));
GEN_AND_CHECK(append_children_array_json(gen, profile->head()));
GEN_AND_CHECK(yajl_gen_map_close(gen));
return yajl_gen_status_ok;
}
static yajl_gen_status append_children_array_json(yajl_gen gen, const JSC::ProfileNode *node) {
GEN_AND_CHECK(yajl_gen_array_open(gen));
for (RefPtr<JSC::ProfileNode> child : node->children()) {
GEN_AND_CHECK(append_node_json(gen, child.get()));
}
GEN_AND_CHECK(yajl_gen_array_close(gen));
return yajl_gen_status_ok;
}
static yajl_gen_status append_node_json(yajl_gen gen, const JSC::ProfileNode *node) {
GEN_AND_CHECK(yajl_gen_map_open(gen));
GEN_AND_CHECK(yajl_gen_cstring(gen, "id"));
GEN_AND_CHECK(yajl_gen_integer(gen, node->id()));
if (!node->functionName().isEmpty()) {
GEN_AND_CHECK(yajl_gen_cstring(gen, "functionName"));
GEN_AND_CHECK(yajl_gen_cstring(gen, node->functionName().utf8().data()));
}
if (!node->url().isEmpty()) {
GEN_AND_CHECK(yajl_gen_cstring(gen, "url"));
GEN_AND_CHECK(yajl_gen_cstring(gen, node->url().utf8().data()));
GEN_AND_CHECK(yajl_gen_cstring(gen, "lineNumber"));
GEN_AND_CHECK(yajl_gen_integer(gen, node->lineNumber()));
GEN_AND_CHECK(yajl_gen_cstring(gen, "columnNumber"));
GEN_AND_CHECK(yajl_gen_integer(gen, node->columnNumber()));
}
GEN_AND_CHECK(yajl_gen_cstring(gen, "calls"));
GEN_AND_CHECK(yajl_gen_array_open(gen));
for (const JSC::ProfileNode::Call &call : node->calls()) {
GEN_AND_CHECK(yajl_gen_map_open(gen));
GEN_AND_CHECK(yajl_gen_cstring(gen, "startTime"));
GEN_AND_CHECK(yajl_gen_double(gen, call.startTime()));
GEN_AND_CHECK(yajl_gen_cstring(gen, "totalTime"));
GEN_AND_CHECK(yajl_gen_double(gen, call.totalTime()));
GEN_AND_CHECK(yajl_gen_map_close(gen));
}
GEN_AND_CHECK(yajl_gen_array_close(gen));
if (!node->children().isEmpty()) {
GEN_AND_CHECK(yajl_gen_cstring(gen, "children"));
GEN_AND_CHECK(append_children_array_json(gen, node));
}
GEN_AND_CHECK(yajl_gen_map_close(gen));
return yajl_gen_status_ok;
}
static char *render_error_code(yajl_gen_status status) {
char err[1024];
snprintf(err, sizeof(err), "{\"error\": %d}", (int)status);
return strdup(err);
}
static char *convert_to_json(const JSC::Profile *profile) {
yajl_gen_status status;
yajl_gen gen = yajl_gen_alloc(NULL);
status = append_root_json(gen, profile);
if (status != yajl_gen_status_ok) {
yajl_gen_free(gen);
return render_error_code(status);
}
const unsigned char *buf;
size_t buf_size;
status = yajl_gen_get_buf(gen, &buf, &buf_size);
if (status != yajl_gen_status_ok) {
yajl_gen_free(gen);
return render_error_code(status);
}
char *json_copy = strdup((const char*)buf);
yajl_gen_free(gen);
return json_copy;
}
static char *JSEndProfilingAndRender(JSContextRef ctx, JSStringRef title)
{
JSC::ExecState *exec = toJS(ctx);
JSC::LegacyProfiler *profiler = JSC::LegacyProfiler::profiler();
RefPtr<JSC::Profile> rawProfile = profiler->stopProfiling(exec, title->string());
return convert_to_json(rawProfile.get());
}
JSValueRef nativeProfilerStart(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef *exception) {
if (argumentCount < 1) {
// Could raise an exception here.
return JSValueMakeUndefined(ctx);
}
JSStringRef title = JSValueToStringCopy(ctx, arguments[0], NULL);
JSStartProfiling(ctx, title);
JSStringRelease(title);
return JSValueMakeUndefined(ctx);
}
JSValueRef nativeProfilerEnd(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef *exception) {
if (argumentCount < 1) {
// Could raise an exception here.
return JSValueMakeUndefined(ctx);
}
JSStringRef title = JSValueToStringCopy(ctx, arguments[0], NULL);
char *rendered = JSEndProfilingAndRender(ctx, title);
JSStringRelease(title);
JSStringRef profile = JSStringCreateWithUTF8CString(rendered);
free(rendered);
return JSValueMakeString(ctx, profile);
}

108
JSCLegacyProfiler/Makefile Normal file
Просмотреть файл

@ -0,0 +1,108 @@
HEADER_PATHS := `find ./tmp/JavaScriptCore -name '*.h' | xargs -I{} dirname {} | uniq | xargs -I{} echo "-I {}"`
CERT ?= "iPhone Developer"
ios8: prepare build generate
prepare: clean create download
build: x86_64 arm64 armv7
generate: lipo codesign
clean:
@rm -rf tmp/ /tmp/RCTJSCProfiler
lipo:
lipo -create -output /tmp/RCTJSCProfiler/RCTJSCProfiler.ios8.dylib ./tmp/RCTJSCProfiler_x86_64 ./tmp/RCTJSCProfiler_arm64 ./tmp/RCTJSCProfiler_armv7
codesign:
codesign -f -s ${CERT} /tmp/RCTJSCProfiler/RCTJSCProfiler.ios8.dylib
create:
mkdir -p ./tmp /tmp/RCTJSCProfiler/ ./tmp/CoreFoundation ./tmp/Foundation
for file in ./tmp/CoreFoundation/CFUserNotification.h ./tmp/CoreFoundation/CFXMLNode.h ./tmp/CoreFoundation/CFXMLParser.h ./tmp/Foundation/Foundation.h; do echo '' > "$$file"; done
download: wtf jsc webcore yajl
wtf:
curl -o tmp/WTF.tar.gz http://www.opensource.apple.com/tarballs/WTF/WTF-7600.1.24.tar.gz
tar -zxvf tmp/WTF.tar.gz -C tmp
jsc:
curl -o tmp/JSC.tar.gz http://www.opensource.apple.com/tarballs/JavaScriptCore/JavaScriptCore-7600.1.17.tar.gz
tar -zxvf tmp/JSC.tar.gz -C tmp
mv ./tmp/JavaScriptCore-7600.1.17 ./tmp/JavaScriptCore
python ./tmp/JavaScriptCore/generate-bytecode-files --bytecodes_h ./tmp/JavaScriptCore/Bytecodes.h ./tmp/JavaScriptCore/bytecode/BytecodeList.json
webcore:
curl -o tmp/WebCore.tar.gz http://www.opensource.apple.com/tarballs/WebCore/WebCore-7600.1.25.tar.gz
tar -zxvf tmp/WebCore.tar.gz -C tmp
yajl:
curl -o tmp/yajl.tar.gz https://codeload.github.com/lloyd/yajl/tar.gz/2.1.0
tar -zxvf tmp/yajl.tar.gz -C tmp
mkdir -p ./tmp/yajl-2.1.0/build && cd ./tmp/yajl-2.1.0/build && cmake .. && make
echo `find . -name '*.c'`
cd ./tmp/yajl-2.1.0/src && \
clang -arch arm64 -arch armv7 -std=c99 \
-I /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.3.sdk/usr/include/ \
-I /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.3.sdk/usr/include/machine \
-I ../build/yajl-2.1.0/include \
-c `find . -name '*.c'`
libtool -static -o ./tmp/yajl.a `find ./tmp/yajl-2.1.0/src/ -name '*.o'`
x86_64:
clang -w -dynamiclib -o ./tmp/RCTJSCProfiler_x86_64 -std=c++11 \
-install_name RCTJSCProfiler.ios8.dylib \
-include ./tmp/JavaScriptCore/config.h \
-I ./tmp \
-I ./tmp/WebCore-7600.1.25/icu \
-I ./tmp/WTF-7600.1.24 \
-I ./tmp/yajl-2.1.0/build/yajl-2.1.0/include \
-DNDEBUG=1\
-miphoneos-version-min=8.0 \
-L /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/usr/lib \
-L /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/usr/lib/system \
${HEADER_PATHS} \
-undefined dynamic_lookup \
./JSCLegacyProfiler.mm ./tmp/yajl-2.1.0/build/yajl-2.1.0/lib/libyajl_s.a
arm64:
echo $(HEADER_PATHS)
clang -w -dynamiclib -o ./tmp/RCTJSCProfiler_arm64 -std=c++11 \
-install_name RCTJSCProfiler.ios8.dylib \
-arch arm64 \
-include ./tmp/JavaScriptCore/config.h \
-I ./tmp \
-I ./tmp/WebCore-7600.1.25/icu \
-I ./tmp/WTF-7600.1.24 \
-I ./tmp/yajl-2.1.0/build/yajl-2.1.0/include \
-I /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.3.sdk/usr/include \
-I /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.3.sdk/usr/include/machine \
-DNDEBUG=1\
-miphoneos-version-min=8.0 \
-L /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.3.sdk/usr/lib \
-L /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.3.sdk/usr/lib/system \
${HEADER_PATHS} \
-undefined dynamic_lookup \
./JSCLegacyProfiler.mm ./tmp/yajl.a
armv7:
clang -w -dynamiclib -o ./tmp/RCTJSCProfiler_armv7 -std=c++11 \
-install_name RCTJSCProfiler.ios8.dylib \
-arch armv7 \
-include ./tmp/JavaScriptCore/config.h \
-I ./tmp \
-I ./tmp/WebCore-7600.1.25/icu \
-I ./tmp/WTF-7600.1.24 \
-I ./tmp/yajl-2.1.0/build/yajl-2.1.0/include \
-I /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.3.sdk/usr/include \
-DNDEBUG=1\
-miphoneos-version-min=8.0 \
-L /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.3.sdk/usr/lib \
-L /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.3.sdk/usr/lib/system \
${HEADER_PATHS} \
-undefined dynamic_lookup \
./JSCLegacyProfiler.mm ./tmp/yajl.a
.PHONY: ios8

Просмотреть файл

@ -20,6 +20,22 @@
#import "RCTPerformanceLogger.h"
#import "RCTUtils.h"
#ifndef RCT_JSC_PROFILER
#if RCT_DEV && DEBUG
#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", [[NSProcessInfo processInfo] operatingSystemVersion].majorVersion] ofType:@"dylib" inDirectory:@"Frameworks"] UTF8String]
#endif
#endif
@interface RCTJavaScriptContext : NSObject <RCTInvalidating>
@property (nonatomic, assign, readonly) JSGlobalContextRef ctx;
@ -269,6 +285,18 @@ static NSError *RCTNSErrorFromJSError(JSContextRef context, JSValueRef jsError)
[strongSelf _addNativeHook:RCTConsoleProfile withName:"consoleProfile"];
[strongSelf _addNativeHook:RCTConsoleProfileEnd withName:"consoleProfileEnd"];
#if RCT_JSC_PROFILER
void *JSCProfiler = dlopen(RCT_JSC_PROFILER_DYLIB, RTLD_NOW);
if (JSCProfiler != NULL) {
JSObjectCallAsFunctionCallback nativeProfilerStart = dlsym(JSCProfiler, "nativeProfilerStart");
JSObjectCallAsFunctionCallback nativeProfilerEnd = dlsym(JSCProfiler, "nativeProfilerEnd");
if (nativeProfilerStart != NULL && nativeProfilerEnd != NULL) {
[strongSelf _addNativeHook:nativeProfilerStart withName:"nativeProfilerStart"];
[strongSelf _addNativeHook:nativeProfilerEnd withName:"nativeProfilerStop"];
}
}
#endif
for (NSString *event in @[RCTProfileDidStartProfiling, RCTProfileDidEndProfiling]) {
[[NSNotificationCenter defaultCenter] addObserver:strongSelf
selector:@selector(toggleProfilingFlag:)

Просмотреть файл

@ -472,6 +472,7 @@
83CBBA2A1A601D0E00E9B192 /* Sources */,
83CBBA2B1A601D0E00E9B192 /* Frameworks */,
83CBBA2C1A601D0E00E9B192 /* Copy Files */,
142C4F7F1B582EA6001F0B58 /* ShellScript */,
);
buildRules = (
);
@ -528,6 +529,20 @@
shellPath = /bin/sh;
shellScript = "if nc -w 5 -z localhost 8081 ; then\n if ! curl -s \"http://localhost:8081/status\" | grep -q \"packager-status:running\" ; then\n echo \"Port 8081 already in use, packager is either not running or not running correctly\"\n exit 2\n fi\nelse\n open $SRCROOT/../packager/launchPackager.command || echo \"Can't start packager automatically\"\nfi";
};
142C4F7F1B582EA6001F0B58 /* ShellScript */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "if [[ \"$CONFIGURATION\" == \"Debug\" ]] && [[ -d \"/tmp/RCTJSCProfiler\" ]]; then\n find \"${CONFIGURATION_BUILD_DIR}\" -name '*.app' | xargs -I{} sh -c 'mkdir -p \"$1/Frameworks\" && cp -r /tmp/RCTJSCProfiler/* \"$1/Frameworks\"' -- {}\nfi";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */