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.ide.eclipse.gltrace.GLProtoBuf.GLMessage;
     20 import com.android.ide.eclipse.gltrace.GLProtoBuf.GLMessage.Function;
     21 import com.android.ide.eclipse.gltrace.format.GLAPISpec;
     22 import com.android.ide.eclipse.gltrace.format.GLMessageFormatter;
     23 import com.android.ide.eclipse.gltrace.model.GLCall;
     24 import com.android.ide.eclipse.gltrace.model.GLFrame;
     25 import com.android.ide.eclipse.gltrace.model.GLTrace;
     26 import com.android.ide.eclipse.gltrace.state.transforms.StateTransformFactory;
     27 
     28 import org.eclipse.core.runtime.IProgressMonitor;
     29 import org.eclipse.jface.operation.IRunnableWithProgress;
     30 
     31 import java.io.File;
     32 import java.io.FileNotFoundException;
     33 import java.io.IOException;
     34 import java.io.RandomAccessFile;
     35 import java.lang.reflect.InvocationTargetException;
     36 import java.util.ArrayList;
     37 import java.util.Collections;
     38 import java.util.Comparator;
     39 import java.util.List;
     40 import java.util.Set;
     41 import java.util.TreeSet;
     42 
     43 public class TraceFileParserTask implements IRunnableWithProgress {
     44     private static final TraceFileReader sReader = new TraceFileReader();
     45 
     46     private static final GLMessageFormatter sGLMessageFormatter =
     47             new GLMessageFormatter(GLAPISpec.getSpecs());
     48 
     49     private String mTraceFilePath;
     50     private RandomAccessFile mFile;
     51 
     52     private List<GLCall> mGLCalls;
     53     private Set<Integer> mGLContextIds;
     54 
     55     private GLTrace mTrace;
     56 
     57     /**
     58      * Construct a GL Trace file parser.
     59      * @param path path to trace file
     60      */
     61     public TraceFileParserTask(String path) {
     62         try {
     63             mFile = new RandomAccessFile(path, "r"); //$NON-NLS-1$
     64         } catch (FileNotFoundException e) {
     65             throw new IllegalArgumentException(e);
     66         }
     67 
     68         mTraceFilePath = path;
     69         mGLCalls = new ArrayList<GLCall>();
     70         mGLContextIds = new TreeSet<Integer>();
     71     }
     72 
     73     private void addMessage(int index, long traceFileOffset, GLMessage msg, long startTime) {
     74         String formattedMsg;
     75         try {
     76             formattedMsg = sGLMessageFormatter.formatGLMessage(msg);
     77         } catch (Exception e) {
     78             formattedMsg = String.format("%s()", msg.getFunction().toString()); //$NON-NLS-1$
     79         }
     80 
     81         GLCall c = new GLCall(index,
     82                                 startTime,
     83                                 traceFileOffset,
     84                                 formattedMsg,
     85                                 msg.getFunction(),
     86                                 msg.hasFb(),
     87                                 msg.getContextId(),
     88                                 msg.getDuration(),
     89                                 msg.getThreadtime());
     90 
     91         addProperties(c, msg);
     92 
     93         try {
     94             c.setStateTransformations(StateTransformFactory.getTransformsFor(msg));
     95         } catch (Exception e) {
     96             c.setStateTransformationCreationError(e.getMessage());
     97             GlTracePlugin.getDefault().logMessage("Error while creating transformations for "
     98                                                         + c.toString() + ":");
     99             GlTracePlugin.getDefault().logMessage(e.getMessage());
    100         }
    101 
    102         mGLCalls.add(c);
    103         mGLContextIds.add(Integer.valueOf(c.getContextId()));
    104     }
    105 
    106     /** Save important values from the {@link GLMessage} in the {@link GLCall} as properties. */
    107     private void addProperties(GLCall c, GLMessage msg) {
    108         switch (msg.getFunction()) {
    109         case glPushGroupMarkerEXT:
    110             // void PushGroupMarkerEXT(sizei length, const char *marker);
    111             // save the marker name
    112             c.addProperty(GLCall.PROPERTY_MARKERNAME,
    113                     msg.getArgs(1).getCharValue(0).toStringUtf8());
    114             break;
    115         case glVertexAttribPointerData:
    116             // void glVertexAttribPointerData(GLuint indx, GLint size, GLenum type,
    117             //         GLboolean normalized, GLsizei stride, const GLvoid* ptr,
    118             //         int minIndex, int maxIndex)
    119             c.addProperty(GLCall.PROPERTY_VERTEX_ATTRIB_POINTER_SIZE,
    120                     Integer.valueOf(msg.getArgs(1).getIntValue(0)));
    121             c.addProperty(GLCall.PROPERTY_VERTEX_ATTRIB_POINTER_TYPE,
    122                     GLEnum.valueOf(msg.getArgs(2).getIntValue(0)));
    123             c.addProperty(GLCall.PROPERTY_VERTEX_ATTRIB_POINTER_DATA,
    124                     msg.getArgs(5).getRawBytes(0).toByteArray());
    125             break;
    126         default:
    127             break;
    128         }
    129     }
    130 
    131     /**
    132      * Parse the entire file and create a {@link GLTrace} object that can be retrieved
    133      * using {@link #getTrace()}.
    134      */
    135     @Override
    136     public void run(IProgressMonitor monitor) throws InvocationTargetException,
    137             InterruptedException {
    138         long fileLength;
    139         try {
    140             fileLength = mFile.length();
    141         } catch (IOException e1) {
    142             fileLength = 0;
    143         }
    144 
    145         monitor.beginTask("Parsing OpenGL Trace File",
    146                 fileLength > 0 ? 100 : IProgressMonitor.UNKNOWN);
    147 
    148         List<GLFrame> glFrames = null;
    149 
    150         try {
    151             GLMessage msg = null;
    152             int msgCount = 0;
    153             long filePointer = mFile.getFilePointer();
    154             int percentParsed = 0;
    155 
    156             // counters that maintain some statistics about the trace messages
    157             long minTraceStartTime = Long.MAX_VALUE;
    158 
    159             while ((msg = sReader.getMessageAtOffset(mFile, -1)) != null) {
    160                 if (minTraceStartTime > msg.getStartTime()) {
    161                     minTraceStartTime = msg.getStartTime();
    162                 }
    163 
    164                 addMessage(msgCount, filePointer, msg, msg.getStartTime() - minTraceStartTime);
    165 
    166                 filePointer = mFile.getFilePointer();
    167                 msgCount++;
    168 
    169                 if (monitor.isCanceled()) {
    170                     throw new InterruptedException();
    171                 }
    172 
    173                 if (fileLength > 0) {
    174                     int percentParsedNow = (int)((filePointer * 100) / fileLength);
    175                     monitor.worked(percentParsedNow - percentParsed);
    176                     percentParsed = percentParsedNow;
    177                 }
    178             }
    179 
    180             if (mGLContextIds.size() > 1) {
    181                 // if there are multiple contexts, then the calls may arrive at the
    182                 // host out of order. So we perform a sort based on the invocation time.
    183                 Collections.sort(mGLCalls, new Comparator<GLCall>() {
    184                     @Override
    185                     public int compare(GLCall c1, GLCall c2) {
    186                         long diff = (c1.getStartTime() - c2.getStartTime());
    187 
    188                         // We could return diff casted to an int. But in Java, casting
    189                         // from a long to an int truncates the bits and will not preserve
    190                         // the sign. So we resort to comparing the diff to 0 and returning
    191                         // the sign.
    192                         if (diff == 0) {
    193                             return 0;
    194                         } else if (diff > 0) {
    195                             return 1;
    196                         } else {
    197                             return -1;
    198                         }
    199                     }
    200                 });
    201 
    202                 // reassign indices after sorting
    203                 for (int i = 0; i < mGLCalls.size(); i++) {
    204                     mGLCalls.get(i).setIndex(i);
    205                 }
    206             }
    207 
    208             glFrames = createFrames(mGLCalls);
    209         } catch (Exception e) {
    210             throw new InvocationTargetException(e);
    211         } finally {
    212             try {
    213                 mFile.close();
    214             } catch (IOException e) {
    215                 // ignore exception while closing file
    216             }
    217             monitor.done();
    218         }
    219 
    220         File f = new File(mTraceFilePath);
    221         TraceFileInfo fileInfo = new TraceFileInfo(mTraceFilePath, f.length(), f.lastModified());
    222         mTrace = new GLTrace(fileInfo, glFrames, mGLCalls, new ArrayList<Integer>(mGLContextIds));
    223     }
    224 
    225     /** Assign GL calls to GL Frames. */
    226     private List<GLFrame> createFrames(List<GLCall> calls) {
    227         List<GLFrame> glFrames = new ArrayList<GLFrame>();
    228         int startCallIndex = 0;
    229         int frameIndex = 0;
    230 
    231         for (int i = 0; i < calls.size(); i++) {
    232             GLCall c = calls.get(i);
    233             if (c.getFunction() == Function.eglSwapBuffers) {
    234                 glFrames.add(new GLFrame(frameIndex, startCallIndex, i + 1));
    235                 startCallIndex = i + 1;
    236                 frameIndex++;
    237             }
    238         }
    239 
    240         // assign left over calls at the end to the last frame
    241         if (startCallIndex != mGLCalls.size()) {
    242             glFrames.add(new GLFrame(frameIndex, startCallIndex, mGLCalls.size()));
    243         }
    244 
    245         return glFrames;
    246     }
    247 
    248     /**
    249      * Retrieve the trace object constructed from messages in the trace file.
    250      */
    251     public GLTrace getTrace() {
    252         return mTrace;
    253     }
    254 }
    255