Capture heap from the dev menu

Reviewed By: foghina

Differential Revision: D3229856

fb-gh-sync-id: c6321cfb309c93572a51bba625d63246a75f0254
fbshipit-source-id: c6321cfb309c93572a51bba625d63246a75f0254
This commit is contained in:
Charles Dick 2016-04-29 07:34:51 -07:00 committed by Facebook Github Bot 5
parent 84e696867b
commit 24b2990467
6 changed files with 180 additions and 0 deletions

View File

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

View File

@ -0,0 +1,28 @@
/**
* 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 HeapCapture
* @flow
*/
'use strict';
var HeapCapture = {
captureHeap: function (token: number, path: string) {
var error = null;
try {
global.nativeCaptureHeap(path);
console.log('HeapCapture.captureHeap succeeded: ' + path);
} catch (e) {
console.log('HeapCapture.captureHeap error: ' + e.toString());
error = e.toString();
}
require('NativeModules').JSCHeapCapture.operationComplete(token, error);
},
};
module.exports = HeapCapture;

View File

@ -16,6 +16,7 @@ import java.util.List;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.devsupport.JSCHeapCapture;
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;
mReactInstanceManager.getSourceUrl(),
mReactInstanceManager.getDevSupportManager().getSourceMapUrl()),
uiManagerModule,
new JSCHeapCapture(catalystApplicationContext),
new DebugComponentOwnershipModule(catalystApplicationContext));
}
@ -97,6 +99,7 @@ import com.facebook.systrace.Systrace;
AppRegistry.class,
com.facebook.react.bridge.Systrace.class,
HMRClient.class,
JSCHeapCapture.HeapCapture.class,
DebugComponentOwnershipModule.RCTDebugComponentOwnership.class);
}

View File

@ -304,6 +304,23 @@ public class DevSupportManagerImpl implements DevSupportManager {
mReactInstanceCommandsHandler.toggleElementInspector();
}
});
options.put(
mApplicationContext.getString(R.string.catalyst_heap_capture),
new DevOptionHandler() {
@Override
public void onOptionSelected() {
try {
String heapDumpPath = mApplicationContext.getCacheDir() + "/heapdump.json";
JSCHeapCapture.captureHeap(heapDumpPath, 60000);
Toast.makeText(
mCurrentContext,
"Heap captured to " + heapDumpPath,
Toast.LENGTH_LONG).show();
} catch (JSCHeapCapture.CaptureException e) {
showNewJavaError(e.getMessage(), e);
}
}
});
options.put(
mDevSettings.isFpsDebugEnabled()
? mApplicationContext.getString(R.string.catalyst_perf_monitor_off)

View File

@ -0,0 +1,130 @@
/**
* 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 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 JSCHeapCapture extends ReactContextBaseJavaModule {
public interface HeapCapture extends JavaScriptModule {
void captureHeap(int token, String path);
void setAllocationTracking(int token, boolean enabled);
}
public static class CaptureException extends Exception {
CaptureException(String message) {
super(message);
}
}
private @Nullable HeapCapture mHeapCapture;
private boolean mOperationInProgress;
private int mOperationToken;
private @Nullable String mOperationError;
private static @Nullable JSCHeapCapture sJSCHeapCapture = null;
private static synchronized void registerHeapCapture(JSCHeapCapture dumper) {
if (sJSCHeapCapture != null) {
throw new RuntimeException(
"JSCHeapCapture already registered. Are you running more than one JSC?");
}
sJSCHeapCapture = dumper;
}
private static synchronized void unregisterHeapCapture(JSCHeapCapture dumper) {
if (sJSCHeapCapture != dumper) {
throw new RuntimeException("Can't unregister JSCHeapCapture that is not registered.");
}
sJSCHeapCapture = null;
}
public static synchronized void captureHeap(String path, long timeout) throws CaptureException {
if (sJSCHeapCapture == null) {
throw new CaptureException("No JSC registered.");
}
sJSCHeapCapture.captureHeapHelper(path, timeout);
}
public JSCHeapCapture(ReactApplicationContext reactContext) {
super(reactContext);
mHeapCapture = null;
mOperationInProgress = false;
mOperationToken = 0;
mOperationError = null;
}
private synchronized void captureHeapHelper(String path, long timeout) throws CaptureException {
if (mHeapCapture == null) {
throw new CaptureException("HeapCapture.js module not connected");
}
mHeapCapture.captureHeap(getOperationToken(), path);
waitForOperation(timeout);
}
private int getOperationToken() throws CaptureException {
if (mOperationInProgress) {
throw new CaptureException("Another operation already in progress.");
}
mOperationInProgress = true;
return ++mOperationToken;
}
private void waitForOperation(long timeout) throws CaptureException {
try {
wait(timeout);
} catch (InterruptedException e) {
throw new CaptureException("Waiting for heap capture failed: " + e.getMessage());
}
if (mOperationInProgress) {
mOperationInProgress = false;
throw new CaptureException("heap capture timed out.");
}
if (mOperationError != null) {
throw new CaptureException(mOperationError);
}
}
@ReactMethod
public synchronized void operationComplete(int token, String error) {
if (token == mOperationToken) {
mOperationInProgress = false;
mOperationError = error;
this.notify();
} else {
throw new RuntimeException("Completed operation is not in progress.");
}
}
@Override
public String getName() {
return "JSCHeapCapture";
}
@Override
public void initialize() {
super.initialize();
mHeapCapture = getReactApplicationContext().getJSModule(HeapCapture.class);
registerHeapCapture(this);
}
@Override
public void onCatalystInstanceDestroy() {
super.onCatalystInstanceDestroy();
unregisterHeapCapture(this);
mHeapCapture = null;
}
}

View File

@ -19,4 +19,5 @@
<string name="catalyst_element_inspector" project="catalyst" translatable="false">Toggle Inspector</string>
<string name="catalyst_start_profile" project="catalyst" translatable="false">Start Profile</string>
<string name="catalyst_stop_profile" project="catalyst" translatable="false">Stop Profile</string>
<string name="catalyst_heap_capture" project="catalyst" translatable="false">Capture Heap</string>
</resources>