Add "Dump HPROF file" to DDMS/DDMS plugin
This uses the (new in cupcake) VM command through JDWP. Older VMs are detected through the (also new) 'FEAT' command that notifies which features the VM supports. The hprof file is right now saved in /sdcard. Due to donut+ apps not having the SD Card permission by default, we may need to change this in the (near) future. Upon completion of the dump by the VM, DDMS will give the user a file selector to choose a place to save the file on the host machine. Future improvements: run (our own) hat, or hprof-conv and a standard hprof tool (Eclipse MAT integration for instance). This should be configurable by the user. Change-Id: I33696b0263e3d0788ad5d90cedf3cd17393d2f9b
This commit is contained in:
@@ -18,13 +18,16 @@ package com.android.ddms;
|
|||||||
|
|
||||||
import com.android.ddmlib.AndroidDebugBridge;
|
import com.android.ddmlib.AndroidDebugBridge;
|
||||||
import com.android.ddmlib.Client;
|
import com.android.ddmlib.Client;
|
||||||
|
import com.android.ddmlib.ClientData;
|
||||||
import com.android.ddmlib.IDevice;
|
import com.android.ddmlib.IDevice;
|
||||||
import com.android.ddmlib.Log;
|
import com.android.ddmlib.Log;
|
||||||
|
import com.android.ddmlib.SyncService;
|
||||||
|
import com.android.ddmlib.ClientData.IHprofDumpHandler;
|
||||||
import com.android.ddmlib.Log.ILogOutput;
|
import com.android.ddmlib.Log.ILogOutput;
|
||||||
import com.android.ddmlib.Log.LogLevel;
|
import com.android.ddmlib.Log.LogLevel;
|
||||||
|
import com.android.ddmlib.SyncService.SyncResult;
|
||||||
import com.android.ddmuilib.AllocationPanel;
|
import com.android.ddmuilib.AllocationPanel;
|
||||||
import com.android.ddmuilib.DevicePanel;
|
import com.android.ddmuilib.DevicePanel;
|
||||||
import com.android.ddmuilib.DevicePanel.IUiSelectionListener;
|
|
||||||
import com.android.ddmuilib.EmulatorControlPanel;
|
import com.android.ddmuilib.EmulatorControlPanel;
|
||||||
import com.android.ddmuilib.HeapPanel;
|
import com.android.ddmuilib.HeapPanel;
|
||||||
import com.android.ddmuilib.ITableFocusListener;
|
import com.android.ddmuilib.ITableFocusListener;
|
||||||
@@ -33,9 +36,11 @@ import com.android.ddmuilib.ImageLoader;
|
|||||||
import com.android.ddmuilib.InfoPanel;
|
import com.android.ddmuilib.InfoPanel;
|
||||||
import com.android.ddmuilib.NativeHeapPanel;
|
import com.android.ddmuilib.NativeHeapPanel;
|
||||||
import com.android.ddmuilib.ScreenShotDialog;
|
import com.android.ddmuilib.ScreenShotDialog;
|
||||||
|
import com.android.ddmuilib.SyncProgressMonitor;
|
||||||
import com.android.ddmuilib.SysinfoPanel;
|
import com.android.ddmuilib.SysinfoPanel;
|
||||||
import com.android.ddmuilib.TablePanel;
|
import com.android.ddmuilib.TablePanel;
|
||||||
import com.android.ddmuilib.ThreadPanel;
|
import com.android.ddmuilib.ThreadPanel;
|
||||||
|
import com.android.ddmuilib.DevicePanel.IUiSelectionListener;
|
||||||
import com.android.ddmuilib.actions.ToolItemAction;
|
import com.android.ddmuilib.actions.ToolItemAction;
|
||||||
import com.android.ddmuilib.explorer.DeviceExplorer;
|
import com.android.ddmuilib.explorer.DeviceExplorer;
|
||||||
import com.android.ddmuilib.log.event.EventLogPanel;
|
import com.android.ddmuilib.log.event.EventLogPanel;
|
||||||
@@ -44,7 +49,10 @@ import com.android.ddmuilib.logcat.LogFilter;
|
|||||||
import com.android.ddmuilib.logcat.LogPanel;
|
import com.android.ddmuilib.logcat.LogPanel;
|
||||||
import com.android.ddmuilib.logcat.LogPanel.ILogFilterStorageManager;
|
import com.android.ddmuilib.logcat.LogPanel.ILogFilterStorageManager;
|
||||||
|
|
||||||
|
import org.eclipse.core.runtime.IProgressMonitor;
|
||||||
import org.eclipse.jface.dialogs.MessageDialog;
|
import org.eclipse.jface.dialogs.MessageDialog;
|
||||||
|
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
|
||||||
|
import org.eclipse.jface.operation.IRunnableWithProgress;
|
||||||
import org.eclipse.jface.preference.IPreferenceStore;
|
import org.eclipse.jface.preference.IPreferenceStore;
|
||||||
import org.eclipse.jface.preference.PreferenceStore;
|
import org.eclipse.jface.preference.PreferenceStore;
|
||||||
import org.eclipse.swt.SWT;
|
import org.eclipse.swt.SWT;
|
||||||
@@ -62,6 +70,7 @@ import org.eclipse.swt.events.ShellListener;
|
|||||||
import org.eclipse.swt.graphics.Color;
|
import org.eclipse.swt.graphics.Color;
|
||||||
import org.eclipse.swt.graphics.Font;
|
import org.eclipse.swt.graphics.Font;
|
||||||
import org.eclipse.swt.graphics.FontData;
|
import org.eclipse.swt.graphics.FontData;
|
||||||
|
import org.eclipse.swt.graphics.Image;
|
||||||
import org.eclipse.swt.graphics.Rectangle;
|
import org.eclipse.swt.graphics.Rectangle;
|
||||||
import org.eclipse.swt.layout.FillLayout;
|
import org.eclipse.swt.layout.FillLayout;
|
||||||
import org.eclipse.swt.layout.FormAttachment;
|
import org.eclipse.swt.layout.FormAttachment;
|
||||||
@@ -72,6 +81,7 @@ import org.eclipse.swt.layout.GridLayout;
|
|||||||
import org.eclipse.swt.widgets.Composite;
|
import org.eclipse.swt.widgets.Composite;
|
||||||
import org.eclipse.swt.widgets.Display;
|
import org.eclipse.swt.widgets.Display;
|
||||||
import org.eclipse.swt.widgets.Event;
|
import org.eclipse.swt.widgets.Event;
|
||||||
|
import org.eclipse.swt.widgets.FileDialog;
|
||||||
import org.eclipse.swt.widgets.Label;
|
import org.eclipse.swt.widgets.Label;
|
||||||
import org.eclipse.swt.widgets.Listener;
|
import org.eclipse.swt.widgets.Listener;
|
||||||
import org.eclipse.swt.widgets.Menu;
|
import org.eclipse.swt.widgets.Menu;
|
||||||
@@ -164,6 +174,7 @@ public class UIThread implements IUiSelectionListener {
|
|||||||
private ToolItem mTBShowHeapUpdates;
|
private ToolItem mTBShowHeapUpdates;
|
||||||
private ToolItem mTBHalt;
|
private ToolItem mTBHalt;
|
||||||
private ToolItem mTBCauseGc;
|
private ToolItem mTBCauseGc;
|
||||||
|
private ToolItem mTBDumpHprof;
|
||||||
|
|
||||||
private ImageLoader mDdmsImageLoader;
|
private ImageLoader mDdmsImageLoader;
|
||||||
private ImageLoader mDdmuiLibImageLoader;
|
private ImageLoader mDdmuiLibImageLoader;
|
||||||
@@ -241,6 +252,7 @@ public class UIThread implements IUiSelectionListener {
|
|||||||
|
|
||||||
private EventLogPanel mEventLogPanel;
|
private EventLogPanel mEventLogPanel;
|
||||||
|
|
||||||
|
|
||||||
private class TableFocusListener implements ITableFocusListener {
|
private class TableFocusListener implements ITableFocusListener {
|
||||||
|
|
||||||
private IFocusedTableActivator mCurrentActivator;
|
private IFocusedTableActivator mCurrentActivator;
|
||||||
@@ -280,6 +292,99 @@ public class UIThread implements IUiSelectionListener {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class HProfHandler implements IHprofDumpHandler {
|
||||||
|
|
||||||
|
private final Shell mParentShell;
|
||||||
|
|
||||||
|
public HProfHandler(Shell parentShell) {
|
||||||
|
mParentShell = parentShell;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onFailure(final Client client) {
|
||||||
|
mDisplay.asyncExec(new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
MessageDialog.openError(mParentShell, "HPROF Error",
|
||||||
|
String.format(
|
||||||
|
"Unable to create HPROF file for application '%1$s'.\n" +
|
||||||
|
"Check logcat for more information.",
|
||||||
|
client.getClientData().getClientDescription()));
|
||||||
|
} finally {
|
||||||
|
// this will make sure the dump hprof button is re-enabled for the
|
||||||
|
// current selection. as the client is finished dumping an hprof file
|
||||||
|
enableButtons();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onSuccess(final String file, final Client client) {
|
||||||
|
mDisplay.asyncExec(new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
final IDevice device = client.getDevice();
|
||||||
|
try {
|
||||||
|
// get the sync service to pull the HPROF file
|
||||||
|
final SyncService sync = client.getDevice().getSyncService();
|
||||||
|
if (sync != null) {
|
||||||
|
promptAndPull(device, client, sync, file);
|
||||||
|
} else {
|
||||||
|
MessageDialog.openError(mParentShell, "HPROF Error",
|
||||||
|
String.format(
|
||||||
|
"Unable to download HPROF file from device '%1$s'.",
|
||||||
|
device.getSerialNumber()));
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
MessageDialog.openError(mParentShell, "HPROF Error",
|
||||||
|
String.format("Unable to download HPROF file from device '%1$s'.",
|
||||||
|
device.getSerialNumber()));
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
// this will make sure the dump hprof button is re-enabled for the
|
||||||
|
// current selection. as the client is finished dumping an hprof file
|
||||||
|
enableButtons();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void promptAndPull(final IDevice device, final Client client,
|
||||||
|
final SyncService sync, final String remoteFile) {
|
||||||
|
try {
|
||||||
|
FileDialog fileDialog = new FileDialog(mParentShell, SWT.SAVE);
|
||||||
|
|
||||||
|
fileDialog.setText("Save HPROF file");
|
||||||
|
fileDialog.setFileName(
|
||||||
|
client.getClientData().getClientDescription() + ".hprof");
|
||||||
|
|
||||||
|
final String localFileName = fileDialog.open();
|
||||||
|
if (localFileName != null) {
|
||||||
|
final File localFile = new File(localFileName);
|
||||||
|
|
||||||
|
new ProgressMonitorDialog(mParentShell).run(true, true,
|
||||||
|
new IRunnableWithProgress() {
|
||||||
|
public void run(IProgressMonitor monitor) {
|
||||||
|
SyncResult result = sync.pullFile(remoteFile, localFileName,
|
||||||
|
new SyncProgressMonitor(monitor, String.format(
|
||||||
|
"Pulling %1$s from the device",
|
||||||
|
localFile.getName())));
|
||||||
|
|
||||||
|
if (result.getCode() != SyncService.RESULT_OK) {
|
||||||
|
MessageDialog.openError(mParentShell, "HPROF Error",
|
||||||
|
String.format("Failed to pull %1$s: %2$s", remoteFile,
|
||||||
|
result.getMessage()));
|
||||||
|
}
|
||||||
|
|
||||||
|
sync.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
MessageDialog.openError(mParentShell, "HPROF Error",
|
||||||
|
String.format("Unable to download HPROF file from device '%1$s'.",
|
||||||
|
device.getSerialNumber()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generic constructor.
|
* Generic constructor.
|
||||||
@@ -328,7 +433,7 @@ public class UIThread implements IUiSelectionListener {
|
|||||||
public void runUI() {
|
public void runUI() {
|
||||||
Display.setAppName("ddms");
|
Display.setAppName("ddms");
|
||||||
mDisplay = new Display();
|
mDisplay = new Display();
|
||||||
Shell shell = new Shell(mDisplay);
|
final Shell shell = new Shell(mDisplay);
|
||||||
|
|
||||||
// create the image loaders for DDMS and DDMUILIB
|
// create the image loaders for DDMS and DDMUILIB
|
||||||
mDdmsImageLoader = new ImageLoader(this.getClass());
|
mDdmsImageLoader = new ImageLoader(this.getClass());
|
||||||
@@ -360,6 +465,9 @@ public class UIThread implements IUiSelectionListener {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// set the handler for hprof dump
|
||||||
|
ClientData.setHprofDumpHandler(new HProfHandler(shell));
|
||||||
|
|
||||||
// [try to] ensure ADB is running
|
// [try to] ensure ADB is running
|
||||||
String adbLocation = System.getProperty("com.android.ddms.bindir"); //$NON-NLS-1$
|
String adbLocation = System.getProperty("com.android.ddms.bindir"); //$NON-NLS-1$
|
||||||
if (adbLocation != null && adbLocation.length() != 0) {
|
if (adbLocation != null && adbLocation.length() != 0) {
|
||||||
@@ -965,6 +1073,23 @@ public class UIThread implements IUiSelectionListener {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// add "cause GC" button
|
||||||
|
mTBDumpHprof = new ToolItem(toolBar, SWT.PUSH);
|
||||||
|
mTBDumpHprof.setToolTipText("Dump HPROF file");
|
||||||
|
mTBDumpHprof.setEnabled(false);
|
||||||
|
mTBDumpHprof.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, display,
|
||||||
|
DevicePanel.ICON_HPROF, DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
|
||||||
|
mTBDumpHprof.addSelectionListener(new SelectionAdapter() {
|
||||||
|
@Override
|
||||||
|
public void widgetSelected(SelectionEvent e) {
|
||||||
|
mDevicePanel.dumpHprof();
|
||||||
|
|
||||||
|
// this will make sure the dump hprof button is disabled for the current selection
|
||||||
|
// as the client is already dumping an hprof file
|
||||||
|
enableButtons();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
toolBar.pack();
|
toolBar.pack();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1305,15 +1430,33 @@ public class UIThread implements IUiSelectionListener {
|
|||||||
|
|
||||||
ToolItemAction pullAction = new ToolItemAction(toolBar, SWT.PUSH);
|
ToolItemAction pullAction = new ToolItemAction(toolBar, SWT.PUSH);
|
||||||
pullAction.item.setToolTipText("Pull File from Device");
|
pullAction.item.setToolTipText("Pull File from Device");
|
||||||
pullAction.item.setImage(mDdmuiLibImageLoader.loadImage("pull.png", mDisplay)); //$NON-NLS-1$
|
Image image = mDdmuiLibImageLoader.loadImage("pull.png", mDisplay); //$NON-NLS-1$
|
||||||
|
if (image != null) {
|
||||||
|
pullAction.item.setImage(image);
|
||||||
|
} else {
|
||||||
|
// this is for debugging purpose when the icon is missing
|
||||||
|
pullAction.item.setText("Pull"); //$NON-NLS-1$
|
||||||
|
}
|
||||||
|
|
||||||
ToolItemAction pushAction = new ToolItemAction(toolBar, SWT.PUSH);
|
ToolItemAction pushAction = new ToolItemAction(toolBar, SWT.PUSH);
|
||||||
pushAction.item.setToolTipText("Push file onto Device");
|
pushAction.item.setToolTipText("Push file onto Device");
|
||||||
pushAction.item.setImage(mDdmuiLibImageLoader.loadImage("push.png", mDisplay)); //$NON-NLS-1$
|
image = mDdmuiLibImageLoader.loadImage("push.png", mDisplay); //$NON-NLS-1$
|
||||||
|
if (image != null) {
|
||||||
|
pushAction.item.setImage(image);
|
||||||
|
} else {
|
||||||
|
// this is for debugging purpose when the icon is missing
|
||||||
|
pushAction.item.setText("Push"); //$NON-NLS-1$
|
||||||
|
}
|
||||||
|
|
||||||
ToolItemAction deleteAction = new ToolItemAction(toolBar, SWT.PUSH);
|
ToolItemAction deleteAction = new ToolItemAction(toolBar, SWT.PUSH);
|
||||||
deleteAction.item.setToolTipText("Delete");
|
deleteAction.item.setToolTipText("Delete");
|
||||||
deleteAction.item.setImage(mDdmuiLibImageLoader.loadImage("delete.png", mDisplay)); //$NON-NLS-1$
|
image = mDdmuiLibImageLoader.loadImage("delete.png", mDisplay); //$NON-NLS-1$
|
||||||
|
if (image != null) {
|
||||||
|
deleteAction.item.setImage(image);
|
||||||
|
} else {
|
||||||
|
// this is for debugging purpose when the icon is missing
|
||||||
|
deleteAction.item.setText("Delete"); //$NON-NLS-1$
|
||||||
|
}
|
||||||
|
|
||||||
// device explorer
|
// device explorer
|
||||||
mExplorer = new DeviceExplorer();
|
mExplorer = new DeviceExplorer();
|
||||||
@@ -1438,6 +1581,9 @@ public class UIThread implements IUiSelectionListener {
|
|||||||
mTBShowHeapUpdates.setEnabled(true);
|
mTBShowHeapUpdates.setEnabled(true);
|
||||||
mTBHalt.setEnabled(true);
|
mTBHalt.setEnabled(true);
|
||||||
mTBCauseGc.setEnabled(true);
|
mTBCauseGc.setEnabled(true);
|
||||||
|
mTBDumpHprof.setEnabled(
|
||||||
|
mCurrentClient.getClientData().hasFeature(ClientData.FEATURE_HPROF) &&
|
||||||
|
mCurrentClient.getClientData().hasPendingHprofDump() == false);
|
||||||
} else {
|
} else {
|
||||||
// list is empty, disable these
|
// list is empty, disable these
|
||||||
mTBShowThreadUpdates.setSelection(false);
|
mTBShowThreadUpdates.setSelection(false);
|
||||||
@@ -1446,6 +1592,7 @@ public class UIThread implements IUiSelectionListener {
|
|||||||
mTBShowHeapUpdates.setEnabled(false);
|
mTBShowHeapUpdates.setEnabled(false);
|
||||||
mTBHalt.setEnabled(false);
|
mTBHalt.setEnabled(false);
|
||||||
mTBCauseGc.setEnabled(false);
|
mTBCauseGc.setEnabled(false);
|
||||||
|
mTBDumpHprof.setEnabled(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -223,6 +223,20 @@ public class Client {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes the VM dump an HPROF file
|
||||||
|
*/
|
||||||
|
public void dumpHprof() {
|
||||||
|
try {
|
||||||
|
String file = "/sdcard/" + mClientData.getClientDescription().replaceAll("\\:", ".") +
|
||||||
|
".hprof";
|
||||||
|
HandleHeap.sendHPDU(this, file);
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.w("ddms", "Send of HPDU message failed");
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enables or disables the thread update.
|
* Enables or disables the thread update.
|
||||||
* <p/>If <code>true</code> the VM will be able to send thread information. Thread information
|
* <p/>If <code>true</code> the VM will be able to send thread information. Thread information
|
||||||
|
|||||||
@@ -112,6 +112,8 @@ public class ClientData {
|
|||||||
*/
|
*/
|
||||||
public final static String FEATURE_HPROF = "hprof-heap-dump"; // $NON-NLS-1$
|
public final static String FEATURE_HPROF = "hprof-heap-dump"; // $NON-NLS-1$
|
||||||
|
|
||||||
|
private static IHprofDumpHandler sHprofDumpHandler;
|
||||||
|
|
||||||
// is this a DDM-aware client?
|
// is this a DDM-aware client?
|
||||||
private boolean mIsDdmAware;
|
private boolean mIsDdmAware;
|
||||||
|
|
||||||
@@ -127,7 +129,7 @@ public class ClientData {
|
|||||||
// how interested are we in a debugger?
|
// how interested are we in a debugger?
|
||||||
private int mDebuggerInterest;
|
private int mDebuggerInterest;
|
||||||
|
|
||||||
// List of supported feature by the client.
|
// List of supported features by the client.
|
||||||
private final HashSet<String> mFeatures = new HashSet<String>();
|
private final HashSet<String> mFeatures = new HashSet<String>();
|
||||||
|
|
||||||
// Thread tracking (THCR, THDE).
|
// Thread tracking (THCR, THDE).
|
||||||
@@ -156,6 +158,8 @@ public class ClientData {
|
|||||||
private AllocationInfo[] mAllocations;
|
private AllocationInfo[] mAllocations;
|
||||||
private int mAllocationStatus = ALLOCATION_TRACKING_UNKNOWN;
|
private int mAllocationStatus = ALLOCATION_TRACKING_UNKNOWN;
|
||||||
|
|
||||||
|
private String mPendingHprofDump;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Heap Information.
|
* Heap Information.
|
||||||
* <p/>The heap is composed of several {@link HeapSegment} objects.
|
* <p/>The heap is composed of several {@link HeapSegment} objects.
|
||||||
@@ -252,10 +256,36 @@ public class ClientData {
|
|||||||
public Map<Integer, ArrayList<HeapSegmentElement>> getProcessedHeapMap() {
|
public Map<Integer, ArrayList<HeapSegmentElement>> getProcessedHeapMap() {
|
||||||
return mProcessedHeapMap;
|
return mProcessedHeapMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handlers able to act on HPROF dumps.
|
||||||
|
*/
|
||||||
|
public interface IHprofDumpHandler {
|
||||||
|
/**
|
||||||
|
* Called when a HPROF dump succeeded.
|
||||||
|
* @param remoteFile the device-side filename of the HPROF file.
|
||||||
|
* @param client the client for which the HPROF file was.
|
||||||
|
*/
|
||||||
|
void onSuccess(String remoteFile, Client client);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the HPROF dump failed.
|
||||||
|
* @param client the client for which the HPROF file was.
|
||||||
|
*/
|
||||||
|
void onFailure(Client client);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the handler to receive notifications when an HPROF dump succeeded or failed.
|
||||||
|
*/
|
||||||
|
public static void setHprofDumpHandler(IHprofDumpHandler handler) {
|
||||||
|
sHprofDumpHandler = handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
static IHprofDumpHandler getHprofDumpHandler() {
|
||||||
|
return sHprofDumpHandler;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generic constructor.
|
* Generic constructor.
|
||||||
@@ -530,5 +560,17 @@ public class ClientData {
|
|||||||
public boolean hasFeature(String feature) {
|
public boolean hasFeature(String feature) {
|
||||||
return mFeatures.contains(feature);
|
return mFeatures.contains(feature);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setPendingHprofDump(String pendingHprofDump) {
|
||||||
|
mPendingHprofDump = pendingHprofDump;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getPendingHprofDump() {
|
||||||
|
return mPendingHprofDump;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasPendingHprofDump() {
|
||||||
|
return mPendingHprofDump != null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package com.android.ddmlib;
|
package com.android.ddmlib;
|
||||||
|
|
||||||
|
import com.android.ddmlib.ClientData.IHprofDumpHandler;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.BufferUnderflowException;
|
import java.nio.BufferUnderflowException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
@@ -243,6 +245,7 @@ final class HandleHeap extends ChunkHandler {
|
|||||||
finishChunkPacket(packet, CHUNK_HPDU, buf.position());
|
finishChunkPacket(packet, CHUNK_HPDU, buf.position());
|
||||||
Log.d("ddm-heap", "Sending " + name(CHUNK_HPDU) + " '" + fileName +"'");
|
Log.d("ddm-heap", "Sending " + name(CHUNK_HPDU) + " '" + fileName +"'");
|
||||||
client.sendAndConsume(packet, mInst);
|
client.sendAndConsume(packet, mInst);
|
||||||
|
client.getClientData().setPendingHprofDump(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -251,13 +254,24 @@ final class HandleHeap extends ChunkHandler {
|
|||||||
private void handleHPDU(Client client, ByteBuffer data) {
|
private void handleHPDU(Client client, ByteBuffer data) {
|
||||||
byte result;
|
byte result;
|
||||||
|
|
||||||
|
// get the filename and make the client not have pending HPROF dump anymore.
|
||||||
|
String filename = client.getClientData().getPendingHprofDump();
|
||||||
|
client.getClientData().setPendingHprofDump(null);
|
||||||
|
|
||||||
|
// get the dump result
|
||||||
result = data.get();
|
result = data.get();
|
||||||
|
|
||||||
if (result == 0) {
|
// get the app-level handler for HPROF dump
|
||||||
Log.i("ddm-heap", "Heap dump request has finished");
|
IHprofDumpHandler handler = ClientData.getHprofDumpHandler();
|
||||||
// TODO: stuff
|
if (handler != null) {
|
||||||
} else {
|
if (result == 0) {
|
||||||
Log.w("ddm-heap", "Heap dump request failed (check device log)");
|
handler.onSuccess(filename, client);
|
||||||
|
|
||||||
|
Log.i("ddm-heap", "Heap dump request has finished");
|
||||||
|
} else {
|
||||||
|
handler.onFailure(client);
|
||||||
|
Log.w("ddm-heap", "Heap dump request failed (check device log)");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -317,7 +331,7 @@ final class HandleHeap extends ChunkHandler {
|
|||||||
|
|
||||||
enabled = (data.get() != 0);
|
enabled = (data.get() != 0);
|
||||||
Log.d("ddm-heap", "REAQ says: enabled=" + enabled);
|
Log.d("ddm-heap", "REAQ says: enabled=" + enabled);
|
||||||
|
|
||||||
client.getClientData().setAllocationStatus(enabled);
|
client.getClientData().setAllocationStatus(enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -359,7 +373,7 @@ final class HandleHeap extends ChunkHandler {
|
|||||||
str = "double";
|
str = "double";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// now add the array part
|
// now add the array part
|
||||||
for (int a = 0 ; a < array; a++) {
|
for (int a = 0 ; a < array; a++) {
|
||||||
str = str + "[]";
|
str = str + "[]";
|
||||||
@@ -410,7 +424,7 @@ final class HandleHeap extends ChunkHandler {
|
|||||||
* (xb) class name strings
|
* (xb) class name strings
|
||||||
* (xb) method name strings
|
* (xb) method name strings
|
||||||
* (xb) source file strings
|
* (xb) source file strings
|
||||||
*
|
*
|
||||||
* As with other DDM traffic, strings are sent as a 4-byte length
|
* As with other DDM traffic, strings are sent as a 4-byte length
|
||||||
* followed by UTF-16 data.
|
* followed by UTF-16 data.
|
||||||
*/
|
*/
|
||||||
@@ -498,10 +512,10 @@ final class HandleHeap extends ChunkHandler {
|
|||||||
list.add(new AllocationInfo(classNames[classNameIndex],
|
list.add(new AllocationInfo(classNames[classNameIndex],
|
||||||
totalSize, (short) threadId, steArray));
|
totalSize, (short) threadId, steArray));
|
||||||
}
|
}
|
||||||
|
|
||||||
// sort biggest allocations first.
|
// sort biggest allocations first.
|
||||||
Collections.sort(list);
|
Collections.sort(list);
|
||||||
|
|
||||||
client.getClientData().setAllocations(list.toArray(new AllocationInfo[numEntries]));
|
client.getClientData().setAllocations(list.toArray(new AllocationInfo[numEntries]));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -521,11 +535,11 @@ final class HandleHeap extends ChunkHandler {
|
|||||||
|
|
||||||
for (StackTraceElement ste: rec.getStackTrace()) {
|
for (StackTraceElement ste: rec.getStackTrace()) {
|
||||||
if (ste.isNativeMethod()) {
|
if (ste.isNativeMethod()) {
|
||||||
System.out.println(" " + ste.getClassName()
|
System.out.println(" " + ste.getClassName()
|
||||||
+ "." + ste.getMethodName()
|
+ "." + ste.getMethodName()
|
||||||
+ " (Native method)");
|
+ " (Native method)");
|
||||||
} else {
|
} else {
|
||||||
System.out.println(" " + ste.getClassName()
|
System.out.println(" " + ste.getClassName()
|
||||||
+ "." + ste.getMethodName()
|
+ "." + ste.getMethodName()
|
||||||
+ " (" + ste.getFileName()
|
+ " (" + ste.getFileName()
|
||||||
+ ":" + ste.getLineNumber() + ")");
|
+ ":" + ste.getLineNumber() + ")");
|
||||||
|
|||||||
@@ -373,6 +373,29 @@ public final class SyncService {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pulls a single file.
|
||||||
|
* <p/>Because this method just deals with a String for the remote file instead of a
|
||||||
|
* {@link FileEntry}, the size of the file being pulled is unknown and the
|
||||||
|
* {@link ISyncProgressMonitor} will not properly show the progress
|
||||||
|
* @param remoteFilepath the full path to the remote file
|
||||||
|
* @param localFilename The local destination.
|
||||||
|
* @param monitor The progress monitor. Cannot be null.
|
||||||
|
* @return a {@link SyncResult} object with a code and an optional message.
|
||||||
|
*
|
||||||
|
* @see #getNullProgressMonitor()
|
||||||
|
*/
|
||||||
|
public SyncResult pullFile(String remoteFilepath, String localFilename,
|
||||||
|
ISyncProgressMonitor monitor) {
|
||||||
|
monitor.start(0);
|
||||||
|
//TODO: use the {@link FileListingService} to get the file size.
|
||||||
|
|
||||||
|
SyncResult result = doPullFile(remoteFilepath, localFilename, monitor);
|
||||||
|
|
||||||
|
monitor.stop();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Push several files.
|
* Push several files.
|
||||||
* @param local An array of loca files to push
|
* @param local An array of loca files to push
|
||||||
@@ -798,7 +821,6 @@ public final class SyncService {
|
|||||||
return new SyncResult(RESULT_OK);
|
return new SyncResult(RESULT_OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the mode of the remote file.
|
* Returns the mode of the remote file.
|
||||||
* @param path the remote file
|
* @param path the remote file
|
||||||
|
|||||||
@@ -75,6 +75,7 @@ public final class DevicePanel extends Panel implements IDebugBridgeChangeListen
|
|||||||
public final static String ICON_HEAP = "heap.png"; //$NON-NLS-1$
|
public final static String ICON_HEAP = "heap.png"; //$NON-NLS-1$
|
||||||
public final static String ICON_HALT = "halt.png"; //$NON-NLS-1$
|
public final static String ICON_HALT = "halt.png"; //$NON-NLS-1$
|
||||||
public final static String ICON_GC = "gc.png"; //$NON-NLS-1$
|
public final static String ICON_GC = "gc.png"; //$NON-NLS-1$
|
||||||
|
public final static String ICON_HPROF = "hprof.png"; //$NON-NLS-1$
|
||||||
|
|
||||||
private IDevice mCurrentDevice;
|
private IDevice mCurrentDevice;
|
||||||
private Client mCurrentClient;
|
private Client mCurrentClient;
|
||||||
@@ -423,6 +424,13 @@ public final class DevicePanel extends Panel implements IDebugBridgeChangeListen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void dumpHprof() {
|
||||||
|
if (mCurrentClient != null) {
|
||||||
|
mCurrentClient.dumpHprof();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public void setEnabledHeapOnSelectedClient(boolean enable) {
|
public void setEnabledHeapOnSelectedClient(boolean enable) {
|
||||||
if (mCurrentClient != null) {
|
if (mCurrentClient != null) {
|
||||||
mCurrentClient.setHeapUpdateEnabled(enable);
|
mCurrentClient.setHeapUpdateEnabled(enable);
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2009 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.ddmuilib;
|
||||||
|
|
||||||
|
import com.android.ddmlib.SyncService.ISyncProgressMonitor;
|
||||||
|
|
||||||
|
import org.eclipse.core.runtime.IProgressMonitor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of the {@link ISyncProgressMonitor} wrapping an Eclipse {@link IProgressMonitor}.
|
||||||
|
*/
|
||||||
|
public class SyncProgressMonitor implements ISyncProgressMonitor {
|
||||||
|
|
||||||
|
private IProgressMonitor mMonitor;
|
||||||
|
private String mName;
|
||||||
|
|
||||||
|
public SyncProgressMonitor(IProgressMonitor monitor, String name) {
|
||||||
|
mMonitor = monitor;
|
||||||
|
mName = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void start(int totalWork) {
|
||||||
|
mMonitor.beginTask(mName, totalWork);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stop() {
|
||||||
|
mMonitor.done();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void advance(int work) {
|
||||||
|
mMonitor.worked(work);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isCanceled() {
|
||||||
|
return mMonitor.isCanceled();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void startSubTask(String name) {
|
||||||
|
mMonitor.subTask(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -25,6 +25,7 @@ import com.android.ddmlib.SyncService.ISyncProgressMonitor;
|
|||||||
import com.android.ddmlib.SyncService.SyncResult;
|
import com.android.ddmlib.SyncService.SyncResult;
|
||||||
import com.android.ddmuilib.DdmUiPreferences;
|
import com.android.ddmuilib.DdmUiPreferences;
|
||||||
import com.android.ddmuilib.Panel;
|
import com.android.ddmuilib.Panel;
|
||||||
|
import com.android.ddmuilib.SyncProgressMonitor;
|
||||||
import com.android.ddmuilib.TableHelper;
|
import com.android.ddmuilib.TableHelper;
|
||||||
import com.android.ddmuilib.actions.ICommonAction;
|
import com.android.ddmuilib.actions.ICommonAction;
|
||||||
import com.android.ddmuilib.console.DdmConsole;
|
import com.android.ddmuilib.console.DdmConsole;
|
||||||
@@ -103,41 +104,6 @@ public class DeviceExplorer extends Panel {
|
|||||||
|
|
||||||
private String mDefaultSave;
|
private String mDefaultSave;
|
||||||
|
|
||||||
/**
|
|
||||||
* Implementation of the SyncService.ISyncProgressMonitor. It wraps a jFace IProgressMonitor
|
|
||||||
* and just forward the calls to the jFace object.
|
|
||||||
*/
|
|
||||||
private static class SyncProgressMonitor implements ISyncProgressMonitor {
|
|
||||||
|
|
||||||
private IProgressMonitor mMonitor;
|
|
||||||
private String mName;
|
|
||||||
|
|
||||||
SyncProgressMonitor(IProgressMonitor monitor, String name) {
|
|
||||||
mMonitor = monitor;
|
|
||||||
mName = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void start(int totalWork) {
|
|
||||||
mMonitor.beginTask(mName, totalWork);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void stop() {
|
|
||||||
mMonitor.done();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void advance(int work) {
|
|
||||||
mMonitor.worked(work);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isCanceled() {
|
|
||||||
return mMonitor.isCanceled();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void startSubTask(String name) {
|
|
||||||
mMonitor.subTask(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public DeviceExplorer() {
|
public DeviceExplorer() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
tools/ddms/libs/ddmuilib/src/resources/images/hprof.png
Normal file
BIN
tools/ddms/libs/ddmuilib/src/resources/images/hprof.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 317 B |
1
tools/eclipse/plugins/.gitignore
vendored
1
tools/eclipse/plugins/.gitignore
vendored
@@ -30,6 +30,7 @@ com.android.ide.eclipse.ddms/icons/forward.png
|
|||||||
com.android.ide.eclipse.ddms/icons/gc.png
|
com.android.ide.eclipse.ddms/icons/gc.png
|
||||||
com.android.ide.eclipse.ddms/icons/halt.png
|
com.android.ide.eclipse.ddms/icons/halt.png
|
||||||
com.android.ide.eclipse.ddms/icons/heap.png
|
com.android.ide.eclipse.ddms/icons/heap.png
|
||||||
|
com.android.ide.eclipse.ddms/icons/hprof.png
|
||||||
com.android.ide.eclipse.ddms/icons/i.png
|
com.android.ide.eclipse.ddms/icons/i.png
|
||||||
com.android.ide.eclipse.ddms/icons/importBug.png
|
com.android.ide.eclipse.ddms/icons/importBug.png
|
||||||
com.android.ide.eclipse.ddms/icons/load.png
|
com.android.ide.eclipse.ddms/icons/load.png
|
||||||
|
|||||||
@@ -17,30 +17,41 @@
|
|||||||
|
|
||||||
package com.android.ide.eclipse.ddms.views;
|
package com.android.ide.eclipse.ddms.views;
|
||||||
|
|
||||||
|
import com.android.ddmlib.AndroidDebugBridge;
|
||||||
import com.android.ddmlib.Client;
|
import com.android.ddmlib.Client;
|
||||||
import com.android.ddmlib.ClientData;
|
import com.android.ddmlib.ClientData;
|
||||||
import com.android.ddmlib.AndroidDebugBridge;
|
|
||||||
import com.android.ddmlib.IDevice;
|
import com.android.ddmlib.IDevice;
|
||||||
|
import com.android.ddmlib.SyncService;
|
||||||
|
import com.android.ddmlib.ClientData.IHprofDumpHandler;
|
||||||
|
import com.android.ddmlib.SyncService.SyncResult;
|
||||||
import com.android.ddmuilib.DevicePanel;
|
import com.android.ddmuilib.DevicePanel;
|
||||||
import com.android.ddmuilib.ScreenShotDialog;
|
import com.android.ddmuilib.ScreenShotDialog;
|
||||||
|
import com.android.ddmuilib.SyncProgressMonitor;
|
||||||
import com.android.ddmuilib.DevicePanel.IUiSelectionListener;
|
import com.android.ddmuilib.DevicePanel.IUiSelectionListener;
|
||||||
import com.android.ide.eclipse.ddms.DdmsPlugin;
|
import com.android.ide.eclipse.ddms.DdmsPlugin;
|
||||||
import com.android.ide.eclipse.ddms.DdmsPlugin.IDebugLauncher;
|
import com.android.ide.eclipse.ddms.DdmsPlugin.IDebugLauncher;
|
||||||
|
|
||||||
|
import org.eclipse.core.runtime.IProgressMonitor;
|
||||||
import org.eclipse.jface.action.Action;
|
import org.eclipse.jface.action.Action;
|
||||||
import org.eclipse.jface.action.IAction;
|
import org.eclipse.jface.action.IAction;
|
||||||
import org.eclipse.jface.action.IMenuManager;
|
import org.eclipse.jface.action.IMenuManager;
|
||||||
import org.eclipse.jface.action.IToolBarManager;
|
import org.eclipse.jface.action.IToolBarManager;
|
||||||
import org.eclipse.jface.action.Separator;
|
import org.eclipse.jface.action.Separator;
|
||||||
import org.eclipse.jface.dialogs.MessageDialog;
|
import org.eclipse.jface.dialogs.MessageDialog;
|
||||||
|
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
|
||||||
|
import org.eclipse.jface.operation.IRunnableWithProgress;
|
||||||
|
import org.eclipse.swt.SWT;
|
||||||
import org.eclipse.swt.widgets.Composite;
|
import org.eclipse.swt.widgets.Composite;
|
||||||
import org.eclipse.swt.widgets.Display;
|
import org.eclipse.swt.widgets.Display;
|
||||||
|
import org.eclipse.swt.widgets.FileDialog;
|
||||||
import org.eclipse.swt.widgets.Shell;
|
import org.eclipse.swt.widgets.Shell;
|
||||||
import org.eclipse.ui.IActionBars;
|
import org.eclipse.ui.IActionBars;
|
||||||
import org.eclipse.ui.ISharedImages;
|
import org.eclipse.ui.ISharedImages;
|
||||||
import org.eclipse.ui.PlatformUI;
|
import org.eclipse.ui.PlatformUI;
|
||||||
import org.eclipse.ui.part.ViewPart;
|
import org.eclipse.ui.part.ViewPart;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
public class DeviceView extends ViewPart implements IUiSelectionListener {
|
public class DeviceView extends ViewPart implements IUiSelectionListener {
|
||||||
|
|
||||||
private final static boolean USE_SELECTED_DEBUG_PORT = true;
|
private final static boolean USE_SELECTED_DEBUG_PORT = true;
|
||||||
@@ -48,6 +59,8 @@ public class DeviceView extends ViewPart implements IUiSelectionListener {
|
|||||||
public static final String ID =
|
public static final String ID =
|
||||||
"com.android.ide.eclipse.ddms.views.DeviceView"; //$NON-NLS-1$
|
"com.android.ide.eclipse.ddms.views.DeviceView"; //$NON-NLS-1$
|
||||||
|
|
||||||
|
private static DeviceView sThis;
|
||||||
|
|
||||||
private DevicePanel mDeviceList;
|
private DevicePanel mDeviceList;
|
||||||
private Action mResetAdbAction;
|
private Action mResetAdbAction;
|
||||||
private Action mCaptureAction;
|
private Action mCaptureAction;
|
||||||
@@ -56,9 +69,103 @@ public class DeviceView extends ViewPart implements IUiSelectionListener {
|
|||||||
private Action mGcAction;
|
private Action mGcAction;
|
||||||
private Action mKillAppAction;
|
private Action mKillAppAction;
|
||||||
private Action mDebugAction;
|
private Action mDebugAction;
|
||||||
|
private Action mHprofAction;
|
||||||
private IDebugLauncher mDebugLauncher;
|
private IDebugLauncher mDebugLauncher;
|
||||||
|
|
||||||
private static DeviceView sThis;
|
private class HProfHandler implements IHprofDumpHandler {
|
||||||
|
|
||||||
|
private final Shell mParentShell;
|
||||||
|
|
||||||
|
public HProfHandler(Shell parentShell) {
|
||||||
|
mParentShell = parentShell;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onFailure(final Client client) {
|
||||||
|
mParentShell.getDisplay().asyncExec(new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
MessageDialog.openError(mParentShell, "HPROF Error",
|
||||||
|
String.format(
|
||||||
|
"Unable to create HPROF file for application '%1$s'.\n" +
|
||||||
|
"Check logcat for more information.",
|
||||||
|
client.getClientData().getClientDescription()));
|
||||||
|
} finally {
|
||||||
|
// this will make sure the dump hprof button is re-enabled for the
|
||||||
|
// current selection. as the client is finished dumping an hprof file
|
||||||
|
doSelectionChanged(mDeviceList.getSelectedClient());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onSuccess(final String file, final Client client) {
|
||||||
|
mParentShell.getDisplay().asyncExec(new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
final IDevice device = client.getDevice();
|
||||||
|
try {
|
||||||
|
// get the sync service to pull the HPROF file
|
||||||
|
final SyncService sync = client.getDevice().getSyncService();
|
||||||
|
if (sync != null) {
|
||||||
|
promptAndPull(device, client, sync, file);
|
||||||
|
} else {
|
||||||
|
MessageDialog.openError(mParentShell, "HPROF Error",
|
||||||
|
String.format(
|
||||||
|
"Unable to download HPROF file from device '%1$s'.",
|
||||||
|
device.getSerialNumber()));
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
MessageDialog.openError(mParentShell, "HPROF Error",
|
||||||
|
String.format("Unable to download HPROF file from device '%1$s'.",
|
||||||
|
device.getSerialNumber()));
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
// this will make sure the dump hprof button is re-enabled for the
|
||||||
|
// current selection. as the client is finished dumping an hprof file
|
||||||
|
doSelectionChanged(mDeviceList.getSelectedClient());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void promptAndPull(final IDevice device, final Client client,
|
||||||
|
final SyncService sync, final String remoteFile) {
|
||||||
|
try {
|
||||||
|
FileDialog fileDialog = new FileDialog(mParentShell, SWT.SAVE);
|
||||||
|
|
||||||
|
fileDialog.setText("Save HPROF file");
|
||||||
|
fileDialog.setFileName(
|
||||||
|
client.getClientData().getClientDescription() + ".hprof");
|
||||||
|
|
||||||
|
final String localFileName = fileDialog.open();
|
||||||
|
if (localFileName != null) {
|
||||||
|
final File localFile = new File(localFileName);
|
||||||
|
|
||||||
|
new ProgressMonitorDialog(mParentShell).run(true, true,
|
||||||
|
new IRunnableWithProgress() {
|
||||||
|
public void run(IProgressMonitor monitor) {
|
||||||
|
SyncResult result = sync.pullFile(remoteFile, localFileName,
|
||||||
|
new SyncProgressMonitor(monitor, String.format(
|
||||||
|
"Pulling %1$s from the device",
|
||||||
|
localFile.getName())));
|
||||||
|
|
||||||
|
if (result.getCode() != SyncService.RESULT_OK) {
|
||||||
|
MessageDialog.openError(mParentShell, "HPROF Error",
|
||||||
|
String.format("Failed to pull %1$s: %2$s", remoteFile,
|
||||||
|
result.getMessage()));
|
||||||
|
}
|
||||||
|
|
||||||
|
sync.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
MessageDialog.openError(mParentShell, "HPROF Error",
|
||||||
|
String.format("Unable to download HPROF file from device '%1$s'.",
|
||||||
|
device.getSerialNumber()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public DeviceView() {
|
public DeviceView() {
|
||||||
// the view is declared with allowMultiple="false" so we
|
// the view is declared with allowMultiple="false" so we
|
||||||
@@ -86,6 +193,8 @@ public class DeviceView extends ViewPart implements IUiSelectionListener {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void createPartControl(Composite parent) {
|
public void createPartControl(Composite parent) {
|
||||||
|
ClientData.setHprofDumpHandler(new HProfHandler(parent.getShell()));
|
||||||
|
|
||||||
mDeviceList = new DevicePanel(DdmsPlugin.getImageLoader(), USE_SELECTED_DEBUG_PORT);
|
mDeviceList = new DevicePanel(DdmsPlugin.getImageLoader(), USE_SELECTED_DEBUG_PORT);
|
||||||
mDeviceList.createPanel(parent);
|
mDeviceList.createPanel(parent);
|
||||||
mDeviceList.addSelectionListener(this);
|
mDeviceList.addSelectionListener(this);
|
||||||
@@ -156,6 +265,18 @@ public class DeviceView extends ViewPart implements IUiSelectionListener {
|
|||||||
mGcAction.setImageDescriptor(DdmsPlugin.getImageLoader()
|
mGcAction.setImageDescriptor(DdmsPlugin.getImageLoader()
|
||||||
.loadDescriptor(DevicePanel.ICON_GC));
|
.loadDescriptor(DevicePanel.ICON_GC));
|
||||||
|
|
||||||
|
mHprofAction = new Action() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
mDeviceList.dumpHprof();
|
||||||
|
doSelectionChanged(mDeviceList.getSelectedClient());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
mHprofAction.setText("Dump HPROF file");
|
||||||
|
mHprofAction.setToolTipText("Dump HPROF file");
|
||||||
|
mHprofAction.setImageDescriptor(DdmsPlugin.getImageLoader()
|
||||||
|
.loadDescriptor(DevicePanel.ICON_HPROF));
|
||||||
|
|
||||||
mUpdateHeapAction = new Action("Update Heap", IAction.AS_CHECK_BOX) {
|
mUpdateHeapAction = new Action("Update Heap", IAction.AS_CHECK_BOX) {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
@@ -270,6 +391,9 @@ public class DeviceView extends ViewPart implements IUiSelectionListener {
|
|||||||
|
|
||||||
mUpdateThreadAction.setEnabled(true);
|
mUpdateThreadAction.setEnabled(true);
|
||||||
mUpdateThreadAction.setChecked(selectedClient.isThreadUpdateEnabled());
|
mUpdateThreadAction.setChecked(selectedClient.isThreadUpdateEnabled());
|
||||||
|
mHprofAction.setEnabled(
|
||||||
|
selectedClient.getClientData().hasFeature(ClientData.FEATURE_HPROF) &&
|
||||||
|
selectedClient.getClientData().hasPendingHprofDump() == false);
|
||||||
} else {
|
} else {
|
||||||
if (USE_SELECTED_DEBUG_PORT) {
|
if (USE_SELECTED_DEBUG_PORT) {
|
||||||
// set the client as the debug client
|
// set the client as the debug client
|
||||||
@@ -286,6 +410,7 @@ public class DeviceView extends ViewPart implements IUiSelectionListener {
|
|||||||
mUpdateHeapAction.setEnabled(false);
|
mUpdateHeapAction.setEnabled(false);
|
||||||
mUpdateThreadAction.setEnabled(false);
|
mUpdateThreadAction.setEnabled(false);
|
||||||
mUpdateThreadAction.setChecked(false);
|
mUpdateThreadAction.setChecked(false);
|
||||||
|
mHprofAction.setEnabled(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -307,6 +432,7 @@ public class DeviceView extends ViewPart implements IUiSelectionListener {
|
|||||||
menuManager.add(mUpdateHeapAction);
|
menuManager.add(mUpdateHeapAction);
|
||||||
menuManager.add(new Separator());
|
menuManager.add(new Separator());
|
||||||
menuManager.add(mGcAction);
|
menuManager.add(mGcAction);
|
||||||
|
menuManager.add(mHprofAction);
|
||||||
menuManager.add(new Separator());
|
menuManager.add(new Separator());
|
||||||
menuManager.add(mKillAppAction);
|
menuManager.add(mKillAppAction);
|
||||||
menuManager.add(new Separator());
|
menuManager.add(new Separator());
|
||||||
@@ -321,6 +447,9 @@ public class DeviceView extends ViewPart implements IUiSelectionListener {
|
|||||||
toolBarManager.add(mUpdateThreadAction);
|
toolBarManager.add(mUpdateThreadAction);
|
||||||
toolBarManager.add(mUpdateHeapAction);
|
toolBarManager.add(mUpdateHeapAction);
|
||||||
toolBarManager.add(new Separator());
|
toolBarManager.add(new Separator());
|
||||||
|
toolBarManager.add(mGcAction);
|
||||||
|
toolBarManager.add(mHprofAction);
|
||||||
|
toolBarManager.add(new Separator());
|
||||||
toolBarManager.add(mKillAppAction);
|
toolBarManager.add(mKillAppAction);
|
||||||
toolBarManager.add(new Separator());
|
toolBarManager.add(new Separator());
|
||||||
toolBarManager.add(mCaptureAction);
|
toolBarManager.add(mCaptureAction);
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ for i in \
|
|||||||
e.png edit.png empty.png emulator.png \
|
e.png edit.png empty.png emulator.png \
|
||||||
forward.png \
|
forward.png \
|
||||||
gc.png \
|
gc.png \
|
||||||
heap.png halt.png \
|
heap.png halt.png hprof.png \
|
||||||
i.png importBug.png \
|
i.png importBug.png \
|
||||||
load.png \
|
load.png \
|
||||||
pause.png play.png pull.png push.png \
|
pause.png play.png pull.png push.png \
|
||||||
|
|||||||
Reference in New Issue
Block a user