• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
18 package com.android.ide.eclipse.ddms.views;
19 
20 import com.android.ddmlib.AndroidDebugBridge;
21 import com.android.ddmlib.Client;
22 import com.android.ddmlib.ClientData;
23 import com.android.ddmlib.IDevice;
24 import com.android.ddmlib.SyncService;
25 import com.android.ddmlib.AndroidDebugBridge.IClientChangeListener;
26 import com.android.ddmlib.ClientData.IHprofDumpHandler;
27 import com.android.ddmlib.ClientData.MethodProfilingStatus;
28 import com.android.ddmlib.SyncService.SyncResult;
29 import com.android.ddmuilib.handler.BaseFileHandler;
30 import com.android.ddmuilib.handler.MethodProfilingHandler;
31 import com.android.ddmuilib.DevicePanel;
32 import com.android.ddmuilib.ScreenShotDialog;
33 import com.android.ddmuilib.DevicePanel.IUiSelectionListener;
34 import com.android.ide.eclipse.ddms.DdmsPlugin;
35 import com.android.ide.eclipse.ddms.DdmsPlugin.IDebugLauncher;
36 import com.android.ide.eclipse.ddms.preferences.PreferenceInitializer;
37 
38 import org.eclipse.core.filesystem.EFS;
39 import org.eclipse.core.filesystem.IFileStore;
40 import org.eclipse.core.runtime.Path;
41 import org.eclipse.jface.action.Action;
42 import org.eclipse.jface.action.IAction;
43 import org.eclipse.jface.action.IMenuManager;
44 import org.eclipse.jface.action.IToolBarManager;
45 import org.eclipse.jface.action.Separator;
46 import org.eclipse.jface.dialogs.MessageDialog;
47 import org.eclipse.jface.preference.IPreferenceStore;
48 import org.eclipse.jface.resource.ImageDescriptor;
49 import org.eclipse.swt.widgets.Composite;
50 import org.eclipse.swt.widgets.Display;
51 import org.eclipse.swt.widgets.Shell;
52 import org.eclipse.ui.IActionBars;
53 import org.eclipse.ui.ISharedImages;
54 import org.eclipse.ui.PartInitException;
55 import org.eclipse.ui.PlatformUI;
56 import org.eclipse.ui.ide.IDE;
57 import org.eclipse.ui.part.ViewPart;
58 
59 import java.io.File;
60 import java.io.IOException;
61 
62 public class DeviceView extends ViewPart implements IUiSelectionListener, IClientChangeListener {
63 
64     private final static boolean USE_SELECTED_DEBUG_PORT = true;
65 
66     public static final String ID =
67         "com.android.ide.eclipse.ddms.views.DeviceView"; //$NON-NLS-1$
68 
69     private static DeviceView sThis;
70 
71     private Shell mParentShell;
72     private DevicePanel mDeviceList;
73 
74     private Action mResetAdbAction;
75     private Action mCaptureAction;
76     private Action mUpdateThreadAction;
77     private Action mUpdateHeapAction;
78     private Action mGcAction;
79     private Action mKillAppAction;
80     private Action mDebugAction;
81     private Action mHprofAction;
82     private Action mTracingAction;
83     private IDebugLauncher mDebugLauncher;
84 
85     private ImageDescriptor mTracingStartImage;
86     private ImageDescriptor mTracingStopImage;
87 
88     public class HProfHandler extends BaseFileHandler implements IHprofDumpHandler {
89         public final static String ACTION_SAVE ="hprof.save"; //$NON-NLS-1$
90         public final static String ACTION_OPEN = "hprof.open"; //$NON-NLS-1$
91 
92         public final static String DOT_HPROF = ".hprof"; //$NON-NLS-1$
93 
HProfHandler(Shell parentShell)94         HProfHandler(Shell parentShell) {
95             super(parentShell);
96         }
97 
onFailure(final Client client)98         public void onFailure(final Client client) {
99             mParentShell.getDisplay().asyncExec(new Runnable() {
100                 public void run() {
101                     try {
102                         displayError("Unable to create HPROF file for application '%1$s'.\n" +
103                                 "Check logcat for more information.",
104                                 client.getClientData().getClientDescription());
105                     } finally {
106                         // this will make sure the dump hprof button is re-enabled for the
107                         // current selection. as the client is finished dumping an hprof file
108                         doSelectionChanged(mDeviceList.getSelectedClient());
109                     }
110                 }
111             });
112         }
113 
onSuccess(final String remoteFilePath, final Client client)114         public void onSuccess(final String remoteFilePath, final Client client) {
115             mParentShell.getDisplay().asyncExec(new Runnable() {
116                 public void run() {
117                     final IDevice device = client.getDevice();
118                     try {
119                         // get the sync service to pull the HPROF file
120                         final SyncService sync = client.getDevice().getSyncService();
121                         if (sync != null) {
122                             // get from the preference what action to take
123                             IPreferenceStore store = DdmsPlugin.getDefault().getPreferenceStore();
124                             String value = store.getString(PreferenceInitializer.ATTR_HPROF_ACTION);
125 
126                             SyncResult result = null;
127                             if (ACTION_OPEN.equals(value)) {
128                                 File temp = File.createTempFile("android", DOT_HPROF); //$NON-NLS-1$
129                                 String tempPath = temp.getAbsolutePath();
130                                 result = pull(sync, tempPath, remoteFilePath);
131                                 if (result != null && result.getCode() == SyncService.RESULT_OK) {
132                                     open(tempPath);
133                                 }
134                             } else {
135                                 // default action is ACTION_SAVE
136                                 result = promptAndPull(sync,
137                                         client.getClientData().getClientDescription() + DOT_HPROF,
138                                         remoteFilePath, "Save HPROF file");
139 
140                             }
141 
142                             if (result != null && result.getCode() != SyncService.RESULT_OK) {
143                                 displayError(
144                                         "Unable to download HPROF file from device '%1$s'.\n\n%2$s",
145                                         device.getSerialNumber(), result.getMessage());
146                             }
147                         } else {
148                             displayError("Unable to download HPROF file from device '%1$s'.",
149                                     device.getSerialNumber());
150                         }
151                     } catch (Exception e) {
152                         displayError("Unable to download HPROF file from device '%1$s'.",
153                                 device.getSerialNumber());
154 
155                     } finally {
156                         // this will make sure the dump hprof button is re-enabled for the
157                         // current selection. as the client is finished dumping an hprof file
158                         doSelectionChanged(mDeviceList.getSelectedClient());
159                     }
160                 }
161             });
162         }
163 
open(String path)164         private void open(String path) throws IOException, InterruptedException, PartInitException {
165             // make a temp file to convert the hprof into something
166             // readable by normal tools
167             File temp = File.createTempFile("android", DOT_HPROF);
168             String tempPath = temp.getAbsolutePath();
169 
170             String[] command = new String[3];
171             command[0] = DdmsPlugin.getHprofConverter();
172             command[1] = path;
173             command[2] = tempPath;
174 
175             Process p = Runtime.getRuntime().exec(command);
176             p.waitFor();
177 
178             IFileStore fileStore =  EFS.getLocalFileSystem().getStore(new Path(tempPath));
179             if (!fileStore.fetchInfo().isDirectory() && fileStore.fetchInfo().exists()) {
180                 IDE.openEditorOnFileStore(
181                         getSite().getWorkbenchWindow().getActivePage(),
182                         fileStore);
183             }
184         }
185 
displayError(String format, Object... args)186         private void displayError(String format, Object... args) {
187             MessageDialog.openError(mParentShell, "HPROF Error",
188                     String.format(format, args));
189         }
190     }
191 
192 
DeviceView()193     public DeviceView() {
194         // the view is declared with allowMultiple="false" so we
195         // can safely do this.
196         sThis = this;
197     }
198 
getInstance()199     public static DeviceView getInstance() {
200         return sThis;
201     }
202 
203     /**
204      * Sets the {@link IDebugLauncher}.
205      * @param debugLauncher
206      */
setDebugLauncher(DdmsPlugin.IDebugLauncher debugLauncher)207     public void setDebugLauncher(DdmsPlugin.IDebugLauncher debugLauncher) {
208         mDebugLauncher = debugLauncher;
209         if (mDebugAction != null && mDeviceList != null) {
210             Client currentClient = mDeviceList.getSelectedClient();
211             if (currentClient != null) {
212                 mDebugAction.setEnabled(true);
213             }
214         }
215     }
216 
217     @Override
createPartControl(Composite parent)218     public void createPartControl(Composite parent) {
219         mParentShell = parent.getShell();
220         ClientData.setHprofDumpHandler(new HProfHandler(mParentShell));
221         AndroidDebugBridge.addClientChangeListener(this);
222         ClientData.setMethodProfilingHandler(new MethodProfilingHandler(mParentShell));
223 
224         mDeviceList = new DevicePanel(DdmsPlugin.getImageLoader(), USE_SELECTED_DEBUG_PORT);
225         mDeviceList.createPanel(parent);
226         mDeviceList.addSelectionListener(this);
227 
228         DdmsPlugin plugin = DdmsPlugin.getDefault();
229         mDeviceList.addSelectionListener(plugin);
230         plugin.setListeningState(true);
231 
232         mCaptureAction = new Action("Screen Capture") {
233             @Override
234             public void run() {
235                 ScreenShotDialog dlg = new ScreenShotDialog(
236                         DdmsPlugin.getDisplay().getActiveShell());
237                 dlg.open(mDeviceList.getSelectedDevice());
238             }
239         };
240         mCaptureAction.setToolTipText("Screen Capture");
241         mCaptureAction.setImageDescriptor(
242                 DdmsPlugin.getImageLoader().loadDescriptor("capture.png")); //$NON-NLS-1$
243 
244         mResetAdbAction = new Action("Reset adb") {
245             @Override
246             public void run() {
247                 AndroidDebugBridge bridge = AndroidDebugBridge.getBridge();
248                 if (bridge != null) {
249                     if (bridge.restart() == false) {
250                         // get the current Display
251                         final Display display = DdmsPlugin.getDisplay();
252 
253                         // dialog box only run in ui thread..
254                         display.asyncExec(new Runnable() {
255                             public void run() {
256                                 Shell shell = display.getActiveShell();
257                                 MessageDialog.openError(shell, "Adb Error",
258                                         "Adb failed to restart!\n\nMake sure the plugin is properly configured.");
259                             }
260                         });
261                     }
262                 }
263             }
264         };
265         mResetAdbAction.setToolTipText("Reset the adb host daemon");
266         mResetAdbAction.setImageDescriptor(PlatformUI.getWorkbench()
267                 .getSharedImages().getImageDescriptor(
268                         ISharedImages.IMG_OBJS_WARN_TSK));
269 
270         mKillAppAction = new Action() {
271             @Override
272             public void run() {
273                 mDeviceList.killSelectedClient();
274             }
275         };
276 
277         mKillAppAction.setText("Stop Process");
278         mKillAppAction.setToolTipText("Stop Process");
279         mKillAppAction.setImageDescriptor(DdmsPlugin.getImageLoader()
280                 .loadDescriptor(DevicePanel.ICON_HALT));
281 
282         mGcAction = new Action() {
283             @Override
284             public void run() {
285                 mDeviceList.forceGcOnSelectedClient();
286             }
287         };
288 
289         mGcAction.setText("Cause GC");
290         mGcAction.setToolTipText("Cause GC");
291         mGcAction.setImageDescriptor(DdmsPlugin.getImageLoader()
292                 .loadDescriptor(DevicePanel.ICON_GC));
293 
294         mHprofAction = new Action() {
295             @Override
296             public void run() {
297                 mDeviceList.dumpHprof();
298                 doSelectionChanged(mDeviceList.getSelectedClient());
299             }
300         };
301         mHprofAction.setText("Dump HPROF file");
302         mHprofAction.setToolTipText("Dump HPROF file");
303         mHprofAction.setImageDescriptor(DdmsPlugin.getImageLoader()
304                 .loadDescriptor(DevicePanel.ICON_HPROF));
305 
306         mUpdateHeapAction = new Action("Update Heap", IAction.AS_CHECK_BOX) {
307             @Override
308             public void run() {
309                 boolean enable = mUpdateHeapAction.isChecked();
310                 mDeviceList.setEnabledHeapOnSelectedClient(enable);
311             }
312         };
313         mUpdateHeapAction.setToolTipText("Update Heap");
314         mUpdateHeapAction.setImageDescriptor(DdmsPlugin.getImageLoader()
315                 .loadDescriptor(DevicePanel.ICON_HEAP));
316 
317         mUpdateThreadAction = new Action("Update Threads", IAction.AS_CHECK_BOX) {
318             @Override
319             public void run() {
320                 boolean enable = mUpdateThreadAction.isChecked();
321                 mDeviceList.setEnabledThreadOnSelectedClient(enable);
322             }
323         };
324         mUpdateThreadAction.setToolTipText("Update Threads");
325         mUpdateThreadAction.setImageDescriptor(DdmsPlugin.getImageLoader()
326                 .loadDescriptor(DevicePanel.ICON_THREAD));
327 
328         mTracingAction = new Action() {
329             @Override
330             public void run() {
331                 mDeviceList.toggleMethodProfiling();
332             }
333         };
334         mTracingAction.setText("Start Method Profiling");
335         mTracingAction.setToolTipText("Start Method Profiling");
336         mTracingStartImage = DdmsPlugin.getImageLoader().loadDescriptor(
337                 DevicePanel.ICON_TRACING_START);
338         mTracingStopImage = DdmsPlugin.getImageLoader().loadDescriptor(
339                 DevicePanel.ICON_TRACING_STOP);
340         mTracingAction.setImageDescriptor(mTracingStartImage);
341 
342         // check if there's already a debug launcher set up in the plugin class
343         mDebugLauncher = DdmsPlugin.getRunningAppDebugLauncher();
344 
345         mDebugAction = new Action("Debug Process") {
346             @Override
347             public void run() {
348                 if (mDebugLauncher != null) {
349                     Client currentClient = mDeviceList.getSelectedClient();
350                     if (currentClient != null) {
351                         ClientData clientData = currentClient.getClientData();
352 
353                         // make sure the client can be debugged
354                         switch (clientData.getDebuggerConnectionStatus()) {
355                             case ERROR: {
356                                 Display display = DdmsPlugin.getDisplay();
357                                 Shell shell = display.getActiveShell();
358                                 MessageDialog.openError(shell, "Process Debug",
359                                         "The process debug port is already in use!");
360                                 return;
361                             }
362                             case ATTACHED: {
363                                 Display display = DdmsPlugin.getDisplay();
364                                 Shell shell = display.getActiveShell();
365                                 MessageDialog.openError(shell, "Process Debug",
366                                         "The process is already being debugged!");
367                                 return;
368                             }
369                         }
370 
371                         // get the name of the client
372                         String packageName = clientData.getClientDescription();
373                         if (packageName != null) {
374                             if (mDebugLauncher.debug(packageName,
375                                     currentClient.getDebuggerListenPort()) == false) {
376 
377                                 // if we get to this point, then we failed to find a project
378                                 // that matched the application to debug
379                                 Display display = DdmsPlugin.getDisplay();
380                                 Shell shell = display.getActiveShell();
381                                 MessageDialog.openError(shell, "Process Debug",
382                                         String.format(
383                                                 "No opened project found for %1$s. Debug session failed!",
384                                                 packageName));
385                             }
386                         }
387                     }
388                 }
389             }
390         };
391         mDebugAction.setToolTipText("Debug the selected process, provided its source project is present and opened in the workspace.");
392         mDebugAction.setImageDescriptor(DdmsPlugin.getImageLoader()
393                 .loadDescriptor("debug-attach.png")); //$NON-NLS-1$
394         if (mDebugLauncher == null) {
395             mDebugAction.setEnabled(false);
396         }
397 
398         placeActions();
399     }
400 
401     @Override
setFocus()402     public void setFocus() {
403         mDeviceList.setFocus();
404     }
405 
406     /**
407      * Sent when a new {@link IDevice} and {@link Client} are selected.
408      * @param selectedDevice the selected device. If null, no devices are selected.
409      * @param selectedClient The selected client. If null, no clients are selected.
410      */
selectionChanged(IDevice selectedDevice, Client selectedClient)411     public void selectionChanged(IDevice selectedDevice, Client selectedClient) {
412         // update the buttons
413         doSelectionChanged(selectedClient);
414         doSelectionChanged(selectedDevice);
415     }
416 
doSelectionChanged(Client selectedClient)417     private void doSelectionChanged(Client selectedClient) {
418         // update the buttons
419         if (selectedClient != null) {
420             if (USE_SELECTED_DEBUG_PORT) {
421                 // set the client as the debug client
422                 selectedClient.setAsSelectedClient();
423             }
424 
425             mDebugAction.setEnabled(mDebugLauncher != null);
426             mKillAppAction.setEnabled(true);
427             mGcAction.setEnabled(true);
428 
429             mUpdateHeapAction.setEnabled(true);
430             mUpdateHeapAction.setChecked(selectedClient.isHeapUpdateEnabled());
431 
432             mUpdateThreadAction.setEnabled(true);
433             mUpdateThreadAction.setChecked(selectedClient.isThreadUpdateEnabled());
434 
435             ClientData data = selectedClient.getClientData();
436 
437             if (data.hasFeature(ClientData.FEATURE_HPROF)) {
438                 mHprofAction.setEnabled(data.hasPendingHprofDump() == false);
439                 mHprofAction.setToolTipText("Dump HPROF file");
440             } else {
441                 mHprofAction.setEnabled(false);
442                 mHprofAction.setToolTipText("Dump HPROF file (not supported by this VM)");
443             }
444 
445             if (data.hasFeature(ClientData.FEATURE_PROFILING)) {
446                 mTracingAction.setEnabled(true);
447                 if (data.getMethodProfilingStatus() == MethodProfilingStatus.ON) {
448                     mTracingAction.setToolTipText("Stop Method Profiling");
449                     mTracingAction.setText("Stop Method Profiling");
450                     mTracingAction.setImageDescriptor(mTracingStopImage);
451                 } else {
452                     mTracingAction.setToolTipText("Start Method Profiling");
453                     mTracingAction.setImageDescriptor(mTracingStartImage);
454                     mTracingAction.setText("Start Method Profiling");
455                 }
456             } else {
457                 mTracingAction.setEnabled(false);
458                 mTracingAction.setImageDescriptor(mTracingStartImage);
459                 mTracingAction.setToolTipText("Start Method Profiling (not supported by this VM)");
460                 mTracingAction.setText("Start Method Profiling");
461             }
462         } else {
463             if (USE_SELECTED_DEBUG_PORT) {
464                 // set the client as the debug client
465                 AndroidDebugBridge bridge = AndroidDebugBridge.getBridge();
466                 if (bridge != null) {
467                     bridge.setSelectedClient(null);
468                 }
469             }
470 
471             mDebugAction.setEnabled(false);
472             mKillAppAction.setEnabled(false);
473             mGcAction.setEnabled(false);
474             mUpdateHeapAction.setChecked(false);
475             mUpdateHeapAction.setEnabled(false);
476             mUpdateThreadAction.setEnabled(false);
477             mUpdateThreadAction.setChecked(false);
478             mHprofAction.setEnabled(false);
479 
480             mHprofAction.setEnabled(false);
481             mHprofAction.setToolTipText("Dump HPROF file");
482 
483             mTracingAction.setEnabled(false);
484             mTracingAction.setImageDescriptor(mTracingStartImage);
485             mTracingAction.setToolTipText("Start Method Profiling");
486             mTracingAction.setText("Start Method Profiling");
487         }
488     }
489 
doSelectionChanged(IDevice selectedDevice)490     private void doSelectionChanged(IDevice selectedDevice) {
491         mCaptureAction.setEnabled(selectedDevice != null);
492     }
493 
494     /**
495      * Place the actions in the ui.
496      */
placeActions()497     private final void placeActions() {
498         IActionBars actionBars = getViewSite().getActionBars();
499 
500         // first in the menu
501         IMenuManager menuManager = actionBars.getMenuManager();
502         menuManager.removeAll();
503         menuManager.add(mDebugAction);
504         menuManager.add(new Separator());
505         menuManager.add(mUpdateHeapAction);
506         menuManager.add(mHprofAction);
507         menuManager.add(mGcAction);
508         menuManager.add(new Separator());
509         menuManager.add(mUpdateThreadAction);
510         menuManager.add(mTracingAction);
511         menuManager.add(new Separator());
512         menuManager.add(mKillAppAction);
513         menuManager.add(new Separator());
514         menuManager.add(mCaptureAction);
515         menuManager.add(new Separator());
516         menuManager.add(mResetAdbAction);
517 
518         // and then in the toolbar
519         IToolBarManager toolBarManager = actionBars.getToolBarManager();
520         toolBarManager.removeAll();
521         toolBarManager.add(mDebugAction);
522         toolBarManager.add(new Separator());
523         toolBarManager.add(mUpdateHeapAction);
524         toolBarManager.add(mHprofAction);
525         toolBarManager.add(mGcAction);
526         toolBarManager.add(new Separator());
527         toolBarManager.add(mUpdateThreadAction);
528         toolBarManager.add(mTracingAction);
529         toolBarManager.add(new Separator());
530         toolBarManager.add(mKillAppAction);
531         toolBarManager.add(new Separator());
532         toolBarManager.add(mCaptureAction);
533     }
534 
clientChanged(final Client client, int changeMask)535     public void clientChanged(final Client client, int changeMask) {
536         if ((changeMask & Client.CHANGE_METHOD_PROFILING_STATUS) ==
537                 Client.CHANGE_METHOD_PROFILING_STATUS) {
538             if (mDeviceList.getSelectedClient() == client) {
539                 mParentShell.getDisplay().asyncExec(new Runnable() {
540                     public void run() {
541                         // force refresh of the button enabled state.
542                         doSelectionChanged(client);
543                     }
544                 });
545             }
546         }
547     }
548 }
549