gltrace: transport buffering and context management

This patch adds two improvements:
1. Protobuf messages are buffered and sent in chunks.
2. Multiple EGL contexts are handled properly: Corresponding
to each EGLContext, a GLTraceContext with a unique ID is created.
On eglMakeCurrent, the appropriate GLTraceContext is set and is
used while tracing subsequent GL Calls in that thread.

Change-Id: I34076376d3e5af205c87c7396ea47659844abd6e
This commit is contained in:
Siva Velusamy
2011-12-14 12:19:56 -08:00
parent efc1265402
commit 1e81e710fc
14 changed files with 1077 additions and 1218 deletions

View File

@@ -84,6 +84,9 @@ void initEglTraceLevel() {
sEGLTraceLevel = propertyLevel > applicationLevel ? propertyLevel : applicationLevel;
property_get("debug.egl.debug_proc", value, "");
if (strlen(value) == 0)
return;
long pid = getpid();
char procPath[128] = {};
sprintf(procPath, "/proc/%ld/cmdline", pid);
@@ -91,8 +94,11 @@ void initEglTraceLevel() {
if (file) {
char cmdline[256] = {};
if (fgets(cmdline, sizeof(cmdline) - 1, file)) {
if (!strcmp(value, cmdline))
if (!strncmp(value, cmdline, strlen(value))) {
// set EGL debug if the "debug.egl.debug_proc" property
// matches the prefix of this application's command line
gEGLDebugLevel = 1;
}
}
fclose(file);
}

View File

@@ -662,7 +662,7 @@ EGLBoolean eglMakeCurrent( EGLDisplay dpy, EGLSurface draw,
egl_tls_t::setContext(ctx);
#if EGL_TRACE
if (gEGLDebugLevel > 0)
GLTrace_eglMakeCurrent(c->version, c->cnx->hooks[c->version]);
GLTrace_eglMakeCurrent(c->version, c->cnx->hooks[c->version], ctx);
#endif
_c.acquire();
_r.acquire();

File diff suppressed because it is too large Load Diff

View File

@@ -15,6 +15,7 @@
*/
#include <pthread.h>
#include <cutils/log.h>
extern "C" {
#include "liblzf/lzf.h"
@@ -42,12 +43,8 @@ void setGLTraceContext(GLTraceContext *c) {
pthread_setspecific(sTLSKey, c);
}
void initContext(unsigned version, gl_hooks_t *hooks) {
void setupTraceContextThreadSpecific(GLTraceContext *context) {
pthread_once(&sPthreadOnceKey, createTLSKey);
GLTraceContext *context = new GLTraceContext();
context->hooks = hooks;
setGLTraceContext(context);
}
@@ -59,9 +56,47 @@ void releaseContext() {
}
}
GLTraceContext::GLTraceContext() {
GLTraceState::GLTraceState(TCPStream *stream) {
mTraceContextIds = 0;
mStream = stream;
}
GLTraceState::~GLTraceState() {
if (mStream) {
mStream->closeStream();
mStream = NULL;
}
}
TCPStream *GLTraceState::getStream() {
return mStream;
}
GLTraceContext *GLTraceState::createTraceContext(int version, EGLContext eglContext) {
int id = __sync_fetch_and_add(&mTraceContextIds, 1);
const size_t DEFAULT_BUFFER_SIZE = 8192;
BufferedOutputStream *stream = new BufferedOutputStream(mStream, DEFAULT_BUFFER_SIZE);
GLTraceContext *traceContext = new GLTraceContext(id, stream);
mPerContextState[eglContext] = traceContext;
return traceContext;
}
GLTraceContext *GLTraceState::getTraceContext(EGLContext c) {
return mPerContextState[c];
}
GLTraceContext::GLTraceContext(int id, BufferedOutputStream *stream) {
mId = id;
fbcontents = fbcompressed = NULL;
fbcontentsSize = 0;
mBufferedOutputStream = stream;
}
int GLTraceContext::getId() {
return mId;
}
void GLTraceContext::resizeFBMemory(unsigned minSize) {
@@ -115,5 +150,16 @@ void GLTraceContext::getCompressedFB(void **fb, unsigned *fbsize, unsigned *fbwi
*fbheight = viewport[3];
}
void GLTraceContext::traceGLMessage(GLMessage *msg) {
mBufferedOutputStream->send(msg);
GLMessage_Function func = msg->function();
if (func == GLMessage::eglSwapBuffers
|| func == GLMessage::glDrawArrays
|| func == GLMessage::glDrawElements) {
mBufferedOutputStream->flush();
}
}
}; // namespace gltrace
}; // namespace android

View File

@@ -17,7 +17,10 @@
#ifndef __GLTRACE_CONTEXT_H_
#define __GLTRACE_CONTEXT_H_
#include <map>
#include "hooks.h"
#include "gltrace_transport.h"
namespace android {
namespace gltrace {
@@ -26,24 +29,45 @@ using ::android::gl_hooks_t;
enum FBBinding {CURRENTLY_BOUND_FB, FB0};
/** GL Trace Context info associated with each EGLContext */
class GLTraceContext {
int mId; /* unique context id */
void *fbcontents; /* memory area to read framebuffer contents */
void *fbcompressed; /* destination for lzf compressed framebuffer */
unsigned fbcontentsSize; /* size of fbcontents & fbcompressed buffers */
BufferedOutputStream *mBufferedOutputStream; /* stream where trace info is sent */
void resizeFBMemory(unsigned minSize);
public:
gl_hooks_t *hooks;
GLTraceContext();
GLTraceContext(int id, BufferedOutputStream *stream);
int getId();
void getCompressedFB(void **fb, unsigned *fbsize,
unsigned *fbwidth, unsigned *fbheight,
FBBinding fbToRead);
void traceGLMessage(GLMessage *msg);
};
/** Per process trace state. */
class GLTraceState {
int mTraceContextIds;
TCPStream *mStream;
std::map<EGLContext, GLTraceContext*> mPerContextState;
public:
GLTraceState(TCPStream *stream);
~GLTraceState();
GLTraceContext *createTraceContext(int version, EGLContext c);
GLTraceContext *getTraceContext(EGLContext c);
TCPStream *getStream();
};
void setupTraceContextThreadSpecific(GLTraceContext *context);
GLTraceContext *getGLTraceContext();
void setGLTraceContext(GLTraceContext *c);
void initContext(unsigned version, gl_hooks_t *hooks);
void releaseContext();
};

View File

@@ -24,16 +24,54 @@
namespace android {
namespace gltrace {
void GLTrace_eglCreateContext(int version, int contextId) {
GLMessage glmessage;
GLTraceContext *glContext = getGLTraceContext();
glmessage.set_context_id(contextId);
glmessage.set_function(GLMessage::eglCreateContext);
// copy argument version
GLMessage_DataType *arg_version = glmessage.add_args();
arg_version->set_isarray(false);
arg_version->set_type(GLMessage::DataType::INT);
arg_version->add_intvalue(version);
// copy argument context
GLMessage_DataType *arg_context = glmessage.add_args();
arg_context->set_isarray(false);
arg_context->set_type(GLMessage::DataType::INT);
arg_context->add_intvalue(contextId);
glContext->traceGLMessage(&glmessage);
}
void GLTrace_eglMakeCurrent(int contextId) {
GLMessage glmessage;
GLTraceContext *glContext = getGLTraceContext();
glmessage.set_context_id(contextId);
glmessage.set_function(GLMessage::eglMakeCurrent);
// copy argument context
GLMessage_DataType *arg_context = glmessage.add_args();
arg_context->set_isarray(false);
arg_context->set_type(GLMessage::DataType::INT);
arg_context->add_intvalue(contextId);
glContext->traceGLMessage(&glmessage);
}
void GLTrace_eglSwapBuffers(void *dpy, void *draw) {
GLMessage glmessage;
GLTraceContext *glContext = getGLTraceContext();
glmessage.set_context_id(1);
glmessage.set_context_id(glContext->getId());
glmessage.set_function(GLMessage::eglSwapBuffers);
// read FB0 since that is what is displayed on the screen
fixup_addFBContents(&glmessage, FB0);
traceGLMessage(&glmessage);
fixup_addFBContents(glContext, &glmessage, FB0);
glContext->traceGLMessage(&glmessage);
}
};

View File

@@ -20,6 +20,8 @@
namespace android {
namespace gltrace {
void GLTrace_eglCreateContext(int version, int contextId);
void GLTrace_eglMakeCurrent(int contextId);
void GLTrace_eglSwapBuffers(void *dpy, void *draw);
};

View File

@@ -28,17 +28,11 @@
namespace android {
void GLTrace_eglMakeCurrent(const unsigned version, gl_hooks_t *hooks) {
gltrace::initContext(version, hooks);
}
using gltrace::GLTraceState;
using gltrace::GLTraceContext;
using gltrace::TCPStream;
void GLTrace_eglReleaseThread() {
gltrace::releaseContext();
}
void GLTrace_eglCreateContext(int version, EGLContext c) {
// TODO
}
static GLTraceState *sGLTraceState;
void GLTrace_start() {
char value[PROPERTY_VALUE_MAX];
@@ -46,19 +40,53 @@ void GLTrace_start() {
property_get("debug.egl.debug_port", value, "5039");
const unsigned short port = (unsigned short)atoi(value);
gltrace::startServer(port);
int clientSocket = gltrace::acceptClientConnection(port);
if (clientSocket < 0) {
LOGE("Error creating GLTrace server socket. Quitting application.");
exit(-1);
}
// create communication channel to the host
TCPStream *stream = new TCPStream(clientSocket);
// initialize tracing state
sGLTraceState = new GLTraceState(stream);
}
void GLTrace_stop() {
gltrace::stopServer();
delete sGLTraceState;
sGLTraceState = NULL;
}
gl_hooks_t *GLTrace_getGLHooks() {
return gltrace::getGLHooks();
void GLTrace_eglCreateContext(int version, EGLContext c) {
// update trace state for new EGL context
GLTraceContext *traceContext = sGLTraceState->createTraceContext(version, c);
gltrace::setupTraceContextThreadSpecific(traceContext);
// trace command through to the host
gltrace::GLTrace_eglCreateContext(version, traceContext->getId());
}
void GLTrace_eglMakeCurrent(const unsigned version, gl_hooks_t *hooks, EGLContext c) {
// setup per context state
GLTraceContext *traceContext = sGLTraceState->getTraceContext(c);
traceContext->hooks = hooks;
gltrace::setupTraceContextThreadSpecific(traceContext);
// trace command through to the host
gltrace::GLTrace_eglMakeCurrent(traceContext->getId());
}
void GLTrace_eglReleaseThread() {
gltrace::releaseContext();
}
void GLTrace_eglSwapBuffers(void *dpy, void *draw) {
gltrace::GLTrace_eglSwapBuffers(dpy, draw);
}
gl_hooks_t *GLTrace_getGLHooks() {
return gltrace::getGLHooks();
}
}

View File

@@ -96,10 +96,10 @@ void fixup_glGetString(GLMessage *glmsg) {
}
/* Add the contents of the framebuffer to the protobuf message */
void fixup_addFBContents(GLMessage *glmsg, FBBinding fbToRead) {
void fixup_addFBContents(GLTraceContext *context, GLMessage *glmsg, FBBinding fbToRead) {
void *fbcontents;
unsigned fbsize, fbwidth, fbheight;
getGLTraceContext()->getCompressedFB(&fbcontents, &fbsize, &fbwidth, &fbheight, fbToRead);
context->getCompressedFB(&fbcontents, &fbsize, &fbwidth, &fbheight, fbToRead);
GLMessage_FrameBuffer *fb = glmsg->mutable_fb();
fb->set_width(fbwidth);
@@ -240,7 +240,11 @@ void fixup_glGetFloatv(GLMessage *glmsg) {
arg_params->add_floatvalue(*src);
}
void fixupGLMessage(GLMessage *glmsg) {
void fixupGLMessage(GLTraceContext *context, GLMessage *glmsg) {
// for all messages, set the current context id
glmsg->set_context_id(context->getId());
// do any custom message dependent processing
switch (glmsg->function()) {
case GLMessage::glGenBuffers: /* void glGenBuffers(GLsizei n, GLuint * buffers); */
case GLMessage::glGenFramebuffers: /* void glGenFramebuffers(GLsizei n, GLuint * buffers); */
@@ -299,7 +303,7 @@ void fixupGLMessage(GLMessage *glmsg) {
case GLMessage::glDrawElements:
/* void glDrawArrays(GLenum mode, GLint first, GLsizei count) */
/* void glDrawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid* indices) */
fixup_addFBContents(glmsg, CURRENTLY_BOUND_FB);
fixup_addFBContents(context, glmsg, CURRENTLY_BOUND_FB);
break;
default:
break;

View File

@@ -23,8 +23,8 @@
namespace android {
namespace gltrace {
void fixupGLMessage(GLMessage *message);
void fixup_addFBContents(GLMessage *message, FBBinding fbToRead);
void fixupGLMessage(GLTraceContext *curContext, GLMessage *message);
void fixup_addFBContents(GLTraceContext *curContext, GLMessage *message, FBBinding fbToRead);
};
};

View File

@@ -28,85 +28,119 @@
namespace android {
namespace gltrace {
int gServerSocket, gClientSocket;
void startServer(int port) {
if (gServerSocket > 0) {
ALOGD("startServer: server socket already open!");
return;
}
gServerSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (gServerSocket < 0) {
int acceptClientConnection(int serverPort) {
int serverSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (serverSocket < 0) {
LOGE("Error (%d) while creating socket. Check if app has network permissions.",
gServerSocket);
exit(-1);
serverSocket);
return -1;
}
struct sockaddr_in server, client;
server.sin_family = AF_INET;
server.sin_addr.s_addr = htonl(INADDR_ANY);
server.sin_port = htons(port);
server.sin_port = htons(serverPort);
socklen_t sockaddr_len = sizeof(sockaddr_in);
if (bind(gServerSocket, (struct sockaddr *) &server, sizeof(server)) < 0) {
close(gServerSocket);
if (bind(serverSocket, (struct sockaddr *) &server, sizeof(server)) < 0) {
close(serverSocket);
LOGE("Failed to bind the server socket");
exit(-1);
return -1;
}
if (listen(gServerSocket, 1) < 0) {
close(gServerSocket);
if (listen(serverSocket, 1) < 0) {
close(serverSocket);
LOGE("Failed to listen on server socket");
exit(-1);
return -1;
}
ALOGD("startServer: server started on %d", port);
ALOGD("gltrace::waitForClientConnection: server listening @ port %d", serverPort);
/* Wait for client connection */
if ((gClientSocket = accept(gServerSocket, (struct sockaddr *)&client, &sockaddr_len)) < 0) {
close(gServerSocket);
int clientSocket = accept(serverSocket, (struct sockaddr *)&client, &sockaddr_len);
if (clientSocket < 0) {
close(serverSocket);
LOGE("Failed to accept client connection");
exit(-1);
return -1;
}
ALOGD("startServer: client connected: %s", inet_ntoa(client.sin_addr));
ALOGD("gltrace::waitForClientConnection: client connected: %s", inet_ntoa(client.sin_addr));
// do not accept any more incoming connections
close(serverSocket);
return clientSocket;
}
void stopServer() {
if (gServerSocket > 0) {
close(gServerSocket);
close(gClientSocket);
gServerSocket = gClientSocket = 0;
TCPStream::TCPStream(int socket) {
mSocket = socket;
pthread_mutex_init(&mSocketWriteMutex, NULL);
}
TCPStream::~TCPStream() {
pthread_mutex_destroy(&mSocketWriteMutex);
}
void TCPStream::closeStream() {
if (mSocket > 0) {
close(mSocket);
mSocket = 0;
}
}
/** Send GLMessage to the receiver on the host. */
void traceGLMessage(GLMessage *call) {
if (gClientSocket <= 0) {
LOGE("traceGLMessage: Attempt to send while client connection is not established");
return;
int TCPStream::send(void *buf, size_t len) {
if (mSocket <= 0) {
return -1;
}
std::string str;
call->SerializeToString(&str);
const uint32_t len = str.length();
pthread_mutex_lock(&mSocketWriteMutex);
int n = write(mSocket, buf, len);
pthread_mutex_unlock(&mSocketWriteMutex);
int n = write(gClientSocket, &len, sizeof len);
if (n != sizeof len) {
LOGE("traceGLMessage: Error (%d) while writing message length\n", n);
stopServer();
exit(-1);
return n;
}
int TCPStream::receive(void *data, size_t len) {
if (mSocket <= 0) {
return -1;
}
n = write(gClientSocket, str.data(), str.length());
if (n != (int) str.length()) {
LOGE("traceGLMessage: Error while writing out message, result = %d, length = %d\n",
n, str.length());
stopServer();
exit(-1);
return read(mSocket, data, len);
}
BufferedOutputStream::BufferedOutputStream(TCPStream *stream, size_t bufferSize) {
mStream = stream;
mBufferSize = bufferSize;
mStringBuffer = "";
mStringBuffer.reserve(bufferSize);
}
int BufferedOutputStream::flush() {
if (mStringBuffer.size() == 0) {
return 0;
}
int n = mStream->send((void *)mStringBuffer.data(), mStringBuffer.size());
mStringBuffer.clear();
return n;
}
void BufferedOutputStream::enqueueMessage(GLMessage *msg) {
const uint32_t len = msg->ByteSize();
mStringBuffer.append((const char *)&len, sizeof(len)); // append header
msg->AppendToString(&mStringBuffer); // append message
}
int BufferedOutputStream::send(GLMessage *msg) {
enqueueMessage(msg);
if (mStringBuffer.size() > mBufferSize) {
return flush();
}
return 0;
}
}; // namespace gltrace

View File

@@ -17,15 +17,69 @@
#ifndef __GLTRACE_TRANSPORT_H_
#define __GLTRACE_TRANSPORT_H_
#include <pthread.h>
#include "gltrace.pb.h"
namespace android {
namespace gltrace {
void startServer(int port);
void stopServer();
/**
* TCPStream provides a TCP based communication channel from the device to
* the host for transferring GLMessages.
*/
class TCPStream {
int mSocket;
pthread_mutex_t mSocketWriteMutex;
public:
/** Create a TCP based communication channel over @socket */
TCPStream(int socket);
~TCPStream();
void traceGLMessage(GLMessage *msg);
/** Close the channel. */
void closeStream();
/** Send @data of size @len to host. . Returns -1 on error, 0 on success. */
int send(void *data, size_t len);
/** Receive data into @buf from the remote end. This is a blocking call. */
int receive(void *buf, size_t size);
};
/**
* BufferedOutputStream provides buffering of data sent to the underlying
* unbuffered channel.
*/
class BufferedOutputStream {
TCPStream *mStream;
size_t mBufferSize;
std::string mStringBuffer;
/** Enqueue message into internal buffer. */
void enqueueMessage(GLMessage *msg);
public:
/**
* Construct a Buffered stream of size @bufferSize, using @stream as
* its underlying channel for transport.
*/
BufferedOutputStream(TCPStream *stream, size_t bufferSize);
/**
* Send @msg. The message could be buffered and sent later with a
* subsequent message. Returns -1 on error, 0 on success.
*/
int send(GLMessage *msg);
/** Send any buffered messages, returns -1 on error, 0 on success. */
int flush();
};
/**
* Utility method: start a server at @serverPort, and wait for a client
* connection. Returns the connected client socket on success, or -1 on failure.
*/
int acceptClientConnection(int serverPort);
};
};

View File

@@ -146,7 +146,6 @@ TRACE_CALL_TEMPLATE = pyratemp.Template(
GLMessage glmsg;
GLTraceContext *glContext = getGLTraceContext();
glmsg.set_context_id(1);
glmsg.set_function(GLMessage::$!func!$);
<!--(if len(parsedArgs) > 0)-->
<!--(for argname, argtype in parsedArgs)-->
@@ -174,8 +173,8 @@ TRACE_CALL_TEMPLATE = pyratemp.Template(
rt->$!retDataType.getProtobufCall()!$retValue);
<!--(end)-->
fixupGLMessage(&glmsg);
traceGLMessage(&glmsg);
fixupGLMessage(glContext, &glmsg);
glContext->traceGLMessage(&glmsg);
<!--(if retType != "void")-->
return retValue;

View File

@@ -25,7 +25,7 @@ namespace android {
/* Hooks to be called by "interesting" EGL functions. */
void GLTrace_eglCreateContext(int version, EGLContext c);
void GLTrace_eglMakeCurrent(unsigned version, gl_hooks_t *hooks);
void GLTrace_eglMakeCurrent(unsigned version, gl_hooks_t *hooks, EGLContext c);
void GLTrace_eglReleaseThread();
void GLTrace_eglSwapBuffers(void*, void*);