aboutsummaryrefslogtreecommitdiffstats
path: root/ddms
diff options
context:
space:
mode:
authorXavier Ducrohet <xav@android.com>2009-09-02 20:06:08 -0700
committerXavier Ducrohet <xav@android.com>2009-09-03 16:01:27 -0700
commitbcf38645bef192b380f0d338b263aea075cd1aeb (patch)
treeff0c8c82db1b0b15b666639f9d98ca680a7687f3 /ddms
parent816c14d9bdb50bb2b8d4bc1e970858955cbbbfda (diff)
downloadsdk-bcf38645bef192b380f0d338b263aea075cd1aeb.zip
sdk-bcf38645bef192b380f0d338b263aea075cd1aeb.tar.gz
sdk-bcf38645bef192b380f0d338b263aea075cd1aeb.tar.bz2
Add method profiling support to DDMS.
New button allows to start/stop tracing. When clicking stop, DDMS downloads the trace file and starts Traceview. Also refactored some common parts of the HPROF and tracing handlers into a common class. The goal is to have a default, extensible implementation of the HPROF handler that DDMS and the plug-in can reuse. This will reduce duplicated code. Change-Id: Ifc48926c7f6f1c3ea49a4aa94053664be83cbb06
Diffstat (limited to 'ddms')
-rw-r--r--ddms/app/.classpath1
-rw-r--r--ddms/app/src/com/android/ddms/PrefsDialog.java9
-rw-r--r--ddms/app/src/com/android/ddms/UIThread.java258
-rw-r--r--ddms/libs/ddmlib/src/com/android/ddmlib/AndroidDebugBridge.java2
-rw-r--r--ddms/libs/ddmlib/src/com/android/ddmlib/Client.java65
-rw-r--r--ddms/libs/ddmlib/src/com/android/ddmlib/ClientData.java163
-rw-r--r--ddms/libs/ddmlib/src/com/android/ddmlib/DdmConstants.java59
-rw-r--r--ddms/libs/ddmlib/src/com/android/ddmlib/Debugger.java10
-rw-r--r--ddms/libs/ddmlib/src/com/android/ddmlib/DeviceMonitor.java3
-rw-r--r--ddms/libs/ddmlib/src/com/android/ddmlib/HandleHeap.java12
-rw-r--r--ddms/libs/ddmlib/src/com/android/ddmlib/HandleHello.java1
-rw-r--r--ddms/libs/ddmlib/src/com/android/ddmlib/HandleProfiling.java63
-rw-r--r--ddms/libs/ddmlib/src/com/android/ddmlib/HandleWait.java6
-rw-r--r--ddms/libs/ddmuilib/.classpath3
-rw-r--r--ddms/libs/ddmuilib/src/com/android/ddmuilib/AllocationPanel.java53
-rw-r--r--ddms/libs/ddmuilib/src/com/android/ddmuilib/DevicePanel.java24
-rw-r--r--ddms/libs/ddmuilib/src/com/android/ddmuilib/HeapPanel.java2
-rw-r--r--ddms/libs/ddmuilib/src/com/android/ddmuilib/InfoPanel.java2
-rw-r--r--ddms/libs/ddmuilib/src/com/android/ddmuilib/NativeHeapPanel.java2
-rw-r--r--ddms/libs/ddmuilib/src/com/android/ddmuilib/ThreadPanel.java2
-rw-r--r--ddms/libs/ddmuilib/src/com/android/ddmuilib/handler/BaseFileHandler.java103
-rw-r--r--ddms/libs/ddmuilib/src/com/android/ddmuilib/handler/MethodProfilingHandler.java154
-rw-r--r--ddms/libs/ddmuilib/src/resources/images/tracing_start.pngbin0 -> 227 bytes
-rw-r--r--ddms/libs/ddmuilib/src/resources/images/tracing_stop.pngbin0 -> 217 bytes
24 files changed, 771 insertions, 226 deletions
diff --git a/ddms/app/.classpath b/ddms/app/.classpath
index 2fa1fb7..1040688 100644
--- a/ddms/app/.classpath
+++ b/ddms/app/.classpath
@@ -1,6 +1,7 @@
<?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/ddms/app/src/com/android/ddms/PrefsDialog.java b/ddms/app/src/com/android/ddms/PrefsDialog.java
index 69c48b0..19d1273 100644
--- a/ddms/app/src/com/android/ddms/PrefsDialog.java
+++ b/ddms/app/src/com/android/ddms/PrefsDialog.java
@@ -17,6 +17,7 @@
package com.android.ddms;
+import com.android.ddmlib.DdmConstants;
import com.android.ddmlib.DdmPreferences;
import com.android.ddmlib.Log;
import com.android.ddmlib.Log.LogLevel;
@@ -126,9 +127,9 @@ public final class PrefsDialog {
*/
public static void init() {
assert mPrefStore == null;
-
+
mPrefStore = SdkStatsService.getPreferenceStore();
-
+
if (mPrefStore == null) {
// we have a serious issue here...
Log.e("ddms",
@@ -158,9 +159,9 @@ public final class PrefsDialog {
String traceview = System.getProperty("com.android.ddms.bindir"); //$NON-NLS-1$
if (traceview != null && traceview.length() != 0) {
- traceview += File.separator + "traceview"; //$NON-NLS-1$
+ traceview += File.separator + DdmConstants.FN_TRACEVIEW;
} else {
- traceview = "traceview"; //$NON-NLS-1$
+ traceview = DdmConstants.FN_TRACEVIEW;
}
DdmUiPreferences.setTraceviewLocation(traceview);
diff --git a/ddms/app/src/com/android/ddms/UIThread.java b/ddms/app/src/com/android/ddms/UIThread.java
index 0e091e9..61df0ab 100644
--- a/ddms/app/src/com/android/ddms/UIThread.java
+++ b/ddms/app/src/com/android/ddms/UIThread.java
@@ -22,7 +22,9 @@ import com.android.ddmlib.ClientData;
import com.android.ddmlib.IDevice;
import com.android.ddmlib.Log;
import com.android.ddmlib.SyncService;
+import com.android.ddmlib.AndroidDebugBridge.IClientChangeListener;
import com.android.ddmlib.ClientData.IHprofDumpHandler;
+import com.android.ddmlib.ClientData.MethodProfilingStatus;
import com.android.ddmlib.Log.ILogOutput;
import com.android.ddmlib.Log.LogLevel;
import com.android.ddmlib.SyncService.SyncResult;
@@ -36,23 +38,21 @@ import com.android.ddmuilib.ImageLoader;
import com.android.ddmuilib.InfoPanel;
import com.android.ddmuilib.NativeHeapPanel;
import com.android.ddmuilib.ScreenShotDialog;
-import com.android.ddmuilib.SyncProgressMonitor;
import com.android.ddmuilib.SysinfoPanel;
import com.android.ddmuilib.TablePanel;
import com.android.ddmuilib.ThreadPanel;
import com.android.ddmuilib.DevicePanel.IUiSelectionListener;
import com.android.ddmuilib.actions.ToolItemAction;
import com.android.ddmuilib.explorer.DeviceExplorer;
+import com.android.ddmuilib.handler.BaseFileHandler;
+import com.android.ddmuilib.handler.MethodProfilingHandler;
import com.android.ddmuilib.log.event.EventLogPanel;
import com.android.ddmuilib.logcat.LogColors;
import com.android.ddmuilib.logcat.LogFilter;
import com.android.ddmuilib.logcat.LogPanel;
import com.android.ddmuilib.logcat.LogPanel.ILogFilterStorageManager;
-import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.dialogs.MessageDialog;
-import org.eclipse.jface.dialogs.ProgressMonitorDialog;
-import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.preference.PreferenceStore;
import org.eclipse.swt.SWT;
@@ -81,7 +81,6 @@ import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
-import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Menu;
@@ -102,7 +101,7 @@ import java.util.ArrayList;
* SWT application. So this class mainly builds the ui, and manages communication between the panels
* when {@link IDevice} / {@link Client} selection changes.
*/
-public class UIThread implements IUiSelectionListener {
+public class UIThread implements IUiSelectionListener, IClientChangeListener {
/*
* UI tab panel definitions. The constants here must match up with the array
* indices in mPanels. PANEL_CLIENT_LIST is a "virtual" panel representing
@@ -175,6 +174,7 @@ public class UIThread implements IUiSelectionListener {
private ToolItem mTBHalt;
private ToolItem mTBCauseGc;
private ToolItem mTBDumpHprof;
+ private ToolItem mTBProfiling;
private ImageLoader mDdmsImageLoader;
private ImageLoader mDdmuiLibImageLoader;
@@ -252,6 +252,10 @@ public class UIThread implements IUiSelectionListener {
private EventLogPanel mEventLogPanel;
+ private Image mTracingStartImage;
+
+ private Image mTracingStopImage;
+
private class TableFocusListener implements ITableFocusListener {
@@ -292,23 +296,23 @@ public class UIThread implements IUiSelectionListener {
}
- private class HProfHandler implements IHprofDumpHandler {
-
- private final Shell mParentShell;
+ /**
+ * Handler for HPROF dumps.
+ * This will always prompt the user to save the HPROF file.
+ */
+ private class HProfHandler extends BaseFileHandler implements IHprofDumpHandler {
public HProfHandler(Shell parentShell) {
- mParentShell = parentShell;
+ super(parentShell);
}
public void onFailure(final Client client) {
mDisplay.asyncExec(new Runnable() {
public void run() {
try {
- MessageDialog.openError(mParentShell, "HPROF Error",
- String.format(
- "Unable to create HPROF file for application '%1$s'.\n" +
- "Check logcat for more information.",
- client.getClientData().getClientDescription()));
+ displayError("Unable to create HPROF file for application '%1$s'.\n" +
+ "Check logcat for more information.",
+ client.getClientData().getClientDescription());
} finally {
// this will make sure the dump hprof button is re-enabled for the
// current selection. as the client is finished dumping an hprof file
@@ -318,7 +322,7 @@ public class UIThread implements IUiSelectionListener {
});
}
- public void onSuccess(final String file, final Client client) {
+ public void onSuccess(final String remoteFilePath, final Client client) {
mDisplay.asyncExec(new Runnable() {
public void run() {
final IDevice device = client.getDevice();
@@ -326,17 +330,21 @@ public class UIThread implements IUiSelectionListener {
// get the sync service to pull the HPROF file
final SyncService sync = client.getDevice().getSyncService();
if (sync != null) {
- promptAndPull(device, client, sync, file);
+ SyncResult result = promptAndPull(sync,
+ client.getClientData().getClientDescription() + ".hprof",
+ remoteFilePath, "Save HPROF file");
+ if (result != null && result.getCode() != SyncService.RESULT_OK) {
+ displayError(
+ "Unable to download HPROF file from device '%1$s'.\n\n%2$s",
+ device.getSerialNumber(), result.getMessage());
+ }
} else {
- MessageDialog.openError(mParentShell, "HPROF Error",
- String.format(
- "Unable to download HPROF file from device '%1$s'.",
- device.getSerialNumber()));
+ displayError("Unable to download HPROF file from device '%1$s'.",
+ device.getSerialNumber());
}
} catch (Exception e) {
- MessageDialog.openError(mParentShell, "HPROF Error",
- String.format("Unable to download HPROF file from device '%1$s'.",
- device.getSerialNumber()));
+ displayError("Unable to download HPROF file from device '%1$s'.",
+ device.getSerialNumber());
} finally {
// this will make sure the dump hprof button is re-enabled for the
@@ -347,45 +355,13 @@ public class UIThread implements IUiSelectionListener {
});
}
- private void promptAndPull(final IDevice device, final Client client,
- final SyncService sync, final String remoteFile) {
- try {
- FileDialog fileDialog = new FileDialog(mParentShell, SWT.SAVE);
-
- fileDialog.setText("Save HPROF file");
- fileDialog.setFileName(
- client.getClientData().getClientDescription() + ".hprof");
-
- final String localFileName = fileDialog.open();
- if (localFileName != null) {
- final File localFile = new File(localFileName);
-
- new ProgressMonitorDialog(mParentShell).run(true, true,
- new IRunnableWithProgress() {
- public void run(IProgressMonitor monitor) {
- SyncResult result = sync.pullFile(remoteFile, localFileName,
- new SyncProgressMonitor(monitor, String.format(
- "Pulling %1$s from the device",
- localFile.getName())));
-
- if (result.getCode() != SyncService.RESULT_OK) {
- MessageDialog.openError(mParentShell, "HPROF Error",
- String.format("Failed to pull %1$s: %2$s", remoteFile,
- result.getMessage()));
- }
-
- sync.close();
- }
- });
- }
- } catch (Exception e) {
- MessageDialog.openError(mParentShell, "HPROF Error",
- String.format("Unable to download HPROF file from device '%1$s'.",
- device.getSerialNumber()));
- }
+ private void displayError(String format, Object... args) {
+ MessageDialog.openError(mParentShell, "HPROF Error",
+ String.format(format, args));
}
}
+
/**
* Generic constructor.
*/
@@ -467,6 +443,7 @@ public class UIThread implements IUiSelectionListener {
// set the handler for hprof dump
ClientData.setHprofDumpHandler(new HProfHandler(shell));
+ ClientData.setMethodProfilingHandler(new MethodProfilingHandler(shell));
// [try to] ensure ADB is running
String adbLocation = System.getProperty("com.android.ddms.bindir"); //$NON-NLS-1$
@@ -479,6 +456,9 @@ public class UIThread implements IUiSelectionListener {
AndroidDebugBridge.init(true /* debugger support */);
AndroidDebugBridge.createBridge(adbLocation, true /* forceNewBridge */);
+ // we need to listen to client change to be notified of client status (profiling) change
+ AndroidDebugBridge.addClientChangeListener(this);
+
shell.setText("Dalvik Debug Monitor");
setConfirmClose(shell);
createMenus(shell);
@@ -1001,27 +981,6 @@ public class UIThread implements IUiSelectionListener {
private void createDevicePanelToolBar(ToolBar toolBar) {
Display display = toolBar.getDisplay();
- // add "show thread updates" button
- mTBShowThreadUpdates = new ToolItem(toolBar, SWT.CHECK);
- mTBShowThreadUpdates.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, display,
- DevicePanel.ICON_THREAD, DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
- mTBShowThreadUpdates.setToolTipText("Show thread updates");
- mTBShowThreadUpdates.setEnabled(false);
- mTBShowThreadUpdates.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetSelected(SelectionEvent e) {
- if (mCurrentClient != null) {
- // boolean status = ((ToolItem)e.item).getSelection();
- // invert previous state
- boolean enable = !mCurrentClient.isThreadUpdateEnabled();
-
- mCurrentClient.setThreadUpdateEnabled(enable);
- } else {
- e.doit = false; // this has no effect?
- }
- }
- });
-
// add "show heap updates" button
mTBShowHeapUpdates = new ToolItem(toolBar, SWT.CHECK);
mTBShowHeapUpdates.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, display,
@@ -1042,27 +1001,26 @@ public class UIThread implements IUiSelectionListener {
}
});
- new ToolItem(toolBar, SWT.SEPARATOR);
-
- // add "kill VM" button; need to make this visually distinct from
- // the status update buttons
- mTBHalt = new ToolItem(toolBar, SWT.PUSH);
- mTBHalt.setToolTipText("Halt the target VM");
- mTBHalt.setEnabled(false);
- mTBHalt.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, display,
- DevicePanel.ICON_HALT, DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
- mTBHalt.addSelectionListener(new SelectionAdapter() {
+ // add "dump HPROF" button
+ mTBDumpHprof = new ToolItem(toolBar, SWT.PUSH);
+ mTBDumpHprof.setToolTipText("Dump HPROF file");
+ mTBDumpHprof.setEnabled(false);
+ mTBDumpHprof.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, display,
+ DevicePanel.ICON_HPROF, DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
+ mTBDumpHprof.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
- mDevicePanel.killSelectedClient();
+ mDevicePanel.dumpHprof();
+
+ // this will make sure the dump hprof button is disabled for the current selection
+ // as the client is already dumping an hprof file
+ enableButtons();
}
});
- new ToolItem(toolBar, SWT.SEPARATOR);
-
// add "cause GC" button
mTBCauseGc = new ToolItem(toolBar, SWT.PUSH);
- mTBCauseGc.setToolTipText("Cause an immediate GC in the target VM");
+ mTBCauseGc.setToolTipText("Cause an immediate GC");
mTBCauseGc.setEnabled(false);
mTBCauseGc.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, display,
DevicePanel.ICON_GC, DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
@@ -1073,20 +1031,60 @@ public class UIThread implements IUiSelectionListener {
}
});
- // add "cause GC" button
- mTBDumpHprof = new ToolItem(toolBar, SWT.PUSH);
- mTBDumpHprof.setToolTipText("Dump HPROF file");
- mTBDumpHprof.setEnabled(false);
- mTBDumpHprof.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, display,
- DevicePanel.ICON_HPROF, DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
- mTBDumpHprof.addSelectionListener(new SelectionAdapter() {
+ new ToolItem(toolBar, SWT.SEPARATOR);
+
+ // add "show thread updates" button
+ mTBShowThreadUpdates = new ToolItem(toolBar, SWT.CHECK);
+ mTBShowThreadUpdates.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, display,
+ DevicePanel.ICON_THREAD, DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
+ mTBShowThreadUpdates.setToolTipText("Show thread updates");
+ mTBShowThreadUpdates.setEnabled(false);
+ mTBShowThreadUpdates.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
- mDevicePanel.dumpHprof();
+ if (mCurrentClient != null) {
+ // boolean status = ((ToolItem)e.item).getSelection();
+ // invert previous state
+ boolean enable = !mCurrentClient.isThreadUpdateEnabled();
- // this will make sure the dump hprof button is disabled for the current selection
- // as the client is already dumping an hprof file
- enableButtons();
+ mCurrentClient.setThreadUpdateEnabled(enable);
+ } else {
+ e.doit = false; // this has no effect?
+ }
+ }
+ });
+
+ // add a start/stop method tracing
+ mTracingStartImage = ImageHelper.loadImage(mDdmuiLibImageLoader, display,
+ DevicePanel.ICON_TRACING_START,
+ DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null);
+ mTracingStopImage = ImageHelper.loadImage(mDdmuiLibImageLoader, display,
+ DevicePanel.ICON_TRACING_STOP,
+ DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null);
+ mTBProfiling = new ToolItem(toolBar, SWT.PUSH);
+ mTBProfiling.setToolTipText("Start Method Profiling");
+ mTBProfiling.setEnabled(false);
+ mTBProfiling.setImage(mTracingStartImage);
+ mTBProfiling.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ mDevicePanel.toggleMethodProfiling();
+ }
+ });
+
+ new ToolItem(toolBar, SWT.SEPARATOR);
+
+ // add "kill VM" button; need to make this visually distinct from
+ // the status update buttons
+ mTBHalt = new ToolItem(toolBar, SWT.PUSH);
+ mTBHalt.setToolTipText("Halt the target VM");
+ mTBHalt.setEnabled(false);
+ mTBHalt.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, display,
+ DevicePanel.ICON_HALT, DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
+ mTBHalt.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ mDevicePanel.killSelectedClient();
}
});
@@ -1581,9 +1579,31 @@ public class UIThread implements IUiSelectionListener {
mTBShowHeapUpdates.setEnabled(true);
mTBHalt.setEnabled(true);
mTBCauseGc.setEnabled(true);
- mTBDumpHprof.setEnabled(
- mCurrentClient.getClientData().hasFeature(ClientData.FEATURE_HPROF) &&
- mCurrentClient.getClientData().hasPendingHprofDump() == false);
+
+ ClientData data = mCurrentClient.getClientData();
+
+ if (data.hasFeature(ClientData.FEATURE_HPROF)) {
+ mTBDumpHprof.setEnabled(data.hasPendingHprofDump() == false);
+ mTBDumpHprof.setToolTipText("Dump HPROF file");
+ } else {
+ mTBDumpHprof.setEnabled(false);
+ mTBDumpHprof.setToolTipText("Dump HPROF file (not supported by this VM)");
+ }
+
+ if (data.hasFeature(ClientData.FEATURE_PROFILING)) {
+ mTBProfiling.setEnabled(true);
+ if (data.getMethodProfilingStatus() == MethodProfilingStatus.ON) {
+ mTBProfiling.setToolTipText("Stop Method Profiling");
+ mTBProfiling.setImage(mTracingStopImage);
+ } else {
+ mTBProfiling.setToolTipText("Start Method Profiling");
+ mTBProfiling.setImage(mTracingStartImage);
+ }
+ } else {
+ mTBProfiling.setEnabled(false);
+ mTBProfiling.setImage(mTracingStartImage);
+ mTBProfiling.setToolTipText("Start Method Profiling (not supported by this VM)");
+ }
} else {
// list is empty, disable these
mTBShowThreadUpdates.setSelection(false);
@@ -1592,7 +1612,13 @@ public class UIThread implements IUiSelectionListener {
mTBShowHeapUpdates.setEnabled(false);
mTBHalt.setEnabled(false);
mTBCauseGc.setEnabled(false);
+
mTBDumpHprof.setEnabled(false);
+ mTBDumpHprof.setToolTipText("Dump HPROF file");
+
+ mTBProfiling.setEnabled(false);
+ mTBProfiling.setImage(mTracingStartImage);
+ mTBProfiling.setToolTipText("Start Method Profiling");
}
}
@@ -1635,4 +1661,18 @@ public class UIThread implements IUiSelectionListener {
enableButtons();
}
}
+
+ public void clientChanged(Client client, int changeMask) {
+ if ((changeMask & Client.CHANGE_METHOD_PROFILING_STATUS) ==
+ Client.CHANGE_METHOD_PROFILING_STATUS) {
+ if (mCurrentClient == client) {
+ mDisplay.asyncExec(new Runnable() {
+ public void run() {
+ // force refresh of the button enabled state.
+ enableButtons();
+ }
+ });
+ }
+ }
+ }
}
diff --git a/ddms/libs/ddmlib/src/com/android/ddmlib/AndroidDebugBridge.java b/ddms/libs/ddmlib/src/com/android/ddmlib/AndroidDebugBridge.java
index 9d6294a..6b9dccc 100644
--- a/ddms/libs/ddmlib/src/com/android/ddmlib/AndroidDebugBridge.java
+++ b/ddms/libs/ddmlib/src/com/android/ddmlib/AndroidDebugBridge.java
@@ -150,7 +150,7 @@ public final class AndroidDebugBridge {
* @param client the updated client.
* @param changeMask the bit mask describing the changed properties. It can contain
* any of the following values: {@link Client#CHANGE_INFO},
- * {@link Client#CHANGE_DEBUGGER_INTEREST}, {@link Client#CHANGE_THREAD_MODE},
+ * {@link Client#CHANGE_DEBUGGER_STATUS}, {@link Client#CHANGE_THREAD_MODE},
* {@link Client#CHANGE_THREAD_DATA}, {@link Client#CHANGE_HEAP_MODE},
* {@link Client#CHANGE_HEAP_DATA}, {@link Client#CHANGE_NATIVE_HEAP_DATA}
*/
diff --git a/ddms/libs/ddmlib/src/com/android/ddmlib/Client.java b/ddms/libs/ddmlib/src/com/android/ddmlib/Client.java
index d51c6a0..d05fa14 100644
--- a/ddms/libs/ddmlib/src/com/android/ddmlib/Client.java
+++ b/ddms/libs/ddmlib/src/com/android/ddmlib/Client.java
@@ -16,6 +16,7 @@
package com.android.ddmlib;
+import com.android.ddmlib.ClientData.MethodProfilingStatus;
import com.android.ddmlib.DebugPortManager.IDebugPortProvider;
import com.android.ddmlib.AndroidDebugBridge.IClientChangeListener;
@@ -40,32 +41,34 @@ public class Client {
private static final int SERVER_PROTOCOL_VERSION = 1;
/** Client change bit mask: application name change */
- public static final int CHANGE_NAME = 0x0001;
- /** Client change bit mask: debugger interest change */
- public static final int CHANGE_DEBUGGER_INTEREST = 0x0002;
+ public static final int CHANGE_NAME = 0x0001;
+ /** Client change bit mask: debugger status change */
+ public static final int CHANGE_DEBUGGER_STATUS = 0x0002;
/** Client change bit mask: debugger port change */
- public static final int CHANGE_PORT = 0x0004;
+ public static final int CHANGE_PORT = 0x0004;
/** Client change bit mask: thread update flag change */
- public static final int CHANGE_THREAD_MODE = 0x0008;
+ public static final int CHANGE_THREAD_MODE = 0x0008;
/** Client change bit mask: thread data updated */
- public static final int CHANGE_THREAD_DATA = 0x0010;
+ public static final int CHANGE_THREAD_DATA = 0x0010;
/** Client change bit mask: heap update flag change */
- public static final int CHANGE_HEAP_MODE = 0x0020;
+ public static final int CHANGE_HEAP_MODE = 0x0020;
/** Client change bit mask: head data updated */
- public static final int CHANGE_HEAP_DATA = 0x0040;
+ public static final int CHANGE_HEAP_DATA = 0x0040;
/** Client change bit mask: native heap data updated */
- public static final int CHANGE_NATIVE_HEAP_DATA = 0x0080;
+ public static final int CHANGE_NATIVE_HEAP_DATA = 0x0080;
/** Client change bit mask: thread stack trace updated */
- public static final int CHANGE_THREAD_STACKTRACE = 0x0100;
+ public static final int CHANGE_THREAD_STACKTRACE = 0x0100;
/** Client change bit mask: allocation information updated */
- public static final int CHANGE_HEAP_ALLOCATIONS = 0x0200;
+ public static final int CHANGE_HEAP_ALLOCATIONS = 0x0200;
/** Client change bit mask: allocation information updated */
- public static final int CHANGE_HEAP_ALLOCATION_STATUS = 0x0400;
+ public static final int CHANGE_HEAP_ALLOCATION_STATUS = 0x0400;
+ /** Client change bit mask: allocation information updated */
+ public static final int CHANGE_METHOD_PROFILING_STATUS = 0x0800;
/** Client change bit mask: combination of {@link Client#CHANGE_NAME},
- * {@link Client#CHANGE_DEBUGGER_INTEREST}, and {@link Client#CHANGE_PORT}.
+ * {@link Client#CHANGE_DEBUGGER_STATUS}, and {@link Client#CHANGE_PORT}.
*/
- public static final int CHANGE_INFO = CHANGE_NAME | CHANGE_DEBUGGER_INTEREST | CHANGE_PORT;
+ public static final int CHANGE_INFO = CHANGE_NAME | CHANGE_DEBUGGER_STATUS | CHANGE_PORT;
private SocketChannel mChan;
@@ -228,7 +231,7 @@ public class Client {
*/
public void dumpHprof() {
try {
- String file = "/sdcard/" + mClientData.getClientDescription().replaceAll("\\:", ".") +
+ String file = "/sdcard/" + mClientData.getClientDescription().replaceAll("\\:.*", "") +
".hprof";
HandleHeap.sendHPDU(this, file);
} catch (IOException e) {
@@ -237,6 +240,38 @@ public class Client {
}
}
+ public void toggleMethodProfiling() {
+ try {
+ if (mClientData.getMethodProfilingStatus() == MethodProfilingStatus.ON) {
+ HandleProfiling.sendMPRE(this);
+ } else {
+ String file = "/sdcard/" + mClientData.getClientDescription().replaceAll("\\:.*", "") +
+ ".trace";
+ HandleProfiling.sendMPRS(this, file, 8*1024*1024, 0 /*flags*/);
+ }
+ } catch (IOException e) {
+ Log.w("ddms", "Toggle method profiling failed");
+ // ignore
+ }
+ }
+
+ /**
+ * Sends a request to the VM to send the enable status of the method profiling.
+ * This is asynchronous.
+ * <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/ddms/libs/ddmlib/src/com/android/ddmlib/ClientData.java b/ddms/libs/ddmlib/src/com/android/ddmlib/ClientData.java
index eea609c..356f5d0 100644
--- a/ddms/libs/ddmlib/src/com/android/ddmlib/ClientData.java
+++ b/ddms/libs/ddmlib/src/com/android/ddmlib/ClientData.java
@@ -51,33 +51,50 @@ public class ClientData {
/** Temporary name of VM to be ignored. */
private final static String PRE_INITIALIZED = "<pre-initialized>"; //$NON-NLS-1$
- /** Debugger connection status: not waiting on one, not connected to one, but accepting
- * new connections. This is the default value. */
- public static final int DEBUGGER_DEFAULT = 1;
- /**
- * Debugger connection status: the application's VM is paused, waiting for a debugger to
- * connect to it before resuming. */
- public static final int DEBUGGER_WAITING = 2;
- /** Debugger connection status : Debugger is connected */
- public static final int DEBUGGER_ATTACHED = 3;
- /** Debugger connection status: The listening port for debugger connection failed to listen.
- * No debugger will be able to connect. */
- public static final int DEBUGGER_ERROR = 4;
-
- /**
- * Allocation tracking status: unknown.
- * <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 DebuggerStatus {
+ /** Debugger connection status: not waiting on one, not connected to one, but accepting
+ * new connections. This is the default value. */
+ DEFAULT,
+ /**
+ * Debugger connection status: the application's VM is paused, waiting for a debugger to
+ * connect to it before resuming. */
+ WAITING,
+ /** Debugger connection status : Debugger is connected */
+ ATTACHED,
+ /** Debugger connection status: The listening port for debugger connection failed to listen.
+ * No debugger will be able to connect. */
+ ERROR;
+ }
+
+ public static enum AllocationTrackingStatus {
+ /**
+ * Allocation tracking status: unknown.
+ * <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 class ClientData {
public final static String FEATURE_HPROF = "hprof-heap-dump"; // $NON-NLS-1$
private static IHprofDumpHandler sHprofDumpHandler;
+ private static IMethodProfilingHandler sMethodProfilingHandler;
// is this a DDM-aware client?
private boolean mIsDdmAware;
@@ -127,7 +145,7 @@ public class ClientData {
private String mClientDescription;
// how interested are we in a debugger?
- private int mDebuggerInterest;
+ private DebuggerStatus mDebuggerInterest;
// List of supported features by the client.
private final HashSet<String> mFeatures = new HashSet<String>();
@@ -156,10 +174,13 @@ public class ClientData {
private int mNativeTotalMemory;
private AllocationInfo[] mAllocations;
- private int mAllocationStatus = ALLOCATION_TRACKING_UNKNOWN;
+ private AllocationTrackingStatus mAllocationStatus = AllocationTrackingStatus.UNKNOWN;
private String mPendingHprofDump;
+ private MethodProfilingStatus mProfilingStatus = MethodProfilingStatus.UNKNOWN;
+ private String mPendingMethodProfiling;
+
/**
* Heap Information.
* <p/>The heap is composed of several {@link HeapSegment} objects.
@@ -264,10 +285,10 @@ public class ClientData {
public interface IHprofDumpHandler {
/**
* Called when a HPROF dump succeeded.
- * @param remoteFile the device-side filename of the HPROF file.
+ * @param remoteFilePath the device-side path of the HPROF file.
* @param client the client for which the HPROF file was.
*/
- void onSuccess(String remoteFile, Client client);
+ void onSuccess(String remoteFilePath, Client client);
/**
* Called when the HPROF dump failed.
@@ -277,6 +298,24 @@ public class ClientData {
}
/**
+ * Handlers able to act on Method profiling info
+ */
+ public interface IMethodProfilingHandler {
+ /**
+ * Called when a method tracing was successful.
+ * @param remoteFilePath the device-side path of the trace file.
+ * @param client the client that was profiled.
+ */
+ void onSuccess(String remoteFilePath, Client client);
+
+ /**
+ * Called when method tracing failed.
+ * @param client the client that was profiled.
+ */
+ void onFailure(Client client);
+ }
+
+ /**
* Sets the handler to receive notifications when an HPROF dump succeeded or failed.
*/
public static void setHprofDumpHandler(IHprofDumpHandler handler) {
@@ -288,12 +327,23 @@ public class ClientData {
}
/**
+ * Sets the handler to receive notifications when an HPROF dump succeeded or failed.
+ */
+ public static void setMethodProfilingHandler(IMethodProfilingHandler handler) {
+ sMethodProfilingHandler = handler;
+ }
+
+ static IMethodProfilingHandler getMethodProfilingHandler() {
+ return sMethodProfilingHandler;
+ }
+
+ /**
* Generic constructor.
*/
ClientData(int pid) {
mPid = pid;
- mDebuggerInterest = DEBUGGER_DEFAULT;
+ mDebuggerInterest = DebuggerStatus.DEFAULT;
mThreadMap = new TreeMap<Integer,ThreadInfo>();
}
@@ -367,18 +417,17 @@ public class ClientData {
}
/**
- * Returns the debugger connection status. Possible values are {@link #DEBUGGER_DEFAULT},
- * {@link #DEBUGGER_WAITING}, {@link #DEBUGGER_ATTACHED}, and {@link #DEBUGGER_ERROR}.
+ * Returns the debugger connection status.
*/
- public int getDebuggerConnectionStatus() {
+ public DebuggerStatus getDebuggerConnectionStatus() {
return mDebuggerInterest;
}
/**
* Sets debugger connection status.
*/
- void setDebuggerConnectionStatus(int val) {
- mDebuggerInterest = val;
+ void setDebuggerConnectionStatus(DebuggerStatus status) {
+ mDebuggerInterest = status;
}
/**
@@ -521,15 +570,15 @@ public class ClientData {
return mNativeLibMapInfo.iterator();
}
- synchronized void setAllocationStatus(boolean enabled) {
- mAllocationStatus = enabled ? ALLOCATION_TRACKING_ON : ALLOCATION_TRACKING_OFF;
+ synchronized void setAllocationStatus(AllocationTrackingStatus status) {
+ mAllocationStatus = status;
}
/**
* Returns the allocation tracking status.
* @see Client#requestAllocationStatus()
*/
- public synchronized int getAllocationStatus() {
+ public synchronized AllocationTrackingStatus getAllocationStatus() {
return mAllocationStatus;
}
@@ -561,10 +610,17 @@ public class ClientData {
return mFeatures.contains(feature);
}
+ /**
+ * Sets the device-side path to the hprof file being written
+ * @param pendingHprofDump the file to the hprof file
+ */
void setPendingHprofDump(String pendingHprofDump) {
mPendingHprofDump = pendingHprofDump;
}
+ /**
+ * Returns the path to the device-side hprof file being written.
+ */
String getPendingHprofDump() {
return mPendingHprofDump;
}
@@ -572,5 +628,32 @@ public class ClientData {
public boolean hasPendingHprofDump() {
return mPendingHprofDump != null;
}
+
+ synchronized void setMethodProfilingStatus(MethodProfilingStatus status) {
+ mProfilingStatus = status;
+ }
+
+ /**
+ * Returns the method profiling status.
+ * @see Client#requestMethodProfilingStatus()
+ */
+ public synchronized MethodProfilingStatus getMethodProfilingStatus() {
+ return mProfilingStatus;
+ }
+
+ /**
+ * Sets the device-side path to the method profile file being written
+ * @param pendingMethodProfiling the file being written
+ */
+ void setPendingMethodProfiling(String pendingMethodProfiling) {
+ mPendingMethodProfiling = pendingMethodProfiling;
+ }
+
+ /**
+ * Returns the path to the device-side method profiling file being written.
+ */
+ String getPendingMethodProfiling() {
+ return mPendingMethodProfiling;
+ }
}
diff --git a/ddms/libs/ddmlib/src/com/android/ddmlib/DdmConstants.java b/ddms/libs/ddmlib/src/com/android/ddmlib/DdmConstants.java
new file mode 100644
index 0000000..d9823f3
--- /dev/null
+++ b/ddms/libs/ddmlib/src/com/android/ddmlib/DdmConstants.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ddmlib;
+
+public final class DdmConstants {
+
+ public final static int PLATFORM_UNKNOWN = 0;
+ public final static int PLATFORM_LINUX = 1;
+ public final static int PLATFORM_WINDOWS = 2;
+ public final static int PLATFORM_DARWIN = 3;
+
+ /**
+ * Returns current platform, one of {@link #PLATFORM_WINDOWS}, {@link #PLATFORM_DARWIN},
+ * {@link #PLATFORM_LINUX} or {@link #PLATFORM_UNKNOWN}.
+ */
+ public final static int CURRENT_PLATFORM = currentPlatform();
+
+ /** hprof-conv executable (with extension for the current OS) */
+ public final static String FN_HPROF_CONVERTER = (CURRENT_PLATFORM == PLATFORM_WINDOWS) ?
+ "hprof-conv.exe" : "hprof-conv"; //$NON-NLS-1$ //$NON-NLS-2$
+
+ /** traceview executable (with extension for the current OS) */
+ public final static String FN_TRACEVIEW = (CURRENT_PLATFORM == PLATFORM_WINDOWS) ?
+ "traceview.bat" : "traceview"; //$NON-NLS-1$ //$NON-NLS-2$
+
+ /**
+ * Returns current platform
+ *
+ * @return one of {@link #PLATFORM_WINDOWS}, {@link #PLATFORM_DARWIN},
+ * {@link #PLATFORM_LINUX} or {@link #PLATFORM_UNKNOWN}.
+ */
+ public static int currentPlatform() {
+ String os = System.getProperty("os.name"); //$NON-NLS-1$
+ if (os.startsWith("Mac OS")) { //$NON-NLS-1$
+ return PLATFORM_DARWIN;
+ } else if (os.startsWith("Windows")) { //$NON-NLS-1$
+ return PLATFORM_WINDOWS;
+ } else if (os.startsWith("Linux")) { //$NON-NLS-1$
+ return PLATFORM_LINUX;
+ }
+
+ return PLATFORM_UNKNOWN;
+ }
+
+}
diff --git a/ddms/libs/ddmlib/src/com/android/ddmlib/Debugger.java b/ddms/libs/ddmlib/src/com/android/ddmlib/Debugger.java
index 39ec4b5..cebbc32 100644
--- a/ddms/libs/ddmlib/src/com/android/ddmlib/Debugger.java
+++ b/ddms/libs/ddmlib/src/com/android/ddmlib/Debugger.java
@@ -16,6 +16,8 @@
package com.android.ddmlib;
+import com.android.ddmlib.ClientData.DebuggerStatus;
+
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
@@ -165,8 +167,8 @@ class Debugger {
mConnState = ST_NOT_CONNECTED;
ClientData cd = mClient.getClientData();
- cd.setDebuggerConnectionStatus(ClientData.DEBUGGER_DEFAULT);
- mClient.update(Client.CHANGE_DEBUGGER_INTEREST);
+ cd.setDebuggerConnectionStatus(DebuggerStatus.DEFAULT);
+ mClient.update(Client.CHANGE_DEBUGGER_STATUS);
}
} catch (IOException ioe) {
Log.w("ddms", "Failed to close data " + this);
@@ -249,8 +251,8 @@ class Debugger {
mConnState = ST_READY;
ClientData cd = mClient.getClientData();
- cd.setDebuggerConnectionStatus(ClientData.DEBUGGER_ATTACHED);
- mClient.update(Client.CHANGE_DEBUGGER_INTEREST);
+ cd.setDebuggerConnectionStatus(DebuggerStatus.ATTACHED);
+ mClient.update(Client.CHANGE_DEBUGGER_STATUS);
// see if we have another packet in the buffer
return getJdwpPacket();
diff --git a/ddms/libs/ddmlib/src/com/android/ddmlib/DeviceMonitor.java b/ddms/libs/ddmlib/src/com/android/ddmlib/DeviceMonitor.java
index 3382067..402699c 100644
--- a/ddms/libs/ddmlib/src/com/android/ddmlib/DeviceMonitor.java
+++ b/ddms/libs/ddmlib/src/com/android/ddmlib/DeviceMonitor.java
@@ -17,6 +17,7 @@
package com.android.ddmlib;
import com.android.ddmlib.AdbHelper.AdbResponse;
+import com.android.ddmlib.ClientData.DebuggerStatus;
import com.android.ddmlib.DebugPortManager.IDebugPortProvider;
import com.android.ddmlib.IDevice.DeviceState;
@@ -748,7 +749,7 @@ final class DeviceMonitor {
client.listenForDebugger(debuggerPort);
}
} catch (IOException ioe) {
- client.getClientData().setDebuggerConnectionStatus(ClientData.DEBUGGER_ERROR);
+ client.getClientData().setDebuggerConnectionStatus(DebuggerStatus.ERROR);
Log.e("ddms", "Can't bind to local " + debuggerPort + " for debugger");
// oh well
}
diff --git a/ddms/libs/ddmlib/src/com/android/ddmlib/HandleHeap.java b/ddms/libs/ddmlib/src/com/android/ddmlib/HandleHeap.java
index 19f367b..23050af 100644
--- a/ddms/libs/ddmlib/src/com/android/ddmlib/HandleHeap.java
+++ b/ddms/libs/ddmlib/src/com/android/ddmlib/HandleHeap.java
@@ -16,6 +16,7 @@
package com.android.ddmlib;
+import com.android.ddmlib.ClientData.AllocationTrackingStatus;
import com.android.ddmlib.ClientData.IHprofDumpHandler;
import java.io.IOException;
@@ -93,22 +94,18 @@ final class HandleHeap extends ChunkHandler {
if (type == CHUNK_HPIF) {
handleHPIF(client, data);
- client.update(Client.CHANGE_HEAP_DATA);
} else if (type == CHUNK_HPST) {
handleHPST(client, data);
} else if (type == CHUNK_HPEN) {
handleHPEN(client, data);
- client.update(Client.CHANGE_HEAP_DATA);
} else if (type == CHUNK_HPSG) {
handleHPSG(client, data);
} else if (type == CHUNK_HPDU) {
handleHPDU(client, data);
} else if (type == CHUNK_REAQ) {
handleREAQ(client, data);
- client.update(Client.CHANGE_HEAP_ALLOCATION_STATUS);
} else if (type == CHUNK_REAL) {
handleREAL(client, data);
- client.update(Client.CHANGE_HEAP_ALLOCATIONS);
} else {
handleUnknownChunk(client, type, data, isReply, msgId);
}
@@ -135,6 +132,7 @@ final class HandleHeap extends ChunkHandler {
client.getClientData().setHeapInfo(heapId, maxHeapSize,
heapSize, bytesAllocated, objectsAllocated);
+ client.update(Client.CHANGE_HEAP_DATA);
}
} catch (BufferUnderflowException ex) {
Log.w("ddm-heap", "malformed HPIF chunk from client");
@@ -176,6 +174,7 @@ final class HandleHeap extends ChunkHandler {
*/
//xxx todo: only seal data that belongs to the heap mentioned in <data>.
client.getClientData().getVmHeapData().sealHeapData();
+ client.update(Client.CHANGE_HEAP_DATA);
}
/*
@@ -332,7 +331,9 @@ final class HandleHeap extends ChunkHandler {
enabled = (data.get() != 0);
Log.d("ddm-heap", "REAQ says: enabled=" + enabled);
- client.getClientData().setAllocationStatus(enabled);
+ client.getClientData().setAllocationStatus(enabled ?
+ AllocationTrackingStatus.ON : AllocationTrackingStatus.OFF);
+ client.update(Client.CHANGE_HEAP_ALLOCATION_STATUS);
}
/**
@@ -517,6 +518,7 @@ final class HandleHeap extends ChunkHandler {
Collections.sort(list);
client.getClientData().setAllocations(list.toArray(new AllocationInfo[numEntries]));
+ client.update(Client.CHANGE_HEAP_ALLOCATIONS);
}
/*
diff --git a/ddms/libs/ddmlib/src/com/android/ddmlib/HandleHello.java b/ddms/libs/ddmlib/src/com/android/ddmlib/HandleHello.java
index 4818bd0..4e62bca 100644
--- a/ddms/libs/ddmlib/src/com/android/ddmlib/HandleHello.java
+++ b/ddms/libs/ddmlib/src/com/android/ddmlib/HandleHello.java
@@ -64,6 +64,7 @@ final class HandleHello extends ChunkHandler {
throws IOException {
sendHELO(client, serverProtocolVersion);
sendFEAT(client);
+ HandleProfiling.sendMPRQ(client);
}
/**
diff --git a/ddms/libs/ddmlib/src/com/android/ddmlib/HandleProfiling.java b/ddms/libs/ddmlib/src/com/android/ddmlib/HandleProfiling.java
index 5fe1ed8..3b69973 100644
--- a/ddms/libs/ddmlib/src/com/android/ddmlib/HandleProfiling.java
+++ b/ddms/libs/ddmlib/src/com/android/ddmlib/HandleProfiling.java
@@ -16,6 +16,9 @@
package com.android.ddmlib;
+import com.android.ddmlib.ClientData.IMethodProfilingHandler;
+import com.android.ddmlib.ClientData.MethodProfilingStatus;
+
import java.io.IOException;
import java.nio.ByteBuffer;
@@ -27,6 +30,7 @@ final class HandleProfiling extends ChunkHandler {
public static final int CHUNK_MPRS = type("MPRS");
public static final int CHUNK_MPRE = type("MPRE");
public static final int CHUNK_MPRQ = type("MPRQ");
+ public static final int CHUNK_FAIL = type("FAIL");
private static final HandleProfiling mInst = new HandleProfiling();
@@ -65,6 +69,8 @@ final class HandleProfiling extends ChunkHandler {
handleMPRE(client, data);
} else if (type == CHUNK_MPRQ) {
handleMPRQ(client, data);
+ } else if (type == CHUNK_FAIL) {
+ handleFAIL(client, data);
} else {
handleUnknownChunk(client, type, data, isReply, msgId);
}
@@ -98,6 +104,13 @@ final class HandleProfiling extends ChunkHandler {
Log.d("ddm-prof", "Sending " + name(CHUNK_MPRS) + " '" + fileName
+ "', size=" + bufferSize + ", flags=" + flags);
client.sendAndConsume(packet, mInst);
+
+ // record the filename we asked for.
+ client.getClientData().setPendingMethodProfiling(fileName);
+
+ // send a status query. this ensure that the status is properly updated if for some
+ // reason starting the tracing failed.
+ sendMPRQ(client);
}
/**
@@ -122,15 +135,28 @@ final class HandleProfiling extends ChunkHandler {
private void handleMPRE(Client client, ByteBuffer data) {
byte result;
+ // get the filename and make the client not have pending HPROF dump anymore.
+ String filename = client.getClientData().getPendingMethodProfiling();
+ client.getClientData().setPendingMethodProfiling(null);
+
result = data.get();
- if (result == 0) {
- Log.d("ddm-prof", "Method profiling has finished");
- } else {
- Log.w("ddm-prof", "Method profiling has failed (check device log)");
+ // get the app-level handler for method tracing dump
+ IMethodProfilingHandler handler = ClientData.getMethodProfilingHandler();
+ if (handler != null) {
+ if (result == 0) {
+ handler.onSuccess(filename, client);
+
+ Log.d("ddm-prof", "Method profiling has finished");
+ } else {
+ handler.onFailure(client);
+
+ Log.w("ddm-prof", "Method profiling has failed (check device log)");
+ }
}
- // TODO: stuff
+ client.getClientData().setMethodProfilingStatus(MethodProfilingStatus.OFF);
+ client.update(Client.CHANGE_METHOD_PROFILING_STATUS);
}
/**
@@ -157,10 +183,37 @@ final class HandleProfiling extends ChunkHandler {
result = data.get();
if (result == 0) {
+ client.getClientData().setMethodProfilingStatus(MethodProfilingStatus.OFF);
Log.d("ddm-prof", "Method profiling is not running");
} else {
+ client.getClientData().setMethodProfilingStatus(MethodProfilingStatus.ON);
Log.d("ddm-prof", "Method profiling is running");
}
+ client.update(Client.CHANGE_METHOD_PROFILING_STATUS);
+ }
+
+ private void handleFAIL(Client client, ByteBuffer data) {
+ // this can be sent if MPRS failed (like wrong permission)
+
+ String filename = client.getClientData().getPendingMethodProfiling();
+ if (filename != null) {
+ // reset the pending file.
+ client.getClientData().setPendingMethodProfiling(null);
+
+ // and notify of failure
+ IMethodProfilingHandler handler = ClientData.getMethodProfilingHandler();
+ if (handler != null) {
+ handler.onFailure(client);
+ }
+
+ }
+
+ // send a query to know the current status
+ try {
+ sendMPRQ(client);
+ } catch (IOException e) {
+ Log.e("HandleProfiling", e);
+ }
}
}
diff --git a/ddms/libs/ddmlib/src/com/android/ddmlib/HandleWait.java b/ddms/libs/ddmlib/src/com/android/ddmlib/HandleWait.java
index 480b525..934cbea 100644
--- a/ddms/libs/ddmlib/src/com/android/ddmlib/HandleWait.java
+++ b/ddms/libs/ddmlib/src/com/android/ddmlib/HandleWait.java
@@ -16,6 +16,8 @@
package com.android.ddmlib;
+import com.android.ddmlib.ClientData.DebuggerStatus;
+
import java.io.IOException;
import java.nio.ByteBuffer;
@@ -80,10 +82,10 @@ final class HandleWait extends ChunkHandler {
ClientData cd = client.getClientData();
synchronized (cd) {
- cd.setDebuggerConnectionStatus(ClientData.DEBUGGER_WAITING);
+ cd.setDebuggerConnectionStatus(DebuggerStatus.WAITING);
}
- client.update(Client.CHANGE_DEBUGGER_INTEREST);
+ client.update(Client.CHANGE_DEBUGGER_STATUS);
}
}
diff --git a/ddms/libs/ddmuilib/.classpath b/ddms/libs/ddmuilib/.classpath
index ce7e7f0..2cd368c 100644
--- a/ddms/libs/ddmuilib/.classpath
+++ b/ddms/libs/ddmuilib/.classpath
@@ -1,6 +1,7 @@
<?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/ddms/libs/ddmuilib/src/com/android/ddmuilib/AllocationPanel.java b/ddms/libs/ddmuilib/src/com/android/ddmuilib/AllocationPanel.java
index 45d45ff..11c0e19 100644
--- a/ddms/libs/ddmuilib/src/com/android/ddmuilib/AllocationPanel.java
+++ b/ddms/libs/ddmuilib/src/com/android/ddmuilib/AllocationPanel.java
@@ -18,8 +18,8 @@ package com.android.ddmuilib;
import com.android.ddmlib.AllocationInfo;
import com.android.ddmlib.Client;
-import com.android.ddmlib.ClientData;
import com.android.ddmlib.AndroidDebugBridge.IClientChangeListener;
+import com.android.ddmlib.ClientData.AllocationTrackingStatus;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.viewers.ILabelProviderListener;
@@ -61,7 +61,7 @@ public class AllocationPanel extends TablePanel {
private final static String PREFS_ALLOC_COL_THREAD = "allocPanel.Col2"; //$NON-NLS-1$
private final static String PREFS_ALLOC_COL_TRACE_CLASS = "allocPanel.Col3"; //$NON-NLS-1$
private final static String PREFS_ALLOC_COL_TRACE_METHOD = "allocPanel.Col4"; //$NON-NLS-1$
-
+
private final static String PREFS_ALLOC_SASH = "allocPanel.sash"; //$NON-NLS-1$
private static final String PREFS_STACK_COL_CLASS = "allocPanel.stack.col0"; //$NON-NLS-1$
@@ -69,7 +69,7 @@ public class AllocationPanel extends TablePanel {
private static final String PREFS_STACK_COL_FILE = "allocPanel.stack.col2"; //$NON-NLS-1$
private static final String PREFS_STACK_COL_LINE = "allocPanel.stack.col3"; //$NON-NLS-1$
private static final String PREFS_STACK_COL_NATIVE = "allocPanel.stack.col4"; //$NON-NLS-1$
-
+
private Composite mAllocationBase;
private Table mAllocationTable;
private TableViewer mAllocationViewer;
@@ -171,18 +171,18 @@ public class AllocationPanel extends TablePanel {
// base composite for selected client with enabled thread update.
mAllocationBase = new Composite(parent, SWT.NONE);
mAllocationBase.setLayout(new FormLayout());
-
+
// table above the sash
Composite topParent = new Composite(mAllocationBase, SWT.NONE);
topParent.setLayout(new GridLayout(2, false));
-
+
mEnableButton = new Button(topParent, SWT.PUSH);
mEnableButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
Client current = getCurrentClient();
- int status = current.getClientData().getAllocationStatus();
- if (status == ClientData.ALLOCATION_TRACKING_ON) {
+ AllocationTrackingStatus status = current.getClientData().getAllocationStatus();
+ if (status == AllocationTrackingStatus.ON) {
current.enableAllocationTracker(false);
} else {
current.enableAllocationTracker(true);
@@ -199,8 +199,8 @@ public class AllocationPanel extends TablePanel {
getCurrentClient().requestAllocationDetails();
}
});
-
- setUpButtons(false /* enabled */, ClientData.ALLOCATION_TRACKING_OFF /* trackingStatus */);
+
+ setUpButtons(false /* enabled */, AllocationTrackingStatus.OFF);
mAllocationTable = new Table(topParent, SWT.MULTI | SWT.FULL_SELECTION);
GridData gridData;
@@ -243,7 +243,7 @@ public class AllocationPanel extends TablePanel {
SWT.LEFT,
"utime", //$NON-NLS-1$
PREFS_ALLOC_COL_TRACE_METHOD, store);
-
+
mAllocationViewer = new TableViewer(mAllocationTable);
mAllocationViewer.setContentProvider(new AllocationContentProvider());
mAllocationViewer.setLabelProvider(new AllocationLabelProvider());
@@ -254,12 +254,12 @@ public class AllocationPanel extends TablePanel {
updateAllocationStackTrace(selectedAlloc);
}
});
-
+
// the separating sash
final Sash sash = new Sash(mAllocationBase, SWT.HORIZONTAL);
Color darkGray = parent.getDisplay().getSystemColor(SWT.COLOR_DARK_GRAY);
sash.setBackground(darkGray);
-
+
// the UI below the sash
mStackTracePanel = new StackTracePanel();
mStackTraceTable = mStackTracePanel.createPanel(mAllocationBase,
@@ -269,7 +269,7 @@ public class AllocationPanel extends TablePanel {
PREFS_STACK_COL_LINE,
PREFS_STACK_COL_NATIVE,
store);
-
+
// now setup the sash.
// form layout data
FormData data = new FormData();
@@ -313,7 +313,7 @@ public class AllocationPanel extends TablePanel {
return mAllocationBase;
}
-
+
/**
* Sets the focus to the proper control inside the panel.
*/
@@ -329,7 +329,7 @@ public class AllocationPanel extends TablePanel {
* @param client the updated client.
* @param changeMask the bit mask describing the changed properties. It can contain
* any of the following values: {@link Client#CHANGE_INFO}, {@link Client#CHANGE_NAME}
- * {@link Client#CHANGE_DEBUGGER_INTEREST}, {@link Client#CHANGE_THREAD_MODE},
+ * {@link Client#CHANGE_DEBUGGER_STATUS}, {@link Client#CHANGE_THREAD_MODE},
* {@link Client#CHANGE_THREAD_DATA}, {@link Client#CHANGE_HEAP_MODE},
* {@link Client#CHANGE_HEAP_DATA}, {@link Client#CHANGE_NATIVE_HEAP_DATA}
*
@@ -382,20 +382,19 @@ public class AllocationPanel extends TablePanel {
}
Client client = getCurrentClient();
-
+
mStackTracePanel.setCurrentClient(client);
mStackTracePanel.setViewerInput(null); // always empty on client selection change.
if (client != null) {
setUpButtons(true /* enabled */, client.getClientData().getAllocationStatus());
} else {
- setUpButtons(false /* enabled */,
- ClientData.ALLOCATION_TRACKING_OFF /* trackingStatus */);
+ setUpButtons(false /* enabled */, AllocationTrackingStatus.OFF);
}
mAllocationViewer.setInput(client);
}
-
+
/**
* Updates the stack call of the currently selected thread.
* <p/>
@@ -406,7 +405,7 @@ public class AllocationPanel extends TablePanel {
if (client != null) {
// get the current selection in the ThreadTable
AllocationInfo selectedAlloc = getAllocationSelection(null);
-
+
if (selectedAlloc != null) {
updateAllocationStackTrace(selectedAlloc);
} else {
@@ -441,7 +440,7 @@ public class AllocationPanel extends TablePanel {
if (selection == null) {
selection = mAllocationViewer.getSelection();
}
-
+
if (selection instanceof IStructuredSelection) {
IStructuredSelection structuredSelection = (IStructuredSelection)selection;
Object object = structuredSelection.getFirstElement();
@@ -449,29 +448,29 @@ public class AllocationPanel extends TablePanel {
return (AllocationInfo)object;
}
}
-
+
return null;
}
/**
- *
+ *
* @param enabled
* @param trackingStatus
*/
- private void setUpButtons(boolean enabled, int trackingStatus) {
+ private void setUpButtons(boolean enabled, AllocationTrackingStatus trackingStatus) {
if (enabled) {
switch (trackingStatus) {
- case ClientData.ALLOCATION_TRACKING_UNKNOWN:
+ case UNKNOWN:
mEnableButton.setText("?");
mEnableButton.setEnabled(false);
mRequestButton.setEnabled(false);
break;
- case ClientData.ALLOCATION_TRACKING_OFF:
+ case OFF:
mEnableButton.setText("Start Tracking");
mEnableButton.setEnabled(true);
mRequestButton.setEnabled(false);
break;
- case ClientData.ALLOCATION_TRACKING_ON:
+ case ON:
mEnableButton.setText("Stop Tracking");
mEnableButton.setEnabled(true);
mRequestButton.setEnabled(true);
diff --git a/ddms/libs/ddmuilib/src/com/android/ddmuilib/DevicePanel.java b/ddms/libs/ddmuilib/src/com/android/ddmuilib/DevicePanel.java
index 7532151..691692f 100644
--- a/ddms/libs/ddmuilib/src/com/android/ddmuilib/DevicePanel.java
+++ b/ddms/libs/ddmuilib/src/com/android/ddmuilib/DevicePanel.java
@@ -24,6 +24,7 @@ import com.android.ddmlib.IDevice;
import com.android.ddmlib.AndroidDebugBridge.IClientChangeListener;
import com.android.ddmlib.AndroidDebugBridge.IDebugBridgeChangeListener;
import com.android.ddmlib.AndroidDebugBridge.IDeviceChangeListener;
+import com.android.ddmlib.ClientData.DebuggerStatus;
import com.android.ddmlib.IDevice.DeviceState;
import org.eclipse.jface.preference.IPreferenceStore;
@@ -76,6 +77,8 @@ public final class DevicePanel extends Panel implements IDebugBridgeChangeListen
public final static String ICON_HALT = "halt.png"; //$NON-NLS-1$
public final static String ICON_GC = "gc.png"; //$NON-NLS-1$
public final static String ICON_HPROF = "hprof.png"; //$NON-NLS-1$
+ public final static String ICON_TRACING_START = "tracing_start.png"; //$NON-NLS-1$
+ public final static String ICON_TRACING_STOP = "tracing_stop.png"; //$NON-NLS-1$
private IDevice mCurrentDevice;
private Client mCurrentClient;
@@ -167,13 +170,13 @@ public final class DevicePanel extends Panel implements IDebugBridgeChangeListen
switch (columnIndex) {
case CLIENT_COL_NAME:
switch (cd.getDebuggerConnectionStatus()) {
- case ClientData.DEBUGGER_DEFAULT:
+ case DEFAULT:
return null;
- case ClientData.DEBUGGER_WAITING:
+ case WAITING:
return mWaitingImage;
- case ClientData.DEBUGGER_ATTACHED:
+ case ATTACHED:
return mDebuggerImage;
- case ClientData.DEBUGGER_ERROR:
+ case ERROR:
return mDebugErrorImage;
}
return null;
@@ -430,6 +433,11 @@ public final class DevicePanel extends Panel implements IDebugBridgeChangeListen
}
}
+ public void toggleMethodProfiling() {
+ if (mCurrentClient != null) {
+ mCurrentClient.toggleMethodProfiling();
+ }
+ }
public void setEnabledHeapOnSelectedClient(boolean enable) {
if (mCurrentClient != null) {
@@ -594,7 +602,7 @@ public final class DevicePanel extends Panel implements IDebugBridgeChangeListen
* @param client the updated client.
* @param changeMask the bit mask describing the changed properties. It can contain
* any of the following values: {@link Client#CHANGE_INFO},
- * {@link Client#CHANGE_DEBUGGER_INTEREST}, {@link Client#CHANGE_THREAD_MODE},
+ * {@link Client#CHANGE_DEBUGGER_STATUS}, {@link Client#CHANGE_THREAD_MODE},
* {@link Client#CHANGE_THREAD_DATA}, {@link Client#CHANGE_HEAP_MODE},
* {@link Client#CHANGE_HEAP_DATA}, {@link Client#CHANGE_NATIVE_HEAP_DATA}
*
@@ -607,10 +615,10 @@ public final class DevicePanel extends Panel implements IDebugBridgeChangeListen
// refresh the client
mTreeViewer.refresh(client);
- if ((changeMask & Client.CHANGE_DEBUGGER_INTEREST) ==
- Client.CHANGE_DEBUGGER_INTEREST &&
+ if ((changeMask & Client.CHANGE_DEBUGGER_STATUS) ==
+ Client.CHANGE_DEBUGGER_STATUS &&
client.getClientData().getDebuggerConnectionStatus() ==
- ClientData.DEBUGGER_WAITING) {
+ DebuggerStatus.WAITING) {
// make sure the device is expanded. Normally the setSelection below
// will auto expand, but the children of device may not already exist
// at this time. Forcing an expand will make the TreeViewer create them.
diff --git a/ddms/libs/ddmuilib/src/com/android/ddmuilib/HeapPanel.java b/ddms/libs/ddmuilib/src/com/android/ddmuilib/HeapPanel.java
index 977203b..f5cf9b1 100644
--- a/ddms/libs/ddmuilib/src/com/android/ddmuilib/HeapPanel.java
+++ b/ddms/libs/ddmuilib/src/com/android/ddmuilib/HeapPanel.java
@@ -217,7 +217,7 @@ public final class HeapPanel extends BaseHeapPanel {
* @param client the updated client.
* @param changeMask the bit mask describing the changed properties. It can contain
* any of the following values: {@link Client#CHANGE_INFO}, {@link Client#CHANGE_NAME}
- * {@link Client#CHANGE_DEBUGGER_INTEREST}, {@link Client#CHANGE_THREAD_MODE},
+ * {@link Client#CHANGE_DEBUGGER_STATUS}, {@link Client#CHANGE_THREAD_MODE},
* {@link Client#CHANGE_THREAD_DATA}, {@link Client#CHANGE_HEAP_MODE},
* {@link Client#CHANGE_HEAP_DATA}, {@link Client#CHANGE_NATIVE_HEAP_DATA}
*
diff --git a/ddms/libs/ddmuilib/src/com/android/ddmuilib/InfoPanel.java b/ddms/libs/ddmuilib/src/com/android/ddmuilib/InfoPanel.java
index 35e071d..6cbb999 100644
--- a/ddms/libs/ddmuilib/src/com/android/ddmuilib/InfoPanel.java
+++ b/ddms/libs/ddmuilib/src/com/android/ddmuilib/InfoPanel.java
@@ -92,7 +92,7 @@ public class InfoPanel extends TablePanel {
* @param client the updated client.
* @param changeMask the bit mask describing the changed properties. It can contain
* any of the following values: {@link Client#CHANGE_PORT}, {@link Client#CHANGE_NAME}
- * {@link Client#CHANGE_DEBUGGER_INTEREST}, {@link Client#CHANGE_THREAD_MODE},
+ * {@link Client#CHANGE_DEBUGGER_STATUS}, {@link Client#CHANGE_THREAD_MODE},
* {@link Client#CHANGE_THREAD_DATA}, {@link Client#CHANGE_HEAP_MODE},
* {@link Client#CHANGE_HEAP_DATA}, {@link Client#CHANGE_NATIVE_HEAP_DATA}
*
diff --git a/ddms/libs/ddmuilib/src/com/android/ddmuilib/NativeHeapPanel.java b/ddms/libs/ddmuilib/src/com/android/ddmuilib/NativeHeapPanel.java
index 46461bf..0b2460b 100644
--- a/ddms/libs/ddmuilib/src/com/android/ddmuilib/NativeHeapPanel.java
+++ b/ddms/libs/ddmuilib/src/com/android/ddmuilib/NativeHeapPanel.java
@@ -668,7 +668,7 @@ public final class NativeHeapPanel extends BaseHeapPanel {
* @param client the updated client.
* @param changeMask the bit mask describing the changed properties. It can contain
* any of the following values: {@link Client#CHANGE_INFO}, {@link Client#CHANGE_NAME}
- * {@link Client#CHANGE_DEBUGGER_INTEREST}, {@link Client#CHANGE_THREAD_MODE},
+ * {@link Client#CHANGE_DEBUGGER_STATUS}, {@link Client#CHANGE_THREAD_MODE},
* {@link Client#CHANGE_THREAD_DATA}, {@link Client#CHANGE_HEAP_MODE},
* {@link Client#CHANGE_HEAP_DATA}, {@link Client#CHANGE_NATIVE_HEAP_DATA}
*
diff --git a/ddms/libs/ddmuilib/src/com/android/ddmuilib/ThreadPanel.java b/ddms/libs/ddmuilib/src/com/android/ddmuilib/ThreadPanel.java
index a034063..d94d4f3 100644
--- a/ddms/libs/ddmuilib/src/com/android/ddmuilib/ThreadPanel.java
+++ b/ddms/libs/ddmuilib/src/com/android/ddmuilib/ThreadPanel.java
@@ -376,7 +376,7 @@ public class ThreadPanel extends TablePanel {
* @param client the updated client.
* @param changeMask the bit mask describing the changed properties. It can contain
* any of the following values: {@link Client#CHANGE_INFO}, {@link Client#CHANGE_NAME}
- * {@link Client#CHANGE_DEBUGGER_INTEREST}, {@link Client#CHANGE_THREAD_MODE},
+ * {@link Client#CHANGE_DEBUGGER_STATUS}, {@link Client#CHANGE_THREAD_MODE},
* {@link Client#CHANGE_THREAD_DATA}, {@link Client#CHANGE_HEAP_MODE},
* {@link Client#CHANGE_HEAP_DATA}, {@link Client#CHANGE_NATIVE_HEAP_DATA}
*
diff --git a/ddms/libs/ddmuilib/src/com/android/ddmuilib/handler/BaseFileHandler.java b/ddms/libs/ddmuilib/src/com/android/ddmuilib/handler/BaseFileHandler.java
new file mode 100644
index 0000000..3a2a2ef
--- /dev/null
+++ b/ddms/libs/ddmuilib/src/com/android/ddmuilib/handler/BaseFileHandler.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ddmuilib.handler;
+
+import com.android.ddmlib.SyncService;
+import com.android.ddmlib.ClientData.IHprofDumpHandler;
+import com.android.ddmlib.ClientData.IMethodProfilingHandler;
+import com.android.ddmlib.SyncService.SyncResult;
+import com.android.ddmuilib.SyncProgressMonitor;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jface.dialogs.ProgressMonitorDialog;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.FileDialog;
+import org.eclipse.swt.widgets.Shell;
+
+import java.lang.reflect.InvocationTargetException;
+
+/**
+ * Base handler class for handler dealing with files located on a device.
+ *
+ * @see IHprofDumpHandler
+ * @see IMethodProfilingHandler
+ */
+public class BaseFileHandler {
+
+ protected final Shell mParentShell;
+
+ public BaseFileHandler(Shell parentShell) {
+ mParentShell = parentShell;
+ }
+
+ /**
+ * Prompts the user for a save location and pulls the remote files into this location.
+ * <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/ddms/libs/ddmuilib/src/com/android/ddmuilib/handler/MethodProfilingHandler.java b/ddms/libs/ddmuilib/src/com/android/ddmuilib/handler/MethodProfilingHandler.java
new file mode 100644
index 0000000..f469a7d
--- /dev/null
+++ b/ddms/libs/ddmuilib/src/com/android/ddmuilib/handler/MethodProfilingHandler.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ddmuilib.handler;
+
+import com.android.ddmlib.Client;
+import com.android.ddmlib.IDevice;
+import com.android.ddmlib.Log;
+import com.android.ddmlib.SyncService;
+import com.android.ddmlib.ClientData.IMethodProfilingHandler;
+import com.android.ddmlib.SyncService.SyncResult;
+import com.android.ddmuilib.DdmUiPreferences;
+import com.android.ddmuilib.console.DdmConsole;
+
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.swt.widgets.Shell;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.lang.reflect.InvocationTargetException;
+
+/**
+ * Handler for Method tracing.
+ * This will pull the trace file into a temp file and launch traceview.
+ */
+public class MethodProfilingHandler extends BaseFileHandler
+ implements IMethodProfilingHandler {
+
+ public MethodProfilingHandler(Shell parentShell) {
+ super(parentShell);
+ }
+
+ public void onFailure(final Client client) {
+ mParentShell.getDisplay().asyncExec(new Runnable() {
+ public void run() {
+ displayError(
+ "Unable to create Method Profiling file for application '%1$s'.\n" +
+ "Check logcat for more information.",
+ client.getClientData().getClientDescription());
+ }
+ });
+ }
+
+ public void onSuccess(final String remoteFilePath, final Client client) {
+ mParentShell.getDisplay().asyncExec(new Runnable() {
+ public void run() {
+ if (remoteFilePath == null) {
+ displayError(
+ "Unable to download trace file: unknown file name.\n" +
+ "This can happen if you disconnected the device while recording the trace.");
+ return;
+ }
+
+ final IDevice device = client.getDevice();
+ try {
+ // get the sync service to pull the HPROF file
+ final SyncService sync = client.getDevice().getSyncService();
+ if (sync != null) {
+ pullAndOpen(sync, remoteFilePath);
+ } else {
+ displayError("Unable to download trace file from device '%1$s'.",
+ device.getSerialNumber());
+ }
+ } catch (Exception e) {
+ displayError("Unable to download trace file from device '%1$s'.",
+ device.getSerialNumber());
+ }
+ }
+
+ });
+ }
+
+ private void pullAndOpen(SyncService sync, String remoteFilePath)
+ throws InvocationTargetException, InterruptedException, IOException {
+ // get a temp file
+ File temp = File.createTempFile("android", ".trace"); //$NON-NLS-1$ //$NON-NLS-2$
+ String tempPath = temp.getAbsolutePath();
+
+ // pull the file
+ SyncResult result = pull(sync, tempPath, remoteFilePath);
+ if (result != null) {
+ if (result.getCode() == SyncService.RESULT_OK) {
+ // open the temp file in traceview
+ openInTraceview(tempPath);
+ } else {
+ displayError("Unable to download trace file:\n\n%1$s",
+ result.getMessage());
+ }
+ } else {
+ // this really shouldn't happen.
+ displayError("Unable to download trace file.");
+ }
+ }
+
+ private void openInTraceview(String tempPath) {
+ // now that we have the file, we need to launch traceview
+ String[] command = new String[2];
+ command[0] = DdmUiPreferences.getTraceview();
+ command[1] = tempPath;
+
+ try {
+ final Process p = Runtime.getRuntime().exec(command);
+
+ // create a thread for the output
+ new Thread("Traceview output") {
+ @Override
+ public void run() {
+ // create a buffer to read the stderr output
+ InputStreamReader is = new InputStreamReader(p.getErrorStream());
+ BufferedReader resultReader = new BufferedReader(is);
+
+ // read the lines as they come. if null is returned, it's
+ // because the process finished
+ try {
+ while (true) {
+ String line = resultReader.readLine();
+ if (line != null) {
+ DdmConsole.printErrorToConsole("Traceview: " + line);
+ } else {
+ break;
+ }
+ }
+ // get the return code from the process
+ p.waitFor();
+ } catch (Exception e) {
+ Log.e("traceview", e);
+ }
+ }
+ }.start();
+ } catch (IOException e) {
+ Log.e("traceview", e);
+ }
+ }
+
+ private void displayError(String format, Object... args) {
+ MessageDialog.openError(mParentShell, "Method Profiling Error",
+ String.format(format, args));
+ }
+}
diff --git a/ddms/libs/ddmuilib/src/resources/images/tracing_start.png b/ddms/libs/ddmuilib/src/resources/images/tracing_start.png
new file mode 100644
index 0000000..88771cc
--- /dev/null
+++ b/ddms/libs/ddmuilib/src/resources/images/tracing_start.png
Binary files differ
diff --git a/ddms/libs/ddmuilib/src/resources/images/tracing_stop.png b/ddms/libs/ddmuilib/src/resources/images/tracing_stop.png
new file mode 100644
index 0000000..71bd215
--- /dev/null
+++ b/ddms/libs/ddmuilib/src/resources/images/tracing_stop.png
Binary files differ