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 sEGLSystraceEnabled;
static bool sEGLGetErrorEnabled; static bool sEGLGetErrorEnabled;
int gEGLDebugLevel; static volatile int sEGLDebugLevel;
static int sEGLApplicationDebugLevel;
extern gl_hooks_t gHooksTrace; extern gl_hooks_t gHooksTrace;
extern gl_hooks_t gHooksSystrace; extern gl_hooks_t gHooksSystrace;
extern gl_hooks_t gHooksErrorTrace; extern gl_hooks_t gHooksErrorTrace;
int getEGLDebugLevel() {
return sEGLDebugLevel;
}
void setEGLDebugLevel(int level) {
sEGLDebugLevel = level;
}
static inline void setGlTraceThreadSpecific(gl_hooks_t const *value) { static inline void setGlTraceThreadSpecific(gl_hooks_t const *value) {
pthread_setspecific(gGLTraceKey, value); pthread_setspecific(gGLTraceKey, value);
} }
@ -122,36 +129,36 @@ void initEglTraceLevel() {
} }
void initEglDebugLevel() { void initEglDebugLevel() {
int propertyLevel = 0; if (getEGLDebugLevel() == 0) {
char value[PROPERTY_VALUE_MAX]; char value[PROPERTY_VALUE_MAX];
// check system property only on userdebug or eng builds // check system property only on userdebug or eng builds
property_get("ro.debuggable", value, "0"); property_get("ro.debuggable", value, "0");
if (value[0] == '0') if (value[0] == '0')
return; return;
property_get("debug.egl.debug_proc", value, ""); property_get("debug.egl.debug_proc", value, "");
if (strlen(value) > 0) { if (strlen(value) > 0) {
long pid = getpid(); FILE * file = fopen("/proc/self/cmdline", "r");
char procPath[128] = {}; if (file) {
sprintf(procPath, "/proc/%ld/cmdline", pid); char cmdline[256];
FILE * file = fopen(procPath, "r"); if (fgets(cmdline, sizeof(cmdline), file)) {
if (file) { if (!strncmp(value, cmdline, strlen(value))) {
char cmdline[256] = {}; // set EGL debug if the "debug.egl.debug_proc" property
if (fgets(cmdline, sizeof(cmdline) - 1, file)) { // matches the prefix of this application's command line
if (!strncmp(value, cmdline, strlen(value))) { setEGLDebugLevel(1);
// set EGL debug if the "debug.egl.debug_proc" property }
// matches the prefix of this application's command line
propertyLevel = 1;
} }
fclose(file);
} }
fclose(file);
} }
} }
gEGLDebugLevel = propertyLevel || sEGLApplicationDebugLevel; if (getEGLDebugLevel() > 0) {
if (gEGLDebugLevel > 0) { if (GLTrace_start() < 0) {
GLTrace_start(); 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) { } else if (sEGLTraceLevel > 0) {
setGlTraceThreadSpecific(value); setGlTraceThreadSpecific(value);
setGlThreadSpecific(&gHooksTrace); setGlThreadSpecific(&gHooksTrace);
} else if (gEGLDebugLevel > 0 && value != &gHooksNoContext) { } else if (getEGLDebugLevel() > 0 && value != &gHooksNoContext) {
setGlTraceThreadSpecific(value); setGlTraceThreadSpecific(value);
setGlThreadSpecific(GLTrace_getGLHooks()); setGlThreadSpecific(GLTrace_getGLHooks());
} else { } else {
setGlTraceThreadSpecific(NULL);
setGlThreadSpecific(value); setGlThreadSpecific(value);
} }
} }
@ -186,9 +194,12 @@ void setGLTraceLevel(int level) {
* Global entry point to allow applications to modify their own debug 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 * Debugging is enabled if either the application requested it, or if the system property
* matches the application's name. * 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) { void EGLAPI setGLDebugLevel(int level) {
sEGLApplicationDebugLevel = level; setEGLDebugLevel(level);
} }
#else #else

View File

@ -97,7 +97,8 @@ namespace android {
extern void setGLHooksThreadSpecific(gl_hooks_t const *value); extern void setGLHooksThreadSpecific(gl_hooks_t const *value);
extern EGLBoolean egl_init_drivers(); extern EGLBoolean egl_init_drivers();
extern const __eglMustCastToProperFunctionPointerType gExtensionForwarders[MAX_NUMBER_OF_GL_EXTENSIONS]; 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; extern gl_hooks_t gHooksTrace;
} // namespace android; } // namespace android;
@ -465,7 +466,7 @@ EGLContext eglCreateContext(EGLDisplay dpy, EGLConfig config,
egl_context_t* c = new egl_context_t(dpy, context, config, cnx, egl_context_t* c = new egl_context_t(dpy, context, config, cnx,
version); version);
#if EGL_TRACE #if EGL_TRACE
if (gEGLDebugLevel > 0) if (getEGLDebugLevel() > 0)
GLTrace_eglCreateContext(version, c); GLTrace_eglCreateContext(version, c);
#endif #endif
return c; return c;
@ -578,7 +579,7 @@ EGLBoolean eglMakeCurrent( EGLDisplay dpy, EGLSurface draw,
setGLHooksThreadSpecific(c->cnx->hooks[c->version]); setGLHooksThreadSpecific(c->cnx->hooks[c->version]);
egl_tls_t::setContext(ctx); egl_tls_t::setContext(ctx);
#if EGL_TRACE #if EGL_TRACE
if (gEGLDebugLevel > 0) if (getEGLDebugLevel() > 0)
GLTrace_eglMakeCurrent(c->version, c->cnx->hooks[c->version], ctx); GLTrace_eglMakeCurrent(c->version, c->cnx->hooks[c->version], ctx);
#endif #endif
_c.acquire(); _c.acquire();
@ -858,8 +859,31 @@ EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface draw)
return setError(EGL_BAD_SURFACE, EGL_FALSE); return setError(EGL_BAD_SURFACE, EGL_FALSE);
#if EGL_TRACE #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); 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 #endif
egl_surface_t const * const s = get_surface(draw); egl_surface_t const * const s = get_surface(draw);
@ -1078,7 +1102,7 @@ EGLBoolean eglReleaseThread(void)
egl_tls_t::clearTLS(); egl_tls_t::clearTLS();
#if EGL_TRACE #if EGL_TRACE
if (gEGLDebugLevel > 0) if (getEGLDebugLevel() > 0)
GLTrace_eglReleaseThread(); GLTrace_eglReleaseThread();
#endif #endif
return EGL_TRUE; 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 control whether tracing should be enabled for a certain process. If tracing is enabled, this
calls GLTrace_start() to start the trace server. 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 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 specific gl_hooks structure to point to the trace implementation. From this point on, every
GLES call is redirected to the trace implementation. 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 to explore if a more graceful method of stopping the application, or detaching tracing from the
application is required. 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: Code Structure:
glestrace.h declares all the hooks exposed by libglestrace. These are used by EGL/egl.cpp and 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::GLTraceContext;
using gltrace::TCPStream; using gltrace::TCPStream;
static pthread_mutex_t sGlTraceStateLock = PTHREAD_MUTEX_INITIALIZER;
static int sGlTraceInProgress;
static GLTraceState *sGLTraceState; static GLTraceState *sGLTraceState;
static pthread_t sReceiveThreadId; static pthread_t sReceiveThreadId;
@ -105,33 +108,66 @@ static void *commandReceiveTask(void *arg) {
return NULL; 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"); pthread_mutex_lock(&sGlTraceStateLock);
int clientSocket = gltrace::acceptClientConnection(udsName);
if (clientSocket < 0) { if (sGlTraceInProgress) {
ALOGE("Error creating GLTrace server socket. Quitting application."); goto done;
exit(-1);
} }
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 // create communication channel to the host
TCPStream *stream = new TCPStream(clientSocket); stream = new TCPStream(clientSocket);
// initialize tracing state // initialize tracing state
sGLTraceState = new GLTraceState(stream); sGLTraceState = new GLTraceState(stream);
pthread_create(&sReceiveThreadId, NULL, commandReceiveTask, sGLTraceState); pthread_create(&sReceiveThreadId, NULL, commandReceiveTask, sGLTraceState);
done:
pthread_mutex_unlock(&sGlTraceStateLock);
return status;
} }
void GLTrace_stop() { void GLTrace_stop() {
delete sGLTraceState; pthread_mutex_lock(&sGlTraceStateLock);
sGLTraceState = NULL;
if (sGlTraceInProgress) {
sGlTraceInProgress = 0;
delete sGLTraceState;
sGLTraceState = NULL;
}
pthread_mutex_unlock(&sGlTraceStateLock);
} }
void GLTrace_eglCreateContext(int version, EGLContext c) { 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 // update trace state for new EGL context
GLTraceContext *traceContext = sGLTraceState->createTraceContext(version, c); GLTraceContext *traceContext = state->createTraceContext(version, c);
gltrace::setupTraceContextThreadSpecific(traceContext); gltrace::setupTraceContextThreadSpecific(traceContext);
// trace command through to the host // 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) { 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 // 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; traceContext->hooks = hooks;
gltrace::setupTraceContextThreadSpecific(traceContext); gltrace::setupTraceContextThreadSpecific(traceContext);

View File

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