mirror of
https://github.com/zhigang1992/react-native.git
synced 2026-04-29 12:45:37 +08:00
#15 Add a button in devtools to start/stop the Sampling Profiler
Differential Revision: D3555704 fbshipit-source-id: 4add16c923fcfd306892efec4630c24ae438d6dd
This commit is contained in:
committed by
Facebook Github Bot 8
parent
e762d961cd
commit
ec0ccf599a
@@ -27,6 +27,7 @@ const JSTimersExecution = require('JSTimersExecution');
|
|||||||
BatchedBridge.registerCallableModule('Systrace', Systrace);
|
BatchedBridge.registerCallableModule('Systrace', Systrace);
|
||||||
BatchedBridge.registerCallableModule('JSTimersExecution', JSTimersExecution);
|
BatchedBridge.registerCallableModule('JSTimersExecution', JSTimersExecution);
|
||||||
BatchedBridge.registerCallableModule('HeapCapture', require('HeapCapture'));
|
BatchedBridge.registerCallableModule('HeapCapture', require('HeapCapture'));
|
||||||
|
BatchedBridge.registerCallableModule('SamplingProfiler', require('SamplingProfiler'));
|
||||||
|
|
||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
BatchedBridge.registerCallableModule('HMRClient', require('HMRClient'));
|
BatchedBridge.registerCallableModule('HMRClient', require('HMRClient'));
|
||||||
|
|||||||
35
Libraries/Utilities/SamplingProfiler.js
Normal file
35
Libraries/Utilities/SamplingProfiler.js
Normal 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;
|
||||||
@@ -18,6 +18,7 @@ import com.facebook.react.bridge.NativeModule;
|
|||||||
import com.facebook.react.bridge.ReactApplicationContext;
|
import com.facebook.react.bridge.ReactApplicationContext;
|
||||||
import com.facebook.react.common.build.ReactBuildConfig;
|
import com.facebook.react.common.build.ReactBuildConfig;
|
||||||
import com.facebook.react.devsupport.JSCHeapCapture;
|
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.DefaultHardwareBackBtnHandler;
|
||||||
import com.facebook.react.modules.core.DeviceEventManagerModule;
|
import com.facebook.react.modules.core.DeviceEventManagerModule;
|
||||||
import com.facebook.react.modules.core.ExceptionsManagerModule;
|
import com.facebook.react.modules.core.ExceptionsManagerModule;
|
||||||
@@ -84,6 +85,7 @@ import com.facebook.systrace.Systrace;
|
|||||||
new Timing(catalystApplicationContext, mReactInstanceManager.getDevSupportManager()),
|
new Timing(catalystApplicationContext, mReactInstanceManager.getDevSupportManager()),
|
||||||
new SourceCodeModule(mReactInstanceManager.getSourceUrl()),
|
new SourceCodeModule(mReactInstanceManager.getSourceUrl()),
|
||||||
uiManagerModule,
|
uiManagerModule,
|
||||||
|
new JSCSamplingProfiler(catalystApplicationContext),
|
||||||
new DebugComponentOwnershipModule(catalystApplicationContext)));
|
new DebugComponentOwnershipModule(catalystApplicationContext)));
|
||||||
|
|
||||||
if (ReactBuildConfig.DEBUG) {
|
if (ReactBuildConfig.DEBUG) {
|
||||||
@@ -103,6 +105,7 @@ import com.facebook.systrace.Systrace;
|
|||||||
AppRegistry.class,
|
AppRegistry.class,
|
||||||
com.facebook.react.bridge.Systrace.class,
|
com.facebook.react.bridge.Systrace.class,
|
||||||
HMRClient.class,
|
HMRClient.class,
|
||||||
|
JSCSamplingProfiler.SamplingProfiler.class,
|
||||||
DebugComponentOwnershipModule.RCTDebugComponentOwnership.class));
|
DebugComponentOwnershipModule.RCTDebugComponentOwnership.class));
|
||||||
|
|
||||||
if (ReactBuildConfig.DEBUG) {
|
if (ReactBuildConfig.DEBUG) {
|
||||||
|
|||||||
@@ -375,6 +375,26 @@ public class DevSupportManagerImpl implements DevSupportManager {
|
|||||||
JSCHeapUpload.captureCallback(mDevServerHelper.getHeapCaptureUploadUrl()));
|
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(
|
options.put(
|
||||||
mApplicationContext.getString(R.string.catalyst_settings), new DevOptionHandler() {
|
mApplicationContext.getString(R.string.catalyst_settings), new DevOptionHandler() {
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,6 +20,7 @@
|
|||||||
<string name="catalyst_heap_capture" project="catalyst" translatable="false">Capture Heap</string>
|
<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_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_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_copy_button" project="catalyst" translatable="false">Copy</string>
|
||||||
<string name="catalyst_report_button" project="catalyst" translatable="false">Report</string>
|
<string name="catalyst_report_button" project="catalyst" translatable="false">Report</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -4,13 +4,30 @@
|
|||||||
|
|
||||||
#include "JSCSamplingProfiler.h"
|
#include "JSCSamplingProfiler.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
#include <JavaScriptCore/API/JSProfilerPrivate.h>
|
#include <JavaScriptCore/API/JSProfilerPrivate.h>
|
||||||
|
#include "JSCHelpers.h"
|
||||||
|
|
||||||
|
#include "Value.h"
|
||||||
|
|
||||||
namespace facebook {
|
namespace facebook {
|
||||||
namespace react {
|
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) {
|
void initSamplingProfilerOnMainJSCThread(JSGlobalContextRef ctx) {
|
||||||
JSStartSamplingProfilingOnMainJSCThread(ctx);
|
JSStartSamplingProfilingOnMainJSCThread(ctx);
|
||||||
|
installGlobalFunction(ctx, "pokeSamplingProfiler", pokeSamplingProfiler);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user