Home | History | Annotate | Download | only in gltrace
      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 
     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      */
     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
    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
    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 
    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 
    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 
    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. */
    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 
    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 
    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 
    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 
    289     private void validateAndSetMessage() {
    290         DialogStatus status = validateDialog();
    291         mOkButton.setEnabled(status.valid);
    292         setErrorMessage(status.message);
    293     }
    294 
    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 
    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 
    332         private DialogStatus(boolean isValid, String errMessage) {
    333             valid = isValid;
    334             message = errMessage;
    335         }
    336     }
    337 
    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
    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 
    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 
    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 
    398     public TraceOptions getTraceOptions() {
    399         return new TraceOptions(mSelectedDevice, mAppPackageToTrace, mActivityToTrace,
    400                 sIsActivityFullyQualified, mTraceFilePath, sCollectFbOnEglSwap,
    401                 sCollectFbOnGlDraw, sCollectTextureData);
    402     }
    403 }
    404