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
diff --git a/tools/ddms/app/.classpath b/tools/ddms/app/.classpath
index 2fa1fb7..1040688 100644
--- a/tools/ddms/app/.classpath
+++ b/tools/ddms/app/.classpath
@@ -1,6 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <classpath>
 	<classpathentry excluding="Makefile|resources/" kind="src" path="src"/>
+	<classpathentry kind="src" path="src/resources"/>
 	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
 	<classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/ANDROID_SWT"/>
 	<classpathentry combineaccessrules="false" kind="src" path="/ddmlib"/>
diff --git a/tools/ddms/app/src/com/android/ddms/PrefsDialog.java b/tools/ddms/app/src/com/android/ddms/PrefsDialog.java
index 69c48b0..19d1273 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 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 @@
 
         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 0e091e9..61df0ab 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.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.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.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 @@
  * 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 @@
     private ToolItem mTBHalt;
     private ToolItem mTBCauseGc;
     private ToolItem mTBDumpHprof;
+    private ToolItem mTBProfiling;
 
     private ImageLoader mDdmsImageLoader;
     private ImageLoader mDdmuiLibImageLoader;
@@ -252,6 +252,10 @@
 
     private EventLogPanel mEventLogPanel;
 
+    private Image mTracingStartImage;
+
+    private Image mTracingStopImage;
+
 
     private class TableFocusListener implements ITableFocusListener {
 
@@ -292,23 +296,23 @@
 
     }
 
-    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 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 @@
                         // 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 @@
             });
         }
 
-        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 @@
 
         // 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 @@
         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 @@
     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 @@
             }
         });
 
-        // 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 @@
             }
         });
 
-        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 @@
             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 @@
             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 @@
             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 9d6294a..6b9dccc 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 @@
          * @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 d51c6a0..d05fa14 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 @@
     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 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 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.
+     * <p/>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 <code>changeMask</code>
+     * 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.
      * <p/>If <code>true</code> 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 eea609c..356f5d0 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 @@
     /** Temporary name of VM to be ignored. */
     private final static String PRE_INITIALIZED = "<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.
-     * <p/>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.
+         * <p/>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.
+         * <p/>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 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 @@
     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<String> mFeatures = new HashSet<String>();
@@ -156,10 +174,13 @@
     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.
      * <p/>The heap is composed of several {@link HeapSegment} objects.
@@ -264,10 +285,10 @@
     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 @@
     }
 
     /**
+     * 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 @@
     }
 
     /**
+     * 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<Integer,ThreadInfo>();
     }
 
@@ -367,18 +417,17 @@
     }
 
     /**
-     * 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 @@
         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 @@
         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 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 0000000..d9823f3
--- /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 39ec4b5..cebbc32 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 @@
                 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 @@
                     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 3382067..402699c 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 @@
                     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 19f367b..23050af 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 @@
 
         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 @@
 
                 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 @@
          */
 //xxx todo: only seal data that belongs to the heap mentioned in <data>.
         client.getClientData().getVmHeapData().sealHeapData();
+        client.update(Client.CHANGE_HEAP_DATA);
     }
 
     /*
@@ -332,7 +331,9 @@
         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 @@
         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 4818bd0..4e62bca 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 @@
             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 5fe1ed8..3b69973 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 @@
     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 @@
             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 @@
         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 @@
     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 @@
         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 480b525..934cbea 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 @@
 
         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 ce7e7f0..2cd368c 100644
--- a/tools/ddms/libs/ddmuilib/.classpath
+++ b/tools/ddms/libs/ddmuilib/.classpath
@@ -1,6 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <classpath>
-	<classpathentry excluding="Makefile|resources" kind="src" path="src"/>
+	<classpathentry excluding="Makefile|resources/" kind="src" path="src"/>
+	<classpathentry kind="src" path="src/resources"/>
 	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
 	<classpathentry combineaccessrules="false" kind="src" path="/ddmlib"/>
 	<classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/ANDROID_SWT"/>
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 45d45ff..11c0e19 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 @@
 
 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 @@
     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 @@
     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 @@
         // 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 @@
                 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 @@
                 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 @@
                 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 @@
                 PREFS_STACK_COL_LINE,
                 PREFS_STACK_COL_NATIVE,
                 store);
-        
+
         // now setup the sash.
         // form layout data
         FormData data = new FormData();
@@ -313,7 +313,7 @@
 
         return mAllocationBase;
     }
-    
+
     /**
      * Sets the focus to the proper control inside the panel.
      */
@@ -329,7 +329,7 @@
      * @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 @@
         }
 
         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.
      * <p/>
@@ -406,7 +405,7 @@
         if (client != null) {
             // get the current selection in the ThreadTable
             AllocationInfo selectedAlloc = getAllocationSelection(null);
-            
+
             if (selectedAlloc != null) {
                 updateAllocationStackTrace(selectedAlloc);
             } else {
@@ -441,7 +440,7 @@
         if (selection == null) {
             selection = mAllocationViewer.getSelection();
         }
-        
+
         if (selection instanceof IStructuredSelection) {
             IStructuredSelection structuredSelection = (IStructuredSelection)selection;
             Object object = structuredSelection.getFirstElement();
@@ -449,29 +448,29 @@
                 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 7532151..691692f 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.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 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 @@
                 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 void toggleMethodProfiling() {
+        if (mCurrentClient != null) {
+            mCurrentClient.toggleMethodProfiling();
+        }
+    }
 
     public void setEnabledHeapOnSelectedClient(boolean enable) {
         if (mCurrentClient != null) {
@@ -594,7 +602,7 @@
      * @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 @@
                     // 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 977203b..f5cf9b1 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 @@
      * @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 35e071d..6cbb999 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 @@
      * @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 46461bf..0b2460b 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 @@
      * @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 a034063..d94d4f3 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 @@
      * @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 0000000..3a2a2ef
--- /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.
+     * <p/>This <strong>must</strong> 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 0000000..f469a7d
--- /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 0000000..88771cc
--- /dev/null
+++ b/tools/ddms/libs/ddmuilib/src/resources/images/tracing_start.png
Binary files 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 0000000..71bd215
--- /dev/null
+++ b/tools/ddms/libs/ddmuilib/src/resources/images/tracing_stop.png
Binary files differ
diff --git a/tools/eclipse/plugins/.gitignore b/tools/eclipse/plugins/.gitignore
index cc105d2..6c13a2b 100644
--- a/tools/eclipse/plugins/.gitignore
+++ b/tools/eclipse/plugins/.gitignore
@@ -40,6 +40,8 @@
 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 b834bcf..eab0c6c 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.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 @@
      * @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 @@
                 }
 
                 // 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 @@
         // 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 631b391..76b005e 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 @@
 
 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 @@
 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 @@
         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 @@
 
         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 50eee26..71efb50 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.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 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.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 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 @@
 
     private static DeviceView sThis;
 
+    private Shell mParentShell;
     private DevicePanel mDeviceList;
+
     private Action mResetAdbAction;
     private Action mCaptureAction;
     private Action mUpdateThreadAction;
@@ -79,29 +79,29 @@
     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 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 @@
                             // 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 @@
             });
         }
 
-        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 @@
                         fileStore);
             }
         }
+
+        private void displayError(String format, Object... args) {
+            MessageDialog.openError(mParentShell, "HPROF Error",
+                    String.format(format, args));
+        }
     }
 
 
@@ -244,7 +216,10 @@
 
     @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 @@
         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 @@
 
                         // 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 @@
 
             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 @@
             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 @@
 
         // 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 @@
 
         // 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 a5b20a8..87cdf68 100755
--- a/tools/eclipse/scripts/create_ddms_symlinks.sh
+++ b/tools/eclipse/scripts/create_ddms_symlinks.sh
@@ -70,7 +70,7 @@
     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