com.android.ide.eclipse.gltrace.TraceFileParserTask.java Source code

Java tutorial

Introduction

Here is the source code for com.android.ide.eclipse.gltrace.TraceFileParserTask.java

Source

/*
 * 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;
    }
}