gltrace: Add support for tracing running applications.

Currently, to activate OpenGL tracing, an application has to be
start with --opengl-trace option (or have a debug prop set).

This CL adds support for tracing an application which may already
be running. This is implemented as follows:
    - DDMS initiates a JDWP message to the VM indicating that
      opengl traces be enabled.
    - When that message is received, a flag is set that indicates
      that tracing should be enabled.
    - The trace flag is checked during every eglSwap() operation,
      and if it finds that tracing should be active and it isn't,
      then it starts the tracing component.

Change-Id: I3347fe89fc06c7404d7aa9360f4b21e5bf36ebcb
This commit is contained in:
Siva Velusamy 2012-12-18 14:56:55 -08:00
parent 0859b78db2
commit a73a97728b
5 changed files with 158 additions and 48 deletions

View File

@ -84,13 +84,20 @@ static int sEGLApplicationTraceLevel;
static bool sEGLSystraceEnabled;
static bool sEGLGetErrorEnabled;
int gEGLDebugLevel;
static int sEGLApplicationDebugLevel;
static volatile int sEGLDebugLevel;
extern gl_hooks_t gHooksTrace;
extern gl_hooks_t gHooksSystrace;
extern gl_hooks_t gHooksErrorTrace;
int getEGLDebugLevel() {
return sEGLDebugLevel;
}
void setEGLDebugLevel(int level) {
sEGLDebugLevel = level;
}
static inline void setGlTraceThreadSpecific(gl_hooks_t const *value) {
pthread_setspecific(gGLTraceKey, value);
}
@ -122,7 +129,7 @@ void initEglTraceLevel() {
}
void initEglDebugLevel() {
int propertyLevel = 0;
if (getEGLDebugLevel() == 0) {
char value[PROPERTY_VALUE_MAX];
// check system property only on userdebug or eng builds
@ -132,26 +139,26 @@ void initEglDebugLevel() {
property_get("debug.egl.debug_proc", value, "");
if (strlen(value) > 0) {
long pid = getpid();
char procPath[128] = {};
sprintf(procPath, "/proc/%ld/cmdline", pid);
FILE * file = fopen(procPath, "r");
FILE * file = fopen("/proc/self/cmdline", "r");
if (file) {
char cmdline[256] = {};
if (fgets(cmdline, sizeof(cmdline) - 1, file)) {
char cmdline[256];
if (fgets(cmdline, sizeof(cmdline), file)) {
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
propertyLevel = 1;
setEGLDebugLevel(1);
}
}
fclose(file);
}
}
}
gEGLDebugLevel = propertyLevel || sEGLApplicationDebugLevel;
if (gEGLDebugLevel > 0) {
GLTrace_start();
if (getEGLDebugLevel() > 0) {
if (GLTrace_start() < 0) {
ALOGE("Error starting Tracer for OpenGL ES. Disabling..");
setEGLDebugLevel(0);
}
}
}
@ -165,10 +172,11 @@ void setGLHooksThreadSpecific(gl_hooks_t const *value) {
} else if (sEGLTraceLevel > 0) {
setGlTraceThreadSpecific(value);
setGlThreadSpecific(&gHooksTrace);
} else if (gEGLDebugLevel > 0 && value != &gHooksNoContext) {
} else if (getEGLDebugLevel() > 0 && value != &gHooksNoContext) {
setGlTraceThreadSpecific(value);
setGlThreadSpecific(GLTrace_getGLHooks());
} else {
setGlTraceThreadSpecific(NULL);
setGlThreadSpecific(value);
}
}
@ -186,9 +194,12 @@ void setGLTraceLevel(int level) {
* Global entry point to allow applications to modify their own debug level.
* Debugging is enabled if either the application requested it, or if the system property
* matches the application's name.
* Note that this only sets the debug level. The value is read and used either in
* initEglDebugLevel() if the application hasn't initialized its display yet, or when
* eglSwapBuffers() is called next.
*/
void EGLAPI setGLDebugLevel(int level) {
sEGLApplicationDebugLevel = level;
setEGLDebugLevel(level);
}
#else

View File

@ -97,7 +97,8 @@ namespace android {
extern void setGLHooksThreadSpecific(gl_hooks_t const *value);
extern EGLBoolean egl_init_drivers();
extern const __eglMustCastToProperFunctionPointerType gExtensionForwarders[MAX_NUMBER_OF_GL_EXTENSIONS];
extern int gEGLDebugLevel;
extern int getEGLDebugLevel();
extern void setEGLDebugLevel(int level);
extern gl_hooks_t gHooksTrace;
} // namespace android;
@ -465,7 +466,7 @@ EGLContext eglCreateContext(EGLDisplay dpy, EGLConfig config,
egl_context_t* c = new egl_context_t(dpy, context, config, cnx,
version);
#if EGL_TRACE
if (gEGLDebugLevel > 0)
if (getEGLDebugLevel() > 0)
GLTrace_eglCreateContext(version, c);
#endif
return c;
@ -578,7 +579,7 @@ EGLBoolean eglMakeCurrent( EGLDisplay dpy, EGLSurface draw,
setGLHooksThreadSpecific(c->cnx->hooks[c->version]);
egl_tls_t::setContext(ctx);
#if EGL_TRACE
if (gEGLDebugLevel > 0)
if (getEGLDebugLevel() > 0)
GLTrace_eglMakeCurrent(c->version, c->cnx->hooks[c->version], ctx);
#endif
_c.acquire();
@ -858,8 +859,31 @@ EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface draw)
return setError(EGL_BAD_SURFACE, EGL_FALSE);
#if EGL_TRACE
if (gEGLDebugLevel > 0)
gl_hooks_t const *trace_hooks = getGLTraceThreadSpecific();
if (getEGLDebugLevel() > 0) {
if (trace_hooks == NULL) {
if (GLTrace_start() < 0) {
ALOGE("Disabling Tracer for OpenGL ES");
setEGLDebugLevel(0);
} else {
// switch over to the trace version of hooks
EGLContext ctx = egl_tls_t::getContext();
egl_context_t * const c = get_context(ctx);
if (c) {
setGLHooksThreadSpecific(c->cnx->hooks[c->version]);
GLTrace_eglMakeCurrent(c->version, c->cnx->hooks[c->version], ctx);
}
}
}
GLTrace_eglSwapBuffers(dpy, draw);
} else if (trace_hooks != NULL) {
// tracing is now disabled, so switch back to the non trace version
EGLContext ctx = egl_tls_t::getContext();
egl_context_t * const c = get_context(ctx);
if (c) setGLHooksThreadSpecific(c->cnx->hooks[c->version]);
GLTrace_stop();
}
#endif
egl_surface_t const * const s = get_surface(draw);
@ -1078,7 +1102,7 @@ EGLBoolean eglReleaseThread(void)
egl_tls_t::clearTLS();
#if EGL_TRACE
if (gEGLDebugLevel > 0)
if (getEGLDebugLevel() > 0)
GLTrace_eglReleaseThread();
#endif
return EGL_TRUE;

View File

@ -9,9 +9,6 @@ Code Runtime Behavior:
control whether tracing should be enabled for a certain process. If tracing is enabled, this
calls GLTrace_start() to start the trace server.
Note that initEglTraceLevel() is also called from early_egl_init(), but that happens in the
context of the zygote, so that invocation has no effect.
egl_display_t::initialize() then calls setGLHooksThreadSpecific() where we set the thread
specific gl_hooks structure to point to the trace implementation. From this point on, every
GLES call is redirected to the trace implementation.
@ -30,6 +27,37 @@ Code Runtime Behavior:
to explore if a more graceful method of stopping the application, or detaching tracing from the
application is required.
Enabling tracing while the application is running:
In order to allow tracing of an already running application, we allow DdmServer to enable
OpenGL tracing. In such a case, the application already has its GL hooks set up to point to the
real GL implementation, and we need to switch them to point to the trace implementation.
This is achieved by checking whether tracing should be enabled at every eglSwap call.
(Note: We were already checking for tracing at every eglSwap, the only change now is that
the tracing could actually be ON/OFF at runtime - earlier it was set once and never changed).
If eglSwap detects that tracing should be enabled now, then it performs the following steps:
- switch the gl hooks to point to the trace implementation.
- call trace eglMakeCurrent to indicate that there is now a new context that is current.
- continue on with tracing the eglSwap call.
This switches the hooks to point to the trace implementation only for the current context.
But the other contexts have their gl hooks updated when they perform eglMakeCurrent.
The GLTrace version of eglMakeCurrent now has to be updated to allow switching to a context
it may not know of. In such a case, it creates a context matching the version that it is now
switching to.
Disabling tracing:
We disable tracing under two conditions:
- stop tracing request from DdmServer
- gltrace transport gets disconnected from the host.
In either case, both actions simply disable the tracing flag. The current context gets its
gl hooks restored in the next eglSwap, and the other traced contexts get their gl hooks
restored when they perform a eglMakeCurrent.
Code Structure:
glestrace.h declares all the hooks exposed by libglestrace. These are used by EGL/egl.cpp and

View File

@ -33,6 +33,9 @@ using gltrace::GLTraceState;
using gltrace::GLTraceContext;
using gltrace::TCPStream;
static pthread_mutex_t sGlTraceStateLock = PTHREAD_MUTEX_INITIALIZER;
static int sGlTraceInProgress;
static GLTraceState *sGLTraceState;
static pthread_t sReceiveThreadId;
@ -105,33 +108,66 @@ static void *commandReceiveTask(void *arg) {
return NULL;
}
void GLTrace_start() {
char udsName[PROPERTY_VALUE_MAX];
/**
* Starts Trace Server and waits for connection from the host.
* Returns -1 in case of connection error, 0 otherwise.
*/
int GLTrace_start() {
int status = 0;
int clientSocket = -1;
TCPStream *stream = NULL;
property_get("debug.egl.debug_portname", udsName, "gltrace");
int clientSocket = gltrace::acceptClientConnection(udsName);
if (clientSocket < 0) {
ALOGE("Error creating GLTrace server socket. Quitting application.");
exit(-1);
pthread_mutex_lock(&sGlTraceStateLock);
if (sGlTraceInProgress) {
goto done;
}
char udsName[PROPERTY_VALUE_MAX];
property_get("debug.egl.debug_portname", udsName, "gltrace");
clientSocket = gltrace::acceptClientConnection(udsName);
if (clientSocket < 0) {
ALOGE("Error creating GLTrace server socket. Tracing disabled.");
status = -1;
goto done;
}
sGlTraceInProgress = 1;
// create communication channel to the host
TCPStream *stream = new TCPStream(clientSocket);
stream = new TCPStream(clientSocket);
// initialize tracing state
sGLTraceState = new GLTraceState(stream);
pthread_create(&sReceiveThreadId, NULL, commandReceiveTask, sGLTraceState);
done:
pthread_mutex_unlock(&sGlTraceStateLock);
return status;
}
void GLTrace_stop() {
pthread_mutex_lock(&sGlTraceStateLock);
if (sGlTraceInProgress) {
sGlTraceInProgress = 0;
delete sGLTraceState;
sGLTraceState = NULL;
}
pthread_mutex_unlock(&sGlTraceStateLock);
}
void GLTrace_eglCreateContext(int version, EGLContext c) {
pthread_mutex_lock(&sGlTraceStateLock);
GLTraceState *state = sGLTraceState;
pthread_mutex_unlock(&sGlTraceStateLock);
if (state == NULL) return;
// update trace state for new EGL context
GLTraceContext *traceContext = sGLTraceState->createTraceContext(version, c);
GLTraceContext *traceContext = state->createTraceContext(version, c);
gltrace::setupTraceContextThreadSpecific(traceContext);
// trace command through to the host
@ -139,8 +175,19 @@ void GLTrace_eglCreateContext(int version, EGLContext c) {
}
void GLTrace_eglMakeCurrent(const unsigned version, gl_hooks_t *hooks, EGLContext c) {
pthread_mutex_lock(&sGlTraceStateLock);
GLTraceState *state = sGLTraceState;
pthread_mutex_unlock(&sGlTraceStateLock);
if (state == NULL) return;
// setup per context state
GLTraceContext *traceContext = sGLTraceState->getTraceContext(c);
GLTraceContext *traceContext = state->getTraceContext(c);
if (traceContext == NULL) {
GLTrace_eglCreateContext(version, c);
traceContext = state->getTraceContext(c);
}
traceContext->hooks = hooks;
gltrace::setupTraceContextThreadSpecific(traceContext);

View File

@ -30,7 +30,7 @@ void GLTrace_eglReleaseThread();
void GLTrace_eglSwapBuffers(void*, void*);
/* Start and stop GL Tracing. */
void GLTrace_start();
int GLTrace_start();
void GLTrace_stop();
/* Obtain the gl_hooks structure filled with the trace implementation for all GL functions. */