Home | History | Annotate | Download | only in views
      1 /*
      2  * Copyright (C) 2008 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.ide.eclipse.ddms.views;
     18 
     19 import com.android.ddmlib.AndroidDebugBridge;
     20 import com.android.ddmlib.AndroidDebugBridge.IClientChangeListener;
     21 import com.android.ddmlib.Client;
     22 import com.android.ddmlib.ClientData;
     23 import com.android.ddmlib.ClientData.IHprofDumpHandler;
     24 import com.android.ddmlib.ClientData.MethodProfilingStatus;
     25 import com.android.ddmlib.CollectingOutputReceiver;
     26 import com.android.ddmlib.DdmPreferences;
     27 import com.android.ddmlib.IDevice;
     28 import com.android.ddmlib.SyncException;
     29 import com.android.ddmlib.SyncService;
     30 import com.android.ddmlib.SyncService.ISyncProgressMonitor;
     31 import com.android.ddmlib.TimeoutException;
     32 import com.android.ddmuilib.DevicePanel;
     33 import com.android.ddmuilib.DevicePanel.IUiSelectionListener;
     34 import com.android.ddmuilib.ImageLoader;
     35 import com.android.ddmuilib.ScreenShotDialog;
     36 import com.android.ddmuilib.SyncProgressHelper;
     37 import com.android.ddmuilib.SyncProgressHelper.SyncRunnable;
     38 import com.android.ddmuilib.handler.BaseFileHandler;
     39 import com.android.ddmuilib.handler.MethodProfilingHandler;
     40 import com.android.ide.eclipse.ddms.DdmsPlugin;
     41 import com.android.ide.eclipse.ddms.IClientAction;
     42 import com.android.ide.eclipse.ddms.IDebuggerConnector;
     43 import com.android.ide.eclipse.ddms.editors.UiAutomatorViewer;
     44 import com.android.ide.eclipse.ddms.i18n.Messages;
     45 import com.android.ide.eclipse.ddms.preferences.PreferenceInitializer;
     46 import com.android.ide.eclipse.ddms.systrace.ISystraceOptions;
     47 import com.android.ide.eclipse.ddms.systrace.ISystraceOptionsDialog;
     48 import com.android.ide.eclipse.ddms.systrace.SystraceOptionsDialogV1;
     49 import com.android.ide.eclipse.ddms.systrace.SystraceOutputParser;
     50 import com.android.ide.eclipse.ddms.systrace.SystraceTask;
     51 import com.android.ide.eclipse.ddms.systrace.SystraceVersionDetector;
     52 import com.android.uiautomator.UiAutomatorHelper;
     53 import com.android.uiautomator.UiAutomatorHelper.UiAutomatorException;
     54 import com.android.uiautomator.UiAutomatorHelper.UiAutomatorResult;
     55 import com.google.common.io.Files;
     56 
     57 import org.eclipse.core.filesystem.EFS;
     58 import org.eclipse.core.filesystem.IFileStore;
     59 import org.eclipse.core.runtime.IAdaptable;
     60 import org.eclipse.core.runtime.IProgressMonitor;
     61 import org.eclipse.core.runtime.IStatus;
     62 import org.eclipse.core.runtime.Path;
     63 import org.eclipse.core.runtime.Status;
     64 import org.eclipse.jface.action.Action;
     65 import org.eclipse.jface.action.IAction;
     66 import org.eclipse.jface.action.IMenuManager;
     67 import org.eclipse.jface.action.IToolBarManager;
     68 import org.eclipse.jface.action.Separator;
     69 import org.eclipse.jface.dialogs.ErrorDialog;
     70 import org.eclipse.jface.dialogs.MessageDialog;
     71 import org.eclipse.jface.dialogs.ProgressMonitorDialog;
     72 import org.eclipse.jface.operation.IRunnableWithProgress;
     73 import org.eclipse.jface.preference.IPreferenceStore;
     74 import org.eclipse.jface.resource.ImageDescriptor;
     75 import org.eclipse.swt.widgets.Composite;
     76 import org.eclipse.swt.widgets.Display;
     77 import org.eclipse.swt.widgets.Shell;
     78 import org.eclipse.ui.IActionBars;
     79 import org.eclipse.ui.ISharedImages;
     80 import org.eclipse.ui.IWorkbench;
     81 import org.eclipse.ui.IWorkbenchPage;
     82 import org.eclipse.ui.IWorkbenchWindow;
     83 import org.eclipse.ui.PartInitException;
     84 import org.eclipse.ui.PlatformUI;
     85 import org.eclipse.ui.WorkbenchException;
     86 import org.eclipse.ui.ide.IDE;
     87 import org.eclipse.ui.part.ViewPart;
     88 
     89 import java.io.File;
     90 import java.io.IOException;
     91 import java.lang.reflect.InvocationTargetException;
     92 import java.util.concurrent.CountDownLatch;
     93 import java.util.concurrent.TimeUnit;
     94 
     95 public class DeviceView extends ViewPart implements IUiSelectionListener, IClientChangeListener {
     96 
     97     private final static boolean USE_SELECTED_DEBUG_PORT = true;
     98 
     99     public static final String ID = "com.android.ide.eclipse.ddms.views.DeviceView"; //$NON-NLS-1$
    100 
    101     private static DeviceView sThis;
    102 
    103     private Shell mParentShell;
    104     private DevicePanel mDeviceList;
    105 
    106     private Action mResetAdbAction;
    107     private Action mCaptureAction;
    108     private Action mViewUiAutomatorHierarchyAction;
    109     private Action mSystraceAction;
    110     private Action mUpdateThreadAction;
    111     private Action mUpdateHeapAction;
    112     private Action mGcAction;
    113     private Action mKillAppAction;
    114     private Action mDebugAction;
    115     private Action mHprofAction;
    116     private Action mTracingAction;
    117 
    118     private ImageDescriptor mTracingStartImage;
    119     private ImageDescriptor mTracingStopImage;
    120 
    121     public class HProfHandler extends BaseFileHandler implements IHprofDumpHandler {
    122         public final static String ACTION_SAVE = "hprof.save"; //$NON-NLS-1$
    123         public final static String ACTION_OPEN = "hprof.open"; //$NON-NLS-1$
    124 
    125         public final static String DOT_HPROF = ".hprof"; //$NON-NLS-1$
    126 
    127         HProfHandler(Shell parentShell) {
    128             super(parentShell);
    129         }
    130 
    131         @Override
    132         protected String getDialogTitle() {
    133             return Messages.DeviceView_HPROF_Error;
    134         }
    135 
    136         @Override
    137         public void onEndFailure(final Client client, final String message) {
    138             mParentShell.getDisplay().asyncExec(new Runnable() {
    139                 @Override
    140                 public void run() {
    141                     try {
    142                         displayErrorFromUiThread(
    143                                 Messages.DeviceView_Unable_Create_HPROF_For_Application,
    144                                 client.getClientData().getClientDescription(),
    145                                 message != null ? message + "\n\n" : ""); //$NON-NLS-1$ //$NON-NLS-2$
    146                     } finally {
    147                         // this will make sure the dump hprof button is
    148                         // re-enabled for the
    149                         // current selection. as the client is finished dumping
    150                         // an hprof file
    151                         doSelectionChanged(mDeviceList.getSelectedClient());
    152                     }
    153                 }
    154             });
    155         }
    156 
    157         @Override
    158         public void onSuccess(final String remoteFilePath, final Client client) {
    159             mParentShell.getDisplay().asyncExec(new Runnable() {
    160                 @Override
    161                 public void run() {
    162                     final IDevice device = client.getDevice();
    163                     try {
    164                         // get the sync service to pull the HPROF file
    165                         final SyncService sync = client.getDevice().getSyncService();
    166                         if (sync != null) {
    167                             // get from the preference what action to take
    168                             IPreferenceStore store = DdmsPlugin.getDefault().getPreferenceStore();
    169                             String value = store.getString(PreferenceInitializer.ATTR_HPROF_ACTION);
    170 
    171                             if (ACTION_OPEN.equals(value)) {
    172                                 File temp = File.createTempFile("android", DOT_HPROF); //$NON-NLS-1$
    173                                 final String tempPath = temp.getAbsolutePath();
    174                                 SyncProgressHelper.run(new SyncRunnable() {
    175 
    176                                     @Override
    177                                     public void run(ISyncProgressMonitor monitor)
    178                                                 throws SyncException, IOException,
    179                                                 TimeoutException {
    180                                         sync.pullFile(remoteFilePath, tempPath, monitor);
    181                                     }
    182 
    183                                     @Override
    184                                     public void close() {
    185                                         sync.close();
    186                                     }
    187                                 },
    188                                         String.format(Messages.DeviceView_Pulling_From_Device,
    189                                                 remoteFilePath),
    190                                         mParentShell);
    191 
    192                                 open(tempPath);
    193                             } else {
    194                                 // default action is ACTION_SAVE
    195                                 promptAndPull(sync,
    196                                         client.getClientData().getClientDescription() + DOT_HPROF,
    197                                         remoteFilePath, Messages.DeviceView_Save_HPROF_File);
    198 
    199                             }
    200                         } else {
    201                             displayErrorFromUiThread(
    202                                     Messages.DeviceView_Unable_Download_HPROF_From_Device_One_Param_First_Message,
    203                                     device.getSerialNumber());
    204                         }
    205                     } catch (SyncException e) {
    206                         if (e.wasCanceled() == false) {
    207                             displayErrorFromUiThread(
    208                                     Messages.DeviceView_Unable_Download_HPROF_From_Device_Two_Param,
    209                                     device.getSerialNumber(), e.getMessage());
    210                         }
    211                     } catch (Exception e) {
    212                         displayErrorFromUiThread(
    213                                 Messages.DeviceView_Unable_Download_HPROF_From_Device_One_Param_Second_Message,
    214                                 device.getSerialNumber());
    215 
    216                     } finally {
    217                         // this will make sure the dump hprof button is
    218                         // re-enabled for the
    219                         // current selection. as the client is finished dumping
    220                         // an hprof file
    221                         doSelectionChanged(mDeviceList.getSelectedClient());
    222                     }
    223                 }
    224             });
    225         }
    226 
    227         @Override
    228         public void onSuccess(final byte[] data, final Client client) {
    229             mParentShell.getDisplay().asyncExec(new Runnable() {
    230                 @Override
    231                 public void run() {
    232                     // get from the preference what action to take
    233                     IPreferenceStore store = DdmsPlugin.getDefault().getPreferenceStore();
    234                     String value = store.getString(PreferenceInitializer.ATTR_HPROF_ACTION);
    235 
    236                     if (ACTION_OPEN.equals(value)) {
    237                         try {
    238                             // no need to give an extension since we're going to
    239                             // convert the
    240                             // file anyway after.
    241                             File tempFile = saveTempFile(data, null /* extension */);
    242                             open(tempFile.getAbsolutePath());
    243                         } catch (Exception e) {
    244                             String errorMsg = e.getMessage();
    245                             displayErrorFromUiThread(
    246                                     Messages.DeviceView_Failed_To_Save_HPROF_Data,
    247                                     errorMsg != null ? ":\n" + errorMsg : "."); //$NON-NLS-1$ //$NON-NLS-2$
    248                         }
    249                     } else {
    250                         // default action is ACTION_SAVE
    251                         promptAndSave(client.getClientData().getClientDescription() + DOT_HPROF,
    252                                 data, Messages.DeviceView_Save_HPROF_File);
    253                     }
    254                 }
    255             });
    256         }
    257 
    258         private void open(String path) throws IOException, InterruptedException, PartInitException {
    259             // make a temp file to convert the hprof into something
    260             // readable by normal tools
    261             File temp = File.createTempFile("android", DOT_HPROF); //$NON-NLS-1$
    262             String tempPath = temp.getAbsolutePath();
    263 
    264             String[] command = new String[3];
    265             command[0] = DdmsPlugin.getHprofConverter();
    266             command[1] = path;
    267             command[2] = tempPath;
    268 
    269             Process p = Runtime.getRuntime().exec(command);
    270             p.waitFor();
    271 
    272             IFileStore fileStore = EFS.getLocalFileSystem().getStore(new Path(tempPath));
    273             if (!fileStore.fetchInfo().isDirectory() && fileStore.fetchInfo().exists()) {
    274                 // before we open the file in an editor window, we make sure the
    275                 // current
    276                 // workbench page has an editor area (typically the ddms
    277                 // perspective doesn't).
    278                 IWorkbench workbench = PlatformUI.getWorkbench();
    279                 IWorkbenchWindow window = workbench.getActiveWorkbenchWindow();
    280                 IWorkbenchPage page = window.getActivePage();
    281                 if (page == null) {
    282                     return;
    283                 }
    284 
    285                 if (page.isEditorAreaVisible() == false) {
    286                     IAdaptable input;
    287                     input = page.getInput();
    288                     try {
    289                         workbench.showPerspective("org.eclipse.debug.ui.DebugPerspective", //$NON-NLS-1$
    290                                 window, input);
    291                     } catch (WorkbenchException e) {
    292                     }
    293                 }
    294 
    295                 IDE.openEditorOnFileStore(page, fileStore);
    296             }
    297         }
    298     }
    299 
    300     public DeviceView() {
    301         // the view is declared with allowMultiple="false" so we
    302         // can safely do this.
    303         sThis = this;
    304     }
    305 
    306     public static DeviceView getInstance() {
    307         return sThis;
    308     }
    309 
    310     @Override
    311     public void createPartControl(Composite parent) {
    312         mParentShell = parent.getShell();
    313 
    314         ImageLoader loader = ImageLoader.getDdmUiLibLoader();
    315 
    316         mDeviceList = new DevicePanel(USE_SELECTED_DEBUG_PORT);
    317         mDeviceList.createPanel(parent);
    318         mDeviceList.addSelectionListener(this);
    319 
    320         DdmsPlugin plugin = DdmsPlugin.getDefault();
    321         mDeviceList.addSelectionListener(plugin);
    322         plugin.setListeningState(true);
    323 
    324         mCaptureAction = new Action(Messages.DeviceView_Screen_Capture) {
    325             @Override
    326             public void run() {
    327                 ScreenShotDialog dlg = new ScreenShotDialog(
    328                         DdmsPlugin.getDisplay().getActiveShell());
    329                 dlg.open(mDeviceList.getSelectedDevice());
    330             }
    331         };
    332         mCaptureAction.setToolTipText(Messages.DeviceView_Screen_Capture_Tooltip);
    333         mCaptureAction.setImageDescriptor(loader.loadDescriptor("capture.png")); //$NON-NLS-1$
    334 
    335         mViewUiAutomatorHierarchyAction = new Action("Dump View Hierarchy for UI Automator") {
    336             @Override
    337             public void run() {
    338                 takeUiAutomatorSnapshot(mDeviceList.getSelectedDevice(),
    339                         DdmsPlugin.getDisplay().getActiveShell());
    340             }
    341         };
    342         mViewUiAutomatorHierarchyAction.setToolTipText("Dump View Hierarchy for UI Automator");
    343         mViewUiAutomatorHierarchyAction.setImageDescriptor(
    344                 DdmsPlugin.getImageDescriptor("icons/uiautomator.png")); //$NON-NLS-1$
    345 
    346         mSystraceAction = new Action("Capture System Wide Trace") {
    347             @Override
    348             public void run() {
    349                 launchSystrace(mDeviceList.getSelectedDevice(),
    350                         DdmsPlugin.getDisplay().getActiveShell());
    351             }
    352         };
    353         mSystraceAction.setToolTipText("Capture system wide trace using Android systrace");
    354         mSystraceAction.setImageDescriptor(
    355                 DdmsPlugin.getImageDescriptor("icons/systrace.png")); //$NON-NLS-1$
    356         mSystraceAction.setEnabled(true);
    357 
    358         mResetAdbAction = new Action(Messages.DeviceView_Reset_ADB) {
    359             @Override
    360             public void run() {
    361                 AndroidDebugBridge bridge = AndroidDebugBridge.getBridge();
    362                 if (bridge != null) {
    363                     if (bridge.restart() == false) {
    364                         // get the current Display
    365                         final Display display = DdmsPlugin.getDisplay();
    366 
    367                         // dialog box only run in ui thread..
    368                         display.asyncExec(new Runnable() {
    369                             @Override
    370                             public void run() {
    371                                 Shell shell = display.getActiveShell();
    372                                 MessageDialog.openError(shell, Messages.DeviceView_ADB_Error,
    373                                         Messages.DeviceView_ADB_Failed_Restart);
    374                             }
    375                         });
    376                     }
    377                 }
    378             }
    379         };
    380         mResetAdbAction.setToolTipText(Messages.DeviceView_Reset_ADB_Host_Deamon);
    381         mResetAdbAction.setImageDescriptor(PlatformUI.getWorkbench()
    382                 .getSharedImages().getImageDescriptor(
    383                         ISharedImages.IMG_OBJS_WARN_TSK));
    384 
    385         mKillAppAction = new Action() {
    386             @Override
    387             public void run() {
    388                 mDeviceList.killSelectedClient();
    389             }
    390         };
    391 
    392         mKillAppAction.setText(Messages.DeviceView_Stop_Process);
    393         mKillAppAction.setToolTipText(Messages.DeviceView_Stop_Process_Tooltip);
    394         mKillAppAction.setImageDescriptor(loader.loadDescriptor(DevicePanel.ICON_HALT));
    395 
    396         mGcAction = new Action() {
    397             @Override
    398             public void run() {
    399                 mDeviceList.forceGcOnSelectedClient();
    400             }
    401         };
    402 
    403         mGcAction.setText(Messages.DeviceView_Cause_GC);
    404         mGcAction.setToolTipText(Messages.DeviceView_Cause_GC_Tooltip);
    405         mGcAction.setImageDescriptor(loader.loadDescriptor(DevicePanel.ICON_GC));
    406 
    407         mHprofAction = new Action() {
    408             @Override
    409             public void run() {
    410                 mDeviceList.dumpHprof();
    411                 doSelectionChanged(mDeviceList.getSelectedClient());
    412             }
    413         };
    414         mHprofAction.setText(Messages.DeviceView_Dump_HPROF_File);
    415         mHprofAction.setToolTipText(Messages.DeviceView_Dump_HPROF_File_Tooltip);
    416         mHprofAction.setImageDescriptor(loader.loadDescriptor(DevicePanel.ICON_HPROF));
    417 
    418         mUpdateHeapAction = new Action(Messages.DeviceView_Update_Heap, IAction.AS_CHECK_BOX) {
    419             @Override
    420             public void run() {
    421                 boolean enable = mUpdateHeapAction.isChecked();
    422                 mDeviceList.setEnabledHeapOnSelectedClient(enable);
    423             }
    424         };
    425         mUpdateHeapAction.setToolTipText(Messages.DeviceView_Update_Heap_Tooltip);
    426         mUpdateHeapAction.setImageDescriptor(loader.loadDescriptor(DevicePanel.ICON_HEAP));
    427 
    428         mUpdateThreadAction = new Action(Messages.DeviceView_Threads, IAction.AS_CHECK_BOX) {
    429             @Override
    430             public void run() {
    431                 boolean enable = mUpdateThreadAction.isChecked();
    432                 mDeviceList.setEnabledThreadOnSelectedClient(enable);
    433             }
    434         };
    435         mUpdateThreadAction.setToolTipText(Messages.DeviceView_Threads_Tooltip);
    436         mUpdateThreadAction.setImageDescriptor(loader.loadDescriptor(DevicePanel.ICON_THREAD));
    437 
    438         mTracingAction = new Action() {
    439             @Override
    440             public void run() {
    441                 mDeviceList.toggleMethodProfiling();
    442             }
    443         };
    444         mTracingAction.setText(Messages.DeviceView_Start_Method_Profiling);
    445         mTracingAction.setToolTipText(Messages.DeviceView_Start_Method_Profiling_Tooltip);
    446         mTracingStartImage = loader.loadDescriptor(DevicePanel.ICON_TRACING_START);
    447         mTracingStopImage = loader.loadDescriptor(DevicePanel.ICON_TRACING_STOP);
    448         mTracingAction.setImageDescriptor(mTracingStartImage);
    449 
    450         mDebugAction = new Action(Messages.DeviceView_Debug_Process) {
    451             @Override
    452             public void run() {
    453                 if (DdmsPlugin.getDefault().hasDebuggerConnectors()) {
    454                     Client currentClient = mDeviceList.getSelectedClient();
    455                     if (currentClient != null) {
    456                         ClientData clientData = currentClient.getClientData();
    457 
    458                         // make sure the client can be debugged
    459                         switch (clientData.getDebuggerConnectionStatus()) {
    460                             case ERROR: {
    461                                 Display display = DdmsPlugin.getDisplay();
    462                                 Shell shell = display.getActiveShell();
    463                                 MessageDialog.openError(shell,
    464                                         Messages.DeviceView_Debug_Process_Title,
    465                                         Messages.DeviceView_Process_Debug_Already_In_Use);
    466                                 return;
    467                             }
    468                             case ATTACHED: {
    469                                 Display display = DdmsPlugin.getDisplay();
    470                                 Shell shell = display.getActiveShell();
    471                                 MessageDialog.openError(shell,
    472                                         Messages.DeviceView_Debug_Process_Title,
    473                                         Messages.DeviceView_Process_Already_Being_Debugged);
    474                                 return;
    475                             }
    476                         }
    477 
    478                         // get the name of the client
    479                         String packageName = clientData.getClientDescription();
    480                         if (packageName != null) {
    481 
    482                             // try all connectors till one returns true.
    483                             IDebuggerConnector[] connectors =
    484                                     DdmsPlugin.getDefault().getDebuggerConnectors();
    485 
    486                             if (connectors != null) {
    487                                 for (IDebuggerConnector connector : connectors) {
    488                                     try {
    489                                         if (connector.connectDebugger(packageName,
    490                                                 currentClient.getDebuggerListenPort(),
    491                                                 DdmPreferences.getSelectedDebugPort())) {
    492                                             return;
    493                                         }
    494                                     } catch (Throwable t) {
    495                                         // ignore, we'll just not use this
    496                                         // implementation
    497                                     }
    498                                 }
    499                             }
    500 
    501                             // if we get to this point, then we failed to find a
    502                             // project
    503                             // that matched the application to debug
    504                             Display display = DdmsPlugin.getDisplay();
    505                             Shell shell = display.getActiveShell();
    506                             MessageDialog.openError(shell, Messages.DeviceView_Debug_Process_Title,
    507                                     String.format(
    508                                             Messages.DeviceView_Debug_Session_Failed,
    509                                             packageName));
    510                         }
    511                     }
    512                 }
    513             }
    514         };
    515         mDebugAction.setToolTipText(Messages.DeviceView_Debug_Process_Tooltip);
    516         mDebugAction.setImageDescriptor(loader.loadDescriptor("debug-attach.png")); //$NON-NLS-1$
    517         mDebugAction.setEnabled(DdmsPlugin.getDefault().hasDebuggerConnectors());
    518 
    519         placeActions();
    520 
    521         // disabling all action buttons
    522         selectionChanged(null, null);
    523 
    524         ClientData.setHprofDumpHandler(new HProfHandler(mParentShell));
    525         AndroidDebugBridge.addClientChangeListener(this);
    526         ClientData.setMethodProfilingHandler(new MethodProfilingHandler(mParentShell) {
    527             @Override
    528             protected void open(String tempPath) {
    529                 if (DdmsPlugin.getDefault().launchTraceview(tempPath) == false) {
    530                     super.open(tempPath);
    531                 }
    532             }
    533         });
    534     }
    535 
    536     private void takeUiAutomatorSnapshot(final IDevice device, final Shell shell) {
    537         ProgressMonitorDialog dialog = new ProgressMonitorDialog(shell);
    538         try {
    539             dialog.run(true, false, new IRunnableWithProgress() {
    540                 @Override
    541                 public void run(IProgressMonitor monitor) throws InvocationTargetException,
    542                                                                         InterruptedException {
    543                     UiAutomatorResult result = null;
    544                     try {
    545                         result = UiAutomatorHelper.takeSnapshot(device, monitor);
    546                     } catch (UiAutomatorException e) {
    547                         throw new InvocationTargetException(e);
    548                     } finally {
    549                         monitor.done();
    550                     }
    551 
    552                     UiAutomatorViewer.openEditor(result);
    553                 }
    554             });
    555         } catch (Exception e) {
    556             Throwable t = e;
    557             if (e instanceof InvocationTargetException) {
    558                 t = ((InvocationTargetException) e).getTargetException();
    559             }
    560             Status s = new Status(IStatus.ERROR, DdmsPlugin.PLUGIN_ID,
    561                                             "Error obtaining UI hierarchy", t);
    562             ErrorDialog.openError(shell, "UI Automator",
    563                     "Unexpected error while obtaining UI hierarchy", s);
    564         }
    565     };
    566 
    567     private void launchSystrace(final IDevice device, final Shell parentShell) {
    568         SystraceVersionDetector detector = new SystraceVersionDetector(device);
    569         try {
    570             new ProgressMonitorDialog(parentShell).run(true, false, detector);
    571         } catch (InvocationTargetException e) {
    572             MessageDialog.openError(parentShell,
    573                     "Systrace",
    574                     "Unexpected error while detecting atrace version: " + e);
    575             return;
    576         } catch (InterruptedException e) {
    577             return;
    578         }
    579 
    580         final ISystraceOptionsDialog dlg =
    581                 (detector.getVersion() == SystraceVersionDetector.SYSTRACE_V1) ?
    582                         new SystraceOptionsDialogV1(parentShell) :
    583                             new SystraceOptionsDialogV2(parentShell, detector.getTags());
    584 
    585         if (dlg.open() != SystraceOptionsDialogV1.OK) {
    586             return;
    587         }
    588 
    589         final ISystraceOptions options = dlg.getSystraceOptions();
    590 
    591         // set trace tag if necessary:
    592         //      adb shell setprop debug.atrace.tags.enableflags <tag>
    593         String tag = options.getTags();
    594         if (tag != null) {
    595             CountDownLatch setTagLatch = new CountDownLatch(1);
    596             CollectingOutputReceiver receiver = new CollectingOutputReceiver(setTagLatch);
    597             try {
    598                 String cmd = "setprop debug.atrace.tags.enableflags " + tag;
    599                 device.executeShellCommand(cmd, receiver);
    600                 setTagLatch.await(5, TimeUnit.SECONDS);
    601             } catch (Exception e) {
    602                 MessageDialog.openError(parentShell,
    603                         "Systrace",
    604                         "Unexpected error while setting trace tags: " + e);
    605                 return;
    606             }
    607 
    608             String shellOutput = receiver.getOutput();
    609             if (shellOutput.contains("Error type")) {                   //$NON-NLS-1$
    610                 throw new RuntimeException(receiver.getOutput());
    611             }
    612         }
    613 
    614         // obtain the output of "adb shell atrace <trace-options>" and generate the html file
    615         ProgressMonitorDialog d = new ProgressMonitorDialog(parentShell);
    616         try {
    617             d.run(true, true, new IRunnableWithProgress() {
    618                 @Override
    619                 public void run(IProgressMonitor monitor) throws InvocationTargetException,
    620                         InterruptedException {
    621                     boolean COMPRESS_DATA = true;
    622 
    623                     monitor.setTaskName("Collecting Trace Information");
    624                     final String atraceOptions = options.getOptions()
    625                                                 + (COMPRESS_DATA ? " -z" : "");
    626                     SystraceTask task = new SystraceTask(device, atraceOptions);
    627                     Thread t = new Thread(task, "Systrace Output Receiver");
    628                     t.start();
    629 
    630                     // check if the user has cancelled tracing every so often
    631                     while (true) {
    632                         t.join(1000);
    633 
    634                         if (t.isAlive()) {
    635                             if (monitor.isCanceled()) {
    636                                 task.cancel();
    637                                 return;
    638                             }
    639                         } else {
    640                             break;
    641                         }
    642                     }
    643 
    644                     if (task.getError() != null) {
    645                         throw new RuntimeException(task.getError());
    646                     }
    647 
    648                     monitor.setTaskName("Saving trace information");
    649                     File systraceAssets = new File(DdmsPlugin.getToolsFolder(), "systrace"); //$NON-NLS-1$
    650                     SystraceOutputParser parser = new SystraceOutputParser(
    651                             COMPRESS_DATA,
    652                             SystraceOutputParser.getJs(systraceAssets),
    653                             SystraceOutputParser.getCss(systraceAssets));
    654 
    655                     parser.parse(task.getAtraceOutput());
    656 
    657                     String html = parser.getSystraceHtml();
    658                     try {
    659                         Files.write(html.getBytes(), new File(dlg.getTraceFilePath()));
    660                     } catch (IOException e) {
    661                         throw new InvocationTargetException(e);
    662                     }
    663                 }
    664             });
    665         } catch (InvocationTargetException e) {
    666             ErrorDialog.openError(parentShell, "Systrace",
    667                     "Unable to collect system trace.",
    668                     new Status(Status.ERROR,
    669                             DdmsPlugin.PLUGIN_ID,
    670                             "Unexpected error while collecting system trace.",
    671                             e.getCause()));
    672         } catch (InterruptedException ignore) {
    673         }
    674     }
    675 
    676     @Override
    677     public void setFocus() {
    678         mDeviceList.setFocus();
    679     }
    680 
    681     /**
    682      * Sent when a new {@link IDevice} and {@link Client} are selected.
    683      *
    684      * @param selectedDevice the selected device. If null, no devices are
    685      *            selected.
    686      * @param selectedClient The selected client. If null, no clients are
    687      *            selected.
    688      */
    689     @Override
    690     public void selectionChanged(IDevice selectedDevice, Client selectedClient) {
    691         // update the buttons
    692         doSelectionChanged(selectedClient);
    693         doSelectionChanged(selectedDevice);
    694     }
    695 
    696     private void doSelectionChanged(Client selectedClient) {
    697         // update the buttons
    698         if (selectedClient != null) {
    699             if (USE_SELECTED_DEBUG_PORT) {
    700                 // set the client as the debug client
    701                 selectedClient.setAsSelectedClient();
    702             }
    703 
    704             mDebugAction.setEnabled(DdmsPlugin.getDefault().hasDebuggerConnectors());
    705             mKillAppAction.setEnabled(true);
    706             mGcAction.setEnabled(true);
    707 
    708             mUpdateHeapAction.setEnabled(true);
    709             mUpdateHeapAction.setChecked(selectedClient.isHeapUpdateEnabled());
    710 
    711             mUpdateThreadAction.setEnabled(true);
    712             mUpdateThreadAction.setChecked(selectedClient.isThreadUpdateEnabled());
    713 
    714             ClientData data = selectedClient.getClientData();
    715 
    716             if (data.hasFeature(ClientData.FEATURE_HPROF)) {
    717                 mHprofAction.setEnabled(data.hasPendingHprofDump() == false);
    718                 mHprofAction.setToolTipText(Messages.DeviceView_Dump_HPROF_File);
    719             } else {
    720                 mHprofAction.setEnabled(false);
    721                 mHprofAction
    722                         .setToolTipText(Messages.DeviceView_Dump_HPROF_File_Not_Supported_By_VM);
    723             }
    724 
    725             if (data.hasFeature(ClientData.FEATURE_PROFILING)) {
    726                 mTracingAction.setEnabled(true);
    727                 if (data.getMethodProfilingStatus() == MethodProfilingStatus.ON) {
    728                     mTracingAction
    729                             .setToolTipText(Messages.DeviceView_Stop_Method_Profiling_Tooltip);
    730                     mTracingAction.setText(Messages.DeviceView_Stop_Method_Profiling);
    731                     mTracingAction.setImageDescriptor(mTracingStopImage);
    732                 } else {
    733                     mTracingAction
    734                             .setToolTipText(Messages.DeviceView_Start_Method_Profiling_Tooltip);
    735                     mTracingAction.setImageDescriptor(mTracingStartImage);
    736                     mTracingAction.setText(Messages.DeviceView_Start_Method_Profiling);
    737                 }
    738             } else {
    739                 mTracingAction.setEnabled(false);
    740                 mTracingAction.setImageDescriptor(mTracingStartImage);
    741                 mTracingAction
    742                         .setToolTipText(Messages.DeviceView_Start_Method_Profiling_Not_Suported_By_Vm);
    743                 mTracingAction.setText(Messages.DeviceView_Start_Method_Profiling);
    744             }
    745         } else {
    746             if (USE_SELECTED_DEBUG_PORT) {
    747                 // set the client as the debug client
    748                 AndroidDebugBridge bridge = AndroidDebugBridge.getBridge();
    749                 if (bridge != null) {
    750                     bridge.setSelectedClient(null);
    751                 }
    752             }
    753 
    754             mDebugAction.setEnabled(false);
    755             mKillAppAction.setEnabled(false);
    756             mGcAction.setEnabled(false);
    757             mUpdateHeapAction.setChecked(false);
    758             mUpdateHeapAction.setEnabled(false);
    759             mUpdateThreadAction.setEnabled(false);
    760             mUpdateThreadAction.setChecked(false);
    761             mHprofAction.setEnabled(false);
    762 
    763             mHprofAction.setEnabled(false);
    764             mHprofAction.setToolTipText(Messages.DeviceView_Dump_HPROF_File);
    765 
    766             mTracingAction.setEnabled(false);
    767             mTracingAction.setImageDescriptor(mTracingStartImage);
    768             mTracingAction.setToolTipText(Messages.DeviceView_Start_Method_Profiling_Tooltip);
    769             mTracingAction.setText(Messages.DeviceView_Start_Method_Profiling);
    770         }
    771 
    772         for (IClientAction a : DdmsPlugin.getDefault().getClientSpecificActions()) {
    773             a.selectedClientChanged(selectedClient);
    774         }
    775     }
    776 
    777     private void doSelectionChanged(IDevice selectedDevice) {
    778         boolean validDevice = selectedDevice != null;
    779 
    780         mCaptureAction.setEnabled(validDevice);
    781         mViewUiAutomatorHierarchyAction.setEnabled(validDevice);
    782         mSystraceAction.setEnabled(validDevice);
    783     }
    784 
    785     /**
    786      * Place the actions in the ui.
    787      */
    788     private final void placeActions() {
    789         IActionBars actionBars = getViewSite().getActionBars();
    790 
    791         // first in the menu
    792         IMenuManager menuManager = actionBars.getMenuManager();
    793         menuManager.removeAll();
    794         menuManager.add(mDebugAction);
    795         menuManager.add(new Separator());
    796         menuManager.add(mUpdateHeapAction);
    797         menuManager.add(mHprofAction);
    798         menuManager.add(mGcAction);
    799         menuManager.add(new Separator());
    800         menuManager.add(mUpdateThreadAction);
    801         menuManager.add(mTracingAction);
    802         menuManager.add(new Separator());
    803         menuManager.add(mKillAppAction);
    804         menuManager.add(new Separator());
    805         menuManager.add(mCaptureAction);
    806         menuManager.add(new Separator());
    807         menuManager.add(mViewUiAutomatorHierarchyAction);
    808         menuManager.add(new Separator());
    809         menuManager.add(mSystraceAction);
    810         menuManager.add(new Separator());
    811         menuManager.add(mResetAdbAction);
    812         for (IClientAction a : DdmsPlugin.getDefault().getClientSpecificActions()) {
    813             menuManager.add(a.getAction());
    814         }
    815 
    816         // and then in the toolbar
    817         IToolBarManager toolBarManager = actionBars.getToolBarManager();
    818         toolBarManager.removeAll();
    819         toolBarManager.add(mDebugAction);
    820         toolBarManager.add(new Separator());
    821         toolBarManager.add(mUpdateHeapAction);
    822         toolBarManager.add(mHprofAction);
    823         toolBarManager.add(mGcAction);
    824         toolBarManager.add(new Separator());
    825         toolBarManager.add(mUpdateThreadAction);
    826         toolBarManager.add(mTracingAction);
    827         toolBarManager.add(new Separator());
    828         toolBarManager.add(mKillAppAction);
    829         toolBarManager.add(new Separator());
    830         toolBarManager.add(mCaptureAction);
    831         toolBarManager.add(new Separator());
    832         toolBarManager.add(mViewUiAutomatorHierarchyAction);
    833         toolBarManager.add(new Separator());
    834         toolBarManager.add(mSystraceAction);
    835         for (IClientAction a : DdmsPlugin.getDefault().getClientSpecificActions()) {
    836             toolBarManager.add(a.getAction());
    837         }
    838     }
    839 
    840     @Override
    841     public void clientChanged(final Client client, int changeMask) {
    842         if ((changeMask & Client.CHANGE_METHOD_PROFILING_STATUS) == Client.CHANGE_METHOD_PROFILING_STATUS) {
    843             if (mDeviceList.getSelectedClient() == client) {
    844                 mParentShell.getDisplay().asyncExec(new Runnable() {
    845                     @Override
    846                     public void run() {
    847                         // force refresh of the button enabled state.
    848                         doSelectionChanged(client);
    849                     }
    850                 });
    851             }
    852         }
    853     }
    854 }
    855