ddms: Add support for sampling profiler
This CL adds supports for sampling based profiling.
When method profiling is initiated, we detect whether sampling
profiling is supported and if so, we show a dialog that allows
users to select the type of profiling to be performed.
The methods to obtain and display trace data is unchanged.
Change-Id: I8ec1a2c2311d6ffa7f73b5690d71e9d2253d09f2
diff --git a/ddms/app/src/main/java/com/android/ddms/UIThread.java b/ddms/app/src/main/java/com/android/ddms/UIThread.java
index 1310429..7680f08 100644
--- a/ddms/app/src/main/java/com/android/ddms/UIThread.java
+++ b/ddms/app/src/main/java/com/android/ddms/UIThread.java
@@ -1719,7 +1719,8 @@
if (data.hasFeature(ClientData.FEATURE_PROFILING)) {
mTBProfiling.setEnabled(true);
- if (data.getMethodProfilingStatus() == MethodProfilingStatus.ON) {
+ if (data.getMethodProfilingStatus() == MethodProfilingStatus.TRACER_ON
+ || data.getMethodProfilingStatus() == MethodProfilingStatus.SAMPLER_ON) {
mTBProfiling.setToolTipText("Stop Method Profiling");
mTBProfiling.setImage(mTracingStopImage);
} else {
@@ -1729,7 +1730,7 @@
} else {
mTBProfiling.setEnabled(false);
mTBProfiling.setImage(mTracingStartImage);
- mTBProfiling.setToolTipText("Start Method Profiling (not supported by this VM)");
+ mTBProfiling.setToolTipText("Method Profiling (not supported by this VM)");
}
} else {
// list is empty, disable these
diff --git a/ddms/ddmuilib/src/main/java/com/android/ddmuilib/DevicePanel.java b/ddms/ddmuilib/src/main/java/com/android/ddmuilib/DevicePanel.java
index a24b8a0..fb97062 100644
--- a/ddms/ddmuilib/src/main/java/com/android/ddmuilib/DevicePanel.java
+++ b/ddms/ddmuilib/src/main/java/com/android/ddmuilib/DevicePanel.java
@@ -16,6 +16,7 @@
package com.android.ddmuilib;
+import com.android.annotations.NonNull;
import com.android.ddmlib.AndroidDebugBridge;
import com.android.ddmlib.AndroidDebugBridge.IClientChangeListener;
import com.android.ddmlib.AndroidDebugBridge.IDebugBridgeChangeListener;
@@ -26,7 +27,10 @@
import com.android.ddmlib.DdmPreferences;
import com.android.ddmlib.IDevice;
import com.android.ddmlib.IDevice.DeviceState;
+import com.android.ddmuilib.vmtrace.VmTraceOptionsDialog;
+import com.google.common.base.Throwables;
+import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.viewers.ILabelProviderListener;
import org.eclipse.jface.viewers.ITableLabelProvider;
@@ -35,6 +39,7 @@
import org.eclipse.jface.viewers.TreeSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.SWTException;
import org.eclipse.swt.events.SelectionAdapter;
@@ -48,8 +53,10 @@
import org.eclipse.swt.widgets.TreeColumn;
import org.eclipse.swt.widgets.TreeItem;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.Locale;
+import java.util.concurrent.TimeUnit;
/**
* A display of both the devices and their clients.
@@ -310,7 +317,6 @@
/**
* Creates the {@link DevicePanel} object.
- * @param loader
* @param advancedPortSupport if true the device panel will add support for selected client port
* and display the ports in the ui.
*/
@@ -447,8 +453,49 @@
}
public void toggleMethodProfiling() {
- if (mCurrentClient != null) {
- mCurrentClient.toggleMethodProfiling();
+ if (mCurrentClient == null) {
+ return;
+ }
+
+ try {
+ toggleMethodProfiling(mCurrentClient);
+ } catch (IOException e) {
+ MessageDialog.openError(mTree.getShell(), "Method Profiling",
+ "Unexpected I/O error while starting/stopping profiling: "
+ + Throwables.getRootCause(e).getMessage());
+ }
+ }
+
+ private void toggleMethodProfiling(@NonNull Client client) throws IOException {
+ ClientData cd = mCurrentClient.getClientData();
+ if (cd.getMethodProfilingStatus() == ClientData.MethodProfilingStatus.TRACER_ON) {
+ mCurrentClient.stopMethodTracer();
+ } else if (cd.getMethodProfilingStatus() == ClientData.MethodProfilingStatus.SAMPLER_ON) {
+ mCurrentClient.stopSamplingProfiler();
+ } else {
+ boolean supportsSampling = cd.hasFeature(ClientData.FEATURE_SAMPLING_PROFILER);
+
+ // default to tracing
+ boolean shouldUseTracing = true;
+ int samplingIntervalMicros = 1;
+
+ // if client supports sampling, then ask the user to choose the method
+ if (supportsSampling) {
+ VmTraceOptionsDialog dialog = new VmTraceOptionsDialog(mTree.getShell());
+ if (dialog.open() == Window.CANCEL) {
+ return;
+ }
+ shouldUseTracing = dialog.shouldUseTracing();
+ if (!shouldUseTracing) {
+ samplingIntervalMicros = dialog.getSamplingIntervalMicros();
+ }
+ }
+
+ if (shouldUseTracing) {
+ mCurrentClient.startMethodTracer();
+ } else {
+ mCurrentClient.startSamplingProfiler(samplingIntervalMicros, TimeUnit.MICROSECONDS);
+ }
}
}
@@ -469,8 +516,6 @@
* <p/>
* This is sent from a non UI thread.
* @param bridge the new {@link AndroidDebugBridge} object.
- *
- * @see IDebugBridgeChangeListener#serverChanged(AndroidDebugBridge)
*/
@Override
public void bridgeChanged(final AndroidDebugBridge bridge) {
@@ -563,7 +608,7 @@
* @param device the device that was updated.
* @param changeMask the mask indicating what changed.
*
- * @see IDeviceChangeListener#deviceChanged(IDevice)
+ * @see IDeviceChangeListener#deviceChanged(IDevice,int)
*/
@Override
public void deviceChanged(final IDevice device, int changeMask) {
diff --git a/ddms/ddmuilib/src/main/java/com/android/ddmuilib/vmtrace/VmTraceOptionsDialog.java b/ddms/ddmuilib/src/main/java/com/android/ddmuilib/vmtrace/VmTraceOptionsDialog.java
new file mode 100644
index 0000000..b3cfb4e
--- /dev/null
+++ b/ddms/ddmuilib/src/main/java/com/android/ddmuilib/vmtrace/VmTraceOptionsDialog.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2013 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.vmtrace;
+
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+/** Dialog that allows users to select between method tracing or sampler based profiling. */
+public class VmTraceOptionsDialog extends Dialog {
+ private static final int DEFAULT_SAMPLING_INTERVAL_US = 1000;
+
+ // Static variables that maintain state across invocations of the dialog
+ private static boolean sTracingEnabled = true;
+ private static int sSamplingIntervalUs = DEFAULT_SAMPLING_INTERVAL_US;
+
+ public VmTraceOptionsDialog(Shell parentShell) {
+ super(parentShell);
+ }
+
+ @Override
+ protected void configureShell(Shell newShell) {
+ super.configureShell(newShell);
+ newShell.setText("Profiling Options");
+ }
+
+ @Override
+ protected Control createDialogArea(Composite shell) {
+ int horizontalIndent = 30;
+
+ Composite parent = (Composite) super.createDialogArea(shell);
+ Composite c = new Composite(parent, SWT.NONE);
+ c.setLayout(new GridLayout(2, false));
+ c.setLayoutData(new GridData(GridData.FILL_BOTH));
+
+ final Button useTracingButton = new Button(c, SWT.RADIO);
+ useTracingButton.setText("Trace based profiling");
+ useTracingButton.setSelection(sTracingEnabled);
+ GridData gd = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING,
+ GridData.VERTICAL_ALIGN_CENTER, true, true, 2, 1);
+ useTracingButton.setLayoutData(gd);
+
+ Label l = new Label(c, SWT.NONE);
+ l.setText("Trace based profiling works by tracing the entry and exit of every method.\n"
+ + "This gives an accurate view of the execution, but has a high overhead.");
+ gd = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING, GridData.VERTICAL_ALIGN_CENTER, true,
+ true, 2, 1);
+ gd.horizontalIndent = horizontalIndent;
+ l.setLayoutData(gd);
+
+ final Button useSamplingButton = new Button(c, SWT.RADIO);
+ useSamplingButton.setText("Sample based profiling");
+ useSamplingButton.setSelection(!sTracingEnabled);
+ gd = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING, GridData.VERTICAL_ALIGN_CENTER, true,
+ true, 2, 1);
+ useSamplingButton.setLayoutData(gd);
+
+ l = new Label(c, SWT.NONE);
+ l.setText("Sample based profiling works by interrupting the VM at a given frequency and "
+ + "collecting the call stacks at that time.\n"
+ + "This has a much lower overhead, but statistical sampling requires longer runs "
+ + "to obtain a representative sample.");
+ gd = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING, GridData.VERTICAL_ALIGN_CENTER, true,
+ true, 2, 1);
+ gd.horizontalIndent = horizontalIndent;
+ l.setLayoutData(gd);
+
+ l = new Label(c, SWT.NONE);
+ l.setText("Sampling frequency (microseconds): ");
+ gd = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING, GridData.VERTICAL_ALIGN_END,
+ false, true);
+ gd.horizontalIndent = horizontalIndent;
+ l.setLayoutData(gd);
+
+ final Text samplingIntervalTextField = new Text(c, SWT.BORDER);
+ gd = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING, GridData.VERTICAL_ALIGN_CENTER, true,
+ true);
+ gd.widthHint = 100;
+ samplingIntervalTextField.setLayoutData(gd);
+ samplingIntervalTextField.setEnabled(!sTracingEnabled);
+ samplingIntervalTextField.setText(Integer.toString(sSamplingIntervalUs));
+ samplingIntervalTextField.addModifyListener(new ModifyListener() {
+ @Override
+ public void modifyText(ModifyEvent modifyEvent) {
+ int v = getIntegerValue(samplingIntervalTextField.getText());
+ getButton(IDialogConstants.OK_ID).setEnabled(v > 0);
+ sSamplingIntervalUs = v > 0 ? v : DEFAULT_SAMPLING_INTERVAL_US;
+ }
+
+ private int getIntegerValue(String text) {
+ try {
+ return Integer.parseInt(text);
+ } catch (NumberFormatException e) {
+ return -1;
+ }
+ }
+ });
+
+ SelectionAdapter selectionAdapter = new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent event) {
+ sTracingEnabled = useTracingButton.getSelection();
+ samplingIntervalTextField.setEnabled(!sTracingEnabled);
+ }
+ };
+ useTracingButton.addSelectionListener(selectionAdapter);
+ useSamplingButton.addSelectionListener(selectionAdapter);
+
+ return c;
+ }
+
+ public boolean shouldUseTracing() {
+ return sTracingEnabled;
+ }
+
+ public int getSamplingIntervalMicros() {
+ return sSamplingIntervalUs;
+ }
+}