Update json generation code and save to file
Summary: @public * Change the JSON generation and remove the dependency on YAJL since it had a 128 depth limit * Enable the profiler bytecode generation to fix missing frames * Save the output to a file on the tmp dir instead of outputting it to the console Reviewed By: @jspahrsummers Differential Revision: D2420754
This commit is contained in:
parent
2a34239c17
commit
af05af7125
|
@ -6,7 +6,8 @@
|
|||
|
||||
extern "C" {
|
||||
|
||||
void nativeProfilerEnableBytecode(void);
|
||||
void nativeProfilerStart(JSContextRef ctx, const char *title);
|
||||
const char *nativeProfilerEnd(JSContextRef ctx, const char *title);
|
||||
void nativeProfilerEnd(JSContextRef ctx, const char *title, const char *filename);
|
||||
|
||||
}
|
||||
|
|
|
@ -6,125 +6,298 @@
|
|||
#include "JSProfilerPrivate.h"
|
||||
#include "JSStringRef.h"
|
||||
#include "String.h"
|
||||
#include "Options.h"
|
||||
|
||||
#include <YAJL/yajl_gen.h>
|
||||
enum json_gen_status {
|
||||
json_gen_status_ok = 0,
|
||||
json_gen_status_error = 1,
|
||||
};
|
||||
|
||||
enum json_entry {
|
||||
json_entry_key,
|
||||
json_entry_value,
|
||||
};
|
||||
|
||||
namespace {
|
||||
|
||||
struct json_state {
|
||||
FILE *fileOut;
|
||||
bool hasFirst;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
typedef json_state *json_gen;
|
||||
|
||||
static void json_escaped_cstring_printf(json_gen gen, const char *str) {
|
||||
const char *cursor = str;
|
||||
fputc('"', gen->fileOut);
|
||||
while (*cursor) {
|
||||
const char *escape = nullptr;
|
||||
switch (*cursor) {
|
||||
case '"':
|
||||
escape = "\\\"";
|
||||
break;
|
||||
case '\b':
|
||||
escape = "\\b";
|
||||
break;
|
||||
case '\f':
|
||||
escape = "\\f";
|
||||
break;
|
||||
case '\n':
|
||||
escape = "\\n";
|
||||
break;
|
||||
case '\r':
|
||||
escape = "\\r";
|
||||
break;
|
||||
case '\t':
|
||||
escape = "\\t";
|
||||
break;
|
||||
case '\\':
|
||||
escape = "\\\\";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (escape != nullptr) {
|
||||
fwrite(escape, 1, strlen(escape), gen->fileOut);
|
||||
} else {
|
||||
fputc(*cursor, gen->fileOut);
|
||||
}
|
||||
cursor++;
|
||||
}
|
||||
fputc('"', gen->fileOut);
|
||||
}
|
||||
|
||||
static json_gen_status json_gen_key_cstring(json_gen gen, const char *buffer) {
|
||||
if (gen->fileOut == nullptr) {
|
||||
return json_gen_status_error;
|
||||
}
|
||||
|
||||
if (gen->hasFirst) {
|
||||
fprintf(gen->fileOut, ",");
|
||||
}
|
||||
gen->hasFirst = true;
|
||||
|
||||
json_escaped_cstring_printf(gen, buffer);
|
||||
return json_gen_status_ok;
|
||||
}
|
||||
|
||||
static json_gen_status json_gen_map_open(json_gen gen, json_entry entryType) {
|
||||
if (gen->fileOut == nullptr) {
|
||||
return json_gen_status_error;
|
||||
}
|
||||
|
||||
if (entryType == json_entry_value) {
|
||||
fprintf(gen->fileOut, ":");
|
||||
} else if (entryType == json_entry_key) {
|
||||
if (gen->hasFirst) {
|
||||
fprintf(gen->fileOut, ",");
|
||||
}
|
||||
}
|
||||
fprintf(gen->fileOut, "{");
|
||||
gen->hasFirst = false;
|
||||
return json_gen_status_ok;
|
||||
}
|
||||
|
||||
static json_gen_status json_gen_map_close(json_gen gen) {
|
||||
if (gen->fileOut == nullptr) {
|
||||
return json_gen_status_error;
|
||||
}
|
||||
|
||||
fprintf(gen->fileOut, "}");
|
||||
gen->hasFirst = true;
|
||||
return json_gen_status_ok;
|
||||
}
|
||||
|
||||
static json_gen_status json_gen_array_open(json_gen gen, json_entry entryType) {
|
||||
if (gen->fileOut == nullptr) {
|
||||
return json_gen_status_error;
|
||||
}
|
||||
|
||||
if (entryType == json_entry_value) {
|
||||
fprintf(gen->fileOut, ":");
|
||||
} else if (entryType == json_entry_key) {
|
||||
if (gen->hasFirst) {
|
||||
fprintf(gen->fileOut, ",");
|
||||
}
|
||||
}
|
||||
fprintf(gen->fileOut, "[");
|
||||
gen->hasFirst = false;
|
||||
return json_gen_status_ok;
|
||||
}
|
||||
|
||||
static json_gen_status json_gen_array_close(json_gen gen) {
|
||||
if (gen->fileOut == nullptr) {
|
||||
return json_gen_status_error;
|
||||
}
|
||||
|
||||
fprintf(gen->fileOut, "]");
|
||||
gen->hasFirst = true;
|
||||
return json_gen_status_ok;
|
||||
}
|
||||
|
||||
static json_gen_status json_gen_keyvalue_cstring(json_gen gen, const char *key, const char *value) {
|
||||
if (gen->fileOut == nullptr) {
|
||||
return json_gen_status_error;
|
||||
}
|
||||
|
||||
if (gen->hasFirst) {
|
||||
fprintf(gen->fileOut, ",");
|
||||
}
|
||||
gen->hasFirst = true;
|
||||
|
||||
fprintf(gen->fileOut, "\"%s\" : ", key);
|
||||
json_escaped_cstring_printf(gen, value);
|
||||
|
||||
return json_gen_status_ok;
|
||||
}
|
||||
|
||||
|
||||
static json_gen_status json_gen_keyvalue_integer(json_gen gen, const char *key, int value) {
|
||||
if (gen->fileOut == nullptr) {
|
||||
return json_gen_status_error;
|
||||
}
|
||||
|
||||
if (gen->hasFirst) {
|
||||
fprintf(gen->fileOut, ",");
|
||||
}
|
||||
gen->hasFirst = true;
|
||||
|
||||
fprintf(gen->fileOut, "\"%s\": %d", key, value);
|
||||
return json_gen_status_ok;
|
||||
}
|
||||
|
||||
static json_gen_status json_gen_keyvalue_double(json_gen gen, const char *key, double value) {
|
||||
if (gen->fileOut == nullptr) {
|
||||
return json_gen_status_error;
|
||||
}
|
||||
|
||||
if (gen->hasFirst) {
|
||||
fprintf(gen->fileOut, ",");
|
||||
}
|
||||
gen->hasFirst = true;
|
||||
|
||||
fprintf(gen->fileOut, "\"%s\": %.20g", key, value);
|
||||
return json_gen_status_ok;
|
||||
}
|
||||
|
||||
static json_gen json_gen_alloc(const char *fileName) {
|
||||
json_gen gen = (json_gen)malloc(sizeof(json_state));
|
||||
memset(gen, 0, sizeof(json_state));
|
||||
gen->fileOut = fopen(fileName, "wb");
|
||||
return gen;
|
||||
}
|
||||
|
||||
static void json_gen_free(json_gen gen) {
|
||||
if (gen->fileOut) {
|
||||
fclose(gen->fileOut);
|
||||
}
|
||||
free(gen);
|
||||
}
|
||||
|
||||
#define GEN_AND_CHECK(expr) \
|
||||
do { \
|
||||
yajl_gen_status GEN_AND_CHECK_status = (expr); \
|
||||
if (GEN_AND_CHECK_status != yajl_gen_status_ok) { \
|
||||
json_gen_status GEN_AND_CHECK_status = (expr); \
|
||||
if (GEN_AND_CHECK_status != json_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 json_gen_status append_children_array_json(json_gen gen, const JSC::ProfileNode *node);
|
||||
static json_gen_status append_node_json(json_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"));
|
||||
static json_gen_status append_root_json(json_gen gen, const JSC::Profile *profile) {
|
||||
GEN_AND_CHECK(json_gen_map_open(gen, json_entry_key));
|
||||
GEN_AND_CHECK(json_gen_key_cstring(gen, "rootNodes"));
|
||||
GEN_AND_CHECK(append_children_array_json(gen, profile->head()));
|
||||
GEN_AND_CHECK(yajl_gen_map_close(gen));
|
||||
GEN_AND_CHECK(json_gen_map_close(gen));
|
||||
|
||||
return yajl_gen_status_ok;
|
||||
return json_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));
|
||||
static json_gen_status append_children_array_json(json_gen gen, const JSC::ProfileNode *node) {
|
||||
GEN_AND_CHECK(json_gen_array_open(gen, json_entry_value));
|
||||
for (RefPtr<JSC::ProfileNode> child : node->children()) {
|
||||
GEN_AND_CHECK(append_node_json(gen, child.get()));
|
||||
}
|
||||
GEN_AND_CHECK(yajl_gen_array_close(gen));
|
||||
GEN_AND_CHECK(json_gen_array_close(gen));
|
||||
|
||||
return yajl_gen_status_ok;
|
||||
return json_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()));
|
||||
static json_gen_status append_node_json(json_gen gen, const JSC::ProfileNode *node) {
|
||||
GEN_AND_CHECK(json_gen_map_open(gen, json_entry_key));
|
||||
GEN_AND_CHECK(json_gen_keyvalue_integer(gen, "id", 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()));
|
||||
GEN_AND_CHECK(json_gen_keyvalue_cstring(gen, "functionName", 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(json_gen_keyvalue_cstring(gen, "url", node->url().utf8().data()));
|
||||
GEN_AND_CHECK(json_gen_keyvalue_integer(gen, "lineNumber", node->lineNumber()));
|
||||
GEN_AND_CHECK(json_gen_keyvalue_integer(gen, "columnNumber", node->columnNumber()));
|
||||
}
|
||||
|
||||
GEN_AND_CHECK(yajl_gen_cstring(gen, "calls"));
|
||||
GEN_AND_CHECK(yajl_gen_array_open(gen));
|
||||
GEN_AND_CHECK(json_gen_key_cstring(gen, "calls"));
|
||||
GEN_AND_CHECK(json_gen_array_open(gen, json_entry_value));
|
||||
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(json_gen_map_open(gen, json_entry_key));
|
||||
GEN_AND_CHECK(json_gen_keyvalue_double(gen, "startTime", call.startTime()));
|
||||
GEN_AND_CHECK(json_gen_keyvalue_double(gen, "totalTime", call.totalTime()));
|
||||
GEN_AND_CHECK(json_gen_map_close(gen));
|
||||
}
|
||||
GEN_AND_CHECK(yajl_gen_array_close(gen));
|
||||
GEN_AND_CHECK(json_gen_array_close(gen));
|
||||
|
||||
if (!node->children().isEmpty()) {
|
||||
GEN_AND_CHECK(yajl_gen_cstring(gen, "children"));
|
||||
GEN_AND_CHECK(json_gen_key_cstring(gen, "children"));
|
||||
GEN_AND_CHECK(append_children_array_json(gen, node));
|
||||
}
|
||||
|
||||
GEN_AND_CHECK(yajl_gen_map_close(gen));
|
||||
GEN_AND_CHECK(json_gen_map_close(gen));
|
||||
|
||||
return yajl_gen_status_ok;
|
||||
return json_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);
|
||||
static void convert_to_json(const JSC::Profile *profile, const char *filename) {
|
||||
json_gen_status status;
|
||||
json_gen gen = json_gen_alloc(filename);
|
||||
|
||||
status = append_root_json(gen, profile);
|
||||
if (status != yajl_gen_status_ok) {
|
||||
yajl_gen_free(gen);
|
||||
return render_error_code(status);
|
||||
if (status != json_gen_status_ok) {
|
||||
FILE *fileOut = fopen(filename, "wb");
|
||||
if (fileOut != nullptr) {
|
||||
fprintf(fileOut, "{\"error\": %d}", (int)status);
|
||||
fclose(fileOut);
|
||||
}
|
||||
|
||||
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;
|
||||
json_gen_free(gen);
|
||||
}
|
||||
|
||||
static const char *JSEndProfilingAndRender(JSContextRef ctx, const char *title)
|
||||
// Based on JSEndProfiling, with a little extra code to return the profile as JSON.
|
||||
static void JSEndProfilingAndRender(JSContextRef ctx, const char *title, const char *filename)
|
||||
{
|
||||
JSC::ExecState *exec = toJS(ctx);
|
||||
JSC::LegacyProfiler *profiler = JSC::LegacyProfiler::profiler();
|
||||
RefPtr<JSC::Profile> rawProfile = profiler->stopProfiling(exec, WTF::String(title));
|
||||
return convert_to_json(rawProfile.get());
|
||||
convert_to_json(rawProfile.get(), filename);
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
void nativeProfilerEnableBytecode(void)
|
||||
{
|
||||
JSC::Options::setOption("forceProfilerBytecodeGeneration=true");
|
||||
}
|
||||
|
||||
void nativeProfilerStart(JSContextRef ctx, const char *title) {
|
||||
JSStartProfiling(ctx, JSStringCreateWithUTF8CString(title));
|
||||
}
|
||||
|
||||
const char *nativeProfilerEnd( JSContextRef ctx, const char *title) {
|
||||
return JSEndProfilingAndRender(ctx, title);
|
||||
void nativeProfilerEnd(JSContextRef ctx, const char *title, const char *filename) {
|
||||
JSEndProfilingAndRender(ctx, title, filename);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ PLATFORM = \
|
|||
|
||||
SYSROOT = -isysroot $(call SDK_PATH,$${PLATFORM})
|
||||
|
||||
IOS8_LIBS = download/WebCore/WebCore-7600.1.25 download/WTF/WTF-7600.1.24 download/JavaScriptCore/JavaScriptCore-7600.1.17 download/JavaScriptCore/JavaScriptCore-7600.1.17/Bytecodes.h libyajl.a
|
||||
IOS8_LIBS = download/WebCore/WebCore-7600.1.25 download/WTF/WTF-7600.1.24 download/JavaScriptCore/JavaScriptCore-7600.1.17 download/JavaScriptCore/JavaScriptCore-7600.1.17/Bytecodes.h
|
||||
|
||||
ios8: RCTJSCProfiler.ios8.dylib /tmp/RCTJSCProfiler
|
||||
ifneq ($(SDK_VERSION), 8)
|
||||
|
@ -47,43 +47,17 @@ RCTJSCProfiler_%.ios8.dylib: $(IOS8_LIBS)
|
|||
-I download \
|
||||
-I download/WebCore/WebCore-7600.1.25/icu \
|
||||
-I download/WTF/WTF-7600.1.24 \
|
||||
-I download/yajl-2.1.0/build/yajl-2.1.0/include \
|
||||
-DNDEBUG=1\
|
||||
-miphoneos-version-min=8.0 \
|
||||
$(SYSROOT) \
|
||||
$(HEADER_PATHS) \
|
||||
-undefined dynamic_lookup \
|
||||
JSCLegacyProfiler.mm libyajl.a
|
||||
JSCLegacyProfiler.mm
|
||||
|
||||
.PRECIOUS: %/Bytecodes.h
|
||||
%/Bytecodes.h:
|
||||
python $*/generate-bytecode-files --bytecodes_h $@ $*/bytecode/BytecodeList.json
|
||||
|
||||
.PRECIOUS: libyajl.a
|
||||
libyajl.a: $(patsubst %,libyajl_%.a,$(ARCHS))
|
||||
lipo -create $^ -output $@
|
||||
|
||||
.PRECIOUS: libyajl_%.a
|
||||
libyajl_%.a: download/yajl-2.1.0
|
||||
$(PLATFORM) \
|
||||
cd download/yajl-2.1.0/src; \
|
||||
clang -arch $(*F) -std=c99 \
|
||||
-miphoneos-version-min=8.0 \
|
||||
$(SYSROOT) \
|
||||
-I ../build/yajl-2.1.0/include \
|
||||
-c `find . -name '*.c'`
|
||||
find download/yajl-2.1.0/src/ -name '*.o' -exec libtool -static -o $@ {} +
|
||||
|
||||
.PRECIOUS: download/yajl-2.1.0
|
||||
download/yajl-2.1.0: download/yajl-2.1.0.tar.gz
|
||||
tar -zxvf $< -C download > /dev/null
|
||||
mkdir -p download/yajl-2.1.0/build && cd download/yajl-2.1.0/build && cmake ..
|
||||
|
||||
.PRECIOUS: download/yajl-2.1.0.tar.gz
|
||||
download/yajl-2.1.0.tar.gz:
|
||||
mkdir -p `dirname $@`
|
||||
curl -o $@ https://codeload.github.com/lloyd/yajl/tar.gz/2.1.0
|
||||
|
||||
.PRECIOUS: download/%
|
||||
download/%: download/%.tar.gz
|
||||
tar -zxvf $< -C `dirname $@` > /dev/null
|
||||
|
@ -95,6 +69,6 @@ download/%: download/%.tar.gz
|
|||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
@rm -rf $(wildcard *.dylib)
|
||||
@rm -rf $(wildcard *.a)
|
||||
@rm -rf download
|
||||
-rm -rf $(wildcard *.dylib)
|
||||
-rm -rf $(wildcard *.a)
|
||||
-rm -rf download
|
||||
|
|
|
@ -206,6 +206,40 @@ static JSValueRef RCTNativeTraceEndSection(JSContextRef context, __unused JSObje
|
|||
return JSValueMakeUndefined(context);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
__block BOOL isProfiling = NO;
|
||||
[bridge.devMenu addItem:@"Profile" handler:^{
|
||||
if (isProfiling) {
|
||||
NSString *outputFile = [NSTemporaryDirectory() stringByAppendingPathComponent:@"cpu_profile.json"];
|
||||
nativeProfilerEnd(context, "profile", outputFile.UTF8String);
|
||||
RCTLogInfo(@"CPU profile outputed to '%@'", outputFile);
|
||||
} else {
|
||||
nativeProfilerStart(context, "profile");
|
||||
}
|
||||
isProfiling = !isProfiling;
|
||||
}];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+ (void)runRunLoopThread
|
||||
|
@ -284,24 +318,7 @@ static JSValueRef RCTNativeTraceEndSection(JSContextRef context, __unused JSObje
|
|||
[strongSelf _addNativeHook:RCTNativeTraceBeginSection withName:"nativeTraceBeginSection"];
|
||||
[strongSelf _addNativeHook:RCTNativeTraceEndSection withName:"nativeTraceEndSection"];
|
||||
|
||||
#if RCT_JSC_PROFILER
|
||||
void *JSCProfiler = dlopen(RCT_JSC_PROFILER_DYLIB, RTLD_NOW);
|
||||
if (JSCProfiler != NULL) {
|
||||
void (*nativeProfilerStart)(JSContextRef, const char *) = (void (*)(JSContextRef, const char *))dlsym(JSCProfiler, "nativeProfilerStart");
|
||||
const char *(*nativeProfilerEnd)(JSContextRef, const char *) = (const char *(*)(JSContextRef, const char *))dlsym(JSCProfiler, "nativeProfilerEnd");
|
||||
if (nativeProfilerStart != NULL && nativeProfilerEnd != NULL) {
|
||||
__block BOOL isProfiling = NO;
|
||||
[_bridge.devMenu addItem:@"Profile" handler:^{
|
||||
if (isProfiling) {
|
||||
RCTLogInfo(@"%s", nativeProfilerEnd(strongSelf->_context.ctx, "profile"));
|
||||
} else {
|
||||
nativeProfilerStart(strongSelf->_context.ctx, "profile");
|
||||
}
|
||||
isProfiling = !isProfiling;
|
||||
}];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
RCTInstallJSCProfiler(_bridge, strongSelf->_context.ctx);
|
||||
|
||||
for (NSString *event in @[RCTProfileDidStartProfiling, RCTProfileDidEndProfiling]) {
|
||||
[[NSNotificationCenter defaultCenter] addObserver:strongSelf
|
||||
|
|
Loading…
Reference in New Issue