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:
parent
0859b78db2
commit
a73a97728b
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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. */
|
||||
|
Loading…
Reference in New Issue
Block a user