diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp index d46f83b49..bf2477a64 100644 --- a/opengl/libs/EGL/egl.cpp +++ b/opengl/libs/EGL/egl.cpp @@ -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,36 +129,36 @@ void initEglTraceLevel() { } void initEglDebugLevel() { - int propertyLevel = 0; - char value[PROPERTY_VALUE_MAX]; + if (getEGLDebugLevel() == 0) { + char value[PROPERTY_VALUE_MAX]; - // check system property only on userdebug or eng builds - property_get("ro.debuggable", value, "0"); - if (value[0] == '0') - return; + // check system property only on userdebug or eng builds + property_get("ro.debuggable", value, "0"); + if (value[0] == '0') + return; - 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"); - if (file) { - char cmdline[256] = {}; - if (fgets(cmdline, sizeof(cmdline) - 1, 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; + property_get("debug.egl.debug_proc", value, ""); + if (strlen(value) > 0) { + FILE * file = fopen("/proc/self/cmdline", "r"); + if (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 + setEGLDebugLevel(1); + } } + fclose(file); } - 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 diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp index 4e4494142..d1f25e094 100644 --- a/opengl/libs/EGL/eglApi.cpp +++ b/opengl/libs/EGL/eglApi.cpp @@ -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; diff --git a/opengl/libs/GLES_trace/DESIGN.txt b/opengl/libs/GLES_trace/DESIGN.txt index a189e1d82..72a2e156d 100644 --- a/opengl/libs/GLES_trace/DESIGN.txt +++ b/opengl/libs/GLES_trace/DESIGN.txt @@ -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 diff --git a/opengl/libs/GLES_trace/src/gltrace_eglapi.cpp b/opengl/libs/GLES_trace/src/gltrace_eglapi.cpp index 9698bf90c..512d5626b 100644 --- a/opengl/libs/GLES_trace/src/gltrace_eglapi.cpp +++ b/opengl/libs/GLES_trace/src/gltrace_eglapi.cpp @@ -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() { - delete sGLTraceState; - sGLTraceState = NULL; + 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); diff --git a/opengl/libs/glestrace.h b/opengl/libs/glestrace.h index a08f97bfb..868b18d4e 100644 --- a/opengl/libs/glestrace.h +++ b/opengl/libs/glestrace.h @@ -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. */