OSDN Git Service

gltrace: Add support for tracing running applications.
authorSiva Velusamy <vsiva@google.com>
Tue, 18 Dec 2012 22:56:55 +0000 (14:56 -0800)
committerSiva Velusamy <vsiva@google.com>
Thu, 20 Dec 2012 18:38:47 +0000 (10:38 -0800)
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

opengl/libs/EGL/egl.cpp
opengl/libs/EGL/eglApi.cpp
opengl/libs/GLES_trace/DESIGN.txt
opengl/libs/GLES_trace/src/gltrace_eglapi.cpp
opengl/libs/glestrace.h

index d46f83b..bf2477a 100644 (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,36 +129,36 @@ void initEglTraceLevel() {
 }
 
 void initEglDebugLevel() {
-    int propertyLevel = 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;
+    if (getEGLDebugLevel() == 0) {
+        char value[PROPERTY_VALUE_MAX];
 
-    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;
+        // 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) {
+            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
index 4e44941..d1f25e0 100644 (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;
index a189e1d..72a2e15 100644 (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
index 9698bf9..512d562 100644 (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;
+
+    pthread_mutex_lock(&sGlTraceStateLock);
 
+    if (sGlTraceInProgress) {
+        goto done;
+    }
+
+    char udsName[PROPERTY_VALUE_MAX];
     property_get("debug.egl.debug_portname", udsName, "gltrace");
-    int clientSocket = gltrace::acceptClientConnection(udsName);
+    clientSocket = gltrace::acceptClientConnection(udsName);
     if (clientSocket < 0) {
-        ALOGE("Error creating GLTrace server socket. Quitting application.");
-        exit(-1);
+        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);
 
index a08f97b..868b18d 100644 (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. */