| /* |
| * Copyright (C) 2008 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.ide.eclipse.ddms.views; |
| |
| import com.android.ddmlib.AndroidDebugBridge; |
| import com.android.ddmlib.AndroidDebugBridge.IClientChangeListener; |
| import com.android.ddmlib.Client; |
| import com.android.ddmlib.ClientData; |
| import com.android.ddmlib.ClientData.IHprofDumpHandler; |
| import com.android.ddmlib.ClientData.MethodProfilingStatus; |
| import com.android.ddmlib.CollectingOutputReceiver; |
| import com.android.ddmlib.DdmPreferences; |
| import com.android.ddmlib.IDevice; |
| import com.android.ddmlib.SyncException; |
| import com.android.ddmlib.SyncService; |
| import com.android.ddmlib.SyncService.ISyncProgressMonitor; |
| import com.android.ddmlib.TimeoutException; |
| import com.android.ddmuilib.DevicePanel; |
| import com.android.ddmuilib.DevicePanel.IUiSelectionListener; |
| import com.android.ddmuilib.ImageLoader; |
| import com.android.ddmuilib.ScreenShotDialog; |
| import com.android.ddmuilib.SyncProgressHelper; |
| import com.android.ddmuilib.SyncProgressHelper.SyncRunnable; |
| import com.android.ddmuilib.handler.BaseFileHandler; |
| import com.android.ddmuilib.handler.MethodProfilingHandler; |
| import com.android.ide.eclipse.ddms.DdmsPlugin; |
| import com.android.ide.eclipse.ddms.IClientAction; |
| import com.android.ide.eclipse.ddms.IDebuggerConnector; |
| import com.android.ide.eclipse.ddms.editors.UiAutomatorViewer; |
| import com.android.ide.eclipse.ddms.i18n.Messages; |
| import com.android.ide.eclipse.ddms.preferences.PreferenceInitializer; |
| import com.android.ide.eclipse.ddms.systrace.ISystraceOptions; |
| import com.android.ide.eclipse.ddms.systrace.ISystraceOptionsDialog; |
| import com.android.ide.eclipse.ddms.systrace.SystraceOptionsDialogV1; |
| import com.android.ide.eclipse.ddms.systrace.SystraceOptionsDialogV2; |
| import com.android.ide.eclipse.ddms.systrace.SystraceOutputParser; |
| import com.android.ide.eclipse.ddms.systrace.SystraceTask; |
| import com.android.ide.eclipse.ddms.systrace.SystraceVersionDetector; |
| import com.android.uiautomator.UiAutomatorHelper; |
| import com.android.uiautomator.UiAutomatorHelper.UiAutomatorException; |
| import com.android.uiautomator.UiAutomatorHelper.UiAutomatorResult; |
| import com.google.common.io.Files; |
| |
| import org.eclipse.core.filesystem.EFS; |
| import org.eclipse.core.filesystem.IFileStore; |
| import org.eclipse.core.runtime.IAdaptable; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.Path; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.jface.action.Action; |
| import org.eclipse.jface.action.IAction; |
| import org.eclipse.jface.action.IMenuManager; |
| import org.eclipse.jface.action.IToolBarManager; |
| import org.eclipse.jface.action.Separator; |
| import org.eclipse.jface.dialogs.ErrorDialog; |
| 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.resource.ImageDescriptor; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Display; |
| import org.eclipse.swt.widgets.Shell; |
| import org.eclipse.ui.IActionBars; |
| import org.eclipse.ui.ISharedImages; |
| import org.eclipse.ui.IWorkbench; |
| import org.eclipse.ui.IWorkbenchPage; |
| import org.eclipse.ui.IWorkbenchWindow; |
| import org.eclipse.ui.PartInitException; |
| import org.eclipse.ui.PlatformUI; |
| import org.eclipse.ui.WorkbenchException; |
| import org.eclipse.ui.ide.IDE; |
| import org.eclipse.ui.part.ViewPart; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.lang.reflect.InvocationTargetException; |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.concurrent.CountDownLatch; |
| import java.util.concurrent.TimeUnit; |
| |
| public class DeviceView extends ViewPart implements IUiSelectionListener, IClientChangeListener { |
| |
| private final static boolean USE_SELECTED_DEBUG_PORT = true; |
| |
| public static final String ID = "com.android.ide.eclipse.ddms.views.DeviceView"; //$NON-NLS-1$ |
| |
| private static DeviceView sThis; |
| |
| private Shell mParentShell; |
| private DevicePanel mDeviceList; |
| |
| private Action mResetAdbAction; |
| private Action mCaptureAction; |
| private Action mViewUiAutomatorHierarchyAction; |
| private Action mSystraceAction; |
| private Action mUpdateThreadAction; |
| private Action mUpdateHeapAction; |
| private Action mGcAction; |
| private Action mKillAppAction; |
| private Action mDebugAction; |
| private Action mHprofAction; |
| private Action mTracingAction; |
| |
| private ImageDescriptor mTracingStartImage; |
| private ImageDescriptor mTracingStopImage; |
| |
| public class HProfHandler extends BaseFileHandler implements IHprofDumpHandler { |
| public final static String ACTION_SAVE = "hprof.save"; //$NON-NLS-1$ |
| public final static String ACTION_OPEN = "hprof.open"; //$NON-NLS-1$ |
| |
| public final static String DOT_HPROF = ".hprof"; //$NON-NLS-1$ |
| |
| HProfHandler(Shell parentShell) { |
| super(parentShell); |
| } |
| |
| @Override |
| protected String getDialogTitle() { |
| return Messages.DeviceView_HPROF_Error; |
| } |
| |
| @Override |
| public void onEndFailure(final Client client, final String message) { |
| mParentShell.getDisplay().asyncExec(new Runnable() { |
| @Override |
| public void run() { |
| try { |
| displayErrorFromUiThread( |
| Messages.DeviceView_Unable_Create_HPROF_For_Application, |
| client.getClientData().getClientDescription(), |
| message != null ? message + "\n\n" : ""); //$NON-NLS-1$ //$NON-NLS-2$ |
| } finally { |
| // this will make sure the dump hprof button is |
| // re-enabled for the |
| // current selection. as the client is finished dumping |
| // an hprof file |
| doSelectionChanged(mDeviceList.getSelectedClient()); |
| } |
| } |
| }); |
| } |
| |
| @Override |
| public void onSuccess(final String remoteFilePath, final Client client) { |
| mParentShell.getDisplay().asyncExec(new Runnable() { |
| @Override |
| public void run() { |
| final IDevice device = client.getDevice(); |
| try { |
| // get the sync service to pull the HPROF file |
| final SyncService sync = client.getDevice().getSyncService(); |
| if (sync != null) { |
| // get from the preference what action to take |
| IPreferenceStore store = DdmsPlugin.getDefault().getPreferenceStore(); |
| String value = store.getString(PreferenceInitializer.ATTR_HPROF_ACTION); |
| |
| if (ACTION_OPEN.equals(value)) { |
| File temp = File.createTempFile("android", DOT_HPROF); //$NON-NLS-1$ |
| final String tempPath = temp.getAbsolutePath(); |
| SyncProgressHelper.run(new SyncRunnable() { |
| |
| @Override |
| public void run(ISyncProgressMonitor monitor) |
| throws SyncException, IOException, |
| TimeoutException { |
| sync.pullFile(remoteFilePath, tempPath, monitor); |
| } |
| |
| @Override |
| public void close() { |
| sync.close(); |
| } |
| }, |
| String.format(Messages.DeviceView_Pulling_From_Device, |
| remoteFilePath), |
| mParentShell); |
| |
| open(tempPath); |
| } else { |
| // default action is ACTION_SAVE |
| promptAndPull(sync, |
| client.getClientData().getClientDescription() + DOT_HPROF, |
| remoteFilePath, Messages.DeviceView_Save_HPROF_File); |
| |
| } |
| } else { |
| displayErrorFromUiThread( |
| Messages.DeviceView_Unable_Download_HPROF_From_Device_One_Param_First_Message, |
| device.getSerialNumber()); |
| } |
| } catch (SyncException e) { |
| if (e.wasCanceled() == false) { |
| displayErrorFromUiThread( |
| Messages.DeviceView_Unable_Download_HPROF_From_Device_Two_Param, |
| device.getSerialNumber(), e.getMessage()); |
| } |
| } catch (Exception e) { |
| displayErrorFromUiThread( |
| Messages.DeviceView_Unable_Download_HPROF_From_Device_One_Param_Second_Message, |
| device.getSerialNumber()); |
| |
| } finally { |
| // this will make sure the dump hprof button is |
| // re-enabled for the |
| // current selection. as the client is finished dumping |
| // an hprof file |
| doSelectionChanged(mDeviceList.getSelectedClient()); |
| } |
| } |
| }); |
| } |
| |
| @Override |
| public void onSuccess(final byte[] data, final Client client) { |
| mParentShell.getDisplay().asyncExec(new Runnable() { |
| @Override |
| public void run() { |
| // get from the preference what action to take |
| IPreferenceStore store = DdmsPlugin.getDefault().getPreferenceStore(); |
| String value = store.getString(PreferenceInitializer.ATTR_HPROF_ACTION); |
| |
| if (ACTION_OPEN.equals(value)) { |
| try { |
| // no need to give an extension since we're going to |
| // convert the |
| // file anyway after. |
| File tempFile = saveTempFile(data, null /* extension */); |
| open(tempFile.getAbsolutePath()); |
| } catch (Exception e) { |
| String errorMsg = e.getMessage(); |
| displayErrorFromUiThread( |
| Messages.DeviceView_Failed_To_Save_HPROF_Data, |
| errorMsg != null ? ":\n" + errorMsg : "."); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| } else { |
| // default action is ACTION_SAVE |
| promptAndSave(client.getClientData().getClientDescription() + DOT_HPROF, |
| data, Messages.DeviceView_Save_HPROF_File); |
| } |
| } |
| }); |
| } |
| |
| private void open(String path) throws IOException, InterruptedException, PartInitException { |
| // make a temp file to convert the hprof into something |
| // readable by normal tools |
| File temp = File.createTempFile("android", DOT_HPROF); //$NON-NLS-1$ |
| String tempPath = temp.getAbsolutePath(); |
| |
| String[] command = new String[3]; |
| command[0] = DdmsPlugin.getHprofConverter(); |
| command[1] = path; |
| command[2] = tempPath; |
| |
| Process p = Runtime.getRuntime().exec(command); |
| p.waitFor(); |
| |
| IFileStore fileStore = EFS.getLocalFileSystem().getStore(new Path(tempPath)); |
| if (!fileStore.fetchInfo().isDirectory() && fileStore.fetchInfo().exists()) { |
| // before we open the file in an editor window, we make sure the |
| // current |
| // workbench page has an editor area (typically the ddms |
| // perspective doesn't). |
| IWorkbench workbench = PlatformUI.getWorkbench(); |
| IWorkbenchWindow window = workbench.getActiveWorkbenchWindow(); |
| IWorkbenchPage page = window.getActivePage(); |
| if (page == null) { |
| return; |
| } |
| |
| if (page.isEditorAreaVisible() == false) { |
| IAdaptable input; |
| input = page.getInput(); |
| try { |
| workbench.showPerspective("org.eclipse.debug.ui.DebugPerspective", //$NON-NLS-1$ |
| window, input); |
| } catch (WorkbenchException e) { |
| } |
| } |
| |
| IDE.openEditorOnFileStore(page, fileStore); |
| } |
| } |
| } |
| |
| public DeviceView() { |
| // the view is declared with allowMultiple="false" so we |
| // can safely do this. |
| sThis = this; |
| } |
| |
| public static DeviceView getInstance() { |
| return sThis; |
| } |
| |
| @Override |
| public void createPartControl(Composite parent) { |
| mParentShell = parent.getShell(); |
| |
| ImageLoader loader = ImageLoader.getDdmUiLibLoader(); |
| |
| mDeviceList = new DevicePanel(USE_SELECTED_DEBUG_PORT); |
| mDeviceList.createPanel(parent); |
| mDeviceList.addSelectionListener(this); |
| |
| DdmsPlugin plugin = DdmsPlugin.getDefault(); |
| mDeviceList.addSelectionListener(plugin); |
| plugin.setListeningState(true); |
| |
| mCaptureAction = new Action(Messages.DeviceView_Screen_Capture) { |
| @Override |
| public void run() { |
| ScreenShotDialog dlg = new ScreenShotDialog( |
| DdmsPlugin.getDisplay().getActiveShell()); |
| dlg.open(mDeviceList.getSelectedDevice()); |
| } |
| }; |
| mCaptureAction.setToolTipText(Messages.DeviceView_Screen_Capture_Tooltip); |
| mCaptureAction.setImageDescriptor(loader.loadDescriptor("capture.png")); //$NON-NLS-1$ |
| |
| mViewUiAutomatorHierarchyAction = new Action("Dump View Hierarchy for UI Automator") { |
| @Override |
| public void run() { |
| takeUiAutomatorSnapshot(mDeviceList.getSelectedDevice(), |
| DdmsPlugin.getDisplay().getActiveShell()); |
| } |
| }; |
| mViewUiAutomatorHierarchyAction.setToolTipText("Dump View Hierarchy for UI Automator"); |
| mViewUiAutomatorHierarchyAction.setImageDescriptor( |
| DdmsPlugin.getImageDescriptor("icons/uiautomator.png")); //$NON-NLS-1$ |
| |
| mSystraceAction = new Action("Capture System Wide Trace") { |
| @Override |
| public void run() { |
| launchSystrace(mDeviceList.getSelectedDevice(), |
| DdmsPlugin.getDisplay().getActiveShell()); |
| } |
| }; |
| mSystraceAction.setToolTipText("Capture system wide trace using Android systrace"); |
| mSystraceAction.setImageDescriptor( |
| DdmsPlugin.getImageDescriptor("icons/systrace.png")); //$NON-NLS-1$ |
| mSystraceAction.setEnabled(true); |
| |
| mResetAdbAction = new Action(Messages.DeviceView_Reset_ADB) { |
| @Override |
| public void run() { |
| AndroidDebugBridge bridge = AndroidDebugBridge.getBridge(); |
| if (bridge != null) { |
| if (bridge.restart() == false) { |
| // get the current Display |
| final Display display = DdmsPlugin.getDisplay(); |
| |
| // dialog box only run in ui thread.. |
| display.asyncExec(new Runnable() { |
| @Override |
| public void run() { |
| Shell shell = display.getActiveShell(); |
| MessageDialog.openError(shell, Messages.DeviceView_ADB_Error, |
| Messages.DeviceView_ADB_Failed_Restart); |
| } |
| }); |
| } |
| } |
| } |
| }; |
| mResetAdbAction.setToolTipText(Messages.DeviceView_Reset_ADB_Host_Deamon); |
| mResetAdbAction.setImageDescriptor(PlatformUI.getWorkbench() |
| .getSharedImages().getImageDescriptor( |
| ISharedImages.IMG_OBJS_WARN_TSK)); |
| |
| mKillAppAction = new Action() { |
| @Override |
| public void run() { |
| mDeviceList.killSelectedClient(); |
| } |
| }; |
| |
| mKillAppAction.setText(Messages.DeviceView_Stop_Process); |
| mKillAppAction.setToolTipText(Messages.DeviceView_Stop_Process_Tooltip); |
| mKillAppAction.setImageDescriptor(loader.loadDescriptor(DevicePanel.ICON_HALT)); |
| |
| mGcAction = new Action() { |
| @Override |
| public void run() { |
| mDeviceList.forceGcOnSelectedClient(); |
| } |
| }; |
| |
| mGcAction.setText(Messages.DeviceView_Cause_GC); |
| mGcAction.setToolTipText(Messages.DeviceView_Cause_GC_Tooltip); |
| mGcAction.setImageDescriptor(loader.loadDescriptor(DevicePanel.ICON_GC)); |
| |
| mHprofAction = new Action() { |
| @Override |
| public void run() { |
| mDeviceList.dumpHprof(); |
| doSelectionChanged(mDeviceList.getSelectedClient()); |
| } |
| }; |
| mHprofAction.setText(Messages.DeviceView_Dump_HPROF_File); |
| mHprofAction.setToolTipText(Messages.DeviceView_Dump_HPROF_File_Tooltip); |
| mHprofAction.setImageDescriptor(loader.loadDescriptor(DevicePanel.ICON_HPROF)); |
| |
| mUpdateHeapAction = new Action(Messages.DeviceView_Update_Heap, IAction.AS_CHECK_BOX) { |
| @Override |
| public void run() { |
| boolean enable = mUpdateHeapAction.isChecked(); |
| mDeviceList.setEnabledHeapOnSelectedClient(enable); |
| } |
| }; |
| mUpdateHeapAction.setToolTipText(Messages.DeviceView_Update_Heap_Tooltip); |
| mUpdateHeapAction.setImageDescriptor(loader.loadDescriptor(DevicePanel.ICON_HEAP)); |
| |
| mUpdateThreadAction = new Action(Messages.DeviceView_Threads, IAction.AS_CHECK_BOX) { |
| @Override |
| public void run() { |
| boolean enable = mUpdateThreadAction.isChecked(); |
| mDeviceList.setEnabledThreadOnSelectedClient(enable); |
| } |
| }; |
| mUpdateThreadAction.setToolTipText(Messages.DeviceView_Threads_Tooltip); |
| mUpdateThreadAction.setImageDescriptor(loader.loadDescriptor(DevicePanel.ICON_THREAD)); |
| |
| mTracingAction = new Action() { |
| @Override |
| public void run() { |
| mDeviceList.toggleMethodProfiling(); |
| } |
| }; |
| mTracingAction.setText(Messages.DeviceView_Start_Method_Profiling); |
| mTracingAction.setToolTipText(Messages.DeviceView_Start_Method_Profiling_Tooltip); |
| mTracingStartImage = loader.loadDescriptor(DevicePanel.ICON_TRACING_START); |
| mTracingStopImage = loader.loadDescriptor(DevicePanel.ICON_TRACING_STOP); |
| mTracingAction.setImageDescriptor(mTracingStartImage); |
| |
| mDebugAction = new Action(Messages.DeviceView_Debug_Process) { |
| @Override |
| public void run() { |
| if (DdmsPlugin.getDefault().hasDebuggerConnectors()) { |
| Client currentClient = mDeviceList.getSelectedClient(); |
| if (currentClient != null) { |
| ClientData clientData = currentClient.getClientData(); |
| |
| // make sure the client can be debugged |
| switch (clientData.getDebuggerConnectionStatus()) { |
| case ERROR: { |
| Display display = DdmsPlugin.getDisplay(); |
| Shell shell = display.getActiveShell(); |
| MessageDialog.openError(shell, |
| Messages.DeviceView_Debug_Process_Title, |
| Messages.DeviceView_Process_Debug_Already_In_Use); |
| return; |
| } |
| case ATTACHED: { |
| Display display = DdmsPlugin.getDisplay(); |
| Shell shell = display.getActiveShell(); |
| MessageDialog.openError(shell, |
| Messages.DeviceView_Debug_Process_Title, |
| Messages.DeviceView_Process_Already_Being_Debugged); |
| return; |
| } |
| } |
| |
| // get the name of the client |
| String packageName = clientData.getClientDescription(); |
| if (packageName != null) { |
| |
| // try all connectors till one returns true. |
| IDebuggerConnector[] connectors = |
| DdmsPlugin.getDefault().getDebuggerConnectors(); |
| |
| if (connectors != null) { |
| for (IDebuggerConnector connector : connectors) { |
| try { |
| if (connector.connectDebugger(packageName, |
| currentClient.getDebuggerListenPort(), |
| DdmPreferences.getSelectedDebugPort())) { |
| return; |
| } |
| } catch (Throwable t) { |
| // ignore, we'll just not use this |
| // implementation |
| } |
| } |
| } |
| |
| // if we get to this point, then we failed to find a |
| // project |
| // that matched the application to debug |
| Display display = DdmsPlugin.getDisplay(); |
| Shell shell = display.getActiveShell(); |
| MessageDialog.openError(shell, Messages.DeviceView_Debug_Process_Title, |
| String.format( |
| Messages.DeviceView_Debug_Session_Failed, |
| packageName)); |
| } |
| } |
| } |
| } |
| }; |
| mDebugAction.setToolTipText(Messages.DeviceView_Debug_Process_Tooltip); |
| mDebugAction.setImageDescriptor(loader.loadDescriptor("debug-attach.png")); //$NON-NLS-1$ |
| mDebugAction.setEnabled(DdmsPlugin.getDefault().hasDebuggerConnectors()); |
| |
| placeActions(); |
| |
| // disabling all action buttons |
| selectionChanged(null, null); |
| |
| ClientData.setHprofDumpHandler(new HProfHandler(mParentShell)); |
| AndroidDebugBridge.addClientChangeListener(this); |
| ClientData.setMethodProfilingHandler(new MethodProfilingHandler(mParentShell) { |
| @Override |
| protected void open(String tempPath) { |
| if (DdmsPlugin.getDefault().launchTraceview(tempPath) == false) { |
| super.open(tempPath); |
| } |
| } |
| }); |
| } |
| |
| private void takeUiAutomatorSnapshot(final IDevice device, final Shell shell) { |
| ProgressMonitorDialog dialog = new ProgressMonitorDialog(shell); |
| try { |
| dialog.run(true, false, new IRunnableWithProgress() { |
| @Override |
| public void run(IProgressMonitor monitor) throws InvocationTargetException, |
| InterruptedException { |
| UiAutomatorResult result = null; |
| try { |
| result = UiAutomatorHelper.takeSnapshot(device, monitor); |
| } catch (UiAutomatorException e) { |
| throw new InvocationTargetException(e); |
| } finally { |
| monitor.done(); |
| } |
| |
| UiAutomatorViewer.openEditor(result); |
| } |
| }); |
| } catch (Exception e) { |
| Throwable t = e; |
| if (e instanceof InvocationTargetException) { |
| t = ((InvocationTargetException) e).getTargetException(); |
| } |
| Status s = new Status(IStatus.ERROR, DdmsPlugin.PLUGIN_ID, |
| "Error obtaining UI hierarchy", t); |
| ErrorDialog.openError(shell, "UI Automator", |
| "Unexpected error while obtaining UI hierarchy", s); |
| } |
| }; |
| |
| private void launchSystrace(final IDevice device, final Shell parentShell) { |
| final File systraceAssets = new File(DdmsPlugin.getPlatformToolsFolder(), "systrace"); //$NON-NLS-1$ |
| if (!systraceAssets.isDirectory()) { |
| MessageDialog.openError(parentShell, "Systrace", |
| "Updated version of platform-tools (18.0.1 or greater) is required.\n" |
| + "Please update your platform-tools using SDK Manager."); |
| return; |
| } |
| |
| SystraceVersionDetector detector = new SystraceVersionDetector(device); |
| try { |
| new ProgressMonitorDialog(parentShell).run(true, false, detector); |
| } catch (InvocationTargetException e) { |
| MessageDialog.openError(parentShell, |
| "Systrace", |
| "Unexpected error while detecting atrace version: " + e); |
| return; |
| } catch (InterruptedException e) { |
| return; |
| } |
| |
| final ISystraceOptionsDialog dlg; |
| if (detector.getVersion() == SystraceVersionDetector.SYSTRACE_V1) { |
| dlg = new SystraceOptionsDialogV1(parentShell); |
| } else { |
| Client[] clients = device.getClients(); |
| List<String> apps = new ArrayList<String>(clients.length); |
| for (int i = 0; i < clients.length; i++) { |
| String name = clients[i].getClientData().getClientDescription(); |
| if (name != null && !name.isEmpty()) { |
| apps.add(name); |
| } |
| } |
| dlg = new SystraceOptionsDialogV2(parentShell, detector.getTags(), apps); |
| } |
| |
| if (dlg.open() != SystraceOptionsDialogV1.OK) { |
| return; |
| } |
| |
| final ISystraceOptions options = dlg.getSystraceOptions(); |
| |
| // set trace tag if necessary: |
| // adb shell setprop debug.atrace.tags.enableflags <tag> |
| String tag = options.getTags(); |
| if (tag != null) { |
| CountDownLatch setTagLatch = new CountDownLatch(1); |
| CollectingOutputReceiver receiver = new CollectingOutputReceiver(setTagLatch); |
| try { |
| String cmd = "setprop debug.atrace.tags.enableflags " + tag; |
| device.executeShellCommand(cmd, receiver); |
| setTagLatch.await(5, TimeUnit.SECONDS); |
| } catch (Exception e) { |
| MessageDialog.openError(parentShell, |
| "Systrace", |
| "Unexpected error while setting trace tags: " + e); |
| return; |
| } |
| |
| String shellOutput = receiver.getOutput(); |
| if (shellOutput.contains("Error type")) { //$NON-NLS-1$ |
| throw new RuntimeException(receiver.getOutput()); |
| } |
| } |
| |
| // obtain the output of "adb shell atrace <trace-options>" and generate the html file |
| ProgressMonitorDialog d = new ProgressMonitorDialog(parentShell); |
| try { |
| d.run(true, true, new IRunnableWithProgress() { |
| @Override |
| public void run(IProgressMonitor monitor) throws InvocationTargetException, |
| InterruptedException { |
| boolean COMPRESS_DATA = true; |
| |
| monitor.setTaskName("Collecting Trace Information"); |
| final String atraceOptions = options.getOptions() |
| + (COMPRESS_DATA ? " -z" : ""); |
| SystraceTask task = new SystraceTask(device, atraceOptions); |
| Thread t = new Thread(task, "Systrace Output Receiver"); |
| t.start(); |
| |
| // check if the user has cancelled tracing every so often |
| while (true) { |
| t.join(1000); |
| |
| if (t.isAlive()) { |
| if (monitor.isCanceled()) { |
| task.cancel(); |
| return; |
| } |
| } else { |
| break; |
| } |
| } |
| |
| if (task.getError() != null) { |
| throw new RuntimeException(task.getError()); |
| } |
| |
| monitor.setTaskName("Saving trace information"); |
| SystraceOutputParser parser = new SystraceOutputParser( |
| COMPRESS_DATA, |
| SystraceOutputParser.getJs(systraceAssets), |
| SystraceOutputParser.getCss(systraceAssets), |
| SystraceOutputParser.getHtmlPrefix(systraceAssets), |
| SystraceOutputParser.getHtmlSuffix(systraceAssets)); |
| |
| parser.parse(task.getAtraceOutput()); |
| |
| String html = parser.getSystraceHtml(); |
| try { |
| Files.write(html.getBytes(), new File(dlg.getTraceFilePath())); |
| } catch (IOException e) { |
| throw new InvocationTargetException(e); |
| } |
| } |
| }); |
| } catch (InvocationTargetException e) { |
| ErrorDialog.openError(parentShell, "Systrace", |
| "Unable to collect system trace.", |
| new Status(Status.ERROR, |
| DdmsPlugin.PLUGIN_ID, |
| "Unexpected error while collecting system trace.", |
| e.getCause())); |
| } catch (InterruptedException ignore) { |
| } |
| } |
| |
| @Override |
| public void setFocus() { |
| mDeviceList.setFocus(); |
| } |
| |
| /** |
| * Sent when a new {@link IDevice} and {@link Client} are selected. |
| * |
| * @param selectedDevice the selected device. If null, no devices are |
| * selected. |
| * @param selectedClient The selected client. If null, no clients are |
| * selected. |
| */ |
| @Override |
| public void selectionChanged(IDevice selectedDevice, Client selectedClient) { |
| // update the buttons |
| doSelectionChanged(selectedClient); |
| doSelectionChanged(selectedDevice); |
| } |
| |
| private void doSelectionChanged(Client selectedClient) { |
| // update the buttons |
| if (selectedClient != null) { |
| if (USE_SELECTED_DEBUG_PORT) { |
| // set the client as the debug client |
| selectedClient.setAsSelectedClient(); |
| } |
| |
| mDebugAction.setEnabled(DdmsPlugin.getDefault().hasDebuggerConnectors()); |
| mKillAppAction.setEnabled(true); |
| mGcAction.setEnabled(true); |
| |
| mUpdateHeapAction.setEnabled(true); |
| mUpdateHeapAction.setChecked(selectedClient.isHeapUpdateEnabled()); |
| |
| mUpdateThreadAction.setEnabled(true); |
| mUpdateThreadAction.setChecked(selectedClient.isThreadUpdateEnabled()); |
| |
| ClientData data = selectedClient.getClientData(); |
| |
| if (data.hasFeature(ClientData.FEATURE_HPROF)) { |
| mHprofAction.setEnabled(data.hasPendingHprofDump() == false); |
| mHprofAction.setToolTipText(Messages.DeviceView_Dump_HPROF_File); |
| } else { |
| mHprofAction.setEnabled(false); |
| mHprofAction |
| .setToolTipText(Messages.DeviceView_Dump_HPROF_File_Not_Supported_By_VM); |
| } |
| |
| if (data.hasFeature(ClientData.FEATURE_PROFILING)) { |
| mTracingAction.setEnabled(true); |
| if (data.getMethodProfilingStatus() == MethodProfilingStatus.TRACER_ON |
| || data.getMethodProfilingStatus() == MethodProfilingStatus.SAMPLER_ON) { |
| mTracingAction |
| .setToolTipText(Messages.DeviceView_Stop_Method_Profiling_Tooltip); |
| mTracingAction.setText(Messages.DeviceView_Stop_Method_Profiling); |
| mTracingAction.setImageDescriptor(mTracingStopImage); |
| } else { |
| mTracingAction |
| .setToolTipText(Messages.DeviceView_Start_Method_Profiling_Tooltip); |
| mTracingAction.setImageDescriptor(mTracingStartImage); |
| mTracingAction.setText(Messages.DeviceView_Start_Method_Profiling); |
| } |
| } else { |
| mTracingAction.setEnabled(false); |
| mTracingAction.setImageDescriptor(mTracingStartImage); |
| mTracingAction |
| .setToolTipText(Messages.DeviceView_Start_Method_Profiling_Not_Suported_By_Vm); |
| mTracingAction.setText(Messages.DeviceView_Start_Method_Profiling); |
| } |
| } else { |
| if (USE_SELECTED_DEBUG_PORT) { |
| // set the client as the debug client |
| AndroidDebugBridge bridge = AndroidDebugBridge.getBridge(); |
| if (bridge != null) { |
| bridge.setSelectedClient(null); |
| } |
| } |
| |
| mDebugAction.setEnabled(false); |
| mKillAppAction.setEnabled(false); |
| mGcAction.setEnabled(false); |
| mUpdateHeapAction.setChecked(false); |
| mUpdateHeapAction.setEnabled(false); |
| mUpdateThreadAction.setEnabled(false); |
| mUpdateThreadAction.setChecked(false); |
| mHprofAction.setEnabled(false); |
| |
| mHprofAction.setEnabled(false); |
| mHprofAction.setToolTipText(Messages.DeviceView_Dump_HPROF_File); |
| |
| mTracingAction.setEnabled(false); |
| mTracingAction.setImageDescriptor(mTracingStartImage); |
| mTracingAction.setToolTipText(Messages.DeviceView_Start_Method_Profiling_Tooltip); |
| mTracingAction.setText(Messages.DeviceView_Start_Method_Profiling); |
| } |
| |
| for (IClientAction a : DdmsPlugin.getDefault().getClientSpecificActions()) { |
| a.selectedClientChanged(selectedClient); |
| } |
| } |
| |
| private void doSelectionChanged(IDevice selectedDevice) { |
| boolean validDevice = selectedDevice != null; |
| |
| mCaptureAction.setEnabled(validDevice); |
| mViewUiAutomatorHierarchyAction.setEnabled(validDevice); |
| mSystraceAction.setEnabled(validDevice); |
| } |
| |
| /** |
| * Place the actions in the ui. |
| */ |
| private final void placeActions() { |
| IActionBars actionBars = getViewSite().getActionBars(); |
| |
| // first in the menu |
| IMenuManager menuManager = actionBars.getMenuManager(); |
| menuManager.removeAll(); |
| menuManager.add(mDebugAction); |
| menuManager.add(new Separator()); |
| menuManager.add(mUpdateHeapAction); |
| menuManager.add(mHprofAction); |
| menuManager.add(mGcAction); |
| menuManager.add(new Separator()); |
| menuManager.add(mUpdateThreadAction); |
| menuManager.add(mTracingAction); |
| menuManager.add(new Separator()); |
| menuManager.add(mKillAppAction); |
| menuManager.add(new Separator()); |
| menuManager.add(mCaptureAction); |
| menuManager.add(new Separator()); |
| menuManager.add(mViewUiAutomatorHierarchyAction); |
| menuManager.add(new Separator()); |
| menuManager.add(mSystraceAction); |
| menuManager.add(new Separator()); |
| menuManager.add(mResetAdbAction); |
| for (IClientAction a : DdmsPlugin.getDefault().getClientSpecificActions()) { |
| menuManager.add(a.getAction()); |
| } |
| |
| // and then in the toolbar |
| IToolBarManager toolBarManager = actionBars.getToolBarManager(); |
| toolBarManager.removeAll(); |
| toolBarManager.add(mDebugAction); |
| toolBarManager.add(new Separator()); |
| toolBarManager.add(mUpdateHeapAction); |
| toolBarManager.add(mHprofAction); |
| toolBarManager.add(mGcAction); |
| toolBarManager.add(new Separator()); |
| toolBarManager.add(mUpdateThreadAction); |
| toolBarManager.add(mTracingAction); |
| toolBarManager.add(new Separator()); |
| toolBarManager.add(mKillAppAction); |
| toolBarManager.add(new Separator()); |
| toolBarManager.add(mCaptureAction); |
| toolBarManager.add(new Separator()); |
| toolBarManager.add(mViewUiAutomatorHierarchyAction); |
| toolBarManager.add(new Separator()); |
| toolBarManager.add(mSystraceAction); |
| for (IClientAction a : DdmsPlugin.getDefault().getClientSpecificActions()) { |
| toolBarManager.add(a.getAction()); |
| } |
| } |
| |
| @Override |
| 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() { |
| @Override |
| public void run() { |
| // force refresh of the button enabled state. |
| doSelectionChanged(client); |
| } |
| }); |
| } |
| } |
| } |
| } |