diff options
author | Xavier Ducrohet <xav@android.com> | 2009-09-02 20:06:08 -0700 |
---|---|---|
committer | Xavier Ducrohet <xav@android.com> | 2009-09-03 16:01:27 -0700 |
commit | bcf38645bef192b380f0d338b263aea075cd1aeb (patch) | |
tree | ff0c8c82db1b0b15b666639f9d98ca680a7687f3 /ddms/app/src | |
parent | 816c14d9bdb50bb2b8d4bc1e970858955cbbbfda (diff) | |
download | sdk-bcf38645bef192b380f0d338b263aea075cd1aeb.zip sdk-bcf38645bef192b380f0d338b263aea075cd1aeb.tar.gz sdk-bcf38645bef192b380f0d338b263aea075cd1aeb.tar.bz2 |
Add method profiling support to DDMS.
New button allows to start/stop tracing. When clicking stop, DDMS downloads
the trace file and starts Traceview.
Also refactored some common parts of the HPROF and tracing handlers into a
common class. The goal is to have a default, extensible implementation
of the HPROF handler that DDMS and the plug-in can reuse. This will reduce
duplicated code.
Change-Id: Ifc48926c7f6f1c3ea49a4aa94053664be83cbb06
Diffstat (limited to 'ddms/app/src')
-rw-r--r-- | ddms/app/src/com/android/ddms/PrefsDialog.java | 9 | ||||
-rw-r--r-- | ddms/app/src/com/android/ddms/UIThread.java | 258 |
2 files changed, 154 insertions, 113 deletions
diff --git a/ddms/app/src/com/android/ddms/PrefsDialog.java b/ddms/app/src/com/android/ddms/PrefsDialog.java index 69c48b0..19d1273 100644 --- a/ddms/app/src/com/android/ddms/PrefsDialog.java +++ b/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/ddms/app/src/com/android/ddms/UIThread.java b/ddms/app/src/com/android/ddms/UIThread.java index 0e091e9..61df0ab 100644 --- a/ddms/app/src/com/android/ddms/UIThread.java +++ b/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,27 +981,6 @@ public class UIThread implements IUiSelectionListener { private void createDevicePanelToolBar(ToolBar toolBar) { Display display = toolBar.getDisplay(); - // add "show thread updates" button - mTBShowThreadUpdates = new ToolItem(toolBar, SWT.CHECK); - mTBShowThreadUpdates.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, display, - DevicePanel.ICON_THREAD, DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null)); - mTBShowThreadUpdates.setToolTipText("Show thread updates"); - mTBShowThreadUpdates.setEnabled(false); - mTBShowThreadUpdates.addSelectionListener(new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent e) { - if (mCurrentClient != null) { - // boolean status = ((ToolItem)e.item).getSelection(); - // invert previous state - boolean enable = !mCurrentClient.isThreadUpdateEnabled(); - - mCurrentClient.setThreadUpdateEnabled(enable); - } else { - e.doit = false; // this has no effect? - } - } - }); - // add "show heap updates" button mTBShowHeapUpdates = new ToolItem(toolBar, SWT.CHECK); mTBShowHeapUpdates.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, display, @@ -1042,27 +1001,26 @@ public class UIThread implements IUiSelectionListener { } }); - new ToolItem(toolBar, SWT.SEPARATOR); - - // add "kill VM" button; need to make this visually distinct from - // the status update buttons - mTBHalt = new ToolItem(toolBar, SWT.PUSH); - mTBHalt.setToolTipText("Halt the target VM"); - mTBHalt.setEnabled(false); - mTBHalt.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, display, - DevicePanel.ICON_HALT, DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null)); - mTBHalt.addSelectionListener(new SelectionAdapter() { + // 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.killSelectedClient(); + 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(); } }); - 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.setToolTipText("Cause an immediate GC"); mTBCauseGc.setEnabled(false); mTBCauseGc.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, display, DevicePanel.ICON_GC, DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null)); @@ -1073,20 +1031,60 @@ 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() { + new ToolItem(toolBar, SWT.SEPARATOR); + + // add "show thread updates" button + mTBShowThreadUpdates = new ToolItem(toolBar, SWT.CHECK); + mTBShowThreadUpdates.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, display, + DevicePanel.ICON_THREAD, DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null)); + mTBShowThreadUpdates.setToolTipText("Show thread updates"); + mTBShowThreadUpdates.setEnabled(false); + mTBShowThreadUpdates.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { - mDevicePanel.dumpHprof(); + if (mCurrentClient != null) { + // boolean status = ((ToolItem)e.item).getSelection(); + // invert previous state + boolean enable = !mCurrentClient.isThreadUpdateEnabled(); - // this will make sure the dump hprof button is disabled for the current selection - // as the client is already dumping an hprof file - enableButtons(); + mCurrentClient.setThreadUpdateEnabled(enable); + } else { + e.doit = false; // this has no effect? + } + } + }); + + // 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) { + mDevicePanel.toggleMethodProfiling(); + } + }); + + new ToolItem(toolBar, SWT.SEPARATOR); + + // add "kill VM" button; need to make this visually distinct from + // the status update buttons + mTBHalt = new ToolItem(toolBar, SWT.PUSH); + mTBHalt.setToolTipText("Halt the target VM"); + mTBHalt.setEnabled(false); + mTBHalt.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, display, + DevicePanel.ICON_HALT, DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null)); + mTBHalt.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + mDevicePanel.killSelectedClient(); } }); @@ -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(); + } + }); + } + } + } } |