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:
@@ -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"/>
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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}
|
||||
*/
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -64,6 +64,7 @@ final class HandleHello extends ChunkHandler {
|
||||
throws IOException {
|
||||
sendHELO(client, serverProtocolVersion);
|
||||
sendFEAT(client);
|
||||
HandleProfiling.sendMPRQ(client);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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}
|
||||
*
|
||||
|
||||
@@ -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}
|
||||
*
|
||||
|
||||
@@ -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}
|
||||
*
|
||||
|
||||
@@ -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}
|
||||
*
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
BIN
tools/ddms/libs/ddmuilib/src/resources/images/tracing_start.png
Normal file
BIN
tools/ddms/libs/ddmuilib/src/resources/images/tracing_start.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 227 B |
BIN
tools/ddms/libs/ddmuilib/src/resources/images/tracing_stop.png
Normal file
BIN
tools/ddms/libs/ddmuilib/src/resources/images/tracing_stop.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 217 B |
2
tools/eclipse/plugins/.gitignore
vendored
2
tools/eclipse/plugins/.gitignore
vendored
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user