#15 Add a button in devtools to start/stop the Sampling Profiler

Differential Revision: D3555704

fbshipit-source-id: 4add16c923fcfd306892efec4630c24ae438d6dd
This commit is contained in:
Lukas Piatkowski 2016-08-02 11:10:48 -07:00 committed by Facebook Github Bot 8
parent e762d961cd
commit ec0ccf599a
7 changed files with 224 additions and 0 deletions

View File

@ -27,6 +27,7 @@ const JSTimersExecution = require('JSTimersExecution');
BatchedBridge.registerCallableModule('Systrace', Systrace);
BatchedBridge.registerCallableModule('JSTimersExecution', JSTimersExecution);
BatchedBridge.registerCallableModule('HeapCapture', require('HeapCapture'));
BatchedBridge.registerCallableModule('SamplingProfiler', require('SamplingProfiler'));
if (__DEV__) {
BatchedBridge.registerCallableModule('HMRClient', require('HMRClient'));

View File

@ -0,0 +1,35 @@
/**
* 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.
*
* @providesModule SamplingProfiler
* @flow
*/
'use strict';
var SamplingProfiler = {
poke: function (token: number): void {
var error = null;
var result = null;
try {
result = global.pokeSamplingProfiler();
if (result === null) {
console.log('The JSC Sampling Profiler has started');
} else {
console.log('The JSC Sampling Profiler has stopped');
}
} catch (e) {
console.log(
'Error occured when restarting Sampling Profiler: ' + e.toString());
error = e.toString();
}
require('NativeModules').JSCSamplingProfiler.operationComplete(
token, result, error);
},
};
module.exports = SamplingProfiler;

View File

@ -18,6 +18,7 @@ import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.common.build.ReactBuildConfig;
import com.facebook.react.devsupport.JSCHeapCapture;
import com.facebook.react.devsupport.JSCSamplingProfiler;
import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.facebook.react.modules.core.ExceptionsManagerModule;
@ -84,6 +85,7 @@ import com.facebook.systrace.Systrace;
new Timing(catalystApplicationContext, mReactInstanceManager.getDevSupportManager()),
new SourceCodeModule(mReactInstanceManager.getSourceUrl()),
uiManagerModule,
new JSCSamplingProfiler(catalystApplicationContext),
new DebugComponentOwnershipModule(catalystApplicationContext)));
if (ReactBuildConfig.DEBUG) {
@ -103,6 +105,7 @@ import com.facebook.systrace.Systrace;
AppRegistry.class,
com.facebook.react.bridge.Systrace.class,
HMRClient.class,
JSCSamplingProfiler.SamplingProfiler.class,
DebugComponentOwnershipModule.RCTDebugComponentOwnership.class));
if (ReactBuildConfig.DEBUG) {

View File

@ -375,6 +375,26 @@ public class DevSupportManagerImpl implements DevSupportManager {
JSCHeapUpload.captureCallback(mDevServerHelper.getHeapCaptureUploadUrl()));
}
});
options.put(
mApplicationContext.getString(R.string.catalyst_poke_sampling_profiler),
new DevOptionHandler() {
@Override
public void onOptionSelected() {
try {
List<String> pokeResults = JSCSamplingProfiler.poke(60000);
for (String result : pokeResults) {
Toast.makeText(
mCurrentContext,
result == null
? "Started JSC Sampling Profiler"
: "Stopped JSC Sampling Profiler",
Toast.LENGTH_LONG).show();
}
} catch (JSCSamplingProfiler.ProfilerException e) {
showNewJavaError(e.getMessage(), e);
}
}
});
options.put(
mApplicationContext.getString(R.string.catalyst_settings), new DevOptionHandler() {
@Override

View File

@ -0,0 +1,147 @@
/**
* 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.devsupport;
import javax.annotation.Nullable;
import java.io.File;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
public class JSCSamplingProfiler extends ReactContextBaseJavaModule {
public interface SamplingProfiler extends JavaScriptModule {
void poke(int token);
}
public static class ProfilerException extends Exception {
ProfilerException(String message) {
super(message);
}
}
private @Nullable SamplingProfiler mSamplingProfiler;
private boolean mOperationInProgress;
private int mOperationToken;
private @Nullable String mOperationError;
private @Nullable String mSamplingProfilerResult;
private static final HashSet<JSCSamplingProfiler> sRegisteredDumpers =
new HashSet<>();
private static synchronized void registerSamplingProfiler(
JSCSamplingProfiler dumper) {
if (sRegisteredDumpers.contains(dumper)) {
throw new RuntimeException(
"a JSCSamplingProfiler registered more than once");
}
sRegisteredDumpers.add(dumper);
}
private static synchronized void unregisterSamplingProfiler(
JSCSamplingProfiler dumper) {
sRegisteredDumpers.remove(dumper);
}
public static synchronized List<String> poke(long timeout)
throws ProfilerException {
LinkedList<String> results = new LinkedList<>();
if (sRegisteredDumpers.isEmpty()) {
throw new ProfilerException("No JSC registered");
}
for (JSCSamplingProfiler dumper : sRegisteredDumpers) {
dumper.pokeHelper(timeout);
results.add(dumper.mSamplingProfilerResult);
}
return results;
}
public JSCSamplingProfiler(ReactApplicationContext reactContext) {
super(reactContext);
mSamplingProfiler = null;
mOperationInProgress = false;
mOperationToken = 0;
mOperationError = null;
mSamplingProfilerResult = null;
}
private synchronized void pokeHelper(long timeout) throws ProfilerException {
if (mSamplingProfiler == null) {
throw new ProfilerException("SamplingProfiler.js module not connected");
}
mSamplingProfiler.poke(getOperationToken());
waitForOperation(timeout);
}
private int getOperationToken() throws ProfilerException {
if (mOperationInProgress) {
throw new ProfilerException("Another operation already in progress.");
}
mOperationInProgress = true;
return ++mOperationToken;
}
private void waitForOperation(long timeout) throws ProfilerException {
try {
wait(timeout);
} catch (InterruptedException e) {
throw new ProfilerException(
"Waiting for heap capture failed: " + e.getMessage());
}
if (mOperationInProgress) {
mOperationInProgress = false;
throw new ProfilerException("heap capture timed out.");
}
if (mOperationError != null) {
throw new ProfilerException(mOperationError);
}
}
@ReactMethod
public synchronized void operationComplete(
int token, String result, String error) {
if (token == mOperationToken) {
mOperationInProgress = false;
mSamplingProfilerResult = result;
mOperationError = error;
this.notify();
} else {
throw new RuntimeException("Completed operation is not in progress.");
}
}
@Override
public String getName() {
return "JSCSamplingProfiler";
}
@Override
public void initialize() {
super.initialize();
mSamplingProfiler =
getReactApplicationContext().getJSModule(SamplingProfiler.class);
registerSamplingProfiler(this);
}
@Override
public void onCatalystInstanceDestroy() {
super.onCatalystInstanceDestroy();
unregisterSamplingProfiler(this);
mSamplingProfiler = null;
}
}

View File

@ -20,6 +20,7 @@
<string name="catalyst_heap_capture" project="catalyst" translatable="false">Capture Heap</string>
<string name="catalyst_dismiss_button" project="catalyst" translatable="false">Dismiss\n(ESC)</string>
<string name="catalyst_reload_button" project="catalyst" translatable="false">Reload\n(R,\u00A0R)</string>
<string name="catalyst_poke_sampling_profiler" project="catalyst" translatable="false">Start/Stop Sampling Profiler</string>
<string name="catalyst_copy_button" project="catalyst" translatable="false">Copy</string>
<string name="catalyst_report_button" project="catalyst" translatable="false">Report</string>
</resources>

View File

@ -4,13 +4,30 @@
#include "JSCSamplingProfiler.h"
#include <stdio.h>
#include <string.h>
#include <JavaScriptCore/API/JSProfilerPrivate.h>
#include "JSCHelpers.h"
#include "Value.h"
namespace facebook {
namespace react {
namespace {
static JSValueRef pokeSamplingProfiler(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef* exception) {
return JSPokeSamplingProfiler(ctx);
}
}
void initSamplingProfilerOnMainJSCThread(JSGlobalContextRef ctx) {
JSStartSamplingProfilingOnMainJSCThread(ctx);
installGlobalFunction(ctx, "pokeSamplingProfiler", pokeSamplingProfiler);
}
}