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