aboutsummaryrefslogtreecommitdiffstats
path: root/ddms/app
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/app
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/app')
-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
3 files changed, 155 insertions, 113 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();
+ }
+ });
+ }
+ }
+ }
}