1/* 2 ** Copyright 2010, The Android Open Source Project 3 ** 4 ** Licensed under the Apache License, Version 2.0 (the "License"); 5 ** you may not use this file except in compliance with the License. 6 ** You may obtain a copy of the License at 7 ** 8 ** http://www.apache.org/licenses/LICENSE-2.0 9 ** 10 ** Unless required by applicable law or agreed to in writing, software 11 ** distributed under the License is distributed on an "AS IS" BASIS, 12 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 ** See the License for the specific language governing permissions and 14 ** limitations under the License. 15 */ 16 17#if EGL_TRACE 18 19#include <stdarg.h> 20#include <stdlib.h> 21 22#include <EGL/egl.h> 23#include <EGL/eglext.h> 24#include <GLES/gl.h> 25#include <GLES/glext.h> 26 27#include <cutils/log.h> 28 29#define ATRACE_TAG ATRACE_TAG_GRAPHICS 30#include <utils/Trace.h> 31 32#include <utils/CallStack.h> 33 34#include "egl_tls.h" 35#include "hooks.h" 36 37// ---------------------------------------------------------------------------- 38namespace android { 39// ---------------------------------------------------------------------------- 40 41struct GLenumString { 42 GLenum e; 43 const char* s; 44}; 45 46#undef GL_ENUM 47#define GL_ENUM(VAL,NAME) {VAL, #NAME}, 48 49static GLenumString g_enumnames[] = { 50#include "enums.in" 51}; 52#undef GL_ENUM 53 54static int compareGLEnum(const void* a, const void* b) { 55 return ((const GLenumString*) a)->e - ((const GLenumString*) b)->e; 56} 57 58static const char* GLEnumToString(GLenum e) { 59 GLenumString key = {e, ""}; 60 const GLenumString* result = (const GLenumString*) bsearch( 61 &key, g_enumnames, 62 sizeof(g_enumnames) / sizeof(g_enumnames[0]), 63 sizeof(g_enumnames[0]), compareGLEnum); 64 if (result) { 65 return result->s; 66 } 67 return NULL; 68} 69 70static const char* GLbooleanToString(GLboolean arg) { 71 return arg ? "GL_TRUE" : "GL_FALSE"; 72} 73 74static GLenumString g_bitfieldNames[] = { 75 {0x00004000, "GL_COLOR_BUFFER_BIT"}, 76 {0x00000400, "GL_STENCIL_BUFFER_BIT"}, 77 {0x00000100, "GL_DEPTH_BUFFER_BIT"} 78}; 79 80class StringBuilder { 81 static const int lineSize = 500; 82 char line[lineSize]; 83 int line_index; 84public: 85 StringBuilder() { 86 line_index = 0; 87 line[0] = '\0'; 88 } 89 void append(const char* fmt, ...) { 90 va_list argp; 91 va_start(argp, fmt); 92 line_index += vsnprintf(line + line_index, lineSize-line_index, fmt, argp); 93 va_end(argp); 94 } 95 const char* getString() { 96 line_index = 0; 97 line[lineSize-1] = '\0'; 98 return line; 99 } 100}; 101 102 103static void TraceGLShaderSource(GLuint shader, GLsizei count, 104 const GLchar** string, const GLint* length) { 105 ALOGD("const char* shaderSrc[] = {"); 106 for (GLsizei i = 0; i < count; i++) { 107 const char* comma = i < count-1 ? "," : ""; 108 const GLchar* s = string[i]; 109 if (length) { 110 GLint len = length[i]; 111 ALOGD(" \"%*s\"%s", len, s, comma); 112 } else { 113 ALOGD(" \"%s\"%s", s, comma); 114 } 115 } 116 ALOGD("};"); 117 if (length) { 118 ALOGD("const GLint* shaderLength[] = {"); 119 for (GLsizei i = 0; i < count; i++) { 120 const char* comma = i < count-1 ? "," : ""; 121 GLint len = length[i]; 122 ALOGD(" \"%d\"%s", len, comma); 123 } 124 ALOGD("};"); 125 ALOGD("glShaderSource(%u, %u, shaderSrc, shaderLength);", 126 shader, count); 127 } else { 128 ALOGD("glShaderSource(%u, %u, shaderSrc, (const GLint*) 0);", 129 shader, count); 130 } 131} 132 133static void TraceValue(int elementCount, char type, 134 GLsizei chunkCount, GLsizei chunkSize, const void* value) { 135 StringBuilder stringBuilder; 136 GLsizei count = chunkCount * chunkSize; 137 bool isFloat = type == 'f'; 138 const char* typeString = isFloat ? "GLfloat" : "GLint"; 139 ALOGD("const %s value[] = {", typeString); 140 for (GLsizei i = 0; i < count; i++) { 141 StringBuilder builder; 142 builder.append(" "); 143 for (int e = 0; e < elementCount; e++) { 144 const char* comma = ", "; 145 if (e == elementCount-1) { 146 if (i == count - 1) { 147 comma = ""; 148 } else { 149 comma = ","; 150 } 151 } 152 if (isFloat) { 153 builder.append("%g%s", * (GLfloat*) value, comma); 154 value = (void*) (((GLfloat*) value) + 1); 155 } else { 156 builder.append("%d%s", * (GLint*) value, comma); 157 value = (void*) (((GLint*) value) + 1); 158 } 159 } 160 ALOGD("%s", builder.getString()); 161 if (chunkSize > 1 && i < count-1 162 && (i % chunkSize) == (chunkSize-1)) { 163 ALOGD("%s", ""); // Print a blank line. 164 } 165 } 166 ALOGD("};"); 167} 168 169static void TraceUniformv(int elementCount, char type, 170 GLuint location, GLsizei count, const void* value) { 171 TraceValue(elementCount, type, count, 1, value); 172 ALOGD("glUniform%d%c(%u, %u, value);", elementCount, type, location, count); 173} 174 175static void TraceUniformMatrix(int matrixSideLength, 176 GLuint location, GLsizei count, GLboolean transpose, const void* value) { 177 TraceValue(matrixSideLength, 'f', count, matrixSideLength, value); 178 ALOGD("glUniformMatrix%dfv(%u, %u, %s, value);", matrixSideLength, location, count, 179 GLbooleanToString(transpose)); 180} 181 182static void TraceGL(const char* name, int numArgs, ...) { 183 va_list argp; 184 va_start(argp, numArgs); 185 int nameLen = strlen(name); 186 187 // glShaderSource 188 if (nameLen == 14 && strcmp(name, "glShaderSource") == 0) { 189 va_arg(argp, const char*); 190 GLuint shader = va_arg(argp, GLuint); 191 va_arg(argp, const char*); 192 GLsizei count = va_arg(argp, GLsizei); 193 va_arg(argp, const char*); 194 const GLchar** string = (const GLchar**) va_arg(argp, void*); 195 va_arg(argp, const char*); 196 const GLint* length = (const GLint*) va_arg(argp, void*); 197 va_end(argp); 198 TraceGLShaderSource(shader, count, string, length); 199 return; 200 } 201 202 // glUniformXXv 203 204 if (nameLen == 12 && strncmp(name, "glUniform", 9) == 0 && name[11] == 'v') { 205 int elementCount = name[9] - '0'; // 1..4 206 char type = name[10]; // 'f' or 'i' 207 va_arg(argp, const char*); 208 GLuint location = va_arg(argp, GLuint); 209 va_arg(argp, const char*); 210 GLsizei count = va_arg(argp, GLsizei); 211 va_arg(argp, const char*); 212 const void* value = (const void*) va_arg(argp, void*); 213 va_end(argp); 214 TraceUniformv(elementCount, type, location, count, value); 215 return; 216 } 217 218 // glUniformMatrixXfv 219 220 if (nameLen == 18 && strncmp(name, "glUniformMatrix", 15) == 0 221 && name[16] == 'f' && name[17] == 'v') { 222 int matrixSideLength = name[15] - '0'; // 2..4 223 va_arg(argp, const char*); 224 GLuint location = va_arg(argp, GLuint); 225 va_arg(argp, const char*); 226 GLsizei count = va_arg(argp, GLsizei); 227 va_arg(argp, const char*); 228 GLboolean transpose = (GLboolean) va_arg(argp, int); 229 va_arg(argp, const char*); 230 const void* value = (const void*) va_arg(argp, void*); 231 va_end(argp); 232 TraceUniformMatrix(matrixSideLength, location, count, transpose, value); 233 return; 234 } 235 236 StringBuilder builder; 237 builder.append("%s(", name); 238 for (int i = 0; i < numArgs; i++) { 239 if (i > 0) { 240 builder.append(", "); 241 } 242 const char* type = va_arg(argp, const char*); 243 bool isPtr = type[strlen(type)-1] == '*' 244 || strcmp(type, "GLeglImageOES") == 0; 245 if (isPtr) { 246 const void* arg = va_arg(argp, const void*); 247 builder.append("(%s) 0x%08x", type, (size_t) arg); 248 } else if (strcmp(type, "GLbitfield") == 0) { 249 size_t arg = va_arg(argp, size_t); 250 bool first = true; 251 for (size_t i = 0; i < sizeof(g_bitfieldNames) / sizeof(g_bitfieldNames[0]); i++) { 252 const GLenumString* b = &g_bitfieldNames[i]; 253 if (b->e & arg) { 254 if (first) { 255 first = false; 256 } else { 257 builder.append(" | "); 258 } 259 builder.append("%s", b->s); 260 arg &= ~b->e; 261 } 262 } 263 if (first || arg != 0) { 264 if (!first) { 265 builder.append(" | "); 266 } 267 builder.append("0x%08x", arg); 268 } 269 } else if (strcmp(type, "GLboolean") == 0) { 270 GLboolean arg = va_arg(argp, int); 271 builder.append("%s", GLbooleanToString(arg)); 272 } else if (strcmp(type, "GLclampf") == 0) { 273 double arg = va_arg(argp, double); 274 builder.append("%g", arg); 275 } else if (strcmp(type, "GLenum") == 0) { 276 GLenum arg = va_arg(argp, int); 277 const char* s = GLEnumToString(arg); 278 if (s) { 279 builder.append("%s", s); 280 } else { 281 builder.append("0x%x", arg); 282 } 283 } else if (strcmp(type, "GLfixed") == 0) { 284 int arg = va_arg(argp, int); 285 builder.append("0x%08x", arg); 286 } else if (strcmp(type, "GLfloat") == 0) { 287 double arg = va_arg(argp, double); 288 builder.append("%g", arg); 289 } else if (strcmp(type, "GLint") == 0) { 290 int arg = va_arg(argp, int); 291 const char* s = NULL; 292 if (strcmp(name, "glTexParameteri") == 0) { 293 s = GLEnumToString(arg); 294 } 295 if (s) { 296 builder.append("%s", s); 297 } else { 298 builder.append("%d", arg); 299 } 300 } else if (strcmp(type, "GLintptr") == 0) { 301 int arg = va_arg(argp, unsigned int); 302 builder.append("%u", arg); 303 } else if (strcmp(type, "GLsizei") == 0) { 304 int arg = va_arg(argp, size_t); 305 builder.append("%u", arg); 306 } else if (strcmp(type, "GLsizeiptr") == 0) { 307 int arg = va_arg(argp, size_t); 308 builder.append("%u", arg); 309 } else if (strcmp(type, "GLuint") == 0) { 310 int arg = va_arg(argp, unsigned int); 311 builder.append("%u", arg); 312 } else { 313 builder.append("/* ??? %s */", type); 314 break; 315 } 316 } 317 builder.append(");"); 318 ALOGD("%s", builder.getString()); 319 va_end(argp); 320} 321 322/////////////////////////////////////////////////////////////////////////// 323// Log trace 324/////////////////////////////////////////////////////////////////////////// 325 326#undef TRACE_GL_VOID 327#undef TRACE_GL 328 329#define TRACE_GL_VOID(_api, _args, _argList, ...) \ 330static void Tracing_ ## _api _args { \ 331 TraceGL(#_api, __VA_ARGS__); \ 332 gl_hooks_t::gl_t const * const _c = &getGLTraceThreadSpecific()->gl; \ 333 _c->_api _argList; \ 334} 335 336#define TRACE_GL(_type, _api, _args, _argList, ...) \ 337static _type Tracing_ ## _api _args { \ 338 TraceGL(#_api, __VA_ARGS__); \ 339 gl_hooks_t::gl_t const * const _c = &getGLTraceThreadSpecific()->gl; \ 340 return _c->_api _argList; \ 341} 342 343extern "C" { 344#include "../trace.in" 345} 346 347#undef TRACE_GL_VOID 348#undef TRACE_GL 349 350#define GL_ENTRY(_r, _api, ...) Tracing_ ## _api, 351EGLAPI gl_hooks_t gHooksTrace = { 352 { 353 #include "entries.in" 354 }, 355 { 356 {0} 357 } 358}; 359#undef GL_ENTRY 360 361#undef TRACE_GL_VOID 362#undef TRACE_GL 363 364// define the ES 1.0 Debug_gl* functions as Tracing_gl functions 365#define TRACE_GL_VOID(_api, _args, _argList, ...) \ 366static void Debug_ ## _api _args { \ 367 TraceGL(#_api, __VA_ARGS__); \ 368 gl_hooks_t::gl_t const * const _c = &getGLTraceThreadSpecific()->gl; \ 369 _c->_api _argList; \ 370} 371 372#define TRACE_GL(_type, _api, _args, _argList, ...) \ 373static _type Debug_ ## _api _args { \ 374 TraceGL(#_api, __VA_ARGS__); \ 375 gl_hooks_t::gl_t const * const _c = &getGLTraceThreadSpecific()->gl; \ 376 return _c->_api _argList; \ 377} 378 379extern "C" { 380#include "../debug.in" 381} 382 383/////////////////////////////////////////////////////////////////////////// 384// Systrace 385/////////////////////////////////////////////////////////////////////////// 386 387#undef TRACE_GL_VOID 388#undef TRACE_GL 389 390#define TRACE_GL_VOID(_api, _args, _argList, ...) \ 391static void Systrace_ ## _api _args { \ 392 ATRACE_NAME(#_api); \ 393 gl_hooks_t::gl_t const * const _c = &getGLTraceThreadSpecific()->gl; \ 394 _c->_api _argList; \ 395} 396 397#define TRACE_GL(_type, _api, _args, _argList, ...) \ 398static _type Systrace_ ## _api _args { \ 399 ATRACE_NAME(#_api); \ 400 gl_hooks_t::gl_t const * const _c = &getGLTraceThreadSpecific()->gl; \ 401 return _c->_api _argList; \ 402} 403 404extern "C" { 405#include "../trace.in" 406} 407 408#undef TRACE_GL_VOID 409#undef TRACE_GL 410 411#define GL_ENTRY(_r, _api, ...) Systrace_ ## _api, 412EGLAPI gl_hooks_t gHooksSystrace = { 413 { 414 #include "entries.in" 415 }, 416 { 417 {0} 418 } 419}; 420#undef GL_ENTRY 421 422/////////////////////////////////////////////////////////////////////////// 423// 424/////////////////////////////////////////////////////////////////////////// 425 426#undef TRACE_GL_VOID 427#undef TRACE_GL 428 429#define CHECK_ERROR(_c, _api) \ 430 GLenum status = GL_NO_ERROR; \ 431 bool error = false; \ 432 while ((status = _c->glGetError()) != GL_NO_ERROR) { \ 433 ALOGD("[" #_api "] 0x%x", status); \ 434 error = true; \ 435 } \ 436 if (error) { \ 437 CallStack s; \ 438 s.update(); \ 439 s.dump("glGetError:" #_api); \ 440 } \ 441 442#define TRACE_GL_VOID(_api, _args, _argList, ...) \ 443static void ErrorTrace_ ## _api _args { \ 444 gl_hooks_t::gl_t const * const _c = &getGLTraceThreadSpecific()->gl; \ 445 _c->_api _argList; \ 446 CHECK_ERROR(_c, _api); \ 447} 448 449#define TRACE_GL(_type, _api, _args, _argList, ...) \ 450static _type ErrorTrace_ ## _api _args { \ 451 gl_hooks_t::gl_t const * const _c = &getGLTraceThreadSpecific()->gl; \ 452 _type _r = _c->_api _argList; \ 453 CHECK_ERROR(_c, _api); \ 454 return _r; \ 455} 456 457extern "C" { 458#include "../trace.in" 459} 460 461#undef TRACE_GL_VOID 462#undef TRACE_GL 463 464#define GL_ENTRY(_r, _api, ...) ErrorTrace_ ## _api, 465EGLAPI gl_hooks_t gHooksErrorTrace = { 466 { 467 #include "entries.in" 468 }, 469 { 470 {0} 471 } 472}; 473#undef GL_ENTRY 474#undef CHECK_ERROR 475 476#undef TRACE_GL_VOID 477#undef TRACE_GL 478 479// ---------------------------------------------------------------------------- 480}; // namespace android 481// ---------------------------------------------------------------------------- 482 483#endif // EGL_TRACE 484