From d2d24351d3414d60807b7fe6b091ebe1548d0f61 Mon Sep 17 00:00:00 2001 From: Xavier Ducrohet Date: Tue, 1 Sep 2009 16:23:16 -0700 Subject: [PATCH] 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 --- .../app/src/com/android/ddms/UIThread.java | 157 +++++++++++++++++- .../ddmlib/src/com/android/ddmlib/Client.java | 14 ++ .../src/com/android/ddmlib/ClientData.java | 48 +++++- .../src/com/android/ddmlib/HandleHeap.java | 38 +++-- .../src/com/android/ddmlib/SyncService.java | 24 ++- .../src/com/android/ddmuilib/DevicePanel.java | 8 + .../android/ddmuilib/SyncProgressMonitor.java | 55 ++++++ .../ddmuilib/explorer/DeviceExplorer.java | 36 +--- .../ddmuilib/src/resources/images/hprof.png | Bin 0 -> 317 bytes tools/eclipse/plugins/.gitignore | 1 + .../ide/eclipse/ddms/views/DeviceView.java | 133 ++++++++++++++- tools/eclipse/scripts/create_ddms_symlinks.sh | 2 +- 12 files changed, 457 insertions(+), 59 deletions(-) create mode 100644 tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/SyncProgressMonitor.java create mode 100644 tools/ddms/libs/ddmuilib/src/resources/images/hprof.png diff --git a/tools/ddms/app/src/com/android/ddms/UIThread.java b/tools/ddms/app/src/com/android/ddms/UIThread.java index 86cab208f..0e091e9df 100644 --- a/tools/ddms/app/src/com/android/ddms/UIThread.java +++ b/tools/ddms/app/src/com/android/ddms/UIThread.java @@ -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); } } diff --git a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/Client.java b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/Client.java index c8e549877..1c47b8526 100644 --- a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/Client.java +++ b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/Client.java @@ -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. *

If true the VM will be able to send thread information. Thread information diff --git a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/ClientData.java b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/ClientData.java index d396d1525..eea609ccc 100644 --- a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/ClientData.java +++ b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/ClientData.java @@ -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 mFeatures = new HashSet(); // 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. *

The heap is composed of several {@link HeapSegment} objects. @@ -252,10 +256,36 @@ public class ClientData { public Map> 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; + } } diff --git a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/HandleHeap.java b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/HandleHeap.java index 51116387e..f186899b8 100644 --- a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/HandleHeap.java +++ b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/HandleHeap.java @@ -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,13 +254,24 @@ 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(); - if (result == 0) { - Log.i("ddm-heap", "Heap dump request has finished"); - // TODO: stuff - } else { - Log.w("ddm-heap", "Heap dump request failed (check device log)"); + // 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"); + } 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); Log.d("ddm-heap", "REAQ says: enabled=" + enabled); - + client.getClientData().setAllocationStatus(enabled); } @@ -359,7 +373,7 @@ final class HandleHeap extends ChunkHandler { str = "double"; } } - + // now add the array part for (int a = 0 ; a < array; a++) { str = str + "[]"; @@ -410,7 +424,7 @@ final class HandleHeap extends ChunkHandler { * (xb) class name strings * (xb) method name strings * (xb) source file strings - * + * * As with other DDM traffic, strings are sent as a 4-byte length * followed by UTF-16 data. */ @@ -498,10 +512,10 @@ final class HandleHeap extends ChunkHandler { list.add(new AllocationInfo(classNames[classNameIndex], totalSize, (short) threadId, steArray)); } - + // sort biggest allocations first. Collections.sort(list); - + client.getClientData().setAllocations(list.toArray(new AllocationInfo[numEntries])); } @@ -521,11 +535,11 @@ final class HandleHeap extends ChunkHandler { for (StackTraceElement ste: rec.getStackTrace()) { if (ste.isNativeMethod()) { - System.out.println(" " + ste.getClassName() + System.out.println(" " + ste.getClassName() + "." + ste.getMethodName() + " (Native method)"); } else { - System.out.println(" " + ste.getClassName() + System.out.println(" " + ste.getClassName() + "." + ste.getMethodName() + " (" + ste.getFileName() + ":" + ste.getLineNumber() + ")"); diff --git a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/SyncService.java b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/SyncService.java index c1d1d3b93..7abe557da 100644 --- a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/SyncService.java +++ b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/SyncService.java @@ -373,6 +373,29 @@ public final class SyncService { return result; } + /** + * Pulls a single file. + *

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 diff --git a/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/DevicePanel.java b/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/DevicePanel.java index d9d6fa1dc..75321519d 100644 --- a/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/DevicePanel.java +++ b/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/DevicePanel.java @@ -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); diff --git a/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/SyncProgressMonitor.java b/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/SyncProgressMonitor.java new file mode 100644 index 000000000..59259849c --- /dev/null +++ b/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/SyncProgressMonitor.java @@ -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); + } +} diff --git a/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/explorer/DeviceExplorer.java b/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/explorer/DeviceExplorer.java index 4652b3186..66843a473 100644 --- a/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/explorer/DeviceExplorer.java +++ b/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/explorer/DeviceExplorer.java @@ -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() { } diff --git a/tools/ddms/libs/ddmuilib/src/resources/images/hprof.png b/tools/ddms/libs/ddmuilib/src/resources/images/hprof.png new file mode 100644 index 0000000000000000000000000000000000000000..123d062071a77d4eb7d4884d7e4f49dc05d86f4e GIT binary patch literal 317 zcmV-D0mA-?P)j@Y+K{ps+VFrnIL1JS84nY^hHG