Added method handler for Sampling Profiler that uses only JSCcontext and JSC API

Reviewed By: cwdick

Differential Revision: D4689688

fbshipit-source-id: d1f9df0b3dcb21420cf813888c19f2d9a9aa5646
This commit is contained in:
Lukas Piatkowski 2017-05-10 03:46:47 -07:00 committed by Facebook Github Bot
parent cb3b744d08
commit 9468c5a733
11 changed files with 295 additions and 16 deletions

View File

@ -108,8 +108,8 @@ public class DevServerHelper {
public interface PackagerCommandListener { public interface PackagerCommandListener {
void onPackagerReloadCommand(); void onPackagerReloadCommand();
void onCaptureHeapCommand(@Nullable final Responder responder); void onCaptureHeapCommand(final Responder responder);
void onPokeSamplingProfilerCommand(@Nullable final Responder responder); void onPokeSamplingProfilerCommand(final Responder responder);
} }
public interface SymbolicationListener { public interface SymbolicationListener {

View File

@ -45,6 +45,7 @@ import com.facebook.react.devsupport.interfaces.StackFrame;
import com.facebook.react.modules.debug.interfaces.DeveloperSettings; import com.facebook.react.modules.debug.interfaces.DeveloperSettings;
import com.facebook.react.packagerconnection.JSPackagerClient; import com.facebook.react.packagerconnection.JSPackagerClient;
import com.facebook.react.packagerconnection.Responder; import com.facebook.react.packagerconnection.Responder;
import com.facebook.react.packagerconnection.SamplingProfilerPackagerMethod;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
@ -433,7 +434,7 @@ public class DevSupportManagerImpl implements
new DevOptionHandler() { new DevOptionHandler() {
@Override @Override
public void onOptionSelected() { public void onOptionSelected() {
handlePokeSamplingProfiler(null); handlePokeSamplingProfiler();
} }
}); });
options.put( options.put(
@ -690,11 +691,17 @@ public class DevSupportManagerImpl implements
} }
@Override @Override
public void onPokeSamplingProfilerCommand(@Nullable final Responder responder) { public void onPokeSamplingProfilerCommand(final Responder responder) {
UiThreadUtil.runOnUiThread(new Runnable() { UiThreadUtil.runOnUiThread(new Runnable() {
@Override @Override
public void run() { public void run() {
handlePokeSamplingProfiler(responder); if (mCurrentContext == null) {
responder.error("JSCContext is missing, unable to profile");
return;
}
SamplingProfilerPackagerMethod method =
new SamplingProfilerPackagerMethod(mCurrentContext.getJavaScriptContext());
method.onRequest(null, responder);
} }
}); });
} }
@ -719,7 +726,7 @@ public class DevSupportManagerImpl implements
}); });
} }
private void handlePokeSamplingProfiler(@Nullable final Responder responder) { private void handlePokeSamplingProfiler() {
try { try {
List<String> pokeResults = JSCSamplingProfiler.poke(60000); List<String> pokeResults = JSCSamplingProfiler.poke(60000);
for (String result : pokeResults) { for (String result : pokeResults) {
@ -729,16 +736,9 @@ public class DevSupportManagerImpl implements
? "Started JSC Sampling Profiler" ? "Started JSC Sampling Profiler"
: "Stopped JSC Sampling Profiler", : "Stopped JSC Sampling Profiler",
Toast.LENGTH_LONG).show(); Toast.LENGTH_LONG).show();
if (responder != null) { new JscProfileTask(getSourceUrl()).executeOnExecutor(
// Responder is provided, so there is a client waiting our response AsyncTask.THREAD_POOL_EXECUTOR,
responder.respond(result == null ? "started" : result); result);
} else if (result != null) {
// The profile was not initiated by external client, so process the
// profile if there is one in the result
new JscProfileTask(getSourceUrl()).executeOnExecutor(
AsyncTask.THREAD_POOL_EXECUTOR,
result);
}
} }
} catch (JSCSamplingProfiler.ProfilerException e) { } catch (JSCSamplingProfiler.ProfilerException e) {
showNewJavaError(e.getMessage(), e); showNewJavaError(e.getMessage(), e);

View File

@ -7,12 +7,16 @@ android_library(
"PUBLIC", "PUBLIC",
], ],
deps = [ deps = [
react_native_dep("java/com/facebook/jni:jni"),
react_native_dep("java/com/facebook/proguard/annotations:annotations"),
react_native_dep("libraries/fbcore/src/main/java/com/facebook/common/logging:logging"), react_native_dep("libraries/fbcore/src/main/java/com/facebook/common/logging:logging"),
react_native_dep("libraries/soloader/java/com/facebook/soloader:soloader"),
react_native_dep("third-party/java/infer-annotations:infer-annotations"), react_native_dep("third-party/java/infer-annotations:infer-annotations"),
react_native_dep("third-party/java/jsr-305:jsr-305"), react_native_dep("third-party/java/jsr-305:jsr-305"),
react_native_dep("third-party/java/okhttp:okhttp3"), react_native_dep("third-party/java/okhttp:okhttp3"),
react_native_dep("third-party/java/okhttp:okhttp3-ws"), react_native_dep("third-party/java/okhttp:okhttp3-ws"),
react_native_dep("third-party/java/okio:okio"), react_native_dep("third-party/java/okio:okio"),
react_native_target("java/com/facebook/react/modules/systeminfo:systeminfo-moduleless"), react_native_target("java/com/facebook/react/modules/systeminfo:systeminfo-moduleless"),
react_native_target("jni/packagerconnection:jni"),
], ],
) )

View File

@ -0,0 +1,55 @@
/**
* 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.
*/
package com.facebook.react.packagerconnection;
import javax.annotation.Nullable;
import android.os.Looper;
import com.facebook.jni.HybridData;
import com.facebook.proguard.annotations.DoNotStrip;
import com.facebook.soloader.SoLoader;
public class SamplingProfilerPackagerMethod extends RequestOnlyHandler {
static {
SoLoader.loadLibrary("packagerconnectionjnifb");
}
final private static class SamplingProfilerJniMethod {
@DoNotStrip
private final HybridData mHybridData;
public SamplingProfilerJniMethod(long javaScriptContext) {
if (Looper.myLooper() == null) {
Looper.prepare();
}
mHybridData = initHybrid(javaScriptContext);
}
@DoNotStrip
private native void poke(Responder responder);
@DoNotStrip
private static native HybridData initHybrid(long javaScriptContext);
}
private SamplingProfilerJniMethod mJniMethod;
public SamplingProfilerPackagerMethod(long javaScriptContext) {
mJniMethod = new SamplingProfilerJniMethod(javaScriptContext);
}
@Override
public void onRequest(@Nullable Object params, Responder responder) {
mJniMethod.poke(responder);
}
}

View File

@ -0,0 +1,29 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := packagerconnectionjnifb
LOCAL_SRC_FILES := \
JSPackagerClientResponder.h \
SamplingProfilerPackagerMethod.cpp \
LOCAL_C_INCLUDES := $(LOCAL_PATH)
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../
LOCAL_CFLAGS += -Wall -Werror -fvisibility=hidden -fexceptions -frtti
CXX11_FLAGS := -std=c++11
LOCAL_CFLAGS += $(CXX11_FLAGS)
LOCAL_EXPORT_CPPFLAGS := $(CXX11_FLAGS)
LOCAL_LDLIBS += -landroid
LOCAL_SHARED_LIBRARIES := libfolly_json libfbjni libjsc libglog_init
include $(BUILD_SHARED_LIBRARY)
$(call import-module,fb)
$(call import-module,jsc)
$(call import-module,folly)
$(call import-module,fbgloginit)
$(call import-module,jni)
$(call import-module,jscwrapper)

View File

@ -0,0 +1,35 @@
include_defs("//ReactAndroid/DEFS")
cxx_library(
name = "jni",
srcs = glob(["*.cpp"]),
compiler_flags = [
"-Wall",
"-Werror",
"-fexceptions",
"-std=c++1y",
"-frtti",
],
header_namespace = "",
headers = glob(
["*.h"],
),
preprocessor_flags = [
"-DLOG_TAG=\"PackagerConnectionJNI\"",
"-DWITH_FBSYSTRACE=1",
"-DWITH_INSPECTOR=1",
],
soname = "libpackagerconnectionjnifb.$(ext)",
visibility = [
"PUBLIC",
],
xcode_public_headers_symlinks = True,
deps = JSC_DEPS + [
"//native/fb:fb",
"//native/third-party/android-ndk:android",
"//xplat/folly:molly",
"//xplat/fbgloginit:fbgloginit",
"//xplat/fbsystrace:fbsystrace",
react_native_xplat_target("jschelpers:jschelpers"),
],
)

View File

@ -0,0 +1,32 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include "JSPackagerClientResponder.h"
#include <jni/LocalString.h>
using namespace facebook::jni;
namespace facebook {
namespace react {
void JSPackagerClientResponder::respond(alias_ref<jobject> result) {
static auto method =
javaClassStatic()->getMethod<void(alias_ref<jobject>)>("respond");
method(self(), result);
}
void JSPackagerClientResponder::respond(const std::string &result) {
respond(LocalString(result).string());
}
void JSPackagerClientResponder::error(alias_ref<jobject> result) {
static auto method =
javaClassStatic()->getMethod<void(alias_ref<jobject>)>("error");
method(self(), result);
}
void JSPackagerClientResponder::error(const std::string &result) {
error(LocalString(result).string());
}
}
}

View File

@ -0,0 +1,22 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include <fb/fbjni.h>
namespace facebook {
namespace react {
class JSPackagerClientResponder
: public jni::JavaClass<JSPackagerClientResponder> {
public:
static constexpr auto kJavaDescriptor =
"Lcom/facebook/react/packagerconnection/Responder;";
void respond(jni::alias_ref<jobject> result);
void respond(const std::string& result);
void error(jni::alias_ref<jobject> result);
void error(const std::string& result);
};
}
}

View File

@ -0,0 +1,17 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include <fb/fbjni.h>
#include <jni.h>
#include "SamplingProfilerJniMethod.h"
using namespace facebook::jni;
namespace facebook {
namespace react {
extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
return initialize(vm, [] { SamplingProfilerJniMethod::registerNatives(); });
}
}
}

View File

@ -0,0 +1,50 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include "SamplingProfilerJniMethod.h"
#include <jni.h>
#include <string>
using namespace facebook::jni;
namespace facebook {
namespace react {
/* static */ jni::local_ref<SamplingProfilerJniMethod::jhybriddata>
SamplingProfilerJniMethod::initHybrid(jni::alias_ref<jclass>,
jlong javaScriptContext) {
return makeCxxInstance(javaScriptContext);
}
/* static */ void SamplingProfilerJniMethod::registerNatives() {
registerHybrid(
{makeNativeMethod("initHybrid", SamplingProfilerJniMethod::initHybrid),
makeNativeMethod("poke", SamplingProfilerJniMethod::poke)});
}
SamplingProfilerJniMethod::SamplingProfilerJniMethod(jlong javaScriptContext) {
context_ = reinterpret_cast<JSGlobalContextRef>(javaScriptContext);
}
void SamplingProfilerJniMethod::poke(
jni::alias_ref<JSPackagerClientResponder::javaobject> responder) {
if (!JSC_JSSamplingProfilerEnabled(context_)) {
responder->error("The JSSamplingProfiler is disabled. See this "
"https://fburl.com/u4lw7xeq for some help");
return;
}
JSValueRef jsResult = JSC_JSPokeSamplingProfiler(context_);
if (JSC_JSValueGetType(context_, jsResult) == kJSTypeNull) {
responder->respond("started");
} else {
JSStringRef resultStrRef = JSValueToStringCopy(context_, jsResult, nullptr);
size_t length = JSStringGetLength(resultStrRef);
char buffer[length + 1];
JSStringGetUTF8CString(resultStrRef, buffer, length + 1);
JSStringRelease(resultStrRef);
responder->respond(buffer);
}
}
}
}

View File

@ -0,0 +1,35 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include <JavaScriptCore/JSProfilerPrivate.h>
#include <JavaScriptCore/JSValueRef.h>
#include <fb/fbjni.h>
#include <jschelpers/JSCHelpers.h>
#include "JSPackagerClientResponder.h"
namespace facebook {
namespace react {
class SamplingProfilerJniMethod
: public jni::HybridClass<SamplingProfilerJniMethod> {
public:
static constexpr auto kJavaDescriptor =
"Lcom/facebook/react/packagerconnection/"
"SamplingProfilerPackagerMethod$SamplingProfilerJniMethod;";
static jni::local_ref<jhybriddata> initHybrid(jni::alias_ref<jclass> jthis,
jlong javaScriptContext);
static void registerNatives();
private:
friend HybridBase;
explicit SamplingProfilerJniMethod(jlong javaScriptContext);
void poke(jni::alias_ref<JSPackagerClientResponder::javaobject> responder);
JSGlobalContextRef context_;
};
}
}