From f51d3b0eaf95bccb521f01b02c8c0ba332c9dd6d Mon Sep 17 00:00:00 2001
From: Siva Velusamy <vsiva@google.com>
Date: Tue, 3 Jan 2012 10:53:20 -0800
Subject: gltrace: Enable trace options.

Provide user options that control how much data is collected.
The user can control whether framebuffer data is read on eglSwap()
or glDraw*(), and whether texture images submitted via
glTexImage() are read.

Change-Id: I99b71d31a985295f0793d94887c0efcc25083fc6
---
 .../ide/eclipse/gltrace/CollectTraceAction.java    |  73 ++++++--
 .../eclipse/gltrace/GLTraceCollectorDialog.java    | 198 +++++++++++++++------
 .../ide/eclipse/gltrace/GLTraceOptionsDialog.java  | 107 +++++------
 .../android/ide/eclipse/gltrace/GLTraceWriter.java | 134 --------------
 .../ide/eclipse/gltrace/TraceCommandWriter.java    |  57 ++++++
 .../ide/eclipse/gltrace/TraceFileWriter.java       | 155 ++++++++++++++++
 .../android/ide/eclipse/gltrace/TraceOptions.java  |  47 +++++
 .../gltrace/editors/GLFunctionTraceViewer.java     |  20 ++-
 .../android/ide/eclipse/gltrace/model/GLCall.java  |   8 +
 9 files changed, 533 insertions(+), 266 deletions(-)
 delete mode 100644 eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/GLTraceWriter.java
 create mode 100644 eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/TraceCommandWriter.java
 create mode 100644 eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/TraceFileWriter.java
 create mode 100644 eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/TraceOptions.java

diff --git a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/CollectTraceAction.java b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/CollectTraceAction.java
index 33b9fd1..7b1b959 100644
--- a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/CollectTraceAction.java
+++ b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/CollectTraceAction.java
@@ -33,9 +33,12 @@ import org.eclipse.swt.widgets.Shell;
 import org.eclipse.ui.IWorkbenchWindow;
 import org.eclipse.ui.IWorkbenchWindowActionDelegate;
 
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.net.Socket;
 
 public class CollectTraceAction implements IWorkbenchWindowActionDelegate {
     public void run(IAction action) {
@@ -60,13 +63,10 @@ public class CollectTraceAction implements IWorkbenchWindowActionDelegate {
             return;
         }
 
-        String activityName = dlg.getApplicationToTrace();
-        String traceDestination = dlg.getTraceDestination();
-        String deviceName = dlg.getDevice();
+        TraceOptions traceOptions = dlg.getTraceOptions();
 
-        IDevice device = getDevice(deviceName);
-
-        String appName = activityName.split("/")[0];
+        IDevice device = getDevice(traceOptions.device);
+        String appName = traceOptions.activityToTrace.split("/")[0]; //$NON-NLS-1$
         killApp(device, appName);
 
         try {
@@ -84,7 +84,7 @@ public class CollectTraceAction implements IWorkbenchWindowActionDelegate {
         }
 
         try {
-            startActivity(device, activityName);
+            startActivity(device, traceOptions.activityToTrace);
         } catch (Exception e) {
             MessageDialog.openError(shell, "Setup GL Trace",
                     "Error while launching application: " + e.getMessage());
@@ -99,19 +99,67 @@ public class CollectTraceAction implements IWorkbenchWindowActionDelegate {
 
         // if everything went well, the app should now be waiting for the gl debugger
         // to connect
-        startTracing(shell, traceDestination, port);
+        startTracing(shell, traceOptions, port);
     }
 
-    private void startTracing(Shell shell, String traceDestination, int port) {
+    private void startTracing(Shell shell, TraceOptions traceOptions, int port) {
         FileOutputStream fos = null;
         try {
-            fos = new FileOutputStream(traceDestination, false);
+            fos = new FileOutputStream(traceOptions.traceDestination, false);
         } catch (FileNotFoundException e) {
             // input path is valid, so this cannot occur
         }
 
-        GLTraceWriter writer = new GLTraceWriter(fos, port, new GLTraceCollectorDialog(shell));
-        writer.start();
+        Socket socket = new Socket();
+        DataInputStream traceDataStream = null;
+        DataOutputStream traceCommandsStream = null;
+        try {
+            socket.connect(new java.net.InetSocketAddress("127.0.0.1", port)); //$NON-NLS-1$
+            socket.setTcpNoDelay(true);
+            traceDataStream = new DataInputStream(socket.getInputStream());
+            traceCommandsStream = new DataOutputStream(socket.getOutputStream());
+        } catch (IOException e) {
+            MessageDialog.openError(shell,
+                    "OpenGL Trace",
+                    "Unable to connect to remote GL Trace Server: " + e.getMessage());
+            return;
+        }
+
+        // create channel to send trace commands to device
+        TraceCommandWriter traceCommandWriter = new TraceCommandWriter(traceCommandsStream);
+        try {
+            traceCommandWriter.setTraceOptions(traceOptions.collectFbOnEglSwap,
+                    traceOptions.collectFbOnGlDraw,
+                    traceOptions.collectTextureData);
+        } catch (IOException e) {
+            MessageDialog.openError(shell,
+                    "OpenGL Trace",
+                    "Unexpected error while setting trace options: " + e.getMessage());
+            closeSocket(socket);
+            return;
+        }
+
+        // create trace writer that writes to a trace file
+        TraceFileWriter traceFileWriter = new TraceFileWriter(fos, traceDataStream);
+        traceFileWriter.start();
+
+        GLTraceCollectorDialog dlg = new GLTraceCollectorDialog(shell,
+                traceFileWriter,
+                traceCommandWriter,
+                traceOptions);
+        dlg.open();
+
+        traceFileWriter.stopTracing();
+        traceCommandWriter.close();
+        closeSocket(socket);
+    }
+
+    private void closeSocket(Socket socket) {
+        try {
+            socket.close();
+        } catch (IOException e) {
+            // ignore error while closing socket
+        }
     }
 
     private void startActivity(IDevice device, String appName)
@@ -120,7 +168,6 @@ public class CollectTraceAction implements IWorkbenchWindowActionDelegate {
         String startAppCmd = String.format(
                 "am start %s -a android.intent.action.MAIN -c android.intent.category.LAUNCHER", //$NON-NLS-1$
                 appName);
-        System.out.println(startAppCmd);
         device.executeShellCommand(startAppCmd, new IgnoreOutputReceiver());
     }
 
diff --git a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/GLTraceCollectorDialog.java b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/GLTraceCollectorDialog.java
index 04944c2..e7897ec 100644
--- a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/GLTraceCollectorDialog.java
+++ b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/GLTraceCollectorDialog.java
@@ -16,29 +16,45 @@
 
 package com.android.ide.eclipse.gltrace;
 
-import org.eclipse.jface.dialogs.Dialog;
 import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.dialogs.TitleAreaDialog;
 import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
 import org.eclipse.swt.layout.GridData;
 import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
 import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Control;
 import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Group;
 import org.eclipse.swt.widgets.Label;
 import org.eclipse.swt.widgets.ProgressBar;
 import org.eclipse.swt.widgets.Shell;
-import org.eclipse.swt.widgets.Text;
+
+import java.io.IOException;
 
 /** Dialog displayed while the trace is being streamed from device to host. */
-public class GLTraceCollectorDialog extends Dialog {
-    private Text mFramesCollectedText;
-    private Text mTraceFileSizeText;
-    private GLTraceWriter mWriter;
-    private int mFramesCollected;
-    private int mTraceFileSize;
-
-    protected GLTraceCollectorDialog(Shell parentShell) {
+public class GLTraceCollectorDialog extends TitleAreaDialog {
+    private static final String TITLE = "OpenGL ES Trace";
+    private static final String DEFAULT_MESSAGE = "Trace collection in progress.";
+
+    private TraceOptions mTraceOptions;
+    private final TraceFileWriter mTraceFileWriter;
+    private final TraceCommandWriter mTraceCommandWriter;
+
+    private Label mFramesCollectedLabel;
+    private Label mTraceFileSizeLabel;
+    private StatusRefreshTask mRefreshTask;
+
+    protected GLTraceCollectorDialog(Shell parentShell, TraceFileWriter traceFileWriter,
+            TraceCommandWriter traceCommandWriter, TraceOptions traceOptions) {
         super(parentShell);
+        mTraceFileWriter = traceFileWriter;
+        mTraceCommandWriter = traceCommandWriter;
+        mTraceOptions = traceOptions;
     }
 
     @Override
@@ -55,84 +71,154 @@ public class GLTraceCollectorDialog extends Dialog {
 
     @Override
     protected Control createDialogArea(Composite parent) {
-        Composite c = new Composite(parent, SWT.NONE);
-        c.setLayout(new GridLayout(2, false));
-        c.setLayoutData(new GridData(GridData.FILL_BOTH));
+        parent.setLayout(new GridLayout());
+
+        setTitle(TITLE);
+        setMessage(DEFAULT_MESSAGE);
+
+        Group controlGroup = new Group(parent, SWT.BORDER);
+        controlGroup.setLayout(new GridLayout(2, false));
+        controlGroup.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+        controlGroup.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_BLUE));
+        controlGroup.setText("Trace Options");
+
+        createLabel(controlGroup, "Collect Framebuffer contents on eglSwapBuffers()");
+        final Button eglSwapCheckBox = createButton(controlGroup,
+                mTraceOptions.collectFbOnEglSwap);
+
+        createLabel(controlGroup, "Collect Framebuffer contents on glDraw*()");
+        final Button glDrawCheckBox = createButton(controlGroup, mTraceOptions.collectFbOnGlDraw);
+
+        createLabel(controlGroup, "Collect texture data for glTexImage*()");
+        final Button glTexImageCheckBox = createButton(controlGroup,
+                mTraceOptions.collectTextureData);
+
+        SelectionListener l = new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent event) {
+                boolean eglSwap = eglSwapCheckBox.getSelection();
+                boolean glDraw = glDrawCheckBox.getSelection();
+                boolean glTexImage = glTexImageCheckBox.getSelection();
+
+                try {
+                    mTraceCommandWriter.setTraceOptions(eglSwap, glDraw, glTexImage);
+                } catch (IOException e) {
+                    eglSwapCheckBox.setEnabled(false);
+                    glDrawCheckBox.setEnabled(false);
+                    glTexImageCheckBox.setEnabled(false);
+
+                    MessageDialog.openError(Display.getDefault().getActiveShell(),
+                            "OpenGL ES Trace",
+                            "Error while setting trace options: " + e.getMessage());
+                }
 
-        createLabel(c, "Frames Collected:");
-        mFramesCollectedText = createText(c);
+                // update the text on the button
+                if (!(event.getSource() instanceof Button)) {
+                    return;
+                }
+                Button sourceButton = (Button) event.getSource();
+                sourceButton.setText(getToggleActionText(sourceButton.getSelection()));
+                sourceButton.pack();
+            }
+        };
+
+        eglSwapCheckBox.addSelectionListener(l);
+        glDrawCheckBox.addSelectionListener(l);
+        glTexImageCheckBox.addSelectionListener(l);
+
+        Group statusGroup = new Group(parent, SWT.NONE);
+        statusGroup.setLayout(new GridLayout(2, false));
+        statusGroup.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+        statusGroup.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_BLUE));
+        statusGroup.setText("Trace Status");
 
-        createLabel(c, "Trace File Size:");
-        mTraceFileSizeText = createText(c);
+        createLabel(statusGroup, "Frames Collected:");
+        mFramesCollectedLabel = createLabel(statusGroup, "");
 
-        ProgressBar pb = new ProgressBar(c, SWT.INDETERMINATE);
+        createLabel(statusGroup, "Trace File Size:");
+        mTraceFileSizeLabel = createLabel(statusGroup, "");
+
+        ProgressBar pb = new ProgressBar(statusGroup, SWT.INDETERMINATE);
         GridData gd = new GridData(GridData.FILL_HORIZONTAL);
         gd.horizontalSpan = 2;
         pb.setLayoutData(gd);
 
+        mRefreshTask = new StatusRefreshTask();
+        new Thread(mRefreshTask, "Trace Status Refresh Thread").start();
+
         return super.createDialogArea(parent);
     }
 
-    private Text createText(Composite c) {
-        Text t = new Text(c, SWT.BORDER);
-
-        GridData gd = new GridData();
-        gd.widthHint = 100;
-        t.setLayoutData(gd);
+    private Button createButton(Composite controlComposite, boolean selection) {
+        Button b = new Button(controlComposite, SWT.TOGGLE);
+        b.setText(getToggleActionText(selection));
+        b.setSelection(selection);
+        return b;
+    }
 
-        return t;
+    /** Get text to show on toggle box given its current selection. */
+    private String getToggleActionText(boolean en) {
+        return en ? "Disable" : "Enable";
     }
 
-    private void createLabel(Composite parent, String text) {
+    private Label createLabel(Composite parent, String text) {
         Label l = new Label(parent, SWT.NONE);
         l.setText(text);
-        GridData gd = new GridData();
-        gd.horizontalAlignment = SWT.RIGHT;
+        GridData gd = new GridData(GridData.FILL_HORIZONTAL);
+        gd.horizontalAlignment = SWT.LEFT;
         gd.verticalAlignment = SWT.CENTER;
         l.setLayoutData(gd);
+
+        return l;
     }
 
     @Override
     protected void okPressed() {
-        if (mWriter != null) {
-            mWriter.stopTracing();
-        }
-
+        mRefreshTask.cancel();
         super.okPressed();
     }
 
-    public void setFrameCount(final int n) {
-        mFramesCollected = n;
-        scheduleDisplayUpdate();
-    }
+    /** Periodically refresh the trace status. */
+    private class StatusRefreshTask implements Runnable {
+        private static final int REFRESH_INTERVAL = 1000;
+        private volatile boolean mIsCancelled = false;
 
-    public void setTraceFileSize(final int n) {
-        mTraceFileSize = n;
-        scheduleDisplayUpdate();
-    }
+        public void run() {
+            if (mTraceFileWriter == null) {
+                return;
+            }
+
+            while (!mIsCancelled) {
+                final String frameCount = Integer.toString(mTraceFileWriter.getCurrentFrameCount());
+
+                double fileSize = (double) mTraceFileWriter.getCurrentFileSize();
+                fileSize /= (1024 * 1024); // convert to size in MB
+                final String frameSize = String.format("%.2g MB", fileSize); //$NON-NLS-1$
 
-    private Runnable mRefreshTask = null;
+                Display.getDefault().syncExec(new Runnable() {
+                    public void run() {
+                        if (mFramesCollectedLabel.isDisposed()) {
+                            return;
+                        }
 
-    /** Schedule a refresh UI task if one is not already pending. */
-    private void scheduleDisplayUpdate() {
-        if (mRefreshTask == null) {
-            mRefreshTask = new Runnable() {
-                public void run() {
-                    mRefreshTask = null;
+                        mFramesCollectedLabel.setText(frameCount);
+                        mTraceFileSizeLabel.setText(frameSize);
 
-                    if (mFramesCollectedText.isDisposed()) {
-                        return;
+                        mFramesCollectedLabel.pack();
+                        mTraceFileSizeLabel.pack();
                     }
+                });
 
-                    mFramesCollectedText.setText(Integer.toString(mFramesCollected));
-                    mTraceFileSizeText.setText(Integer.toString(mTraceFileSize) + " bytes");
+                try {
+                    Thread.sleep(REFRESH_INTERVAL);
+                } catch (InterruptedException e) {
+                    return;
                 }
-            };
-            Display.getDefault().asyncExec(mRefreshTask);
+            }
         }
-    }
 
-    public void setTraceWriter(GLTraceWriter writer) {
-        mWriter = writer;
+        public void cancel() {
+            mIsCancelled = true;
+        }
     }
 }
diff --git a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/GLTraceOptionsDialog.java b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/GLTraceOptionsDialog.java
index 33d22ab..18f9968 100644
--- a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/GLTraceOptionsDialog.java
+++ b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/GLTraceOptionsDialog.java
@@ -29,6 +29,7 @@ import org.eclipse.swt.events.ModifyEvent;
 import org.eclipse.swt.events.ModifyListener;
 import org.eclipse.swt.events.SelectionAdapter;
 import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
 import org.eclipse.swt.layout.GridData;
 import org.eclipse.swt.layout.GridLayout;
 import org.eclipse.swt.widgets.Button;
@@ -45,13 +46,10 @@ import org.osgi.service.prefs.BackingStoreException;
 import java.util.ArrayList;
 import java.util.List;
 
-// FIXME: Not all elements in this dialog are functional. They are there for UI review,
-// and once we figure out what needs to be there and what not, we'll fix this.
 /** Dialog displaying all the trace options before the user initiates tracing. */
 public class GLTraceOptionsDialog extends TitleAreaDialog {
-    private static final String TITLE = "OpenGL ES 2.0 Trace Options";
-    private static final String DEFAULT_MESSAGE = "Provide the application to be traced. The application needs to have INTERNET permission for tracing.";
-    private static final String DEFAULT_NUM_FRAMES_TEXT = "10";
+    private static final String TITLE = "OpenGL ES Trace Options";
+    private static final String DEFAULT_MESSAGE = "Provide the application and activity to be traced. The application needs to have INTERNET permission for tracing.";
 
     private static final String PREF_APPNAME = "gl.trace.appname";
     private static final String PREF_TRACEFILE = "gl.trace.destfile";
@@ -61,13 +59,17 @@ public class GLTraceOptionsDialog extends TitleAreaDialog {
     private Button mOkButton;
 
     private Combo mDeviceCombo;
-    private Text mAppToTraceText;
+    private Text mActivityToTraceText;
     private Text mTraceFilePathText;
 
     private String mSelectedDevice = "";
-    private String mAppToTrace = "";
+    private String mActivityToTrace = "";
     private String mTraceFilePath = "";
 
+    private boolean mCollectFbOnEglSwap = true;
+    private boolean mCollectFbOnGlDraw = false;
+    private boolean mCollectTextureData = false;
+
     public GLTraceOptionsDialog(Shell parentShell) {
         super(parentShell);
         loadPreferences();
@@ -89,11 +91,8 @@ public class GLTraceOptionsDialog extends TitleAreaDialog {
         createLabel(c, "Activity:");
         createAppToTraceText(c, "e.g. com.example.android.apis");
 
-        createLabel(c, "Capture Mode:");
-        createCaptureModeOptions(c);
-
-        createLabel(c, "Capture Framebuffer:");
-        createCaptureFBOptions(c);
+        createLabel(c, "Capture Image:");
+        createCaptureImageOptions(c);
 
         createSeparator(c);
 
@@ -166,60 +165,51 @@ public class GLTraceOptionsDialog extends TitleAreaDialog {
     }
 
     /** Options controlling when the FB should be captured. */
-    private void createCaptureFBOptions(Composite parent) {
+    private void createCaptureImageOptions(Composite parent) {
         Composite c = new Composite(parent, SWT.NONE);
         c.setLayout(new GridLayout(1, false));
         c.setLayoutData(new GridData(GridData.FILL_BOTH));
 
-        Button b1 = new Button(c, SWT.CHECK);
-        b1.setText("On eglSwap()");
-        b1.setSelection(true);
+        final Button readFbOnEglSwapCheckBox = new Button(c, SWT.CHECK);
+        readFbOnEglSwapCheckBox.setText("Read back framebuffer 0 on eglSwapBuffers()");
+        readFbOnEglSwapCheckBox.setSelection(mCollectFbOnEglSwap);
 
-        Button b2 = new Button(c, SWT.CHECK);
-        b2.setText("On glDrawElements() and glDrawArrays()");
-        b2.setSelection(false);
-    }
+        final Button readFbOnGlDrawCheckBox = new Button(c, SWT.CHECK);
+        readFbOnGlDrawCheckBox.setText("Read back currently bound framebuffer On glDraw*()");
+        readFbOnGlDrawCheckBox.setSelection(mCollectFbOnGlDraw);
 
-    private void createCaptureModeOptions(Composite parent) {
-        Composite c = new Composite(parent, SWT.NONE);
-        c.setLayout(new GridLayout(2, false));
-        c.setLayoutData(new GridData(GridData.FILL_BOTH));
-
-        Button b1 = new Button(c, SWT.RADIO);
-        b1.setText("Infinite Buffer");
-        GridData gd = new GridData();
-        gd.horizontalSpan = 2;
-        b1.setLayoutData(gd);
+        final Button readTextureDataCheckBox = new Button(c, SWT.CHECK);
+        readTextureDataCheckBox.setText("Collect texture data submitted using glTexImage*()");
+        readTextureDataCheckBox.setSelection(mCollectTextureData);
 
-        Button b2 = new Button(c, SWT.RADIO);
-        b2.setText("Last N frames");
-        b2.setEnabled(false);
-
-        b1.setSelection(true);
+        SelectionListener l = new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                mCollectFbOnEglSwap = readFbOnEglSwapCheckBox.getSelection();
+                mCollectFbOnGlDraw = readFbOnGlDrawCheckBox.getSelection();
+                mCollectTextureData = readTextureDataCheckBox.getSelection();
+            }
+        };
 
-        Text t = new Text(c, SWT.BORDER);
-        t.setMessage(DEFAULT_NUM_FRAMES_TEXT);
-        gd = new GridData();
-        gd.widthHint = 30;
-        t.setLayoutData(gd);
-        t.setEditable(false);
-        t.setEnabled(false);
+        readFbOnEglSwapCheckBox.addSelectionListener(l);
+        readFbOnGlDrawCheckBox.addSelectionListener(l);
+        readTextureDataCheckBox.addSelectionListener(l);
     }
 
     private Text createAppToTraceText(Composite parent, String defaultMessage) {
-        mAppToTraceText = new Text(parent, SWT.BORDER);
-        mAppToTraceText.setMessage(defaultMessage);
-        mAppToTraceText.setText(mAppToTrace);
+        mActivityToTraceText = new Text(parent, SWT.BORDER);
+        mActivityToTraceText.setMessage(defaultMessage);
+        mActivityToTraceText.setText(mActivityToTrace);
 
-        mAppToTraceText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+        mActivityToTraceText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
 
-        mAppToTraceText.addModifyListener(new ModifyListener() {
+        mActivityToTraceText.addModifyListener(new ModifyListener() {
             public void modifyText(ModifyEvent e) {
                 validateAndSetMessage();
             }
         });
 
-        return mAppToTraceText;
+        return mActivityToTraceText;
     }
 
     private void validateAndSetMessage() {
@@ -269,7 +259,7 @@ public class GLTraceOptionsDialog extends TitleAreaDialog {
     }
 
     private DialogStatus validateDialog() {
-        if (mAppToTraceText.getText().trim().length() == 0) {
+        if (mActivityToTraceText.getText().trim().length() == 0) {
             return new DialogStatus(false, "Provide an application name");
         }
 
@@ -282,7 +272,7 @@ public class GLTraceOptionsDialog extends TitleAreaDialog {
 
     @Override
     protected void okPressed() {
-        mAppToTrace = mAppToTraceText.getText();
+        mActivityToTrace = mActivityToTraceText.getText();
         mTraceFilePath = mTraceFilePathText.getText();
         mSelectedDevice = mDeviceCombo.getText();
 
@@ -293,7 +283,7 @@ public class GLTraceOptionsDialog extends TitleAreaDialog {
 
     private void savePreferences() {
         IEclipsePreferences prefs = new InstanceScope().getNode(Activator.PLUGIN_ID);
-        prefs.put(PREF_APPNAME, mAppToTrace);
+        prefs.put(PREF_APPNAME, mActivityToTrace);
         prefs.put(PREF_TRACEFILE, mTraceFilePath);
         try {
             prefs.flush();
@@ -304,19 +294,12 @@ public class GLTraceOptionsDialog extends TitleAreaDialog {
 
     private void loadPreferences() {
         IEclipsePreferences prefs = new InstanceScope().getNode(Activator.PLUGIN_ID);
-        mAppToTrace = prefs.get(PREF_APPNAME, "");
+        mActivityToTrace = prefs.get(PREF_APPNAME, "");
         mTraceFilePath = prefs.get(PREF_TRACEFILE, "");
     }
 
-    public String getDevice() {
-        return mSelectedDevice;
-    }
-
-    public String getApplicationToTrace() {
-        return mAppToTrace.trim();
-    }
-
-    public String getTraceDestination() {
-        return mTraceFilePath;
+    public TraceOptions getTraceOptions() {
+        return new TraceOptions(mSelectedDevice, mActivityToTrace.trim(), mTraceFilePath,
+                mCollectFbOnEglSwap, mCollectFbOnGlDraw, mCollectTextureData);
     }
 }
diff --git a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/GLTraceWriter.java b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/GLTraceWriter.java
deleted file mode 100644
index d57d677..0000000
--- a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/GLTraceWriter.java
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.ide.eclipse.gltrace;
-
-import com.android.ide.eclipse.gltrace.GLProtoBuf.GLMessage;
-import com.google.protobuf.InvalidProtocolBufferException;
-
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.net.Socket;
-
-/** A class that streams data received from a socket into the trace file. */
-public class GLTraceWriter {
-    private DataOutputStream mOutputStream;
-    private int mPort;
-    private GLTraceCollectorDialog mDialog;
-    private volatile boolean stopTracing = false;
-    private Thread mReceiverThread;
-
-    private int mFileSize = 0;
-    private int mFrameCount = 0;
-
-    public GLTraceWriter(FileOutputStream fos, int port,
-            GLTraceCollectorDialog glTraceCollectorDialog) {
-        mOutputStream = new DataOutputStream(fos);
-        mPort = port;
-        mDialog = glTraceCollectorDialog;
-    }
-
-    public void start() {
-        // launch thread
-        mReceiverThread = new Thread(new GLTraceReceiverTask());
-        mReceiverThread.setName("GL Trace Receiver");
-        mReceiverThread.start();
-
-        // launch dialog
-        mDialog.setTraceWriter(this);
-        mDialog.open();
-    }
-
-    public void stopTracing() {
-        // stop thread
-        stopTracing = true;
-
-        // wait for receiver to complete
-        try {
-            mReceiverThread.join();
-        } catch (InterruptedException e1) {
-            // ignore, this cannot be interrupted
-        }
-
-        // close stream
-        try {
-            mOutputStream.close();
-        } catch (IOException e) {
-            // ignore error while closing stream
-        }
-    }
-
-    private class GLTraceReceiverTask implements Runnable {
-        public void run() {
-            try {
-                Socket socket = new Socket();
-                socket.connect(new java.net.InetSocketAddress("127.0.0.1", mPort));
-                DataInputStream dis = new DataInputStream(socket.getInputStream());
-
-                while (!stopTracing) {
-                    if (dis.available() > 0) {
-                        readMessage(dis);
-                        mDialog.setFrameCount(mFrameCount);
-                        mDialog.setTraceFileSize(mFileSize);
-                    } else {
-                        try {
-                            Thread.sleep(100);
-                        } catch (InterruptedException e) {
-                            // ignore, as this thread is not interrupted by any other thread.
-                        }
-                    }
-                }
-
-                socket.close();
-            } catch (IOException e) {
-            }
-        }
-    }
-
-    private void readMessage(DataInputStream dis) throws IOException {
-        int len = dis.readInt();
-        len = Integer.reverseBytes(len);    // readInt is big endian, we want little endian
-
-        byte[] buffer = new byte[len];
-        int readLen = 0;
-        while (readLen < len) {
-            int read = dis.read(buffer, readLen, len - readLen);
-            if (read < 0) {
-                throw new IOException();
-            } else {
-                readLen += read;
-            }
-        }
-
-        GLMessage msg = null;
-        try {
-            msg = GLMessage.parseFrom(buffer);
-        } catch (InvalidProtocolBufferException e) {
-            System.out.println("Invalid protocol buffer: " + e.getMessage());
-            return;
-        }
-        mOutputStream.writeInt(len);
-        mOutputStream.write(buffer);
-
-        mFileSize += readLen;
-
-        if (msg.getFunction() == GLMessage.Function.eglSwapBuffers) {
-            mFrameCount++;
-        }
-    }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/TraceCommandWriter.java b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/TraceCommandWriter.java
new file mode 100644
index 0000000..f8d97b7
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/TraceCommandWriter.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.eclipse.gltrace;
+
+import java.io.DataOutputStream;
+import java.io.IOException;
+
+/**
+ * Write trace control options to the trace backend.
+ * Currently, the number of options is limited, so all the options are packed into a
+ * single integer. Any changes to this protocol have to be updated on the device as well.
+ */
+public class TraceCommandWriter {
+    private static final int READ_FB_ON_EGLSWAP_BIT = 0;
+    private static final int READ_FB_ON_GLDRAW_BIT = 1;
+    private static final int READ_TEXTURE_DATA_ON_GLTEXIMAGE_BIT = 2;
+
+    private final DataOutputStream mStream;;
+
+    public TraceCommandWriter(DataOutputStream traceCommandStream) {
+        mStream = traceCommandStream;
+    }
+
+    public void setTraceOptions(boolean readFbOnEglSwap, boolean readFbOnGlDraw,
+            boolean readTextureOnGlTexImage) throws IOException {
+        int eglSwap = readFbOnEglSwap ? (1 << READ_FB_ON_EGLSWAP_BIT) : 0;
+        int glDraw = readFbOnGlDraw ? (1 << READ_FB_ON_GLDRAW_BIT) : 0;
+        int tex = readTextureOnGlTexImage ? ( 1 << READ_TEXTURE_DATA_ON_GLTEXIMAGE_BIT) : 0;
+
+        int cmd = eglSwap | glDraw | tex;
+
+        mStream.writeInt(cmd);
+        mStream.flush();
+    }
+
+    public void close() {
+        try {
+            mStream.close();
+        } catch (IOException e) {
+            // ignore exception while closing stream
+        }
+    }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/TraceFileWriter.java b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/TraceFileWriter.java
new file mode 100644
index 0000000..a6c02c4
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/TraceFileWriter.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.eclipse.gltrace;
+
+import com.android.ide.eclipse.gltrace.GLProtoBuf.GLMessage;
+import com.android.ide.eclipse.gltrace.GLProtoBuf.GLMessage.Function;
+import com.google.protobuf.InvalidProtocolBufferException;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+/** A class that streams data received from a socket into the trace file. */
+public class TraceFileWriter {
+    private DataInputStream mInputStream;
+    private DataOutputStream mOutputStream;
+    private Thread mReceiverThread;
+
+    private int mFileSize = 0;
+    private int mFrameCount = 0;
+
+    /**
+     * Construct a trace file writer.
+     * @param fos output stream to write trace data to
+     * @param is input stream from which trace data is read
+     */
+    public TraceFileWriter(FileOutputStream fos, DataInputStream is) {
+        mOutputStream = new DataOutputStream(fos);
+        mInputStream = is;
+    }
+
+    public void start() {
+        // launch thread
+        mReceiverThread = new Thread(new GLTraceReceiverTask());
+        mReceiverThread.setName("GL Trace Receiver");
+        mReceiverThread.start();
+    }
+
+    public void stopTracing() {
+        // close socket to stop the receiver thread
+        try {
+            mInputStream.close();
+        } catch (IOException e) {
+            // ignore exception while closing socket
+        }
+
+        // wait for receiver to complete
+        try {
+            mReceiverThread.join();
+        } catch (InterruptedException e1) {
+            // ignore, this cannot be interrupted
+        }
+
+        // close stream
+        try {
+            mOutputStream.close();
+        } catch (IOException e) {
+            // ignore error while closing stream
+        }
+    }
+
+    /**
+     * The GLTraceReceiverTask collects trace data from the device and writes it
+     * into a file while collecting some stats on the way.
+     */
+    private class GLTraceReceiverTask implements Runnable {
+        public void run() {
+            while (true) {
+                byte[] buffer = readTraceData(mInputStream);
+                if (buffer == null) {
+                    break;
+                }
+
+                try {
+                    writeTraceData(buffer, mOutputStream);
+                } catch (IOException e) {
+                    break;
+                }
+
+                updateTraceStats(buffer);
+            }
+        }
+    }
+
+    private byte[] readTraceData(DataInputStream dis) {
+        int len;
+        try {
+            len = dis.readInt();
+        } catch (IOException e1) {
+            return null;
+        }
+        len = Integer.reverseBytes(len);    // readInt is big endian, we want little endian
+
+        byte[] buffer = new byte[len];
+        int readLen = 0;
+        while (readLen < len) {
+            try {
+                int read = dis.read(buffer, readLen, len - readLen);
+                if (read < 0) {
+                    return null;
+                } else {
+                    readLen += read;
+                }
+            } catch (IOException e) {
+                return null;
+            }
+        }
+
+        return buffer;
+    }
+
+
+    private void writeTraceData(byte[] buffer, DataOutputStream stream) throws IOException {
+        stream.writeInt(buffer.length);
+        stream.write(buffer);
+    }
+
+    private void updateTraceStats(byte[] buffer) {
+        GLMessage msg = null;
+        try {
+            msg = GLMessage.parseFrom(buffer);
+        } catch (InvalidProtocolBufferException e) {
+            return;
+        }
+
+        mFileSize += buffer.length;
+
+        if (msg.getFunction() == Function.eglSwapBuffers) {
+            mFrameCount++;
+        }
+    }
+
+    public int getCurrentFileSize() {
+        return mFileSize;
+    }
+
+    public int getCurrentFrameCount() {
+        return mFrameCount;
+    }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/TraceOptions.java b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/TraceOptions.java
new file mode 100644
index 0000000..d67d167
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/TraceOptions.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.eclipse.gltrace;
+
+public class TraceOptions {
+    /** Device on which the application should be run. */
+    public final String device;
+
+    /** Activity to trace. */
+    public final String activityToTrace;
+
+    /** Path where the trace file should be saved. */
+    public final String traceDestination;
+
+    /** Flag indicating whether Framebuffer should be captured on eglSwap() */
+    public final boolean collectFbOnEglSwap;
+
+    /** Flag indicating whether Framebuffer should be captured on glDraw*() */
+    public final boolean collectFbOnGlDraw;
+
+    /** Flag indicating whether texture data should be captured on glTexImage*() */
+    public final boolean collectTextureData;
+
+    public TraceOptions(String device, String activity, String destinationPath,
+            boolean collectFbOnEglSwap, boolean collectFbOnGlDraw, boolean collectTextureData) {
+        this.device = device;
+        this.activityToTrace = activity;
+        this.traceDestination = destinationPath;
+        this.collectFbOnEglSwap = collectFbOnEglSwap;
+        this.collectFbOnGlDraw = collectFbOnGlDraw;
+        this.collectTextureData = collectTextureData;
+    }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/editors/GLFunctionTraceViewer.java b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/editors/GLFunctionTraceViewer.java
index fc6ce18..31d48c8 100644
--- a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/editors/GLFunctionTraceViewer.java
+++ b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/editors/GLFunctionTraceViewer.java
@@ -260,7 +260,21 @@ public class GLFunctionTraceViewer extends EditorPart implements ISelectionProvi
         tvc.setLabelProvider(labelProvider);
         TableColumn column = tvc.getColumn();
         column.setText("Context");
-        column.setWidth(50);
+        column.setWidth(150);
+
+        // column showing the GL function called
+        tvc = new TableViewerColumn(mFrameTableViewer, SWT.NONE);
+        tvc.setLabelProvider(labelProvider);
+        column = tvc.getColumn();
+        column.setText("Start");
+        column.setWidth(150);
+
+        // column showing the GL function called
+        tvc = new TableViewerColumn(mFrameTableViewer, SWT.NONE);
+        tvc.setLabelProvider(labelProvider);
+        column = tvc.getColumn();
+        column.setText("Duration");
+        column.setWidth(150);
 
         // column showing the GL function called
         tvc = new TableViewerColumn(mFrameTableViewer, SWT.NONE);
@@ -359,6 +373,10 @@ public class GLFunctionTraceViewer extends EditorPart implements ISelectionProvi
             switch (columnIndex) {
             case 0:
                 return Integer.toString(c.getContextId());
+            case 1:
+                return Long.toString(c.getStartTime());
+            case 2:
+                return Integer.toString(c.getDuration());
             default:
                 try {
                     return sGLCallFormatter.formatGLCall(c);
diff --git a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/model/GLCall.java b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/model/GLCall.java
index c868bde..98b1db6 100644
--- a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/model/GLCall.java
+++ b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/model/GLCall.java
@@ -101,4 +101,12 @@ public class GLCall {
     public Image getThumbnailImage() {
         return mThumbnailImage;
     }
+
+    public long getStartTime() {
+        return mMessage.getStartTime();
+    }
+
+    public int getDuration() {
+        return mMessage.getDuration();
+    }
 }
-- 
cgit v1.1