Merge change 23546 into eclair
* changes: Add "Dump HPROF file" to DDMS/DDMS plugin
This commit is contained in:
@@ -18,13 +18,16 @@ package com.android.ddms;
|
||||
|
||||
import com.android.ddmlib.AndroidDebugBridge;
|
||||
import com.android.ddmlib.Client;
|
||||
import com.android.ddmlib.ClientData;
|
||||
import com.android.ddmlib.IDevice;
|
||||
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.LogLevel;
|
||||
import com.android.ddmlib.SyncService.SyncResult;
|
||||
import com.android.ddmuilib.AllocationPanel;
|
||||
import com.android.ddmuilib.DevicePanel;
|
||||
import com.android.ddmuilib.DevicePanel.IUiSelectionListener;
|
||||
import com.android.ddmuilib.EmulatorControlPanel;
|
||||
import com.android.ddmuilib.HeapPanel;
|
||||
import com.android.ddmuilib.ITableFocusListener;
|
||||
@@ -33,9 +36,11 @@ import com.android.ddmuilib.ImageLoader;
|
||||
import com.android.ddmuilib.InfoPanel;
|
||||
import com.android.ddmuilib.NativeHeapPanel;
|
||||
import com.android.ddmuilib.ScreenShotDialog;
|
||||
import com.android.ddmuilib.SyncProgressMonitor;
|
||||
import com.android.ddmuilib.SysinfoPanel;
|
||||
import com.android.ddmuilib.TablePanel;
|
||||
import com.android.ddmuilib.ThreadPanel;
|
||||
import com.android.ddmuilib.DevicePanel.IUiSelectionListener;
|
||||
import com.android.ddmuilib.actions.ToolItemAction;
|
||||
import com.android.ddmuilib.explorer.DeviceExplorer;
|
||||
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.ILogFilterStorageManager;
|
||||
|
||||
import org.eclipse.core.runtime.IProgressMonitor;
|
||||
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.PreferenceStore;
|
||||
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.Font;
|
||||
import org.eclipse.swt.graphics.FontData;
|
||||
import org.eclipse.swt.graphics.Image;
|
||||
import org.eclipse.swt.graphics.Rectangle;
|
||||
import org.eclipse.swt.layout.FillLayout;
|
||||
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.Display;
|
||||
import org.eclipse.swt.widgets.Event;
|
||||
import org.eclipse.swt.widgets.FileDialog;
|
||||
import org.eclipse.swt.widgets.Label;
|
||||
import org.eclipse.swt.widgets.Listener;
|
||||
import org.eclipse.swt.widgets.Menu;
|
||||
@@ -164,6 +174,7 @@ public class UIThread implements IUiSelectionListener {
|
||||
private ToolItem mTBShowHeapUpdates;
|
||||
private ToolItem mTBHalt;
|
||||
private ToolItem mTBCauseGc;
|
||||
private ToolItem mTBDumpHprof;
|
||||
|
||||
private ImageLoader mDdmsImageLoader;
|
||||
private ImageLoader mDdmuiLibImageLoader;
|
||||
@@ -241,6 +252,7 @@ public class UIThread implements IUiSelectionListener {
|
||||
|
||||
private EventLogPanel mEventLogPanel;
|
||||
|
||||
|
||||
private class TableFocusListener implements ITableFocusListener {
|
||||
|
||||
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.
|
||||
@@ -328,7 +433,7 @@ public class UIThread implements IUiSelectionListener {
|
||||
public void runUI() {
|
||||
Display.setAppName("ddms");
|
||||
mDisplay = new Display();
|
||||
Shell shell = new Shell(mDisplay);
|
||||
final Shell shell = new Shell(mDisplay);
|
||||
|
||||
// create the image loaders for DDMS and DDMUILIB
|
||||
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
|
||||
String adbLocation = System.getProperty("com.android.ddms.bindir"); //$NON-NLS-1$
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -1305,15 +1430,33 @@ public class UIThread implements IUiSelectionListener {
|
||||
|
||||
ToolItemAction pullAction = new ToolItemAction(toolBar, SWT.PUSH);
|
||||
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);
|
||||
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);
|
||||
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
|
||||
mExplorer = new DeviceExplorer();
|
||||
@@ -1438,6 +1581,9 @@ public class UIThread implements IUiSelectionListener {
|
||||
mTBShowHeapUpdates.setEnabled(true);
|
||||
mTBHalt.setEnabled(true);
|
||||
mTBCauseGc.setEnabled(true);
|
||||
mTBDumpHprof.setEnabled(
|
||||
mCurrentClient.getClientData().hasFeature(ClientData.FEATURE_HPROF) &&
|
||||
mCurrentClient.getClientData().hasPendingHprofDump() == false);
|
||||
} else {
|
||||
// list is empty, disable these
|
||||
mTBShowThreadUpdates.setSelection(false);
|
||||
@@ -1446,6 +1592,7 @@ public class UIThread implements IUiSelectionListener {
|
||||
mTBShowHeapUpdates.setEnabled(false);
|
||||
mTBHalt.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.
|
||||
* <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$
|
||||
|
||||
private static IHprofDumpHandler sHprofDumpHandler;
|
||||
|
||||
// is this a DDM-aware client?
|
||||
private boolean mIsDdmAware;
|
||||
|
||||
@@ -127,7 +129,7 @@ public class ClientData {
|
||||
// how interested are we in a debugger?
|
||||
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>();
|
||||
|
||||
// Thread tracking (THCR, THDE).
|
||||
@@ -156,6 +158,8 @@ public class ClientData {
|
||||
private AllocationInfo[] mAllocations;
|
||||
private int mAllocationStatus = ALLOCATION_TRACKING_UNKNOWN;
|
||||
|
||||
private String mPendingHprofDump;
|
||||
|
||||
/**
|
||||
* Heap Information.
|
||||
* <p/>The heap is composed of several {@link HeapSegment} objects.
|
||||
@@ -252,10 +256,36 @@ public class ClientData {
|
||||
public Map<Integer, ArrayList<HeapSegmentElement>> getProcessedHeapMap() {
|
||||
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.
|
||||
@@ -530,5 +560,17 @@ public class ClientData {
|
||||
public boolean hasFeature(String 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;
|
||||
|
||||
import com.android.ddmlib.ClientData.IHprofDumpHandler;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.BufferUnderflowException;
|
||||
import java.nio.ByteBuffer;
|
||||
@@ -243,6 +245,7 @@ final class HandleHeap extends ChunkHandler {
|
||||
finishChunkPacket(packet, CHUNK_HPDU, buf.position());
|
||||
Log.d("ddm-heap", "Sending " + name(CHUNK_HPDU) + " '" + fileName +"'");
|
||||
client.sendAndConsume(packet, mInst);
|
||||
client.getClientData().setPendingHprofDump(fileName);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -251,15 +254,26 @@ final class HandleHeap extends ChunkHandler {
|
||||
private void handleHPDU(Client client, ByteBuffer data) {
|
||||
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();
|
||||
|
||||
// get the app-level handler for HPROF dump
|
||||
IHprofDumpHandler handler = ClientData.getHprofDumpHandler();
|
||||
if (handler != null) {
|
||||
if (result == 0) {
|
||||
handler.onSuccess(filename, client);
|
||||
|
||||
Log.i("ddm-heap", "Heap dump request has finished");
|
||||
// TODO: stuff
|
||||
} else {
|
||||
handler.onFailure(client);
|
||||
Log.w("ddm-heap", "Heap dump request failed (check device log)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a REAE (REcent Allocation Enable) request to the client.
|
||||
|
||||
@@ -373,6 +373,29 @@ public final class SyncService {
|
||||
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.
|
||||
* @param local An array of loca files to push
|
||||
@@ -798,7 +821,6 @@ public final class SyncService {
|
||||
return new SyncResult(RESULT_OK);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the mode of 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_HALT = "halt.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 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) {
|
||||
if (mCurrentClient != null) {
|
||||
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.ddmuilib.DdmUiPreferences;
|
||||
import com.android.ddmuilib.Panel;
|
||||
import com.android.ddmuilib.SyncProgressMonitor;
|
||||
import com.android.ddmuilib.TableHelper;
|
||||
import com.android.ddmuilib.actions.ICommonAction;
|
||||
import com.android.ddmuilib.console.DdmConsole;
|
||||
@@ -103,41 +104,6 @@ public class DeviceExplorer extends Panel {
|
||||
|
||||
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() {
|
||||
|
||||
}
|
||||
|
||||
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/halt.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/importBug.png
|
||||
com.android.ide.eclipse.ddms/icons/load.png
|
||||
|
||||
@@ -17,30 +17,41 @@
|
||||
|
||||
package com.android.ide.eclipse.ddms.views;
|
||||
|
||||
import com.android.ddmlib.AndroidDebugBridge;
|
||||
import com.android.ddmlib.Client;
|
||||
import com.android.ddmlib.ClientData;
|
||||
import com.android.ddmlib.AndroidDebugBridge;
|
||||
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.ScreenShotDialog;
|
||||
import com.android.ddmuilib.SyncProgressMonitor;
|
||||
import com.android.ddmuilib.DevicePanel.IUiSelectionListener;
|
||||
import com.android.ide.eclipse.ddms.DdmsPlugin;
|
||||
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.IAction;
|
||||
import org.eclipse.jface.action.IMenuManager;
|
||||
import org.eclipse.jface.action.IToolBarManager;
|
||||
import org.eclipse.jface.action.Separator;
|
||||
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.Display;
|
||||
import org.eclipse.swt.widgets.FileDialog;
|
||||
import org.eclipse.swt.widgets.Shell;
|
||||
import org.eclipse.ui.IActionBars;
|
||||
import org.eclipse.ui.ISharedImages;
|
||||
import org.eclipse.ui.PlatformUI;
|
||||
import org.eclipse.ui.part.ViewPart;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class DeviceView extends ViewPart implements IUiSelectionListener {
|
||||
|
||||
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 =
|
||||
"com.android.ide.eclipse.ddms.views.DeviceView"; //$NON-NLS-1$
|
||||
|
||||
private static DeviceView sThis;
|
||||
|
||||
private DevicePanel mDeviceList;
|
||||
private Action mResetAdbAction;
|
||||
private Action mCaptureAction;
|
||||
@@ -56,9 +69,103 @@ public class DeviceView extends ViewPart implements IUiSelectionListener {
|
||||
private Action mGcAction;
|
||||
private Action mKillAppAction;
|
||||
private Action mDebugAction;
|
||||
private Action mHprofAction;
|
||||
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() {
|
||||
// the view is declared with allowMultiple="false" so we
|
||||
@@ -86,6 +193,8 @@ public class DeviceView extends ViewPart implements IUiSelectionListener {
|
||||
|
||||
@Override
|
||||
public void createPartControl(Composite parent) {
|
||||
ClientData.setHprofDumpHandler(new HProfHandler(parent.getShell()));
|
||||
|
||||
mDeviceList = new DevicePanel(DdmsPlugin.getImageLoader(), USE_SELECTED_DEBUG_PORT);
|
||||
mDeviceList.createPanel(parent);
|
||||
mDeviceList.addSelectionListener(this);
|
||||
@@ -156,6 +265,18 @@ public class DeviceView extends ViewPart implements IUiSelectionListener {
|
||||
mGcAction.setImageDescriptor(DdmsPlugin.getImageLoader()
|
||||
.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) {
|
||||
@Override
|
||||
public void run() {
|
||||
@@ -270,6 +391,9 @@ public class DeviceView extends ViewPart implements IUiSelectionListener {
|
||||
|
||||
mUpdateThreadAction.setEnabled(true);
|
||||
mUpdateThreadAction.setChecked(selectedClient.isThreadUpdateEnabled());
|
||||
mHprofAction.setEnabled(
|
||||
selectedClient.getClientData().hasFeature(ClientData.FEATURE_HPROF) &&
|
||||
selectedClient.getClientData().hasPendingHprofDump() == false);
|
||||
} else {
|
||||
if (USE_SELECTED_DEBUG_PORT) {
|
||||
// set the client as the debug client
|
||||
@@ -286,6 +410,7 @@ public class DeviceView extends ViewPart implements IUiSelectionListener {
|
||||
mUpdateHeapAction.setEnabled(false);
|
||||
mUpdateThreadAction.setEnabled(false);
|
||||
mUpdateThreadAction.setChecked(false);
|
||||
mHprofAction.setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -307,6 +432,7 @@ public class DeviceView extends ViewPart implements IUiSelectionListener {
|
||||
menuManager.add(mUpdateHeapAction);
|
||||
menuManager.add(new Separator());
|
||||
menuManager.add(mGcAction);
|
||||
menuManager.add(mHprofAction);
|
||||
menuManager.add(new Separator());
|
||||
menuManager.add(mKillAppAction);
|
||||
menuManager.add(new Separator());
|
||||
@@ -321,6 +447,9 @@ public class DeviceView extends ViewPart implements IUiSelectionListener {
|
||||
toolBarManager.add(mUpdateThreadAction);
|
||||
toolBarManager.add(mUpdateHeapAction);
|
||||
toolBarManager.add(new Separator());
|
||||
toolBarManager.add(mGcAction);
|
||||
toolBarManager.add(mHprofAction);
|
||||
toolBarManager.add(new Separator());
|
||||
toolBarManager.add(mKillAppAction);
|
||||
toolBarManager.add(new Separator());
|
||||
toolBarManager.add(mCaptureAction);
|
||||
|
||||
@@ -65,7 +65,7 @@ for i in \
|
||||
e.png edit.png empty.png emulator.png \
|
||||
forward.png \
|
||||
gc.png \
|
||||
heap.png halt.png \
|
||||
heap.png halt.png hprof.png \
|
||||
i.png importBug.png \
|
||||
load.png \
|
||||
pause.png play.png pull.png push.png \
|
||||
|
||||
Reference in New Issue
Block a user