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