diff --git a/tools/ddms/app/.classpath b/tools/ddms/app/.classpath index 2fa1fb7c2..1040688c3 100644 --- a/tools/ddms/app/.classpath +++ b/tools/ddms/app/.classpath @@ -1,6 +1,7 @@ + diff --git a/tools/ddms/app/src/com/android/ddms/PrefsDialog.java b/tools/ddms/app/src/com/android/ddms/PrefsDialog.java index 69c48b006..19d127367 100644 --- a/tools/ddms/app/src/com/android/ddms/PrefsDialog.java +++ b/tools/ddms/app/src/com/android/ddms/PrefsDialog.java @@ -17,6 +17,7 @@ package com.android.ddms; +import com.android.ddmlib.DdmConstants; import com.android.ddmlib.DdmPreferences; import com.android.ddmlib.Log; import com.android.ddmlib.Log.LogLevel; @@ -126,9 +127,9 @@ public final class PrefsDialog { */ public static void init() { assert mPrefStore == null; - + mPrefStore = SdkStatsService.getPreferenceStore(); - + if (mPrefStore == null) { // we have a serious issue here... Log.e("ddms", @@ -158,9 +159,9 @@ public final class PrefsDialog { String traceview = System.getProperty("com.android.ddms.bindir"); //$NON-NLS-1$ if (traceview != null && traceview.length() != 0) { - traceview += File.separator + "traceview"; //$NON-NLS-1$ + traceview += File.separator + DdmConstants.FN_TRACEVIEW; } else { - traceview = "traceview"; //$NON-NLS-1$ + traceview = DdmConstants.FN_TRACEVIEW; } DdmUiPreferences.setTraceviewLocation(traceview); diff --git a/tools/ddms/app/src/com/android/ddms/UIThread.java b/tools/ddms/app/src/com/android/ddms/UIThread.java index 0e091e9df..61df0abd5 100644 --- a/tools/ddms/app/src/com/android/ddms/UIThread.java +++ b/tools/ddms/app/src/com/android/ddms/UIThread.java @@ -22,7 +22,9 @@ import com.android.ddmlib.ClientData; import com.android.ddmlib.IDevice; import com.android.ddmlib.Log; import com.android.ddmlib.SyncService; +import com.android.ddmlib.AndroidDebugBridge.IClientChangeListener; import com.android.ddmlib.ClientData.IHprofDumpHandler; +import com.android.ddmlib.ClientData.MethodProfilingStatus; import com.android.ddmlib.Log.ILogOutput; import com.android.ddmlib.Log.LogLevel; import com.android.ddmlib.SyncService.SyncResult; @@ -36,23 +38,21 @@ 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.handler.BaseFileHandler; +import com.android.ddmuilib.handler.MethodProfilingHandler; import com.android.ddmuilib.log.event.EventLogPanel; import com.android.ddmuilib.logcat.LogColors; 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; @@ -81,7 +81,6 @@ 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; @@ -102,7 +101,7 @@ import java.util.ArrayList; * SWT application. So this class mainly builds the ui, and manages communication between the panels * when {@link IDevice} / {@link Client} selection changes. */ -public class UIThread implements IUiSelectionListener { +public class UIThread implements IUiSelectionListener, IClientChangeListener { /* * UI tab panel definitions. The constants here must match up with the array * indices in mPanels. PANEL_CLIENT_LIST is a "virtual" panel representing @@ -175,6 +174,7 @@ public class UIThread implements IUiSelectionListener { private ToolItem mTBHalt; private ToolItem mTBCauseGc; private ToolItem mTBDumpHprof; + private ToolItem mTBProfiling; private ImageLoader mDdmsImageLoader; private ImageLoader mDdmuiLibImageLoader; @@ -252,6 +252,10 @@ public class UIThread implements IUiSelectionListener { private EventLogPanel mEventLogPanel; + private Image mTracingStartImage; + + private Image mTracingStopImage; + private class TableFocusListener implements ITableFocusListener { @@ -292,23 +296,23 @@ public class UIThread implements IUiSelectionListener { } - private class HProfHandler implements IHprofDumpHandler { - - private final Shell mParentShell; + /** + * Handler for HPROF dumps. + * This will always prompt the user to save the HPROF file. + */ + private class HProfHandler extends BaseFileHandler implements IHprofDumpHandler { public HProfHandler(Shell parentShell) { - mParentShell = parentShell; + super(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())); + displayError("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 @@ -318,7 +322,7 @@ public class UIThread implements IUiSelectionListener { }); } - public void onSuccess(final String file, final Client client) { + public void onSuccess(final String remoteFilePath, final Client client) { mDisplay.asyncExec(new Runnable() { public void run() { final IDevice device = client.getDevice(); @@ -326,17 +330,21 @@ public class UIThread implements IUiSelectionListener { // get the sync service to pull the HPROF file final SyncService sync = client.getDevice().getSyncService(); if (sync != null) { - promptAndPull(device, client, sync, file); + SyncResult result = promptAndPull(sync, + client.getClientData().getClientDescription() + ".hprof", + remoteFilePath, "Save HPROF file"); + if (result != null && result.getCode() != SyncService.RESULT_OK) { + displayError( + "Unable to download HPROF file from device '%1$s'.\n\n%2$s", + device.getSerialNumber(), result.getMessage()); + } } else { - MessageDialog.openError(mParentShell, "HPROF Error", - String.format( - "Unable to download HPROF file from device '%1$s'.", - device.getSerialNumber())); + displayError("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())); + displayError("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 @@ -347,45 +355,13 @@ public class UIThread implements IUiSelectionListener { }); } - 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())); - } + private void displayError(String format, Object... args) { + MessageDialog.openError(mParentShell, "HPROF Error", + String.format(format, args)); } } + /** * Generic constructor. */ @@ -467,6 +443,7 @@ public class UIThread implements IUiSelectionListener { // set the handler for hprof dump ClientData.setHprofDumpHandler(new HProfHandler(shell)); + ClientData.setMethodProfilingHandler(new MethodProfilingHandler(shell)); // [try to] ensure ADB is running String adbLocation = System.getProperty("com.android.ddms.bindir"); //$NON-NLS-1$ @@ -479,6 +456,9 @@ public class UIThread implements IUiSelectionListener { AndroidDebugBridge.init(true /* debugger support */); AndroidDebugBridge.createBridge(adbLocation, true /* forceNewBridge */); + // we need to listen to client change to be notified of client status (profiling) change + AndroidDebugBridge.addClientChangeListener(this); + shell.setText("Dalvik Debug Monitor"); setConfirmClose(shell); createMenus(shell); @@ -1001,6 +981,58 @@ public class UIThread implements IUiSelectionListener { private void createDevicePanelToolBar(ToolBar toolBar) { Display display = toolBar.getDisplay(); + // add "show heap updates" button + mTBShowHeapUpdates = new ToolItem(toolBar, SWT.CHECK); + mTBShowHeapUpdates.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, display, + DevicePanel.ICON_HEAP, DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null)); + mTBShowHeapUpdates.setToolTipText("Show heap updates"); + mTBShowHeapUpdates.setEnabled(false); + mTBShowHeapUpdates.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + if (mCurrentClient != null) { + // boolean status = ((ToolItem)e.item).getSelection(); + // invert previous state + boolean enable = !mCurrentClient.isHeapUpdateEnabled(); + mCurrentClient.setHeapUpdateEnabled(enable); + } else { + e.doit = false; // this has no effect? + } + } + }); + + // add "dump HPROF" 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(); + } + }); + + // add "cause GC" button + mTBCauseGc = new ToolItem(toolBar, SWT.PUSH); + mTBCauseGc.setToolTipText("Cause an immediate GC"); + mTBCauseGc.setEnabled(false); + mTBCauseGc.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, display, + DevicePanel.ICON_GC, DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null)); + mTBCauseGc.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + mDevicePanel.forceGcOnSelectedClient(); + } + }); + + new ToolItem(toolBar, SWT.SEPARATOR); + // add "show thread updates" button mTBShowThreadUpdates = new ToolItem(toolBar, SWT.CHECK); mTBShowThreadUpdates.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, display, @@ -1022,23 +1054,21 @@ public class UIThread implements IUiSelectionListener { } }); - // add "show heap updates" button - mTBShowHeapUpdates = new ToolItem(toolBar, SWT.CHECK); - mTBShowHeapUpdates.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, display, - DevicePanel.ICON_HEAP, DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null)); - mTBShowHeapUpdates.setToolTipText("Show heap updates"); - mTBShowHeapUpdates.setEnabled(false); - mTBShowHeapUpdates.addSelectionListener(new SelectionAdapter() { + // add a start/stop method tracing + mTracingStartImage = ImageHelper.loadImage(mDdmuiLibImageLoader, display, + DevicePanel.ICON_TRACING_START, + DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null); + mTracingStopImage = ImageHelper.loadImage(mDdmuiLibImageLoader, display, + DevicePanel.ICON_TRACING_STOP, + DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null); + mTBProfiling = new ToolItem(toolBar, SWT.PUSH); + mTBProfiling.setToolTipText("Start Method Profiling"); + mTBProfiling.setEnabled(false); + mTBProfiling.setImage(mTracingStartImage); + mTBProfiling.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { - if (mCurrentClient != null) { - // boolean status = ((ToolItem)e.item).getSelection(); - // invert previous state - boolean enable = !mCurrentClient.isHeapUpdateEnabled(); - mCurrentClient.setHeapUpdateEnabled(enable); - } else { - e.doit = false; // this has no effect? - } + mDevicePanel.toggleMethodProfiling(); } }); @@ -1058,38 +1088,6 @@ public class UIThread implements IUiSelectionListener { } }); - new ToolItem(toolBar, SWT.SEPARATOR); - - // add "cause GC" button - mTBCauseGc = new ToolItem(toolBar, SWT.PUSH); - mTBCauseGc.setToolTipText("Cause an immediate GC in the target VM"); - mTBCauseGc.setEnabled(false); - mTBCauseGc.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, display, - DevicePanel.ICON_GC, DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null)); - mTBCauseGc.addSelectionListener(new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent e) { - mDevicePanel.forceGcOnSelectedClient(); - } - }); - - // 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(); } @@ -1581,9 +1579,31 @@ 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); + + ClientData data = mCurrentClient.getClientData(); + + if (data.hasFeature(ClientData.FEATURE_HPROF)) { + mTBDumpHprof.setEnabled(data.hasPendingHprofDump() == false); + mTBDumpHprof.setToolTipText("Dump HPROF file"); + } else { + mTBDumpHprof.setEnabled(false); + mTBDumpHprof.setToolTipText("Dump HPROF file (not supported by this VM)"); + } + + if (data.hasFeature(ClientData.FEATURE_PROFILING)) { + mTBProfiling.setEnabled(true); + if (data.getMethodProfilingStatus() == MethodProfilingStatus.ON) { + mTBProfiling.setToolTipText("Stop Method Profiling"); + mTBProfiling.setImage(mTracingStopImage); + } else { + mTBProfiling.setToolTipText("Start Method Profiling"); + mTBProfiling.setImage(mTracingStartImage); + } + } else { + mTBProfiling.setEnabled(false); + mTBProfiling.setImage(mTracingStartImage); + mTBProfiling.setToolTipText("Start Method Profiling (not supported by this VM)"); + } } else { // list is empty, disable these mTBShowThreadUpdates.setSelection(false); @@ -1592,7 +1612,13 @@ public class UIThread implements IUiSelectionListener { mTBShowHeapUpdates.setEnabled(false); mTBHalt.setEnabled(false); mTBCauseGc.setEnabled(false); + mTBDumpHprof.setEnabled(false); + mTBDumpHprof.setToolTipText("Dump HPROF file"); + + mTBProfiling.setEnabled(false); + mTBProfiling.setImage(mTracingStartImage); + mTBProfiling.setToolTipText("Start Method Profiling"); } } @@ -1635,4 +1661,18 @@ public class UIThread implements IUiSelectionListener { enableButtons(); } } + + public void clientChanged(Client client, int changeMask) { + if ((changeMask & Client.CHANGE_METHOD_PROFILING_STATUS) == + Client.CHANGE_METHOD_PROFILING_STATUS) { + if (mCurrentClient == client) { + mDisplay.asyncExec(new Runnable() { + public void run() { + // force refresh of the button enabled state. + enableButtons(); + } + }); + } + } + } } diff --git a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/AndroidDebugBridge.java b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/AndroidDebugBridge.java index 9d6294add..6b9dccc7e 100644 --- a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/AndroidDebugBridge.java +++ b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/AndroidDebugBridge.java @@ -150,7 +150,7 @@ public final class AndroidDebugBridge { * @param client the updated client. * @param changeMask the bit mask describing the changed properties. It can contain * any of the following values: {@link Client#CHANGE_INFO}, - * {@link Client#CHANGE_DEBUGGER_INTEREST}, {@link Client#CHANGE_THREAD_MODE}, + * {@link Client#CHANGE_DEBUGGER_STATUS}, {@link Client#CHANGE_THREAD_MODE}, * {@link Client#CHANGE_THREAD_DATA}, {@link Client#CHANGE_HEAP_MODE}, * {@link Client#CHANGE_HEAP_DATA}, {@link Client#CHANGE_NATIVE_HEAP_DATA} */ 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 d51c6a02c..d05fa1492 100644 --- a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/Client.java +++ b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/Client.java @@ -16,6 +16,7 @@ package com.android.ddmlib; +import com.android.ddmlib.ClientData.MethodProfilingStatus; import com.android.ddmlib.DebugPortManager.IDebugPortProvider; import com.android.ddmlib.AndroidDebugBridge.IClientChangeListener; @@ -40,32 +41,34 @@ public class Client { private static final int SERVER_PROTOCOL_VERSION = 1; /** Client change bit mask: application name change */ - public static final int CHANGE_NAME = 0x0001; - /** Client change bit mask: debugger interest change */ - public static final int CHANGE_DEBUGGER_INTEREST = 0x0002; + public static final int CHANGE_NAME = 0x0001; + /** Client change bit mask: debugger status change */ + public static final int CHANGE_DEBUGGER_STATUS = 0x0002; /** Client change bit mask: debugger port change */ - public static final int CHANGE_PORT = 0x0004; + public static final int CHANGE_PORT = 0x0004; /** Client change bit mask: thread update flag change */ - public static final int CHANGE_THREAD_MODE = 0x0008; + public static final int CHANGE_THREAD_MODE = 0x0008; /** Client change bit mask: thread data updated */ - public static final int CHANGE_THREAD_DATA = 0x0010; + public static final int CHANGE_THREAD_DATA = 0x0010; /** Client change bit mask: heap update flag change */ - public static final int CHANGE_HEAP_MODE = 0x0020; + public static final int CHANGE_HEAP_MODE = 0x0020; /** Client change bit mask: head data updated */ - public static final int CHANGE_HEAP_DATA = 0x0040; + public static final int CHANGE_HEAP_DATA = 0x0040; /** Client change bit mask: native heap data updated */ - public static final int CHANGE_NATIVE_HEAP_DATA = 0x0080; + public static final int CHANGE_NATIVE_HEAP_DATA = 0x0080; /** Client change bit mask: thread stack trace updated */ - public static final int CHANGE_THREAD_STACKTRACE = 0x0100; + public static final int CHANGE_THREAD_STACKTRACE = 0x0100; /** Client change bit mask: allocation information updated */ - public static final int CHANGE_HEAP_ALLOCATIONS = 0x0200; + public static final int CHANGE_HEAP_ALLOCATIONS = 0x0200; /** Client change bit mask: allocation information updated */ - public static final int CHANGE_HEAP_ALLOCATION_STATUS = 0x0400; + public static final int CHANGE_HEAP_ALLOCATION_STATUS = 0x0400; + /** Client change bit mask: allocation information updated */ + public static final int CHANGE_METHOD_PROFILING_STATUS = 0x0800; /** Client change bit mask: combination of {@link Client#CHANGE_NAME}, - * {@link Client#CHANGE_DEBUGGER_INTEREST}, and {@link Client#CHANGE_PORT}. + * {@link Client#CHANGE_DEBUGGER_STATUS}, and {@link Client#CHANGE_PORT}. */ - public static final int CHANGE_INFO = CHANGE_NAME | CHANGE_DEBUGGER_INTEREST | CHANGE_PORT; + public static final int CHANGE_INFO = CHANGE_NAME | CHANGE_DEBUGGER_STATUS | CHANGE_PORT; private SocketChannel mChan; @@ -228,7 +231,7 @@ public class Client { */ public void dumpHprof() { try { - String file = "/sdcard/" + mClientData.getClientDescription().replaceAll("\\:", ".") + + String file = "/sdcard/" + mClientData.getClientDescription().replaceAll("\\:.*", "") + ".hprof"; HandleHeap.sendHPDU(this, file); } catch (IOException e) { @@ -237,6 +240,38 @@ public class Client { } } + public void toggleMethodProfiling() { + try { + if (mClientData.getMethodProfilingStatus() == MethodProfilingStatus.ON) { + HandleProfiling.sendMPRE(this); + } else { + String file = "/sdcard/" + mClientData.getClientDescription().replaceAll("\\:.*", "") + + ".trace"; + HandleProfiling.sendMPRS(this, file, 8*1024*1024, 0 /*flags*/); + } + } catch (IOException e) { + Log.w("ddms", "Toggle method profiling failed"); + // ignore + } + } + + /** + * Sends a request to the VM to send the enable status of the method profiling. + * This is asynchronous. + *

The allocation status can be accessed by {@link ClientData#getAllocationStatus()}. + * The notification that the new status is available will be received through + * {@link IClientChangeListener#clientChanged(Client, int)} with a changeMask + * containing the mask {@link #CHANGE_HEAP_ALLOCATION_STATUS}. + */ + public void requestMethodProfilingStatus() { + try { + HandleHeap.sendREAQ(this); + } catch (IOException e) { + Log.e("ddmlib", e); + } + } + + /** * 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 eea609ccc..356f5d09b 100644 --- a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/ClientData.java +++ b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/ClientData.java @@ -51,33 +51,50 @@ public class ClientData { /** Temporary name of VM to be ignored. */ private final static String PRE_INITIALIZED = ""; //$NON-NLS-1$ - /** Debugger connection status: not waiting on one, not connected to one, but accepting - * new connections. This is the default value. */ - public static final int DEBUGGER_DEFAULT = 1; - /** - * Debugger connection status: the application's VM is paused, waiting for a debugger to - * connect to it before resuming. */ - public static final int DEBUGGER_WAITING = 2; - /** Debugger connection status : Debugger is connected */ - public static final int DEBUGGER_ATTACHED = 3; - /** Debugger connection status: The listening port for debugger connection failed to listen. - * No debugger will be able to connect. */ - public static final int DEBUGGER_ERROR = 4; + public static enum DebuggerStatus { + /** Debugger connection status: not waiting on one, not connected to one, but accepting + * new connections. This is the default value. */ + DEFAULT, + /** + * Debugger connection status: the application's VM is paused, waiting for a debugger to + * connect to it before resuming. */ + WAITING, + /** Debugger connection status : Debugger is connected */ + ATTACHED, + /** Debugger connection status: The listening port for debugger connection failed to listen. + * No debugger will be able to connect. */ + ERROR; + } - /** - * Allocation tracking status: unknown. - *

This happens right after a {@link Client} is discovered - * by the {@link AndroidDebugBridge}, and before the {@link Client} answered the query regarding - * its allocation tracking status. - * @see Client#requestAllocationStatus() - */ - public static final int ALLOCATION_TRACKING_UNKNOWN = -1; - /** - * Allocation tracking status: the {@link Client} is not tracking allocations. */ - public static final int ALLOCATION_TRACKING_OFF = 0; - /** - * Allocation tracking status: the {@link Client} is tracking allocations. */ - public static final int ALLOCATION_TRACKING_ON = 1; + public static enum AllocationTrackingStatus { + /** + * Allocation tracking status: unknown. + *

This happens right after a {@link Client} is discovered + * by the {@link AndroidDebugBridge}, and before the {@link Client} answered the query + * regarding its allocation tracking status. + * @see Client#requestAllocationStatus() + */ + UNKNOWN, + /** Allocation tracking status: the {@link Client} is not tracking allocations. */ + OFF, + /** Allocation tracking status: the {@link Client} is tracking allocations. */ + ON; + } + + public static enum MethodProfilingStatus { + /** + * Method profiling status: unknown. + *

This happens right after a {@link Client} is discovered + * by the {@link AndroidDebugBridge}, and before the {@link Client} answered the query + * regarding its method profiling status. + * @see Client#requestMethodProfilingStatus() + */ + UNKNOWN, + /** Method profiling status: the {@link Client} is not profiling method calls. */ + OFF, + /** Method profiling status: the {@link Client} is profiling method calls. */ + ON; + } /** * Name of the value representing the max size of the heap, in the {@link Map} returned by @@ -113,6 +130,7 @@ public class ClientData { public final static String FEATURE_HPROF = "hprof-heap-dump"; // $NON-NLS-1$ private static IHprofDumpHandler sHprofDumpHandler; + private static IMethodProfilingHandler sMethodProfilingHandler; // is this a DDM-aware client? private boolean mIsDdmAware; @@ -127,7 +145,7 @@ public class ClientData { private String mClientDescription; // how interested are we in a debugger? - private int mDebuggerInterest; + private DebuggerStatus mDebuggerInterest; // List of supported features by the client. private final HashSet mFeatures = new HashSet(); @@ -156,10 +174,13 @@ public class ClientData { private int mNativeTotalMemory; private AllocationInfo[] mAllocations; - private int mAllocationStatus = ALLOCATION_TRACKING_UNKNOWN; + private AllocationTrackingStatus mAllocationStatus = AllocationTrackingStatus.UNKNOWN; private String mPendingHprofDump; + private MethodProfilingStatus mProfilingStatus = MethodProfilingStatus.UNKNOWN; + private String mPendingMethodProfiling; + /** * Heap Information. *

The heap is composed of several {@link HeapSegment} objects. @@ -264,10 +285,10 @@ public class ClientData { public interface IHprofDumpHandler { /** * Called when a HPROF dump succeeded. - * @param remoteFile the device-side filename of the HPROF file. + * @param remoteFilePath the device-side path of the HPROF file. * @param client the client for which the HPROF file was. */ - void onSuccess(String remoteFile, Client client); + void onSuccess(String remoteFilePath, Client client); /** * Called when the HPROF dump failed. @@ -276,6 +297,24 @@ public class ClientData { void onFailure(Client client); } + /** + * Handlers able to act on Method profiling info + */ + public interface IMethodProfilingHandler { + /** + * Called when a method tracing was successful. + * @param remoteFilePath the device-side path of the trace file. + * @param client the client that was profiled. + */ + void onSuccess(String remoteFilePath, Client client); + + /** + * Called when method tracing failed. + * @param client the client that was profiled. + */ + void onFailure(Client client); + } + /** * Sets the handler to receive notifications when an HPROF dump succeeded or failed. */ @@ -287,13 +326,24 @@ public class ClientData { return sHprofDumpHandler; } + /** + * Sets the handler to receive notifications when an HPROF dump succeeded or failed. + */ + public static void setMethodProfilingHandler(IMethodProfilingHandler handler) { + sMethodProfilingHandler = handler; + } + + static IMethodProfilingHandler getMethodProfilingHandler() { + return sMethodProfilingHandler; + } + /** * Generic constructor. */ ClientData(int pid) { mPid = pid; - mDebuggerInterest = DEBUGGER_DEFAULT; + mDebuggerInterest = DebuggerStatus.DEFAULT; mThreadMap = new TreeMap(); } @@ -367,18 +417,17 @@ public class ClientData { } /** - * Returns the debugger connection status. Possible values are {@link #DEBUGGER_DEFAULT}, - * {@link #DEBUGGER_WAITING}, {@link #DEBUGGER_ATTACHED}, and {@link #DEBUGGER_ERROR}. + * Returns the debugger connection status. */ - public int getDebuggerConnectionStatus() { + public DebuggerStatus getDebuggerConnectionStatus() { return mDebuggerInterest; } /** * Sets debugger connection status. */ - void setDebuggerConnectionStatus(int val) { - mDebuggerInterest = val; + void setDebuggerConnectionStatus(DebuggerStatus status) { + mDebuggerInterest = status; } /** @@ -521,15 +570,15 @@ public class ClientData { return mNativeLibMapInfo.iterator(); } - synchronized void setAllocationStatus(boolean enabled) { - mAllocationStatus = enabled ? ALLOCATION_TRACKING_ON : ALLOCATION_TRACKING_OFF; + synchronized void setAllocationStatus(AllocationTrackingStatus status) { + mAllocationStatus = status; } /** * Returns the allocation tracking status. * @see Client#requestAllocationStatus() */ - public synchronized int getAllocationStatus() { + public synchronized AllocationTrackingStatus getAllocationStatus() { return mAllocationStatus; } @@ -561,10 +610,17 @@ public class ClientData { return mFeatures.contains(feature); } + /** + * Sets the device-side path to the hprof file being written + * @param pendingHprofDump the file to the hprof file + */ void setPendingHprofDump(String pendingHprofDump) { mPendingHprofDump = pendingHprofDump; } + /** + * Returns the path to the device-side hprof file being written. + */ String getPendingHprofDump() { return mPendingHprofDump; } @@ -572,5 +628,32 @@ public class ClientData { public boolean hasPendingHprofDump() { return mPendingHprofDump != null; } + + synchronized void setMethodProfilingStatus(MethodProfilingStatus status) { + mProfilingStatus = status; + } + + /** + * Returns the method profiling status. + * @see Client#requestMethodProfilingStatus() + */ + public synchronized MethodProfilingStatus getMethodProfilingStatus() { + return mProfilingStatus; + } + + /** + * Sets the device-side path to the method profile file being written + * @param pendingMethodProfiling the file being written + */ + void setPendingMethodProfiling(String pendingMethodProfiling) { + mPendingMethodProfiling = pendingMethodProfiling; + } + + /** + * Returns the path to the device-side method profiling file being written. + */ + String getPendingMethodProfiling() { + return mPendingMethodProfiling; + } } diff --git a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/DdmConstants.java b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/DdmConstants.java new file mode 100644 index 000000000..d9823f3a7 --- /dev/null +++ b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/DdmConstants.java @@ -0,0 +1,59 @@ +/* + * 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.ddmlib; + +public final class DdmConstants { + + public final static int PLATFORM_UNKNOWN = 0; + public final static int PLATFORM_LINUX = 1; + public final static int PLATFORM_WINDOWS = 2; + public final static int PLATFORM_DARWIN = 3; + + /** + * Returns current platform, one of {@link #PLATFORM_WINDOWS}, {@link #PLATFORM_DARWIN}, + * {@link #PLATFORM_LINUX} or {@link #PLATFORM_UNKNOWN}. + */ + public final static int CURRENT_PLATFORM = currentPlatform(); + + /** hprof-conv executable (with extension for the current OS) */ + public final static String FN_HPROF_CONVERTER = (CURRENT_PLATFORM == PLATFORM_WINDOWS) ? + "hprof-conv.exe" : "hprof-conv"; //$NON-NLS-1$ //$NON-NLS-2$ + + /** traceview executable (with extension for the current OS) */ + public final static String FN_TRACEVIEW = (CURRENT_PLATFORM == PLATFORM_WINDOWS) ? + "traceview.bat" : "traceview"; //$NON-NLS-1$ //$NON-NLS-2$ + + /** + * Returns current platform + * + * @return one of {@link #PLATFORM_WINDOWS}, {@link #PLATFORM_DARWIN}, + * {@link #PLATFORM_LINUX} or {@link #PLATFORM_UNKNOWN}. + */ + public static int currentPlatform() { + String os = System.getProperty("os.name"); //$NON-NLS-1$ + if (os.startsWith("Mac OS")) { //$NON-NLS-1$ + return PLATFORM_DARWIN; + } else if (os.startsWith("Windows")) { //$NON-NLS-1$ + return PLATFORM_WINDOWS; + } else if (os.startsWith("Linux")) { //$NON-NLS-1$ + return PLATFORM_LINUX; + } + + return PLATFORM_UNKNOWN; + } + +} diff --git a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/Debugger.java b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/Debugger.java index 39ec4b5b3..cebbc32f8 100644 --- a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/Debugger.java +++ b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/Debugger.java @@ -16,6 +16,8 @@ package com.android.ddmlib; +import com.android.ddmlib.ClientData.DebuggerStatus; + import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; @@ -165,8 +167,8 @@ class Debugger { mConnState = ST_NOT_CONNECTED; ClientData cd = mClient.getClientData(); - cd.setDebuggerConnectionStatus(ClientData.DEBUGGER_DEFAULT); - mClient.update(Client.CHANGE_DEBUGGER_INTEREST); + cd.setDebuggerConnectionStatus(DebuggerStatus.DEFAULT); + mClient.update(Client.CHANGE_DEBUGGER_STATUS); } } catch (IOException ioe) { Log.w("ddms", "Failed to close data " + this); @@ -249,8 +251,8 @@ class Debugger { mConnState = ST_READY; ClientData cd = mClient.getClientData(); - cd.setDebuggerConnectionStatus(ClientData.DEBUGGER_ATTACHED); - mClient.update(Client.CHANGE_DEBUGGER_INTEREST); + cd.setDebuggerConnectionStatus(DebuggerStatus.ATTACHED); + mClient.update(Client.CHANGE_DEBUGGER_STATUS); // see if we have another packet in the buffer return getJdwpPacket(); diff --git a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/DeviceMonitor.java b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/DeviceMonitor.java index 338206751..402699cf5 100644 --- a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/DeviceMonitor.java +++ b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/DeviceMonitor.java @@ -17,6 +17,7 @@ package com.android.ddmlib; import com.android.ddmlib.AdbHelper.AdbResponse; +import com.android.ddmlib.ClientData.DebuggerStatus; import com.android.ddmlib.DebugPortManager.IDebugPortProvider; import com.android.ddmlib.IDevice.DeviceState; @@ -748,7 +749,7 @@ final class DeviceMonitor { client.listenForDebugger(debuggerPort); } } catch (IOException ioe) { - client.getClientData().setDebuggerConnectionStatus(ClientData.DEBUGGER_ERROR); + client.getClientData().setDebuggerConnectionStatus(DebuggerStatus.ERROR); Log.e("ddms", "Can't bind to local " + debuggerPort + " for debugger"); // oh well } 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 19f367b0e..23050affe 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,7 @@ package com.android.ddmlib; +import com.android.ddmlib.ClientData.AllocationTrackingStatus; import com.android.ddmlib.ClientData.IHprofDumpHandler; import java.io.IOException; @@ -93,22 +94,18 @@ final class HandleHeap extends ChunkHandler { if (type == CHUNK_HPIF) { handleHPIF(client, data); - client.update(Client.CHANGE_HEAP_DATA); } else if (type == CHUNK_HPST) { handleHPST(client, data); } else if (type == CHUNK_HPEN) { handleHPEN(client, data); - client.update(Client.CHANGE_HEAP_DATA); } else if (type == CHUNK_HPSG) { handleHPSG(client, data); } else if (type == CHUNK_HPDU) { handleHPDU(client, data); } else if (type == CHUNK_REAQ) { handleREAQ(client, data); - client.update(Client.CHANGE_HEAP_ALLOCATION_STATUS); } else if (type == CHUNK_REAL) { handleREAL(client, data); - client.update(Client.CHANGE_HEAP_ALLOCATIONS); } else { handleUnknownChunk(client, type, data, isReply, msgId); } @@ -135,6 +132,7 @@ final class HandleHeap extends ChunkHandler { client.getClientData().setHeapInfo(heapId, maxHeapSize, heapSize, bytesAllocated, objectsAllocated); + client.update(Client.CHANGE_HEAP_DATA); } } catch (BufferUnderflowException ex) { Log.w("ddm-heap", "malformed HPIF chunk from client"); @@ -176,6 +174,7 @@ final class HandleHeap extends ChunkHandler { */ //xxx todo: only seal data that belongs to the heap mentioned in . client.getClientData().getVmHeapData().sealHeapData(); + client.update(Client.CHANGE_HEAP_DATA); } /* @@ -332,7 +331,9 @@ final class HandleHeap extends ChunkHandler { enabled = (data.get() != 0); Log.d("ddm-heap", "REAQ says: enabled=" + enabled); - client.getClientData().setAllocationStatus(enabled); + client.getClientData().setAllocationStatus(enabled ? + AllocationTrackingStatus.ON : AllocationTrackingStatus.OFF); + client.update(Client.CHANGE_HEAP_ALLOCATION_STATUS); } /** @@ -517,6 +518,7 @@ final class HandleHeap extends ChunkHandler { Collections.sort(list); client.getClientData().setAllocations(list.toArray(new AllocationInfo[numEntries])); + client.update(Client.CHANGE_HEAP_ALLOCATIONS); } /* diff --git a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/HandleHello.java b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/HandleHello.java index 4818bd0a7..4e62bca29 100644 --- a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/HandleHello.java +++ b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/HandleHello.java @@ -64,6 +64,7 @@ final class HandleHello extends ChunkHandler { throws IOException { sendHELO(client, serverProtocolVersion); sendFEAT(client); + HandleProfiling.sendMPRQ(client); } /** diff --git a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/HandleProfiling.java b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/HandleProfiling.java index 5fe1ed89d..3b699736f 100644 --- a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/HandleProfiling.java +++ b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/HandleProfiling.java @@ -16,6 +16,9 @@ package com.android.ddmlib; +import com.android.ddmlib.ClientData.IMethodProfilingHandler; +import com.android.ddmlib.ClientData.MethodProfilingStatus; + import java.io.IOException; import java.nio.ByteBuffer; @@ -27,6 +30,7 @@ final class HandleProfiling extends ChunkHandler { public static final int CHUNK_MPRS = type("MPRS"); public static final int CHUNK_MPRE = type("MPRE"); public static final int CHUNK_MPRQ = type("MPRQ"); + public static final int CHUNK_FAIL = type("FAIL"); private static final HandleProfiling mInst = new HandleProfiling(); @@ -65,6 +69,8 @@ final class HandleProfiling extends ChunkHandler { handleMPRE(client, data); } else if (type == CHUNK_MPRQ) { handleMPRQ(client, data); + } else if (type == CHUNK_FAIL) { + handleFAIL(client, data); } else { handleUnknownChunk(client, type, data, isReply, msgId); } @@ -98,6 +104,13 @@ final class HandleProfiling extends ChunkHandler { Log.d("ddm-prof", "Sending " + name(CHUNK_MPRS) + " '" + fileName + "', size=" + bufferSize + ", flags=" + flags); client.sendAndConsume(packet, mInst); + + // record the filename we asked for. + client.getClientData().setPendingMethodProfiling(fileName); + + // send a status query. this ensure that the status is properly updated if for some + // reason starting the tracing failed. + sendMPRQ(client); } /** @@ -122,15 +135,28 @@ final class HandleProfiling extends ChunkHandler { private void handleMPRE(Client client, ByteBuffer data) { byte result; + // get the filename and make the client not have pending HPROF dump anymore. + String filename = client.getClientData().getPendingMethodProfiling(); + client.getClientData().setPendingMethodProfiling(null); + result = data.get(); - if (result == 0) { - Log.d("ddm-prof", "Method profiling has finished"); - } else { - Log.w("ddm-prof", "Method profiling has failed (check device log)"); + // get the app-level handler for method tracing dump + IMethodProfilingHandler handler = ClientData.getMethodProfilingHandler(); + if (handler != null) { + if (result == 0) { + handler.onSuccess(filename, client); + + Log.d("ddm-prof", "Method profiling has finished"); + } else { + handler.onFailure(client); + + Log.w("ddm-prof", "Method profiling has failed (check device log)"); + } } - // TODO: stuff + client.getClientData().setMethodProfilingStatus(MethodProfilingStatus.OFF); + client.update(Client.CHANGE_METHOD_PROFILING_STATUS); } /** @@ -157,10 +183,37 @@ final class HandleProfiling extends ChunkHandler { result = data.get(); if (result == 0) { + client.getClientData().setMethodProfilingStatus(MethodProfilingStatus.OFF); Log.d("ddm-prof", "Method profiling is not running"); } else { + client.getClientData().setMethodProfilingStatus(MethodProfilingStatus.ON); Log.d("ddm-prof", "Method profiling is running"); } + client.update(Client.CHANGE_METHOD_PROFILING_STATUS); + } + + private void handleFAIL(Client client, ByteBuffer data) { + // this can be sent if MPRS failed (like wrong permission) + + String filename = client.getClientData().getPendingMethodProfiling(); + if (filename != null) { + // reset the pending file. + client.getClientData().setPendingMethodProfiling(null); + + // and notify of failure + IMethodProfilingHandler handler = ClientData.getMethodProfilingHandler(); + if (handler != null) { + handler.onFailure(client); + } + + } + + // send a query to know the current status + try { + sendMPRQ(client); + } catch (IOException e) { + Log.e("HandleProfiling", e); + } } } diff --git a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/HandleWait.java b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/HandleWait.java index 480b525bc..934cbea1e 100644 --- a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/HandleWait.java +++ b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/HandleWait.java @@ -16,6 +16,8 @@ package com.android.ddmlib; +import com.android.ddmlib.ClientData.DebuggerStatus; + import java.io.IOException; import java.nio.ByteBuffer; @@ -80,10 +82,10 @@ final class HandleWait extends ChunkHandler { ClientData cd = client.getClientData(); synchronized (cd) { - cd.setDebuggerConnectionStatus(ClientData.DEBUGGER_WAITING); + cd.setDebuggerConnectionStatus(DebuggerStatus.WAITING); } - client.update(Client.CHANGE_DEBUGGER_INTEREST); + client.update(Client.CHANGE_DEBUGGER_STATUS); } } diff --git a/tools/ddms/libs/ddmuilib/.classpath b/tools/ddms/libs/ddmuilib/.classpath index ce7e7f0be..2cd368c29 100644 --- a/tools/ddms/libs/ddmuilib/.classpath +++ b/tools/ddms/libs/ddmuilib/.classpath @@ -1,6 +1,7 @@ - + + diff --git a/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/AllocationPanel.java b/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/AllocationPanel.java index 45d45ff93..11c0e19c3 100644 --- a/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/AllocationPanel.java +++ b/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/AllocationPanel.java @@ -18,8 +18,8 @@ package com.android.ddmuilib; import com.android.ddmlib.AllocationInfo; import com.android.ddmlib.Client; -import com.android.ddmlib.ClientData; import com.android.ddmlib.AndroidDebugBridge.IClientChangeListener; +import com.android.ddmlib.ClientData.AllocationTrackingStatus; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.viewers.ILabelProviderListener; @@ -61,7 +61,7 @@ public class AllocationPanel extends TablePanel { private final static String PREFS_ALLOC_COL_THREAD = "allocPanel.Col2"; //$NON-NLS-1$ private final static String PREFS_ALLOC_COL_TRACE_CLASS = "allocPanel.Col3"; //$NON-NLS-1$ private final static String PREFS_ALLOC_COL_TRACE_METHOD = "allocPanel.Col4"; //$NON-NLS-1$ - + private final static String PREFS_ALLOC_SASH = "allocPanel.sash"; //$NON-NLS-1$ private static final String PREFS_STACK_COL_CLASS = "allocPanel.stack.col0"; //$NON-NLS-1$ @@ -69,7 +69,7 @@ public class AllocationPanel extends TablePanel { private static final String PREFS_STACK_COL_FILE = "allocPanel.stack.col2"; //$NON-NLS-1$ private static final String PREFS_STACK_COL_LINE = "allocPanel.stack.col3"; //$NON-NLS-1$ private static final String PREFS_STACK_COL_NATIVE = "allocPanel.stack.col4"; //$NON-NLS-1$ - + private Composite mAllocationBase; private Table mAllocationTable; private TableViewer mAllocationViewer; @@ -171,18 +171,18 @@ public class AllocationPanel extends TablePanel { // base composite for selected client with enabled thread update. mAllocationBase = new Composite(parent, SWT.NONE); mAllocationBase.setLayout(new FormLayout()); - + // table above the sash Composite topParent = new Composite(mAllocationBase, SWT.NONE); topParent.setLayout(new GridLayout(2, false)); - + mEnableButton = new Button(topParent, SWT.PUSH); mEnableButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { Client current = getCurrentClient(); - int status = current.getClientData().getAllocationStatus(); - if (status == ClientData.ALLOCATION_TRACKING_ON) { + AllocationTrackingStatus status = current.getClientData().getAllocationStatus(); + if (status == AllocationTrackingStatus.ON) { current.enableAllocationTracker(false); } else { current.enableAllocationTracker(true); @@ -199,8 +199,8 @@ public class AllocationPanel extends TablePanel { getCurrentClient().requestAllocationDetails(); } }); - - setUpButtons(false /* enabled */, ClientData.ALLOCATION_TRACKING_OFF /* trackingStatus */); + + setUpButtons(false /* enabled */, AllocationTrackingStatus.OFF); mAllocationTable = new Table(topParent, SWT.MULTI | SWT.FULL_SELECTION); GridData gridData; @@ -243,7 +243,7 @@ public class AllocationPanel extends TablePanel { SWT.LEFT, "utime", //$NON-NLS-1$ PREFS_ALLOC_COL_TRACE_METHOD, store); - + mAllocationViewer = new TableViewer(mAllocationTable); mAllocationViewer.setContentProvider(new AllocationContentProvider()); mAllocationViewer.setLabelProvider(new AllocationLabelProvider()); @@ -254,12 +254,12 @@ public class AllocationPanel extends TablePanel { updateAllocationStackTrace(selectedAlloc); } }); - + // the separating sash final Sash sash = new Sash(mAllocationBase, SWT.HORIZONTAL); Color darkGray = parent.getDisplay().getSystemColor(SWT.COLOR_DARK_GRAY); sash.setBackground(darkGray); - + // the UI below the sash mStackTracePanel = new StackTracePanel(); mStackTraceTable = mStackTracePanel.createPanel(mAllocationBase, @@ -269,7 +269,7 @@ public class AllocationPanel extends TablePanel { PREFS_STACK_COL_LINE, PREFS_STACK_COL_NATIVE, store); - + // now setup the sash. // form layout data FormData data = new FormData(); @@ -313,7 +313,7 @@ public class AllocationPanel extends TablePanel { return mAllocationBase; } - + /** * Sets the focus to the proper control inside the panel. */ @@ -329,7 +329,7 @@ public class AllocationPanel extends TablePanel { * @param client the updated client. * @param changeMask the bit mask describing the changed properties. It can contain * any of the following values: {@link Client#CHANGE_INFO}, {@link Client#CHANGE_NAME} - * {@link Client#CHANGE_DEBUGGER_INTEREST}, {@link Client#CHANGE_THREAD_MODE}, + * {@link Client#CHANGE_DEBUGGER_STATUS}, {@link Client#CHANGE_THREAD_MODE}, * {@link Client#CHANGE_THREAD_DATA}, {@link Client#CHANGE_HEAP_MODE}, * {@link Client#CHANGE_HEAP_DATA}, {@link Client#CHANGE_NATIVE_HEAP_DATA} * @@ -382,20 +382,19 @@ public class AllocationPanel extends TablePanel { } Client client = getCurrentClient(); - + mStackTracePanel.setCurrentClient(client); mStackTracePanel.setViewerInput(null); // always empty on client selection change. if (client != null) { setUpButtons(true /* enabled */, client.getClientData().getAllocationStatus()); } else { - setUpButtons(false /* enabled */, - ClientData.ALLOCATION_TRACKING_OFF /* trackingStatus */); + setUpButtons(false /* enabled */, AllocationTrackingStatus.OFF); } mAllocationViewer.setInput(client); } - + /** * Updates the stack call of the currently selected thread. *

@@ -406,7 +405,7 @@ public class AllocationPanel extends TablePanel { if (client != null) { // get the current selection in the ThreadTable AllocationInfo selectedAlloc = getAllocationSelection(null); - + if (selectedAlloc != null) { updateAllocationStackTrace(selectedAlloc); } else { @@ -441,7 +440,7 @@ public class AllocationPanel extends TablePanel { if (selection == null) { selection = mAllocationViewer.getSelection(); } - + if (selection instanceof IStructuredSelection) { IStructuredSelection structuredSelection = (IStructuredSelection)selection; Object object = structuredSelection.getFirstElement(); @@ -449,29 +448,29 @@ public class AllocationPanel extends TablePanel { return (AllocationInfo)object; } } - + return null; } /** - * + * * @param enabled * @param trackingStatus */ - private void setUpButtons(boolean enabled, int trackingStatus) { + private void setUpButtons(boolean enabled, AllocationTrackingStatus trackingStatus) { if (enabled) { switch (trackingStatus) { - case ClientData.ALLOCATION_TRACKING_UNKNOWN: + case UNKNOWN: mEnableButton.setText("?"); mEnableButton.setEnabled(false); mRequestButton.setEnabled(false); break; - case ClientData.ALLOCATION_TRACKING_OFF: + case OFF: mEnableButton.setText("Start Tracking"); mEnableButton.setEnabled(true); mRequestButton.setEnabled(false); break; - case ClientData.ALLOCATION_TRACKING_ON: + case ON: mEnableButton.setText("Stop Tracking"); mEnableButton.setEnabled(true); mRequestButton.setEnabled(true); 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 75321519d..691692f94 100644 --- a/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/DevicePanel.java +++ b/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/DevicePanel.java @@ -24,6 +24,7 @@ import com.android.ddmlib.IDevice; import com.android.ddmlib.AndroidDebugBridge.IClientChangeListener; import com.android.ddmlib.AndroidDebugBridge.IDebugBridgeChangeListener; import com.android.ddmlib.AndroidDebugBridge.IDeviceChangeListener; +import com.android.ddmlib.ClientData.DebuggerStatus; import com.android.ddmlib.IDevice.DeviceState; import org.eclipse.jface.preference.IPreferenceStore; @@ -76,6 +77,8 @@ public final class DevicePanel extends Panel implements IDebugBridgeChangeListen 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$ + public final static String ICON_TRACING_START = "tracing_start.png"; //$NON-NLS-1$ + public final static String ICON_TRACING_STOP = "tracing_stop.png"; //$NON-NLS-1$ private IDevice mCurrentDevice; private Client mCurrentClient; @@ -167,13 +170,13 @@ public final class DevicePanel extends Panel implements IDebugBridgeChangeListen switch (columnIndex) { case CLIENT_COL_NAME: switch (cd.getDebuggerConnectionStatus()) { - case ClientData.DEBUGGER_DEFAULT: + case DEFAULT: return null; - case ClientData.DEBUGGER_WAITING: + case WAITING: return mWaitingImage; - case ClientData.DEBUGGER_ATTACHED: + case ATTACHED: return mDebuggerImage; - case ClientData.DEBUGGER_ERROR: + case ERROR: return mDebugErrorImage; } return null; @@ -430,6 +433,11 @@ public final class DevicePanel extends Panel implements IDebugBridgeChangeListen } } + public void toggleMethodProfiling() { + if (mCurrentClient != null) { + mCurrentClient.toggleMethodProfiling(); + } + } public void setEnabledHeapOnSelectedClient(boolean enable) { if (mCurrentClient != null) { @@ -594,7 +602,7 @@ public final class DevicePanel extends Panel implements IDebugBridgeChangeListen * @param client the updated client. * @param changeMask the bit mask describing the changed properties. It can contain * any of the following values: {@link Client#CHANGE_INFO}, - * {@link Client#CHANGE_DEBUGGER_INTEREST}, {@link Client#CHANGE_THREAD_MODE}, + * {@link Client#CHANGE_DEBUGGER_STATUS}, {@link Client#CHANGE_THREAD_MODE}, * {@link Client#CHANGE_THREAD_DATA}, {@link Client#CHANGE_HEAP_MODE}, * {@link Client#CHANGE_HEAP_DATA}, {@link Client#CHANGE_NATIVE_HEAP_DATA} * @@ -607,10 +615,10 @@ public final class DevicePanel extends Panel implements IDebugBridgeChangeListen // refresh the client mTreeViewer.refresh(client); - if ((changeMask & Client.CHANGE_DEBUGGER_INTEREST) == - Client.CHANGE_DEBUGGER_INTEREST && + if ((changeMask & Client.CHANGE_DEBUGGER_STATUS) == + Client.CHANGE_DEBUGGER_STATUS && client.getClientData().getDebuggerConnectionStatus() == - ClientData.DEBUGGER_WAITING) { + DebuggerStatus.WAITING) { // make sure the device is expanded. Normally the setSelection below // will auto expand, but the children of device may not already exist // at this time. Forcing an expand will make the TreeViewer create them. diff --git a/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/HeapPanel.java b/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/HeapPanel.java index 977203b2d..f5cf9b119 100644 --- a/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/HeapPanel.java +++ b/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/HeapPanel.java @@ -217,7 +217,7 @@ public final class HeapPanel extends BaseHeapPanel { * @param client the updated client. * @param changeMask the bit mask describing the changed properties. It can contain * any of the following values: {@link Client#CHANGE_INFO}, {@link Client#CHANGE_NAME} - * {@link Client#CHANGE_DEBUGGER_INTEREST}, {@link Client#CHANGE_THREAD_MODE}, + * {@link Client#CHANGE_DEBUGGER_STATUS}, {@link Client#CHANGE_THREAD_MODE}, * {@link Client#CHANGE_THREAD_DATA}, {@link Client#CHANGE_HEAP_MODE}, * {@link Client#CHANGE_HEAP_DATA}, {@link Client#CHANGE_NATIVE_HEAP_DATA} * diff --git a/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/InfoPanel.java b/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/InfoPanel.java index 35e071d06..6cbb999ae 100644 --- a/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/InfoPanel.java +++ b/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/InfoPanel.java @@ -92,7 +92,7 @@ public class InfoPanel extends TablePanel { * @param client the updated client. * @param changeMask the bit mask describing the changed properties. It can contain * any of the following values: {@link Client#CHANGE_PORT}, {@link Client#CHANGE_NAME} - * {@link Client#CHANGE_DEBUGGER_INTEREST}, {@link Client#CHANGE_THREAD_MODE}, + * {@link Client#CHANGE_DEBUGGER_STATUS}, {@link Client#CHANGE_THREAD_MODE}, * {@link Client#CHANGE_THREAD_DATA}, {@link Client#CHANGE_HEAP_MODE}, * {@link Client#CHANGE_HEAP_DATA}, {@link Client#CHANGE_NATIVE_HEAP_DATA} * diff --git a/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/NativeHeapPanel.java b/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/NativeHeapPanel.java index 46461bf32..0b2460ba4 100644 --- a/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/NativeHeapPanel.java +++ b/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/NativeHeapPanel.java @@ -668,7 +668,7 @@ public final class NativeHeapPanel extends BaseHeapPanel { * @param client the updated client. * @param changeMask the bit mask describing the changed properties. It can contain * any of the following values: {@link Client#CHANGE_INFO}, {@link Client#CHANGE_NAME} - * {@link Client#CHANGE_DEBUGGER_INTEREST}, {@link Client#CHANGE_THREAD_MODE}, + * {@link Client#CHANGE_DEBUGGER_STATUS}, {@link Client#CHANGE_THREAD_MODE}, * {@link Client#CHANGE_THREAD_DATA}, {@link Client#CHANGE_HEAP_MODE}, * {@link Client#CHANGE_HEAP_DATA}, {@link Client#CHANGE_NATIVE_HEAP_DATA} * diff --git a/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/ThreadPanel.java b/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/ThreadPanel.java index a034063dc..d94d4f340 100644 --- a/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/ThreadPanel.java +++ b/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/ThreadPanel.java @@ -376,7 +376,7 @@ public class ThreadPanel extends TablePanel { * @param client the updated client. * @param changeMask the bit mask describing the changed properties. It can contain * any of the following values: {@link Client#CHANGE_INFO}, {@link Client#CHANGE_NAME} - * {@link Client#CHANGE_DEBUGGER_INTEREST}, {@link Client#CHANGE_THREAD_MODE}, + * {@link Client#CHANGE_DEBUGGER_STATUS}, {@link Client#CHANGE_THREAD_MODE}, * {@link Client#CHANGE_THREAD_DATA}, {@link Client#CHANGE_HEAP_MODE}, * {@link Client#CHANGE_HEAP_DATA}, {@link Client#CHANGE_NATIVE_HEAP_DATA} * diff --git a/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/handler/BaseFileHandler.java b/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/handler/BaseFileHandler.java new file mode 100644 index 000000000..3a2a2ef2e --- /dev/null +++ b/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/handler/BaseFileHandler.java @@ -0,0 +1,103 @@ +/* + * 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.handler; + +import com.android.ddmlib.SyncService; +import com.android.ddmlib.ClientData.IHprofDumpHandler; +import com.android.ddmlib.ClientData.IMethodProfilingHandler; +import com.android.ddmlib.SyncService.SyncResult; +import com.android.ddmuilib.SyncProgressMonitor; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jface.dialogs.ProgressMonitorDialog; +import org.eclipse.jface.operation.IRunnableWithProgress; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.FileDialog; +import org.eclipse.swt.widgets.Shell; + +import java.lang.reflect.InvocationTargetException; + +/** + * Base handler class for handler dealing with files located on a device. + * + * @see IHprofDumpHandler + * @see IMethodProfilingHandler + */ +public class BaseFileHandler { + + protected final Shell mParentShell; + + public BaseFileHandler(Shell parentShell) { + mParentShell = parentShell; + } + + /** + * Prompts the user for a save location and pulls the remote files into this location. + *

This must be called from the UI Thread. + * @param sync the {@link SyncService} to use to pull the file from the device + * @param localFileName The default local name + * @param remoteFilePath The name of the file to pull off of the device + * @param title The title of the File Save dialog. + * @return The result of the pull as a {@link SyncResult} object, or null if the sync + * didn't happen (canceled by the user). + * @throws InvocationTargetException + * @throws InterruptedException + */ + protected SyncResult promptAndPull(SyncService sync, + String localFileName, String remoteFilePath, String title) + throws InvocationTargetException, InterruptedException { + FileDialog fileDialog = new FileDialog(mParentShell, SWT.SAVE); + + fileDialog.setText(title); + fileDialog.setFileName(localFileName); + + String localFilePath = fileDialog.open(); + if (localFilePath != null) { + return pull(sync, localFilePath, remoteFilePath); + } + + return null; + } + + /** + * Pulls a file off of a device + * @param sync the {@link SyncService} to use to pull the file. + * @param localFilePath the path of the local file to create + * @param remoteFilePath the path of the remote file to pull + * @return the result of the sync as an instance of {@link SyncResult} + * @throws InvocationTargetException + * @throws InterruptedException + */ + protected SyncResult pull(final SyncService sync, final String localFilePath, + final String remoteFilePath) + throws InvocationTargetException, InterruptedException { + final SyncResult[] res = new SyncResult[1]; + new ProgressMonitorDialog(mParentShell).run(true, true, new IRunnableWithProgress() { + public void run(IProgressMonitor monitor) { + try { + res[0] = sync.pullFile(remoteFilePath, localFilePath, + new SyncProgressMonitor(monitor, String.format( + "Pulling %1$s from the device", remoteFilePath))); + } finally { + sync.close(); + } + } + }); + + return res[0]; + } +} diff --git a/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/handler/MethodProfilingHandler.java b/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/handler/MethodProfilingHandler.java new file mode 100644 index 000000000..f469a7dda --- /dev/null +++ b/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/handler/MethodProfilingHandler.java @@ -0,0 +1,154 @@ +/* + * 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.handler; + +import com.android.ddmlib.Client; +import com.android.ddmlib.IDevice; +import com.android.ddmlib.Log; +import com.android.ddmlib.SyncService; +import com.android.ddmlib.ClientData.IMethodProfilingHandler; +import com.android.ddmlib.SyncService.SyncResult; +import com.android.ddmuilib.DdmUiPreferences; +import com.android.ddmuilib.console.DdmConsole; + +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.swt.widgets.Shell; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.lang.reflect.InvocationTargetException; + +/** + * Handler for Method tracing. + * This will pull the trace file into a temp file and launch traceview. + */ +public class MethodProfilingHandler extends BaseFileHandler + implements IMethodProfilingHandler { + + public MethodProfilingHandler(Shell parentShell) { + super(parentShell); + } + + public void onFailure(final Client client) { + mParentShell.getDisplay().asyncExec(new Runnable() { + public void run() { + displayError( + "Unable to create Method Profiling file for application '%1$s'.\n" + + "Check logcat for more information.", + client.getClientData().getClientDescription()); + } + }); + } + + public void onSuccess(final String remoteFilePath, final Client client) { + mParentShell.getDisplay().asyncExec(new Runnable() { + public void run() { + if (remoteFilePath == null) { + displayError( + "Unable to download trace file: unknown file name.\n" + + "This can happen if you disconnected the device while recording the trace."); + return; + } + + final IDevice device = client.getDevice(); + try { + // get the sync service to pull the HPROF file + final SyncService sync = client.getDevice().getSyncService(); + if (sync != null) { + pullAndOpen(sync, remoteFilePath); + } else { + displayError("Unable to download trace file from device '%1$s'.", + device.getSerialNumber()); + } + } catch (Exception e) { + displayError("Unable to download trace file from device '%1$s'.", + device.getSerialNumber()); + } + } + + }); + } + + private void pullAndOpen(SyncService sync, String remoteFilePath) + throws InvocationTargetException, InterruptedException, IOException { + // get a temp file + File temp = File.createTempFile("android", ".trace"); //$NON-NLS-1$ //$NON-NLS-2$ + String tempPath = temp.getAbsolutePath(); + + // pull the file + SyncResult result = pull(sync, tempPath, remoteFilePath); + if (result != null) { + if (result.getCode() == SyncService.RESULT_OK) { + // open the temp file in traceview + openInTraceview(tempPath); + } else { + displayError("Unable to download trace file:\n\n%1$s", + result.getMessage()); + } + } else { + // this really shouldn't happen. + displayError("Unable to download trace file."); + } + } + + private void openInTraceview(String tempPath) { + // now that we have the file, we need to launch traceview + String[] command = new String[2]; + command[0] = DdmUiPreferences.getTraceview(); + command[1] = tempPath; + + try { + final Process p = Runtime.getRuntime().exec(command); + + // create a thread for the output + new Thread("Traceview output") { + @Override + public void run() { + // create a buffer to read the stderr output + InputStreamReader is = new InputStreamReader(p.getErrorStream()); + BufferedReader resultReader = new BufferedReader(is); + + // read the lines as they come. if null is returned, it's + // because the process finished + try { + while (true) { + String line = resultReader.readLine(); + if (line != null) { + DdmConsole.printErrorToConsole("Traceview: " + line); + } else { + break; + } + } + // get the return code from the process + p.waitFor(); + } catch (Exception e) { + Log.e("traceview", e); + } + } + }.start(); + } catch (IOException e) { + Log.e("traceview", e); + } + } + + private void displayError(String format, Object... args) { + MessageDialog.openError(mParentShell, "Method Profiling Error", + String.format(format, args)); + } +} diff --git a/tools/ddms/libs/ddmuilib/src/resources/images/tracing_start.png b/tools/ddms/libs/ddmuilib/src/resources/images/tracing_start.png new file mode 100644 index 000000000..88771cc6b Binary files /dev/null and b/tools/ddms/libs/ddmuilib/src/resources/images/tracing_start.png differ diff --git a/tools/ddms/libs/ddmuilib/src/resources/images/tracing_stop.png b/tools/ddms/libs/ddmuilib/src/resources/images/tracing_stop.png new file mode 100644 index 000000000..71bd215f4 Binary files /dev/null and b/tools/ddms/libs/ddmuilib/src/resources/images/tracing_stop.png differ diff --git a/tools/eclipse/plugins/.gitignore b/tools/eclipse/plugins/.gitignore index cc105d221..6c13a2bd0 100644 --- a/tools/eclipse/plugins/.gitignore +++ b/tools/eclipse/plugins/.gitignore @@ -40,6 +40,8 @@ com.android.ide.eclipse.ddms/icons/pull.png com.android.ide.eclipse.ddms/icons/push.png com.android.ide.eclipse.ddms/icons/save.png com.android.ide.eclipse.ddms/icons/thread.png +com.android.ide.eclipse.ddms/icons/tracing_start.png +com.android.ide.eclipse.ddms/icons/tracing_stop.png com.android.ide.eclipse.ddms/icons/up.png com.android.ide.eclipse.ddms/icons/v.png com.android.ide.eclipse.ddms/icons/w.png diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/AndroidLaunchController.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/AndroidLaunchController.java index b834bcf92..eab0c6c9b 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/AndroidLaunchController.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/AndroidLaunchController.java @@ -24,6 +24,7 @@ import com.android.ddmlib.Log; import com.android.ddmlib.AndroidDebugBridge.IClientChangeListener; import com.android.ddmlib.AndroidDebugBridge.IDebugBridgeChangeListener; import com.android.ddmlib.AndroidDebugBridge.IDeviceChangeListener; +import com.android.ddmlib.ClientData.DebuggerStatus; import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.adt.internal.launch.AndroidLaunchConfiguration.TargetMode; import com.android.ide.eclipse.adt.internal.launch.DelayedLaunchInfo.InstallRetryMode; @@ -1382,7 +1383,7 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener * @param client the updated client. * @param changeMask the bit mask describing the changed properties. It can contain * any of the following values: {@link Client#CHANGE_INFO}, {@link Client#CHANGE_NAME} - * {@link Client#CHANGE_DEBUGGER_INTEREST}, {@link Client#CHANGE_THREAD_MODE}, + * {@link Client#CHANGE_DEBUGGER_STATUS}, {@link Client#CHANGE_THREAD_MODE}, * {@link Client#CHANGE_THREAD_DATA}, {@link Client#CHANGE_HEAP_MODE}, * {@link Client#CHANGE_HEAP_DATA}, {@link Client#CHANGE_NATIVE_HEAP_DATA} * @@ -1445,7 +1446,7 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener } // check if it's already waiting for a debugger, and if so we connect to it. - if (client.getClientData().getDebuggerConnectionStatus() == ClientData.DEBUGGER_WAITING) { + if (client.getClientData().getDebuggerConnectionStatus() == DebuggerStatus.WAITING) { // search for this client in the list; synchronized (sListLock) { int index = mUnknownClientsWaitingForDebugger.indexOf(client); @@ -1461,10 +1462,10 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener // if it's not home, it could be an app that is now in debugger mode that we're waiting for // lets check it - if ((changeMask & Client.CHANGE_DEBUGGER_INTEREST) == Client.CHANGE_DEBUGGER_INTEREST) { + if ((changeMask & Client.CHANGE_DEBUGGER_STATUS) == Client.CHANGE_DEBUGGER_STATUS) { ClientData clientData = client.getClientData(); String applicationName = client.getClientData().getClientDescription(); - if (clientData.getDebuggerConnectionStatus() == ClientData.DEBUGGER_WAITING) { + if (clientData.getDebuggerConnectionStatus() == DebuggerStatus.WAITING) { // Get the application name, and make sure its valid. if (applicationName == null) { // looks like we don't have the client yet, so we keep it around for when its diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/DdmsPlugin.java b/tools/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/DdmsPlugin.java index 631b391a3..76b005e9d 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/DdmsPlugin.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/DdmsPlugin.java @@ -18,6 +18,7 @@ package com.android.ide.eclipse.ddms; import com.android.ddmlib.AndroidDebugBridge; import com.android.ddmlib.Client; +import com.android.ddmlib.DdmConstants; import com.android.ddmlib.DdmPreferences; import com.android.ddmlib.IDevice; import com.android.ddmlib.Log; @@ -56,20 +57,6 @@ import java.util.Calendar; public final class DdmsPlugin extends AbstractUIPlugin implements IDeviceChangeListener, IUiSelectionListener { - public final static int PLATFORM_UNKNOWN = 0; - public final static int PLATFORM_LINUX = 1; - public final static int PLATFORM_WINDOWS = 2; - public final static int PLATFORM_DARWIN = 3; - - /** - * Returns current platform, one of {@link #PLATFORM_WINDOWS}, {@link #PLATFORM_DARWIN}, - * {@link #PLATFORM_LINUX} or {@link #PLATFORM_UNKNOWN}. - */ - public final static int CURRENT_PLATFORM = currentPlatform(); - - /** hprof-conv executable (with extension for the current OS) */ - public final static String FN_HPROF_CONVERTER = (CURRENT_PLATFORM == PLATFORM_WINDOWS) ? - "hprof-conv.exe" : "hprof-conv"; //$NON-NLS-1$ //$NON-NLS-2$ // The plug-in ID public static final String PLUGIN_ID = "com.android.ide.eclipse.ddms"; // $NON-NLS-1$ @@ -312,8 +299,11 @@ public final class DdmsPlugin extends AbstractUIPlugin implements IDeviceChangeL File toolsFolder = adb.getParentFile(); sToolsFolder = toolsFolder.getAbsolutePath(); - File hprofConverter = new File(toolsFolder, FN_HPROF_CONVERTER); + File hprofConverter = new File(toolsFolder, DdmConstants.FN_HPROF_CONVERTER); sHprofConverter = hprofConverter.getAbsolutePath(); + + File traceview = new File(toolsFolder, DdmConstants.FN_TRACEVIEW); + DdmUiPreferences.setTraceviewLocation(traceview.getAbsolutePath()); } /** @@ -598,25 +588,4 @@ public final class DdmsPlugin extends AbstractUIPlugin implements IDeviceChangeL return String.format("[%1$tF %1$tT - %2$s]", c, tag); } - - - /** - * Returns current platform - * - * @return one of {@link #PLATFORM_WINDOWS}, {@link #PLATFORM_DARWIN}, - * {@link #PLATFORM_LINUX} or {@link #PLATFORM_UNKNOWN}. - */ - public static int currentPlatform() { - String os = System.getProperty("os.name"); //$NON-NLS-1$ - if (os.startsWith("Mac OS")) { //$NON-NLS-1$ - return PLATFORM_DARWIN; - } else if (os.startsWith("Windows")) { //$NON-NLS-1$ - return PLATFORM_WINDOWS; - } else if (os.startsWith("Linux")) { //$NON-NLS-1$ - return PLATFORM_LINUX; - } - - return PLATFORM_UNKNOWN; - } - } diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/DeviceView.java b/tools/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/DeviceView.java index 50eee2633..71efb50d6 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/DeviceView.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/views/DeviceView.java @@ -22,11 +22,14 @@ import com.android.ddmlib.Client; import com.android.ddmlib.ClientData; import com.android.ddmlib.IDevice; import com.android.ddmlib.SyncService; +import com.android.ddmlib.AndroidDebugBridge.IClientChangeListener; import com.android.ddmlib.ClientData.IHprofDumpHandler; +import com.android.ddmlib.ClientData.MethodProfilingStatus; import com.android.ddmlib.SyncService.SyncResult; +import com.android.ddmuilib.handler.BaseFileHandler; +import com.android.ddmuilib.handler.MethodProfilingHandler; 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; @@ -34,7 +37,6 @@ import com.android.ide.eclipse.ddms.preferences.PreferenceInitializer; import org.eclipse.core.filesystem.EFS; import org.eclipse.core.filesystem.IFileStore; -import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.Path; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.IAction; @@ -42,13 +44,10 @@ 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.jface.preference.IPreferenceStore; -import org.eclipse.swt.SWT; +import org.eclipse.jface.resource.ImageDescriptor; 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; @@ -59,9 +58,8 @@ import org.eclipse.ui.part.ViewPart; import java.io.File; import java.io.IOException; -import java.lang.reflect.InvocationTargetException; -public class DeviceView extends ViewPart implements IUiSelectionListener { +public class DeviceView extends ViewPart implements IUiSelectionListener, IClientChangeListener { private final static boolean USE_SELECTED_DEBUG_PORT = true; @@ -70,7 +68,9 @@ public class DeviceView extends ViewPart implements IUiSelectionListener { private static DeviceView sThis; + private Shell mParentShell; private DevicePanel mDeviceList; + private Action mResetAdbAction; private Action mCaptureAction; private Action mUpdateThreadAction; @@ -79,29 +79,29 @@ public class DeviceView extends ViewPart implements IUiSelectionListener { private Action mKillAppAction; private Action mDebugAction; private Action mHprofAction; + private Action mTracingAction; private IDebugLauncher mDebugLauncher; - public class HProfHandler implements IHprofDumpHandler { + private ImageDescriptor mTracingStartImage; + private ImageDescriptor mTracingStopImage; + + public class HProfHandler extends BaseFileHandler implements IHprofDumpHandler { public final static String ACTION_SAVE ="hprof.save"; //$NON-NLS-1$ public final static String ACTION_OPEN = "hprof.open"; //$NON-NLS-1$ public final static String DOT_HPROF = ".hprof"; //$NON-NLS-1$ - private final Shell mParentShell; - HProfHandler(Shell parentShell) { - mParentShell = parentShell; + super(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())); + displayError("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 @@ -111,7 +111,7 @@ public class DeviceView extends ViewPart implements IUiSelectionListener { }); } - public void onSuccess(final String remoteFile, final Client client) { + public void onSuccess(final String remoteFilePath, final Client client) { mParentShell.getDisplay().asyncExec(new Runnable() { public void run() { final IDevice device = client.getDevice(); @@ -122,26 +122,35 @@ public class DeviceView extends ViewPart implements IUiSelectionListener { // get from the preference what action to take IPreferenceStore store = DdmsPlugin.getDefault().getPreferenceStore(); String value = store.getString(PreferenceInitializer.ATTR_HPROF_ACTION); + + SyncResult result = null; if (ACTION_OPEN.equals(value)) { File temp = File.createTempFile("android", DOT_HPROF); //$NON-NLS-1$ String tempPath = temp.getAbsolutePath(); - pull(sync, tempPath, remoteFile); - - open(tempPath); + result = pull(sync, tempPath, remoteFilePath); + if (result != null && result.getCode() == SyncService.RESULT_OK) { + open(tempPath); + } } else { // default action is ACTION_SAVE - promptAndPull(device, client, sync, remoteFile); + result = promptAndPull(sync, + client.getClientData().getClientDescription() + DOT_HPROF, + remoteFilePath, "Save HPROF file"); + + } + + if (result != null && result.getCode() != SyncService.RESULT_OK) { + displayError( + "Unable to download HPROF file from device '%1$s'.\n\n%2$s", + device.getSerialNumber(), result.getMessage()); } } else { - MessageDialog.openError(mParentShell, "HPROF Error", - String.format( - "Unable to download HPROF file from device '%1$s'.", - device.getSerialNumber())); + displayError("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())); + displayError("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 @@ -152,48 +161,6 @@ public class DeviceView extends ViewPart implements IUiSelectionListener { }); } - 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() + DOT_HPROF); - - final String localFileName = fileDialog.open(); - if (localFileName != null) { - pull(sync, localFileName, remoteFile); - } - } catch (Exception e) { - MessageDialog.openError(mParentShell, "HPROF Error", - String.format("Unable to download HPROF file from device '%1$s'.", - device.getSerialNumber())); - } - } - - private void pull(final SyncService sync, - final String localFileName, final String remoteFile) - throws InvocationTargetException, InterruptedException { - 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", - remoteFile))); - - 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(); - } - }); - } - private void open(String path) throws IOException, InterruptedException, PartInitException { // make a temp file to convert the hprof into something // readable by normal tools @@ -215,6 +182,11 @@ public class DeviceView extends ViewPart implements IUiSelectionListener { fileStore); } } + + private void displayError(String format, Object... args) { + MessageDialog.openError(mParentShell, "HPROF Error", + String.format(format, args)); + } } @@ -244,7 +216,10 @@ public class DeviceView extends ViewPart implements IUiSelectionListener { @Override public void createPartControl(Composite parent) { - ClientData.setHprofDumpHandler(new HProfHandler(parent.getShell())); + mParentShell = parent.getShell(); + ClientData.setHprofDumpHandler(new HProfHandler(mParentShell)); + AndroidDebugBridge.addClientChangeListener(this); + ClientData.setMethodProfilingHandler(new MethodProfilingHandler(mParentShell)); mDeviceList = new DevicePanel(DdmsPlugin.getImageLoader(), USE_SELECTED_DEBUG_PORT); mDeviceList.createPanel(parent); @@ -350,6 +325,20 @@ public class DeviceView extends ViewPart implements IUiSelectionListener { mUpdateThreadAction.setImageDescriptor(DdmsPlugin.getImageLoader() .loadDescriptor(DevicePanel.ICON_THREAD)); + mTracingAction = new Action() { + @Override + public void run() { + mDeviceList.toggleMethodProfiling(); + } + }; + mTracingAction.setText("Start Method Profiling"); + mTracingAction.setToolTipText("Start Method Profiling"); + mTracingStartImage = DdmsPlugin.getImageLoader().loadDescriptor( + DevicePanel.ICON_TRACING_START); + mTracingStopImage = DdmsPlugin.getImageLoader().loadDescriptor( + DevicePanel.ICON_TRACING_STOP); + mTracingAction.setImageDescriptor(mTracingStartImage); + // check if there's already a debug launcher set up in the plugin class mDebugLauncher = DdmsPlugin.getRunningAppDebugLauncher(); @@ -363,14 +352,14 @@ public class DeviceView extends ViewPart implements IUiSelectionListener { // make sure the client can be debugged switch (clientData.getDebuggerConnectionStatus()) { - case ClientData.DEBUGGER_ERROR: { + case ERROR: { Display display = DdmsPlugin.getDisplay(); Shell shell = display.getActiveShell(); MessageDialog.openError(shell, "Process Debug", "The process debug port is already in use!"); return; } - case ClientData.DEBUGGER_ATTACHED: { + case ATTACHED: { Display display = DdmsPlugin.getDisplay(); Shell shell = display.getActiveShell(); MessageDialog.openError(shell, "Process Debug", @@ -442,9 +431,34 @@ 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); + + ClientData data = selectedClient.getClientData(); + + if (data.hasFeature(ClientData.FEATURE_HPROF)) { + mHprofAction.setEnabled(data.hasPendingHprofDump() == false); + mHprofAction.setToolTipText("Dump HPROF file"); + } else { + mHprofAction.setEnabled(false); + mHprofAction.setToolTipText("Dump HPROF file (not supported by this VM)"); + } + + if (data.hasFeature(ClientData.FEATURE_PROFILING)) { + mTracingAction.setEnabled(true); + if (data.getMethodProfilingStatus() == MethodProfilingStatus.ON) { + mTracingAction.setToolTipText("Stop Method Profiling"); + mTracingAction.setText("Stop Method Profiling"); + mTracingAction.setImageDescriptor(mTracingStopImage); + } else { + mTracingAction.setToolTipText("Start Method Profiling"); + mTracingAction.setImageDescriptor(mTracingStartImage); + mTracingAction.setText("Start Method Profiling"); + } + } else { + mTracingAction.setEnabled(false); + mTracingAction.setImageDescriptor(mTracingStartImage); + mTracingAction.setToolTipText("Start Method Profiling (not supported by this VM)"); + mTracingAction.setText("Start Method Profiling"); + } } else { if (USE_SELECTED_DEBUG_PORT) { // set the client as the debug client @@ -462,6 +476,14 @@ public class DeviceView extends ViewPart implements IUiSelectionListener { mUpdateThreadAction.setEnabled(false); mUpdateThreadAction.setChecked(false); mHprofAction.setEnabled(false); + + mHprofAction.setEnabled(false); + mHprofAction.setToolTipText("Dump HPROF file"); + + mTracingAction.setEnabled(false); + mTracingAction.setImageDescriptor(mTracingStartImage); + mTracingAction.setToolTipText("Start Method Profiling"); + mTracingAction.setText("Start Method Profiling"); } } @@ -477,13 +499,15 @@ public class DeviceView extends ViewPart implements IUiSelectionListener { // first in the menu IMenuManager menuManager = actionBars.getMenuManager(); + menuManager.removeAll(); menuManager.add(mDebugAction); menuManager.add(new Separator()); - menuManager.add(mUpdateThreadAction); menuManager.add(mUpdateHeapAction); - menuManager.add(new Separator()); - menuManager.add(mGcAction); menuManager.add(mHprofAction); + menuManager.add(mGcAction); + menuManager.add(new Separator()); + menuManager.add(mUpdateThreadAction); + menuManager.add(mTracingAction); menuManager.add(new Separator()); menuManager.add(mKillAppAction); menuManager.add(new Separator()); @@ -493,17 +517,32 @@ public class DeviceView extends ViewPart implements IUiSelectionListener { // and then in the toolbar IToolBarManager toolBarManager = actionBars.getToolBarManager(); + toolBarManager.removeAll(); toolBarManager.add(mDebugAction); toolBarManager.add(new Separator()); - toolBarManager.add(mUpdateThreadAction); toolBarManager.add(mUpdateHeapAction); - toolBarManager.add(new Separator()); - toolBarManager.add(mGcAction); toolBarManager.add(mHprofAction); + toolBarManager.add(mGcAction); + toolBarManager.add(new Separator()); + toolBarManager.add(mUpdateThreadAction); + toolBarManager.add(mTracingAction); toolBarManager.add(new Separator()); toolBarManager.add(mKillAppAction); toolBarManager.add(new Separator()); toolBarManager.add(mCaptureAction); } + public void clientChanged(final Client client, int changeMask) { + if ((changeMask & Client.CHANGE_METHOD_PROFILING_STATUS) == + Client.CHANGE_METHOD_PROFILING_STATUS) { + if (mDeviceList.getSelectedClient() == client) { + mParentShell.getDisplay().asyncExec(new Runnable() { + public void run() { + // force refresh of the button enabled state. + doSelectionChanged(client); + } + }); + } + } + } } diff --git a/tools/eclipse/scripts/create_ddms_symlinks.sh b/tools/eclipse/scripts/create_ddms_symlinks.sh index a5b20a879..87cdf68f2 100755 --- a/tools/eclipse/scripts/create_ddms_symlinks.sh +++ b/tools/eclipse/scripts/create_ddms_symlinks.sh @@ -70,7 +70,7 @@ for i in \ load.png \ pause.png play.png pull.png push.png \ save.png \ - thread.png \ + thread.png tracing_start.png tracing_stop.png \ up.png \ v.png \ w.png warning.png ; do