From bcf38645bef192b380f0d338b263aea075cd1aeb Mon Sep 17 00:00:00 2001 From: Xavier Ducrohet Date: Wed, 2 Sep 2009 20:06:08 -0700 Subject: 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 --- ddms/app/.classpath | 1 + ddms/app/src/com/android/ddms/PrefsDialog.java | 9 +- ddms/app/src/com/android/ddms/UIThread.java | 258 ++++++++++++--------- .../src/com/android/ddmlib/AndroidDebugBridge.java | 2 +- .../libs/ddmlib/src/com/android/ddmlib/Client.java | 65 ++++-- .../ddmlib/src/com/android/ddmlib/ClientData.java | 163 +++++++++---- .../src/com/android/ddmlib/DdmConstants.java | 59 +++++ .../ddmlib/src/com/android/ddmlib/Debugger.java | 10 +- .../src/com/android/ddmlib/DeviceMonitor.java | 3 +- .../ddmlib/src/com/android/ddmlib/HandleHeap.java | 12 +- .../ddmlib/src/com/android/ddmlib/HandleHello.java | 1 + .../src/com/android/ddmlib/HandleProfiling.java | 63 ++++- .../ddmlib/src/com/android/ddmlib/HandleWait.java | 6 +- ddms/libs/ddmuilib/.classpath | 3 +- .../src/com/android/ddmuilib/AllocationPanel.java | 53 +++-- .../src/com/android/ddmuilib/DevicePanel.java | 24 +- .../src/com/android/ddmuilib/HeapPanel.java | 2 +- .../src/com/android/ddmuilib/InfoPanel.java | 2 +- .../src/com/android/ddmuilib/NativeHeapPanel.java | 2 +- .../src/com/android/ddmuilib/ThreadPanel.java | 2 +- .../android/ddmuilib/handler/BaseFileHandler.java | 103 ++++++++ .../ddmuilib/handler/MethodProfilingHandler.java | 154 ++++++++++++ .../src/resources/images/tracing_start.png | Bin 0 -> 227 bytes .../ddmuilib/src/resources/images/tracing_stop.png | Bin 0 -> 217 bytes 24 files changed, 771 insertions(+), 226 deletions(-) create mode 100644 ddms/libs/ddmlib/src/com/android/ddmlib/DdmConstants.java create mode 100644 ddms/libs/ddmuilib/src/com/android/ddmuilib/handler/BaseFileHandler.java create mode 100644 ddms/libs/ddmuilib/src/com/android/ddmuilib/handler/MethodProfilingHandler.java create mode 100644 ddms/libs/ddmuilib/src/resources/images/tracing_start.png create mode 100644 ddms/libs/ddmuilib/src/resources/images/tracing_stop.png (limited to 'ddms') diff --git a/ddms/app/.classpath b/ddms/app/.classpath index 2fa1fb7..1040688 100644 --- a/ddms/app/.classpath +++ b/ddms/app/.classpath @@ -1,6 +1,7 @@ + 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(); + } + }); + } + } + } } diff --git a/ddms/libs/ddmlib/src/com/android/ddmlib/AndroidDebugBridge.java b/ddms/libs/ddmlib/src/com/android/ddmlib/AndroidDebugBridge.java index 9d6294a..6b9dccc 100644 --- a/ddms/libs/ddmlib/src/com/android/ddmlib/AndroidDebugBridge.java +++ b/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/ddms/libs/ddmlib/src/com/android/ddmlib/Client.java b/ddms/libs/ddmlib/src/com/android/ddmlib/Client.java index d51c6a0..d05fa14 100644 --- a/ddms/libs/ddmlib/src/com/android/ddmlib/Client.java +++ b/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/ddms/libs/ddmlib/src/com/android/ddmlib/ClientData.java b/ddms/libs/ddmlib/src/com/android/ddmlib/ClientData.java index eea609c..356f5d0 100644 --- a/ddms/libs/ddmlib/src/com/android/ddmlib/ClientData.java +++ b/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; - - /** - * 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 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; + } + + 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. @@ -277,6 +298,24 @@ public class ClientData { } /** + * 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. */ public static void setHprofDumpHandler(IHprofDumpHandler handler) { @@ -288,12 +327,23 @@ public class ClientData { } /** + * 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/ddms/libs/ddmlib/src/com/android/ddmlib/DdmConstants.java b/ddms/libs/ddmlib/src/com/android/ddmlib/DdmConstants.java new file mode 100644 index 0000000..d9823f3 --- /dev/null +++ b/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/ddms/libs/ddmlib/src/com/android/ddmlib/Debugger.java b/ddms/libs/ddmlib/src/com/android/ddmlib/Debugger.java index 39ec4b5..cebbc32 100644 --- a/ddms/libs/ddmlib/src/com/android/ddmlib/Debugger.java +++ b/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/ddms/libs/ddmlib/src/com/android/ddmlib/DeviceMonitor.java b/ddms/libs/ddmlib/src/com/android/ddmlib/DeviceMonitor.java index 3382067..402699c 100644 --- a/ddms/libs/ddmlib/src/com/android/ddmlib/DeviceMonitor.java +++ b/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/ddms/libs/ddmlib/src/com/android/ddmlib/HandleHeap.java b/ddms/libs/ddmlib/src/com/android/ddmlib/HandleHeap.java index 19f367b..23050af 100644 --- a/ddms/libs/ddmlib/src/com/android/ddmlib/HandleHeap.java +++ b/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/ddms/libs/ddmlib/src/com/android/ddmlib/HandleHello.java b/ddms/libs/ddmlib/src/com/android/ddmlib/HandleHello.java index 4818bd0..4e62bca 100644 --- a/ddms/libs/ddmlib/src/com/android/ddmlib/HandleHello.java +++ b/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/ddms/libs/ddmlib/src/com/android/ddmlib/HandleProfiling.java b/ddms/libs/ddmlib/src/com/android/ddmlib/HandleProfiling.java index 5fe1ed8..3b69973 100644 --- a/ddms/libs/ddmlib/src/com/android/ddmlib/HandleProfiling.java +++ b/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/ddms/libs/ddmlib/src/com/android/ddmlib/HandleWait.java b/ddms/libs/ddmlib/src/com/android/ddmlib/HandleWait.java index 480b525..934cbea 100644 --- a/ddms/libs/ddmlib/src/com/android/ddmlib/HandleWait.java +++ b/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/ddms/libs/ddmuilib/.classpath b/ddms/libs/ddmuilib/.classpath index ce7e7f0..2cd368c 100644 --- a/ddms/libs/ddmuilib/.classpath +++ b/ddms/libs/ddmuilib/.classpath @@ -1,6 +1,7 @@ - + + diff --git a/ddms/libs/ddmuilib/src/com/android/ddmuilib/AllocationPanel.java b/ddms/libs/ddmuilib/src/com/android/ddmuilib/AllocationPanel.java index 45d45ff..11c0e19 100644 --- a/ddms/libs/ddmuilib/src/com/android/ddmuilib/AllocationPanel.java +++ b/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/ddms/libs/ddmuilib/src/com/android/ddmuilib/DevicePanel.java b/ddms/libs/ddmuilib/src/com/android/ddmuilib/DevicePanel.java index 7532151..691692f 100644 --- a/ddms/libs/ddmuilib/src/com/android/ddmuilib/DevicePanel.java +++ b/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/ddms/libs/ddmuilib/src/com/android/ddmuilib/HeapPanel.java b/ddms/libs/ddmuilib/src/com/android/ddmuilib/HeapPanel.java index 977203b..f5cf9b1 100644 --- a/ddms/libs/ddmuilib/src/com/android/ddmuilib/HeapPanel.java +++ b/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/ddms/libs/ddmuilib/src/com/android/ddmuilib/InfoPanel.java b/ddms/libs/ddmuilib/src/com/android/ddmuilib/InfoPanel.java index 35e071d..6cbb999 100644 --- a/ddms/libs/ddmuilib/src/com/android/ddmuilib/InfoPanel.java +++ b/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/ddms/libs/ddmuilib/src/com/android/ddmuilib/NativeHeapPanel.java b/ddms/libs/ddmuilib/src/com/android/ddmuilib/NativeHeapPanel.java index 46461bf..0b2460b 100644 --- a/ddms/libs/ddmuilib/src/com/android/ddmuilib/NativeHeapPanel.java +++ b/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/ddms/libs/ddmuilib/src/com/android/ddmuilib/ThreadPanel.java b/ddms/libs/ddmuilib/src/com/android/ddmuilib/ThreadPanel.java index a034063..d94d4f3 100644 --- a/ddms/libs/ddmuilib/src/com/android/ddmuilib/ThreadPanel.java +++ b/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/ddms/libs/ddmuilib/src/com/android/ddmuilib/handler/BaseFileHandler.java b/ddms/libs/ddmuilib/src/com/android/ddmuilib/handler/BaseFileHandler.java new file mode 100644 index 0000000..3a2a2ef --- /dev/null +++ b/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/ddms/libs/ddmuilib/src/com/android/ddmuilib/handler/MethodProfilingHandler.java b/ddms/libs/ddmuilib/src/com/android/ddmuilib/handler/MethodProfilingHandler.java new file mode 100644 index 0000000..f469a7d --- /dev/null +++ b/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/ddms/libs/ddmuilib/src/resources/images/tracing_start.png b/ddms/libs/ddmuilib/src/resources/images/tracing_start.png new file mode 100644 index 0000000..88771cc Binary files /dev/null and b/ddms/libs/ddmuilib/src/resources/images/tracing_start.png differ diff --git a/ddms/libs/ddmuilib/src/resources/images/tracing_stop.png b/ddms/libs/ddmuilib/src/resources/images/tracing_stop.png new file mode 100644 index 0000000..71bd215 Binary files /dev/null and b/ddms/libs/ddmuilib/src/resources/images/tracing_stop.png differ -- cgit v1.1