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
This commit is contained in:
Xavier Ducrohet
2009-09-02 20:06:08 -07:00
parent 2b6f5d17a3
commit 3ce85131be
29 changed files with 906 additions and 350 deletions

View File

@@ -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"/>

View File

@@ -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);

View File

@@ -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,6 +981,58 @@ public class UIThread implements IUiSelectionListener {
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 @@ public class UIThread implements IUiSelectionListener {
}
});
// 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 @@ public class UIThread implements IUiSelectionListener {
}
});
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 @@ 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();
}
});
}
}
}
}

View File

@@ -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}
*/

View File

@@ -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

View File

@@ -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;
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 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.
@@ -276,6 +297,24 @@ public class ClientData {
void onFailure(Client client);
}
/**
* 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.
*/
@@ -287,13 +326,24 @@ public class ClientData {
return sHprofDumpHandler;
}
/**
* 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;
}
}

View File

@@ -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;
}
}

View File

@@ -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();

View File

@@ -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
}

View File

@@ -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);
}
/*

View File

@@ -64,6 +64,7 @@ final class HandleHello extends ChunkHandler {
throws IOException {
sendHELO(client, serverProtocolVersion);
sendFEAT(client);
HandleProfiling.sendMPRQ(client);
}
/**

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}

View File

@@ -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"/>

View File

@@ -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);

View File

@@ -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.

View File

@@ -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}
*

View File

@@ -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}
*

View File

@@ -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}
*

View File

@@ -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}
*

View File

@@ -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];
}
}

View File

@@ -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));
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 227 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 B

View File

@@ -40,6 +40,8 @@ com.android.ide.eclipse.ddms/icons/pull.png
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

View File

@@ -24,6 +24,7 @@ import com.android.ddmlib.Log;
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 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
* @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 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
}
// 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 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
// 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

View File

@@ -18,6 +18,7 @@ package com.android.ide.eclipse.ddms;
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 @@ import java.util.Calendar;
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 @@ public final class DdmsPlugin extends AbstractUIPlugin implements IDeviceChangeL
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 @@ public final class DdmsPlugin extends AbstractUIPlugin implements IDeviceChangeL
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;
}
}

View File

@@ -22,11 +22,14 @@ import com.android.ddmlib.Client;
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 com.android.ide.eclipse.ddms.preferences.PreferenceInitializer;
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.IMenuManager;
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 org.eclipse.ui.part.ViewPart;
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 @@ public class DeviceView extends ViewPart implements IUiSelectionListener {
private static DeviceView sThis;
private Shell mParentShell;
private DevicePanel mDeviceList;
private Action mResetAdbAction;
private Action mCaptureAction;
private Action mUpdateThreadAction;
@@ -79,29 +79,29 @@ public class DeviceView extends ViewPart implements IUiSelectionListener {
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 class DeviceView extends ViewPart implements IUiSelectionListener {
});
}
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 @@ public class DeviceView extends ViewPart implements IUiSelectionListener {
// 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 @@ public class DeviceView extends ViewPart 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() + 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 @@ public class DeviceView extends ViewPart implements IUiSelectionListener {
fileStore);
}
}
private void displayError(String format, Object... args) {
MessageDialog.openError(mParentShell, "HPROF Error",
String.format(format, args));
}
}
@@ -244,7 +216,10 @@ public class DeviceView extends ViewPart implements IUiSelectionListener {
@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 @@ public class DeviceView extends ViewPart implements IUiSelectionListener {
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 @@ public class DeviceView extends ViewPart implements IUiSelectionListener {
// 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 @@ public class DeviceView extends ViewPart implements IUiSelectionListener {
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 @@ public class DeviceView extends ViewPart implements IUiSelectionListener {
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 @@ public class DeviceView extends ViewPart implements IUiSelectionListener {
// 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 @@ public class DeviceView extends ViewPart implements IUiSelectionListener {
// 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);
}
});
}
}
}
}

View File

@@ -70,7 +70,7 @@ for i in \
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