Merge "Rename fields of AudioSessionRef"
diff --git a/opengl/libs/GLES_trace/dev.make b/opengl/libs/GLES_trace/dev.make
index 1d89999..a46260c 100644
--- a/opengl/libs/GLES_trace/dev.make
+++ b/opengl/libs/GLES_trace/dev.make
@@ -6,10 +6,9 @@
 	aprotoc --cpp_out=src --java_out=java gltrace.proto
 	mv src/gltrace.pb.cc src/gltrace.pb.cpp
 
-# NOTE: $OUT should be defined in the shell by doing a "lunch <config>"
-# push updated files to device
-push:
-	adb push $(OUT)/system/lib/libGLESv2.so /system/lib/
-	adb push $(OUT)/system/lib/libGLESv1_CM.so /system/lib/
-	adb push $(OUT)/system/lib/libGLES_trace.so /system/lib/
-	adb push $(OUT)/system/lib/libEGL.so /system/lib/
+sync:
+	adb root
+	adb remount
+	adb shell stop
+	adb sync
+	adb shell start
diff --git a/opengl/libs/GLES_trace/gltrace.proto b/opengl/libs/GLES_trace/gltrace.proto
index 2893e6e..bbf3554 100644
--- a/opengl/libs/GLES_trace/gltrace.proto
+++ b/opengl/libs/GLES_trace/gltrace.proto
@@ -510,7 +510,7 @@
         eglGetSystemTimeNV = 2045;
 
         invalid = 3000;
-        frameBufferContents = 3001;
+        glVertexAttribPointerData = 3001;
     }
 
     // A GL call's return data and arguments are formatted into this DataType
diff --git a/opengl/libs/GLES_trace/src/gltrace.pb.cpp b/opengl/libs/GLES_trace/src/gltrace.pb.cpp
index d5f8180..d587c49 100644
--- a/opengl/libs/GLES_trace/src/gltrace.pb.cpp
+++ b/opengl/libs/GLES_trace/src/gltrace.pb.cpp
@@ -1018,7 +1018,7 @@
 const GLMessage_Function GLMessage::eglGetSystemTimeFrequencyNV;
 const GLMessage_Function GLMessage::eglGetSystemTimeNV;
 const GLMessage_Function GLMessage::invalid;
-const GLMessage_Function GLMessage::frameBufferContents;
+const GLMessage_Function GLMessage::glVertexAttribPointerData;
 const GLMessage_Function GLMessage::Function_MIN;
 const GLMessage_Function GLMessage::Function_MAX;
 const int GLMessage::Function_ARRAYSIZE;
diff --git a/opengl/libs/GLES_trace/src/gltrace.pb.h b/opengl/libs/GLES_trace/src/gltrace.pb.h
index a4fcbd3..0901be7 100644
--- a/opengl/libs/GLES_trace/src/gltrace.pb.h
+++ b/opengl/libs/GLES_trace/src/gltrace.pb.h
@@ -535,11 +535,11 @@
   GLMessage_Function_eglGetSystemTimeFrequencyNV = 2044,
   GLMessage_Function_eglGetSystemTimeNV = 2045,
   GLMessage_Function_invalid = 3000,
-  GLMessage_Function_frameBufferContents = 3001
+  GLMessage_Function_glVertexAttribPointerData = 3001
 };
 bool GLMessage_Function_IsValid(int value);
 const GLMessage_Function GLMessage_Function_Function_MIN = GLMessage_Function_glActiveTexture;
-const GLMessage_Function GLMessage_Function_Function_MAX = GLMessage_Function_frameBufferContents;
+const GLMessage_Function GLMessage_Function_Function_MAX = GLMessage_Function_glVertexAttribPointerData;
 const int GLMessage_Function_Function_ARRAYSIZE = GLMessage_Function_Function_MAX + 1;
 
 // ===================================================================
@@ -1351,7 +1351,7 @@
   static const Function eglGetSystemTimeFrequencyNV = GLMessage_Function_eglGetSystemTimeFrequencyNV;
   static const Function eglGetSystemTimeNV = GLMessage_Function_eglGetSystemTimeNV;
   static const Function invalid = GLMessage_Function_invalid;
-  static const Function frameBufferContents = GLMessage_Function_frameBufferContents;
+  static const Function glVertexAttribPointerData = GLMessage_Function_glVertexAttribPointerData;
   static inline bool Function_IsValid(int value) {
     return GLMessage_Function_IsValid(value);
   }
diff --git a/opengl/libs/GLES_trace/src/gltrace_context.cpp b/opengl/libs/GLES_trace/src/gltrace_context.cpp
index 65b7662..45dbb43 100644
--- a/opengl/libs/GLES_trace/src/gltrace_context.cpp
+++ b/opengl/libs/GLES_trace/src/gltrace_context.cpp
@@ -129,13 +129,14 @@
     return mPerContextState[c];
 }
 
-GLTraceContext::GLTraceContext(int id, GLTraceState *state, BufferedOutputStream *stream) {
-    mId = id;
-    mState = state;
-
+GLTraceContext::GLTraceContext(int id, GLTraceState *state, BufferedOutputStream *stream) :
+    mId(id),
+    mState(state),
+    mBufferedOutputStream(stream),
+    mElementArrayBuffers(DefaultKeyedVector<GLuint, ElementArrayBuffer*>(NULL))
+{
     fbcontents = fbcompressed = NULL;
     fbcontentsSize = 0;
-    mBufferedOutputStream = stream;
 }
 
 int GLTraceContext::getId() {
@@ -208,5 +209,74 @@
     }
 }
 
+void GLTraceContext::bindBuffer(GLuint bufferId, GLvoid *data, GLsizeiptr size) {
+    // free previously bound buffer if any
+    ElementArrayBuffer *oldBuffer = mElementArrayBuffers.valueFor(bufferId);
+    if (oldBuffer != NULL) {
+        delete oldBuffer;
+    }
+
+    mElementArrayBuffers.add(bufferId, new ElementArrayBuffer(data, size));
+}
+
+void GLTraceContext::getBuffer(GLuint bufferId, GLvoid **data, GLsizeiptr *size) {
+    ElementArrayBuffer *buffer = mElementArrayBuffers.valueFor(bufferId);
+    if (buffer == NULL) {
+        *data = NULL;
+        *size = 0;
+    } else {
+        *data = buffer->getBuffer();
+        *size = buffer->getSize();
+    }
+}
+
+void GLTraceContext::updateBufferSubData(GLuint bufferId, GLintptr offset, GLvoid *data,
+                                                            GLsizeiptr size) {
+    ElementArrayBuffer *buffer = mElementArrayBuffers.valueFor(bufferId);
+    if (buffer != NULL) {
+        buffer->updateSubBuffer(offset, data, size);
+    }
+}
+
+void GLTraceContext::deleteBuffer(GLuint bufferId) {
+    ElementArrayBuffer *buffer = mElementArrayBuffers.valueFor(bufferId);
+    if (buffer != NULL) {
+        delete buffer;
+        mElementArrayBuffers.removeItem(bufferId);
+    }
+}
+
+ElementArrayBuffer::ElementArrayBuffer(GLvoid *buf, GLsizeiptr size) {
+    mBuf = malloc(size);
+    mSize = size;
+
+    if (buf != NULL) {
+        memcpy(mBuf, buf, size);
+    }
+}
+
+ElementArrayBuffer::~ElementArrayBuffer() {
+    if (mBuf != NULL) {
+        free(mBuf);
+        mSize = 0;
+    }
+
+    mBuf = NULL;
+}
+
+void ElementArrayBuffer::updateSubBuffer(GLintptr offset, const GLvoid* data, GLsizeiptr size) {
+    if (offset + size <= mSize) {
+        memcpy((char*)mBuf + offset, data, size);
+    }
+}
+
+GLvoid *ElementArrayBuffer::getBuffer() {
+    return mBuf;
+}
+
+GLsizeiptr ElementArrayBuffer::getSize() {
+    return mSize;
+}
+
 }; // namespace gltrace
 }; // namespace android
diff --git a/opengl/libs/GLES_trace/src/gltrace_context.h b/opengl/libs/GLES_trace/src/gltrace_context.h
index 129116a..323cfdc 100644
--- a/opengl/libs/GLES_trace/src/gltrace_context.h
+++ b/opengl/libs/GLES_trace/src/gltrace_context.h
@@ -19,6 +19,7 @@
 
 #include <map>
 #include <pthread.h>
+#include <utils/KeyedVector.h>
 
 #include "hooks.h"
 #include "gltrace_transport.h"
@@ -32,6 +33,20 @@
 
 class GLTraceState;
 
+class ElementArrayBuffer {
+    GLvoid *mBuf;
+    GLsizeiptr mSize;
+
+public:
+    ElementArrayBuffer():mBuf(NULL), mSize(0) {}
+    ElementArrayBuffer(GLvoid *buf, GLsizeiptr size);
+    ~ElementArrayBuffer();
+
+    void updateSubBuffer(GLintptr offset, const GLvoid* data, GLsizeiptr size);
+    GLvoid *getBuffer();
+    GLsizeiptr getSize();
+};
+
 /** GL Trace Context info associated with each EGLContext */
 class GLTraceContext {
     int mId;                    /* unique context id */
@@ -43,6 +58,9 @@
 
     BufferedOutputStream *mBufferedOutputStream; /* stream where trace info is sent */
 
+    /* list of element array buffers in use. */
+    DefaultKeyedVector<GLuint, ElementArrayBuffer*> mElementArrayBuffers;
+
     void resizeFBMemory(unsigned minSize);
 public:
     gl_hooks_t *hooks;
@@ -53,6 +71,13 @@
     void getCompressedFB(void **fb, unsigned *fbsize,
                             unsigned *fbwidth, unsigned *fbheight,
                             FBBinding fbToRead);
+
+    // Methods to work with element array buffers
+    void bindBuffer(GLuint bufferId, GLvoid *data, GLsizeiptr size);
+    void getBuffer(GLuint bufferId, GLvoid **data, GLsizeiptr *size);
+    void updateBufferSubData(GLuint bufferId, GLintptr offset, GLvoid *data, GLsizeiptr size);
+    void deleteBuffer(GLuint bufferId);
+
     void traceGLMessage(GLMessage *msg);
 };
 
diff --git a/opengl/libs/GLES_trace/src/gltrace_fixup.cpp b/opengl/libs/GLES_trace/src/gltrace_fixup.cpp
index c69ba5e..3597b26 100644
--- a/opengl/libs/GLES_trace/src/gltrace_fixup.cpp
+++ b/opengl/libs/GLES_trace/src/gltrace_fixup.cpp
@@ -193,7 +193,7 @@
                         GLint border,
                         GLenum format,
                         GLenum type,
-                        const GLvoid *data); 
+                        const GLvoid *data);
     */
     int widthIndex = 3;
     int heightIndex = 4;
@@ -218,7 +218,7 @@
 }
 
 void fixup_glShaderSource(GLMessage *glmsg, void *pointersToFixup[]) {
-    /* void glShaderSource(GLuint shader, GLsizei count, const GLchar** string, 
+    /* void glShaderSource(GLuint shader, GLsizei count, const GLchar** string,
                                     const GLint* length) */
     GLMessage_DataType arg_count  = glmsg->args(1);
     GLMessage_DataType arg_lenp   = glmsg->args(3);
@@ -256,33 +256,13 @@
 }
 
 void fixup_glUniformMatrixGeneric(int matrixSize, GLMessage *glmsg, void *pointersToFixup[]) {
-    /* void glUniformMatrix?fv(GLint location, GLsizei count, GLboolean transpose, 
+    /* void glUniformMatrix?fv(GLint location, GLsizei count, GLboolean transpose,
                                                                 const GLfloat* value) */
     GLMessage_DataType arg_count  = glmsg->args(1);
     int n_matrices = arg_count.intvalue(0);
     fixup_glUniformGeneric(3, matrixSize * matrixSize * n_matrices, glmsg, pointersToFixup[0]);
 }
 
-void fixup_glBufferData(int sizeIndex, int dataIndex, GLMessage *glmsg, void *pointersToFixup[]) {
-    /* void glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid* data) */
-    /* void glBufferData(GLenum target, GLsizeiptr size, const GLvoid* data, GLenum usage) */
-    GLsizeiptr size = glmsg->args(sizeIndex).intvalue(0);
-
-    GLMessage_DataType *arg_datap = glmsg->mutable_args(dataIndex);
-    GLvoid *datap = (GLvoid *) pointersToFixup[0];
-
-    if (datap == NULL) {
-        // glBufferData can be called with a NULL data pointer
-        return;
-    }
-
-    arg_datap->set_type(GLMessage::DataType::VOID);
-    arg_datap->set_isarray(true);
-    arg_datap->clear_intvalue();
-
-    arg_datap->add_rawbytes(datap, size);
-}
-
 void fixup_glGenGeneric(GLMessage *glmsg, void *pointersToFixup[]) {
     /* void glGen*(GLsizei n, GLuint * buffers); */
     GLMessage_DataType arg_n  = glmsg->args(0);
@@ -375,7 +355,7 @@
     }
 }
 
-void fixup_glGetActiveAttribOrUniform(GLTraceContext *context, GLMessage *glmsg, 
+void fixup_glGetActiveAttribOrUniform(GLTraceContext *context, GLMessage *glmsg,
                                                                 void *pointersToFixup[]) {
     /* void glGetActiveAttrib(GLuint program, GLuint index, GLsizei bufsize,
                 GLsizei* length, GLint* size, GLenum* type, GLchar* name); */
@@ -400,6 +380,303 @@
     arg_location->add_intvalue(location);
 }
 
+GLint glGetInteger(GLTraceContext *context, GLenum param) {
+    GLint x;
+    context->hooks->gl.glGetIntegerv(param, &x);
+    return x;
+}
+
+GLint glGetVertexAttrib(GLTraceContext *context, GLuint index, GLenum pname) {
+    GLint x;
+    context->hooks->gl.glGetVertexAttribiv(index, pname, &x);
+    return x;
+}
+
+bool isUsingArrayBuffers(GLTraceContext *context) {
+    return glGetInteger(context, GL_ARRAY_BUFFER_BINDING) != 0;
+}
+
+bool isUsingElementArrayBuffers(GLTraceContext *context) {
+    return glGetInteger(context, GL_ELEMENT_ARRAY_BUFFER_BINDING) != 0;
+}
+
+/** Copy @len bytes of data from @src into the @dataIndex'th argument of the message. */
+void addGlBufferData(GLMessage *glmsg, int dataIndex, GLvoid *src, GLsizeiptr len) {
+    GLMessage_DataType *arg_datap = glmsg->mutable_args(dataIndex);
+    arg_datap->set_type(GLMessage::DataType::VOID);
+    arg_datap->set_isarray(true);
+    arg_datap->clear_intvalue();
+    arg_datap->add_rawbytes(src, len);
+}
+
+void fixup_glBufferData(GLTraceContext *context, GLMessage *glmsg, void *pointersToFixup[]) {
+    /* void glBufferData(GLenum target, GLsizeiptr size, const GLvoid* data, GLenum usage) */
+    GLsizeiptr size = glmsg->args(1).intvalue(0);
+    GLvoid *datap = (GLvoid *) pointersToFixup[0];
+
+    // Save element array buffers for future use to fixup glVertexAttribPointers
+    // when a glDrawElements() call is performed.
+    GLenum target = glmsg->args(0).intvalue(0);
+    if (target == GL_ELEMENT_ARRAY_BUFFER) {
+        GLint bufferId = glGetInteger(context, GL_ELEMENT_ARRAY_BUFFER_BINDING);
+        context->bindBuffer(bufferId, datap, size);
+    }
+
+    // add buffer data to the protobuf message
+    if (datap != NULL) {
+        addGlBufferData(glmsg, 2, datap, size);
+    }
+}
+
+void fixup_glBufferSubData(GLTraceContext *context, GLMessage *glmsg, void *pointersToFixup[]) {
+    /* void glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid* data) */
+    GLenum target = glmsg->args(0).intvalue(0);
+    GLintptr offset = glmsg->args(1).intvalue(0);
+    GLsizeiptr size = glmsg->args(2).intvalue(0);
+    GLvoid *datap = (GLvoid *) pointersToFixup[0];
+    if (target == GL_ELEMENT_ARRAY_BUFFER) {
+        GLint bufferId = glGetInteger(context, GL_ELEMENT_ARRAY_BUFFER_BINDING);
+        context->updateBufferSubData(bufferId, offset, datap, size);
+    }
+
+    // add buffer data to the protobuf message
+    addGlBufferData(glmsg, 3, datap, size);
+}
+
+/** Obtain the size of each vertex attribute. */
+int vertexAttribSize(GLenum type, GLsizei numComponents) {
+    int sizePerComponent;
+
+    switch(type) {
+    case GL_BYTE:
+    case GL_UNSIGNED_BYTE:
+        sizePerComponent = 1;
+        break;
+    case GL_SHORT:
+    case GL_UNSIGNED_SHORT:
+        sizePerComponent = 2;
+        break;
+    case GL_FIXED:
+    case GL_FLOAT:
+    default:
+        sizePerComponent = 4;
+        break;
+    }
+
+    return sizePerComponent * numComponents;
+}
+
+/** Create and send a glVertexAttribPointerData trace message to the host. */
+void trace_glVertexAttribPointerData(GLTraceContext *context,
+                    GLuint indx, GLint size, GLenum type,
+                    GLboolean normalized, GLsizei stride, const GLvoid* ptr,
+                    GLuint minIndex, GLuint maxIndex, nsecs_t startTime) {
+    /* void glVertexAttribPointerData(GLuint indx, GLint size, GLenum type,
+                    GLboolean normalized, GLsizei stride, const GLvoid* ptr,
+                    int minIndex, int maxIndex) */
+    GLMessage glmsg;
+    GLTraceContext *glContext = context;
+
+    glmsg.set_function(GLMessage::glVertexAttribPointerData);
+
+    // copy argument indx
+    GLMessage_DataType *arg_indx = glmsg.add_args();
+    arg_indx->set_isarray(false);
+    arg_indx->set_type(GLMessage::DataType::INT);
+    arg_indx->add_intvalue(indx);
+
+    // copy argument size
+    GLMessage_DataType *arg_size = glmsg.add_args();
+    arg_size->set_isarray(false);
+    arg_size->set_type(GLMessage::DataType::INT);
+    arg_size->add_intvalue(size);
+
+    // copy argument type
+    GLMessage_DataType *arg_type = glmsg.add_args();
+    arg_type->set_isarray(false);
+    arg_type->set_type(GLMessage::DataType::ENUM);
+    arg_type->add_intvalue((int)type);
+
+    // copy argument normalized
+    GLMessage_DataType *arg_normalized = glmsg.add_args();
+    arg_normalized->set_isarray(false);
+    arg_normalized->set_type(GLMessage::DataType::BOOL);
+    arg_normalized->add_boolvalue(normalized);
+
+    // copy argument stride
+    GLMessage_DataType *arg_stride = glmsg.add_args();
+    arg_stride->set_isarray(false);
+    arg_stride->set_type(GLMessage::DataType::INT);
+    arg_stride->add_intvalue(stride);
+
+    // copy argument ptr
+    GLMessage_DataType *arg_ptr = glmsg.add_args();
+    arg_ptr->set_isarray(true);
+    arg_ptr->set_type(GLMessage::DataType::BYTE);
+    int perVertexSize = vertexAttribSize(type, size);
+    GLchar *p = (GLchar*) ptr;
+    std::string data;
+    for (GLuint i = minIndex; i < maxIndex; i++) {
+        data.append(p, perVertexSize);
+        p += stride == 0 ? perVertexSize : stride;
+    }
+    arg_ptr->add_rawbytes(data);
+
+    // copy argument min index
+    GLMessage_DataType *arg_min = glmsg.add_args();
+    arg_min->set_isarray(false);
+    arg_min->set_type(GLMessage::DataType::INT);
+    arg_min->add_intvalue(minIndex);
+
+    // copy argument max index
+    GLMessage_DataType *arg_max = glmsg.add_args();
+    arg_max->set_isarray(false);
+    arg_max->set_type(GLMessage::DataType::INT);
+    arg_max->add_intvalue(maxIndex);
+
+    glmsg.set_context_id(context->getId());
+    glmsg.set_start_time(startTime);
+    glmsg.set_threadtime(0);
+    glmsg.set_duration(0);
+
+    context->traceGLMessage(&glmsg);
+}
+
+void findMinAndMaxIndices(GLvoid *indices, GLsizei count, GLenum type,
+                            GLuint *minIndex, GLuint *maxIndex) {
+    GLuint index;
+    *minIndex = UINT_MAX;
+    *maxIndex = 0;
+
+    if (indices == NULL) {
+        return;
+    }
+
+    for (GLsizei i = 0; i < count; i++) {
+        if (type == GL_UNSIGNED_BYTE) {
+            index = *((GLubyte*) indices + i);
+        } else {
+            index = *((GLushort*) indices + i);
+        }
+
+        if (index < *minIndex) *minIndex = index;
+        if (index > *maxIndex) *maxIndex = index;
+    }
+}
+
+void trace_VertexAttribPointerData(GLTraceContext *context,
+                            GLuint minIndex, GLuint maxIndex, nsecs_t time) {
+    GLuint maxAttribs = glGetInteger(context, GL_MAX_VERTEX_ATTRIBS);
+    for (GLuint index = 0; index < maxAttribs; index++) {
+        if (!glGetVertexAttrib(context, index, GL_VERTEX_ATTRIB_ARRAY_ENABLED)) {
+            // vertex array disabled
+            continue;
+        }
+
+        if (glGetVertexAttrib(context, index, GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING)) {
+            // vbo
+            continue;
+        }
+
+        GLint size = glGetVertexAttrib(context, index, GL_VERTEX_ATTRIB_ARRAY_SIZE);
+        GLenum type = glGetVertexAttrib(context, index, GL_VERTEX_ATTRIB_ARRAY_TYPE);
+        GLboolean norm = glGetVertexAttrib(context, index, GL_VERTEX_ATTRIB_ARRAY_NORMALIZED);
+        GLsizei stride = glGetVertexAttrib(context, index, GL_VERTEX_ATTRIB_ARRAY_STRIDE);
+        GLvoid* ptr;
+        context->hooks->gl.glGetVertexAttribPointerv(index, GL_VERTEX_ATTRIB_ARRAY_POINTER, &ptr);
+
+        trace_glVertexAttribPointerData(context,
+                    index, size, type, norm, stride, ptr,
+                    minIndex, maxIndex, time);
+    }
+}
+
+void trace_VertexAttribPointerDataForGlDrawArrays(GLTraceContext *context, GLMessage *glmsg) {
+    /* void glDrawArrays(GLenum mode, GLint first, GLsizei count) */
+    GLsizei count = glmsg->args(2).intvalue(0);
+
+    // Vertex attrib pointer data patchup calls should appear as if
+    // they occurred right before the draw call.
+    nsecs_t time = glmsg->start_time() - 1;
+
+    trace_VertexAttribPointerData(context, 0, count, time);
+}
+
+void trace_VertexAttribPointerDataForGlDrawElements(GLTraceContext *context, GLMessage *glmsg,
+                            GLvoid *indices) {
+    /* void glDrawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid* indices) */
+    GLsizei count = glmsg->args(1).intvalue(0);
+    GLenum type = glmsg->args(2).intvalue(0);
+    GLuint index;
+
+    GLuint minIndex, maxIndex;
+
+    // The index buffer is either passed in as an argument to the glDrawElements() call,
+    // or it is stored in the current GL_ELEMENT_ARRAY_BUFFER.
+    GLvoid *indexBuffer;
+    if (isUsingElementArrayBuffers(context)) {
+        GLsizeiptr eaBufferSize;
+        GLuint bufferId = glGetInteger(context, GL_ELEMENT_ARRAY_BUFFER_BINDING);
+        context->getBuffer(bufferId, &indexBuffer, &eaBufferSize);
+    } else {
+        indexBuffer = indices;
+    }
+
+    // Rather than sending vertex attribute data that corresponds to the indices
+    // being drawn, we send the vertex attribute data for the entire range of
+    // indices being drawn, including the ones not drawn. The min & max indices
+    // provide the range of indices being drawn.
+    findMinAndMaxIndices(indexBuffer, count, type, &minIndex, &maxIndex);
+
+    // Vertex attrib pointer data patchup calls should appear as if
+    // they occurred right before the draw call.
+    nsecs_t time = glmsg->start_time() - 1;
+
+    trace_VertexAttribPointerData(context, minIndex, maxIndex + 1, time);
+}
+
+void fixup_glDrawArrays(GLTraceContext *context, GLMessage *glmsg) {
+    // Trace all vertex attribute data stored in client space.
+    trace_VertexAttribPointerDataForGlDrawArrays(context, glmsg);
+
+    // Attach the FB if requested
+    if (context->getGlobalTraceState()->shouldCollectFbOnGlDraw()) {
+        fixup_addFBContents(context, glmsg, CURRENTLY_BOUND_FB);
+    }
+}
+
+void fixup_glDrawElements(GLTraceContext *context, GLMessage *glmsg, void *pointersToFixup[]) {
+    /* void glDrawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid* indices) */
+    GLvoid *indices = pointersToFixup[0];
+    GLenum type = glmsg->args(2).intvalue(0);
+    GLsizei count = glmsg->args(1).intvalue(0);
+    GLuint index;
+
+    // Trace all vertex attribute data stored in client space.
+    trace_VertexAttribPointerDataForGlDrawElements(context, glmsg, indices);
+
+    // Fixup indices argument
+    if (!isUsingElementArrayBuffers(context)) {
+        GLMessage_DataType *arg_indices = glmsg->mutable_args(3);
+        arg_indices->set_isarray(true);
+        arg_indices->clear_intvalue();
+        arg_indices->set_type(GLMessage::DataType::INT);
+        for (GLsizei i = 0; i < count; i++) {
+            if (type == GL_UNSIGNED_BYTE) {
+                index = *((GLubyte*) indices + i);
+            } else {
+                index = *((GLushort*) indices + i);
+            }
+            arg_indices->add_intvalue(index);
+        }
+    }
+
+    // Attach the FB if requested
+    if (context->getGlobalTraceState()->shouldCollectFbOnGlDraw()) {
+        fixup_addFBContents(context, glmsg, CURRENTLY_BOUND_FB);
+    }
+}
+
 void fixupGLMessage(GLTraceContext *context, nsecs_t wallStart, nsecs_t wallEnd,
                                              nsecs_t threadStart, nsecs_t threadEnd,
                                              GLMessage *glmsg, void *pointersToFixup[]) {
@@ -438,8 +715,8 @@
         /* void glBindAttribLocation(GLuint program, GLuint index, const GLchar* name); */
         fixup_CStringPtr(2, glmsg, pointersToFixup[0]);
         break;
-    case GLMessage::glGetAttribLocation:  
-    case GLMessage::glGetUniformLocation: 
+    case GLMessage::glGetAttribLocation:
+    case GLMessage::glGetUniformLocation:
         /* int glGetAttribLocation(GLuint program, const GLchar* name) */
         /* int glGetUniformLocation(GLuint program, const GLchar* name) */
         fixup_CStringPtr(1, glmsg, pointersToFixup[0]);
@@ -526,23 +803,19 @@
         break;
     case GLMessage::glBufferData:
         /* void glBufferData(GLenum target, GLsizeiptr size, const GLvoid* data, GLenum usage) */
-        fixup_glBufferData(1, 2, glmsg, pointersToFixup);
+        fixup_glBufferData(context, glmsg, pointersToFixup);
         break;
     case GLMessage::glBufferSubData:
         /* void glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid* data) */
-        fixup_glBufferData(2, 3, glmsg, pointersToFixup);
+        fixup_glBufferSubData(context, glmsg, pointersToFixup);
         break;
     case GLMessage::glDrawArrays:
         /* void glDrawArrays(GLenum mode, GLint first, GLsizei count) */
-        if (context->getGlobalTraceState()->shouldCollectFbOnGlDraw()) {
-            fixup_addFBContents(context, glmsg, CURRENTLY_BOUND_FB);
-        }
+        fixup_glDrawArrays(context, glmsg);
         break;
     case GLMessage::glDrawElements:
         /* void glDrawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid* indices) */
-        if (context->getGlobalTraceState()->shouldCollectFbOnGlDraw()) {
-            fixup_addFBContents(context, glmsg, CURRENTLY_BOUND_FB);
-        }
+        fixup_glDrawElements(context, glmsg, pointersToFixup);
         break;
     case GLMessage::glPushGroupMarkerEXT:
         /* void PushGroupMarkerEXT(sizei length, const char *marker); */