Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a73a9772 authored by Siva Velusamy's avatar Siva Velusamy
Browse files

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
parent 0859b78d
Loading
Loading
Loading
Loading
+39 −28
Original line number Diff line number Diff line
@@ -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
+29 −5
Original line number Diff line number Diff line
@@ -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;
+31 −3
Original line number Diff line number Diff line
@@ -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
+57 −10
Original line number Diff line number Diff line
@@ -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() {
    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);

+1 −1
Original line number Diff line number Diff line
@@ -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. */