blob: b90c004b08b463f0c819ba0645991233138e849a [file] [log] [blame]
/*
* 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.android.ide.eclipse.gltrace.format.GLAPISpec;
import com.android.ide.eclipse.gltrace.format.GLMessageFormatter;
import com.android.ide.eclipse.gltrace.model.GLCall;
import com.android.ide.eclipse.gltrace.model.GLFrame;
import com.android.ide.eclipse.gltrace.model.GLTrace;
import com.android.ide.eclipse.gltrace.state.transforms.StateTransformFactory;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.operation.IRunnableWithProgress;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
public class TraceFileParserTask implements IRunnableWithProgress {
private static final TraceFileReader sReader = new TraceFileReader();
private static final GLMessageFormatter sGLMessageFormatter =
new GLMessageFormatter(GLAPISpec.getSpecs());
private String mTraceFilePath;
private RandomAccessFile mFile;
private List<GLCall> mGLCalls;
private Set<Integer> mGLContextIds;
private GLTrace mTrace;
/**
* Construct a GL Trace file parser.
* @param path path to trace file
*/
public TraceFileParserTask(String path) {
try {
mFile = new RandomAccessFile(path, "r"); //$NON-NLS-1$
} catch (FileNotFoundException e) {
throw new IllegalArgumentException(e);
}
mTraceFilePath = path;
mGLCalls = new ArrayList<GLCall>();
mGLContextIds = new TreeSet<Integer>();
}
private void addMessage(int index, long traceFileOffset, GLMessage msg, long startTime) {
String formattedMsg;
try {
formattedMsg = sGLMessageFormatter.formatGLMessage(msg);
} catch (Exception e) {
formattedMsg = String.format("%s()", msg.getFunction().toString()); //$NON-NLS-1$
}
GLCall c = new GLCall(index,
startTime,
traceFileOffset,
formattedMsg,
msg.getFunction(),
msg.hasFb(),
msg.getContextId(),
msg.getDuration(),
msg.getThreadtime());
addProperties(c, msg);
try {
c.setStateTransformations(StateTransformFactory.getTransformsFor(msg));
} catch (Exception e) {
c.setStateTransformationCreationError(e.getMessage());
GlTracePlugin.getDefault().logMessage("Error while creating transformations for "
+ c.toString() + ":");
GlTracePlugin.getDefault().logMessage(e.getMessage());
}
mGLCalls.add(c);
mGLContextIds.add(Integer.valueOf(c.getContextId()));
}
/** Save important values from the {@link GLMessage} in the {@link GLCall} as properties. */
private void addProperties(GLCall c, GLMessage msg) {
switch (msg.getFunction()) {
case glPushGroupMarkerEXT:
// void PushGroupMarkerEXT(sizei length, const char *marker);
// save the marker name
c.addProperty(GLCall.PROPERTY_MARKERNAME,
msg.getArgs(1).getCharValue(0).toStringUtf8());
break;
case glVertexAttribPointerData:
// void glVertexAttribPointerData(GLuint indx, GLint size, GLenum type,
// GLboolean normalized, GLsizei stride, const GLvoid* ptr,
// int minIndex, int maxIndex)
c.addProperty(GLCall.PROPERTY_VERTEX_ATTRIB_POINTER_SIZE,
Integer.valueOf(msg.getArgs(1).getIntValue(0)));
c.addProperty(GLCall.PROPERTY_VERTEX_ATTRIB_POINTER_TYPE,
GLEnum.valueOf(msg.getArgs(2).getIntValue(0)));
c.addProperty(GLCall.PROPERTY_VERTEX_ATTRIB_POINTER_DATA,
msg.getArgs(5).getRawBytes(0).toByteArray());
break;
default:
break;
}
}
/**
* Parse the entire file and create a {@link GLTrace} object that can be retrieved
* using {@link #getTrace()}.
*/
@Override
public void run(IProgressMonitor monitor) throws InvocationTargetException,
InterruptedException {
long fileLength;
try {
fileLength = mFile.length();
} catch (IOException e1) {
fileLength = 0;
}
monitor.beginTask("Parsing OpenGL Trace File",
fileLength > 0 ? 100 : IProgressMonitor.UNKNOWN);
List<GLFrame> glFrames = null;
try {
GLMessage msg = null;
int msgCount = 0;
long filePointer = mFile.getFilePointer();
int percentParsed = 0;
// counters that maintain some statistics about the trace messages
long minTraceStartTime = Long.MAX_VALUE;
while ((msg = sReader.getMessageAtOffset(mFile, -1)) != null) {
if (minTraceStartTime > msg.getStartTime()) {
minTraceStartTime = msg.getStartTime();
}
addMessage(msgCount, filePointer, msg, msg.getStartTime() - minTraceStartTime);
filePointer = mFile.getFilePointer();
msgCount++;
if (monitor.isCanceled()) {
throw new InterruptedException();
}
if (fileLength > 0) {
int percentParsedNow = (int)((filePointer * 100) / fileLength);
monitor.worked(percentParsedNow - percentParsed);
percentParsed = percentParsedNow;
}
}
if (mGLContextIds.size() > 1) {
// if there are multiple contexts, then the calls may arrive at the
// host out of order. So we perform a sort based on the invocation time.
Collections.sort(mGLCalls, new Comparator<GLCall>() {
@Override
public int compare(GLCall c1, GLCall c2) {
long diff = (c1.getStartTime() - c2.getStartTime());
// We could return diff casted to an int. But in Java, casting
// from a long to an int truncates the bits and will not preserve
// the sign. So we resort to comparing the diff to 0 and returning
// the sign.
if (diff == 0) {
return 0;
} else if (diff > 0) {
return 1;
} else {
return -1;
}
}
});
// reassign indices after sorting
for (int i = 0; i < mGLCalls.size(); i++) {
mGLCalls.get(i).setIndex(i);
}
}
glFrames = createFrames(mGLCalls);
} catch (Exception e) {
throw new InvocationTargetException(e);
} finally {
try {
mFile.close();
} catch (IOException e) {
// ignore exception while closing file
}
monitor.done();
}
File f = new File(mTraceFilePath);
TraceFileInfo fileInfo = new TraceFileInfo(mTraceFilePath, f.length(), f.lastModified());
mTrace = new GLTrace(fileInfo, glFrames, mGLCalls, new ArrayList<Integer>(mGLContextIds));
}
/** Assign GL calls to GL Frames. */
private List<GLFrame> createFrames(List<GLCall> calls) {
List<GLFrame> glFrames = new ArrayList<GLFrame>();
int startCallIndex = 0;
int frameIndex = 0;
for (int i = 0; i < calls.size(); i++) {
GLCall c = calls.get(i);
if (c.getFunction() == Function.eglSwapBuffers) {
glFrames.add(new GLFrame(frameIndex, startCallIndex, i + 1));
startCallIndex = i + 1;
frameIndex++;
}
}
// assign left over calls at the end to the last frame
if (startCallIndex != mGLCalls.size()) {
glFrames.add(new GLFrame(frameIndex, startCallIndex, mGLCalls.size()));
}
return glFrames;
}
/**
* Retrieve the trace object constructed from messages in the trace file.
*/
public GLTrace getTrace() {
return mTrace;
}
}