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