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