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