• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 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.gltrace;
18 
19 import com.android.ddmlib.AndroidDebugBridge;
20 import com.android.ddmlib.IDevice;
21 
22 import org.eclipse.core.runtime.preferences.IEclipsePreferences;
23 import org.eclipse.core.runtime.preferences.InstanceScope;
24 import org.eclipse.jface.dialogs.IDialogConstants;
25 import org.eclipse.jface.dialogs.TitleAreaDialog;
26 import org.eclipse.swt.SWT;
27 import org.eclipse.swt.events.ModifyEvent;
28 import org.eclipse.swt.events.ModifyListener;
29 import org.eclipse.swt.events.SelectionAdapter;
30 import org.eclipse.swt.events.SelectionEvent;
31 import org.eclipse.swt.events.SelectionListener;
32 import org.eclipse.swt.layout.GridData;
33 import org.eclipse.swt.layout.GridLayout;
34 import org.eclipse.swt.widgets.Button;
35 import org.eclipse.swt.widgets.Combo;
36 import org.eclipse.swt.widgets.Composite;
37 import org.eclipse.swt.widgets.Control;
38 import org.eclipse.swt.widgets.Display;
39 import org.eclipse.swt.widgets.FileDialog;
40 import org.eclipse.swt.widgets.Label;
41 import org.eclipse.swt.widgets.Shell;
42 import org.eclipse.swt.widgets.Text;
43 import org.osgi.service.prefs.BackingStoreException;
44 
45 import java.io.File;
46 import java.util.ArrayList;
47 import java.util.List;
48 
49 /** Dialog displaying all the trace options before the user initiates tracing. */
50 public class GLTraceOptionsDialog extends TitleAreaDialog {
51     private static final String TITLE = "OpenGL ES Trace Options";
52     private static final String DEFAULT_MESSAGE = "Provide the application and activity to be traced.";
53 
54     private static final String PREF_APP_PACKAGE = "gl.trace.apppackage";   //$NON-NLS-1$
55     private static final String PREF_ACTIVITY = "gl.trace.activity";        //$NON-NLS-1$
56     private static final String PREF_TRACEFILE = "gl.trace.destfile";       //$NON-NLS-1$
57     private static final String PREF_DEVICE = "gl.trace.device";            //$NON-NLS-1$
58     private String mLastUsedDevice;
59 
60     private static String sSaveToFolder = System.getProperty("user.home"); //$NON-NLS-1$
61 
62     private Button mOkButton;
63 
64     private Combo mDeviceCombo;
65     private Text mAppPackageToTraceText;
66     private Text mActivityToTraceText;
67     private Button mIsActivityFullyQualifiedButton;
68     private Text mTraceFilePathText;
69 
70     private String mSelectedDevice = "";
71     private String mAppPackageToTrace = "";
72     private String mActivityToTrace = "";
73     private String mTraceFilePath = "";
74     private boolean mAllowAppSelection;
75 
76     private static boolean sCollectFbOnEglSwap = true;
77     private static boolean sCollectFbOnGlDraw = false;
78     private static boolean sCollectTextureData = false;
79     private static boolean sIsActivityFullyQualified = false;
80     private IDevice[] mDevices;
81 
GLTraceOptionsDialog(Shell parentShell)82     public GLTraceOptionsDialog(Shell parentShell) {
83         this(parentShell, true, null);
84     }
85 
86     /**
87      * Constructs a dialog displaying options for the tracer.
88      * @param allowAppSelection true if user can change the application to trace
89      * @param appToTrace default application package to trace
90      */
GLTraceOptionsDialog(Shell parentShell, boolean allowAppSelection, String appToTrace)91     public GLTraceOptionsDialog(Shell parentShell, boolean allowAppSelection,
92             String appToTrace) {
93         super(parentShell);
94         loadPreferences();
95 
96         mAllowAppSelection = allowAppSelection;
97         if (appToTrace != null) {
98             mAppPackageToTrace = appToTrace;
99         }
100     }
101 
102     @Override
createDialogArea(Composite shell)103     protected Control createDialogArea(Composite shell) {
104         setTitle(TITLE);
105         setMessage(DEFAULT_MESSAGE);
106 
107         Composite parent = (Composite) super.createDialogArea(shell);
108         Composite c = new Composite(parent, SWT.BORDER);
109         c.setLayout(new GridLayout(2, false));
110         c.setLayoutData(new GridData(GridData.FILL_BOTH));
111 
112         createLabel(c, "Device:");
113         mDevices = AndroidDebugBridge.getBridge().getDevices();
114         createDeviceDropdown(c, mDevices);
115 
116         createSeparator(c);
117 
118         createLabel(c, "Application Package:");
119         createAppToTraceText(c, "e.g. com.example.package");
120 
121         createLabel(c, "Activity to launch:");
122         createActivityToTraceText(c, "Leave blank to launch default activity");
123 
124         createLabel(c, "");
125         createIsFullyQualifedActivityButton(c,
126                 "Activity name is fully qualified, do not prefix with package name");
127 
128         if (!mAllowAppSelection) {
129             mAppPackageToTraceText.setEnabled(false);
130             mActivityToTraceText.setEnabled(false);
131             mIsActivityFullyQualifiedButton.setEnabled(false);
132         }
133 
134         createSeparator(c);
135 
136         createLabel(c, "Data Collection Options:");
137         createCaptureImageOptions(c);
138 
139         createSeparator(c);
140 
141         createLabel(c, "Destination File: ");
142         createSaveToField(c);
143 
144         return c;
145     }
146 
147     @Override
createButtonsForButtonBar(Composite parent)148     protected void createButtonsForButtonBar(Composite parent) {
149         super.createButtonsForButtonBar(parent);
150 
151         mOkButton = getButton(IDialogConstants.OK_ID);
152         mOkButton.setText("Trace");
153 
154         DialogStatus status = validateDialog();
155         mOkButton.setEnabled(status.valid);
156     }
157 
createSeparator(Composite c)158     private void createSeparator(Composite c) {
159         Label l = new Label(c, SWT.SEPARATOR | SWT.HORIZONTAL);
160         GridData gd = new GridData(GridData.FILL_HORIZONTAL);
161         gd.horizontalSpan = 2;
162         l.setLayoutData(gd);
163     }
164 
createSaveToField(Composite parent)165     private void createSaveToField(Composite parent) {
166         Composite c = new Composite(parent, SWT.NONE);
167         c.setLayout(new GridLayout(2, false));
168         c.setLayoutData(new GridData(GridData.FILL_BOTH));
169 
170         mTraceFilePathText = new Text(c, SWT.BORDER);
171         mTraceFilePathText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
172         mTraceFilePathText.setText(mTraceFilePath);
173         mTraceFilePathText.addModifyListener(new ModifyListener() {
174             @Override
175             public void modifyText(ModifyEvent e) {
176                 validateAndSetMessage();
177             }
178         });
179 
180         Button browse = new Button(c, SWT.PUSH);
181         browse.setText("Browse...");
182         browse.addSelectionListener(new SelectionAdapter() {
183             @Override
184             public void widgetSelected(SelectionEvent e) {
185                 String fName = openBrowseDialog();
186                 if (fName == null) {
187                     return;
188                 }
189 
190                 mTraceFilePathText.setText(fName);
191                 validateAndSetMessage();
192             }
193         });
194     }
195 
openBrowseDialog()196     private String openBrowseDialog() {
197         FileDialog fd = new FileDialog(Display.getDefault().getActiveShell(), SWT.SAVE);
198 
199         fd.setText("Save To");
200         fd.setFileName("trace1.gltrace");
201 
202         fd.setFilterPath(sSaveToFolder);
203         fd.setFilterExtensions(new String[] { "*.gltrace" });
204 
205         String fname = fd.open();
206         if (fname == null || fname.trim().length() == 0) {
207             return null;
208         }
209 
210         sSaveToFolder = fd.getFilterPath();
211         return fname;
212     }
213 
214     /** Options controlling when the FB should be captured. */
createCaptureImageOptions(Composite parent)215     private void createCaptureImageOptions(Composite parent) {
216         Composite c = new Composite(parent, SWT.NONE);
217         c.setLayout(new GridLayout(1, false));
218         c.setLayoutData(new GridData(GridData.FILL_BOTH));
219 
220         final Button readFbOnEglSwapCheckBox = new Button(c, SWT.CHECK);
221         readFbOnEglSwapCheckBox.setText("Read back framebuffer 0 on eglSwapBuffers()");
222         readFbOnEglSwapCheckBox.setSelection(sCollectFbOnEglSwap);
223 
224         final Button readFbOnGlDrawCheckBox = new Button(c, SWT.CHECK);
225         readFbOnGlDrawCheckBox.setText("Read back currently bound framebuffer On glDraw*()");
226         readFbOnGlDrawCheckBox.setSelection(sCollectFbOnGlDraw);
227 
228         final Button readTextureDataCheckBox = new Button(c, SWT.CHECK);
229         readTextureDataCheckBox.setText("Collect texture data submitted using glTexImage*()");
230         readTextureDataCheckBox.setSelection(sCollectTextureData);
231 
232         SelectionListener l = new SelectionAdapter() {
233             @Override
234             public void widgetSelected(SelectionEvent e) {
235                 sCollectFbOnEglSwap = readFbOnEglSwapCheckBox.getSelection();
236                 sCollectFbOnGlDraw = readFbOnGlDrawCheckBox.getSelection();
237                 sCollectTextureData = readTextureDataCheckBox.getSelection();
238             }
239         };
240 
241         readFbOnEglSwapCheckBox.addSelectionListener(l);
242         readFbOnGlDrawCheckBox.addSelectionListener(l);
243         readTextureDataCheckBox.addSelectionListener(l);
244     }
245 
createAppToTraceText(Composite parent, String defaultMessage)246     private Text createAppToTraceText(Composite parent, String defaultMessage) {
247         mAppPackageToTraceText = new Text(parent, SWT.BORDER);
248         mAppPackageToTraceText.setMessage(defaultMessage);
249         mAppPackageToTraceText.setText(mAppPackageToTrace);
250 
251         mAppPackageToTraceText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
252 
253         mAppPackageToTraceText.addModifyListener(new ModifyListener() {
254             @Override
255             public void modifyText(ModifyEvent e) {
256                 validateAndSetMessage();
257             }
258         });
259 
260         return mActivityToTraceText;
261     }
262 
createActivityToTraceText(Composite parent, String defaultMessage)263     private Text createActivityToTraceText(Composite parent, String defaultMessage) {
264         mActivityToTraceText = new Text(parent, SWT.BORDER);
265         mActivityToTraceText.setMessage(defaultMessage);
266         mActivityToTraceText.setText(mActivityToTrace);
267 
268         mActivityToTraceText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
269 
270         mActivityToTraceText.addModifyListener(new ModifyListener() {
271             @Override
272             public void modifyText(ModifyEvent e) {
273                 validateAndSetMessage();
274             }
275         });
276 
277         return mActivityToTraceText;
278     }
279 
createIsFullyQualifedActivityButton(Composite parent, String message)280     private Button createIsFullyQualifedActivityButton(Composite parent, String message) {
281         mIsActivityFullyQualifiedButton = new Button(parent, SWT.CHECK);
282         mIsActivityFullyQualifiedButton.setText(message);
283         mIsActivityFullyQualifiedButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
284         mIsActivityFullyQualifiedButton.setSelection(sIsActivityFullyQualified);
285 
286         return mIsActivityFullyQualifiedButton;
287     }
288 
validateAndSetMessage()289     private void validateAndSetMessage() {
290         DialogStatus status = validateDialog();
291         mOkButton.setEnabled(status.valid);
292         setErrorMessage(status.message);
293     }
294 
createDeviceDropdown(Composite parent, IDevice[] devices)295     private Combo createDeviceDropdown(Composite parent, IDevice[] devices) {
296         mDeviceCombo = new Combo(parent, SWT.READ_ONLY | SWT.BORDER);
297 
298         List<String> items = new ArrayList<String>(devices.length);
299         for (IDevice d : devices) {
300             items.add(d.getName());
301         }
302         mDeviceCombo.setItems(items.toArray(new String[items.size()]));
303 
304         int index = 0;
305         if (items.contains(mLastUsedDevice)) {
306             index = items.indexOf(mLastUsedDevice);
307         }
308         if (index >= 0 && index < items.size()) {
309             mDeviceCombo.select(index);
310         }
311         return mDeviceCombo;
312     }
313 
createLabel(Composite parent, String text)314     private void createLabel(Composite parent, String text) {
315         Label l = new Label(parent, SWT.NONE);
316         l.setText(text);
317         GridData gd = new GridData();
318         gd.horizontalAlignment = SWT.RIGHT;
319         gd.verticalAlignment = SWT.CENTER;
320         l.setLayoutData(gd);
321     }
322 
323     /**
324      * A tuple that specifies whether the current state of the inputs
325      * on the dialog is valid or not. If it is not valid, the message
326      * field stores the reason why it isn't.
327      */
328     private final class DialogStatus {
329         final boolean valid;
330         final String message;
331 
DialogStatus(boolean isValid, String errMessage)332         private DialogStatus(boolean isValid, String errMessage) {
333             valid = isValid;
334             message = errMessage;
335         }
336     }
337 
validateDialog()338     private DialogStatus validateDialog() {
339         if (mDevices.length == 0) {
340             return new DialogStatus(false, "No connected devices.");
341         }
342 
343         if (mAppPackageToTraceText.getText().trim().isEmpty()) {
344             return new DialogStatus(false, "Provide an application name");
345         }
346 
347         String traceFile = mTraceFilePathText.getText().trim();
348         if (traceFile.isEmpty()) {
349             return new DialogStatus(false, "Specify the location where the trace will be saved.");
350         }
351 
352         File f = new File(traceFile).getParentFile();
353         if (f != null && !f.exists()) {
354             return new DialogStatus(false,
355                     String.format("Folder %s does not exist", f.getAbsolutePath()));
356         }
357 
358         return new DialogStatus(true, null);
359     }
360 
361     @Override
okPressed()362     protected void okPressed() {
363         mAppPackageToTrace = mAppPackageToTraceText.getText().trim();
364         mActivityToTrace = mActivityToTraceText.getText().trim();
365         if (mActivityToTrace.startsWith(".")) { //$NON-NLS-1$
366             mActivityToTrace = mActivityToTrace.substring(1);
367         }
368         sIsActivityFullyQualified = mIsActivityFullyQualifiedButton.getSelection();
369         mTraceFilePath = mTraceFilePathText.getText().trim();
370         mSelectedDevice = mDeviceCombo.getText();
371 
372         savePreferences();
373 
374         super.okPressed();
375     }
376 
savePreferences()377     private void savePreferences() {
378         IEclipsePreferences prefs = new InstanceScope().getNode(GlTracePlugin.PLUGIN_ID);
379         prefs.put(PREF_APP_PACKAGE, mAppPackageToTrace);
380         prefs.put(PREF_ACTIVITY, mActivityToTrace);
381         prefs.put(PREF_TRACEFILE, mTraceFilePath);
382         prefs.put(PREF_DEVICE, mSelectedDevice);
383         try {
384             prefs.flush();
385         } catch (BackingStoreException e) {
386             // ignore issues while persisting preferences
387         }
388     }
389 
loadPreferences()390     private void loadPreferences() {
391         IEclipsePreferences prefs = new InstanceScope().getNode(GlTracePlugin.PLUGIN_ID);
392         mAppPackageToTrace = prefs.get(PREF_APP_PACKAGE, "");
393         mActivityToTrace = prefs.get(PREF_ACTIVITY, "");
394         mTraceFilePath = prefs.get(PREF_TRACEFILE, "");
395         mLastUsedDevice = prefs.get(PREF_DEVICE, "");
396     }
397 
getTraceOptions()398     public TraceOptions getTraceOptions() {
399         return new TraceOptions(mSelectedDevice, mAppPackageToTrace, mActivityToTrace,
400                 sIsActivityFullyQualified, mTraceFilePath, sCollectFbOnEglSwap,
401                 sCollectFbOnGlDraw, sCollectTextureData);
402     }
403 }
404