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"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<classpath>
|
<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 kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
|
||||||
<classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/ANDROID_SWT"/>
|
<classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/ANDROID_SWT"/>
|
||||||
<classpathentry combineaccessrules="false" kind="src" path="/ddmlib"/>
|
<classpathentry combineaccessrules="false" kind="src" path="/ddmlib"/>
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
package com.android.ddms;
|
package com.android.ddms;
|
||||||
|
|
||||||
|
import com.android.ddmlib.DdmConstants;
|
||||||
import com.android.ddmlib.DdmPreferences;
|
import com.android.ddmlib.DdmPreferences;
|
||||||
import com.android.ddmlib.Log;
|
import com.android.ddmlib.Log;
|
||||||
import com.android.ddmlib.Log.LogLevel;
|
import com.android.ddmlib.Log.LogLevel;
|
||||||
@@ -158,9 +159,9 @@ public final class PrefsDialog {
|
|||||||
|
|
||||||
String traceview = System.getProperty("com.android.ddms.bindir"); //$NON-NLS-1$
|
String traceview = System.getProperty("com.android.ddms.bindir"); //$NON-NLS-1$
|
||||||
if (traceview != null && traceview.length() != 0) {
|
if (traceview != null && traceview.length() != 0) {
|
||||||
traceview += File.separator + "traceview"; //$NON-NLS-1$
|
traceview += File.separator + DdmConstants.FN_TRACEVIEW;
|
||||||
} else {
|
} else {
|
||||||
traceview = "traceview"; //$NON-NLS-1$
|
traceview = DdmConstants.FN_TRACEVIEW;
|
||||||
}
|
}
|
||||||
DdmUiPreferences.setTraceviewLocation(traceview);
|
DdmUiPreferences.setTraceviewLocation(traceview);
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,9 @@ import com.android.ddmlib.ClientData;
|
|||||||
import com.android.ddmlib.IDevice;
|
import com.android.ddmlib.IDevice;
|
||||||
import com.android.ddmlib.Log;
|
import com.android.ddmlib.Log;
|
||||||
import com.android.ddmlib.SyncService;
|
import com.android.ddmlib.SyncService;
|
||||||
|
import com.android.ddmlib.AndroidDebugBridge.IClientChangeListener;
|
||||||
import com.android.ddmlib.ClientData.IHprofDumpHandler;
|
import com.android.ddmlib.ClientData.IHprofDumpHandler;
|
||||||
|
import com.android.ddmlib.ClientData.MethodProfilingStatus;
|
||||||
import com.android.ddmlib.Log.ILogOutput;
|
import com.android.ddmlib.Log.ILogOutput;
|
||||||
import com.android.ddmlib.Log.LogLevel;
|
import com.android.ddmlib.Log.LogLevel;
|
||||||
import com.android.ddmlib.SyncService.SyncResult;
|
import com.android.ddmlib.SyncService.SyncResult;
|
||||||
@@ -36,23 +38,21 @@ import com.android.ddmuilib.ImageLoader;
|
|||||||
import com.android.ddmuilib.InfoPanel;
|
import com.android.ddmuilib.InfoPanel;
|
||||||
import com.android.ddmuilib.NativeHeapPanel;
|
import com.android.ddmuilib.NativeHeapPanel;
|
||||||
import com.android.ddmuilib.ScreenShotDialog;
|
import com.android.ddmuilib.ScreenShotDialog;
|
||||||
import com.android.ddmuilib.SyncProgressMonitor;
|
|
||||||
import com.android.ddmuilib.SysinfoPanel;
|
import com.android.ddmuilib.SysinfoPanel;
|
||||||
import com.android.ddmuilib.TablePanel;
|
import com.android.ddmuilib.TablePanel;
|
||||||
import com.android.ddmuilib.ThreadPanel;
|
import com.android.ddmuilib.ThreadPanel;
|
||||||
import com.android.ddmuilib.DevicePanel.IUiSelectionListener;
|
import com.android.ddmuilib.DevicePanel.IUiSelectionListener;
|
||||||
import com.android.ddmuilib.actions.ToolItemAction;
|
import com.android.ddmuilib.actions.ToolItemAction;
|
||||||
import com.android.ddmuilib.explorer.DeviceExplorer;
|
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.log.event.EventLogPanel;
|
||||||
import com.android.ddmuilib.logcat.LogColors;
|
import com.android.ddmuilib.logcat.LogColors;
|
||||||
import com.android.ddmuilib.logcat.LogFilter;
|
import com.android.ddmuilib.logcat.LogFilter;
|
||||||
import com.android.ddmuilib.logcat.LogPanel;
|
import com.android.ddmuilib.logcat.LogPanel;
|
||||||
import com.android.ddmuilib.logcat.LogPanel.ILogFilterStorageManager;
|
import com.android.ddmuilib.logcat.LogPanel.ILogFilterStorageManager;
|
||||||
|
|
||||||
import org.eclipse.core.runtime.IProgressMonitor;
|
|
||||||
import org.eclipse.jface.dialogs.MessageDialog;
|
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.IPreferenceStore;
|
||||||
import org.eclipse.jface.preference.PreferenceStore;
|
import org.eclipse.jface.preference.PreferenceStore;
|
||||||
import org.eclipse.swt.SWT;
|
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.Composite;
|
||||||
import org.eclipse.swt.widgets.Display;
|
import org.eclipse.swt.widgets.Display;
|
||||||
import org.eclipse.swt.widgets.Event;
|
import org.eclipse.swt.widgets.Event;
|
||||||
import org.eclipse.swt.widgets.FileDialog;
|
|
||||||
import org.eclipse.swt.widgets.Label;
|
import org.eclipse.swt.widgets.Label;
|
||||||
import org.eclipse.swt.widgets.Listener;
|
import org.eclipse.swt.widgets.Listener;
|
||||||
import org.eclipse.swt.widgets.Menu;
|
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
|
* SWT application. So this class mainly builds the ui, and manages communication between the panels
|
||||||
* when {@link IDevice} / {@link Client} selection changes.
|
* 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
|
* UI tab panel definitions. The constants here must match up with the array
|
||||||
* indices in mPanels. PANEL_CLIENT_LIST is a "virtual" panel representing
|
* 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 mTBHalt;
|
||||||
private ToolItem mTBCauseGc;
|
private ToolItem mTBCauseGc;
|
||||||
private ToolItem mTBDumpHprof;
|
private ToolItem mTBDumpHprof;
|
||||||
|
private ToolItem mTBProfiling;
|
||||||
|
|
||||||
private ImageLoader mDdmsImageLoader;
|
private ImageLoader mDdmsImageLoader;
|
||||||
private ImageLoader mDdmuiLibImageLoader;
|
private ImageLoader mDdmuiLibImageLoader;
|
||||||
@@ -252,6 +252,10 @@ public class UIThread implements IUiSelectionListener {
|
|||||||
|
|
||||||
private EventLogPanel mEventLogPanel;
|
private EventLogPanel mEventLogPanel;
|
||||||
|
|
||||||
|
private Image mTracingStartImage;
|
||||||
|
|
||||||
|
private Image mTracingStopImage;
|
||||||
|
|
||||||
|
|
||||||
private class TableFocusListener implements ITableFocusListener {
|
private class TableFocusListener implements ITableFocusListener {
|
||||||
|
|
||||||
@@ -292,23 +296,23 @@ public class UIThread implements IUiSelectionListener {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class HProfHandler implements IHprofDumpHandler {
|
/**
|
||||||
|
* Handler for HPROF dumps.
|
||||||
private final Shell mParentShell;
|
* This will always prompt the user to save the HPROF file.
|
||||||
|
*/
|
||||||
|
private class HProfHandler extends BaseFileHandler implements IHprofDumpHandler {
|
||||||
|
|
||||||
public HProfHandler(Shell parentShell) {
|
public HProfHandler(Shell parentShell) {
|
||||||
mParentShell = parentShell;
|
super(parentShell);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onFailure(final Client client) {
|
public void onFailure(final Client client) {
|
||||||
mDisplay.asyncExec(new Runnable() {
|
mDisplay.asyncExec(new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
MessageDialog.openError(mParentShell, "HPROF Error",
|
displayError("Unable to create HPROF file for application '%1$s'.\n" +
|
||||||
String.format(
|
"Check logcat for more information.",
|
||||||
"Unable to create HPROF file for application '%1$s'.\n" +
|
client.getClientData().getClientDescription());
|
||||||
"Check logcat for more information.",
|
|
||||||
client.getClientData().getClientDescription()));
|
|
||||||
} finally {
|
} finally {
|
||||||
// this will make sure the dump hprof button is re-enabled for the
|
// this will make sure the dump hprof button is re-enabled for the
|
||||||
// current selection. as the client is finished dumping an hprof file
|
// 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() {
|
mDisplay.asyncExec(new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
final IDevice device = client.getDevice();
|
final IDevice device = client.getDevice();
|
||||||
@@ -326,17 +330,21 @@ public class UIThread implements IUiSelectionListener {
|
|||||||
// get the sync service to pull the HPROF file
|
// get the sync service to pull the HPROF file
|
||||||
final SyncService sync = client.getDevice().getSyncService();
|
final SyncService sync = client.getDevice().getSyncService();
|
||||||
if (sync != null) {
|
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 {
|
} else {
|
||||||
MessageDialog.openError(mParentShell, "HPROF Error",
|
displayError("Unable to download HPROF file from device '%1$s'.",
|
||||||
String.format(
|
device.getSerialNumber());
|
||||||
"Unable to download HPROF file from device '%1$s'.",
|
|
||||||
device.getSerialNumber()));
|
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
MessageDialog.openError(mParentShell, "HPROF Error",
|
displayError("Unable to download HPROF file from device '%1$s'.",
|
||||||
String.format("Unable to download HPROF file from device '%1$s'.",
|
device.getSerialNumber());
|
||||||
device.getSerialNumber()));
|
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
// this will make sure the dump hprof button is re-enabled for the
|
// 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,
|
private void displayError(String format, Object... args) {
|
||||||
final SyncService sync, final String remoteFile) {
|
MessageDialog.openError(mParentShell, "HPROF Error",
|
||||||
try {
|
String.format(format, args));
|
||||||
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()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generic constructor.
|
* Generic constructor.
|
||||||
*/
|
*/
|
||||||
@@ -467,6 +443,7 @@ public class UIThread implements IUiSelectionListener {
|
|||||||
|
|
||||||
// set the handler for hprof dump
|
// set the handler for hprof dump
|
||||||
ClientData.setHprofDumpHandler(new HProfHandler(shell));
|
ClientData.setHprofDumpHandler(new HProfHandler(shell));
|
||||||
|
ClientData.setMethodProfilingHandler(new MethodProfilingHandler(shell));
|
||||||
|
|
||||||
// [try to] ensure ADB is running
|
// [try to] ensure ADB is running
|
||||||
String adbLocation = System.getProperty("com.android.ddms.bindir"); //$NON-NLS-1$
|
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.init(true /* debugger support */);
|
||||||
AndroidDebugBridge.createBridge(adbLocation, true /* forceNewBridge */);
|
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");
|
shell.setText("Dalvik Debug Monitor");
|
||||||
setConfirmClose(shell);
|
setConfirmClose(shell);
|
||||||
createMenus(shell);
|
createMenus(shell);
|
||||||
@@ -1001,6 +981,58 @@ public class UIThread implements IUiSelectionListener {
|
|||||||
private void createDevicePanelToolBar(ToolBar toolBar) {
|
private void createDevicePanelToolBar(ToolBar toolBar) {
|
||||||
Display display = toolBar.getDisplay();
|
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
|
// add "show thread updates" button
|
||||||
mTBShowThreadUpdates = new ToolItem(toolBar, SWT.CHECK);
|
mTBShowThreadUpdates = new ToolItem(toolBar, SWT.CHECK);
|
||||||
mTBShowThreadUpdates.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, display,
|
mTBShowThreadUpdates.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, display,
|
||||||
@@ -1022,23 +1054,21 @@ public class UIThread implements IUiSelectionListener {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// add "show heap updates" button
|
// add a start/stop method tracing
|
||||||
mTBShowHeapUpdates = new ToolItem(toolBar, SWT.CHECK);
|
mTracingStartImage = ImageHelper.loadImage(mDdmuiLibImageLoader, display,
|
||||||
mTBShowHeapUpdates.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, display,
|
DevicePanel.ICON_TRACING_START,
|
||||||
DevicePanel.ICON_HEAP, DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
|
DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null);
|
||||||
mTBShowHeapUpdates.setToolTipText("Show heap updates");
|
mTracingStopImage = ImageHelper.loadImage(mDdmuiLibImageLoader, display,
|
||||||
mTBShowHeapUpdates.setEnabled(false);
|
DevicePanel.ICON_TRACING_STOP,
|
||||||
mTBShowHeapUpdates.addSelectionListener(new SelectionAdapter() {
|
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
|
@Override
|
||||||
public void widgetSelected(SelectionEvent e) {
|
public void widgetSelected(SelectionEvent e) {
|
||||||
if (mCurrentClient != null) {
|
mDevicePanel.toggleMethodProfiling();
|
||||||
// boolean status = ((ToolItem)e.item).getSelection();
|
|
||||||
// invert previous state
|
|
||||||
boolean enable = !mCurrentClient.isHeapUpdateEnabled();
|
|
||||||
mCurrentClient.setHeapUpdateEnabled(enable);
|
|
||||||
} else {
|
|
||||||
e.doit = false; // this has no effect?
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -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();
|
toolBar.pack();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1581,9 +1579,31 @@ public class UIThread implements IUiSelectionListener {
|
|||||||
mTBShowHeapUpdates.setEnabled(true);
|
mTBShowHeapUpdates.setEnabled(true);
|
||||||
mTBHalt.setEnabled(true);
|
mTBHalt.setEnabled(true);
|
||||||
mTBCauseGc.setEnabled(true);
|
mTBCauseGc.setEnabled(true);
|
||||||
mTBDumpHprof.setEnabled(
|
|
||||||
mCurrentClient.getClientData().hasFeature(ClientData.FEATURE_HPROF) &&
|
ClientData data = mCurrentClient.getClientData();
|
||||||
mCurrentClient.getClientData().hasPendingHprofDump() == false);
|
|
||||||
|
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 {
|
} else {
|
||||||
// list is empty, disable these
|
// list is empty, disable these
|
||||||
mTBShowThreadUpdates.setSelection(false);
|
mTBShowThreadUpdates.setSelection(false);
|
||||||
@@ -1592,7 +1612,13 @@ public class UIThread implements IUiSelectionListener {
|
|||||||
mTBShowHeapUpdates.setEnabled(false);
|
mTBShowHeapUpdates.setEnabled(false);
|
||||||
mTBHalt.setEnabled(false);
|
mTBHalt.setEnabled(false);
|
||||||
mTBCauseGc.setEnabled(false);
|
mTBCauseGc.setEnabled(false);
|
||||||
|
|
||||||
mTBDumpHprof.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();
|
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 client the updated client.
|
||||||
* @param changeMask the bit mask describing the changed properties. It can contain
|
* @param changeMask the bit mask describing the changed properties. It can contain
|
||||||
* any of the following values: {@link Client#CHANGE_INFO},
|
* 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_THREAD_DATA}, {@link Client#CHANGE_HEAP_MODE},
|
||||||
* {@link Client#CHANGE_HEAP_DATA}, {@link Client#CHANGE_NATIVE_HEAP_DATA}
|
* {@link Client#CHANGE_HEAP_DATA}, {@link Client#CHANGE_NATIVE_HEAP_DATA}
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package com.android.ddmlib;
|
package com.android.ddmlib;
|
||||||
|
|
||||||
|
import com.android.ddmlib.ClientData.MethodProfilingStatus;
|
||||||
import com.android.ddmlib.DebugPortManager.IDebugPortProvider;
|
import com.android.ddmlib.DebugPortManager.IDebugPortProvider;
|
||||||
import com.android.ddmlib.AndroidDebugBridge.IClientChangeListener;
|
import com.android.ddmlib.AndroidDebugBridge.IClientChangeListener;
|
||||||
|
|
||||||
@@ -40,32 +41,34 @@ public class Client {
|
|||||||
private static final int SERVER_PROTOCOL_VERSION = 1;
|
private static final int SERVER_PROTOCOL_VERSION = 1;
|
||||||
|
|
||||||
/** Client change bit mask: application name change */
|
/** Client change bit mask: application name change */
|
||||||
public static final int CHANGE_NAME = 0x0001;
|
public static final int CHANGE_NAME = 0x0001;
|
||||||
/** Client change bit mask: debugger interest change */
|
/** Client change bit mask: debugger status change */
|
||||||
public static final int CHANGE_DEBUGGER_INTEREST = 0x0002;
|
public static final int CHANGE_DEBUGGER_STATUS = 0x0002;
|
||||||
/** Client change bit mask: debugger port change */
|
/** 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 */
|
/** 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 */
|
/** 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 */
|
/** 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 */
|
/** 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 */
|
/** 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 */
|
/** 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 */
|
/** 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 */
|
/** 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},
|
/** 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;
|
private SocketChannel mChan;
|
||||||
|
|
||||||
@@ -228,7 +231,7 @@ public class Client {
|
|||||||
*/
|
*/
|
||||||
public void dumpHprof() {
|
public void dumpHprof() {
|
||||||
try {
|
try {
|
||||||
String file = "/sdcard/" + mClientData.getClientDescription().replaceAll("\\:", ".") +
|
String file = "/sdcard/" + mClientData.getClientDescription().replaceAll("\\:.*", "") +
|
||||||
".hprof";
|
".hprof";
|
||||||
HandleHeap.sendHPDU(this, file);
|
HandleHeap.sendHPDU(this, file);
|
||||||
} catch (IOException e) {
|
} 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.
|
* Enables or disables the thread update.
|
||||||
* <p/>If <code>true</code> the VM will be able to send thread information. Thread information
|
* <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. */
|
/** Temporary name of VM to be ignored. */
|
||||||
private final static String PRE_INITIALIZED = "<pre-initialized>"; //$NON-NLS-1$
|
private final static String PRE_INITIALIZED = "<pre-initialized>"; //$NON-NLS-1$
|
||||||
|
|
||||||
/** Debugger connection status: not waiting on one, not connected to one, but accepting
|
public static enum DebuggerStatus {
|
||||||
* new connections. This is the default value. */
|
/** Debugger connection status: not waiting on one, not connected to one, but accepting
|
||||||
public static final int DEBUGGER_DEFAULT = 1;
|
* 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. */
|
* Debugger connection status: the application's VM is paused, waiting for a debugger to
|
||||||
public static final int DEBUGGER_WAITING = 2;
|
* connect to it before resuming. */
|
||||||
/** Debugger connection status : Debugger is connected */
|
WAITING,
|
||||||
public static final int DEBUGGER_ATTACHED = 3;
|
/** Debugger connection status : Debugger is connected */
|
||||||
/** Debugger connection status: The listening port for debugger connection failed to listen.
|
ATTACHED,
|
||||||
* No debugger will be able to connect. */
|
/** Debugger connection status: The listening port for debugger connection failed to listen.
|
||||||
public static final int DEBUGGER_ERROR = 4;
|
* No debugger will be able to connect. */
|
||||||
|
ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
public static enum AllocationTrackingStatus {
|
||||||
* Allocation tracking status: unknown.
|
/**
|
||||||
* <p/>This happens right after a {@link Client} is discovered
|
* Allocation tracking status: unknown.
|
||||||
* by the {@link AndroidDebugBridge}, and before the {@link Client} answered the query regarding
|
* <p/>This happens right after a {@link Client} is discovered
|
||||||
* its allocation tracking status.
|
* by the {@link AndroidDebugBridge}, and before the {@link Client} answered the query
|
||||||
* @see Client#requestAllocationStatus()
|
* regarding its allocation tracking status.
|
||||||
*/
|
* @see Client#requestAllocationStatus()
|
||||||
public static final int ALLOCATION_TRACKING_UNKNOWN = -1;
|
*/
|
||||||
/**
|
UNKNOWN,
|
||||||
* Allocation tracking status: the {@link Client} is not tracking allocations. */
|
/** Allocation tracking status: the {@link Client} is not tracking allocations. */
|
||||||
public static final int ALLOCATION_TRACKING_OFF = 0;
|
OFF,
|
||||||
/**
|
/** Allocation tracking status: the {@link Client} is tracking allocations. */
|
||||||
* Allocation tracking status: the {@link Client} is tracking allocations. */
|
ON;
|
||||||
public static final int ALLOCATION_TRACKING_ON = 1;
|
}
|
||||||
|
|
||||||
|
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
|
* 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$
|
public final static String FEATURE_HPROF = "hprof-heap-dump"; // $NON-NLS-1$
|
||||||
|
|
||||||
private static IHprofDumpHandler sHprofDumpHandler;
|
private static IHprofDumpHandler sHprofDumpHandler;
|
||||||
|
private static IMethodProfilingHandler sMethodProfilingHandler;
|
||||||
|
|
||||||
// is this a DDM-aware client?
|
// is this a DDM-aware client?
|
||||||
private boolean mIsDdmAware;
|
private boolean mIsDdmAware;
|
||||||
@@ -127,7 +145,7 @@ public class ClientData {
|
|||||||
private String mClientDescription;
|
private String mClientDescription;
|
||||||
|
|
||||||
// how interested are we in a debugger?
|
// how interested are we in a debugger?
|
||||||
private int mDebuggerInterest;
|
private DebuggerStatus mDebuggerInterest;
|
||||||
|
|
||||||
// List of supported features by the client.
|
// List of supported features by the client.
|
||||||
private final HashSet<String> mFeatures = new HashSet<String>();
|
private final HashSet<String> mFeatures = new HashSet<String>();
|
||||||
@@ -156,10 +174,13 @@ public class ClientData {
|
|||||||
private int mNativeTotalMemory;
|
private int mNativeTotalMemory;
|
||||||
|
|
||||||
private AllocationInfo[] mAllocations;
|
private AllocationInfo[] mAllocations;
|
||||||
private int mAllocationStatus = ALLOCATION_TRACKING_UNKNOWN;
|
private AllocationTrackingStatus mAllocationStatus = AllocationTrackingStatus.UNKNOWN;
|
||||||
|
|
||||||
private String mPendingHprofDump;
|
private String mPendingHprofDump;
|
||||||
|
|
||||||
|
private MethodProfilingStatus mProfilingStatus = MethodProfilingStatus.UNKNOWN;
|
||||||
|
private String mPendingMethodProfiling;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Heap Information.
|
* Heap Information.
|
||||||
* <p/>The heap is composed of several {@link HeapSegment} objects.
|
* <p/>The heap is composed of several {@link HeapSegment} objects.
|
||||||
@@ -264,10 +285,10 @@ public class ClientData {
|
|||||||
public interface IHprofDumpHandler {
|
public interface IHprofDumpHandler {
|
||||||
/**
|
/**
|
||||||
* Called when a HPROF dump succeeded.
|
* 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.
|
* @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.
|
* Called when the HPROF dump failed.
|
||||||
@@ -276,6 +297,24 @@ public class ClientData {
|
|||||||
void onFailure(Client client);
|
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.
|
* Sets the handler to receive notifications when an HPROF dump succeeded or failed.
|
||||||
*/
|
*/
|
||||||
@@ -287,13 +326,24 @@ public class ClientData {
|
|||||||
return sHprofDumpHandler;
|
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.
|
* Generic constructor.
|
||||||
*/
|
*/
|
||||||
ClientData(int pid) {
|
ClientData(int pid) {
|
||||||
mPid = pid;
|
mPid = pid;
|
||||||
|
|
||||||
mDebuggerInterest = DEBUGGER_DEFAULT;
|
mDebuggerInterest = DebuggerStatus.DEFAULT;
|
||||||
mThreadMap = new TreeMap<Integer,ThreadInfo>();
|
mThreadMap = new TreeMap<Integer,ThreadInfo>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -367,18 +417,17 @@ public class ClientData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the debugger connection status. Possible values are {@link #DEBUGGER_DEFAULT},
|
* Returns the debugger connection status.
|
||||||
* {@link #DEBUGGER_WAITING}, {@link #DEBUGGER_ATTACHED}, and {@link #DEBUGGER_ERROR}.
|
|
||||||
*/
|
*/
|
||||||
public int getDebuggerConnectionStatus() {
|
public DebuggerStatus getDebuggerConnectionStatus() {
|
||||||
return mDebuggerInterest;
|
return mDebuggerInterest;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets debugger connection status.
|
* Sets debugger connection status.
|
||||||
*/
|
*/
|
||||||
void setDebuggerConnectionStatus(int val) {
|
void setDebuggerConnectionStatus(DebuggerStatus status) {
|
||||||
mDebuggerInterest = val;
|
mDebuggerInterest = status;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -521,15 +570,15 @@ public class ClientData {
|
|||||||
return mNativeLibMapInfo.iterator();
|
return mNativeLibMapInfo.iterator();
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized void setAllocationStatus(boolean enabled) {
|
synchronized void setAllocationStatus(AllocationTrackingStatus status) {
|
||||||
mAllocationStatus = enabled ? ALLOCATION_TRACKING_ON : ALLOCATION_TRACKING_OFF;
|
mAllocationStatus = status;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the allocation tracking status.
|
* Returns the allocation tracking status.
|
||||||
* @see Client#requestAllocationStatus()
|
* @see Client#requestAllocationStatus()
|
||||||
*/
|
*/
|
||||||
public synchronized int getAllocationStatus() {
|
public synchronized AllocationTrackingStatus getAllocationStatus() {
|
||||||
return mAllocationStatus;
|
return mAllocationStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -561,10 +610,17 @@ public class ClientData {
|
|||||||
return mFeatures.contains(feature);
|
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) {
|
void setPendingHprofDump(String pendingHprofDump) {
|
||||||
mPendingHprofDump = pendingHprofDump;
|
mPendingHprofDump = pendingHprofDump;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the path to the device-side hprof file being written.
|
||||||
|
*/
|
||||||
String getPendingHprofDump() {
|
String getPendingHprofDump() {
|
||||||
return mPendingHprofDump;
|
return mPendingHprofDump;
|
||||||
}
|
}
|
||||||
@@ -572,5 +628,32 @@ public class ClientData {
|
|||||||
public boolean hasPendingHprofDump() {
|
public boolean hasPendingHprofDump() {
|
||||||
return mPendingHprofDump != null;
|
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;
|
package com.android.ddmlib;
|
||||||
|
|
||||||
|
import com.android.ddmlib.ClientData.DebuggerStatus;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
@@ -165,8 +167,8 @@ class Debugger {
|
|||||||
mConnState = ST_NOT_CONNECTED;
|
mConnState = ST_NOT_CONNECTED;
|
||||||
|
|
||||||
ClientData cd = mClient.getClientData();
|
ClientData cd = mClient.getClientData();
|
||||||
cd.setDebuggerConnectionStatus(ClientData.DEBUGGER_DEFAULT);
|
cd.setDebuggerConnectionStatus(DebuggerStatus.DEFAULT);
|
||||||
mClient.update(Client.CHANGE_DEBUGGER_INTEREST);
|
mClient.update(Client.CHANGE_DEBUGGER_STATUS);
|
||||||
}
|
}
|
||||||
} catch (IOException ioe) {
|
} catch (IOException ioe) {
|
||||||
Log.w("ddms", "Failed to close data " + this);
|
Log.w("ddms", "Failed to close data " + this);
|
||||||
@@ -249,8 +251,8 @@ class Debugger {
|
|||||||
mConnState = ST_READY;
|
mConnState = ST_READY;
|
||||||
|
|
||||||
ClientData cd = mClient.getClientData();
|
ClientData cd = mClient.getClientData();
|
||||||
cd.setDebuggerConnectionStatus(ClientData.DEBUGGER_ATTACHED);
|
cd.setDebuggerConnectionStatus(DebuggerStatus.ATTACHED);
|
||||||
mClient.update(Client.CHANGE_DEBUGGER_INTEREST);
|
mClient.update(Client.CHANGE_DEBUGGER_STATUS);
|
||||||
|
|
||||||
// see if we have another packet in the buffer
|
// see if we have another packet in the buffer
|
||||||
return getJdwpPacket();
|
return getJdwpPacket();
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
package com.android.ddmlib;
|
package com.android.ddmlib;
|
||||||
|
|
||||||
import com.android.ddmlib.AdbHelper.AdbResponse;
|
import com.android.ddmlib.AdbHelper.AdbResponse;
|
||||||
|
import com.android.ddmlib.ClientData.DebuggerStatus;
|
||||||
import com.android.ddmlib.DebugPortManager.IDebugPortProvider;
|
import com.android.ddmlib.DebugPortManager.IDebugPortProvider;
|
||||||
import com.android.ddmlib.IDevice.DeviceState;
|
import com.android.ddmlib.IDevice.DeviceState;
|
||||||
|
|
||||||
@@ -748,7 +749,7 @@ final class DeviceMonitor {
|
|||||||
client.listenForDebugger(debuggerPort);
|
client.listenForDebugger(debuggerPort);
|
||||||
}
|
}
|
||||||
} catch (IOException ioe) {
|
} 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");
|
Log.e("ddms", "Can't bind to local " + debuggerPort + " for debugger");
|
||||||
// oh well
|
// oh well
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package com.android.ddmlib;
|
package com.android.ddmlib;
|
||||||
|
|
||||||
|
import com.android.ddmlib.ClientData.AllocationTrackingStatus;
|
||||||
import com.android.ddmlib.ClientData.IHprofDumpHandler;
|
import com.android.ddmlib.ClientData.IHprofDumpHandler;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -93,22 +94,18 @@ final class HandleHeap extends ChunkHandler {
|
|||||||
|
|
||||||
if (type == CHUNK_HPIF) {
|
if (type == CHUNK_HPIF) {
|
||||||
handleHPIF(client, data);
|
handleHPIF(client, data);
|
||||||
client.update(Client.CHANGE_HEAP_DATA);
|
|
||||||
} else if (type == CHUNK_HPST) {
|
} else if (type == CHUNK_HPST) {
|
||||||
handleHPST(client, data);
|
handleHPST(client, data);
|
||||||
} else if (type == CHUNK_HPEN) {
|
} else if (type == CHUNK_HPEN) {
|
||||||
handleHPEN(client, data);
|
handleHPEN(client, data);
|
||||||
client.update(Client.CHANGE_HEAP_DATA);
|
|
||||||
} else if (type == CHUNK_HPSG) {
|
} else if (type == CHUNK_HPSG) {
|
||||||
handleHPSG(client, data);
|
handleHPSG(client, data);
|
||||||
} else if (type == CHUNK_HPDU) {
|
} else if (type == CHUNK_HPDU) {
|
||||||
handleHPDU(client, data);
|
handleHPDU(client, data);
|
||||||
} else if (type == CHUNK_REAQ) {
|
} else if (type == CHUNK_REAQ) {
|
||||||
handleREAQ(client, data);
|
handleREAQ(client, data);
|
||||||
client.update(Client.CHANGE_HEAP_ALLOCATION_STATUS);
|
|
||||||
} else if (type == CHUNK_REAL) {
|
} else if (type == CHUNK_REAL) {
|
||||||
handleREAL(client, data);
|
handleREAL(client, data);
|
||||||
client.update(Client.CHANGE_HEAP_ALLOCATIONS);
|
|
||||||
} else {
|
} else {
|
||||||
handleUnknownChunk(client, type, data, isReply, msgId);
|
handleUnknownChunk(client, type, data, isReply, msgId);
|
||||||
}
|
}
|
||||||
@@ -135,6 +132,7 @@ final class HandleHeap extends ChunkHandler {
|
|||||||
|
|
||||||
client.getClientData().setHeapInfo(heapId, maxHeapSize,
|
client.getClientData().setHeapInfo(heapId, maxHeapSize,
|
||||||
heapSize, bytesAllocated, objectsAllocated);
|
heapSize, bytesAllocated, objectsAllocated);
|
||||||
|
client.update(Client.CHANGE_HEAP_DATA);
|
||||||
}
|
}
|
||||||
} catch (BufferUnderflowException ex) {
|
} catch (BufferUnderflowException ex) {
|
||||||
Log.w("ddm-heap", "malformed HPIF chunk from client");
|
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>.
|
//xxx todo: only seal data that belongs to the heap mentioned in <data>.
|
||||||
client.getClientData().getVmHeapData().sealHeapData();
|
client.getClientData().getVmHeapData().sealHeapData();
|
||||||
|
client.update(Client.CHANGE_HEAP_DATA);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -332,7 +331,9 @@ final class HandleHeap extends ChunkHandler {
|
|||||||
enabled = (data.get() != 0);
|
enabled = (data.get() != 0);
|
||||||
Log.d("ddm-heap", "REAQ says: enabled=" + enabled);
|
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);
|
Collections.sort(list);
|
||||||
|
|
||||||
client.getClientData().setAllocations(list.toArray(new AllocationInfo[numEntries]));
|
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 {
|
throws IOException {
|
||||||
sendHELO(client, serverProtocolVersion);
|
sendHELO(client, serverProtocolVersion);
|
||||||
sendFEAT(client);
|
sendFEAT(client);
|
||||||
|
HandleProfiling.sendMPRQ(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -16,6 +16,9 @@
|
|||||||
|
|
||||||
package com.android.ddmlib;
|
package com.android.ddmlib;
|
||||||
|
|
||||||
|
import com.android.ddmlib.ClientData.IMethodProfilingHandler;
|
||||||
|
import com.android.ddmlib.ClientData.MethodProfilingStatus;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.ByteBuffer;
|
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_MPRS = type("MPRS");
|
||||||
public static final int CHUNK_MPRE = type("MPRE");
|
public static final int CHUNK_MPRE = type("MPRE");
|
||||||
public static final int CHUNK_MPRQ = type("MPRQ");
|
public static final int CHUNK_MPRQ = type("MPRQ");
|
||||||
|
public static final int CHUNK_FAIL = type("FAIL");
|
||||||
|
|
||||||
private static final HandleProfiling mInst = new HandleProfiling();
|
private static final HandleProfiling mInst = new HandleProfiling();
|
||||||
|
|
||||||
@@ -65,6 +69,8 @@ final class HandleProfiling extends ChunkHandler {
|
|||||||
handleMPRE(client, data);
|
handleMPRE(client, data);
|
||||||
} else if (type == CHUNK_MPRQ) {
|
} else if (type == CHUNK_MPRQ) {
|
||||||
handleMPRQ(client, data);
|
handleMPRQ(client, data);
|
||||||
|
} else if (type == CHUNK_FAIL) {
|
||||||
|
handleFAIL(client, data);
|
||||||
} else {
|
} else {
|
||||||
handleUnknownChunk(client, type, data, isReply, msgId);
|
handleUnknownChunk(client, type, data, isReply, msgId);
|
||||||
}
|
}
|
||||||
@@ -98,6 +104,13 @@ final class HandleProfiling extends ChunkHandler {
|
|||||||
Log.d("ddm-prof", "Sending " + name(CHUNK_MPRS) + " '" + fileName
|
Log.d("ddm-prof", "Sending " + name(CHUNK_MPRS) + " '" + fileName
|
||||||
+ "', size=" + bufferSize + ", flags=" + flags);
|
+ "', size=" + bufferSize + ", flags=" + flags);
|
||||||
client.sendAndConsume(packet, mInst);
|
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) {
|
private void handleMPRE(Client client, ByteBuffer data) {
|
||||||
byte result;
|
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();
|
result = data.get();
|
||||||
|
|
||||||
if (result == 0) {
|
// get the app-level handler for method tracing dump
|
||||||
Log.d("ddm-prof", "Method profiling has finished");
|
IMethodProfilingHandler handler = ClientData.getMethodProfilingHandler();
|
||||||
} else {
|
if (handler != null) {
|
||||||
Log.w("ddm-prof", "Method profiling has failed (check device log)");
|
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();
|
result = data.get();
|
||||||
|
|
||||||
if (result == 0) {
|
if (result == 0) {
|
||||||
|
client.getClientData().setMethodProfilingStatus(MethodProfilingStatus.OFF);
|
||||||
Log.d("ddm-prof", "Method profiling is not running");
|
Log.d("ddm-prof", "Method profiling is not running");
|
||||||
} else {
|
} else {
|
||||||
|
client.getClientData().setMethodProfilingStatus(MethodProfilingStatus.ON);
|
||||||
Log.d("ddm-prof", "Method profiling is running");
|
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;
|
package com.android.ddmlib;
|
||||||
|
|
||||||
|
import com.android.ddmlib.ClientData.DebuggerStatus;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
@@ -80,10 +82,10 @@ final class HandleWait extends ChunkHandler {
|
|||||||
|
|
||||||
ClientData cd = client.getClientData();
|
ClientData cd = client.getClientData();
|
||||||
synchronized (cd) {
|
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"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<classpath>
|
<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 kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
|
||||||
<classpathentry combineaccessrules="false" kind="src" path="/ddmlib"/>
|
<classpathentry combineaccessrules="false" kind="src" path="/ddmlib"/>
|
||||||
<classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/ANDROID_SWT"/>
|
<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.AllocationInfo;
|
||||||
import com.android.ddmlib.Client;
|
import com.android.ddmlib.Client;
|
||||||
import com.android.ddmlib.ClientData;
|
|
||||||
import com.android.ddmlib.AndroidDebugBridge.IClientChangeListener;
|
import com.android.ddmlib.AndroidDebugBridge.IClientChangeListener;
|
||||||
|
import com.android.ddmlib.ClientData.AllocationTrackingStatus;
|
||||||
|
|
||||||
import org.eclipse.jface.preference.IPreferenceStore;
|
import org.eclipse.jface.preference.IPreferenceStore;
|
||||||
import org.eclipse.jface.viewers.ILabelProviderListener;
|
import org.eclipse.jface.viewers.ILabelProviderListener;
|
||||||
@@ -181,8 +181,8 @@ public class AllocationPanel extends TablePanel {
|
|||||||
@Override
|
@Override
|
||||||
public void widgetSelected(SelectionEvent e) {
|
public void widgetSelected(SelectionEvent e) {
|
||||||
Client current = getCurrentClient();
|
Client current = getCurrentClient();
|
||||||
int status = current.getClientData().getAllocationStatus();
|
AllocationTrackingStatus status = current.getClientData().getAllocationStatus();
|
||||||
if (status == ClientData.ALLOCATION_TRACKING_ON) {
|
if (status == AllocationTrackingStatus.ON) {
|
||||||
current.enableAllocationTracker(false);
|
current.enableAllocationTracker(false);
|
||||||
} else {
|
} else {
|
||||||
current.enableAllocationTracker(true);
|
current.enableAllocationTracker(true);
|
||||||
@@ -200,7 +200,7 @@ public class AllocationPanel extends TablePanel {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
setUpButtons(false /* enabled */, ClientData.ALLOCATION_TRACKING_OFF /* trackingStatus */);
|
setUpButtons(false /* enabled */, AllocationTrackingStatus.OFF);
|
||||||
|
|
||||||
mAllocationTable = new Table(topParent, SWT.MULTI | SWT.FULL_SELECTION);
|
mAllocationTable = new Table(topParent, SWT.MULTI | SWT.FULL_SELECTION);
|
||||||
GridData gridData;
|
GridData gridData;
|
||||||
@@ -329,7 +329,7 @@ public class AllocationPanel extends TablePanel {
|
|||||||
* @param client the updated client.
|
* @param client the updated client.
|
||||||
* @param changeMask the bit mask describing the changed properties. It can contain
|
* @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}
|
* 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_THREAD_DATA}, {@link Client#CHANGE_HEAP_MODE},
|
||||||
* {@link Client#CHANGE_HEAP_DATA}, {@link Client#CHANGE_NATIVE_HEAP_DATA}
|
* {@link Client#CHANGE_HEAP_DATA}, {@link Client#CHANGE_NATIVE_HEAP_DATA}
|
||||||
*
|
*
|
||||||
@@ -389,8 +389,7 @@ public class AllocationPanel extends TablePanel {
|
|||||||
if (client != null) {
|
if (client != null) {
|
||||||
setUpButtons(true /* enabled */, client.getClientData().getAllocationStatus());
|
setUpButtons(true /* enabled */, client.getClientData().getAllocationStatus());
|
||||||
} else {
|
} else {
|
||||||
setUpButtons(false /* enabled */,
|
setUpButtons(false /* enabled */, AllocationTrackingStatus.OFF);
|
||||||
ClientData.ALLOCATION_TRACKING_OFF /* trackingStatus */);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mAllocationViewer.setInput(client);
|
mAllocationViewer.setInput(client);
|
||||||
@@ -458,20 +457,20 @@ public class AllocationPanel extends TablePanel {
|
|||||||
* @param enabled
|
* @param enabled
|
||||||
* @param trackingStatus
|
* @param trackingStatus
|
||||||
*/
|
*/
|
||||||
private void setUpButtons(boolean enabled, int trackingStatus) {
|
private void setUpButtons(boolean enabled, AllocationTrackingStatus trackingStatus) {
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
switch (trackingStatus) {
|
switch (trackingStatus) {
|
||||||
case ClientData.ALLOCATION_TRACKING_UNKNOWN:
|
case UNKNOWN:
|
||||||
mEnableButton.setText("?");
|
mEnableButton.setText("?");
|
||||||
mEnableButton.setEnabled(false);
|
mEnableButton.setEnabled(false);
|
||||||
mRequestButton.setEnabled(false);
|
mRequestButton.setEnabled(false);
|
||||||
break;
|
break;
|
||||||
case ClientData.ALLOCATION_TRACKING_OFF:
|
case OFF:
|
||||||
mEnableButton.setText("Start Tracking");
|
mEnableButton.setText("Start Tracking");
|
||||||
mEnableButton.setEnabled(true);
|
mEnableButton.setEnabled(true);
|
||||||
mRequestButton.setEnabled(false);
|
mRequestButton.setEnabled(false);
|
||||||
break;
|
break;
|
||||||
case ClientData.ALLOCATION_TRACKING_ON:
|
case ON:
|
||||||
mEnableButton.setText("Stop Tracking");
|
mEnableButton.setText("Stop Tracking");
|
||||||
mEnableButton.setEnabled(true);
|
mEnableButton.setEnabled(true);
|
||||||
mRequestButton.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.IClientChangeListener;
|
||||||
import com.android.ddmlib.AndroidDebugBridge.IDebugBridgeChangeListener;
|
import com.android.ddmlib.AndroidDebugBridge.IDebugBridgeChangeListener;
|
||||||
import com.android.ddmlib.AndroidDebugBridge.IDeviceChangeListener;
|
import com.android.ddmlib.AndroidDebugBridge.IDeviceChangeListener;
|
||||||
|
import com.android.ddmlib.ClientData.DebuggerStatus;
|
||||||
import com.android.ddmlib.IDevice.DeviceState;
|
import com.android.ddmlib.IDevice.DeviceState;
|
||||||
|
|
||||||
import org.eclipse.jface.preference.IPreferenceStore;
|
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_HALT = "halt.png"; //$NON-NLS-1$
|
||||||
public final static String ICON_GC = "gc.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_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 IDevice mCurrentDevice;
|
||||||
private Client mCurrentClient;
|
private Client mCurrentClient;
|
||||||
@@ -167,13 +170,13 @@ public final class DevicePanel extends Panel implements IDebugBridgeChangeListen
|
|||||||
switch (columnIndex) {
|
switch (columnIndex) {
|
||||||
case CLIENT_COL_NAME:
|
case CLIENT_COL_NAME:
|
||||||
switch (cd.getDebuggerConnectionStatus()) {
|
switch (cd.getDebuggerConnectionStatus()) {
|
||||||
case ClientData.DEBUGGER_DEFAULT:
|
case DEFAULT:
|
||||||
return null;
|
return null;
|
||||||
case ClientData.DEBUGGER_WAITING:
|
case WAITING:
|
||||||
return mWaitingImage;
|
return mWaitingImage;
|
||||||
case ClientData.DEBUGGER_ATTACHED:
|
case ATTACHED:
|
||||||
return mDebuggerImage;
|
return mDebuggerImage;
|
||||||
case ClientData.DEBUGGER_ERROR:
|
case ERROR:
|
||||||
return mDebugErrorImage;
|
return mDebugErrorImage;
|
||||||
}
|
}
|
||||||
return null;
|
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) {
|
public void setEnabledHeapOnSelectedClient(boolean enable) {
|
||||||
if (mCurrentClient != null) {
|
if (mCurrentClient != null) {
|
||||||
@@ -594,7 +602,7 @@ public final class DevicePanel extends Panel implements IDebugBridgeChangeListen
|
|||||||
* @param client the updated client.
|
* @param client the updated client.
|
||||||
* @param changeMask the bit mask describing the changed properties. It can contain
|
* @param changeMask the bit mask describing the changed properties. It can contain
|
||||||
* any of the following values: {@link Client#CHANGE_INFO},
|
* 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_THREAD_DATA}, {@link Client#CHANGE_HEAP_MODE},
|
||||||
* {@link Client#CHANGE_HEAP_DATA}, {@link Client#CHANGE_NATIVE_HEAP_DATA}
|
* {@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
|
// refresh the client
|
||||||
mTreeViewer.refresh(client);
|
mTreeViewer.refresh(client);
|
||||||
|
|
||||||
if ((changeMask & Client.CHANGE_DEBUGGER_INTEREST) ==
|
if ((changeMask & Client.CHANGE_DEBUGGER_STATUS) ==
|
||||||
Client.CHANGE_DEBUGGER_INTEREST &&
|
Client.CHANGE_DEBUGGER_STATUS &&
|
||||||
client.getClientData().getDebuggerConnectionStatus() ==
|
client.getClientData().getDebuggerConnectionStatus() ==
|
||||||
ClientData.DEBUGGER_WAITING) {
|
DebuggerStatus.WAITING) {
|
||||||
// make sure the device is expanded. Normally the setSelection below
|
// make sure the device is expanded. Normally the setSelection below
|
||||||
// will auto expand, but the children of device may not already exist
|
// will auto expand, but the children of device may not already exist
|
||||||
// at this time. Forcing an expand will make the TreeViewer create them.
|
// 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 client the updated client.
|
||||||
* @param changeMask the bit mask describing the changed properties. It can contain
|
* @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}
|
* 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_THREAD_DATA}, {@link Client#CHANGE_HEAP_MODE},
|
||||||
* {@link Client#CHANGE_HEAP_DATA}, {@link Client#CHANGE_NATIVE_HEAP_DATA}
|
* {@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 client the updated client.
|
||||||
* @param changeMask the bit mask describing the changed properties. It can contain
|
* @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}
|
* 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_THREAD_DATA}, {@link Client#CHANGE_HEAP_MODE},
|
||||||
* {@link Client#CHANGE_HEAP_DATA}, {@link Client#CHANGE_NATIVE_HEAP_DATA}
|
* {@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 client the updated client.
|
||||||
* @param changeMask the bit mask describing the changed properties. It can contain
|
* @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}
|
* 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_THREAD_DATA}, {@link Client#CHANGE_HEAP_MODE},
|
||||||
* {@link Client#CHANGE_HEAP_DATA}, {@link Client#CHANGE_NATIVE_HEAP_DATA}
|
* {@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 client the updated client.
|
||||||
* @param changeMask the bit mask describing the changed properties. It can contain
|
* @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}
|
* 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_THREAD_DATA}, {@link Client#CHANGE_HEAP_MODE},
|
||||||
* {@link Client#CHANGE_HEAP_DATA}, {@link Client#CHANGE_NATIVE_HEAP_DATA}
|
* {@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/push.png
|
||||||
com.android.ide.eclipse.ddms/icons/save.png
|
com.android.ide.eclipse.ddms/icons/save.png
|
||||||
com.android.ide.eclipse.ddms/icons/thread.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/up.png
|
||||||
com.android.ide.eclipse.ddms/icons/v.png
|
com.android.ide.eclipse.ddms/icons/v.png
|
||||||
com.android.ide.eclipse.ddms/icons/w.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.IClientChangeListener;
|
||||||
import com.android.ddmlib.AndroidDebugBridge.IDebugBridgeChangeListener;
|
import com.android.ddmlib.AndroidDebugBridge.IDebugBridgeChangeListener;
|
||||||
import com.android.ddmlib.AndroidDebugBridge.IDeviceChangeListener;
|
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.AdtPlugin;
|
||||||
import com.android.ide.eclipse.adt.internal.launch.AndroidLaunchConfiguration.TargetMode;
|
import com.android.ide.eclipse.adt.internal.launch.AndroidLaunchConfiguration.TargetMode;
|
||||||
import com.android.ide.eclipse.adt.internal.launch.DelayedLaunchInfo.InstallRetryMode;
|
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 client the updated client.
|
||||||
* @param changeMask the bit mask describing the changed properties. It can contain
|
* @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}
|
* 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_THREAD_DATA}, {@link Client#CHANGE_HEAP_MODE},
|
||||||
* {@link Client#CHANGE_HEAP_DATA}, {@link Client#CHANGE_NATIVE_HEAP_DATA}
|
* {@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.
|
// 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;
|
// search for this client in the list;
|
||||||
synchronized (sListLock) {
|
synchronized (sListLock) {
|
||||||
int index = mUnknownClientsWaitingForDebugger.indexOf(client);
|
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
|
// if it's not home, it could be an app that is now in debugger mode that we're waiting for
|
||||||
// lets check it
|
// 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();
|
ClientData clientData = client.getClientData();
|
||||||
String applicationName = client.getClientData().getClientDescription();
|
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.
|
// Get the application name, and make sure its valid.
|
||||||
if (applicationName == null) {
|
if (applicationName == null) {
|
||||||
// looks like we don't have the client yet, so we keep it around for when its
|
// 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.AndroidDebugBridge;
|
||||||
import com.android.ddmlib.Client;
|
import com.android.ddmlib.Client;
|
||||||
|
import com.android.ddmlib.DdmConstants;
|
||||||
import com.android.ddmlib.DdmPreferences;
|
import com.android.ddmlib.DdmPreferences;
|
||||||
import com.android.ddmlib.IDevice;
|
import com.android.ddmlib.IDevice;
|
||||||
import com.android.ddmlib.Log;
|
import com.android.ddmlib.Log;
|
||||||
@@ -56,20 +57,6 @@ import java.util.Calendar;
|
|||||||
public final class DdmsPlugin extends AbstractUIPlugin implements IDeviceChangeListener,
|
public final class DdmsPlugin extends AbstractUIPlugin implements IDeviceChangeListener,
|
||||||
IUiSelectionListener {
|
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
|
// The plug-in ID
|
||||||
public static final String PLUGIN_ID = "com.android.ide.eclipse.ddms"; // $NON-NLS-1$
|
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();
|
File toolsFolder = adb.getParentFile();
|
||||||
sToolsFolder = toolsFolder.getAbsolutePath();
|
sToolsFolder = toolsFolder.getAbsolutePath();
|
||||||
|
|
||||||
File hprofConverter = new File(toolsFolder, FN_HPROF_CONVERTER);
|
File hprofConverter = new File(toolsFolder, DdmConstants.FN_HPROF_CONVERTER);
|
||||||
sHprofConverter = hprofConverter.getAbsolutePath();
|
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);
|
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.ClientData;
|
||||||
import com.android.ddmlib.IDevice;
|
import com.android.ddmlib.IDevice;
|
||||||
import com.android.ddmlib.SyncService;
|
import com.android.ddmlib.SyncService;
|
||||||
|
import com.android.ddmlib.AndroidDebugBridge.IClientChangeListener;
|
||||||
import com.android.ddmlib.ClientData.IHprofDumpHandler;
|
import com.android.ddmlib.ClientData.IHprofDumpHandler;
|
||||||
|
import com.android.ddmlib.ClientData.MethodProfilingStatus;
|
||||||
import com.android.ddmlib.SyncService.SyncResult;
|
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.DevicePanel;
|
||||||
import com.android.ddmuilib.ScreenShotDialog;
|
import com.android.ddmuilib.ScreenShotDialog;
|
||||||
import com.android.ddmuilib.SyncProgressMonitor;
|
|
||||||
import com.android.ddmuilib.DevicePanel.IUiSelectionListener;
|
import com.android.ddmuilib.DevicePanel.IUiSelectionListener;
|
||||||
import com.android.ide.eclipse.ddms.DdmsPlugin;
|
import com.android.ide.eclipse.ddms.DdmsPlugin;
|
||||||
import com.android.ide.eclipse.ddms.DdmsPlugin.IDebugLauncher;
|
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.EFS;
|
||||||
import org.eclipse.core.filesystem.IFileStore;
|
import org.eclipse.core.filesystem.IFileStore;
|
||||||
import org.eclipse.core.runtime.IProgressMonitor;
|
|
||||||
import org.eclipse.core.runtime.Path;
|
import org.eclipse.core.runtime.Path;
|
||||||
import org.eclipse.jface.action.Action;
|
import org.eclipse.jface.action.Action;
|
||||||
import org.eclipse.jface.action.IAction;
|
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.IToolBarManager;
|
||||||
import org.eclipse.jface.action.Separator;
|
import org.eclipse.jface.action.Separator;
|
||||||
import org.eclipse.jface.dialogs.MessageDialog;
|
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.IPreferenceStore;
|
||||||
import org.eclipse.swt.SWT;
|
import org.eclipse.jface.resource.ImageDescriptor;
|
||||||
import org.eclipse.swt.widgets.Composite;
|
import org.eclipse.swt.widgets.Composite;
|
||||||
import org.eclipse.swt.widgets.Display;
|
import org.eclipse.swt.widgets.Display;
|
||||||
import org.eclipse.swt.widgets.FileDialog;
|
|
||||||
import org.eclipse.swt.widgets.Shell;
|
import org.eclipse.swt.widgets.Shell;
|
||||||
import org.eclipse.ui.IActionBars;
|
import org.eclipse.ui.IActionBars;
|
||||||
import org.eclipse.ui.ISharedImages;
|
import org.eclipse.ui.ISharedImages;
|
||||||
@@ -59,9 +58,8 @@ import org.eclipse.ui.part.ViewPart;
|
|||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
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;
|
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 static DeviceView sThis;
|
||||||
|
|
||||||
|
private Shell mParentShell;
|
||||||
private DevicePanel mDeviceList;
|
private DevicePanel mDeviceList;
|
||||||
|
|
||||||
private Action mResetAdbAction;
|
private Action mResetAdbAction;
|
||||||
private Action mCaptureAction;
|
private Action mCaptureAction;
|
||||||
private Action mUpdateThreadAction;
|
private Action mUpdateThreadAction;
|
||||||
@@ -79,29 +79,29 @@ public class DeviceView extends ViewPart implements IUiSelectionListener {
|
|||||||
private Action mKillAppAction;
|
private Action mKillAppAction;
|
||||||
private Action mDebugAction;
|
private Action mDebugAction;
|
||||||
private Action mHprofAction;
|
private Action mHprofAction;
|
||||||
|
private Action mTracingAction;
|
||||||
private IDebugLauncher mDebugLauncher;
|
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_SAVE ="hprof.save"; //$NON-NLS-1$
|
||||||
public final static String ACTION_OPEN = "hprof.open"; //$NON-NLS-1$
|
public final static String ACTION_OPEN = "hprof.open"; //$NON-NLS-1$
|
||||||
|
|
||||||
public final static String DOT_HPROF = ".hprof"; //$NON-NLS-1$
|
public final static String DOT_HPROF = ".hprof"; //$NON-NLS-1$
|
||||||
|
|
||||||
private final Shell mParentShell;
|
|
||||||
|
|
||||||
HProfHandler(Shell parentShell) {
|
HProfHandler(Shell parentShell) {
|
||||||
mParentShell = parentShell;
|
super(parentShell);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onFailure(final Client client) {
|
public void onFailure(final Client client) {
|
||||||
mParentShell.getDisplay().asyncExec(new Runnable() {
|
mParentShell.getDisplay().asyncExec(new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
MessageDialog.openError(mParentShell, "HPROF Error",
|
displayError("Unable to create HPROF file for application '%1$s'.\n" +
|
||||||
String.format(
|
"Check logcat for more information.",
|
||||||
"Unable to create HPROF file for application '%1$s'.\n" +
|
client.getClientData().getClientDescription());
|
||||||
"Check logcat for more information.",
|
|
||||||
client.getClientData().getClientDescription()));
|
|
||||||
} finally {
|
} finally {
|
||||||
// this will make sure the dump hprof button is re-enabled for the
|
// this will make sure the dump hprof button is re-enabled for the
|
||||||
// current selection. as the client is finished dumping an hprof file
|
// 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() {
|
mParentShell.getDisplay().asyncExec(new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
final IDevice device = client.getDevice();
|
final IDevice device = client.getDevice();
|
||||||
@@ -122,26 +122,35 @@ public class DeviceView extends ViewPart implements IUiSelectionListener {
|
|||||||
// get from the preference what action to take
|
// get from the preference what action to take
|
||||||
IPreferenceStore store = DdmsPlugin.getDefault().getPreferenceStore();
|
IPreferenceStore store = DdmsPlugin.getDefault().getPreferenceStore();
|
||||||
String value = store.getString(PreferenceInitializer.ATTR_HPROF_ACTION);
|
String value = store.getString(PreferenceInitializer.ATTR_HPROF_ACTION);
|
||||||
|
|
||||||
|
SyncResult result = null;
|
||||||
if (ACTION_OPEN.equals(value)) {
|
if (ACTION_OPEN.equals(value)) {
|
||||||
File temp = File.createTempFile("android", DOT_HPROF); //$NON-NLS-1$
|
File temp = File.createTempFile("android", DOT_HPROF); //$NON-NLS-1$
|
||||||
String tempPath = temp.getAbsolutePath();
|
String tempPath = temp.getAbsolutePath();
|
||||||
pull(sync, tempPath, remoteFile);
|
result = pull(sync, tempPath, remoteFilePath);
|
||||||
|
if (result != null && result.getCode() == SyncService.RESULT_OK) {
|
||||||
open(tempPath);
|
open(tempPath);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// default action is ACTION_SAVE
|
// 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 {
|
} else {
|
||||||
MessageDialog.openError(mParentShell, "HPROF Error",
|
displayError("Unable to download HPROF file from device '%1$s'.",
|
||||||
String.format(
|
device.getSerialNumber());
|
||||||
"Unable to download HPROF file from device '%1$s'.",
|
|
||||||
device.getSerialNumber()));
|
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
MessageDialog.openError(mParentShell, "HPROF Error",
|
displayError("Unable to download HPROF file from device '%1$s'.",
|
||||||
String.format("Unable to download HPROF file from device '%1$s'.",
|
device.getSerialNumber());
|
||||||
device.getSerialNumber()));
|
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
// this will make sure the dump hprof button is re-enabled for the
|
// 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 {
|
private void open(String path) throws IOException, InterruptedException, PartInitException {
|
||||||
// make a temp file to convert the hprof into something
|
// make a temp file to convert the hprof into something
|
||||||
// readable by normal tools
|
// readable by normal tools
|
||||||
@@ -215,6 +182,11 @@ public class DeviceView extends ViewPart implements IUiSelectionListener {
|
|||||||
fileStore);
|
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
|
@Override
|
||||||
public void createPartControl(Composite parent) {
|
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 = new DevicePanel(DdmsPlugin.getImageLoader(), USE_SELECTED_DEBUG_PORT);
|
||||||
mDeviceList.createPanel(parent);
|
mDeviceList.createPanel(parent);
|
||||||
@@ -350,6 +325,20 @@ public class DeviceView extends ViewPart implements IUiSelectionListener {
|
|||||||
mUpdateThreadAction.setImageDescriptor(DdmsPlugin.getImageLoader()
|
mUpdateThreadAction.setImageDescriptor(DdmsPlugin.getImageLoader()
|
||||||
.loadDescriptor(DevicePanel.ICON_THREAD));
|
.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
|
// check if there's already a debug launcher set up in the plugin class
|
||||||
mDebugLauncher = DdmsPlugin.getRunningAppDebugLauncher();
|
mDebugLauncher = DdmsPlugin.getRunningAppDebugLauncher();
|
||||||
|
|
||||||
@@ -363,14 +352,14 @@ public class DeviceView extends ViewPart implements IUiSelectionListener {
|
|||||||
|
|
||||||
// make sure the client can be debugged
|
// make sure the client can be debugged
|
||||||
switch (clientData.getDebuggerConnectionStatus()) {
|
switch (clientData.getDebuggerConnectionStatus()) {
|
||||||
case ClientData.DEBUGGER_ERROR: {
|
case ERROR: {
|
||||||
Display display = DdmsPlugin.getDisplay();
|
Display display = DdmsPlugin.getDisplay();
|
||||||
Shell shell = display.getActiveShell();
|
Shell shell = display.getActiveShell();
|
||||||
MessageDialog.openError(shell, "Process Debug",
|
MessageDialog.openError(shell, "Process Debug",
|
||||||
"The process debug port is already in use!");
|
"The process debug port is already in use!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case ClientData.DEBUGGER_ATTACHED: {
|
case ATTACHED: {
|
||||||
Display display = DdmsPlugin.getDisplay();
|
Display display = DdmsPlugin.getDisplay();
|
||||||
Shell shell = display.getActiveShell();
|
Shell shell = display.getActiveShell();
|
||||||
MessageDialog.openError(shell, "Process Debug",
|
MessageDialog.openError(shell, "Process Debug",
|
||||||
@@ -442,9 +431,34 @@ public class DeviceView extends ViewPart implements IUiSelectionListener {
|
|||||||
|
|
||||||
mUpdateThreadAction.setEnabled(true);
|
mUpdateThreadAction.setEnabled(true);
|
||||||
mUpdateThreadAction.setChecked(selectedClient.isThreadUpdateEnabled());
|
mUpdateThreadAction.setChecked(selectedClient.isThreadUpdateEnabled());
|
||||||
mHprofAction.setEnabled(
|
|
||||||
selectedClient.getClientData().hasFeature(ClientData.FEATURE_HPROF) &&
|
ClientData data = selectedClient.getClientData();
|
||||||
selectedClient.getClientData().hasPendingHprofDump() == false);
|
|
||||||
|
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 {
|
} else {
|
||||||
if (USE_SELECTED_DEBUG_PORT) {
|
if (USE_SELECTED_DEBUG_PORT) {
|
||||||
// set the client as the debug client
|
// set the client as the debug client
|
||||||
@@ -462,6 +476,14 @@ public class DeviceView extends ViewPart implements IUiSelectionListener {
|
|||||||
mUpdateThreadAction.setEnabled(false);
|
mUpdateThreadAction.setEnabled(false);
|
||||||
mUpdateThreadAction.setChecked(false);
|
mUpdateThreadAction.setChecked(false);
|
||||||
mHprofAction.setEnabled(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
|
// first in the menu
|
||||||
IMenuManager menuManager = actionBars.getMenuManager();
|
IMenuManager menuManager = actionBars.getMenuManager();
|
||||||
|
menuManager.removeAll();
|
||||||
menuManager.add(mDebugAction);
|
menuManager.add(mDebugAction);
|
||||||
menuManager.add(new Separator());
|
menuManager.add(new Separator());
|
||||||
menuManager.add(mUpdateThreadAction);
|
|
||||||
menuManager.add(mUpdateHeapAction);
|
menuManager.add(mUpdateHeapAction);
|
||||||
menuManager.add(new Separator());
|
|
||||||
menuManager.add(mGcAction);
|
|
||||||
menuManager.add(mHprofAction);
|
menuManager.add(mHprofAction);
|
||||||
|
menuManager.add(mGcAction);
|
||||||
|
menuManager.add(new Separator());
|
||||||
|
menuManager.add(mUpdateThreadAction);
|
||||||
|
menuManager.add(mTracingAction);
|
||||||
menuManager.add(new Separator());
|
menuManager.add(new Separator());
|
||||||
menuManager.add(mKillAppAction);
|
menuManager.add(mKillAppAction);
|
||||||
menuManager.add(new Separator());
|
menuManager.add(new Separator());
|
||||||
@@ -493,17 +517,32 @@ public class DeviceView extends ViewPart implements IUiSelectionListener {
|
|||||||
|
|
||||||
// and then in the toolbar
|
// and then in the toolbar
|
||||||
IToolBarManager toolBarManager = actionBars.getToolBarManager();
|
IToolBarManager toolBarManager = actionBars.getToolBarManager();
|
||||||
|
toolBarManager.removeAll();
|
||||||
toolBarManager.add(mDebugAction);
|
toolBarManager.add(mDebugAction);
|
||||||
toolBarManager.add(new Separator());
|
toolBarManager.add(new Separator());
|
||||||
toolBarManager.add(mUpdateThreadAction);
|
|
||||||
toolBarManager.add(mUpdateHeapAction);
|
toolBarManager.add(mUpdateHeapAction);
|
||||||
toolBarManager.add(new Separator());
|
|
||||||
toolBarManager.add(mGcAction);
|
|
||||||
toolBarManager.add(mHprofAction);
|
toolBarManager.add(mHprofAction);
|
||||||
|
toolBarManager.add(mGcAction);
|
||||||
|
toolBarManager.add(new Separator());
|
||||||
|
toolBarManager.add(mUpdateThreadAction);
|
||||||
|
toolBarManager.add(mTracingAction);
|
||||||
toolBarManager.add(new Separator());
|
toolBarManager.add(new Separator());
|
||||||
toolBarManager.add(mKillAppAction);
|
toolBarManager.add(mKillAppAction);
|
||||||
toolBarManager.add(new Separator());
|
toolBarManager.add(new Separator());
|
||||||
toolBarManager.add(mCaptureAction);
|
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 \
|
load.png \
|
||||||
pause.png play.png pull.png push.png \
|
pause.png play.png pull.png push.png \
|
||||||
save.png \
|
save.png \
|
||||||
thread.png \
|
thread.png tracing_start.png tracing_stop.png \
|
||||||
up.png \
|
up.png \
|
||||||
v.png \
|
v.png \
|
||||||
w.png warning.png ; do
|
w.png warning.png ; do
|
||||||
|
|||||||
Reference in New Issue
Block a user