Merge change 23546 into eclair

* changes:
  Add "Dump HPROF file" to DDMS/DDMS plugin
This commit is contained in:
Android (Google) Code Review
2009-09-02 10:54:54 -07:00
12 changed files with 457 additions and 59 deletions

View File

@@ -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);
} }
} }

View File

@@ -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

View File

@@ -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;
}
} }

View File

@@ -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,15 +254,26 @@ 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();
// get the app-level handler for HPROF dump
IHprofDumpHandler handler = ClientData.getHprofDumpHandler();
if (handler != null) {
if (result == 0) { if (result == 0) {
handler.onSuccess(filename, client);
Log.i("ddm-heap", "Heap dump request has finished"); Log.i("ddm-heap", "Heap dump request has finished");
// TODO: stuff
} else { } else {
handler.onFailure(client);
Log.w("ddm-heap", "Heap dump request failed (check device log)"); Log.w("ddm-heap", "Heap dump request failed (check device log)");
} }
} }
}
/** /**
* Sends a REAE (REcent Allocation Enable) request to the client. * Sends a REAE (REcent Allocation Enable) request to the client.

View File

@@ -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

View 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);

View File

@@ -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);
}
}

View File

@@ -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() {
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 317 B

View File

@@ -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

View File

@@ -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);

View File

@@ -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 \